Writing Custom Display Fonts

Andrew describes a TSR he's written for the EGA that automatically replaces the standard screen font with a resident custom font.


March 01, 1988
URL:http://www.drdobbs.com/architecture-and-design/writing-custom-display-fonts/184407917

Figure 1

Figure 1

Figure 1

MAR88: WRITING CUSTOM DISPLAY FONTS

WRITING CUSTOM DISPLAY FONTS

A custom font for the EGA in Turbo C with source code for a resident program that makes the font permanent

Andrew J. Chalk

Andrew J. Chalk is president of Magna Carta Software, P.O. Box 475594, Garland, TX 75047.


Innovative screen displays sell software, which is one reason why so many developers provide demonstration disks, often free. One thing that makes a program distinctive is having a custom typeface for the text that it displays. Until the advent of the EGA, this was a luxury reserved for programs that operated in graphics mode.

Developing a nongraphics application to operate in graphics mode (for example, Framework in its EGA two-color graphics mode) is more costly than using the strong text mode support in the PC, however. Furthermore, prior to the EGA, graphics (with the exception of Hercules) were too poor to be very attractive. The EGA was such a rarity a year after its introduction that Peter Norton could write in his authoritative 1985 Programmers's Guide to the IBM PC that "we won't be discussing the 64-color palette of the EGA/ECD combo because it's quite rare and specialized and doesn't really fit into the mainstream of the PC family" (page 77).

This situation changed with the advent of EGA clones from Chips and Technologies and others. At the present time, a no-frills EGA card sells for around $150 and will probably cost around $100 before the end of 1988. At least for a while, the EGA will be the de facto video standard, so it behooves developers to take advantage of its special features.

In this article I show how to exploit one of the interesting features of the EGA--the ability to replace the standard character font in text mode with one of your own choosing. As an example, I use an italic font and provide source code for a TSR that loads the font into RAM so that DOS and (most) application programs can use it. The TSR is necessary because the EGA reloads the ROM character set when the video mode is changed. By intercepting the BIOS for video mode changes, you can reload your custom font in its place. The TSR can be deinstalled, freeing up the 17K of memory it occupies for other programs and preventing incompatibilities.

The source code is written in C (Borland International's Turbo C, to be precise), so this article also serves a second purpose--explaining some of the techniques for writing resident code in high-level languages. As I will explain later, I have concluded that this is basically a bad idea. If the on-line services and bulletin boards are good indicators, however, it is a subject of great interest, so it is worth spreading the techniques just so that others can become similarly disabused of the practice.

In order to use the code included here, you first need to understand fonts on the EGA. Then I'll talk about the C implementation in the source code. After that I'll discuss the TSR aspects of the source code and why you should write TSRs in assembly language.

Fonts on the EGA

Programmers writing for the monochrome display adapter (MDA) and the color graphics adapter (CGA) are stuck with the character sets provided in those board's ROMs. If you want a different character set, say as users of APL do, you have to replace the ROM. On the EGA, things are different. The fonts are "soft," meaning that although the ROM character generator is used by default, it can be replaced by a character set of your choosing. In fact, the EGA can support four character sets in what IBM calls four different blocks. Normally, you use block 0.

The EGA has BIOS support for the loading of an alternate character set through interrupt 10h, function 11h, subfunction 0. You make a call to this function with ES:BP pointing to a table containing your font (in a format I will explain), DX set to the ASCII ordinality of the first character in your character set, CX set to the number of characters in your character set (maximum 100h), BH set to the number of bytes per character, and BL set to the block to load (usually 0). These parameters provide the BIOS with sufficient information to load your font because of the way that fonts are stored.

Figure 1, below, shows a letter g as a magnified version of its screen image and as a stored character in memory. The figure assumes an enhanced color display in 25-line mode, in which case each character is 14 scan lines high and 8 pixels wide. A 25-line screen therefore fills 14 x 25, or 350 scan lines, as does the EGA. On the monochrome display, a 14 x 9-character box is used, and on the regular color display, the CGA 8 X 8-character box is used. The 43-line mode that is popular on the EGA driving an enhanced color or a monochrome display is achieved by loading the 8 X 8 ROM character set (because 8 X 43 is 344, just less than the 350 scan lines available). BIOS support exists for this, too, although two bugs in the original EGA BIOS make its implementation too big a subject to digress into here.

Figure 1: EGA character representation on the screen and in memory.

Let's assume that the EGA is driving an enhanced color display. In this case, each character is 14 scan lines high and 8 pixels wide. Representing this in RAM is simplified by the fact that each pixel that forms part of a character can be considered to be either "on" or "off" when a given character is on the screen. This means that the state of each pixel can be represented in binary by 1 bit. Furthermore, the designers of the EGA seem to have chosen a character width of 8 because this permits each character-scan line to be represented by exactly 1 byte.

In Figure 1, the hexadecimal values of each scan line are shown above the character. The arrows that lead in the direction of memory show that the letter g is stored as 14 contiguous bytes of data. Because g has an ASCII value of 67h, the characters next to it are the ASCII values 66h and 68h. The latter of these is h.

When you load a custom font into the EGA, you tell the BIOS, through ES:BP, the address of a buffer containing your chosen characters. If you want to load a complete character set, this buffer is 3,584 (14 X 256) bytes long. The EGA lets you load fewer than 256 characters, and it lets you choose the starting position in the ASCII sequence. The sequence of characters for any one load operation must be consecutive members of the ASCII character set, however, or you will get garbage on the screen.

Note two important limitations of the EGA font features. First, characters have a fixed width (but may vary from 1 to 32 scan lines high), so you do not have the same flexibility as in graphics modes. Second, although your 14 X 8 fonts work fine on a monochrome monitor in 25-line mode, a different character set is required if you support different numbers of screen lines. Suppose, for example, you were going to incorporate a feature into a database such that the user could press a hot key and immediately switch into 43-line mode and thereby see more records in "table view." If you had a custom typeface, you would need a 43-line-mode equivalent of it that would be loaded at the same time.

Notwithstanding these limitations, the custom font capability of the EGA is impressive. As I stated earlier, a video mode reset restores the ROM character set, so your application should perform a reload operation every time it performs a video mode reset. Furthermore, if you want to load your custom fonts only in certain video modes, you should check the video mode before loading. The example program ITALIC.C in Listing One, page 50, only loads the custom character set in modes 0-3 (text modes) and 7 (monochrome).

A Resident Italic Font

The example program consists of two parts: ITALIC.C is the source code, and ITALIC.ASC Listing Two, page 52) contains the code for the font in a form I will explain shortly.

The program first checks the system configuration to see if custom fonts are supported. This is done by means of the function get_video_info() which first checks for the presence of an EGA in the system through the recommended BIOS call (function 12h). If an EGA is not present, the value of BL is returned unchanged in which case you exit with a message explaining to the user that an EGA is required. There is a practice in the literature of checking for the IBM signature in the EGA BIOS. Not only is this kludgy but it is also specifically disapproved of in the EGA BIOS listing.

It is not enough to know that an EGA is present--it must also be active. The user may, for example, have an EGA driving a color monitor and an MDA driving a monocbrome monitor and be using the MDA. To see if the EGA is active, you check that bit 4 of the EGA information byte in the ROM data area at 0:0487h is 0. If not, you exit with a message to the user explaining that the EGA must be active.

If space had permitted, I would have included code to save the video configuration and switch adapters. The saved information could then be used to restore the ex ante state of the machine on exit. For the same reason, I have also omitted code to detect a switch of adapters while ITALIC is resident. Be warned: ITALIC illustrates a technique; it is not a full-blown professional program.

Having determined that an EGA is active, you then determine whether it drives a monochrome or a color monitor. The value of BH is 1 if monochrome and 0 if color. The result is used to set the global variable ega_color appropriately. If a color monitor is in use, you test for an enhanced color display. If it's not present, you exit with an explanatory message because the EGA will use the 8 X 8 font on a regular color display and my example requires 14 scan lines per character.

If the system checks out, you return to main() and set the video mode based on the type of monitor in use. You then check whether a copy of the program has already been installed. This involves scanning down through memory from the program segment prefix (PSP) for two identification words inserted in the global data area near the top of the program. These words are our_d1 = FACh and our_d2 = 1000h. The function already_installed() first peeks at the offset of our_id1 with the segment value equal to 1 less than the PSP. The segment value is then decremented until 0 is reached or a match is found. If a match is found, the next word is peeked at and compared with our_d2. If you assume that all characters are equally likely, the chance of erroneously concluding that ITALIC is resident when it is not if you load hallway up memory on a 640K machine is 1 in 13,158. If you are uncomfortable with this, you can lengthen the odds by searching for more than two words.

If you find a copy of ITALIC, you deinstall it and print a message telling the user. The mechanics of deinstalling a resident program are explained in the next section. If ITALIC is not present in memory, you can proceed with installation. There are three distinct phases of this process.

First, you only want to replace the alphanumeric characters in the ASCII set. Box-drawing characters simply don't draw boxes if they are italicized. My strategy is to load a copy of the whole 14 x 8 ROM character set into the buffer fontarray and then load only the alphanumeric characters from the data file ITALIC.ASC. The advantage of this is that your .EXE file need not be swollen with unnecessary data for characters that do not differ from the ROM 14 x 8-character set. Retrieving the ROM character set is easy thanks to BIOS support and is accomplished in get_egafont(). Next, a for() loop overlays the ASCII characters 32 through 127 with italic characters.

If you refer to the listing of ITALIC.ASC, you find that the italic font data is set up as a two-dimensional array with each row consisting of the appropriate hexadecimal values for a single character. The listing shown is actually the output of FONTEDIT, a full-screen, real-time EGA font editor included in C Windows Toolkit (see the "Availability" section at the end of this article). Don't worry about typing the listing; download details are given at the end of the article.

The second stage is the loading of your font (telling the EGA to use it) using interrupt 10h, function 11h, as described earlier. This is accomplished by the function load user_ega_font(). At this point the screen display immediately changes to reflect the new font.

The third stage is the resident installation of a replacement interrnpt handler for interrupt 10h so that you can detect video mode changes and reload your font if necessary. First, you save the PSP and environment pointer in the PSP at offset 2ch to use for later deinstallation. Next, you save the old interrupt 10h address using getvect() and install your own handler with setvect(). Finally, you terminate and stay resident (more on this in the next section).

The new font affects every character on the screen. The italic font presented here is probably not distinct enough for serious text work, but it could be edited to be so. The italic font in Microsoft Word is created through exactly the same kind of techniques. Other fonts along the lines of the large selection supplied with the Hercules Graphics Card Plus that are practical fonts for text-intensive work are also possible.

Programming Resident Programs in High-Level Languages

As I stated in the introduction, the experience of writing this (simple) TSR in C has lead me to conclude that such programs are best written in assembly language. There are several reasons for this. Perhaps the most important one is the sheer size. ITALIC occupies 13K RAM, of which 3,585 bytes are the font buffer, 1,344 bytes are the replacement font data, less than 100 bytes are for other global data, and 512 bytes are for the .EXE header. The remaining 11,500 odd bytes are "code." It is this portion that assembly language could shrink down to perhaps 3,000 bytes (I have not done it for this program). A second reason in favor of assembly language is the irrelevance of the portability issue.

An argument often legitimately raised in favor of high-level languages is their advantage in terms of development time. In fact, for resident code, even given that I was working without the benefit of a large literature on the ins and outs of programming TSRs in high-level languages such as exists for assembly language, so many problems resulted from having a compiler between me and the machine that I spent a lot of time inside the debugger. Assembly-language TSRs are easier to debug.

It can hardly be argued that the problems arose from the choice of language. Wasn't it the low-level links that made C so popular as an application language? It is also difficult to argue that the choice of compiler was the problem. Turbo C has library support for all the function calls associated with resident code. Although it lacks a built-in debugger at the time of writing, Periscope does an admirable job. In-line assembly language is also available, but the objective here was to not use a single line of in-line code (that isn't really writing a ThR in a high-level language).

It might be argued that programming TSRs obviates the need to learn assembly language. The last person I would recommend to write a ThR in a high-level language is someone who did not know assembly language. Ironically, the alleged portability of C code to different compilers (because it is a high-level language) becomes the opposite with TSRs. Because the generated code differs across compilers, code that runs flawlessly compiled with one compiler can produce subtle and hard-to-track bugs on another. The truly "safe" code becomes the one that is "immune" from portability virtue--assembly language.

These objections aside, here is how you implement a simple resident program in Turbo C. Bear in mind that this program does not access the disk or the keyboard, so many TSR techniques are not discussed. For a fuller treatment I recommend Turbo C: The Art of Advanced Program Design, Optimization and Debugging by Stephen Randy Davis. I have no doubt that ITALIC could be implemented more efficiently in C than I have donc here, but the relevant question is how these improvements compare with the real alternative-assembly language.

First, consider the way that you would implement this TSR in assembly language. Near the top of the source code would be the resident section, preceded by a jump to the installation section, which you would jettison when the resident part was installed. In C you perform the same installation steps of saving the old interrupt 10h vector and installing your own.

A problem arises when you wise to terminate and stay resident Turbo C contains the keep() function, which implements interrupt 21h, function 31h, and requires the number of paragraphs of memory to reserve as one of its parameters Whereas this is simple to compute in assembly language, it is not so in high-level languages generally or in Turbo C in particular (the Turbo C manual does not even mention this problem).

The method I used in ITALIC was discovered by Dean McCrory and generously posted by him on CompuServe. Turbo C uses two interna variables: _psp (to contain the PSI address) and _brklvl (to contain the address of the end ot the initialized and uninitialized data). As memory is dynamically obtained and released, _brklvl is adjusted accordingly. If you visualize Turbo C memory allocation as in the diagram for the small model in the User's Guide (that is, the data segment follows the code), then adding _brklvl to DS and subtracting the PSP address gives the size of the code and data. That is what I do in the keep() statement at the end of main.

Although this is ingenious, you should be aware of some potential problems and limitations. First, this will not work in the large data models (compact, large, and huge). Second, I do not know what happens, but you must presumably not farmalloc() any memory you wish the resident program to use. Third, this is undocumented and should be considered not fully tested.

The other part of the TSR code is the deinstallation routine, including the deallocation of the program's memory. This practice appears to be little known in both C and assembly language (it is certainly not officially documented). It probably enhances the value of a TSR to the user if it is removable, however.

The first thing you do is restore interrupt 10h to its original value. Next, you must deallocate memory. Deallocating the space occupied by a resident program is as easy in C as in assembly language, and the technique is the same. You must remove the program itself and its copy of the environment. In order to do so, when you first load the TSR, you must store the PSP and the contents of the environment pointer, which is located at offset 2ch in the program's PSP.

When you try to install ITALIC, already installed() finds a match and stores the data segment address of the installed copy in the global variable old_ds. When you deinstall the program, you peek at old_ds, offset by the address of old_psp to get the PSP, and then you repeat this using the offset of old_env to get the environment address. Next, you deallocate the program memory using interrupt 21h, function 49h. ES must hold the address of the installed program's PSP. Finally, you repeat this function call with ES pointing to the installed program's copy of the environment.

It is instructive to run CHKDSK before and after installation to confirm that this procedure works. I have found it to be reliable, but TSRs are a world without rules.

Summary

The techniques for loading custom fonts described here give a program an edge of distinctiveness in an ever more crowded software marketplace. Not only is this now worthwhile for developers because of the greater abundance of EGAs but the code also works on the VGA. This means a fairly long time span for the investment in program development to pay off. Graphics environments offer users custom fonts, but program development is longer. The custom font facilities of the EGA offer a faster development path that does not require the wholesale recoding of applications.

DDJ

[LISTING ONE]



/* ITALIC.ASC -- This is an ASCII representation of the italic font    */
/* characters used in ITALIC.C.   This file is #includeD.         */
/* In the table below, each row corresponds to a character.  The      */
/* 14 elements of each row correspond to the 14 scan lines of the     */
/* character.                                                         */
char italic_arr[128-32][14] = {
/* Font character 32 (ASCII value   ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 33 (ASCII value ! ) is */ {0x00, 0x00, 0x06, 0x0f, 0x1e, 0x1e, 0x18, 0x18, 0x00, 0x30, 0x60, 0x00, 0x00, 0x00},
/* Font character 34 (ASCII value " ) is */ {0xc0, 0x0c, 0x99, 0x19, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 35 (ASCII value # ) is */ {0x00, 0x00, 0x1b, 0x1b, 0x7f, 0x36, 0x6c, 0x6c, 0xfc, 0xd9, 0xb0, 0x01, 0x00, 0x00},
/* Font character 36 (ASCII value $ ) is */ {0x03, 0x03, 0x9f, 0xf1, 0x61, 0xe0, 0x7c, 0x06, 0x0c, 0x8d, 0xf0, 0x61, 0xc0, 0x00},
/* Font character 37 (ASCII value % ) is */ {0x00, 0x00, 0x00, 0x00, 0x61, 0xe3, 0x0c, 0x18, 0x60, 0xcc, 0x18, 0x03, 0x00, 0x00},
/* Font character 38 (ASCII value & ) is */ {0x00, 0x00, 0x0e, 0x1b, 0x36, 0x1c, 0x76, 0xdc, 0x98, 0x99, 0xd8, 0x01, 0x00, 0x00},
/* Font character 39 (ASCII value ' ) is */ {0x00, 0x06, 0x0c, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 40 (ASCII value ( ) is */ {0x00, 0x00, 0x03, 0x06, 0x18, 0x18, 0x30, 0x30, 0x60, 0x30, 0x30, 0x00, 0x00, 0x00},
/* Font character 41 (ASCII value ) ) is */ {0x00, 0x00, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x30, 0xc0, 0x00, 0x00, 0x00},
/* Font character 42 (ASCII value * ) is */ {0x00, 0x00, 0x00, 0x00, 0x33, 0x1e, 0xff, 0x3c, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 43 (ASCII value + ) is */ {0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x7e, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 44 (ASCII value , ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, 0xc0, 0x00, 0x00},
/* Font character 45 (ASCII value - ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 46 (ASCII value . ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x00, 0x00, 0x00},
/* Font character 47 (ASCII value / ) is */ {0x00, 0x00, 0x80, 0x01, 0x06, 0x0c, 0x30, 0x60, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00},
/* Font character 48 (ASCII value 0 ) is */ {0x00, 0x00, 0x1f, 0x71, 0x63, 0xcf, 0xf6, 0xe6, 0x8c, 0x8c, 0xf8, 0x00, 0x00, 0x00},
/* Font character 49 (ASCII value 1 ) is */ {0x00, 0x00, 0x06, 0x0e, 0x3c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0xf8, 0x00, 0x00, 0x00},
/* Font character 50 (ASCII value 2 ) is */ {0x00, 0x00, 0x1f, 0x31, 0x03, 0x06, 0x18, 0x30, 0xc0, 0x8c, 0xf8, 0x00, 0x00, 0x00},
/* Font character 51 (ASCII value 3 ) is */ {0x00, 0x00, 0x1f, 0x11, 0x03, 0x03, 0x3c, 0x06, 0x0c, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 52 (ASCII value 4 ) is */ {0x00, 0x00, 0x03, 0x07, 0x1e, 0x36, 0xcc, 0xfe, 0x18, 0x18, 0x78, 0x00, 0x00, 0x00},
/* Font character 53 (ASCII value 5 ) is */ {0x00, 0x00, 0x3f, 0x30, 0x60, 0x60, 0x7c, 0x06, 0x0c, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 54 (ASCII value 6 ) is */ {0x00, 0x00, 0x0e, 0x18, 0x60, 0x60, 0xfc, 0xc6, 0x8c, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 55 (ASCII value 7 ) is */ {0x00, 0x00, 0x3f, 0x71, 0x03, 0x06, 0x18, 0x30, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00},
/* Font character 56 (ASCII value 8 ) is */ {0x00, 0x00, 0x1f, 0x71, 0x63, 0x63, 0x7c, 0xc6, 0x8c, 0xcc, 0xf8, 0x00, 0x00, 0x00},
/* Font character 57 (ASCII value 9 ) is */ {0x00, 0x00, 0x1f, 0x71, 0x63, 0x63, 0x7e, 0x06, 0x0c, 0x18, 0xe0, 0x00, 0x00, 0x00},
/* Font character 58 (ASCII value : ) is */ {0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00},
/* Font character 59 (ASCII value ; ) is */ {0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x30, 0xc0, 0x00, 0x00, 0x00},
/* Font character 60 (ASCII value < ) is */ {0x00, 0x00, 0x01, 0x03, 0x0c, 0x18, 0x60, 0x30, 0x30, 0x18, 0x18, 0x00, 0x00, 0x00},
/* Font character 61 (ASCII value = ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 62 (ASCII value > ) is */ {0x00, 0x00, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x0c, 0x30, 0x60, 0x80, 0x01, 0x00, 0x00},
/* Font character 63 (ASCII value ? ) is */ {0x00, 0x00, 0x9f, 0xf1, 0x63, 0x06, 0x18, 0x18, 0x00, 0x30, 0x60, 0x00, 0x00, 0x00},
/* Font character 64 (ASCII value @ ) is */ {0x00, 0x00, 0x9f, 0xf1, 0x63, 0xef, 0xde, 0xde, 0xb8, 0x81, 0xf0, 0x01, 0x00, 0x00},
/* Font character 65 (ASCII value A ) is */ {0x00, 0x00, 0x04, 0x0e, 0x36, 0x62, 0xc6, 0xfe, 0x8c, 0x8c, 0x98, 0x00, 0x00, 0x00},
/* Font character 66 (ASCII value B ) is */ {0x00, 0x00, 0x3f, 0x1b, 0x33, 0x33, 0x7c, 0x66, 0xcc, 0xcc, 0xf0, 0x00, 0x00, 0x00},
/* Font character 67 (ASCII value C ) is */ {0x00, 0x00, 0x0f, 0x19, 0x61, 0xe0, 0xc0, 0xc0, 0x84, 0xcc, 0xf0, 0x00, 0x00, 0x00},
/* Font character 68 (ASCII value D ) is */ {0x00, 0x00, 0x3e, 0x1b, 0x33, 0x33, 0x66, 0x66, 0xcc, 0xd8, 0xe0, 0x00, 0x00, 0x00},
/* Font character 69 (ASCII value E ) is */ {0x00, 0x00, 0x3f, 0x19, 0x31, 0x30, 0x78, 0x60, 0xc4, 0xcc, 0xfc, 0x00, 0x00, 0x00},
/* Font character 70 (ASCII value F ) is */ {0x00, 0x00, 0x3f, 0x19, 0x31, 0x30, 0x7c, 0x60, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00},
/* Font character 71 (ASCII value G ) is */ {0x00, 0x00, 0x0f, 0x19, 0x61, 0xe0, 0xc0, 0xde, 0x8c, 0xcc, 0xf8, 0x00, 0x00, 0x00},
/* Font character 72 (ASCII value H ) is */ {0x00, 0x00, 0x31, 0x71, 0x63, 0xe3, 0xfe, 0xc6, 0xcc, 0x8c, 0x88, 0x00, 0x00, 0x00},
/* Font character 73 (ASCII value I ) is */ {0x00, 0x00, 0x0f, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0xf0, 0x00, 0x00, 0x00},
/* Font character 74 (ASCII value J ) is */ {0x00, 0x00, 0x07, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x98, 0x98, 0xe0, 0x00, 0x00, 0x00},
/* Font character 75 (ASCII value K ) is */ {0x00, 0x00, 0x39, 0x19, 0x36, 0x36, 0x78, 0x68, 0xc8, 0xc8, 0x98, 0x00, 0x00, 0x00},
/* Font character 76 (ASCII value L ) is */ {0x00, 0x00, 0x3c, 0x18, 0x30, 0x30, 0x60, 0x60, 0xc4, 0xcc, 0xf8, 0x00, 0x00, 0x00},
/* Font character 77 (ASCII value M ) is */ {0x00, 0x00, 0x31, 0x7b, 0x7f, 0xfe, 0xd6, 0xc6, 0x8c, 0x8c, 0x98, 0x00, 0x00, 0x00},
/* Font character 78 (ASCII value N ) is */ {0x00, 0x00, 0x31, 0x79, 0x7b, 0xff, 0xde, 0xce, 0x8c, 0x8c, 0x98, 0x00, 0x00, 0x00},
/* Font character 79 (ASCII value O ) is */ {0x00, 0x00, 0x0e, 0x1b, 0x63, 0xe3, 0xc6, 0xc6, 0x8c, 0xd8, 0xe0, 0x00, 0x00, 0x00},
/* Font character 80 (ASCII value P ) is */ {0x00, 0x00, 0x3f, 0x19, 0x33, 0x33, 0x7c, 0x60, 0xc0, 0xc0, 0xc0, 0x80, 0x00, 0x00},
/* Font character 81 (ASCII value Q ) is */ {0x00, 0x00, 0x1f, 0x71, 0x63, 0xe3, 0xc6, 0xd6, 0xbc, 0xf8, 0x30, 0x38, 0x00, 0x00},
/* Font character 82 (ASCII value R ) is */ {0x00, 0x00, 0x3f, 0x13, 0x33, 0x33, 0x7c, 0x6c, 0xcc, 0xcc, 0x98, 0x00, 0x00, 0x00},
/* Font character 83 (ASCII value S ) is */ {0x00, 0x00, 0x1f, 0x71, 0x63, 0x30, 0x38, 0x0c, 0xcc, 0xcc, 0xf0, 0x00, 0x00, 0x00},
/* Font character 84 (ASCII value T ) is */ {0x00, 0x00, 0x3f, 0x3f, 0x2d, 0x0c, 0x18, 0x18, 0x30, 0x60, 0xf0, 0x00, 0x00, 0x00},
/* Font character 85 (ASCII value U ) is */ {0x00, 0x00, 0x31, 0x71, 0x63, 0xe3, 0xc6, 0xc6, 0xcc, 0xcc, 0xf8, 0x00, 0x00, 0x00},
/* Font character 86 (ASCII value V ) is */ {0x00, 0x00, 0x31, 0x71, 0x63, 0xe3, 0xc6, 0xc6, 0xd8, 0xf0, 0xc0, 0x00, 0x00, 0x00},
/* Font character 87 (ASCII value W ) is */ {0x00, 0x00, 0x31, 0x71, 0x63, 0xc3, 0xd6, 0xd6, 0xdc, 0xf8, 0xb0, 0x00, 0x00, 0x00},
/* Font character 88 (ASCII value X ) is */ {0x00, 0x00, 0x31, 0x71, 0x36, 0x1c, 0x38, 0x78, 0xd8, 0x8c, 0x98, 0x00, 0x00, 0x00},
/* Font character 89 (ASCII value Y ) is */ {0x00, 0x00, 0x1b, 0x1b, 0x32, 0x36, 0x3c, 0x18, 0x30, 0x30, 0xf0, 0x00, 0x00, 0x00},
/* Font character 90 (ASCII value Z ) is */ {0x00, 0x00, 0x3f, 0x71, 0x46, 0x0c, 0x30, 0x60, 0x84, 0x8c, 0xf8, 0x00, 0x00, 0x00},
/* Font character 91 (ASCII value [ ) is */ {0x00, 0x00, 0x0f, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00},
/* Font character 92 (ASCII value \ ) is */ {0x00, 0x00, 0x20, 0xf0, 0x70, 0x38, 0x38, 0x1c, 0x1c, 0x0c, 0x08, 0x00, 0x00, 0x00},
/* Font character 93 (ASCII value ] ) is */ {0x00, 0x00, 0x0f, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0xf0, 0x00, 0x00, 0x00},
/* Font character 94 (ASCII value ^ ) is */ {0x02, 0x07, 0x9b, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 95 (ASCII value _ ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00},
/* Font character 96 (ASCII value ` ) is */ {0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 97 (ASCII value a ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x7c, 0x98, 0x98, 0xd8, 0x00, 0x00, 0x00},
/* Font character 98 (ASCII value b ) is */ {0x00, 0x00, 0x38, 0x18, 0x30, 0x3c, 0x6c, 0x66, 0xcc, 0xcc, 0xf0, 0x00, 0x00, 0x00},
/* Font character 99 (ASCII value c ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xc6, 0xc0, 0x80, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 100 (ASCII value d ) is */ {0x00, 0x00, 0x07, 0x03, 0x06, 0x1e, 0x6c, 0xcc, 0x98, 0x98, 0xd8, 0x00, 0x00, 0x00},
/* Font character 101 (ASCII value e ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xc6, 0xfe, 0x80, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 102 (ASCII value f ) is */ {0x00, 0x00, 0x0e, 0x1b, 0x32, 0x30, 0xf8, 0x60, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00},
/* Font character 103 (ASCII value g ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xcc, 0xcc, 0x98, 0xf8, 0x30, 0x30, 0xc0, 0x00},
/* Font character 104 (ASCII value h ) is */ {0x00, 0x00, 0x38, 0x18, 0x30, 0x36, 0x76, 0x66, 0xcc, 0xcc, 0x98, 0x00, 0x00, 0x00},
/* Font character 105 (ASCII value i ) is */ {0x00, 0x00, 0x06, 0x06, 0x00, 0x1c, 0x18, 0x18, 0x30, 0x30, 0xf0, 0x00, 0x00, 0x00},
/* Font character 106 (ASCII value j ) is */ {0x00, 0x00, 0x01, 0x01, 0x00, 0x07, 0x06, 0x06, 0x0c, 0x0c, 0x98, 0x98, 0xe0, 0x00},
/* Font character 107 (ASCII value k ) is */ {0x00, 0x00, 0x38, 0x18, 0x30, 0x33, 0x6c, 0x78, 0xd8, 0xcc, 0x98, 0x00, 0x00, 0x00},
/* Font character 108 (ASCII value l ) is */ {0x00, 0x00, 0x0e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0xf0, 0x00, 0x00, 0x00},
/* Font character 109 (ASCII value m ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xfe, 0xd6, 0xac, 0xac, 0x88, 0x00, 0x00, 0x00},
/* Font character 110 (ASCII value n ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x66, 0x66, 0xcc, 0xcc, 0x98, 0x00, 0x00, 0x00},
/* Font character 111 (ASCII value o ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xc6, 0xc6, 0x8c, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 112 (ASCII value p ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x66, 0x66, 0xcc, 0xf8, 0x80, 0x80, 0x80, 0x00},
/* Font character 113 (ASCII value q ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xcc, 0xcc, 0x98, 0xf8, 0x30, 0x30, 0xf0, 0x00},
/* Font character 114 (ASCII value r ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x76, 0x66, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00},
/* Font character 115 (ASCII value s ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x46, 0x70, 0x38, 0x8c, 0xf0, 0x00, 0x00, 0x00},
/* Font character 116 (ASCII value t ) is */ {0x00, 0x00, 0x04, 0x0c, 0x18, 0x7e, 0x30, 0x30, 0x60, 0x6c, 0x70, 0x00, 0x00, 0x00},
/* Font character 117 (ASCII value u ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xcc, 0xcc, 0x98, 0x98, 0xd8, 0x00, 0x00, 0x00},
/* Font character 118 (ASCII value v ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66, 0x66, 0xcc, 0xf8, 0x60, 0x00, 0x00, 0x00},
/* Font character 119 (ASCII value w ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x46, 0xd6, 0xac, 0xfc, 0xb0, 0x00, 0x00, 0x00},
/* Font character 120 (ASCII value x ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x38, 0x70, 0xd8, 0x98, 0x00, 0x00, 0x00},
/* Font character 121 (ASCII value y ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0xc6, 0x8c, 0xfc, 0x18, 0x30, 0xe0, 0x00},
/* Font character 122 (ASCII value z ) is */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x4c, 0x18, 0x60, 0xcc, 0xf8, 0x00, 0x00, 0x00},
/* Font character 123 (ASCII value { ) is */ {0x00, 0x00, 0x03, 0x06, 0x0c, 0x0c, 0x70, 0x18, 0x30, 0x30, 0x38, 0x00, 0x00, 0x00},
/* Font character 124 (ASCII value | ) is */ {0x00, 0x00, 0x06, 0x06, 0x0c, 0x0c, 0x00, 0x18, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00},
/* Font character 125 (ASCII value } ) is */ {0x00, 0x00, 0x1c, 0x06, 0x0c, 0x0c, 0x0e, 0x18, 0x30, 0x30, 0xc0, 0x01, 0x00, 0x00},
/* Font character 126 (ASCII value ~ ) is */ {0x00, 0x00, 0x1d, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* Font character 127 (ASCII value  ) is */ {0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00}
} ;  /* End of italic array */



[LISTING TWO]


/* Copyright (C) Magna Carta Software, 1987.  All Rights Reserved.      */
/* MAKEFONT -- Makes an italic font for the EGA.                        */
/* Note: Do not run this program from within the IDE.                   */
/* First version 10/29/87.  Last update: 10/31/87.                      */

#ifndef __SMALL__
     #error Should use SMALL compilation model
#endif

#include <stdio.h>
#include <dos.h>
#include <process.h>
#include <mem.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0


/* Functions related to video operations */
void load_user_egaxfont(char *fptr,int block,int bpc,int char_count,int spos);
void get_egafont(char *fptr, int font);
int get_video_info(void);

/* Global variables and #DEFINES related to video operations */
#include "mk_ital.asc"          /* ITALIC.ASC contains our italic font */
#define VIDEO 0x10                              /* BIOS video interrupt */
char fontarray[3585];                           /* buffer for font storage */
int our_id1 = 0xfac, our_id2 = 0x1000;          /* our ID words */
char ega_color;

/* Functions related to TSR operations */
int already_installed(void);
int deinstall(void);
void interrupt (*old_int10h)(void);
void interrupt int10h(unsigned bp, unsigned di, unsigned si,
                      unsigned ds, unsigned es, unsigned dx,
                      unsigned cx, unsigned bx, unsigned ax);

/* Global variables related to TSR operations */
unsigned save_bp1, save_bp2, old_ds, old_psp;
unsigned old_env;


/* Turbo C system variables (see text for explanation ) */
extern unsigned __brklvl;
extern unsigned _psp;


/* Other functions */
void error(int errnum);


main()
{
        int i, j, k;
        union REGS regs;

        get_video_info();
        if (!ega_color) regs.x.ax = 0x7;        /* set the video mode */
        else regs.x.ax = 0x3;
        int86(VIDEO,®s,®s);
        if (already_installed()) {
                deinstall();
                printf("\The Italic font is now no longer installed");
                exit(0);
        }

        /* system checks out -- go ahead and put italic chars. in font */
        get_egafont(fontarray,14);      /* store the ROM font in fontarray */
        for(i=14*32,j=0; i< 14*128;j++) {
                for(k=0;k<14;k++) fontarray[i++] = italic_arr[j][k];
        }
        load_user_egaxfont(fontarray,0,14,256,0);       /* load our font */

        old_psp = _psp;                 /* save the resident program's PSP */
        old_env = peek(_psp,0x2c);      /* save the resident program's ENV */
        old_int10h = getvect(VIDEO);    /* save the old vector */
        setvect(VIDEO,int10h);          /* install our INT10h handler */

        /* terminate and stay resident. Program length is determined by */
        /* subtracting the psp address (_psp) from __brkval which is    */
        /* dynamically set to the address of the end of DS.  This       */
        /* appears to be reliable in the TINY and SMALL models, but     */
        /* results are unknown for other models.                        */
        keep(FALSE,_DS + (__brklvl + 15)/16 - _psp);
}


/* ALREADY_INSTALLED: This routine scans through memory for our ID byte.*/
/* Returns: 0 if not found, 1 if found.                                 */
int already_installed()
{
        unsigned int next_seg;

        for(next_seg = _psp-1; next_seg > 0; next_seg--) {
                if (peek(next_seg,(unsigned) &our_id1) == our_id1) {
                        if (peek(next_seg,(unsigned) &our_id2) == our_id2) {
                                old_ds = next_seg;
                                return (1);
                        }
                }
        }
        return (0);
}


/* DEINSTALL: Remove our TSR by reseting interrupt 0x10 and video mode. */
int deinstall()
{
        union REGS regs;
        struct SREGS sregs;

        /* initialize old interrupt vector */
        old_int10h = MK_FP(peek(old_ds,(unsigned) &old_int10h+2),peek(old_ds,(unsigned) &old_int10h));
        setvect(VIDEO,old_int10h);      /* reset the interrupt vector */

        if (!ega_color) regs.x.ax = 0x7;        /* reset the video mode */
        else regs.x.ax = 0x3;
        int86(VIDEO,®s,®s);

        /* Deallocate the memory used by the resident program */
        old_psp = peek(old_ds, (unsigned) &old_psp);
        old_env = peek(old_ds, (unsigned) &old_env);

        regs.x.ax = 0x4900;     /* DOS function to free allocated memory */
        sregs.es = old_psp;
        intdosx(®s,®s,&sregs);
        if (regs.x.cflag) error(3);

        regs.x.ax = 0x4900;     /* DOS function to free allocated memory */
        sregs.es = old_env;
        intdosx(®s,®s,&sregs);
        if (regs.x.cflag) error(4);
        return (0);
}


/* INT10: This function is the BIOS video interrupt handler.            */
void interrupt int10h(unsigned bp, unsigned di, unsigned si,
                      unsigned ds, unsigned es, unsigned dx,
                      unsigned cx, unsigned bx, unsigned ax)
{
        /* execute the old video interrupt */
        if (ax >> 8) {          /* it is not a video mode reset */
                _AX = ax;
                _BX = bx;
                _CX = cx;
                _DX = dx;
                save_bp1 = _BP;
                _BP = bp;
                (*old_int10h)();
                _BP = save_bp1;
                ax = _AX;
                bx = _BX;
                cx = _CX;
                dx = _DX;
        }
        else {                  /* AH == 0 for a video mode reset */
                _AX = ax;
                _BX = bx;
                _CX = cx;
                _DX = dx;
                (*old_int10h)();
                ax = _AX;
                bx = _BX;
                cx = _CX;
                dx = _DX;

                /* reload our font destroyed by the mode reset */
                load_user_egaxfont(fontarray,0,14,256,0);
        }
}


/* LOAD_USER_EGAXFONT -- Load a user-defined font and reset page length.*/
/* Parms: ptr. to user table, block to load, bytes-per-char,            */
/* number of chars to store, starting position in font table.           */
/* First version 7/13/87.  Last update 10/31/87.                        */
void load_user_egaxfont(char *fptr,int block,int bpc,int char_count,int spos)
{
        unsigned byte_block;

        byte_block = (bpc << 8) | block;

        /* Can't use intr() due to Turbo C v1.0 compiler bug.           */
        /* Note: we must do any assignments to segments prior to doing  */
        /* assignments to AX since AX is destroyed.                     */
        _ES = _DS;
        _AX = 0x1100;                   /* call function 0x11 */
        _BX = byte_block;               /* block to load */
        _CX = char_count;               /* number of characters to load */
        _DX = spos;                     /* character offset into table */
        save_bp2  = _BP;                /* save BP for stack addressing */
        _BP = FP_OFF(fptr);             /* load address of user font */
        geninterrupt(VIDEO);
        _BP = save_bp2;                 /* restore BP -- or die... */
}


/* GET_EGAFONT: This routine grabs an EGA font from ROM and stores it   */
/* in the global variable fontarray */
void get_egafont(char *fptr, int font)
{
        struct REGPACK regs;

        regs.r_ax = 0x1130;             /* EGA BIOS call to return font */
        if (font == 8) regs.r_bx = 0x0300;
        else if (font == 14) regs.r_bx = 0x0200;
        intr(VIDEO,®s);
        movedata(regs.r_es,regs.r_bp,_DS, (unsigned) fptr,14*256);
}


/* GET__VIDEO_INFO: A VGA or an EGA must be installed for this program  */
/* to work. The monitor must be an Enahanced Color or Monochrome        */
/* display and the correct adaptor must be active.                      */
int get_video_info()
{
        union REGS regs;
        unsigned char e_byte;

        /* First check for the presence of an EGA */
        regs.h.ah = 0x12;       /* EGA BIOS alternate select            */
        regs.h.bl = 0x10;       /* return EGA information.              */
        int86(VIDEO, ®s, ®s);
        if (regs.h.bl == 0x10) error(1);        /* EGA not found */

        /* EGA is present -- is it active? */
        e_byte = peekb(0,0x487);        /* EGA info. byte */
        if (e_byte & 8) error(2);       /* EGA not active */

        /* Does the present, active EGA drive a color or mono monitor? */
        if (regs.h.bh) ega_color = FALSE;  /* EGA drives a mono monitor */
        else ega_color = TRUE;          /* EGA drives a color monitor */

        /* See if EGA drives an Enhanced Color Display */
        if (ega_color) if (!(regs.h.cl == 3 || regs.h.cl == 9)) error(1);
        return (1);
}


/* ERROR: A simple error handler.                                       */
void error(int errnum)
{
        switch (errnum) {
                case 1: printf("\An EGA and Enhanced Color or Monochrome Display");
                        printf("\nmust be present to use this program.");
                        break;

                case 2: printf("\Please make the EGA the active adapter");
                        printf("in order to run this program.");
                        break;

                case 3: printf("\nError deallocating program memory.");
                        break;

                case 4: printf("\nError deallocating program PSP.");
                        break;

                default:break;
        }
        printf("\nProgram exiting.\n");
        exit(0xf);      /* Return code for DOS errorlevel */
}








Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.