After building one of Geoff Graham's ASCII video terminals for my Arduino Altair simulator, I found myself with a couple extra PIC32MX250 microcontrollers I had ordered and was looking for a project to use them in.
Since the PIC32 could obviously be used to generate a video image, I looked into what kind of graphics extensions existed for the Altair and found two main contenders. There was Processor Technologies VDM-1 display board. However, the VDM-1 was mainly a text terminal that connects directly to the S-100 bus. Interesting - but not very different from just a regular serial terminal.
Then I came across the Cromemco Dazzler board, released in 1976. A real graphics card for the Altair (or other S-100 computers) that could produce a picture with a resolution of up to 128x128 pixels in single-color mode or 64x64 pixels in multi-color mode with 15 different colors.
According to Stan Veit, who in 1976 placed a TV connected to a Dazzler in his computer store's window in NYC: “People driving by began to stop and look – they had never seen anything like it before. In a short time the Dazzler had caused a traffic jam on 5th Avenue!” The police had to contact the building landlord and make him disconnect the television [Wikipedia].
That seemed like a worthy project to devote some time and energy to!
BTW - the program Stan was running on the Dazzler was this:
Here's a video filmed directly off my TV. It really looks much better in in real life - my camera(-work) is less than stellar.Architecture
Ultimately I imagined a separate board that could be plugged into the Altair simulator's native USB port (since that port is not generally used).
Note that the original Dazzler, as it produced the picture line by line, read the video data directly from the Altair's memory via DMA, so in the above architecture the arrow from Altair to Dazzler would go the other way and use the Altair's address/data bus instead of USB.
Doing the same in this project would require too much data constantly being sent over USB. It wouldn't actually saturate the USB bandwidth but it would likely slow down the Altair simulation. A more efficient solution was to keep a copy of the screen memory in the simulated Dazzler and send updates to the Dazzler whenever the memory area devoted to the Dazzler was modified in the Altair.Windows software client
Before starting to work on any hardware I wanted to make sure that the basic concept would work. So to start I wrote a Windows application that just runs on a PC which connects to the Altair simulator via USB. Working out the details of how the Dazzler produces the picture in its different video modes took a while but was certainly easier to debug on Windows than when running on a PIC. I was also able to confirm that the communication between Arduino and Dazzler client would not slow down the Altair emulation, even when running a demanding program with fast picture changes like the Kaleidoscope.
The Windows client is included in the GIT repository, so anyone wanting to try out this project can do so without any extra hardware (assuming they already have the Altair simulator). If you are not interested in building the hardware you can just skip ahead to the "Using the Dazzler" section below.PIC32 hardware client
Of course running the Dazzler application on a PC is less satisfying than having a complete hardware implementation of a Dazzler. So the next part of the project was to (a) figure out how to produce a video signal on a PIC32 and (b) how to connect the PIC32 to the Arduino via USB.
Producing a video picture directly requires exact timing - otherwise the picture becomes distorted or wobbly. Geoff's terminal implementation uses the SPI device in the PIC32 to push out the video data to a pin. While that produces very precise timing, I couldn't use it because SPI only outputs 1 bit. That works for a single-color video terminal but is not enough for a 15-color graphics card - I needed to output 4 bits of color information synchronously.
Searching the web for solutions that others may have come up, I found Paul Boddie's blog post about his color VGA signal generation on a PIC32. Paul did an awesome job describing the challenges and his solutions to them. Instead of the single-channel SPI he ingeniously used the DMA device in the PIC32 to write out pixel data, 7 bits at a time for an impressive 128 colors - all while keeping the CPU load to a minimum!
While - for various reasons - I ended up not using the DMA implementation and instead am pushing out pixel data directly from the CPU, Paul's great write-up of his endeavor was immensely helpful to get me on the right track. Thanks Paul!
After a lot of more experimentation, frustration and finally success, the result is a pretty stable VGA picture that (I believe) reproduces the Dazzler output pretty well. The reason it produces a VGA signal instead of a more time-period appropriate Composite video is two-fold: for one, creating a color composite signal requires much more precise timing than VGA. Secondly, few monitors these days even accept a composite signal. VGA too is becoming rarer but there was no way I could produce HDMI without adding more specialized hardware, so VGA it is.
After getting the VGA picture generator to work I set out to implement the actual Dazzler client code on the PIC32. That wasn't too hard (since I had already figured out most of the details when writing the Windows client). Communicating with the Altair/Arduino however was another matter. USB is a very versatile protocol but that versatility comes with the cost of high complexity. In order to not get lost, I started by using serial communication instead of USB. Turns out both the Arduino Due and the PIC32 can communicate via serial at exactly 750000 baud. That was plenty fast for the communication and allowed me to focus on getting the Dazzler emulation to work.
The last piece of the puzzle was to finally tackle USB. I wanted to use the Arduino Due's native USB port which is mostly unused in the Altair Simulator. The Due can operate its native USB port as a host, so client devices can be plugged in. However, the Due's firmware is quite restricted in the kind of devices it can support. I needed serial-over-USB communication, and that is not supported. However, the PIC32 can also operate as a USB host and its firmware does support serial-over-USB clients. And the Due's native port can act as a serial-over-USB client. So, from the USB point of view in my implementation the Dazzler is the host and the Arduino is a device plugged into the Dazzler. Sort of the wrong way around but it works.
Note that the Due actually provides 5V on the USB port even though it is running as a device (not a host). The Dazzler (host) uses the power provided by the device. Also the wrong way around and certainly not conforming to USB specifications but this is not supposed to be a generic USB host - it only needs to work with the Arduino Due. And that it does:
The VGA standard requires 5 signals to define the picture:
- Horizontal sync (TTL level, signifies the end of a line)
- Vertical sync (TTL level, signifies the end of a frame - i.e. a full picture)
- Red, green and blue color data (0-0.7V)
The 3.3V-level output pins of the PIC can be directly connected to the TTL-level VGA sync signals so that's simple. The software running on the PIC generates the SYNC signals using a timer and output compare functionality available on the PIC32, see the comments in the source code for more information.
For the pixel data, the PIC software outputs the same 4 bits of color information as the Dazzler: red on/off, blue on/off, green on/off and intensity on/off (RGBI). If intensity is on, the pixel should be bright otherwise it should be darker. If all color bits are off, the intensity bit is ignored (i.e. there is no bright black). That results in 15 possible colors.
In addition to color mode the Dazzler can be switched into gray-scale mode. In that mode the four color bits are used as a scale to define the brightness: all off is black, all on is white and in between are different levels of gray.
Converting the five digital bits of information coming out of the PIC to the three analog 0-0.7V levels required some more hardware, but not much:
- Resistors to drop the voltage for the color channels
- Three AND gates and resistors to boost the voltage if the intensity bit is on
- Four diodes and a four-resistor ladder to generate the gray-scale levels
- A 4053 analog switch to switch between the color and gray-scale output
The final circuit looks like this:
I determined the resistor values by experiment. They work fine on the three monitors I have tested them on and I verified the signal levels on a scope. Still, I'm just a hobbyist and can't guarantee that this will work on every monitor. Build/use at your own risk!Joystick support
The Dazzler itself was just a graphics card but Cromemco also offered the D+7A digital/analog I/O card as well as the JS-1 joysticks which could be plugged into the D+7A. Some of the software available for the Dazzler (mostly games) is only usable with a joystick so it seemed like a waste to not also support joysticks.
The PIC32 has built-in A/D converters, so hooking up a thumb joystick is simple. I wanted to support two joysticks and since each joystick has four buttons I ran out of GPIO pins on the PIC. A shift register was the solution for that problem.
The prototype joysticks I built are not exactly pretty but they work very well (you can see them in the picture in the "Final thoughts" section).Sound support
In addition to joysticks, the D+7A card also provided digital-to-analog converters that in combination with the speaker included in the JS-1 joysticks could be used to output sound.
There are no digital-to-analog converters in the PIC32 but there are timers that can be configured to produce pulse width modulated output signals. A filter circuit connected to such an output can then convert the digital signal to an analog signal. Instead of building speakers into the joysticks I opted to just add a 3.5mm audio connector to the board. The output on that is a line level signal, so it can be plugged into any kind of amplifier or powered external speaker. The output signal is stereo, since the Dazzler did support two channel audio (one for each joystick).Building the Dazzler hardware
The schematics directory in the GIT repository should contain everything needed to build the hardware, including Gerber files for the main PCB as well as the joysticks. The schematics are provided in EasyEDA format (.json) as well as PDF. There is also a BOM (bill of materials) file listing every part and where to order it, including supplier part numbers. I was not able to source everything at LCSC so the parts are split between LCSC and DigiKey. Obviously you can use other suppliers, just make sure the parts will fit in the footprint of the PCB. The BOM file has a column named "verified" - parts with a * in that column are ones that I actually ordered myself from the supplier and can vouch that they fit. All other parts I already had here at home so I did not order them again. I tried my best to pick the correct ones but can't guarantee that I didn't make any mistakes in the part numbers.
Note that in addition to the hardware for the board you will need a programmer (either PICkit3 or PICkit4) to upload the firmware to the PIC32 microprocessor. Simply connect the programmer's connector to the ICSP header on the Dazzler board (between the "Reset" and "Test" buttons, pin 1 is marked with a white triangle) and use MPLAB's MPE tool to upload the HEX file containing the firmware.
Here are some pictures of the assembled Dazzler main board and joysticks:
If you end up building the hardware, there are a few test modes built into the software that allow you to test the video generator and joysticks:
Holding the Test button down during power-up or reset will enter test mode. Five test screens can be cycled through by pressing the Test button. These screens represent some of the Dazzler's graphics modes:
- 32x32 pixel color: 512 bytes of screen RAM, individual pixel color.
In this test screen, the top-left pixel will be green if a USB connection is established and red if not.
- 64x64 pixel color: 2048 bytes of screen RAM, individual pixel color
- 64x64 pixel monochrome: 512 bytes of screen RAM, common pixel color
- 128x128 pixel monochrome: 2048 bytes of screen RAM, common pixel color
- 32x32 pixel gray-scale: shades of gray instead of color.
While these test screens are active you can use the joystick buttons to test audio output. Each joystick controls one audio channel.
- Button 1: 440 Hertz square wave
- Button 2: 440 Hertz sawtooth wave
- Button 3: 440 Hertz triangle wave
- Button 4: 440 Hertz sine wave
The joystick's X axis can be used to change the frequency (in 5 steps), the Y axis controls volume (also in 5 steps).
Holding any button on one of the joysticks down during reset displays a joystick test screen that shows a sight with a red dot for the current joystick position. In the center of the line on top, pixels are removed according to the button presses. Note that since all pixels are red it can be hard to make out the joystick dot among the other pixels.
Once you have tested the basic functionality I would suggest to run the "Kaleidoscope" program first, as it does not require any inputs and should immediately start filling the screen.Using the Dazzler
This works just the same whether you're using the Windows software client or the hardware implementation.
- First you need to update the Altair simulator to include Dazzler support. Download the latest firmware for the simulator from its GIT archive.
- Load the
config.hfile into an editor and change
#define USE_DAZZLER 0to
#define USE_DAZZLER 1
- Upload the new firmware to the Arduino Due using the Arduino IDE
- Enter the configuration menu and set the Dazzler option to "USB Native port"
Now connect the USB cable from the Arduino's native USB port to your PC or the PIC. If you are using Windows, you may have to select the proper serial port in the application (tip: if you start the Windows application first and then connect the USB cable, the application should automatically select the correct port).
The updated Altair simulator firmware includes a the Kaleidoscope program (program #16). To start it, set SW7-0 switches to 00010000 and press AUX1 down.
A number of additional examples are provided on a disk (disk #16 or #10h). Mount the disk and run the boot loader. This will boot CP/M. You can now start the different programs on the disk from within CP/M. Note that none of these programs have any exit mechanism, so to run a different program you will have to stop and reset the Altair and boot into CP/M again.
- DazzleDoodle (very basic drawing program operated by joystick)
- DazzleMation (basic animation program, needs text input, see below)
- DazzleWriter (allows to type text, reads text input from 2-SIO card)
- Life (Conway's game of life, needs text input, see below)
- Track (needs joystick)
- Chase (needs two joysticks)
- Space War! (needs two joysticks)
- Gotcha! (needs two joysticks)
- Tank War! (needs two joystick and Z80 processor)
- Ambush (needs one joystick and Z80 processor)
- Dogfight (needs two joystick and Z80 processor)
- Chess (Microchess using the Dazzler as a graphics display)
The DazzleMation, DazzleWriter and 4d Tic-Tac-Toe programs require keyboard input from a Cromemco serial interface. This serial interface behaved very similar to (but not the same as) a SIO card. The new Altair simulator firmware includes an option for the SIO card to emulate the Cromemco serial interface. To activate, go into the configuration menu, select the SIO configuration option and then change the "Revision" setting to "Cromemco".
Note that the Windows Dazzler client application will recognize joysticks plugged into the Windows PC and send the joystick data to the Altair. It will also capture keypresses and send them to the Altair as if they were coming through the SIO input, which is helpful for DazzleMation and DazzleWriter.
Here are some screen shots from the programs:
All in all I'm pretty happy with the way this turned out, most notably that the end result functions exactly as initially imagined - not every project works out like that. There are a few artifacts in the video generator that I could live without (see the black stripes in the grayscale test picture) but overall the picture is really crisp and stable.
I would like to thank Paul Boddie for his very useful information about VGA picture generation using a PIC32, as well as Udo Munk of z80pack for tracking down and collecting the original Cromemco software and documentation.