I would like my computer to be able to display text and images on a monitor. The plan is to build a graphics card, which will connect between the computer and a monitor.
One of the features of a graphics card is an output interface, which connects the rest of the card to the monitor to deliver the video signal.
Over the years there have been many standards of output interfaces. The main ones are CGA, EGA, VGA and SVGA. Of these VGA (Video Graphics Array), which was introduced with the IBM PS/2, in 1987, became ubiquitous. I have chosen to implement VGA in my computer because many devices (monitors, TVs, projectors) have VGA connections. Also, it would be possible to interface with other output interfaces, including BNC and DVI. BNC will work because it is just a breakout of the connections that I will be using in the VGA standard. DVI is a digital standard but is workable because it is compatible with analogue signals, via a mode called DVI-A.
Figure 1 shows a VGA connection. The VGA connector is a 15 pin D-subminiature, with 3 rows – their functions are listed in table 1. The signal is analogue.
However, I will only need to use 5 of these, as shown in figure 2 and table 2. These 5 connections are red, green, blue, horizontal sync and vertical sync.
I will also be making connections to pins 5, 6, 7, 8 and 10 – but that will be just to tie them to ground.
There are some problems that will need to be overcome to be able to implement VGA. The first is the VGA signal timing. To keep each frame in time, there is a signal timing that needs to be adhered to. What signal timing is required is dependent on the resolution and refresh rate. Even the lowest (21.175 Mhz), is a lot higher than the clock of the computer! The 6502 that I am using has a maximum clock rate of 14MHz. One way of solving this problem is if I use fewer pixels. Timing is critical but the timing is based on the pixel frequency and the number of pixels. Therefore if I lower the number of pixels, I should be able to use the clock signal that is generated in my computer!
Originally VGA was one resolution: 640 x 480 pixels, with 16 colours.
If you look at figure 3, you will see the anatomy of a frame. Each frame is created by starting in the top-left corner and moving to the right. When one line is complete, the next line begins, progressing from left to right. When all of the lines have been drawn, one frame is complete. The process then repeats. As well as the visual information, there is information that corresponds to horizontal and vertical timing. These are necessary to ensure that the image is drawn at the correct point! These timing signals can be broken down into a back porch, synch pulse and front porch.
The front porch occurs after the line of video information has ended and blanks the signal, ready for the sync pulse that follows. The back porch occurs after the sync pulse and gives time for the beam to move from right to left, ready for the next line. There is no beam with an LCD monitor but the timing still needs to be followed to keep the timings correct. These pulses also apply to the vertical timing. However, the back porch for the vertical signal is also needed for the beam to return not just from right to left but also to the top!
Having determined that each horizontal line needs to be 264 pixels, I need a way of knowing when I have reached 264 and then reset to 0. To achieve this I will build a counter that counts from 0 to 264 and then resets to 0.
To build my counter circuit, I will use a 74161. This is a synchronous 4-bit counter. Counters can be synchronous or asynchronous – it’s important that I use one that is synchronous. Synchronous means in time. Because this is a 4-bit counter, it means that the highest that I will be able to count to is 16. To count to 264, I would need a 9-bit counter. Therefore I can connect 3 4-bit counters together, meaning that I could count up to 4096. This is more than enough. I will be able to tie all of their clock pins together to make everything count together. I will also tie all of the clear pins together, so that all of the counters are reset to 0 at the same time.
I have a counter that counts from 0 to 4096. Now I need to modify it to count to 264 and then reset to 0. I will use logic gates to detect when 264 has been reached. In binary, 264 is 100001000. If I connected a 9-input AND gate to the outputs of the counter and an inverter at the input of the 4th and 9th bit, the AND gate will have a 1 at its output when it has counted to 100001000. This will work but can be improved on with some optimisations. I could reduce this to just a 2-input AND gate! This is possible because I only really need to know when the 4th AND 9th bit is 1. Any 1s in the 5th, 6th, 7th and 8th bit is irrelevant because the counter will never count that high! I could then connect the output to the clear pins of the 74161s. However, the clear pin is active low, so I would need to invert the output. However, there is another solution, to use a NAND gate to replace the separate AND and NOT gate. I will use a 7400, which contains four 2-input NAND gates. Now when 264 is reached both inputs of the NAND gate will be 1 and the output will be 0, causing the counter to reset to 0.
Now that I have a counter that can count to 264 and then resets to 0, I need circuitry to detect when the front porch, sync pulse and back porch has been reached. I will use the same method of using NAND gates to achieve this.
Now I need to add logic to detect when the counter has reached 242. 242 in binary is 11110010. Using the same method as I did to detect when 264 had been reached, I need to detect when the 2nd, 5th, 6th, 7th and 8th bit has changed to a 1. There are no ICs in the 74 series that have NAND gates with 5-inputs. There is an 8-input (7430) NAND gate. This will work fine because I will tie the unused inputs high, which will behave the same as a 5-input NAND gate.
Now I need to add logic to detect when the counter has reached 200. 200 in binary is 11001000. Using the same method as I did to detect when 264 had been reached, I need to detect when the 4th, 7th and 8th bit has changed to a 1.
I will build two counters, one that counts the pixels horizontally and one that counts the pixels vertically. To count the pixels I will use a 74161. I need it to count up to 264, which will require 9 bits. The 74161 is a 4 bit counter, so I will need 3 of them.
The clear pin will reset the counters to 0. I will need them all to clear at the same time, so I will tie them together. All of the counters will use the same clock, so I will tie all of the clock pins together.
I won’t be using the data inputs because I won’t be loading any data into this. The 75161s will be used purely as counters. I will connect all of the enable pins high, as I want the chips to always be enabled. Because I will not be using the data inputs, I will not need to use the load pins. The load pins are active low, so I will tie all of them high. The enable T is what is used for the ripple carry. So I will use the first one, so that the 4 bits in the first counter rolls over to the seconds counter and then to the third. The ripple carry output of the 1st is connected to the enable of the second. Then the ripple carry of the second chip to the enable of the third chip. This creates a cascade, so I can count 12 bits, although I only care about the first 9 bits. I should now have a 12-bit ripple counter. To check that it is working as intended, I will connect it to 12 LEDs. I can’t connect the clock signal from the computer because that would be too fast to see anything happening. I will use my 555 timer circuit to provide the clock signal. It’s counting in binary, which is what I want.
We want to start at 0 and know when we have reached 200, 210, 242 and 264. Then I want to reset back to 0.
The pixel data needs to be stored somewhere in memory. I will store images in eeprom. eeprom can give byte stored at address according to the x and y position. The byte can represent the pixel data. VGA expects a voltage between 0 and 0.7 V. This range controls the intensity of each colour. I will connect each output from the counters to the address bus of the eeprom. The data bus will be connected to the inputs to the VGA connector. I will use 6 lines of the data bus, 2 for each colour. The horizontal sync signals generated from the counter circuitry will be connected to their corresponding VGA connections.
Now that I have a working graphics card, I want to connect it to the computer, so I can run programs and load pictures that output to it. I plan to remove the eeprom that I used for testing and use the eeprom in the computer to store programs and pictures. I will then use the ram in the computer to load these into. For this to happen, I need a way of isolating the graphics card from the computer when the CPU is accessing the RAM. The reason for this is that if I did not do this, then whatever is being outputted to the RAM would cause ‘garbage’ to be displayed! There are a number of solutions to this problem, the one that I will use is DMA – direct memory access. DMA is a way of allowing a connection to be made to the RAM, without the involvement of the CPU. This means that only visual data will be outputted and also allows the CPU to do work at the same time.
To implement DMA I will use a bus transceiver. A transceiver allows two-way communication between 2 buses. Transceiver is a portmanteau of transmitter and receiver. I will use a 74245, which is an octal bus transceiver. The 74245 contains a direction pin, which is used to control the direction of data. There is also an enable pin, which can be used to isolate circuits. Using these 2 pins I can control the connections between the graphics card and the RAM. This is called bus mastering, controlling which bus has control. The plan is to give the graphics card control during the period that a frame is drawn and to give the CPU control during blanking periods.
As well as controlling when the graphics card has access to the RAM, I need to be able to stop the CPU from running when it should not be accessing the RAM. The 6502 has 2 pins that I can use to implement this: RDY and BE. RDY is the ready pin. BE is the bus enable pin.