Computer Labs 2012/2013 - 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 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, int r, int c, char attr)
  4. int vt_print_string(char *str, int r, int c, char attr)
  5. int vt_print_int(int num, int r, int c, char attr)/* Cannot use sprintf() or similar functions */
  6. int vt_draw_frame(char *title, char attr, int r, int c, int width, int height)

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 other functions, 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.

2.1 Class Preparation

So that you can accomplish this lab's objectives, you should do some homework. As an incentive, 20% of the grade of each lab will depend on whether or not you reach some objectives early in the lab class -- that is you should accomplish the objectives before class, or be very close to reaching them, but you need some help for that. For this lab, the early objectives are to implement the two first functions in the list above, i.e. vt_fill() and vt_blank().

Because at this point in time most of you do not know how to use the development environment that we'll use, I've written some support code that emulates video RAM. You can find it in vt_info.c, and also in a video_txt.c. This code uses only standard C library functions, and therefore you should be able to use it in any platform that has C language development tools, i.e. a C compiler. In addition, to test your code you'll need to run it in some kind of virtual terminal. It has been tested on Linux with gcc and GNU make, using a terminal (emulator). Below, you can find more details about this emulation environment.

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 3.

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:

vdu_info_t get_vdu_info(); void display_vdu_info(vdu_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_get_info()
  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.

4. Accessing Video RAM in C

As we have already mentioned, each character in 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 the scope of the variables to 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 .

If you want to test your code in a platform different from Minix 3, you should read the the annex about the emulation environment prepared for this lab. Otherwise, you should read these notes for a quick overview of the development environment and process that you'll use in this course, and write your code by completing the file /usr/src/drivers/lab1/video_txt.c in Minix 3. You need not create new files or even change the other files in the /usr/src/drivers/lab1/ directory.

5. Compiling your Program

Privileged user processes that access HW resources directly, must be compiled with special MINIX 3 libraries. The make utility can make this task a lot easier. All you have to do is to copy the Makefile to /usr/src/drivers/lab1/ and give the command as user lcom:

$ make

6. Installing your Program

To install your program in /usr/sbin/ all you need to do is, to give the following command as root:

# make install

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 /usr/sbin/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.

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: Video RAM Emulation

In order to access video RAM your program must perform some special actions that depend on the platform being used.

So that you can develop your code as independently as possible of those special actions, and you can test it without having Minix installed, we have developed some code that for all purposes emulates video RAM as a two dimensional array of characters. This array is declared in file vt_info.c:

static char video_mem[NO_LINES][NO_COLUMNS*2];

This file includes also a modified version of vt_info_get(), and you should use it to test your code only if you are not running it on Minix 3.

Other functions also had to be modified (for more details, check the class notes), but we use the same source code files, independently of the platform being used. For this, we use #ifdef, #ifndef directives of the C preprocessor. Thus if you wish to compile your code so that it does not run in Minix 3, you should define the macro EMUL.

When this macro is defined, only code that uses standard C library functions will be compiled. Therefore, you should be able to run your code in any platform that has a standard C development environment. To simplify the compilation process, you can use this Makefile, which uses gcc as the compiler, and just give the command:

$ make

This process was tested in Linux. In other platforms, you may have to modify the Makefile, or even invoke the compiler manually.

Finally, to test your program you should run it in some kind of terminal. If you are using Linux, you should give the command:

$ ./lab1

This will output usage instructions to the terminal, and you can use them to invoke the program as required to test the desired function.

Annex 3: 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.

A3.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);