Computer Labs 2013/2014 - 1st Semester
Lab 1: The PC's Graphics Card in Text Mode
Based on Lab by: João Cardoso and Miguel P. Monteiro


1. Objectives

The objectives of this lab are threefold. First to introduce you to the development environment and process that will be used throughout this course. Second, that you learn the basics of C language pointers. And third, that you learn how to use the PC's video graphics adapter in text mode, by accessing directly to its memory.

2. What to Do

Write in C language several functions for Minix to print characters on the screen, by directly modifying the video RAM area for text mode rather than using the I/O functions of the standard C library or of the POSIX standard.

Specifically, you shall develop the following functions whose protoypes are declared in video_txt.h:

  1. void vt_fill(char ch, char attr)
  2. void vt_blank()
  3. int vt_print_char(char ch, char attr, int r, int c)
  4. int vt_print_string(char *str, char attr, int r, int c)
  5. int vt_print_int(int num, char attr, int r, int c)/* Cannot use sprintf() or similar functions */
  6. int vt_draw_frame(int width, int height, char attr, int r, int c)

in a file whose name is video_txt.c. (Actually, you should use the file video_txt.c, which includes stubs for these functions as well as the implementation of another function, without which you will not be able to test your code.) The goal is to develop a generic module that will be used to create a library, which you will be able to use in the course's project.

All the code should be tested on the VM Minix 3 image we have made available for you. Therefore, all the code for this lab should be in put in directory /home/lcom/lab1 that you will have to create on Minix 3.

2.1 Class Preparation

So that you can accomplish this lab's objectives, you should implement the two first functions in the list above, i.e. vt_fill() and vt_blank(). Please check the section on coding/development below.

Since this is the first programming lab in LCOM, you should also read the overview of the development process with Minix 3, so that you can more easily use the development environment during the lab class.

3. The PC graphics card in text mode

By default, Minix 3 uses the PC graphics card in text mode with 25 lines each with 80 characters (that is with 80 columns). Each character may have different attributes such as foreground and background colors. Thus, each character takes 2 consecutive bytes of the video RAM: the first byte contains the code of the character to display, and the following byte its attributes

The code of the character depends on the codepage in effect. By default, Minix 3 uses the the US codepage, also known as CP 437, which is shown in Annex 1.

Figure 1 shows the meaning of the bits of the attribute byte:

bits

Figure 1: Meaning of the attribute byte bits.

Therefore, in text mode Minix 3 uses 25*80*2 bytes of video RAM, which usually starts at physical memory address 0xB8000.

Each character in the screen is mapped sequentially from the left to the right, and from top to bottom in video RAM. That is, the top line comes first, followed by the second line and so on until the last line. In each line, the left most character appears first, then the first character to its right and so on until the right most character. By writing to the appropriate positions of video RAM your program can display on the screen different characters with different attributes.

3.1 Mapping of Video RAM on a Process' Address Space

The physical memory address range used by VRAM is not usually available to user-level processes. So that a user-level process is able to access video RAM, it has to take some special actions that depend on the platform being used. For Minix 3 and privileged user-level processes this can be done as shown in function vt_init() in video_txt.c:

char *vt_init(vt_info_t *vip);
You need not understand the details: you can abstract that this function does the initialization steps required for this lab, and returns the address of the first byte of the process' address space region onto which the VRAM was mapped. However, if you are interested in learning more, you can read a short explanation of that function in the Annex 2.

3.2 Obtaining the information required by vt_init()

In order to perform the initialization of the video text module, vt_init() needs to know some parameters of the operation of the video card in text mode. These parameters include the physical address of VRAM, the number of lines and the number of columns of the screen. These are encapsulated in a data structure of type vt_info_t, which is defined in vt_info.h. The details of how to obtain these parameters from the video card in Minix are somewhat obscure, thus we provide a function that does that and another helper function that just prints the contents of the a data structure of type vt_into_t:

vt_info_t vt_info_get(); void vt_info_display(vt_info_t *info_p);

These functions are provided in a library whose name is libvt.a, so you need not worry with the details. Their prototypes can be found in vt_info.h.

3.3 Initialization of Privileged User Processes in MINIX 3

As described in the MINIX 3 notes, the programs that you'll develop in LCOM must run as privileged user processes so that they can access directly HW resources.

Privileged user processes in MINIX 3 are created by a special process, the ressurection server (RS), which is itself a privileged process. During initialization a privileged user process must synchronize with the RS. For simple privileged programs like this lab's this can be done just by including the function call:

sef_startup();

as the first instruction of main().

3.4 Summary

To summarize, the sequence of operations of the program to develop in this lab is as follows:

  1. Initialize the service, by calling sef_startup()
  2. Obtain the video card text mode parameters, by calling vt_info_get()
  3. Initialize the video text mode module, by calling vt_init()
  4. Modify video RAM, by calling the functions you'll develop for this lab.

Actually, you need not write the code that performs this sequence: you can find it in file lab1.c. All you need is to implement the functions listed in Section 2 which should do the actual modification of video RAM.

4. Accessing Video RAM in C

As we have already mentioned, each character on the screen is mapped sequentially from the left to the right, and from the top to the bottom to video RAM. Thus, to access VRAM in C, after mapping it in the process' address space, you can use C pointers. For example, to print char 'A' at the top-left corner of the screen, you can write

#define BLK_WHITE 0x07 /* White foreground on black background */ ... char *vptr; /* Pointer to video RAM */ ... vptr = video_mem; /* Point to first byte of video RAM */ *vptr = 'A'; /* First byte, is for the character to display */ vptr++; /* Point to second byte of video RAM */ *vptr = BLK_WHITE; /* ... which is for the attributes of the first character */ ...

If you want to print a character in an arbitrary position on the screen, you will have to use C pointer arithmetic in a slightly more sophisticated way than above.

Note To keep the prototypes of the functions vt_print_char(), vt_print_string(), etc. simple, they do not include the address on which VRAM is mapped nor the number of columns and so on as arguments. Instead, we have declared 3 static variables in video_txt.c, which are inititialized by vt_init():

static char *video_mem; /* Process address to which VRAM is mapped */ static unsigned scr_width; /* Width of screen in columns */ static unsigned scr_lines; /* Height of screen in lines */

By defining these variables as static we limit their scope to source file video_txt.c. Thus these variables are similar to class or object variables, which in many object oriented languages can only be accessed by the methods of a class; in this case by the functions in video_txt.c .

5. Coding/Development

Our suggestion is that you use Eclipse's RSE as outlined in Lab 0. More specifically, to create directory /home/lcom/lab1/ and to copy the files we provide you for this lab to that directory. Furthermore, you should use Eclipse's RSE to complete the functions already defined in video_txt.c.

You may be tempted using your preferred text editor on your preferred OS. In my opinion, there may be at least two drawbacks. First, you will have to move the files from your preferred OS to Minix 3 image on VMware Player every time you make a change to your code. Second, sometimes Minix's development tools have trouble with files created by some text editors on Windows. Often, you can configure these text editors not to use the offending characters, but how to do it is not always straightforward. If you choose to use a text editor on Windows, in spite of these warnings, I suggest you test them as soon as possible to avoid wasting your time.

If you have not yet read these notes with a quick overview of the development environment and process that you'll use in this course, now is a good time to do so.

6. Compiling your Program

Privileged user processes that access HW resources directly, must be compiled with special MINIX 3 libraries. The make utility can simplify this task. All you have to do, every time you need to compile your program, is to give the command as user lcom:

$ make

It will automatically invoke the compiler and other binary tools required to generate your program. But previously, you must have copied the Makefile and the libvt.a library with the vt_info_xxx functions to /home/lcom/lab1/.

7. Configuring your Program

Before you can run your program, you need to perform two configuration steps. These steps need to be performed only once, as they change the contents of files on disk.

7.1 Granting Permissions

Functions vt_init() and main() invoke functions that are privileged. Thus, to grant your program the permissions to execute those functions you need to add to /etc/system.conf.d/ the file lab1, whose contents is as follows:

service lab1 { system READBIOS PRIVCTL ; ipc SYSTEM # for retrieving system information rs # for service initialization vm # to map video RAM pm # for malloc, if used ; uid 0; };

7.2 Ensuring Exclusive Access to the Screen

Minix 3 supports the concept of virtual terminals (VT). I.e. it is possible to use Minix 3 as if it had more than one terminal. The first virtual terminal is known as the console and is used by Minix 3 to output debugging information. All terminals have their own shell, a command line interface, which can be used to issue commands to Minix 3. You can select the virtual terminal to use by typing Alt+F1, Alt+F2, Alt+F3 and so on. The default Minix 3 configuration, supports up to 4 virtual terminals.

In this lab, you will output to the screen of the 4th VT. I.e., the characters that you write to the VRAM should be visible when you select the 4th VT, by typing Alt+F4. In order to prevent interference from that virtual terminal shell, we will not run any shell on that terminal. This is done by editing, as root, the file /etc/ttytab and commenting out the line for ttyc3, i.e. by adding the character # at the beginning of the corresponding line, as shown below:

# ttytab - terminals # # Device Type Program Init console minix getty ttyc1 minix getty ttyc2 minix getty #ttyc3 minix getty tty00 unknown tty01 unknown ttyp0 network ttyp1 network ...

For this change to take effect in a clean way, you will need to reboot Minix.

8. Running your program

You are now ready to run your program. For that you'll need to give the following command as root:

# service run /home/lcom/lab1/lab1 [-args "<list of space separated args>"]

Where the [] delimit optional input. If absent, code in main(), in lab1.c, will display information on usage of the program. If present, the <list of space separated args> will be handled as the list of command line arguments of a standard user program and passed as command line arguments to your program. This option will be used in future labs for grading your work, as described below, and you should use it for testing. If the directory where you run service is /home/lcom/lab1 you can avoid typing it, by giving the following command instead:

# service `pwd`/lab1 [-args "<list of space separated args>"]

9. Grading your program

Starting with the next lab we will grade your progams, and we will use the -args option already described. The file lab1.c includes code that processes the arguments and invokes the functions specified in Section 2. If you wish, you can use it for testing your code in a way similar to the one we'll use to grade your code in the future.

If you use the lab1.c file provided and you want to test your code gradually, i.e. as you develop it, you should also use the video_txt.c file, which contains stub implementations of the functions called in lab1.c.

Annex 1: Codepage 437


(The Unicode equivalent code is displayed under each character.)
Retrieved from http://ascii-table.com/

Annex 2: Mapping VRAM in a Process's Address Space

In Minix 3, so that a user-process can access VRAM it has to map it in its own address space using MINIX 3 kernel call:

void *vm_map_phys(endpoint_t who, void *phaddr, size_t len);

The first argument, is a value that identifies the process on whose address space the physical memory region should be mapped. Of course phaddr specifies the physical address of the first memory byte in that region and len the region's length. This call returns the virtual address (of the first physical memory position) on which the physical address range was mapped. Subsequently, a process can use the returned virtual address to access the contents in the mapped physical address range.

vm_map_phys() is a MINIX 3 kernel call and a process must have not only the necessary permissions to execute that call, but it must also have the permission to map the desired physical address range. To grant a process the permission to map a given address range you can use MINIX 3 kernel call:

int sys_privctl(endpoint_t proc_ep, int request, void *p)

Again, the first argument specifies the process whose privileges will be affected, and the other two arguments depend on the privileges to change.

The code of function vt_init() that calls these functions and that initializes the 3 static variables in video_txt.c is the following:

#include <minix/drivers.h> #include <sys/mman.h> char *vt_init(vt_info_t *vi_p) { int r; struct mem_range mr; /* Allow memory mapping */ mr.mr_base = (phys_bytes)(vi_p->vram_base); mr.mr_limit = mr.mr_base + vi_p->vram_size; if( OK != (r = sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr))) panic("video_txt: sys_privctl (ADD_MEM) failed: %d\n", r); /* Map memory */ video_mem = vm_map_phys(SELF, (void *)mr.mr_base, vi_p->vram_size); if(video_mem == MAP_FAILED) panic("video_txt couldn't map video memory"); /* Save text mode resolution */ scr_lines = vi_p->scr_lines; scr_width = vi_p->scr_width; return video_mem; }

Note that in almost all programs, you'll need to include <minix/drivers.h>, which includes several other header files. The header file <sys/mman.h> is needed here only because it is not included by <minix/drivers.h> and it has the definition of MAP_FAILED.

A2.1 Finding Out the VRAM Physical Address

In order to obtain relevant parameters of the video display and its controller, you can use the libvt.a, a library that includes the implementation of the following functions:

vt_info_t *vt_info_get(); void vt_info_display(vt_info_t *info_p);

The type vt_info_t is defined in file vt_info.h. Its fields contain information regarding the video RAM and the text-mode configuration parameters:

vt_info_t *vt_info_get(); void vt_info_display(vt_info_t *info_p);