Microcontroller: Microchip PIC18LF252
Program Memory: 32K bytes (16K instructions)
RAM: 1536 bytes
EEPROM: 256 bytes

Useful links
PIC18LF252 spec sheet
VGA timing information
PIC Game System
SX Game System

PIC microcontroller

This is my first attempt at generating video. This is also my first actual project with a PIC. It is inspired by a number of ideas: Primarily, I'm seeing and actually understanding the layout of a number of small (8 bit) computers. Secondly, I think having some form of video output for a computer is a good thing. Thirdly, there really doesn't appear to be any general purpose, off-the-shelf, low-end video chip(s) available for hobbyists. Fourthly, I have seen others to specialized video generation from PIC chips - and I now have that equipment.

Being the first attempt, this is really a proof-of-concept. Can Rob do it? As you'll see, with a little head-scratching, Googling, and asking of questions, I did!!

My general goal is 40x40 resolution. The PIC18LF252 microcontroller has plenty of memory, so the limitation becomes knowledge and timing. These are challenges I can deal with.

As far as what the output should be - after digging, the VGA timing information is far easier to understand than NTSC. I can generally understand the NTSC black and white scheme - but color is beyond my comprehension at this time! The only realy problem with VGA is the 31.5KHz horizontal sync rate.

As a side note, wiring isn't quite this basic. The Hsync and Vsync signals do run at 5V (TTL?) but the RGB lines are only 0.7V or 1V, I'm not quite certain. I've seen documents for both - but I was assured by people who have worked with video before that 0.7V is the correct target voltage.

Source Code

Early versions

I didn't really track intial versions well. The source code that I've supplied are copies of the working ASM file - if you do look, sort them by date to get the order of their creation. I figure that this isn't a great loss - if you want code, you'll grab a later version anyway.

First working video generation As a newbie, my first attempts were direct and not too successful. I manually calculated timing and misunderstood the information on VGA timing. Ultimately, I did get a signal, however. Note that the upper lines are skewed. This happened because I thought the Hsync signal only occurred during display phased and not during the vertical retrace interval.

After some headscratching, I realized that I needed to drive the routines by interrupts. This lead to reading up on the interrupts, calculating what 31.5KHz equated to in terms of clock frequency. Early on, the fastest clock I had was 2MHz which came out to 0.5MIPS. Not much, but I did achieve 13 horizontal lines of resolution (well, 12 1/2 in reality).

After reading the spec sheet, there were some optimizations (besides raw speed) which could improve performance:

  • Early interrupt-driven routine FAST interrupt return. All interrupts generated within the PIC place the WREG, STATUS, and BSR registers on a shadow stack. High priority interrupts will over-write low priority interrupt values (meaning you need to be careful). What this means for interrupts is cutting off 5 instruction cycles saving and then restoring the values. The code went from:
    movwf  w_temp               ; 1 cycle
    movff  STATUS,status_temp   ; 2 cycles
    movff  BSR,bsr_temp         ; 2 cycles
    movff  bsr_temp,BSR         ; 2 cycles
    movf   w_temp,W             ; 1 cycle
    movff  status_temp,STATUS   ; 2 cycles
    retfie                      ; 2 cycles
    retfie FAST                 ; 2 cycles
    That simplified the code quite a bit! You just need to be careful.

  • Early interrupt-driven routine with optimizations (FAST interrupt) PLL mode. Not that I understand how it functions, but HS PLL mode (highspeed phase-locked-loop) allows the PIC microcontroller to run at clockspeed. Normally, the number of instructions is 1/4 of the clock speed (MHz). Thus, my 2MHz oscillator was achieving 0.5MIPS. With this realization and scavanging a 4MHz oscillator from the PIC demo board, I actually started running at 4MIPS!

  • Table-based jump. Initially, my lookup tables were based on adding a value to PC and forcing the microcontroller to branch to the new address. However, I found that by using TBLRD, the number of instructions used was reduced. I went from code like this:
    ; Increment line# (4 cycles)
        movlw   4          ; number of bytes in GOTO instruction
        addwf   linelo,F
        movlw   0
        addwfc  linehi,F
    ; Calculate branch to subroutine (8 cycles)
        movlw   low jumptable
        addwf   linelo,W
        movwf   temp
        movlw   high jumptable
        addwfc  linehi,W
        movwf   PCLATH
        movf    temp,W
        movwf   PCL
        goto	vsynch_on		; line #0
        goto	vsynch_off		; line #1
        goto	endofinterrupt	; line #2
        goto	endofinterrupt	; line #3
        goto	endofinterrupt	; line #4
        goto	endofinterrupt	; line #5
    ; etc...
    to code like this:
    ; Read branch address from TABLAT (8 cycles)
        tblrd*+	                ;2 post increment
        movff   TABLAT,PCLATH   ;2
        tblrd*+                 ;2
        movf    TABLAT,W        ;1
        movwf   PCL             ;1
    ; This the jump datatable used for branches in the
    ; interrupt routine.  Each DATA entry is in HI/LO
    ; format.
        daddr   vsynch_on		; line #0
        daddr   vsynch_off		; line #1
        daddr   endofinterrupt	; line #2
        daddr   endofinterrupt	; line #3
    ; etc...
    It went from 12 instruction cycles to 8. Additionally, the macro that I created generates the address in two bytes instead of four bytes that a goto instruction required. Plus, the GOTO is not involved anymore - that adds 2 more instruction cycles to the count for a total of 6 instruction cycles saved.

After optimizations and adding PLOT, VLINE, HLINE Finally, I was ready to start using the PIC memory. Because of the limited memory, and because of the SWAPF instruction, it was simple to use one byte for two pixels. Using the FSR0 register, displaying two pixels looks like this:

    movf   POSTINC0,W  ;1
    movwf  PORTB       ;1
    swapf  WREG        ;1
    movwf  PORTB       ;1
Please note that this actually gives 16 colors instead of the 8 I use throughout the rest of this documentation. I termed the color scheme XRGB where X was applied to the other colors. To some extent, X is a palette.

Source Code

v1 - 20030928 - Code separation

It was becoming apparant that I needed to separate the code. A quick bit of digging introduced me to the #include directive. The code is now seprated into three pieces: main program, video generation code, and graphics drawing code.

You will note that the application code is nice an neat now.

The only functional addition is the creating of a cls routine to clear the screen.

Source Code

v2 - 20030928 - Text drawing routines

Addition of letter drawing routine What use is a video display without some form of text? Not much. Up to this point, "HI" was being spelled out with a series of plot, hline, and vline calls.

I created a font for the Apple IIe a number of years ago for a game I wrote in assembly. Some scrounging around with my disk images and AppleCommander made quick work of locating the file and making it available to me. Using a regular expression in TextPad formatted the file the way that mpasm expected.

The drawchar, fcolor, and bcolor routines were added.

Source Code

v3 - 20030928 - Text drawing routines

Addition of drawimage After text has been added, graphic images are next, of course. I laboriously created a simply picture - the US flag. Not a great rendition for sure, but functional. It is ironic that the assembly code was a lot easier to create than the manual image creation.

At this point, I decided that the foreground color (fcolor) and background color (bcolor) should be utilized more fully. Already the bcolor was used for the cls routine and the fcolor was used for any plotting being done. However, I decided that drawchar should use them for coloring of the letters - hence the funky colors used in the word "Hello".

The drawimage routine was added.

Source Code

v4 - 20041004 - Increase to 10MIPS

Full 10MIPS! One of my orders included a 10MHz oscillator - which would allow me to push the PIC18LF252 to its max! Unfortunately, the limit definately turned out to be memory.

At 10MIPS, the PIC is able to do roughly 251 instructions during the active display interval. At 2 instructions per pixel, this comes out to be 124 horizontal pixels. The microcontroller only has enough memory for 22 lines (remember that there are some variables using up RAM), so I decided on 64 pixels across the screen. This gives enough memory for 44 lines of resolution.

64 lines across wasn't quite perfect, but I had pushed the microcontroller to its maximum as a single solution for video. Not bad, but not worth too much, either. I had achieved my goal of a resolution of 40x40 however!


Final thoughts

This is a good start for a completely home-built computer. With some more exposure to electronics and the hardware that is available, video will be achievable. Next steps will include external components (SRAM initially) and also experimentation with interchip communication.

Useful routines developed:

  • Video signal generation. Not only a good excercise but somewhat reusable code.
  • Character bitmap. I won't have to look for this anymore nor dread doing it manually!
  • Graphics routines/macros:
    • plot x,y: Plot a pixel at x,y in the foreground color.
    • hline x1,x2,y: Draw a horizontal line from x1,y to x2,y in the foreground color.
    • vline y1,y2,x: Draw a vertical line from x,y1 to x,y2 in the foreground color.
    • cls: Clear the screen to the background color.
    • fcolor color: Set the foreground color.
    • bcolor color: Set the background color.
    • drawchar x,y,ch: Draw the character, ch, at x,y using the foreground and background colors.
    • drawimage imageaddress,x,y,width,height: Draw the image at imageaddress at the given x,y coordinates and assume that it is width pixels wide and height pixels high.