Test the video modes

I wanted to be able to use some colour when displaying messages on the screen, and I started to study which interrupts could help with it. The more I looked into the topic the more I realized how huge it was and I have to admit that this discouraged me a bit. I knew I couldn’t learn all of it, so I decided to reach some minimum level of experience: I would like to use the text modes of the Video Graphic Array (VGA) not only utilizing the BIOS Interrupts, but also writing directly in the video RAM. As an intermediate step towards this target I designed a new experiment that I am going to present in this post:

Some basic information about the standard video modes

Fig. A - Real mode memory map
Fig. A - Real mode memory map

As far as I understood, the address range from 0x0A_0000 to 0x0B_FFFF is used to map the memory of the Video Card into the address space that the CPU can reach (see Fig. A). In other terms, every time I address a byte in this range the CPU will not read or write from the RAM on the motherboard but rather directly from the Video-RAM on the Display Card. The address range is furthermore divided into three sub-ranges each of them used with a different purpose as described in Fig. A (highlighted with orange colour). I understood also that the same CPU address range can be mapped into many different Video Card memory ranges. Additionally, two addresses that are consecutive in the CPU address space (for instance 0x0A_0001 and 0x0A_0002) are not necessarily addressing two consecutive bytes in the Video Card RAM. It is also possible that one single address in the CPU address space is mapped to eight bits (for instance always Bit.0) of eight bytes in the Video Card RAM. I learned also that not always the complete CPU address range from 0x0A_0000 to 0xB_FFFF gets mapped. It can well be that, for instance, the CPU address ranges from 0x0A_0000 to _0xB_7FFF are not mapped at all, meanwhile the only mapped range is the one from 0xB_8000 to 0xB_FFFF (I saw this happening in colour text mode). In conclusion, I understood the first concept which is that the CPU address space is the access gate to the Video Card RAM but what gets mapped to the Video Card side of the gate can be manipulated. To me, it is something like the experience I do when I use an elevator of a hotel that connects the complete building from the garage thru the lobby until the panorama roof. I enter, I select a target floor and when the elevator cabin opens I can find a completely different environment every time.

I am currently learning that there are different possibilities to change the mapping of the Video Card RAM into the host CPU address space. Of course, it is possible to set the Video Card using the IN and OUT instructions and sending / reading data and commands directly to the Video Card Registers, but I am not able to do that now. The other possibility is to use the Interrupts to perform a complete setup of the Video Card within several predefined setups available for the Video Card. For this purpose, I used the INT 0x10 with AH = 0x00 and AL equal to the desired video mode to set. The first 0x13 modes are standard and I summarized them in Fig. B.

Fig. B - Standard video Modes
Fig. B - Standard video Modes

I created the table in Fig. B based on what I found on the Internet from many different sources1. I have tested some of these video modes (as you are going to read in this post) but not all of them and not in full details. I use the Fig. B as a kind of check list of the things that I would like to test by myself (if I can...) and I would like to enrich it with the experience I will made along the learning path.

I learned that the BIOS data area contains additional information about the current setup of the video mode. I summarized the table in Fig. C based on what I found on the internet2.

Fig. C - Video information in the BIOS data area
Fig. C - Video information in the BIOS data area

There are a lot of things to check and I created a test that changes the different video modes and then retrieves the information stored in the BIOS data area to display them on the screen. I collected a lot of information but I haven't analyzed all of them in every detail yet. What you will find in this post is the program that performs this analysis so that you can repeat the same on your own computer if you wish to do so.

Organization of the program

I am not going to describe in all details the source code that I created for this program, but I will highlight some aspects only. I expect that you download the file LearnVideoModes.zip from the DOWNLOAD AREA and use it. Even if I explained already in the previous post "How to make a SOFTWARE.BIG with NASM", I will repeat here some concepts since this occasion appears to me to be good for a refresh.

Fig. D - Organization of files
Fig. D - Organization of files

Once you extract the ZIP-Archive you will find the organization of files as in Fig. D. These are all text files except the STD-RUN.BIG which is a binary file necessary to test the SOFTWARE.BIG with DEBUG.EXE. LearnVideoModes.asm is the main file of the program. Once you build it, it pulls all other files into a single package and produces the SOFTWARE.BIG file in output. LearnVideoModes.inc is the file that enables the pulling of all other required files into a single package. When you will build the LearnVideoModes.asm file, NASM will produce another text file in output: LearnVideoModes.lst. There you can see how the complete package looks like one moment before NASM creates the SOFTWARE.BIG. All other remaining files inside the subfolder SWBIG-LIB are the service procedures used by the main program.

Fig. E - Hierarchy of procedures
Fig. E - Hierarchy of procedures

Fig. E shows the hierarchy among the different service procedures. I highlighted with the orange colour the calls towards BIOS service procedures (interrupts).

I wanted to test the different video modes so I prepared a program that worked with the following steps:

  1. get the current video mode immediately as soon as the program starts;
  2. set the video mode under test with the INT 0x10/AH=0x00;
  3. read all registers of CPU immediately after return from the interrupt;
  4. get the current video mode with INT 0x10/AH=0x0F;
  5. read all registers of CPU immediately after return from the interrupt;
  6. read the BIOS communication area;
  7. return back in video mode 0x03 and displays all information collected during the tested mode;
  8. ask the user what to do next (test another video mode or terminate).

The steps 4, 5 and 6 are redundant, but I wanted to acquire the same information from the different sources in order to be able to compare them and check that they were all returning the data about the current video mode in a consistent way.

Fig. F - Output of the program
Fig. F - Output of the program

Fig. F shows the output of the program. I prepared the strings for the yellow boxes with the sub-procedure _AP_PROBE. The concept behind this sub-procedure is similar to the Alien Planet Probe described in chapter II, but the source code is completely different since I was using NASM this time to create the program rather than DEBUG.EXE. I prepared the strings for the blue box with the sub-procedure _getBIOSComVideo which additionally needed to call two other sub-procedure to prepare the red boxes: _AX2UnsignDec and _JustifyRight. To be honest with you, I think that the program works perfectly good also without the need of justifying to the right the decimal conversion of the hexadecimal numbers. I created and used the sub-procedure _JustifyRight just for my own training. I wanted to learn how I could possibly solve the problem of justifying a string to the right.

This was the first program that I did in NASM using a certain kind of modularity. As a matter of fact, I was working around the linking stage with the expedient of including the source file of every sub-procedure within the main and then let the assembler (NASM) doing all the job. This technique avoids the use of the linker, but I had to be very careful with the name of the labels to avoid conflicts if, for instance, the same label-name was used in the source file of two different sub-procedures. I solved this problem with the use of the so-called local labels. Chapter 3.9 of the NASM Documentation, describes what local labels are very well. In simple terms, this allowed me to define labels that were local to each sub-procedure so that two local labels with the same name could exist in two separate contexts. Every time I opened a new sub-procedure file, I started it with the master label that was the entry point of the sub-procedure at the same time. For my own code style, I decided to start every master label with an underscore, for instance: _AP_PROBE:. All other labels within the file started with a dot so that NASM interpreted them as local labels. For instance: .Loop1:. The use of local labels is a writing convenience that NASM makes available for the programmer in such a way that he or she can refer to the label .Loop1: instead of having to write the full name such as _AP_PROBE.Loop1:. NASM rebuilds for the programmer the full name behind the curtains, but as soon as the programmer uses a new master label (such as _WRITEASCII) this creates a new local context for the local labels. The NASM syntax allows the use of multiple master labels and local labels within the same file and this is what happens when the include file brings all pieces of code together when building the main procedure. So I used this facility of NASM to my advantage in such a way to solve the problem of repeating names of labels in different sub-procedures.

Testing of the program with DEBUG.EXE

Fig. G - SOFTWARE.BIG in the test configuration for DEBUG.EXE
Fig. G - SOFTWARE.BIG in the test configuration for DEBUG.EXE

At lines 97, 98, 100 and 340 of LearnVideoModes.asm that you find in the ZIP-File, you will find code that has been commented out. I used this code to produce a version of SOFTWARE.BIG for the testing purpose in DEBUG.EXE. In the test configuration (as in Fig. G), SOFTWARE.BIG never executed the instruction at line 341, but once the tests were concluded, I commented the lines out to produce the final SOFTWARE.BIG that I used for the exploration of the alien planet3. As you know, the command p in DEBUG.EXE allows to run a complete procedure rather than stepping into it. So I recreated this CALL-RET wrapper around the main program in such a way to use the command p on the complete program too. The lines 97, 98, 100 and 340 are this CALL-RET envelop.

Fig. H - Testing setup in DEBUG.EXE
Fig. H - Testing setup in DEBUG.EXE

In Fig. H, I show the standard steps necessary to prepare the testing environment in DEBUG.EXE. To test the SOFTWARE.BIG, you have to load SOFTWARE.BIG at address 0x7C00 using the commands n and l. After that, you have to load STD-RUN.BIG at address 0x100. Once this preparation is done, you start the test by running STD-RUN.BIG, which then jumps and continues in the _general_reset routine of SOFTWARE.BIG4. I used to let DEBUG.EXE execute the first 12 instructions all at once with the command p c (marked in yellow in Fig. H). This saved me some tedious step by step until the point where the real test started.

Fig. I - SOFTWARE.BIG runs within DEBUG.EXE
Fig. I - SOFTWARE.BIG runs within DEBUG.EXE

In Fig. I you see the result of the p c command which brought DEBUG.EXE until the last jump of the _general_reset routine (I marked this in blue). From this point on, I continued with a single p command. The next following instruction was the CALL (marked in yellow) that wrapped the complete core of SOFTWARE.BIG into a CALL-RET envelop which is the trick that I used to see the full program running in DEBUG.EXE. At this point the complete core of SOFTWARE.BIG was running in DEBUG.EXE without interruption. During my tests with DEBUG.EXE, I discovered some limits of the simulation environment in Windows 7: when I reached the mode 0x04, Windows 7 popped up a window and killed DEBUG.EXE.

Fig. J - Error message in Windows 7
Fig. J - Error message in Windows 7

In Fig. J you can see the message that occurred with Windows 7. As you may remember, I also have a second test PC with Windows XP on it, so I tried the same there and I realized with a positive surprise that Windows XP switched to full screen mode and continued the test through the other different video modes without any problems.

Data collected from real runs of the program

Fig. K - Test over the standard video modes
Fig. K - Test over the standard video modes

In Fig. K, I summarized some results of the tests done over the standard video modes with the two different test-rigs that I have: The IBM T41 and the HP EliteBook. I observed that not every video mode was available with the IBM T41 (I marked such modes in orange). When I attempted to set one of these video modes the reaction was that the Video Card remained in the current video mode.

Fig. L - Video mode 0x08 failed
Fig. L - Video mode 0x08 failed

I use Fig. L as an example of the behaviour that I experienced for all video modes that I marked with orange colour (in Fig. K, but also in Fig. N). As you can see, I attempted to set the mode 0x08 with the INT 0x10 and AH=0x00, AL=0x08 (AX=0x0008) but nothing happened and the Video Card remained in the mode 0x03. In fact, when I queried the BIOS to get the current video mode with INT 0x10, AH=0x0F, I got back AL=0x03. Similarly, the BIOS Data Area contained the parameterization corresponding to the video mode 0x03 (i.e. the video mode that the Video Card was in when the CPU called the INT 0x10 /AX=0x0008 attempting a switch of the mode).

Fig. M - Video mode 0x05 failed
Fig. M - Video mode 0x05 failed

Fig. M shows a different kind of failure that I experienced during the test. Some video modes produced just a black screen (I marked them with the yellow colour in Fig. K and Fig. N), but both the INT 0x10/AH=0x0F (Get current video mode) and the BIOS Data Area were such as if this mode was effectively working. In other terms, from the point of view of the program that was running, everything was fine: there is no means to detect this failing video mode programmatically (or at least I didn't find a way to detect programmatically such kind of unavailability of the video mode, so if you know how to solve this puzzle, please write it in the comment). I assumed that perhaps if the monitor of the laptop went blank but the parameters were right, probably the video card was sending the signals to the VGA output instead of the display of the laptop. Based on this assumption, I repeated the experiment with an external monitor connected to the VGA output but again I got that both screens (the external monitor and the laptop one) were just black meanwhile the BIOS parameters were set correctly.

Another difference between the IBM T41 and the HP EliteBook was that in video mode 0x13 the size of the memory buffer (0x0_044C of BIOS Data area) were different: 0x2000 for the IBM and 0xFA00 for the HP. I don't know the reason for this difference and what is going on behind the curtains in this case and I have to research further to find it out. However, if any of you readers of this blog have the solution for it, please write it down in the comments. I will appreciate that.

As far as it concerns the Equipment list (2 bytes at 0x0_0410 of the BIOS Data Area)5 the bits 5 and 4 tells which video mode is active. I had a wrong understanding of the equipment list before these experiments. I thought that the equipment list was telling information about the actual hardware that the BIOS had detected. In other terms, I thought that if a colour display was attached to the motherboard, the BIOS was detecting it and the information was saved in the Equipment list telling exactly that a colour display was available. Based on this wrong understanding of mine about the equipment list, I was surprised to see that it was possible indeed to set the video modes 0x07 and 0x0F and that the two bits of the equipment list were changing accordingly to reflect the new setup of the selected mode. So it is not about the list of the equipment that I have available, or at least it is not so as far as it concerns bits 4 and 5. But, if the experience tells that these two bits can change every time (as I did) depending on the setting, then the description: "Initial video mode" used for these two bits is also wrong. I mean that it is not about the initial video mode, but it is about the current video mode.

Since I programmed the software to test beyond the standard video modes I continued with my test until mode 0x5F. I knew that there is no standardization for the video modes above 0x13, so I have not attempted to produce a table such as the one in Fig. B. The "Ralf Brown Interrupt List" offers an extension of the description for the modes beyond the 0x13 at table #00010. Honestly, I have not compared the results of my experiments for the extended modes with that table: I was not as rigorous as I was for the standard video modes. I tested the extended video modes "just for fun" so to say.

Fig. N - Tests on the extended modes
Fig. N - Tests on the extended modes

In Fig. N I summarized the fast test that I did with them. I discovered some additional text modes with more than 80 columns and 25 rows and, very surprisingly, I discovered that the IBM T41 displays turned green for all modes above 0x40 (you can see examples in Fig. O and Fig. P).

Fig. O - Mode 0x40 on IBM T41
Fig. O - Mode 0x40 on IBM T41


Fig. P - Mode 0x4B on IBM T41
Fig. P - Mode 0x4B on IBM T41

As I said at the beginning of this post, this topic is really huge and I am currently just gently scratching the surface of the iceberg. I still have many things to learn, but for the moment I am very happy that I could conclude this space mission with success and discover a small new aspect of this alien planet which is the bare bones x86 computer.

Comments