Retro Z80 is now Zalt

Previously I have headed my blog posts with ‘Retro Z80’ to indicate my Z80 computer project.

Although the Z80 part is correct and can be considered retro, the rest of my project is not so retro. I use a Cypress PSoC 5 as a System Controller that is an ARM processor and a CPLD/FPGA combined. I also use the Altera Max II (EPM240) CPLD. Both these devices are hardly retro, not the latest greatest – bleeding edge, but certainly not retro – at least not in the next few years.

So when I was creating a target for the z88dk I had to come up with a name. It was at that time that I choose Zalt. Obviously it sounds like salt. Sweet short and a little different.

The Z in Zalt refers to the Z80 CPU. The ‘alt’ in Zalt is for alternative. Because I am not sticking to true retro components I thought this was very appropriate.

So from now on, my blog posts about my Z80 computer project will be headed with Zalt instead of Retro Z80.

 

Advertisements
Published in: on June 24, 2016 at 5:04 pm  Leave a Comment  

Retro Z80: The Decoder Board

I have finished the board layout for the decoder board. The CPU Board I finished previous has an extra connector on the side (besides the two bus header connectors) that contains the signals needed to drive the Memory Management Unit. These are the top 4 chips including the SMD RAM part. The signals for these lines are provided by the decoder board that is built around an Altera MAX II (240) part. This is an (old-ish) CPLD that are available on ebay for less than a $1 each (in quantity of 10). The Max II has 80 general purpose IO pins. All CPU Bus signals are fed into the CPLD and it outputs the needed signals for the MMU and I still have more IO to spare for future extensions and enhancements.

Below the decoder board layout (KiCad).

DecoderPCB_1

As you can see I have incorporated a fairly large section of prototyping area (left). Because not all the details are known at this time I attempted to make the design as flexible as possible. I feared that if I waited until everything was worked out in detail, I would take a long time and I would lose the momentum and enthusiasm. Also I like to test my designs in practice and learn from that experience. So it’s an iterative approach.

The board has a lot of SMD diodes and resistors (and decoupling caps) on both sides of the board. This resulted in having a fairly efficient layout and having extra spare room when I was finished. I considered putting on a couple of GAL16V8’s on there (more programmable logic) or even my entire System Controller (Cypress PSoC 5). But I settled for the prototyping area as it was suggested by Ron, a fellow retro designer from the US and indeed the most flexible solution. There are two small prototyping areas on the right – that were just spots I had no use for…

The Decoder Board sports a couple of connectors on the right. The 40 pin contains the extra control signals that can be programmed into the Max II. The first half of that connector is reserved for the CPU Board MMU signals. The second half is still unassigned. In addition to that I have also routed 8 extra lines to the IO Bus Header (bottom) and two remaining lines to the (top) Memory Bus Header.

I have ordered 10 of these boards with Elecrow again – this time a green solder mask- and it will be some time before they arrive. This allows me to focus again on the software side of things and write support routines for the MMU and figure out some way to get it working in the z88dk C compiler.

PS: I have save a partial board layout (and schema) to perhaps make a general purpose board for the Max II with 5 Volt tolerance. I think If I made an Arduino compatible board (shield) I could be on to something.

Published in: on May 1, 2016 at 8:56 am  Leave a Comment  
Tags:

Retro Z80: CPU Board

Hardware

I have been busy with designing a PCB for the Z80 CPU. This CPU Board has 4x 64k (x8 bits)  static RAM on board next to the Z80. It also has an additional 32k of RAM that serves as a Memory Management Unit (MMU) for deciding which 4k blocks of the 4 banks of 64k are in the active 64k range and where.

Here is the board as it arrived today. I had it made in China with Elecrow (found through pcb-shopper) and they did an excellent job for a small price tag.

WP_20160417_12_40_11_Pro

There are two 40 pin headers on each side of the board that runs bus signals to the other boards that are yet to be designed. The idea is that a stack of these 10cm x 10cm boards all connected to the common bus, makes a complete computer system.

Next up is the decoder board that decodes all the control signals from the common bus feeds them back to the CPU-board – thats the connector on the top-right side. The decoder board contains an Altera Max II (240) with a load of passives (clamp diodes, pull-up and series resistors) to adapt the 3.3V CPLD to the 5V bus. I am designing that now. I have waited until I got my CPU board back. If there is any (stupid) mistakes in the CPU-board, I can correct them (or learn from them) in the decoder board. All these boards will have the 4 mounting holes in each corner and the two bus connectors (as well as the power supply connector in the top right).

The only downside…. I have 12 of them (I ordered 10)…

Software

I have also been busy with the software. I have created a custom target for the z88dk. It was quite a tedious process because my test setup had a couple of hardware bugs (wiring faults). These errors caused the system to run ok on small programs but hang on larger programs. So always check your wiring twice!

Once I had the hardware sorted, the software started to work pretty quickly. A z88dk target consists of startup code to connect the C program with its execution environment (the computer). This can optionally contain a bios at address $0000 which I used to test with. I only coded the entry points for the RST instructions and NMI interrupt so I could catch those if they happened. The z88dk uses drivers to connect the C-porgram to things like the stdin and stdout streams (although in C this structure is called FILE – talk about a bad name). If you recall I had the basic input and output working on the System Contoller so I implementing these drivers for basic IO was dead-easy.

The complexity (at first) in creating a z88dk target lies in the structure, organization and mechanisms used in the z88dk. Thankfully they have a forum and you receive very elaborate answers on your questions – excellent support.

I added two commands for the System Controller. A Memory Fill (mf) that allows you to initialize a part of the RAM memory. This has been useful to ensure you start with a clean slate for each test run. I also added a Terminal Off command (to) that makes the System Controller ignore the incoming serial data. It just keeps it in its buffer and now the Z80 can use the input instruction to retrieve the characters.

Next Steps

Next I will finish designing the decoder board and get started with advancing the bios for the system as well as the C integration. There are a couple of more drivers I need to look at in detail.

I will also start to think on how I wish to manage those 4 banks of 64k of RAM memory. The z88dk C compiler supports a 24 bit far memory model but I cannot see (yet) how to map that to my MMU mechanism of 4k blocks that can be swapped and positioned anywhere…

Published in: on April 17, 2016 at 1:13 pm  Leave a Comment  

Retro Z80: The Z88dk

After spending time in the hardware I recently focused on the software. If I have to write all the software in assembler, I will be busy for a long time and debugging till the cows come home.

There is this development kit for the Z80 that allows you to write C and will produce Z80 machine code; its called the Z88dk.

This development kit has default support for all kinds of old retro machines – my personal favorite the ZX Spectrum. The Z88dk calls these targets. When you compile your C code you specify the target machine you wish to run the code on.

But because I am making my own hardware I also need to create my own target. And that is where it gets hairy. The structure of the Z88dk is pretty complex because of its flexibility to service so many targets. That means that each target has to implement interaction with the hardware for standard C-RunTime (CRT) IO functions (stdio.h), like stdin, stdout and stderr streams. Each target can also specify what C libraries are available to the programmer. A target could replace a default implementation for something specific, restrict access to certain functions and add additional libraries to be used. Then I am not even talking about the fact that there are two sets of these libraries and two different C compilers as well. It is a good thing that these are pretty transparent to the target author.

The Z88dk uses .m4 files to generate code for targets. The m4.exe is a (pre)processing program that interprets the text of an .m4 file, executes its macro’s and spits out the result in another file. The Z88dk uses this to setup the CRT startup code and to statically define the stdio streams.

I won’t go into the details here, but needless to say, it takes a while before I was able to wrap my head around how it all worked (and why). If you want to know more details, this page is the place to go. It describes in some detail the steps involved. Also check out the Z88dk forum for it contains several discussions on the subject of creating your own target (including mine).

It took me several days to get comfortable with Z88dk target setup and I have gotten to a point where a C-program compiles into a Z80 machine code binary file. However, when I let the program run, it (still) does not work when I use printf – that uses stdout – that is defined in my target. Simpler programs seem to run fine (not using my target custom code). So I still have some work in front of me.

 

 

Published in: on April 9, 2016 at 11:33 am  Leave a Comment  
Tags: ,

Retro Z80: Lessons learned IO

Well it took me a while but I finally have the System Controller picking up the Z80 Input and Output instructions.

There were several problems I have faced and a couple of them took me a while to get my head around.

This is the hardware logic that is in the SystemController to manage the Z80 Wait states. I have decided to always engage wait-states when the Z80 perorms IO. Any optimization that I may think of will come later.

So the hardware in the PSoC SystemController detects when IO is happening and triggers the wait-state (RS-FlipFlop) and the software interrupt (ISR_IO). The And-gate on the left just makes sure that only happens when this part of the hardware is enable. An Enable signal is necessary because the software may request the Z80 Bus (BUSREQ/BUSACK) which will automatically disable this logic so we don’t get any interrupts from DMA sequences the SystemController may be performing.

Z80-WaitStateLogic

The CPUWAIT output is connect to the Z80-Wait signal to stall its processing. The EnableDataOutput signal is connected to the output buffer of the Data Bus on the SystemController. So when the logic detects an Input instruction (IOREQ | RD), it automatically enables the outgoing direction of the Data Bus. The actual value is determined by software in the ISR_IO interrupt handler.

Activation of the Z80 IORQ line also clocks in a 1 so set the RS-FlipFlop that activiates the Z80 Wait line (active low – so inverted). The risaing edge of the (Z80) CPU Clock will latch the set value nicely in-sync with the Z80. This is because the SystemController supports a wide range of CPU clock speeds. The extra D-FlipFlop that Sets the value is used to also to be able to reset the value. One problem I had was that the Z80 needs time between us inactivating the Wait signal and the Z80 being done with the IOREQ. That caused the RS-FlipFlop to be set again causing the mechanism to hang.

The ISR handler grabs the address and the data of the bus (also hardware supported but not shown here) and determines what to do with the data of an Output instruction or which data to fetch for an Input instruction. When its done, it pulses the WaitCtrlReg (Bit0) that sends a pulse into the ClearWaitState pulse converter.The incoming pulse from the register is sampled at Bus speed (a built-in clock of the PSoC5)) but the outgoing pulse is synchronized to the CPU clock – again because we allow for a wide range of clock speeds.

When the pulse from the converter resets the RS-FlipFlop, the Wait line is deactivated and the Z80 finishes up the IO sequence. The reset also resets the D-FlipFlop that was triggered when the IO operation started, allowing it to be triggered again by the next IO operation. When the IO sequence ends and it was an Input instruction, the SystemController releases its hold over the Data bus (EnableDataBus goes low).

Now I have got the IO working between the Z80 and the SystemController I can finally focus more on starting to write the beginnings of the BIOS code. I plan to program the Z80 BIOS code in the Flash of the PSoC so it can initialize the system on startup. The user can always stop the Z80, perform DMA to download some other code and start that. This will not be the usual way to start programs however, they will be written against the BIOS.

Published in: on March 18, 2016 at 5:41 pm  Comments (1)  

Retro Z80: Input / Output

It has been a while but I am currently looking at a running Z80 with 64kb memory, running a small test loop-program. I have added some LEDs to the address and data buses to better see what is going on.

On the right is the System Controller (PSoC) with two USB links to the PC – one for programming the PSoC and one I use for communicating with the System Controller’s logic. In the middle, under that mess of wires is the Z80 (front) and a 64k RAM chip (back). Utmost left are the LEDs that show the address bus (front) and the data bus (back) as well as some other signals like, the clock, reset and halt (I use halt in test programs sometimes).

I have programmed the PSoC 5LP with a basic beginnings of a system terminal to interact with it from the PC. I currently have the following commands:

  • Memory-Write (mw)
    This allows you to send over a file that will be written to RAM. This is how I download a program or ‘program’ the Z80. The ‘mw’ command takes two parameters, first the start address in hex, second the number of bytes to expect and write (also in hex). I am planning to make the last parameter optional because it is a pain to have to specify that each time you want to write the entire program. The System Controller uses BUSREQ/BUSACK to gain access to the RAM chip.
  • Memory-Read (mr)
    This is the opposite of ‘mw’ and reads back what is in the RAM. Same two parameter also. The bytes are send back so you can display them in the terminal program that is running on the PC.
  • Clock-Mode (cm)
    This controls the clock that goes to the Z80. There are four modes: fast, slow, step and off. The fast clock runs in the MHz, the slow maxes out at 100Hz. The step mode is meant to manually pulse the clock with the ‘cp’ command (see below) and the off mode ensures no clock is sent out.
  • Clock-Divider (cd)
    The clock source (fast or slow) can be divided by 1, 2, 4 and 8. Gives you more to play with.
  • Clock-Pulse (cp)
    Pulses the clock line one time. It also sets the clock mode to step if that was not done yet. Unfortunately this does not work yet. I have not figured out how to reuse the same IO PSoC pin for a hardware connection and/or drive it in software. I also want to allow a mode that stops at every instruction (M1).
  • Reset (rst)
    This controls the reset line of the Z80. Issuing the ‘rst’ command without parameter will activate the reset for a fixed number (4) of clock-cycles. The Z80 needs a couple of cycles to properly reset. Using a numeric (decimal) parameter will use that value to wait with releasing the reset line. Using zero for the parameter, activates the reset line until another ‘rst’ command is issued. This is handy to halt (and reset) the Z80.

These command give me the basic tools to setup the Z80 (clock and reset), load the program in RAM (memory-write) and execute it (reset).

Input / Output

The next step is to allow the Z80 program to use input/output instructions to communicate with the outside world. I am currently trying to setup the System Controller to respond to IO address zero ($00) for debugging purposes. That will be the address the Z80 debug code will use to publish CPU register content etc.

The PSoC allows clocking in address (lsb) and data bus bits into status registers in hardware, which then can be read in an interrupt routing (on the PSoC) to further handle it and be ready for the next one. So the out-instruction will probably work pretty quick.

The in-instruction however, requires the System Controller (PSoC) to output data on the data bus when it has decoded the address. But that data has to be retrieved in software and will take relatively long to fetch. This will require the PSoC to activate the WAIT line on the Z80 to stall the CPU until the data has been fetched.

Z80 BIOS

In the meantime I am also writing a bit Z80 assembly for the bios that will be running on the Z80. Nothing major yet. I have a routine that fills a memory area with $FF, which translates to the RST38 instruction. The idea is that when code jumps to an uninitialized RAM address, or IM1 is used by accident – I am planning on only using IM2 – it issues the RST38 instruction and there code runs that saves the CPU registers in a dedicated RAM block and communicates this fault to the System Controller (this is where I need the out instruction). I have also written a transparent assembly routine to save the CPU registers in RAM without affecting any of the register values. This also saves the alternate registers as well as IX, IY the stack pointer and the program counter.

Hardware Build

I just started to build a perf-board CPU module to migrate from my bread board setup to something more solid and less error prone. But this is not ready to receive the CPU and RAM yet.

All in all I am pleased with the progress. Most time is spent programming the hard- and software in the System Controller. Once I have the IO sorted out I hope to write more Z80 BIOS code to make at least the PC serial link available to the Z80 programs.

 

 

Published in: on February 13, 2016 at 4:22 pm  Leave a Comment  

Retro Z80: System Controller

I am currently experimenting with an implementation of the System Controller. Before I dive any deeper lets talk about the global picture.

Basically I see three main parts in my Z80 computer v1. First there is the Z80 CPU of course, then there is the core memory and finally the System Controller.

The Z80 CPU runs programs from the core memory in a normal fashion – no surprises there. The core memory will be more than 64k in a future version but for now it is 64k. I use a single UM61512AK-15 chip. I have four of those to expand the memory when we add bank switching.

The System Controller is basically a black box that contains “all the stuff” to make it work. The following functionality would come to (my) mind for the System Controller:

Terminal Console

To allow communication between the Z80 computer and the outside world (a PC) a serial UART (USB) channel will be available. A number of commands can be implemented by the System Controller that would allow remote access to the functionality. It is also relatively easy to extend and expand.

Boot Loader

Because the Z80 computer does not have any ROM (or flash) I would need something (external to the CPU) that loads programs into the RAM of the computer. I say programs, but the minimum requirement would be some sort of BIOS that would further allow the user to load additional programs. I will be writing the Z80 code on the PC, compile it using a cross compiler and download it to the System Controller on the Z80 computer. The System Controller will then take control of the system bus and perform Direct Memory Access (DMA) to put the program into RAM. When its done, it releases the bus and resets the CPU, which will then boot up running the downloaded BIOS. This mechanism allows for a fairly rapid development cycle – at least it beats burning EPROMs!

Debugger

When the code is running on the Z80, the System Controller can help with diagnostics and debugging the program. The details are not fully thought out yet, but the general idea is that the System Controller will use its connection to the system bus to monitor program execution. It will also handle the info-points that I will probably implement. Info-points are similar to break-points but they do not halt the execution to wait for user input (continue), instead they halt the processor (with a HALT) instruction which is a signal to the System Controller to get ready to receive a CPU dump and issue a Non-Maskable Interrupt (NMI). The NMI will trigger code in the BIOS that dumps the CPU registers to the System Controller after it will continue execution. The System Controller will buffer and relay (store and forward) the dump over the serial terminal connection to the PC.

Other

Other things you can think of are managing memory bank switching, managing device interrupts, implementing glue logic in programmable hardware and providing other IO such as USB, SPI, I2C or even SD-Card or IDE interfaces. Also an adjustable CPU clock will be very handy for debugging purposes. For now the two described are the main focus.

Hardware

Initially I had planned to take an FPGA and program it with the System Controller logic. However, the functionality I had in mind for the System Controller would mean I need a considerable FPGA. Also, those (larger) chips are not cheap and will require a dev-board to get to know them. They also almost always run on 3.3V (not sure, but that’s all I’ve seen). Which means I would need a whole lot of level-shifters, at least for the FPGA inputs. The cheaper CPLD’s just do not have enough elements or blocks to be useful in this scenario.

I have come across the Cypress PSoC 5LP and the CY8CKIT-059 devlopment board which is pretty cheap (around $10,=). The PSoC 5 is a wonderful device that combines an ARM M3 with some programmable digital as well as analog logic. The Creator software (don’t be discouraged by the download popup, you can also download without using their download manager), which seems to be based on the Visual Studio Shell – although an older version) is pretty good. I would say the only down-side is that it only supports programming in C for the processor.

I am currently exploring the PSoC5 and the Creator software and its capabilities. One thing is clear, the PSoC5 does not have enough IO to allow everything to be implemented into the chip. I may consider implementing the Memory Controller (manages bank switching) as discrete logic components. I figure I only need a couple of latches and a fast RAM for storing the bank registers (but more details on that later). The USB is on the PSoC5 dev-board and already connected to dedicated pins on the chip. So that I will keep. But exposing extra IO peripherals (SPI, I2C, UART etc) will probably need a separate chip (another PSoc5?).

For now everything is on a breadboard running at fairly low speeds.

Software

I currently have the serial terminal UART connection working and am working on the software. Having to program in C takes some getting used to after having thought in objects for more than 20 years (yes, I am that old).

The SystemTerminal component takes care of communicating with the PC and controlling the UART. Internally it uses an extra send and receive buffer for longer messages. It also implements a CommandDispatcher that tests incoming text commands and dispatches them to the appropriate CommandHandler. A CommandHandler will interpret the buffer and continue the communication protocol with the PC until its is finished. For now I only have one command implemented and that is the command to boot-load a program into memory.

The MemoryController component manages accessing the external (to the PSoC) memory over the system bus. It uses the BusController to gain access to the system bus and then implements an interface for simple data transfers between the external memory and the PSoC.

The idea is to be able to download a program in a little while and be able to run my first program on the Z80.

Back to work.

Published in: on January 14, 2016 at 6:27 pm  Leave a Comment  

Retro Z80: CPU Test Circuit

There is this simple test circuit that allows you to test your Z80 CPU in a very basic way. There are several incarnations and I have found several YouTube videos, but I will link in the Z80.info site.

So I took out the bread board and set to work and got the NE555 running with a nice enough clock pulse. I put in a pot to be able to vary the clock frequency a bit. Then I plugged in the Z80 and connected all the wires and resistors. I used a segmented LED bar for visualizing the address lines, but initially only connected 3 address lines.

Then I turned it on and no lights… Doh! I always forget that the outer power tracks are divided in two on this bread board. So after placing a jumper wire, it worked.

I thought it would be cool to use all the LEDs in the LED bar so I connected the other address lines (A0-A9) too.

That worked nice and you could see the binary counting of the address lines… But wait, why is A7/LED 8 and higher blinking!?

Here is a YouTube video I made of my test circuit where you can see in action.

First it starts slow, then there’s a short segment that’s really  fast, then at the end, you can see the blinking.

So why do you think that is?

Remember that the Z80 also does DRAM refresh. For the NOPs we’re executing 2 clock cycles are spent using the address bus to access memory, and the last two cycles are used to refresh the DRAM. During the refresh the lower 7 bits of the address bus (A0-A6) is set using the contents of the R register. The other address lines are reset to zero (this is actually the I register and the I register is zeroed out on reset). The R register is incremented at every instruction (M1 cycle, the first 2 clock cycles) and so, in this case, are synchronous to the counting of the address lines.

The blinking red led on the left side on the video is on when the REFRESH line is active (low). It is perhaps a bit hard to see on the video, but if you slow it down, you can see the upper address lines (A7-A9) are on when REFRESH is off and visa versa.

You don’t realize that immediately (at least I didn’t) when you see the circuit and the video’s other people made. They usually have only 3 LEDs on there.

Learned something today.

 

Published in: on December 22, 2015 at 3:51 pm  Leave a Comment  

Retro Z80: Debug facilities

When building a computer from scratch you are bound to run into bugs and problems. Also for software development (the bios for instance) you will want some way to debug your code. There are no fancy integrated development environments for the Z80 (as far as I know) – so this will be roughing it.

So what can we do to make life a little easier?

Hardware

There are hardware related facilities as well as software related facilities. Lets look at the things we can do to or for the hardware to make debugging easier.

Clock Speed

We have a 20MHz Z80 CPU which at full speed goes pretty fast. It would be difficult to see anything actually happen. The use of a logic analyzer may help of course but only if the sample rate is fast enough.

It sometimes helps to slow things down to a crawl and actually see the state changes in front of your eyes – then usually it clicks in your brain what the problem is.

So having a clock that can be very slow or even hand-operated pulsing clocks with a button is very handy. Also a medium speed clock will come in useful to flesh out any hardware timing related issues the design may have. And of course a full speed clock at the intended CPU operating speed.

If you really want to make it spiffy, then a single shot instruction clock would be nice. This clock would monitor the M1 line of the Z80 that indicates the start cycle of a new instruction. The Z80 instruction usually take multiple clock cycles to fully complete – or even multiple machine (M1) cycles. I haven’t seen any circuit diagrams for this anywhere, but I’m sure we can think of something.

Bus Spy

Spying the signals on the bus of the computer can be a good way to understand what is going on. This can be as simple as attaching LEDs to each bus signal (buffered as not to load the bus too much) and running at low clock speeds or single step clock mode.

At the other end of the spectrum is the option of having an intelligent bus spy that can decode the address and instructions etc. I am sure something like this exists (I am not the first to build a computer), I just haven’t looked for it. Do you know any good/cheap ones? Please leave a link in the comments.

General Purpose Visualization

We could attach a module to the system bus that visualizes data – the simplest being several LEDs. The software could latch data into the registers of this module to turn LEDs on and off – visualizing the data.

Other visualizations could include a 7-segment display or even a LCD display (HD44780).

Test Routines

We could write some software routines the exercise specific parts of the hardware in some way. Think of these as test scripts. The simplest one is a thorough memory test routine that checks if all memory addresses can be written to and read form with the correct value. Main interest here are the memory boundaries but a full sweep of all memory addresses may even catch more types of faults.

Debugging

As the software is being developed, it will become necessary to have some form of debug support. I assume that the Z80 system is connected to a PC over a dedicated serial debug port and that the PC runs software that interacts with our on-board debug facility.

This also implies that we have a separate entity on the system that controls all this without involving the CPU – lets call it a system controller. For now the idea is to put an MCU with an external bus interface on the system to provide the debug services as well as provide I/O (SPI, I2C, UART etc). At a later stage this may be swapped out for an FPGA. But because I am an FPGA beginner – learning along the way- I’d thought we’d start (relatively) simple.

Catch stray execution

It probably would be beneficial to catch bad jumps due to programming errors or bad data in a vector address table. The Z80 could fly off into nothingness, never to be seen again. So I have been thinking if you could catch something like that. I have come up with a simple trick that just might be enough.

At startup, initialize all memory locations with the value for one of the RST instructions (0xFF for RST38). Then load the program at the specific address and jump to it. Now if the execution flow spins out of control it may jump to a memory location that has the RST instruction written and the (debug) handler there, can even retrieve the jump address from the stack and report the problem over the debug serial link.

Further stack analysis may reveal more info on the execution flow previous to the fault. That analysis can be done by the software on the PC.

Breakpoints

I though it would be awesome to support debug breakpoints for the Z80. That would certainly help with software development. But how?

There is one instruction on the Z80 that came to my mind immediately: HALT. This makes the CPU execute NOPs and signals the !HALT line on the CPU. This is excellent for hooking in a breakpoint mechanism.

The HALT instruction is only one byte and therefor easy to fit in between existing code. The breakpoint have to be compiled in (for now) so you cannot set them dynamically. Allowing to do so would require in-memory modifying the code, which brings all kinds of nasty (re)addressing issues to the -already full- table.

A Non-Maskable Interrupt is issued to start the program again and continue where we left off.

One thing we need to watch out for is to disable and interrupts on a hardware level when the HALT signal is active. This is because any kind of interrupt will exit the HALT condition.

Wrap up

These are just some ideas I came up with and not all of them will probably work right of the bat. It is a start anyway and perhaps even inspire the reader.

 

Published in: on December 21, 2015 at 9:53 am  Leave a Comment  

Retro Z80: Deciding on Memory design

I am trying to decide how to organize the memory layout for the Z80 retro computer project I am doing. The Z80 only has 16 address lines which allows it to access 64k of linear address space. If you want more memory than that – and we do – you have to organize the memory in parallel banks and switch between them, also know as (memory) bank switching. There are many flavors of how to design bank switching and there are many factors that would favor one over the other. Lets look at some of them.

I write this mainly for myself because I find that writing things down forces you to organize your thoughts and reveals weak spots in your thinking.

Bank Switching

With bank switching you add multiple banks of memory (in parallel) for a specific address space and determine through a latch which one of the banks is active. There can be only one active bank at a time for one address space. It is good to realize that these memory banks do not have to reside in separate physical chips. So say you have a 128k RAM chip, that would allow you to implement 2 banks of 64k by controlling the A16 line of the chip. If you do have separate chips, you usually control them with the Chip-Enable (CE) line to make sure the data lines are in a high impedance state and no data is written into the chip when the chip is not active.

So bank switching involves some address decoding to control what (part of the) chip(s) is/are active when others are not.

Video Memory

Another aspect of managing memory is accessing video memory. Video memory is a dedicated memory address space (or bank) that will contain the graphics to display on the (VGA) screen. Dependent on the screen resolution and color depth a specific amount of memory is required. This adds up quickly.

Contention will occur between the processor wanting to write into the video memory to update the display and the VGA controller that continuously need to read the video memory to output it to the monitor. Usually the VGA controller takes precedence over the CPU access to avoid glitches in the image on the display.

This video memory contention complicates matters further and need to be thought through carefully. I see several solutions to this problem.

Dual-Ported Memory

The first obvious solution is to have dual-ported memory. A port is a complete address (for the RAM size of the chip) and data bus. This is a special type of memory that has two ports that can access the memory simultaneously. It has built-in logic to lock out access to the same memory cell by the two ports (semaphore). The granularity is at cell level so there is a high level op parallelism. This is a very easy solution to the problem with the only downside is that this type of memory is not cheap.

There is also two-ported memory that is also suitable. Two-ported memory has one dedicated read port and one dedicated write port. Dual-ported memory has at least one read/write port. Also note that FPGAs often have internal dual ported memory blocks.

Segmented Memory

Another solution would be to use separate physical RAM chips. This would allow simultaneous access to different chips for the two ports (CPU and VGA controller). These ports would require extra logic IC’s like latches. The smaller the size of the memory chips the more of them you need but also the more parallel the two processes can access the video memory. The granularity is determined by the chip memory size and has a low(er) level of parallelism. Because of this you still would need something to halt the CPU when a collision is eminent – there is still a chance that the CPU and VGA controller want to both access the same chip.

Double Buffering

There is also the option to use double buffered video memory. This is a technique that toggles two parallel video memory spaces (banks) between the CPU and the VGA controller. At any time the CPU has access its own video memory bank and the VGA controller has access to the other bank. When the CPU is done writing to the video memory it indicates the at the next appropriate moment the banks can be switched (swapped) presenting the VGA controller with the new video information.

One problem that occurs here is that the CPU’s video memory now contains an old snapshot of the video data. That means if the CPU partially updates the video memory – for instance only updates (bit-blit) one small area on the screen, the rest of the scene is old data from the previous display image. I do not have any ideas how to solve this yet – so leave a comment if you do.

Graphics Processor

An alternate way of doing video is to use a separate graphics processor. This relieves the CPU from the task of creating the graphics. It also opens the door to a different hardware solution where communication with the graphics processor is done through I/O and not shared memory. So there is no contention.

The program would communicate in drawing primitives to the graphics processor which would reduce the amount of data to be exchanged quite a bit.

I need to delve into sprites and this type of graphics processing in general a bit more before making any final decisions.

Software Implications

Getting the hardware to correctly bank switch memory blocks is relatively easy although there are a lot of flavors in varying levels of difficulty. Another question is how is the software going to deal with memory spaces ‘suddenly’ appearing and disappearing in the program? I could not find very much on this on the interweb – so again, if you know any resources, please leave them in a comment.

One obvious implication is the stack pointer (SP). What happens to the stack when you swap out the memory block it is currently located in? Or if you want to jump or call code that is loaded in another memory block? And how do you get back to the calling address (RET)?

This all requires very precise orchestration in the software to not crash the system. It stands to reason to facilitate managing memory block switches by some sort of built-in service routine that knows the details of how to manage this for the program that is running. Using a RST instruction is a very common way to provide service routines that perform this type of important and frequently used operations.

Note that these RST instructions require the lower memory page (high address byte is $00) to be initialized with some sensible code. That means that this lower area of all memory banks in this address space, is off-limits to a any program and needs to be initialized by a boot mechanism. For now I assume that this is all RAM.

Note that other addresses may need to be included in this safe area, such as the NMI Service Routine ($0066) or perhaps an ISR vector table for IM2 (depends on the value of the I register).

Idea: A system controller can perform the boot sequence and initialize all the RAM banks that overlap with this address space. It could then set a bit (latch) to disable writing to this part of the memory (of any bank) at a hardware level. This way that crucial address space cannot be corrupted by buggy programs.

All RST instructions push a return address onto the stack. This can be used to resume the program where the call was made. It also allows the RST functionality to intercept and redirect execution flow as well as retrieve extra parameters from the call address etc.

When execution is transferred from one memory bank to another, there always has to be a ‘common ground’ to make the switch from. It stands to reason to leave the lower memory space static to provide this basis.

Stack Pointer

The stack pointer and the contents of the stack itself are tied to the code that is currently executing, including all the calls and push instructions that may have been performed up until this moment.

So it seems logical to locate the stack in the same memory page as the code is executing.

Jumps and Calls

Jumps and calls are always within the 64k address space. This is not to complicate the instruction set. A separate way of calling out-of-context code is provided with RST service routine. A small structure can be supplied by the caller to indicate what code to execute on what memory page.

We could reuse the notion of a vector table (by index) we use for interrupts and will probably use for public BIOS functions.

Interruspts

The I register is initialized to 1 so a protected version of the ISR addresses is at the bottom of the memory (base page or all banks). A program (or OS) can override by setting I to another value but has to do all management – including bank switches.

BIOS ROM

BIOS contains the bare essentials for interfacing with the hardware (I/O). This includes bank switching but also keyboard, mouse, display, printer, network and storage (disk) access. It does also require a bit of RAM to maintain several structures for its services.

These service routines can be used by any program to help with memory bank/page switching.

Loading Programs

The size of the memory blocks also influences how programs are loaded. What if programs are larger than a memory block. What if they are larger than 64k?

We may have to design a program file layout that is relocatable and publishes entry and exit points. This clearly needs some more thoughts.

Memory Usage

Memory is typically used for several different types of data. One is programs. Another type is data. The system itself requires a reserved region of memory.

These different types of memory usage could be taken into account by the memory manager to enable/disable certain services for those blocks (performance optimization).

Multi-Threading

An OS could use memory banks to execute parallel tasks and use the memory manager to direct the programs to specific parts of the memory space and memory blocks.

Each process (program) can get its own base page. Not sure if that helps or just makes things worse. We’ll have to see.

 

Published in: on December 14, 2015 at 7:44 am  Leave a Comment