Video Scroll (part I)

In this post, I write about accessing Video RAM directly in mode 0x03 to create visual effects.

I am currently investigating the video component of a personal computer. After many experiments, I felt like trying to access the Video RAM directly to create some video effects on the screen. Of course, learning was my motivation, but I wanted to create a visual effect of a certain utility, rather than just playing around with experiments. I decided to try to reproduce, in a primitive way, the visual effect of a chatter such as Whastapp on mobile phones. It turned out that to create such a visual effect was quite hard indeed, and I couldn't do that just in one step so I decided to prepare an intermediate step. This post is about that intermediate step. As you will see, this intermediate step is already more than enough for learning how to access the Video RAM directly and create visual effects; however, I am determined to continue on this path and create a complete chatter-like text-user interface. This post covers just the first step (it is already a lot to write about) and as you will see, many things are implemented the way they are just because I have already a rough idea of what the next step will be.

I divided this post into a few paragraphs to organize the presentation of the material a little bit.

Check the character set

This paragraph is a preparatory step. I wanted to use some special graphic characters to create the visual effect that I had in mind and for this reason, I wanted to check the appearance of the different symbols on the screen. Once you download the file <scroll_test.zip> from the DOWNLOAD AREA and extract the archive, you will find the file <[...]\scroll_test\FontTable.asm>. This is the file that I have created for this purpose. I wrote the full table of all 256 characters (from 0x00 to 0xFF) in the section _justData of the program and the code simply copied it in the Video RAM region so that I could see on screen the full table.

Fig. A - FontTable.asm running on DEBUG.EXE for testing
Fig. A - FontTable.asm running on DEBUG.EXE for testing

Fig. B - FontTable.asm running on the bare metal PC
Fig. B - FontTable.asm running on the bare metal PC

As you can see from the comparison of Fig. A and Fig. B, not all character bytes produce the same graphical symbol. The reason is that the font table used to convert a byte into a graphical symbol is not the same in DEBUG.EXE and on the bare metal PC.

Description of the visual effect and memory organization

In text-mode 0x03, there are only 25 lines of text available on the screen and one normally uses just 4000 bytes of the 32 kb of Video Memory available to display only one page (read also the posts "Test The Video Modes" and "RAM or not"). I decided to extend virtually the screen to have more than just 25 rows and to use as much Video RAM as possible for the extra rows that I was planning to add but, of course, only 25 rows at each time could be displayed. I imagined that when the display scrolls to accommodate new lines on the screen, the old lines, which exit from the top, would be better stored rather than lost, in such a way that the user may recall them back on screen if needed.

Fig. C - memory organization
Fig. C - memory organization

I can continue the description of what my idea was with the help of Fig. C. I organized a virtual screen from address 0xB800:1020 to 0xB800:7FFF and containing 28640 bytes (0x7FFF - 0x1020 + 0x01 to account for the conversion from address to quantity) which divided by 160 bytes per each row of text on video (1 byte for the attribute and 1 byte for the character) returns a total of 179 rows. From 0xB800:0FA0 to 0xB800:101F there are only 128 bytes so it is not possible to have an additional line and, for this reason, I decided to use these bytes in memory as storage for variables required by the different procedures. The virtual screen with its 179 rows, exists in Video RAM but it is not visible on the screen. I copy and paste a portion of it from the blue box to Page 0, in such a way that it becomes visible. When the user presses the key "PgUp" or "PgDn", I change the position of the blue box accordingly and I copy and paste the Video RAM again thus producing the effect of a scrolling screen.

Before continuing with the description of my experiment, I'd like to share with you my opinion about the canonical representation of RAM addresses. The nomenclature about this is that RAM addresses lie on a vertical line whose origin (0x00) is on the bottom and grows upwards towards infinity. In this sense, the interpretation of the term "an address is lower (or higher) than another one" is that the address is smaller (or bigger) than the other one. I find the canonical representation bad because it twists the way things are. For instance, the word at address 0xB800:0000 (the red dot) is on the bottom-right corner of the canonical representation meanwhile it appears on the top left corner of the screen. The same is also true for the latest word on Page 0 (green dot 0xB800:0F0E). Furthermore, moving the blue box up and down to follow the command given by the "PgUp" and "PgDn" keys, appears to work in the opposite direction due to the canonical representation that sets the address 0x00 at the bottom. I would like you also to observe that if you execute a "d" command (stands for "memory dump") in DEBUG.EXE, the addresses are presented on the screen exactly in the opposite way of the canonical representation. The consequence is that the longer I explore the world of the bare bone PC, the more I feel the canonical representation to be very uncomfortable and against the natural way of thinking. For this reason, I feel like abandoning the canonical representation in future.

Talking back again about the experiment in this post, I want you to observe that the virtual screen that I created is just a little bit better than the standard screen because of the 179 lines instead of the just 25, but once the user needs to send more than 179 lines on the virtual screen, it behaves in the same way as the 25 lines screen: it scrolls the oldest line out of scope (out or Video RAM) to free space for a new line of text. You have realized by now that the scroll doesn't apply to the full history of lines but just to the latest 179 of them.

To conclude this paragraph, I present in short the file <[...]\SWBIG-LIB\video_scroll\scroll_mng_global_costants.asm>. The purpose of this file is to define constants that are used by all procedures. So, this is a file that is included in the build of the final program. Some constants defined in this file are used as such, so these are actual constants; other constants are addresses so these are to be used to access variables in RAM. Additionally, I placed in this file all the comments necessary to describe the organization of the RAM for this experiment. The black box in Fig. C that goes from 0xB800:1020 to 0xB800:7FFF, is what I named .EchoBuffer in the scroll_mng_global_costants.asm file. The blue box in Fig. C is what I named .EchoFrame. If you read carefully the <[...]\SWBIG-LIB\video_scroll\scroll_mng_global_costants.asm> you are probably wondering why I defined the variable .EchoFrmLength at all. In fact, if .EchoFrmLength is a variable, that makes the size of the blue box of Fig. C variable too. For this step, the size of the blue box is fixed and equal to 23 lines of text, but in the step coming next, I will need to change this value as the procedure runs.

Project organization

Fig. D - Project's organization
Fig. D - Project's organization

In Fig. D, I present the different procedures that I have developed for this step. I marked with the colour green the procedures that are going to be used for the next step and in sky-blue the procedure used to create the testing environment.

The only caller to the procedure get_scroll_bar_parameters.asm is the procedure update_echo_screen.asm so you may wonder why I have defined the get_scroll_bar_parameters.asm as a separate one rather than as a local procedure inside the caller. I plan to have a second caller for this procedure in the next step, and this is the reason why I have already defined it as a separate entity.

To draw the picture of Fig. D, I used the software yED. I find yED a good software to draw such diagrams and I really enjoy using it. Maybe, you would also like to try it, so you can find it on the following web-page: https://www.yworks.com/products/yed. Once you have the yED software you can also open the file <[...]\SWBIG-LIB\video_scroll\SW Architecture.graphml>.

The procedure set_VRAM_text.asm is responsible to write the string in the Video RAM in the so-called "echo buffer" (from address 0xB800:1020 to 0xB800:7FFF), and the procedure update_echo_screen.asm copies and pastes the Video RAM marked by the position of the blue-box of Fig. C into the visible Video RAM of Page 0. set_VRAM_text.asm also applies a certain formating to the string while storing it into the Video RAM: it sets the attribute byte and it squeezes the width of the screen inside a narrower range than the 80 columns of the screen. This ability to assign a colour and squeeze the message in a specific range of columns of the screen is the functionality that I need to recreate a chatter-like terminal. As Whatsapp does, the incoming messages have a different background colour and different margins left and right compared to the messages created and sent by the user. Thanks to the behaviour just described above, I think that I will be able to recreate this effect with the set_VRAM_text.asm procedure.

Why is the assembly code slow?

Fig. E - SOFTWARE.BIG runs inside DEBUG.EXE
Fig. E - SOFTWARE.BIG runs inside DEBUG.EXE

This question is a big puzzle for me, but let me tell you a little bit about the background. By this, I don't mean that my assembly code is slow (I think that I wrote an inefficient code indeed, after all, I am just a hobbyist), but rather that the very same code runs at a remarkably different speed in different conditions. As you know, I moved to a Windows 7 64-bit notebook. To keep my 32-bit environment with DEBUG.EXE, I have installed a virtual machine with Oracle VirtualBox inside which I have installed the Windows-XP 32-Bit version. During the development and testing phase, I was using the DEBUG.EXE from within the virtual machine with Windows-XP. Within this environment, the code was running fast. Once I finished the development, I rebooted the PC and my bootloader SHUTTLE21 launched the SOFTWARE.BIG2 in the space. I expect that the software had to run even faster on the bare metal machine instead, it was some 20 times slower!!! How is it possible that inside the win7-VirtualBox-Win-XP environment with multiple layers of code translation the software can run faster than on the bare metal PC?

Fig. F - SOFTWARE.BIG runs on the bare metal PC
Fig. F - SOFTWARE.BIG runs on the bare metal PC

As you know, my motivation for learning assembly is just because it helps me to understand how the computer works, but many people say that their motivation is because nothing runs faster than assembly. This last statement sounds plausible to me but then how is it possible that the same code runs 20 times faster when there are multiple software translations layers in between than when it runs on the absolute bare-metal PC?!? This is a question that I cannot answer and it would be nice if somebody, with more knowledge than the one I have, was so kind to leave a comment and solve this puzzle. For the moment, I can only say that testing the code on the real bare metal was worth it because it lets me detect such a strange and unexpected behaviour.

Comments