TRS-80 Model 1 - Wilson Expansion Interface
The TRS-80 Model 1 originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the Expansion Interface (EI), which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.
Overview
My EI contains:
- Blinkenlights - Connected to address bus, data bus, and control signals.
- 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)
- INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)
- WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler[1] but this particular chip also has good internal data separation too, saving a few chips.
- 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.
- ATMEGA1284p microcontroller, running the Mightycore bootloader; this makes it compatible with and able to be programmed by Arduino software.
- SD card, connected to Arduino
Things not covered yet are:
- Printer port
- TRS-80 RS232 port
- Screen printer port
- Extra cassette port / switch
Theory of operation / Features
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes[2]. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.
ATMEGA Section
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices at the same time. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section.
My additions to the TRS-80 are broken down into two programs:
- SHared Ram Interface Program - a short z80 program that the TRS-80 runs from the shared dual port RAM
- Atmega Control Program Terminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).
ACPT command-line functions include:
- Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.
ls
- display entire contends of RAM (in hex editor-like grid)lr haddress
- reads single bytell file.bin hstartaddress
- load a dump file to RAM, starting at the given addressld file.bin hstartaddress hendaddress
- dumps a section of memory to a file on the SD card
- TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.
rs hstartaddress hendaddress
- works like ls, but you can spec sections of Z80 memory instead of the whole thing.lr / ll / ld
- work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffffj address
- jump to code at addressr
- exit the monitor program and Return to normal TRS-80 functioning.p
- Pause the TRS-80 and run the monitor code.rv file.bin hstartaddress
- verify a file matches a section of RAM
- SD file commands, special for managing files on the SD card.
dir
- shows list of files and their sizesdel file.bin
- delete a file
- File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.
xs file.bin
- send raw file over serial port. If you log these bytes, you can reconstruct the file.xr file.bin
- receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file.
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions.
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:
k sometext-to-type
- everything after the "k " is typed on the TRS-80 keyboard.- It also works with escape characters, like
\n
for eNter and\k
for breaK,\u \d \l \r
for the direction keys, and\c
for the clear key.
- It also works with escape characters, like
ks file.txt
- an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).
Files
Revision 6 - File:Trs80ei2-v6.zip
Revision 7 - File:Trs80ei2-v7.zip
Hardware/Construction
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals.
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins.
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).
The first board I started with was actually the light board, and the layout for that was decided based on how I could line up the components for less soldering. I made this board removable so I could install chips underneath. When it came time to make a floppy driver board and later a RS-232 board, I used the connector for the light board, since it had the whole Z80 bus exposed. The boards were stacked on top of each other with those stackable header things that Arduino shields use. In hindsight, this was a bad idea and I have had a lot of contact problems with the ones I used - there is an opportunity for a bad connection for each connector you add to the stack. Oh well, the whole thing took tens of hours to construct and now I'm stuck with it until I do a PCB.
Log
Debugging a bad RAM chip - May 31, 2019
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue.
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic.
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.
FreHD clone experiment - June 3, 2019
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The frehd project is an open source project that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is now a closed source version assembled and sold by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.
My first experiment was to try a program called TRSVID (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at my Tetris program to see the normal limitations of the TRS-80 graphics.
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and VHDUTL
seems to work ok. However, trying to actually format the disks using RSHARD1
(using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using SYSTEM (DRIVE=4,DISABLE,DRIVER="RSHARD1")
Other times, it would lock up after trying the RSFORM1
program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory.
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.
Incidentally, I also noticed that the IMPORT2
command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are.
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.
Version 7 of software - June 19, 2019
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester.
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the rl command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command tstram
that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like rd
inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.
v9 - Aug 2019
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks.
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines [3] that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.
v12 - December 2019
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.
RS-232 board - 3-24-2020
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason.
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.
edit 4/12/2020 - It works! I implemented everything in a real Model I RS232 board, except:
- I didn't implement the DIP switches. This is just some dip switches hooked to an IO port input. You can set everything from software. Model 3 removed these.
- This left me with an extra unused output pin on the GAL22V10. I found some TIL-311 hexadecimal LED displays / decoders, so I wired them up to IO port 0xEC. Now, I can have a debug display, simply OUT (0ech),a to write to the display.
- Did not implement the original TTL-RS232 voltage converters (I didn't have any of these ICs) What I did have was a bunch of MAX232 converter ICs, which do the same thing but have a capacitor charge pump to get the RS232 voltage. Also, per chip, they have just 2 TTL-->232 and 2 232-->TTL paths. So, I implemented tx/rx, rts/cts, dtr/dsr and DCD. RI is not connected to any 232 pin, and the TRS-80 thinks it is always OFF.
I wrote my own terminal emulator after finding the TRS-80 offerings lacking. See TRS-80 - WTERM.
Orchestra 80/85 - April-May 2020
The next thing I wanted to try out was ways to produce sounds and music on the TRS-80. Back in the day, the king of TRS-80 music was apparently a hardware add on called the Orchestra-80. According to trs-80.org, this added "four simultaneous voices over a six octave range." However, the sound did not come from a hardware sound chip, like the specialized ones Commodore or Atari had. Instead, the audio hardware was basically just a latch and some resistors, wired into an IO port. It's an R-2R DAC - basically the same thing as the much later Covox Speech Thing or Disney Sound Source for PCs. The real genius of the Orchestra-80 system was the excellent software, which synthesizes all the audio on the fly, and spits it out the IO port. Plus, the people who made it actually knew something about music theory, so it is possible to properly write music in it. Later, the Orchestra-80 was superseded by the Orchestra-85 and 90, which use slightly different hardware but worked with the model 3 and 4 too, and add stereo sound.
I found very little information on the technical details - anyone who has the boards seem unwilling to share the chips that are on them, and there were no schematics in the original manuals. However, we have emulators and the original software.
I started with the Orchestra-80. This writes data to just 1 IO port - 0xBD. Audio is unsigned 8-bit PCM, so it can be hooked up directly to the resistors. I used my extra GAL pin from the RS-232 board that is hooked to the LED hex displays, and redefined it to act on writes port 0xBD instead. From there, it's just a 74x374 latch, and some 10K and 20K resistors. I followed a schematic for a Covox. You can also look up R-2R DAC and get the same thing. D7 is the most significant bit. Later, I added a low pass filter - the sampling frequency is about 4Khz, and the resistors aren't perfect, so the audio can be quite noisy in the high frequencies.
So that's the 80 done. But later, I found out that most of the song files out there were written for the Orchestra-85/90, (85 is the Model 1 version) which has different hardware. First of all, it uses 2 IO ports (0xD5 and 0xD9) for 2 DACs and 2 stereo channels. Also, there is something different about the way the audio PCM is formatted - it's not PCM. If you take a dump of the data written to the ports and dump it into Audacity, you will find it sounds correct when imported as signed 8 bit PCM. But for signed, you need to take the 2's complement - invert the MSB and subtract 1. But, I found evidence to support the Orchestra-85/90 was still using R-2R resistors. How is the hardware doing the subtraction?
Before trying the hardware, I did the much more difficult thing of disassembling the Orchestra-85 software, and patched it to do the D7 inversion, average the result, and spit it out port 0xBD - my existing Orchestra-80 DAC. This did work, but the averaged audio was of even less quality (we are throwing away 1 bit), and the additional CPU overhead also slowed down the audio.
Back to hardware - I did some research, but was unable to find any DAC IC that took an 8-channel signed 2s complement inputs. It turns out that the audio data is probably not unsigned after all; it's probably "offset binary" which is the same process as 2's complement but you don't subtract 1, you just invert the MSB. That's easy.
Since I didn't feel like wiring up a bunch of resistors, I looked for a parallel DAC chip instead, which should be more accurate anyway. I settled on the AD7302, which is a 2 channel, 8 bit DAC. After a sample order from Analog Devices, I had one. Basically, the whole ADC is now on this one chip instead of a bunch of resistor packs and latches. For address decoding, I decided to use a GAL16v8 chip, since I had good luck using another one on the RS232 build. The GAL can also do the needed inversion of D7. So, this boils down the entire Orchestra-85 down to 2 chips, and 2 RC low pass filters. The solution fits on a tiny proto-pcb, neatly between the floppy controller and top boards. Because of the GAL, I also can decode the Orchestra-80 port, and not invert D7 for that one.
So, how does it sound? Actually, pretty good! Much better than anything you would expect is possible on a 1978 machine. It does use 100% of the CPU's processing power though, which is completely expected.
Another experiment I did was to fill the TRS-80's 48K of RAM with 8-bit PCM samples, then write a short program in my secret 0x3000 ram area to dump it out through the Orchestra-80 port. Realistically, 49152 samples only holds a couple samples of audio at a low bitrate, but it's enough for short sound effects, or a 5 second loop of a song that repeats. This is true digital audio on the TRS-80!
References
- ↑ Percom Doubler
- ↑ Northern Bytes, Volume 7, Number 5[1]
- ↑ http://www.trs-80.org/trs-80-rom-routines-documented/