Z80 Project

Nathan's Z80 Project Mark 2: Serial bug killer

Last updated: Jan. 10, 2010, 9:50 p.m.

I left my Z80 project behind when I went to see family over Christmas, I did do a tiny bit of coding while I was on holiday but not much. Back in snowy Bath this weekend I got some more done on the project. I've spent a lot of time on the documentation. The main reason for this is the fact that I keep doing bits then not looking at it for a couple of weeks and I forget where I was and what changes I'd made. I'm also hoping to use a lot of the circuit designs in the Mark 3 project at some point in the future and if they're out of date and un-documented it could result in some expensive and useless PCBs.

Actual real progress: Update BIOS image via debug port. A python library to make comms with the PIC easier. UART fitted and debugged. Some additional commands in the PIC firmware including the ability to perform a system reboot from the host PC.

After some head-scratching I managed to get the BIOS update via the serial port working. I basically copied the code examples from the PIC data sheet and added some pointers and flags to synchronise it with my code. The BIOS update routine takes a 128 byte data block and a start address (must be an integer multiple of 128, but it checks this and gives an error if you got it wrong) and erases then programs that block of Flash memory. This gets confusing because the erase only works on blocks of 64 bytes, write only works on blocks of 32 bytes and the packets are 128 byte long. A couple of flags are used to keep track of all that in the code and it seems to work. A significant advantage to doing this block-wise update to the BIOS image is that if the BIOS is quite short (as it is now) there is no need to write the whole 8K so the download can be very fast. I wrote a python program with the new seriallib to download the image so it's as simple as running a script and giving it a pointer to the latest compiled image.

The serial protocol I've developed isn't particularly complicated, but hand coding the checksum every time or writing the same chunks of byte counting serial code repeatedly quickly becomes very dull. I wrote a simple library that handles most of the communications protocol simply. There are a lot of constants in the file and these have all been added to the documentation at the right places. The rest of the library is just a pair of classes. One is the packet class which contains code to generate length and checksum fields on the fly and can return a byte string suitable for transmitting or a human readable hex code string with the str() and repr() functions as with most python classes. The other class is a wrapper around the serial port which deals with receiving packets and returns a packet class object with the data once a whole response has been received. The seriallib file is in the PC software section of the downloads page.

I've now fitted the UART chip (a 6402) and got that working. There were a couple of problems I had with this. The chip generally seems to use an odd mix of active-high and active-low signals, load and unload lines for reading and writing data are active-low as is the status register enable line. However the reset line is active high. I hadn't noticed this initially and tied it into the system reset, I've also found that the reset on the UART is needed to clear transmission errors when receiving data, this means it needs to be accessible to the CPU if any serious software is to use it. I've solved the problem by taking the MSb of the DEBUG port and wiring the reset to that. This means that the Z80 can no longer drive the debug port with any 8 bit value, but does give complete software control of the reset line. Once this code was implemented the UART behaved mainly as expected. The remaining bug was particularly odd, the Baud rate was exactly half what I'd calculated it should be. I spent a fair amount of time yesterday making sure I'd documented the UART jumper settings on the board right and was sure that it was all correct. In the end I guessed I'd read the datasheet for the chip wrong and went looking online. The first datasheet I found for the 74HC4060 that I've used as a clock source for the UART was a Fairchild semiconductor document and sure enough the pins were swapped. However I noticed that they weren't just a bit different they were completely re-arranged. I thought this was odd and checked the 74HC4040 a similar chip to make sure I hadn't mixed them up when drawing the circuit diagram, but that wasn't right either. In the end I found a Philips/NXP datasheet for the 74HC4060 that exactly matched the one I'd drawn in the schematic, however the chip I was using was a Fairchild one.

To summarise a little: The Fairchild Semiconductor MM74HC4060 is not pin compatible with a Philips/NXP 74HC4060 (at least, the pins have the same general input/output direction and tolerances, but the clock divisions on them are different). This utterly astounds me, I had until now assumed that a 74xx part was the same pinout and functionality regardless of who makes it.

Once I'd realised what was going on and updated the circuit diagrams and moved the jumpers around suddenly it all worked! The uart_write function in the BIOS is now complete and functional and puts a value from the Z80 register D out to the UART.

The other main thing I've got working are some additional functions in the PIC's debug library, these are all part of a new "Do" command. Basically it takes a single byte and calls one of 256 functions which can only provide an acknowledgement and take no data. So far I've defined 4 of these functions: do-get-reset which claims the reset line, putting the Z80 into idle until further notice; do-get-dma which does the same but leaves the Z80's internal registers and program counter in tact so it can carry on when released; do-get-slave which releases the reset or DMA lines and lets the Z80 run again. These can be used in conjunction with things like memory read and write to group a number of operations without letting the Z80 change anything between commands. The final command is the do-reset command which does a soft-reset in the PIC. This in turn will cause the whole system to reset because as soon as the PIC ceases driving the Z80 reset line it gets pulled low resetting the CPU. This function caused me some confusion as well, it was sending only the first two bytes of the acknowledgement unless I added a huge delay after the send packet command. In the end I realised that this was the hardware UART in the PIC not finishing transmitting before the software reset was issued. I've added a test to see if the transmit shift register is empty before issuing the reset command now.

Z80 Project
Z80 Mark 2,
Z80 homebrew,


Posting comments is not currently possible. If you want to discuss this article you can reach me on twitter or via email.


Email: nathan@nathandumont.com

Twitter: @hairymnstr