Retro computing

img_9151
Why
Old computers fascinate me. With resources so limited they were able to do so much.
They can do so much as a result of huge work and clever tricks from both hardware and software designers.
Wanting to learn more about them I built myself a some kind of retro computer.
What I achieved at the moment is a system that can take some input from a PS/2 keyboard, do some processing on the input and display a result on a monitor with VGA interface.

Achieved:
– input from PS/2 keyboard
– application processor
– video processor: 640×480 VGA resolution, monochrome, 10×10 video text mode

The processors:
– a PIC16F877A MCU that acts as the application processor: read PS/2 keyboard input, process input, sends information to video interface
– a PIC18F25K50 MCU that acts as a video interface: takes input from application processor, stores and modifies the framebuffer, generates VGA signals to be displayed on the monitor

Video processor:

Making the PIC18F25K50 mcu to generate a static VGA video signal for the monitor to lock was not very hard. But getting this signal to be usefull and stable got me through a lot of thinking and debugging with the logic analyzer. The video signal still has a minor issue.
A minimal VGA signal needs the following signals:
– a horizontal sync (HSYNC), used to notify the monitor that it should jump to paint a new line
– a vertical sync (VSYNC), usded to notify the monitor that a full screen was painted and it needs to position back at the top of the screen
– color data (at least one of the red, green or blue), which represent the actual pixels painted on the screen

The HSYNC and VSYNC signals need to have precise timing in order to get a stable video signal.
Timings of the sync and data signals are explained in detail by many other people in similar projects.
The websites that I used mostly for this project to be done are the following:
http://dpeckett.com/vga-on-the-arduino-with-no-external-parts –> the site that inspired me the most
http://tinyvga.com/vga-timing/640×480@60Hz –> detailed timing description for my VGA signal
http://www.pyroelectro.com/tutorials/arduino_basic_vga/

My code was written in C language with some assembly and compiled with the Microhip XC-8 compiler.
As I was saying above, the hardest part was to get the video signal to look clear.
The way I decided to generate the HSYNC and VSYNC signals was by bit-banging.
The HSYNC signal needs to have a period 31.7us, 3.8us being low to indicate the monitor to jump to the next line and 28.9us needs to be high.
Of the 28.9us high time, during 25.4us we will draw the pixels(active region) and the rest of the time is used as front porch and back porch.
Trying to bit bang this signal and draw pixels during the 25.4us was not very time precise and the output video wiggled and wobbled.
The architecture of the mcu(8 bit, only one work register) and the fact that the code was written in C didn’t allow me to have very precise timing.

Consider the following simplified VGA generator code snippet:

int line_nr = 0;
while(line_nr < 480)
{
    hsync_pin_high();
    delay_back_porch();
    draw_some_pixels();
    delay_front_porch();
    hsync_pin_low();
    delay_hsync();
    hsync_pin_high();
    line_nr++;
}

What will happen when the variable line_nr exceeds the maximum 8-bit value 255 while it counts to 480?
If you look through the assembly code, you will notice that the boundary check will take additional CPU cycles than before.
This way the timing consistency of the signals is lost and video output gets bad.
The PIC18F25K50 mcu runs at a frequencly of 16MHz so every CPU cycle takes 62.5ns.

To fix this problem I made the HSYNC signal to be hardware generated as a PWM signal.
I set the MCU to trigger an interrupt when the PWM signal gets low.
In the interrupt function I will start to draw the current line from the framebuffer.
Because of the timing it takes to read from the framebuffer in the interrupt function, the actual drawing of the pixels will start after the HSYNC singal goes high again.
This way, the drawing of the pixels happens during the active region.

After drawing the pixels, a small part of the time remaining until the next interrupt is used to increment the line number and the rest is idle time.
Vertical sync time takes 2 lines starting from line number 480.
During vertical sync I disable interrupts and draw additional characters into the framebuffer if necessary.

To draw the pixels I used the trick learned from Daniel Peckett’s project(see first link).
The trick is to push the pixels from the SPI data line since this is very fast and doesn’t need any CPU cycle.
Every byte loaded in the SPI TX register means 8 pixels drawn on the screen.
The pixel clock for 640×480 VGA mode is 25.17 MHz.
I used 8MHz as the SPI frequency and this way one pixel drawn by us is actually ~3 times wider than the pixel in the standard 640×480 mode.
Another detail is to choose a SPI mode where SPI data line is low while idle, because the VGA monitor expects the data signals to be low when it’s not in the active region.

video-data

Horizontal sync and video data

For the characters drawn on the screen I chose the fonts to be the ones from the well known HD44780 LCD controller.
The font dimensions are 5 pixels wide and 7 pixels tall.
I manually transposed these fonts from their drawing in the datasheet to a 2 dimension C array.
The 5 pixels from every character line are loaded in the 8 bits SPI register.
The remaining 3 pixels from the register being zero, will appear as black on the screen.
These 3 black pixels will be interpreted as space between the characters and this way we don’t need to add additional spaces between characters in our framebuffer.

The framebuffer is a two dimension C array of 140 lines and 10 columns = 1400 bytes. The total amount of RAM in the chip is 1522 bytes.
The size was chose to fit the 10 characters per line of text multiplied by the 10 lines of text.
A line of text means 7 lines drawn on the screen because height of the fonts is 7 lines.
For readability, between two consecutive text lines I used 7 empty lines.
This sums up as 10(lines of text) * 7(lines per character) + 10(lines of text) * 7(spacing lines) = 140 lines of the framebuffer.
Actually the 7 line spacing after the last text line is unnecessary.
My frambuffer will be drawn of 140 lines out of the 640 lines available at our resolution.

The fonts that are drawn on the screen are received on the UART RX line of the MCU.
During the vertical sync time frame, which has a duration of 2 lines(63 us), I check if any character was received over UART and if it is the case, I draw it to the framebuffer.
The characters supported at the moment are all capital letters, digits, some symbols, new line for drawing to the next line on the screen, backspace that will delete previous characters and ESC character that will clear the screen.
This makes the video processor to act like a 9600 baud serial terminal with 10×10 video text mode.

The application processor

The application processor at this moment runs a program that checks if a number read from the keyboard is prime and displays that on the screen.

On how to use the PS/2 keyboard I read this document http://www.jkmicro.com/ps2keyboard_en.pdf.
PS/2 protocol is easy for grabbing simple key presses and gets quite complex depending on how much of keyboard interaction you want to implement.
The way I wrote my code is to get the keys after they are released(finger is lifted from the key).
When a key is pressed the keyboard sends the code of the pressed key. When the key is released the keyboard sends first a key break signal followed by the code of the released key.
This way my code first expects a break signal and then takes the key code of the released key.

At this moment the code expects a maximum 4 digit number inserted from the keyboard and calculates if that number is prime and then displays on the screen.

schematic

Schematic

The schematic

Conclusion
This is a really simple system but I managed to build something that looks and feels like an old computer.
I’m very happy that I managed to build this project and I want to add future improvements.

Code
In the github at https://github.com/spanceac/retrocomp

Update

One of the things that kept me unsatisfied at this project was the big space between characters displayed on the screen.

The framebuffer as I said, is a 2 dimensional array of this form: fb[line_number][font_line]

Reading from a 2 dimensional array takes a lot of time and this leads to the big spaces between two adjacent characters displayed on the screen.

The solution to fix this was to implement the framebuffer as a one dimension array where consecutive bytes are the font lines that are needed to be displayed on the current line. This way, to display a line of text is necessary only to read from consecutive addresses in memory.

This picture explains the concept:

mem

Characters positioning in the video memory

This is a comparison between the images and you can definitely can see the improvement in the space between the characters and the overall look.

improvement

Another problem that bothered me at this project was the small text resolution: 10×10.

This is a memory limitation. To hold the characters and space between the text lines, almost all of the available 1,5KB of RAM was necessary.

I managed to reduce the memory footprint by removing the empty bytes that represented the space between text lines from the framebuffer.

This added more complexity to the code, because now I need to keep track of when the space between lines needs to be drawn to screen and make the video data pointer to point to some empty array of 1 line instead of pointing to the framebuffer lines.

The gain is that I have a smaller RAM footprint. The empty lines would take 700 bytes in my framebuffer in 10×10 text mode. And these were just empty bytes.

With this smaller footprint I managed to increase the text resolution to 14×14. This way we can now display 196 characters instead of 100. That’s almost double.

IMG_9309 (copy)

14×14 text mode

8 comments on “Retro computing

  1. Mockseemight February 19, 2017 10:54 am

    The Maximite does (iirc) 80 chars/line with a PIC32MX…

    • seb February 19, 2017 7:50 pm

      Maximite is an awesome project.
      Of course it does better since it uses a PIC32 MCU with 32 bits, 128KB RAM and DMA vs. the PIC18 that I used which is 8 bits, 1,5KB RAM and no DMA.

  2. Jari Tulilahti February 19, 2017 10:59 pm

    Well, I’ve done 32×16 characters + UART @9600bps + ANSI escape parsing with attiny85 😉

    But still fun project!

    • seb February 20, 2017 7:33 am

      Hi Jari,

      I see that your MCU had only 512 bytes of RAM.
      With so many limitations there are for sure some smart tricks you did there.
      Do you have a link for your project?

      • Jari Tulilahti February 25, 2017 11:43 pm

        Hi!

        Sure thing I do:

        https://github.com/rakettitiede/octapentaveega

        There’s source code, schematics et.al.

        I even made another version, which has 3 attiny85 chips running in parallel, each drawing single color :) That allows me to get 32×16 characters on screen using 8 colors 😀

        • seb March 11, 2017 1:37 pm

          Hi Jari,

          Thanks for the links!
          It’s an awsome project that you did.
          I now understand how you can do 32×16 text mode with only 512 bytes of memory.
          You keep the ASCII text in memory(32 * 16 = 512 bytes) and you read the fonts from the program memory as you display them and don’t use a framebuffer.
          I can see that the Attiny’s 85 are pretty smart: 32 work register, 20MHz speed.
          My PIC has only one work register and works at 16MHz.

          Greetings!

  3. grounded February 20, 2017 9:36 pm

    For monochrome video output, look at the _syncronous_ mode of the EUSART module in the PIC18F chip. You can pack the transmitted bytes back to back and avoid the black spot between bytes. Just read the chip errata carefully as some of the very high baud rates are buggy.

Leave a Reply

Your email address will not be published.


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>