RAM or not

During the preparation of the post "Test the video modes", I collected information from the internet and I did some very quick and dirty tests to check whether I understood it right or not. At that time, I felt already that I had to prepare a new space mission to explore the x86-bare-bones alien planet and dug a little bit deeper. In this post, I write about the experience I made about it and I present the topic dividing it into two paragraphs:

Analysis of results

Fig. A - Detected address space
Fig. A - Detected address space

Fig. A shows the result of my tests. I marked the modes 0x04, 0x05 and 0x06 in yellow and orange since these modes produced just a back screen with the HP Elitebook. I didn't report modes from 0x08 to 0x0C since these modes are either not available or produce just a black screen (see also Fig. K of the post "Test the video modes").

As you can see, I could distinguish between "RAM" and "not RAM", which can be considered as "ROM" in 99,99% of the cases. The reason why I labelled it "not RAM" rather than "ROM" is that I didn't find a way to distinguish between true ROM and not installed memory. The program that I wrote, determines whether an address is a RAM trying to write a value in memory and reading it back. If the read value corresponded to the written one, then I knew that the tested address was RAM while in the other two cases (either ROM or not installed memory) there was a mismatch between the written value and the read one. I asked myself how could I possibly distinguish ROM from not installed memory. I imagined that reading from an address where no physical memory was available could probably return a value of 0x00, but even so, how could I possibly tell apart not installed memory from a byte of ROM whose actual value was 0x00? I couldn't solve this puzzle, and it was also not really important for me to tell apart ROM from not installed memory so I decided that it was ok for me to think at these addresses and consider them as ROM but, for the sake of precision, I labelled them as "not RAM" rather than ROM.

From now on, you should have a look back to Fig. A and Fig. B of the post "Test the video modes" and keep them in mind since I am going to compare them against what I found with my experiments.

The first column of Fig. A shows that the address range from 0x00_0000 to 0x09_FFFF belongs to RAM for both the IBM T41 and the HP Elitebook.

The range from 0x0A_0000 to 0x0A_FFFF is not mapped to any Video-RAM in modes from 0x00 to 0x07 and it gets mapped in the graphic modes from 0x0D to 0x13. As far as I know, there is no flag, label, interrupt or any special value in the BIOS Data Area to check if a specific video mode is a graphic or a text one. I have the video mode code and the table. By getting back the code of the video mode and reading the table I know if a mode is a graphic or a text one. This was a little bit unsatisfactory for me since all modes above 0x13 are not standardized and, meanwhile a lot of information can be found on the internet, there is no official table that can tell me back if a non-standard mode is a text or graphic one. I wanted to find a way to determine if a video mode was graphic or text one programmatically and I imagined that I could have looked at the range 0x0A_0000 to 0x0A_FFFF and tell by its behaviour (like "RAM" or "not RAM") whether the video mode was a graphic one or not. Unfortunately the modes 0x04, 0x05 and 0x06 don’t follow this rule: these are graphic modes and they use the address range from 0x0B_8000 to 0x0B_FFFF which is typically used by the other standard text modes (see also Fig. B of the post "Test the video modes"). In conclusion, I cannot extrapolate a general rule for the determination of the mode (either graphic or text one) just looking at the range from 0x0A_0000 to 0x0A_FFFF since the modes 0x04, 0x05 and 0x06 don't follow this rule.

The range from 0x0B_0000 to 0x0B_7FFF is used only in mode 0x07 which is a monochrome text mode. This behaviour is as I expected, but it differs a little bit from the mode 0x0F which is as well a monochrome mode but also a graphic one. So, it seems to me that, the branching graphic or text is stronger than the branching colour or monochrome when this has to tell how the address space maps the Video-RAM of the graphic card (I marked it with blue colour in Fig. A).

In Fig. A of the post "Test the video modes", I presented the Real-Mode Memory-Map. I built it with information that I found on the Internet, but, as usual, I always like to test by myself for double-check. I could confirm that the Video BIOS ROM is from address 0x0C_0000 to 0x0C_7FFF but the BIOS expansion area, which contains ROM and RAM (from 0x0C_8000 to 0x0E_FFFF), is organized in a different way on my two test computers: in the IBM I found 16 KB RAM and then 64 KB "not RAM" and I found the opposite on the HP (see Fig. A).

In conclusion, I learned how different portion of the Video-RAM get mapped into the CPU address space depending on the currently active video mode, and the remaining Video-RAM mapping region remains unusable as if it was not installed RAM. This is a very important point that I want to make here thanks to my experiments. I have read on the internet wrong information about this topic as someone writing that the 32KB RAM space from 0x0B_0000 to 0x0B_7FFF can be considered as "free RAM" and used by programs if needed. The argument used was that since monochrome terminals don't exist anymore, one may use this address space for other purposes such as data of a program. This statement is wrong because I have tested that this portion of the address space is either properly used in video mode 0x07 or not mapped (hence not available at all) in all other video modes.

The program that I developed and used for the tests

Fig. B - Calling hierarchy
Fig. B - Calling hierarchy

Once you download the file "LearnVRAM.zip" from the DOWNLOAD AREA and extract the archive, you will find the familiar structure1 of this small project. I present in Fig. B the calling hierarchy among the procedures (each procedure is into a separate file). This project is very close to the previous one. Basically, I replaced the job of the local procedure _getBIOSComVideo (see Fig. E of the Post "Test the video modes") with a new local procedure _scnAddr. _scnAddr analyzes the address space and prepares an output string as the result of its job but _scnIndex is the procedure that performs the actual check of the address space. _scnIndex returns a flag in the register AX: 1 if the range was RAM and 0 otherwise. As you will read in the comments of the programs, I decided to solve the problem arising from the fact that many different segmented addresses corresponded to the same flat address with the use of just flat addresses instead of segmented ones. I imagined that I will probably have to convert segmented addresses into flat ones also in future, so I decided to write a separate sub-procedure (_seg2flat) in such a way that I can have it ready for use in my library.

I learned from the previous experience made in the Post "Test the video modes" and I decided to create a call into the MAIN program in such a way that I could test in DEBUG.EXE and run on the real bare-metal PC using the same source code.

Fig. C - Call to MAIN as workaround for testing with DEBUG.EXE
Fig. C - Call to MAIN as workaround for testing with DEBUG.EXE

If you compare Fig. C here with Fig. G of the Post "Test the video modes", you will notice that in the past I used to have additional lines of code to produce a test version of the program for DEBUG.EXE. After that, I used to comment out these additional lines. I found this technique annoying since I had to do a couple of recursions until I got the final program and every time I had to comment in and out the lines to produce alternatively the test version for DEBUG.EXE and the one that got booted.

I encourage you to read all the comments in all the files of this project in order or see which kind of problems occur when you want to test if an address is RAM or not, but I want to add something also directly here in the post. The way I determined if an address was RAM or not, consisted in reading the original value of the byte at the given address, and then changing it, checking if the change was applied and then restoring the original value back again. Of course, if I was able to change the original value, then this address had to be RAM. I developed _scnIndex and tested it in DEBUG.EXE up to the point that I thought it was all right, but then it was crashing on the bare-metal PC. It took me a while to understand the reason and fix it. The problem was that when the program was modifying a critical byte for the correct working of the PC during the scanning, then the program was crushing. Critical bytes are in the address range from 0x00_0000 to 0x00_04FF. Here is the Interrupt Vector Table and the BIOS Data Area. The PC may crash if an interrupt occurs while the scanning is running in this area. One way to solve it is to switch the interrupts off during the scan of this address range with a CLI instruction, but alternatively, I just considered that this range is always guaranteed to be RAM so I could avoid actively scanning it.

A second critical RAM range that cannot be modified without crushing the program is the address range where the very OPCODES of the scanning procedure are. I didn't realize it at the very beginning until I discovered this problem during the testing. In practice, the scanning was modifying its own source code and inevitably crushing. I solved this issue by paying attention to check the address range where the procedure _scnIndex was loaded in RAM and skipping active testing of this range. Actually, I did something more: I considered everything to be RAM from address 0x00_0000 until the actual loaded position of _scnIndex thus conceptually merging in the same logical group all the critical RAM areas (the Interrupt Vector Table, the BIOS Data Area and the OPCODES of _scnIndex).

To conclude this post, I present here the screenshots of the running program. Figures from Fig. D to Fig. G show the steps within DEBUG.EXE used for the final test. Fig. H shows the result on the real PC (the HP Elitebook in this case). You may have noticed that the complete address space is RAM within DEBUG.EXE (Fig. G) and that the programs can detect "RAM" and "not RAM" once it runs on the real hardware (Fig. H).

Fig. D - Loading the code into DEBUG.EXE
Fig. D - Loading the code into DEBUG.EXE

Fig. E - Debugging the code with DEBUG.EXE
Fig. E - Debugging the code with DEBUG.EXE

Fig. F - Debugging the code with DEBUG.EXE
Fig. F - Debugging the code with DEBUG.EXE

Fig. G - Debugging the code with DEBUG.EXE
Fig. G - Debugging the code with DEBUG.EXE

Fig. H - LearnVRAM runs on bare-metal PC
Fig. H - LearnVRAM runs on bare-metal PC

I hope that you enjoyed the post and that you may have found useful information.
See you soon.

Comments