Wednesday, 6 November 2013

Pull-up and Pull-down Resistors

Pull-up and Pull-down

It's a boring subject to some, but resistors are important for the correct operation of electronic circuits. According to Wikipedia pull-up (and pull-down) resistors are used to ensure that inputs are at expected levels when external devices are disconnected. Pull-up resistors are connected to +ve voltage to drive the line high and pull-down resistors are connected to ground to drive it low. Without a pull-up or pull down resistor the output could be set to an undesired state or jump between 0 and 1. When the input is connected to another component it can still be driven to the desired state, overriding the default pull-up or pull-down state.

Floating Inputs

The Raspberry Pi GPIO pins are all initialized as inputs when power is applied even though later on the boot code and drivers will change the pin functions and whether they are inputs or outputs. One of the issues with my first prototype board was that the floppy drive motor would spin up when power was turned on to the system. After some investigation I realized that this resulted from a floating input because the boot state connected the GPIO input to the 74LS06 input with no other source to set the inputs low or high. The solution is to add pull-up or pull-down resistors to set the desired power-on state.

I'd like to thank my brother, Noel, for this diagram showing suggested values for pull-up and pull-down resistors based on the data sheet for the 76LS06 IC, I could not have designed the interface electronics without his advice. Based on his suggestions I will be using 2.2k resistors for pull-down and 10k resistors for pull-up (to 3.3v).

The motor, drive select and step signals must be pulled to ground to prevent them becoming active on power up. Other signals such as step direction and side select can be pulled in either direction, but pulling them high with 10k resistors consumes less power and is the preferred solution. The write gate signal is connected to GPIO 2 (SDA) which already has a 1.8K pull-up resistor on the Pi circuit board so it can be left as-is.

Open Collector Pull-Up

Pull up resistors are also used for connecting the GPIO inputs to the output of open collector devices such as the 74LS06 device I am using. In this case the pull up resistors are connected to 3.3v, not 5v to ensure that the Raspberry Pi GPIO pins are not driven higher than 3.3v, which could damage them. In this configuration a lower value resistor improves the switching speed of the signal. This is important when reading floppy disk data at high clock rates (4-8 MHz) but less important for long duration signals such as the index pulse. Again, based on advice from my brother I have chosen 330 ohm for the floppy disk data and 10k for other signals. The track zero signal is connected to GPIO pin 3 (SCL) which already has a 1.8K pull up resistor on the Pi circuit board. I could also have used the internal GPIO pull-up resistors but they are not connected at power on.




Tuesday, 29 October 2013

Schematic and Layout (updated)

Schematic V2
I've updated the original post with a better schematic that supports (in theory) write operation and side select for Model III computers. The improved layout is suitable for building by hobbyists as it is single-sided and the traces have been widened and corners smoothed for improved etching according to advice I received. The large open area in the bottom right is designed to be cut out to clear the audio and video connectors. I hope to build a few of these using single-sided PCB prototype boards. The program that I am using is KiCad EDA Software Suite and project files for the design are available on request or I can export Gerber files.While I believe the schematic above is correct I offer no warranty for anyone who chooses to use this design. Though if you have the tools and experience to build your own board you probably are capable of checking and even improving on my layout.


Wider traces and smooth corners for better etching. Large pads for easier drilling.

CONN_2 is a jumper wire to avoid needing a double-sided board as I could not route that line directly. P1 is a right-angle female 34-pin cable connector. The outline in the picture is misleading as the ribbon cable plugs into the left side with the cable exiting up and to the left. P2 is a 26 pin female pin header which is fitted underneath the board and plugs into the male header on the Raspberry Pi. The finished board is approximately 2-1/2 x 2-12 inches square.

I have successfully read some copy-protected game disks using the software and I believe that with further tweaking I should be able to read most copy-protected disks.



Monday, 21 October 2013

Using the Raspberry Pi to Read Floppy Disks

Reversing the Process to Read TRS-80 Floppy Disks

The Catweasel is an add-on card for the PC which allows reading TRS-80 floppy disks. It works by emulating a floppy disk controller using a combination of hardware and software. At the very start of the current project I believed that similar functionality would be possible by reversing the input and output signals and connecting a TRS-80 floppy drive to the interface board. Just recently I have succeeded in doing this

Interface Card
This picture of the device shows the interface board on the left and the floppy drive connector on the right. The interface board plugs into the top of the Raspberry Pi and the floppy card edge connector plugs into the TRS-80 floppy disk drive. The logic analyzer plug is for testing and development only and is not required for operation.


Raspberry Pi
The Raspberry Pi is pictured to the left. The Pi contains connectors for a keyboard, mouse, monitor and network. The operating system on the SD card is Raspbian which is based on Debian Linux.


The interface board sits on top of the Pi during use. It is about the same size as the Raspberry Pi. No PC or Mac is required as this is a complete standalone computer system.


Complete Device

The complete device is shown to the left plugged into the Raspberry Pi and with the floppy cable plugged into an old TRS-80 floppy disk drive.

Technical Details

I used the same pins as in the original device but rewired so inputs become outputs and vice versa with one exception. The original card connected the Raspberry Pi SPI output pin to the expansion interface floppy data input line. This project needs to connect the Raspberry Pi SPI input pin (GPIO 9 - MISO) to floppy disk cable pin 30 to read floppy disk data into the Pi. To write data to the floppy disk you could connect SPI output pin (GPIO 10 - MOSI) to floppy disk cable 22 (WRITE GATE signal would also have to be connected).

I found during testing that enabling the internal pull-up resistor for GPIO 9 had no effect when SPI mode was enabled. An external 10k pull-up resistor solved the problem. Another minor issue is that, using the current pin connections, the floppy disk drive motor spins and the head is loaded during the Raspberry Pi boot since those pins seem to be output high during the boot process. I suspect that selecting different pins will solve that problem. In the meantime I use the wiringPi gpio utility during boot to set the desired pin configuration and I/O direction. (Note: solved with the use of pull-down resistors.)

Software

The initial version was written under Raspbian for the Pi and used both the WiringPi and the bcm2835 low level library. I had hoped to using WiringPi exclusively but the current state of the driver is insufficient for my needs. Reading from the SPI using WiringPi leaves gaps in the data and the methods to wait for edge detect are very slow and end up returning far too late to be useful. However, a lot of work is being done by various people to implement DMA for SPI in Raspbian and I hope eventually to make use of this work when it shows up in the standard releases.

The software initializes the Pi GPIO pins and SPI interface then sets the correct output values to step the read/write head to the desired track. Once at the desired track it starts a high-priority thread which waits for the index signal and then reads an entire track of data in a single pass. It then processes the data and writes it to the DMK file format before stepping to the next track. A single-density 35 track floppy disk can be read in under 20 seconds using the initial version of the software.

The software itself is essentially the Tim Mann cw2dmk utility rewritten to handle the input data after it has been tweaked to generate clock and data bits. In the absence of DMA support I use a data rate of approximately 3 MHZ which is sufficient for single density but would not handle double density.

Signal Details

This picture shows the raw data coming from the drive at the top and the SPI clock at the bottom. As you can see, the SPI clock is not synchronized with the data coming from the drive. This is to be expected since there is no mechanism to cause synchronization and the SPI clock divider is not granular enough to allow a perfect match. In any case, it doesn't cause a problem. The main concern is to use a SPI clock rate fast enough to avoid missing clock or data pulses that extend into the 'dead zone' between SPI bytes. The SPI interface will not see pulses that fall inside the gap between bytes so I increase the clock rate to a little over 3 MHZ and I find that a single clock or data bit from the disk drive is received as 2-3 bits through the SPI interface. Worst case, if a clock or data bit exactly straddles the gap between bytes I would expect to see a single bit set in the output. The software counts the number of zero bits between the ones and, if the number of zero bits is greater than 16 it counts it as a zero, otherwise it counts it as a one.

Because I am polling for the index signal in software there is a 10 microsecond gap between the start of the index signal and the start of data. This represents less than three clock/data pairs so is unlikely to cause problems.

I have successfully read actual floppy boot disks into the Pi and used them in the TRS-80 emulator from www.trs-80emulators.com. While I was able to read some copy-protected disks into the Pi, not all of them worked correctly in the emulator. I believe this is because the cw2dmk utility was designed to make copies of unprotected disks and, by default, ignores misplaced address marks and other deliberate errors on copy protected disks.

I have not tried to write back to a floppy disk but it should be easy to do so.




Sunday, 13 October 2013

Building a Board

Building a Board

The initial prototype was hand-wired on a piece of prototyping board. The next version was built on a Adafruit Prototyping Pi Plate and wired up with jumpers. I chose jumpers because I wanted the flexibility to use the same board as a floppy drive controller like the Catweasel project eventually. I will describe the Adafruit board as it's likely to be easier to build than the hand-wired version.

Step 1

Drive Cable Connector
Remove (or if you buy as a kit, don't install) the four pin connector at the end of the board beside the adafruit logo. and install a 34-pin male connector. e.g. Digikey OR903-ND or equivalent. The original TRS-80 had card edge connectors on the drive cable but a pin edge connector is much more convenient for connecting to the Adafruit plate. The slot at the is used to orient the ribbon cable, but since there is only space for a ribbon exiting upwards it is almost impossible to plug in the ribbon cable incorrectly.




I chose to solder the connector to the board. I don't think there would be room underneath the plate to wire wrap this connector. I put a single row female connector beside the header as I wanted to use jumpers. You may want to hard-wire the board if you want a permanent solution.

Cable Connector Soldering


The row of pins nearest the end of the plate contains only ground signals so every pin on that row is connected together and then attached to the nearby ground pin. The other row is connected to the female header on my board but you could choose to solder it directly to the 74LS06 pins instead.







Step 2

The project only requires two 74LS06 ICs and fortunately there is just enough room for two 14-pin IC sockets on the plate. I have put pin 1 of the IC sockets at the end of the board furthest away from the cable socket. The sockets must be positioned so that the pins on the left and right go into the rows of interconnected holes on the right and left sides of the prototype area. As there is a bit more clearance underneath it might be possible to use wire-wrap sockets, but check first before trying this. I chose to solder the sockets to the plate and mount some female headers beside the sockets.

Back of Board
This picture of the back of the board shows the sockets and pin headers soldered in as well as some of the ground and +5 volt power supply lines. (One of the power supply lines is on the other side of the board.) I put two female pin headers on each side of the ICs to allow connecting pull-up resistors and the logic analyzer.






74LS06
The 74LS06 has ground on pin 7 and 5 volts on pin 14. These need to be connected to the ground and +5 volt supply on the Adafruit board. Each 74LS06 has 6 gates which input on 'A' and output on 'B'. The gates invert the input signals but the software is already written with this in mind. Every signal from the TRS-80 to the Pi is passed through a gate on the 74LS06. This protects the Pi from the 5 volt TTL signal as we use the internal Pi pull-up resistors to provide a suitable input voltage and the 74LS06 is also able to generate a suitable TTL output from the Pi GPIO output signals.

Pull-up Resistors

Pull-up Resistors

As mentioned in a previous post, the TRS-80 Expansion Interface has 150 ohm pull-up resistors on the Read Data, Write Protect, Index Pulse and Track Zero lines. All the other lines must be terminated on the floppy drive or, in this case, but the emulator board. Only one set of termination resistors should be attached to external devices. I suggest you put a jumper between the termination resistors on the Adafruit board and the +5 volt connector so you can enable/disable termination. The picture shows a suitable location for pull-up resistors between the cable connector and the header which connects to the Pi. I use jumper wires to connect each termination resistor but you may choose to hard-wire them and add a single jumper to +5 volts to enable/disable them all.

Schematic?

There is no schematic yet, but a dedicated hobbyist should be able to wire it up from the information provided in these blog posts. Every signal coming from the TRS-80 Expansion Interface goes to an input (labelled 'A') on one of the 74LS06 ICs. Each input should also be connected to a 150 ohm pull-up resistor unless you have an actual floppy drive connected to the cable which is already terminated. The other side of the gate is connected to the corresponding GPIO pin.

Each of the four outputs (Read Data, Write Protect, Index Pulse and Track Zero) to the TRS-80 is connected to an output (labelled 'B') on one of the 74LS06 ICs. The other side of the gate (labelled 'A') for the outputs is connected to the corresponding GPIO pin which is configured as an input. The Expansion Interface already has pull-up resistors for those lines so no additional pull-up is required.

Programming the Pi GPIO and SPI

GPIO Software

The original software ran under RISC OS. I chose this OS because it is single-user with cooperative multitasking and because I am not an experienced Linux programmer. The cooperative multitasking would hopefully avoid having the program interrupted during time-sensitive operations. The single-user environment allows software very free access to hardware and memory with few security constraints. I have since ported it to Rasbpian as Linux has better documentation and more people working on the kernel.

I used the bcm2835 C library for GPIO for the project. I modified the original library, which was written for Linux, to work under RISC OS. It's a very simple, bare-bones library that directly accesses the various hardware registers using Memory-mapped I/O. Since it runs under both Linux and RISCO OS (with my modifications) this made porting to Linux easier.

When I ported to Linux I found that simple edge detection did not work. I'm fairly sure that's because edge detect interrupts are now supported on the newer kernels and the interrupt service routine is clearing the edge detect bit before my code can detect it. Therefore I switched to using the /sys/class/gpio sysfs interface as described here (see paragraph 'Sysfs interface for Userspace) for the step signal.

The program starts by initializing the library with the call bcm2835_init(). This function memory-maps the registers into shared memory so that the library can access them directly. Under Linux this calls mmap. Under RISC OS this calls OS_Memory 13 to map in the same memory regions as under Linux.

To accommodate any differences in the GPIO pins selected for various function I define which pins are assigned to which function in a header "pin.h" shown below. The definitions are based on bcm2835.h. Since I have a Model B, revision 2.0 Raspberry Pi I use the revision 2.0 definitions.

// input_pins
#define DS0_IN   RPI_V2_GPIO_P1_03 // GPIO 2 connects (through 74LS06) to cable 10
#define DS1_IN   RPI_V2_GPIO_P1_22 // GPIO 25 connects (through 74LS06) to cable 12
#define DS2_IN   RPI_V2_GPIO_P1_15 // GPIO 22 connects (through 74LS06) to cable 14
#define MOTOR_ON RPI_V2_GPIO_P1_07 // GPIO 4 connects (through 74LS06) to cable 16
#define DIR_SEL  RPI_V2_GPIO_P1_11 // GPIO 17 connects (through 74LS06) to cable 18
#define DIR_STEP RPI_V2_GPIO_P1_13 // GPIO 27 connects (through 74LS06) to cable 20
#define WRITE_GATE  RPI_V2_GPIO_P1_10 // not currently implemented
#define WRITE_DATA  RPI_V2_GPIO_P1_21 // not currently implemented

// output pins

#define TRACK_0  RPI_V2_GPIO_P1_16 // GPIO 23 connects (through 74LS06) to cable 26
#define WRITE_PROTECT RPI_V2_GPIO_P1_12 // GPIO 18 connects (through 74LS06) to cable 28
#define READ_DATA     RPI_V2_GPIO_P1_19 // GPIO 10 connects (through 74LS06) to cable 30
#define INDEX_PULSE  RPI_V2_GPIO_P1_18 // GPIO 24 connects (through 74LS06) to cable 8

I also define a few other macros to make typing faster. They are shown below.

#define GPIO_IN   BCM2835_GPIO_FSEL_INPT
#define GPIO_OUT  BCM2835_GPIO_FSEL_OUTP
#define PULL_UP   BCM2835_GPIO_PUD_UP

The next step after initialization is to configure the various GPIO pins as inputs and outputs according to which floppy disk signal is being handled. The first group of library calls set the input signals as GPIO inputs and enable the pull-up resistors for all the inputs. You must enable pull-up resistors for all inputs because the ICs are open collector.

    bcm2835_gpio_fsel(DS0_IN,GPIO_IN);
    bcm2835_gpio_set_pud(DS0_IN,PULL_UP);
    bcm2835_gpio_fsel(DS1_IN,GPIO_IN);
    bcm2835_gpio_set_pud(DS1_IN,PULL_UP);
    bcm2835_gpio_fsel(DS2_IN,GPIO_IN);
    bcm2835_gpio_set_pud(DS2_IN,PULL_UP);
    bcm2835_gpio_fsel(MOTOR_ON,GPIO_IN);
    bcm2835_gpio_set_pud(MOTOR_ON,PULL_UP);
    bcm2835_gpio_fsel(DIR_SEL,GPIO_IN);
    bcm2835_gpio_set_pud(DIR_SEL,PULL_UP);
    bcm2835_gpio_fsel(DIR_STEP,GPIO_IN);
    bcm2835_gpio_set_pud(DIR_STEP,PULL_UP);

Though they are not used currently, we can also enable the floppy disk Write Gate and Write data signals if they are connected to the board.

    bcm2835_gpio_fsel(WRITE_GATE,GPIO_IN);
    bcm2835_gpio_set_pud(WRITE_GATE,PULL_UP);
    bcm2835_gpio_fsel(WRITE_DATA,GPIO_IN);
    bcm2835_gpio_set_pud(WRITE_DATA,PULL_UP);

Next the SPI interface must be initialized through the library call bcm2835_spi_begin() and the appropriate clock rate set. Finally the 'step' signal must be configured through the /sys/class/gpio interface for rising edge detection. I wrote my own small class to do this.

Virtual Disk Image

I currently support DMK virtual disk images as described here. This format is comprehensive enough to hopefully allow supporting of copy-protected disk images as well as normal formats. Since I don't have a double-density controller I only implemented single-density. The Pi has enough memory available that the entire disk image can be loaded into memory into a suitable C++ class. Since the floppy disk controller expects an actual drive to take several milliseconds to step the read head from one track to the next we have sufficient time to convert the active track to SPI output format during that time.

Converting the raw track to SPI output format is done by a lookup table. Currently I support two different SPI clock rates. The 'single' rate outputs one clock and data bit per byte so a single track byte is sent as eight SPI output bytes for a total single-density track size of 25,000 bytes. The 'double' rate outputs either a clock or a data bit per byte so a single track byte is sent as sixteen SPI output bytes for a total single-density track size of 50,000 bytes. After the initial translation we then have to fix up the various special index and data address marks which have special clock and data patterns. Fortunately there are only seven of these special marks and the location of these special bytes are stored as part of the disk format. Another lookup table is used to set the index and data address marks to the correct pattern.

SPI Output

The SPI output is time critical since we are trying to fill the FIFO in a very short period of time as well as toggle the index pulse up and down in synchronization with the SPI output. I created a TransmitTrack class to handle this class. It uses a separate thread which runs at high priority and directly accesses the SPI registers. It runs continuously until a flag is set (and a condition signaled) to exit. At the beginning of the track the index signal is set to HIGH and the current time is recorded. The index pulse should remain high for around 4-5 milliseconds so we add that to the current time so we know when to set the index pulse LOW. Next we write to the SPI FIFO until the SPI register indicates the input FIFO is full. Then we enter a loop where we call pthread_cond_timedwait for 100 microseconds or until the exit condition is signaled. Each time pthread_cond_timedwait returns we either exit (because the condition was signaled) or we continue to fill the FIFO. We also check to see if the index pulse needs to be set low. A real floppy disk spins continuously so when we reach the end of the track we wait until the last byte has been transmitted and start all over again.

The SPI interface reads and writes data at the same time and expects the input FIFO to be emptied as data arrives. Currently we read the data from the input FIFO but discard it as virtual write has not been implemented. I suspect the write access will be require much higher data rates and will probably have to use DMA for SPI input/output.

Source Code
Source code on Google Drive includes a Geany project and a simple makefile. The binary file ldos.dmk is a disk image of a bootable LDOS single-density disk. Note that you must run the program using sudo or from a root terminal.

Note: current code doesn't use the kernel spi driver so you might need to blacklist spi-bcm2708.

Thursday, 26 September 2013

SPI - the Key to the Project

Using SPI to Output Track Data

I looked at several floppy disk projects for ideas on how to make the Raspberry Pi work as a virtual floppy disk. On in particular has been quite useful - the SVD. It was based on a MicroChip PIC controller and bit-banged the output line representing the floppy disk track data. The difficulty with this approach is that it relies on assembler code and very tight timing where each instruction time is measured and accounted for. This is not compatible with an operating system where interrupts could stop the flow of execution and cause gaps in the output data.

Let the Hardware do the Heavy Lifting


After a bit of investigation I decided to try the SPI bus instead. This would let the specialized hardware of the Raspberry Pi do a lot of the work for me. Put simply, I'm ignoring the SPI protocol and asking the SPI hardware to perform parallel to serial conversions and to do it according to a specified clock rate. The logic analyzer screen shot below shows the SPI clock on channel 1 and the SPI output on channel 0. The first two output bytes are 0xFF and the third byte is 0x00.


Note that the SPI clock is not continuous. There are eight clock pulses for each output byte followed by a one clock cycle pause. So what we actually have on the output are eight bits that can be toggled on and off followed by a ninth 'spacer' bit which is always zero. The challenge is to use this capability to output a signal that emulates the floppy drive data output.

The initial attempt set the SPI clock so that the total time for the eight bits plus the ninth 'spacer' bit equaled 8 microseconds. A value of 0x80 was transmitted for a data bit 0 which only has the clock bit set and a value of 0x88 was transmitted for a data bit 1 which has both clock and data bits set. Therefore each byte of track data was translated into eight bytes of SPI data where each byte of SPI data represents a single bit of track data. The screenshot below shows the timing.


The output signal deviated from the ideal signal because of the spacer bit, but the floppy disk controller was still able to decode this correctly as the bits appeared within the expected timing window.

A more accurate approach doubled the SPI clock speed and represented each clock or data bit with a single SPI byte using a bit pattern of 11000000b or 0xC0 in hexadecimal for a '1' and zero for a '0'. (Clock bits are always '1' except for the special address marks noted in the previous blog post.) Note that because the clock speed is doubled we have to set two bits instead of one for each output '1'. Therefore each byte of track data was translated into sixteen bytes of SPI data where the even bytes of SPI data represented the clock bits and the odd bytes represented the data bits. The screenshot below shoes the timing. The clock and data bits are still .888 ┬Ás but the output pulses and gaps are now much closer to the ideal signal.


With the double-rate scheme a single-density track becomes 50,000 bytes of SPI data which must be transmitted every 200 milliseconds or one byte every four microseconds. You may wonder what happens if the software is interrupted for a period greater than four microseconds. Will there be a gap in the SPI output signal? Fortunately the Raspberry Pi SPI has a 64 byte input buffer which will take 256 microseconds to empty using the double-rate scheme. The current software runs on Risc OS where the recommended maximum time to disable interrupts is 100 microseconds so even in the worst case situation the buffer is unlikely to be emptied while the operating system is handling an interrupt. If I ever figure out how to enable DMA for the SPI even faster data rates would be possible.

More on SPI and GPIO programming in later blog posts.

Wednesday, 25 September 2013

Floppy Disk Format From Bytes to Bits

Floppy Disk Track Format

The original TRS-80 Expansion Interface supported single-sided, single-density floppy disks with 35 tracks and ten sectors per track with 256 bytes per sector. The floppy disk controller supported many more format variations, but this was the standard format used on the first systems sold.

The actual track data contains padding bytes at the start of the track and between sectors. Each sector starts with a header that contains a special address byte followed by the track number, the head number (used in later drives), the sector number, a sector length code and a two byte CRC.

A typical track layout would look something like this:

  • 16 bytes of 0xFF
  • 6 bytes of 0x00
  • 1 byte ID Address Mark - 0xFE (with special bit pattern)
  • 1 byte track number (0x00 for track zero)
  • 1 byte head number (always 0x00 for original TRS-80 drives)
  • 1 byte sector number (sector numbers start at zero)
  • 1 byte sector length code (always 0x01 for standard format)
  • 2 byte CRC (calculated by the floppy disk controller)
  • 256 bytes of sector data
  • 11 bytes of 0xFF for sector 0, 12 bytes of 0xFF for all other sectors
  • 6 bytes of zero
  • 1 byte Data Address Mark - 0xFB (with special bit pattern)
  • 256 bytes of sector data.
  • 2 byte CRC (calculate by the floppy disk controller)
  • 12 bytes of 0xFF
  • 6 bytes of 0x00
  • Then nine more sectors as above, starting with another ID Address Mark
  • Finish with a bunch of 0xFF until the sector is finished. Typically around 130 bytes.
The extra zeros and 0xFF between the sectors headers and sector data allow the floppy disk controller to synchronize to the data stream while waiting for the special address marks. Since the drive speed can vary slightly between drives, especially on older models drives, sectors can be slightly shorter or longer. The padding allows room for these speed variations and on a disk that's been written on different drives there may be slight variations in the number of padding bytes.

Soon after the introduction of the original floppy disk drives, third-party manufacturers started to offer disks with 40 and even 80 tracks. 80 track drives had twice as many tracks per inch and therefore floppy disks formatted on those drives could not be read or written in older 35/40 track drives.

Data Encoding

The actual data on a single density floppy disk is written using a scheme called FM - Frequency Modulation. In FM encoding each byte is represented by a stream of clock bits and data bits.


The picture above is from an actual floppy disk as captured by a Saleae Logic Analyzer with the data bits colored red and the clock bits colored blue. The picture represents a single byte of data. Since all the data bits are present, the byte is 0xFF. If none of the data bits were present and only the clock bits appeared, the byte would be 0x00. On the TRS-80 Model I with single-density the clock and data bits are 1 microsecond wide and the gaps between clock and data bits are 3 microseconds wide. That corresponds to 8 microseconds per bit.

For all normal data bytes from 0x00 to 0xFF the clock bits are always present and the data bits are present or missing according to what byte value is represented. However, there are seven special address mark exceptions to this rule which are used by the 1771 floppy disk controller. These are 0xF8 through 0xFE but with certain clock bits missing. The missing clock bits allow the floppy disk controller to find these address marks in the stream of bits coming from the drive.

The Address Marks look like this:

'Deleted' Address Mark 0xF8

Non Standard Address Mark 0xF9 (only used by 1771 FDC)

Non Standard Address Mark 0xFA (only used by 1771 FDC)

Data Address Mark 0xFB
'Index' Address Mark 0xFC

Address Mark 0xFD
'ID' Address Mark 0xFE





Two file formats that are commonly used to store TRS-80 floppy disk as files are described here. When turning these formats into raw bit data the correct bit patterns for both normal data bits and the address marks need to be generated. Therefore knowledge of the address marks is important for correct emulation. The emulator will also have to add the various padding bytes to create a complete bit image of a track. Since the drive rotates at a speed of 300 rpm an ideal track should contain 50,000 bits of raw data (including both clock and data bits) which will take 0.2 seconds to transmit at a data rate of 4 microseconds per bit.

Tuesday, 24 September 2013

Introduction to the project

Last winter I started a project to create a Virtual Floppy disk emulator for the TRS-80 Model I using a Raspberry Pi. The original project description is here.

My TRS-80 Model 1


The project was inspired by several similar projects based on Arduino and other micro-controllers and because my ancient TRS-80 Model I was having problems reading and writing floppy disks as the electronics and hardware aged. It's also rather difficult to transfer software to and from the TRS-80 without such a system.

The advantage of the Raspberry Pi is that it is a complete system with video, USB, keyboard, mouse and storage support. The disadvantage is that the current available operating systems are not ideal for real-time. But the hardware features of the processor help overcome that limitation as will be seen later.

TRS-80 Floppy Disk Interface (Model I)

The TRS-80 Model I expansion interface (which supported the first floppy drives for the TRS-80) was created around 1978. It was designed to use existing floppy disk drives (with perhaps some minor customizations) connected by ribbon cable to the expansion interface. It used a Western Digital 1771 floppy disk controller IC which supported single density only, running at a clock speed of 1 MHz. (The timing diagrams on the 1771 data sheet are for 2 MHz so you have to adjust them for the slower speed of the Model I.)

Interface Signals

For this discussion I will refer to output signals as signals output from the Raspberry Pi emulator that input to the TRS-80 expansion interface and input signals as signals coming into the Raspberry Pi emulator from the expansion interface. Because several floppy drives could be connected to the same line the TRS-80 used open collector TTL interfacing with 150 ohm pull-up resistors to 5 volts. This meant that 'active' signals pulled the voltage down to zero.

Note: The connectors on the TRS-80 Expansion Interface are flat two-sided card-edge connectors but the schematic in the technical reference manual refers to "Pin numbers" so "Cable pin" refers to the pin numbers from the schematic.

The output signals are:
  1. Index Pulse. Cable Pin 8. This short (4 millisecond) pulse is sent at the beginning of every track. On a real floppy drive this is generated by a photocell pointed at a LED shining through the index hole on the floppy disk.
  2. Track Zero. Cable pin 26. Active when the R/W/ head is at track zero (the home position). This is used by the floppy disk controller when seeking.
  3. Write Protect. Cable pin 28. Also generated by a photocell/LED combination when the write protect notch on the disk is covered.
  4. Read Data. Cable pin 30. This is the data from the disk R/W head that has been transformed from the raw analog signal to digital pulses by circuitry in the floppy disk drive. Because drives may vary slightly in speed from the ideal 300 rpm the floppy disk controller is somewhat tolerant of variations from the ideal signal. My system has an add-on data separator board from Percom that improves the performance in this area.
Because more than one floppy drive can be connected to the cable, a drive should only pull an output signal low when it has been made active (selected) by the drive select signal. The TRS-80 supported up to four drives, numbers from 0 to 3.

The input signals are:
  1. Drive Select 0. Cable pin 10. Selects drive 0 when active. In an actual drive this would cause the R/W head to press against the disk surface. DS0 on the schematic.
  2. Drive Select 1. Cable pin 12. Selects drive 1 when active. DS1 on the schematic.
  3. Drive Select 2. Cable pin 14. Selects drive 2 when active. DS2 on the schematic.
  4. Drive Select 3. Cable pin 32. Selects drive 3 when active. DS3 on the schematic.
  5. Motor On. Cable pin 16. Turns all drive motors on. The emulator should only be transmitting track data on the Read Data pin and accepting data on the Write Data pin when both Motor On and the appropriate Drive Select signals are active.The expansion interface contained a timer circuit that would automatically shut off this signal after a few seconds if the operating system was not using the drive interface.
  6. Direction Select. Cable pin 18. Selects the direction of movement of the R/W head when the step signal is pulsed.
  7. Step. Cable pin 20. This short (about 8 microseconds) pulse indicated that the R/W head should be moved one step in the direction indicated by the Direction Select pin. Fortunately the Raspberry Pi has programmable edge detect triggers that will watch for this and set a bit when the line is pulsed. Since an actual floppy disk takes a couple of milliseconds (or more) to move the physical head from one track to the next but the emulator can move the virtual R/W head almost instantly, the timing is not critical. I have been able to poll the edge detect signal rather than trying to use interrupts (which are not well documented).
  8. Write Data. Cable pin 22. Not yet implemented. This would contain the data to be written to the disk surface.
  9. Write Gate. Cable pin 24. Not yet implemented. Signals that the write data is valid and should be written to the disk. Stays low for the duration of the write operation.

Interface Circuitry

There were two main considerations when choosing what ICs to interface between the Rasperry Pi and the TRS-90 expansion interface. The first was the TTL logic (5v) used by the TRS-80 which is not compatible with the lower voltage (3.3v) logic on the Raspberry Pi. The second was the very low resistance (150 ohm) of the pull-up resistors. This results in a fairly high current and I needed to use 74LS06 to drive the output signals. Also, many level translation chips, especially bi-directional ones, would not work with such low resistance pull-up values. Fortunately I found I was able to avoid level translation chips in my design. While I could have used a 74LS05 for the input signals I found it was simpler to use a 74LS06 for both input and output so I could chose the IC that was most convenient.

Originally I put a level translation between the Pi and the 74LS06 for the output signals but it turned out not to be necessary and the Pi seems to be able to drive the 74LS06 directly. The input signals use the open collector of the 74LS06 (could be a 74LS05, but easier to use the same IC for both) and rely on the pull-up resistors on the GPIO pins to perform the level translation to the correct voltage. The Pi has programmable pull-up resistors on the GPIO pins so I just program them in the initialization code. Two GPIO pins also have pull-up resistors on the board itself, so it's convenient to use these two as input pins for the floppy drive interface.

Note: all Raspberry Pi pins refer to the Rev 2 pins as seen here.

Current configuration is shown below.

Read Data must be connected to GPIO pin 10.
Write Data must be connected to GPIO pin 9.
GPIO pin 11 (which is the SPI clock) should be left alone. I connect it to a pin for use with a logic analyzer to help me see what's happening.
I also leave GPIO pins 7 and 8 alone as they are part of the SPI interface and unavailable as I am using that interface for read and write data. GPIO 2 and 3 have pull-up resistors on the board itself so I use them as inputs.

Note: All outputs are pulled up to 5v by 150 ohm resistors on the Expansion Interface.

Write Protect (output) connected to GPIO 18 - Header pin P1-12.
Track 0 (output) connected to GPIO 23 - Header pin P1-16.
Read data (output) connected to GPIO 10 - Header pin P1-19.
Index Pulse (output) connected to GPIO 24 - Header pin P1-18.


Drive Select 0 (input) connected to  GPIO 2 - Header pin P1-03. (has 1K8 pull-up on the board)
Drive Select 1 (input) connected to GPIO 25 - Header pin P1-22.
Motor On (input) connected to GPIO 4 - Header pin P1-07.
Direction Select (input) connected to GPIO 17 - Header pin P1-11.
Direction Step (input) connected to GPIO 27 - Header pin P1-13.
Drive Select 2 (input) connected to GPIO 22 - Header pin P1-15.
Write Gate (input) connected to GPIO 15 - Header pin P1-10.
Write Data (input) connected to GOIP 9 - Header pin P1-21.

Update Sept 30. I found that using GPIO 3 as an input caused Rasbpian to hang while booting so I have changed Drive Select 1 to use GPIO 25, header pin P1-22 instead. This problem doesn't exist under RISC OS so perhaps a driver needed to be disabled.

One of the drives connected to the cable should have 150 ohm pull-up resistors to 5v. Only one drive should have the pull-up resistors and generally it is the last drive on the cable. I put a set of pull-up resistors on my interface board and a jumper between the resistors and 5v from the Pi power supply. The TRS-80 drive cable does not provide +5 volts so it's necessary to use +5 volts from the Pi board.

Not listed here are the ground signals from the cable which should be connected to ground on the Pi.

 This is a picture of the top of the original prototype (plugged into the Raspberry Pi beneath it). The IC on the left is a level translation board which I found was not necessary. The connector to the Pi is at the bottom left. The cable that connects to the Expansion Interface is at the top. The resistors are pull-up resistors and the yellow jumper connects them to +5v. The board itself is just a plain prototype board with a cutout for one of the Pi connectors that is too high for the pin header.

This is a picture of the bottom of the original prototype. The connector that is plugged into the Pi is at the left. The floppy disk connector is at the right.