|Introduction to VGA Mode 'X'|
DOWNLOAD ... The example file mentioned in this article is contained in the file MX_SCROL.EXE (3,799 bytes) which can be downloaded by clicking this disk icon.
In the previous GDM articles, Phil discussed Mode 13 to the readers very nicely. One of the points he made was that in Mode 13, the addressing of pixels was in a linear fasion. That is, increasing our address in video memory by 1 byte resulted in the address of the next pixel. This can be really handy for writing very optimised assembly code using the assembly language string instructions.
However, Mode 13 has a few quirks which tend to make it unusable for serious game work. The biggest problem is that with Mode 13, you can only have one video page. This is a big problem since standard VGA cards today have at least 256k of video memory (some early cards had only 64k!). Looking at Mode 13 we see that it uses up 320x200 pixels = 64000 bytes, or approximately 64k. We appear to have lost the remaining 192k of video memory that we paid for. Using Mode 13 we cannot even access this memory let alone use it as a second, third or fourth video page.
Firstly, if we are going to program the VGA registers then we should know what we are going to program them with. The VGA registers (of which there are more than 50) contain information regarding the state of the display. The registers fall into 5 basic groups :-
1. General or external registers.
2. Sequencer registers.
3. CRTC (Cathode Ray Tube Controller) registers.
4. Graphics registers.
5. Attribute registers.
To setup Mode X, we will mainly need to make changes to the CRTC registers, but we will also need to make a few minor changes to some of the other registers too.
Before describing how this is done, I should make mention that we do not reprogram the VGA from scratch since doing so is tedious at best and inefficient anyway since we have the BIOS there to do all the really boring stuff.
So, to switch into Mode X, we simply call the BIOS's set video mode interrupt to switch us into Mode 13. Once we are in Mode 13, we make adjustments to many of the CRTC registers to switch us into Mode X.
On the VGA there are 26 CRTC registers in all. Instead of making changes to the registers here and there, we create a table of values containing the information needed for each CRTC register we intend to change and copy the entire table to the VGA. One thing that should be realised when doing this, is that our VGA card must be 100% register level compatible with the VGA standard. This should not be a problem anymore but some earlier cards were only BIOS level compatible with VGA.
Before proceeding to "quote" the table of values, I will briefly list each register's function so that you can see what each value does. I originally started describing each register but that was taking up way too much space and probably would not have served much use anyway.
Register Bits Name 360x240 Value ---------------------------------------------------------------- 0 0-7 Horizontal Total 107 1 0-7 Horizontal Display End 89 2 0-7 Start Horizontal Blanking 90 3 0-4 End Horizontal 14 5-6 Display Enable Skew 0 7 Compatible Read 1 4 0-7 Start Horizontal Retrace 94 5 0-4 End Horizontal Retrace 10 5-6 Horizontal Retrace Delay 0 7 End Horizontal Blanking, 6th bit 1 6 0-7 Vertical Total Register 13 7 0-7 =Overflow register= - 0 Vertical Total 8th bit 0 1 Vertical Display End 8th bit 1 2 Vertical Retrace Start 8th bit 1 3 Start Vertical Blanking 8th bit 1 4 Line Compare 8th bit 1 5 Vertical Total 9th bit 1 6 Vertical Display Enable 9th bit 0 7 Vertical Retrace Start 9th bit 0 8 0-4 Preset Row Scan unused 5-6 Byte Panning unused 9 0-4 Maximum Scan Line 9 5 Start Vertical Blanking 9th bit 1 6 Line Compare 9th bit 0 7 200-To-400-Line Conversion 0 A 0-4 Cursor Start unused 5 Cursor On/Off unused 6-7 *Not Used (keep as 0)* - B 0-4 Cursor End unused 5-6 Cursor Skew unused 7 *Not Used (keep as 0)* - C 0-7 Start Address High unused D 0-7 Start Adress Low unused E 0-7 Cursor Location High unused F 0-7 Cursor Location Low unused 10 0-7 Vertical Retrace Start 234 11 0-3 Vertical Retrace End 12 4 Clear Vertical Interrupts 0 5 Disable Vertical Interrupts 1 6 Bandwidth 0 7 Protect Registers 0-7 1 12 0-7 Vertical Display End 223 13 0-7 Offset Register 45 14 0-4 Underline Location 0 5 Count By Four 0 6 Double Word Mode 0 7 *Not Used (keep as 0)* - 15 0-7 Start Vertical Blank 231 16 0-6 End Vertical Blank 6 7 *Not Used (keep as 0)* - 17 0-7 =Mode control register= - 0 Compatability Mode Support 1 1 Select Row Scan Counter 1 2 Horizontal Retrace Select 0 3 Count By Two 0 4 Output Control 0 5 Address Wrap 1 6 Word/Byte Mode 1 7 Hardware Reset 1 18 0-7 Line Compare unused ----------------------------------------------------------------Whew!, that was fun but I wouldn't like to do it again. That's the values we will need to program the VGA into Mode X using a resolution of 360x240. I have listed the names of some registers which aren't really needed to setup the mode but are documented for completeness sake.
On a less technical note, I would like to relate an incident that I came across on the PASCAL echo on Fidonet:
An inexperienced programmer had somehow figured out how to use the OUT instruction to set up some values on his VGA card. He soon released a public domain "screen saver" program which would blank the screen for you. What he didn't tell people initially was that to arrive at this program, he sent junk data to some of the CRTC registers. He noted that a certain combination of values could be used to blank the screen. He didn't realise that the screen went blank because the poor monitor was thrown out of synchronisation by the data sent from the VGA card.
OUT DX, AL or OUT DX, AXwhere DX is the port which will receive the data and AL/AX is the byte/word of data to send. An interesting side effect with the second mode is that the low byte (AL) is sent to the port specified by DX, however the high byte (AH) isn't. It is instead sent to the port specified by DX+1. This may seem like a dumb "feature" but it does have it's uses, as I'll now explain.
The VGA has over 50 registers but not all of them have their own unique port address. Many of them are multiplexed onto one "shared" port address to save on hardware. When registers are multiplexed in this way, there are usually two registers which have unique port addresses and allow access to the "real" registers. The first of these registers is called the "Address" register and the second is called the "Data" register. To set the value for one of our "real" registers, we first tell the address register which one of our registers we would like to set up and then send the data for it through the data register.
(Once again, in assembler)
MOV DX, 03D4h ; Port address of CRTC Address register. MOV AL, 00h ; Register number 0 to change. OUT DX, AL ; Inform VGA. INC DX ; Point DX to the CRTC Data register. ; (remembering that it is the next one) MOV AL, 06Bh ; Value for Horizontal Total. (6Bh=107d) OUT DX, AL ; Inform VGA.This will work ok, except that it is very inefficient, even though we used INC DX instead of reloading DX directly with 03D5h. A better way to accomplish the same goal is as follows :-
MOV DX, 03D4h ; Port address of CRTC Address register. MOV AX, 06B00h ; Put register number (00) in AL & ; register data (6B) in AH. OUT DX, AX ; Output combined data to VGA.This has the effect of sending the value 00h to port 03D4h, ie. the CRTC address register, and the value 6Bh to port 03D5h, ie. the CRTC data (Horizontal Total) register. This is much more efficient and also has an added bonus when working within a loop. With the original code we had to increase DX by one with the INC DX line, but if we want to send more data, (as in a loop) we would have to restore DX to 03D4 by using DEC DX. If we use the second example then we can leave out both the INC DX & DEC DX lines altogether.
All that is left now is to tabulate our values along with their register numbers and then code a loop to send each word out to the VGA in sequence. I won't go into much detail here on how to do this, as I have included a source code file called MX_INIT.ASM to show you how this is done. Note that I have *NOT* tried assembling this particular file and is is presented purely for reference. It should work however, since 90% of it came directly from my sprite engine software.
Before leaving this topic, there are a few more registers which need to be set up prior to our modifying the CRTC registers, as noted in the source code. They are as follows:
1. Sequencer Register 4 should have a value of 6. This effectively disables the Chain by 4 mode used to give Mode 13 it's "linear" addressing.
2. The sequencer must then be reset via an asynchronous reset. I am not 100% certain why but I think it is so that the VGA applies the change made in 1 above.
3. We must set up the clocking frequencies for our screen by fiddling with the Miscellaneous Output register.
4. As you may have noticed in the table of registers above, their is a bit called the "Protect Registers 0-7" and as it's name suggests, it write protects CRTC registers 0-7. We must first disable this before sending our data and then re-enable after we are finished.
The Mode X screen is arranged as four tables (planes), each of which is made up of 90 columns and 240 rows (for our 360x240 pixel resolution). The colour information for each pixel spans across all four planes of video memory. Each colour value is eight bits since we are allowed 256 colours and as a result, each video plane contains two bits of colour information for each pixel. To illustrate this properly, we definately need a diagram :-
+--+--+--+--+-------------------------+ Plane 3 |10|00|00|00|... | +--+--+--+--+-------------------------+ | Plane 2 |00|00|00|00|... | | +--+--+--+--+-------------------------+ | Plane 1 |11|00|00|00|... | | +--+--+--+--+-------------------------+ | Plane 0 |01|00|00|00|... | | ---------------------------------------The above diagram is not very good, but it should convey the general idea behind what is termed PLANAR memory addressing. This is the method used by Mode X and also by EGA/VGA 16 colour modes, so theory on this type of addressing should be fairly abundant. In the diagram, the pixel at coordinates (0,0) has a colour value of 10001101 in binary or 141 in decimal. Notice how this value is spread across the four video planes; bits 0&1 are on plane 0, bits 2&3 are on plane 1, 4&5 on plane 2, etc. The next pixel (at coordinates (1,0)) has bits 0&1 back on plane 0 and are stored directly after the first pixel's bits 0&1.
Now, since each byte of display memory is eight bits wide, and since each pixel has 2 bits of information on every display plane, that means that each byte of display memory indirectly manipulates 4 pixels. Some programmers were raving about Mode X's 4x speed advantage over Mode 13 because of this ability, but this increase can only be realised in certain operations.
One further point of interest is that each of the video planes occupies identical address spaces in the system address space. This means that, to manipulate the video data, we must inform the VGA which plane we wish to modify so that it can "bank in" that plane for use. Once the plane has been banked in, we can manipulate the information on the plane through normal memory addressing procedures. The important thing to note here is that only one plane can be modified at a time. This point should not be taken as FACT since it is possible to modify several pages at once to perform some advanced operations, but for now I would like to keep things simple.
Well I'm glad you asked, remember that 192k of missing display memory we discussed earlier, well here it is. A second important feature when working in this mode is a speed increase when doing certain operations. The VGA has a 32 bit internal latch which it uses to transmit data to/from the VGA/CPU. The latch is most useful when we are transferring data from one portion of video memory to another, since the data is transferred 32 bits at a time, and not 8 or 16 bits at a time, as is the case when transferring data to/from the CPU from/to the VGA. This makes it easy (and fast) to copy data from one area of video memory to another and in particular makes it easy to write "page-flipping" animation, but more on that another time.
Before explaining the page addressing/scrolling features, it would be handy to imagine the planes of video memory as four playing cards, one behind the other, thus occupying the same memory addresses (or space), though not at the same time. To access the data contained within any plane, you would first need to bring it to the front, since you couldn't normally see what was on it.
Keeping this in mind, recall that our planes only take up 16000 bytes each to make up a 320x200 pixel screen. The rest of the data (ie the other 48000 bytes) is actually off-screen and can either be paged or scrolled in using the same technique. Try to think of our 320x200 screen as a window on a much larger screen, ie a 320x800 pixel screen which is four times as large, taking up the whole 64000 bytes per page. The actual position that our "window" can be in is easily configurable through the VGA's SAL (Start Address Low) and SAH (Start Address High) registers.
+------------------------------+ + + |(0,0) address = A000h:0000h | | | | | Displayed | | | Screen | |(0,199) address = A000h:3E30h | | Total Video +------------------------------+ + Memory | . | | | . | | |(0,799) address = A000h:F9B0h | | +------------------------------+ +The above diagram hopefully explains what I am on about here. The "Displayed Screen" rectangle is the area of memory that is currently displayed on the screen and is 200 lines high. However we can shift where the top left corner of this window is actually positioned within video memory by changing the SAL & SAH registers.
To implement paging we simply divide up our video memory into four sections, each 200 lines high and set the start address to the top of each page. For example, page 0 would have a start address of 0000h, page 1 would start at line 200 and would have a start address of 3E80h or 16000 (80 bytes per line * 200 lines) The next two pages would have starting addresses of 7D00h (32000) and BB80h (48000) respectively.
Note that we only tell the VGA controller where the offset is in video memory, not the whole address. The VGA graphics screen still begins at segment A000h, however we are telling it to change where it starts reading in video data for display purposes. In our paging example above, our page 0 data is located at the absolute address of A000h:0000h, page 1 at A000h:03E80h, etc.
Consider what would happen if there was a delay between sending the low and high bytes to the VGA. During this delay period, the VGA may have an incorrect starting address value, since the high value is yet to be set up. Since the combination of SAL & SAH may point to an area of the screen which is somewhere off screen, during the delay the VGA will be displaying video information from the wrong source addresses. Once we send out the high byte, the VGA will be fine again and will be displaying data from the correct source and our screen will be as we expected it. You may think that this is ok since, "in the long run" we got what we wanted, but that unwanted data that was appearing on our screen during the delay period will appear as an unwanted flicker which is bad news for any animation.
To get around this problem, we wait until the screen is in it's vertical retrace period. This is the time when the monitor's electron beam (the one that lights up all those lovely pixels) moves from the bottom right hand corner of the screen back up to the top left hand corner. During this time, the electron gun is switched off, so that no dots on the screen are changed. This event happens about 60-70 times a second and this refresh rate is usually referred to in Hertz or hz for short. This means that a complete screen update will take from 1/70 - 1/60 of a second, quite fast indeed. Any routine that waits for the refresh period to do it's work must get its job done quickly, taking only as long as is required.
One further precaution that should be taken is the disabling of interrupts during our change of SAL & SAH. This is to ensure that we are not interrupted during our changing of these registers, since there is no guarantee that the interrupt routine will be finished it's processing before the VGA finishes it's retrace period. As a point of interest, this actual retrace period is called the Vertical retrace period since there also exists a Horizontal retrace period. This occurs after the electron beam has completed highlighting a row of pixels (including the border) and needs to move to the start of the next line.
Both the above ideas apply to many areas in VGA programming, most notably in palette cycling which is a fairly time consuming process since we need to shift 768 bytes from the system ram to the VGA registers during the refresh period. Although 768 bytes may not sound like much, copying them to the VGA card through the ISA bus controller chip is SLOW since the bus only runs at a fixed 8Mhz and is only 8 or 16 bits wide. Local Bus has helped a lot in this area, but not everyone has Local Bus yet. :-(
(The demo program is in the MX_SCROL.EXE file. I seem to have lost the source - Robert, contact me if you can! Phil).