Making a Sol-20 Emulator
The Sol-20 was an interesting machine. It was introduced in July 1976, appearing on the cover of Popular Electronics magazine. With its 8080 microprocessor and S-100 bus, it had more in common with the Altair 8800s and IMSAI 8080s of the day, than it did with the Apple and Commodore computers that were soon to follow, despite looking more like the latter.
I distinctly remember seeing a Sol-20 in the wild. It would have been the fall of 1977 or perhaps the summer of 1978. I had just started at the University of Waterloo and was visiting my cousin in Toronto. While walking along Queen Street we passed a small computer store and the Sol was prominently displayed in the front window. What a great looking machine with it's bright blue case and walnut sides, but at roughly $1650 (CAD) it was out of my poor student price range.
Only 12,000 were ever produced. Sol-20s are virtually unobtainable today. Because of this I am in the process of creating a Sol-20 reproduction. While detailed schematics and board layouts for the machine exist, the cost of reproducing a Sol-20 PCB and populating it with "vintage" parts would be prohibitive. So I decided to emulate the machine's hardware in software. This is possible because modern computers are orders of magnitude faster than the little 8-bit 8080 microprocessor powering the Sol-20.
Which leads me to this Instructable. I had never attempted to emulate a hardware platform before, and found the process of creating the Sol-20 emulator fascinating and thought others might be interested as well. So what follows are the steps that I took to make a functioning Sol-20 emulator.
Background
I always like to get to know the history of any machine I'm working with. If you are interested in the details of how the Sol-20 came to be, I highly recommend you read Lee Felsenstein's own story published in ROM magazine, July 1977. Here I want to focus on the people involved all of whom are luminaries in the early personal computer era.
Lee Felsenstein was the principal designer of the Sol-20. The design was based on a Tom Swift Terminal concept he had been working on and the VDM-1 graphics card he had developed for Processor Technology. There is also some reports that Gordon French, founder of the Homebrew Computer Club, was also involved in he development. Lee would later go on to design the Osborne 1 in the early 80s.
Processor Technology Corporation was a personal computer company founded in April 1975 by Gary Ingram and Bob Marsh in Berkeley, California. Based on their relationship with the development of the VDM-1 graphics card and other projects, Bob Marsh commissioned Lee Felsenstein to design and build the Sol-20. Bob and Lee were also founding members of the Homebrew Computer Club.
The design was originally suggested by Les Solomon, the editor of Popular Electronics. Les Solomon was instrumental in getting the Altair 8800 launched with a featured article on the cover of the January 1975 issue of Popular Electronics. He asked Bob Marsh if he could design a smart terminal for use with the Altair 8800. So the Sol-20 was initially positioned as a smart terminal to satisfy Les Solomon's request, but of course was a capable stand alone computer under the covers.
One final thought. This is an excerpt from a March 31st, 2014 Engadget article:
Many readers won't be aware of the importance of the SOL-20 to Apple's legacy. According to legend (aka Walter Isaacson's book Steve Jobs) in 1976 at the first annual Personal Computer Festival, held on Labor Day weekend, Steve Jobs and Steve Wozniak arrived with the Apple I in a cigar box. While Jobs walked around the exhibition hall, he was reassured that the Apple I was better than the competition in terms of functionality.
There was just one thing nagging at him; the SOL-20. According to Isaacson, Jobs was confident that his product had the best circuitry, but the SOL-20 was better looking. It came in a beautiful metal case, with a built-in keyboard and power supply. When compared to the scrappy Apple I, the SOL-20 looked more like a professional machine.
Funny coincidence that the Apple II also looked more like a professional machine with a built-in keyboard and power supply.
Research
Normally when I undertake a reproduction project like this, I spend a lot of time on the internet researching my intended target. Usually the information I need is scattered across many sites and is often hard to find. Not this time though.
Jim Battle has done an amazing job of gathering a huge treasure trove of Sol-20 materials into one site. A one stop shop for the would be Sol-20 replicator, or anyone with an interest in this wonderful retro computer. Not only that, Jim created a great Sol-20 emulator Solace which stands for Sol Anachronistic Computer Emulation. So why didn't I just use Jim's emulator? The main reason is that it is written for a Window's environment only and ultimately I hope to run my Sol-20 reproduction on a Raspberry Pi 4.
I especially like Jim's virtual cassette interface.
With much attention to detail it faithfully simulates reading tapes at either 300 or 1200 bits per second and will complain if data was "saved" at 300 and you try to read it back at 1200 :-) Of course you can turn these "features" off. Very cool. Thanks Jim for all your hard work.
For the emulator the most important document is the Sol Systems Manual. At 312 pages this is a comprehensive and very detailed overview of the Sol-20 hardware. Literally everything I needed to create the emulator was in this document. Today you are lucky if you get a four page "Quick Start Guide" with a new computer.
The CPU
The heart of any machine emulator is the CPU. Since the Sol-20 was based on the Intel 8080 microprocessor I thought that would be the logical place to start. Fortunately for me, Space Invaders and some of the other early arcade machines also used the 8080. Because there is a very active gaming community helping to preserve these retro classics, there are a number of great 8080 emulators to be found on GitHub. I ended up cloning py8080 because it's Python based and I'm more comfortable right now with Python than I am with C++.
So with a working virtual 8080 processor it was a pretty easy task to allocate some memory for it (64K because why not), load a monitor program (Solos since it was the default shipped), set the instruction pointer to the start of the program (0xC000), and run the emulator. Success! Technically at this point I had a Sol-20 running in emulation, but it was pretty boring since I had no way to interact with it. Time to create some virtual devices.
The Display
The system memory used by the Sol-20 is as follows:
C000-C7FF - System ROM. Sol-20 "Personality Modules" like Solos are mapped here. C800-CBFF - System RAM. Reserved by the system. CC00-CFFF - Display RAM. Shared memory between the CPU and VDM-1 video circuit.
Although most systems shipped with only 8K or 16K of memory (or less) in those days, I have to wonder why they didn't map these addresses up into the F000-FFFF space to allow a contiguous 60K memory space for user programs. At any rate the important thing to note for display purposes is the CC00-CFFF shared video RAM. This 1K space was used to store 16 lines of text each of which is 64 characters long. Any text written to this memory would automatically be displayed on the screen.
Using PyGame I created my virtual screen. I could have written the characters from the shared video memory directly to the screen but the Sol-20 used a lot of unique character glyphs. These special characters helped to offset the fact that the Sol-20 as shipped did not have a graphics mode.
So based on the original Sol-20 6574 character ROM I created a set of character tiles that I could easily "blit" to the screen. I wrote a function to iterate over the 64 x 16 array of characters in the shared video memory and write them out to the screen and added this function to the main loop of my emulator. When I did that I was rewarded with this. I know the aspect ratio is off here. I'll deal with that later.
Wow. Still super boring but I see a > prompt so progress. I realized that the display was being updated every time through the main loop so I added a check in the 8080 emulator to set a dirty flag whenever the shared video memory was written to and now only update the screen when the dirty flag is set. Good enough for now.
The Keyboard
The 8080 can communicate with peripherals through shared memory locations like the Sol-20 display does, or through I/O ports. I/O ports are accessed via the 8080 op codes IN and OUT. There are 256 possible ports numbered 0x00-0xFF. When an IN instruction is executed the 16-bit address bus is set with the port number of the request in both the upper and lower bytes of the address, the IORQ (Input/Output Request) line is held high, and a data byte is retrieved from the 8-bit data bus. Similarly, for an OUT instruction, the 16-bit address bus is set with the response port number in both the upper and lower bytes of the address, the IORQ line is held high, and a data byte is written to the 8-bit data bus. You can think of this block of ports as a separate "memory space" for the 8080.
For the exception of the memory mapped display, all communication between the Sol-20 and peripherals is accomplished via I/O ports. I have added code that "handles" the IN and OUT op codes and emulates device interaction. Let's look at the keyboard implementation as an example.
I created a small keyboard buffer to store key presses that are mapped from my "modern" keyboard to keys that the Sol-20 is expecting. 95% of the key codes are the same but some are different and the Sol-20 has a few extra keys like LOCAL and CLEAR.
When the Sol-20 monitor program is waiting for a key to be pressed, it will make repeated IN requests to port 0xFA to fetch the General Status Port. If there is a key in my local buffer I return the 8-bit status result with the Keyboard Data Ready bit (0x01) "set" otherwise that bit will be "cleared". When it "sees" that there is a key ready, the monitor program will make a subsequent IN request on port 0xFC to fetch the key which I then returned from the head of my buffer.
More Screen Stuff
With the virtual keyboard running I can now enter commands to Solos.
A little more interesting. But something is missing. There is no cursor. It turns out that the Sol-20 marks the cursor position on the screen by setting the high bit of the character the cursor is on. The video hardware on the original machine uses this bit indicator to inverse the character at that position (black character on white background) and optionally blinking that character. I added a cursor by doubling the size of my character table with the characters 0-127 as normal white on black background characters and the corresponding characters in the 128-255 range as inverted black on white background characters. This gets me the block cursor that I want. I'm leaving the blinking part as an exercise for future me.
One more thing I noticed was that the display did not scroll as expected with long command results, but instead wrapped from the bottom to the top of the screen.
Sol-20 had a solution for this too. A "start display line" value is continuously output from port 0xFE via the OUT op code. The VDM-1 hardware would use this value to implement scrolling behavior by emitting the lines from the shared video memory beginning with the start display line and ending with the last line to the top of the screen first, then it emits the lines from the start of the shared video memory and ending with the start display line to the bottom of the screen. My display implementation does the same. For the above example the start display line value would be 4.
And here is the "corrected" scroll result.
Virtual Cassette Tapes
With just keyboard and display you can view and enter values into memory but not much else. I guess you could hand assemble a program, enter it into memory, then execute it were you so inclined. But to be truly useful and interesting you need the ability to load and save programs. The Sol-20 and Solos had built in support for two cassette tape drives. I have mentioned Jim Battle's excellent Solace Virtual Tape Drive and it's faithful reproduction of the cassette tape experience. My goal here is not nearly as lofty. At least initially I just want to support the Solos commands for loading (GET), saving (SAVE) and listing (CATALOG) "files" from "tape".
A Sol-20 file on tape has the following format.
Lead-in: At least 30 null (0x00) characters followed by a 1 (0x01). Header: 16 byte header followed by a Cyclic Redundancy Check (CRC) byte. * 6 bytes - file name. Actual name maximum is 5 bytes. Named padded out to 6 bytes with 0s. * 1 byte - file type. * 2 bytes - data length excluding CRCs. * 2 bytes - starting memory location to load data. * 2 bytes - execution address in not the same as the starting address. * 3 bytes - padded with 0s. * 1 byte - CRC byte. Data: The file contents with length defined by the header. After every 256 bytes there is a CRC byte that is not included in the header length.
When the emulator starts up it looks for a couple of special files TAPE1.svt and TAPE2.svt in a TAPEs folder and uses them to load into memory virtual tape images for cassette 1 and 2 using the format defined above. These .svt files use a subset of Jim Battle's Solace Virtual Tape Format 1 to define the tape's content. The commands implemented are:
H NNNNN TT LLLL SSSS XXXX Create a header with name NNNNN, type TT, date length LLLL, start address SSSS, and execution address XXXX. All parameters except name are assumed to be in hex notation. D AABBCCDDEEFF... Specifies a sequence of data bytes, as hex digits. There can be up to 32 bytes on one line. F [file name] Load the file from [file name]. There are two types of files supported. .HEX files are assumed to be binary images of a properly formatted tape file and are loaded "as is". .ENT files are text files whose first line is an ENTER command with the starting address and subsequent lines have the format AAAA: HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH where AAAA: is a memory address followed by 16 hex encoded bytes to load starting at that address.
All other SVT commands are ignored for now.
OK so I have virtual tape images now, how does Solos access them? As with all devices through ports of course. When Solos wants to read from or write to a tape the first thing that it does is attempt to turn the tape drive on by performing an OUT byte to port 0xFA with the TT1 bit (0x80) set for tape 1 or the TT2 bit (0x40) set for tape 2. Sol-20 had a hardware connection to the cassette players AUX jack to accomplish this. My emulator uses this signal to prepare for tape activity. Since I don't know if the operation will be a read (GET) or write (SAVE) I prepare for both. For reading I set the tape head to 0 to start reading from the beginning of the tape, and switching to virtual tape 1 or 2 depending on what TTx bit was set. For writing I prepare a empty buffer to accept tape writes from Solos. A tape on flag is also set at this point.
Once the tape is started, Solos will perform an IN on port 0xFA to see the tape ready status. For reading if the tape head is less than the length of the current virtual tape the status byte will be returned with the TDR (Tape Data Ready - 0x80) bit set. Solos will then issue INs on port 0xFB to retrieve consecutive bytes from the current virtual tape. For writing a TTBE (Tape Transmitter Buffer Empty - 0x40) bit will be set. Solos will issue OUTs to port 0xFB with the bytes to save which get appended to my output buffer.
Finally when the GET or SAVE operation is complete Solos will turn off the drive by sending an OUT to port 0xFA with a null byte clearing the TDR and TTBE bits. For reads there is nothing more to do here, but for writes (SAVEs) at this point if tape on is set, the output buffer is written to a file with the tape filename used in the SAVE command and .HEX extension. At the same time a "F filename.HEX" entry is added to the appropriate TAPEn.svt file (if it's not there already) then the virtual tape is reloaded to pick up the changes.
So with the default TAPE1.svt that looks this:
SVT1: Solace Virtual Tape Format 1 F aster.ent F atc.ent F chess.ent F minefield.ent F msbasic.ent F raiders.ent F reversi.ent F trap.ent
You can now issue the CATALOG command.
Then GET the CHESS game.
And EXECUTE it with an >EX 0000 command.
Finally something interesting!
Finishing Touches
When I was working on the virtual display I skipped a couple of improvements in the interest of finishing some of the other stuff like keyboard and virtual cassette tapes. The first was the aspect ratio which was more like 16:9 than the period 4:3. This has been adjusted by altering the character ROM images, basically by doubling every scan line.
I have also added an optional virtual blinking cursor, the original's display hardware took care of this. The original Sol-20 also had a bank of 8 dip switches that controlled display options like SOLID and BLINK cursor. I'll be adding a similar set of switches to my reproduction, but for now the BLINKING_CURSOR option is a Boolean in my Python code.
Running the Sol-20 Emulator
Requirements
There are only two prerequisites for the basic Sol-20 emulator to run.
- Python 3 must be installed onto the target machine.
- PyGame modules must be loaded into the Python environment.
How this is done is going to vary depending on what operating system you are using. Here is a great link on How to Install Python 3 in various environments. Similarly here is a link to Install PyGame into those same environments.
Installing the Sol-20 Emulator
- Go to the Sol-20 Reproduction page on GitHub.
- Download the Source code archive for the latest release, either the .zip or the tar.gz file.
- Unpack the archive retrieved into a folder of your choice.
Running the Sol-20 Emulator
- Open a command prompt.
- Navigate to the folder that you expanded the Source code into.
- Run the command: python3 main.py
References
The commands available in Solos can be found in the SOLOS/CUTER User's Manual.
A few programs have been preloaded into the distribution in the TAPEs folder. See the Virtual Cassette Tapes section above for how these are organized and made available to the Sol-20. Many more programs can be found in the programs section of the Sol20.org site.
Here is a short clip of my Sol-20 emulator running a Space Raiders game (in attract mode I'm not nearly this good at the game ;-)
Final Thoughts
Writing the Sol-20 emulator was a lot of fun, and I learned a great deal. I hope by documenting my experience I can shed some light on how to emulate hardware that is becoming inaccessible to the average person and perhaps encourage other to do the same so that these machines and the software they ran will live on.
If you would like, you can follow my progress on the larger Sol-20 Reproduction project here.