AtoZ of C
AtoZ of C
AtoZ of C
First Chapter
Greetings.
It is a common practice to skip the acknowledgement and book organization. So we have
placed them in the First Chapter! Please read this chapter without fail to understand this book
better.
1.1 Acknowledgement
Throughout the world many people have contributed to this book. We must acknowledge
all those good people. We sincerely thank Dr. Dennis M. Ritchie, creator of C language for
granting permission to use his photo. Our thanks also goes to Dr. Ralf Brown for providing us
his great source—Ralf Brown’s Interrupt List for this book. We must thank Mr. Alexander
Russell for his unconditional support to this book. We are proud to thank all the real and
international programmers who provided their source code to us.
Ms. Lyril Sugitha (lyrils@yahoo.com) helped us a lot to translate this book from
“Tanglish” to English! We sincerely thank her as she worked with us even in her tight schedules.
I specially thank my mother for her prayers for the success of this project and my father
for his support by presenting me a computer. My sincere thanks to my sister Lincy, brother
Bensley and my friend Brighton for their encouragement. I benefited greatly from my uncle
Azariah, who helped me in finding many useful materials. I thank all my friends and relatives
who remembered me in their prayers.
K. Joseph Wesley
I am grateful to all my friends who are interested in me. I remember all my teachers for
their care towards me. I especially thank my Lecturer Mr. Richard Devaraj, American College
for his concern towards my career. I must thank Mr. D. Joseph Devadason (Lecturer in
Management Studies, American College, joseph_d@rediffmail.com), one of my good and old
friends for helping me to understand English in a better way. Finally, I would like to express
my sincere gratitude to my family members who are behind my development: Papa, Amma, Patti,
Mama, Mami & Akka.
R. Rajesh Jeba Anbiah
Q: I want to score more marks in University examination. Will this book help me?
A: No. We are dead against the mark-based culture. This book is purely for enthusiasts. This
book is written to open many secrets of C.
Q: What are the special features of this book?
A: This book is not only written by K. Joseph Wesley & R. Rajesh Jeba Anbiah, but many
renowned International programmers’ and authors’ materials are also used with permission. The
supplement CD got many sources, and utilities. For more details about CD , see “Contents
of CD ”
Q: How far I can trust source codes of this book?
A: We have tested all the codes. Certain source codes of this book are of real programmers.
We have used their codes according to their terms. So all codes should logically work! But,
obviously there must be some flaws in the approach/solution; the readers are encouraged to find
better—alternate solution.
Q: Which compiler & IDE you are going to use?
A: We have used TC++3.0. And all parts of this book refer the IDE (Integrated
Development Environment) TC++3.0 unless otherwise noted.
Q: How should I use this book?
A: Read all the contents of the book first. Then workout examples and exercises. After
gaining confidence, dare to do projects!
Birth of C
Dennis M. Ritchie,
Creator of C Language
Courtesy: Lucent Technologies
Timeline
Year Language/OS Remarks
1956-63 Assembly Language IBM developed Assembly language
1954-57 FORTRAN (FORmula A team lead by John W. Backus developed a
TRANslation) numerically orientated language called
FORTRAN
1958 ALGOL(ALGOrithmic An important structured programming
Language) language was developed by committee of
European and American computer scientists
FORTRAN & ALGOL’s type structures later
influenced many other languages including
BCPL, B & C
1964 PL/I (Programming IBM developed a clean language intended for
Language 1) both business and scientific purposes
1965 The famous “Multics project” was started by
MIT, Bell Labs & General Electric as a joint
venture. Multics (Multiplexed Information
and Computing Service) is an experimental
Operating System. It was the first operating
system written in a high level language
namely PL/I
Important Notice
The date of introduction of many languages in the above table is merely a rough approximation.
Experts have divided regarding the date of introduction of many languages. Even the creators of many
languages are also not clear; especially Dennis M Ritchie didn’t specify the exact release date of C. I think,
those languages are developed for personal needs and not aimed for commercial hit, that’s why they lack
the clear release date.
So if you are a teacher, please don’t ask the questions regarding the date of release of certain
languages, as they are not clear. If you are a student and you’re asked such questions, raise your voice
for a better system of questioning.
3 “The wise listen to advice.”
Coding Style
error:
clean up the error
return( what );
}
b) All global variables should begin with capital letter and must contain underscore.
(e.g.) Next_Tick
f) Structure that won’t require more than one name can be typedefed.
(e.g.) typedef struct
{
BYTE fileid;
:
:
} FILEHEADER;
g) The definition with typedef or #define should contain only capital letters.
h) All declarations should precede functions, all functions should precede main( ).
i) Don’t use goto statement.
j) Don’t use more than one return statement in a single function.
k) Try to avoid use of exit( ) in programs. But exit( ) can appear in the
beginning of the program or on a separate procedure for checking errors.
l) Don’t use continue and break, instead use BOOLEAN variable.
Part I
ANSI C
“Never give in, never give in, never give in—in nothing great or
small, large or petty—never give in except to convictions of honor
and good sense”
—Winston Churchill
4
“A good person gives life to others.”
ANSI C - Prelude
When C language was developed, it took its popularity and many changes have been
done on the language by other people. It necessitates the need for a good standard for C. Thus in
1983 American National Standards Institute (ANSI) Note
established a committee to “standardize” the C language. As Part I, fully concentrates on ANSI
The main objective of ANSI was to provide portability to C. C, choose ANSI C from your Turbo
(Portability is nothing but how far your code is portable, i.e. C++3.0 IDE (Options > Compiler >
Source > ANSI C) to let your
how far your code can be transported between different standard to ANSI C.
machines & different operating systems). The result of the
committee’s work was completed by the end of 1988. And the result is the ANSI standard or
ANSI C.
Thus the word ‘C’ directly or indirectly refers to ANSI C. Indian Programmers very often
misunderstand that DOS programming is C programming. There is a vast difference between
DOS programming and C programming. C programming always refers to ANSI C standard.
ANSI C was accepted by ISO too. Thus ANSI C is the international standard for C.
Caution
ANSI C does not have getch( ), dos.h and other DOS based functions. If you are not sure about
the functions, place the cursor over the function and press Ctrl+F1 and check the documentation, whether
it is acceptable in ANSI standard or not.
But if you code with WAR coding rules your code will be more readable. The following
code fragments use WAR coding style for the same strcmp( ).
/* strcmp( ) with WAR coding style */
int strcmp( char *s, char *t )
{
int n;
A to Z of C 15
Now you might have found that how far WAR coding style is better than other coding
styles.
4.2.2 Boolean Variables
In C, ‘0’ refers to ‘False’ and any other number refers to ‘True’. But however, we don’t
have separate data type for Boolean. But it is wise to have Boolean, for better programming.
Boolean can be defined like:
Version 1
enum BOOLEAN
{
FALSE, TRUE
};
Version 2
enum BOOLEAN
{
FALSE = 0,TRUE = 1
};
Version 3
enum BOOLEAN
{
FALSE=0, TRUE
};
Version 4
enum BOOLEAN
{
TRUE=1,FALSE=0
};
All the above four versions use enum. But programmers rarely use enum.
Some people use
Version 5
typedef char BOOLEAN;
#define TRUE (1)
#define FALSE (0)
Version 6 uses int for BOOLEAN. Since int is the fastest data type in C, version 6 is better
than any other implementations. Also FALSE & TRUE are defined with macro #define. So it is
the fastest implementation of BOOLEAN. So I recommend you to use version 6 for BOOLEAN
implementation.
4.2.3 How to code better?
Beginners usually ask the question: How to develop programming skills? According to
me, the programs related to ‘Calendar’ will help you to develop programming skills. You must
remember to use all features of the language when you program.
The following points will help you to program better:
a) Your code should be efficient. ‘Efficient’ refers to less in code size and faster in
execution.
b) Your code should have good readability.
c) Your code should use all the good features of the language
Try to rewrite your code. It will help you to reduce the size of the code and to increase
readability.
5
“If you act too quickly, you might make a mistake.”
Many people mishandle the main( ) function. You can avoid such mishandling by
setting your compiler to ANSI C standard so that it will point out the error.
Undefined
If the “grammar” was not defined for a given particular operation, it is called as
“Undefined”. So each compiler would give different answers for a given particular operation.
Usually compilers won’t check such ‘Undefined’ usage. So it is our responsibility to check it.
6.1 Example
char buffer[5];
strcpy(buffer, "Hello World"); /* Undefined */
For example the operation of copying a string to buffer, which is smaller than the string is
‘Undefined’. That means Dennis Ritchie didn’t say (or define) anything about such operations.
b) What would happen to the array after executing the following statements?
int a[5], i = 1;
a[i] = i++;
These idiotic questions are very often asked in Indian Programming world. The outputs
are undefined. Even if such questions are asked, the right answer will be “the result is undefined”.
Note
For the above program, you may get some output. But it is wrong. You have to understand that compilers
may not check ‘Undefined’ grammars.
7
“The slap of a friend can be trusted to help you.”
The powerful XOR operator (^) is rarely used by Indian C Programmers. Let’s see some
of its uses.
But this doesn’t work with floating point values. It also doesn’t work when we send values as
SWAP (a, a).
Now you can crypt or decrypt your file with a single function CryptOrDecrypt( ). If
you want to send some crypted message to someone else, both of you must have this
CryptOrDecrypt( )function.
Caution
‘key’ value should not be 0. If key value is 0, the line will not be crypted
because N^0=N.
9
“Pride will destroy a person.”
Recursion
A function that calls itself is an important feature of C and such functions are called
recursive functions. The term “recursion” was derived from the Latin word recursus, which
means, “to run back”. “Recursive” thinking may be tough for beginners. In this chapter, I have
presented some interesting recursive programs. Few programs are my original work, others are
improved version of existing recursive programs.
Note
As recursive programs use “memory stack”, it reduces execution speed. And it may cause “stack overflow”
which would in turn crash your system. If you compile your program with “Test stack overflow” option,
you can avoid this problem. For this, choose OPTIONS >COMPILER >ENTRY/EXIT CODE > Test stack
overflow.
9.1 Factorial
This is the most famous program on recursion. Many versions of this program are
available. All programs differ only in checking conditions. I prefer to write like the following
one.
long Factorial( int n ) /* returns factorial */
{
if ( n>0 )
return( n * Factorial(n-1) );
else
return( 1 );
} /*--Factorial( )------*/
9.2 Fibonacci
The following program returns the nth Fibonacci number. Fibonacci series is : 1, 1, 2, 3, 5,
8, 13, 21…
int Fibonacci( int n ) /* returns nth Fibonacci number */
{
if ( n==1 || n==2 )
return( 1 );
else
return( Fibonacci(n-1) + Fibonacci(n-2) );
} /*--Fibonacci( )-------*/
A to Z of C 23
9.3 GCD
Here is the program to find the Greatest Common Divisior (GCD) of two numbers a & b.
int GCD( int a, int b ) /* returns GCD of a, b */
{
if ( a>=b && a%b==0 )
return( b );
else if ( a<b )
return( GCD( b, a ) );
else
return( GCD( b, a%b ) );
} /*--GCD( )---------*/
9.4 Power
I haven’t yet come across user defined power function, which could handle negative n
(say, 4.5-5). Here is the program I tried…it could handle negative n too!
double Power( double x, int n ) /* returns x power n */
{
if ( n==0 )
return( 1 );
else if ( n>0 )
return( x * Power( x, n-1 ) );
else
return( (1/x) * Power( x, n+1 ) );
} /*--Power( )-----------*/
Interesting Programs
Everybody might have the question: why programmers are prone to C? The answer is
very simple: C’s structure allows programmers to write a small-tight code for complex programs.
In this chapter let’s see a few interesting programs that use C’s real power.
10.1 Power of 2
How to find whether the given number is a power of 2? i.e., 1, 2, 4, 8, 16, 32.. are powers
of 2.
#define ISPOWOF2( n ) ( ! ( n & ( n-1 ) )
See, the BOOLEAN variable flag in IsPrime( ). It is used to break the for loop. As
we haven’t used any break or jump statement, it is considered as a good programming.
Note
The above program works fine upto 4999, because for 5000 we have V. In ANSI C, we can’t get V. It can
be done with Turbo C(DOS programming) by changing character set with int 10h.
A to Z of C 27
10.5 Calendar
The following program prints the calendar for a given year like Unix’s cal utility.
However, it won’t work exactly like “cal” for year-wise output. For that you need to store the
output in an array as a grid.
#include <stdio.h>
#include <stdlib.h>
int Days_Tbl[2][12] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
char *Month_Tbl[12] = {
"January", "February", "March", "April", "May",
"June", "July", "August", "September",
"October", "November", "December"
};
d = 365L*1752 + 1752/4;
d += 365L*(y-1752-1) + (y-1752-1)/4 - (y-1752-1)/100
+ (y-1752-1)/400 + 6;
}
else /* for Julian Calendar */
{
leap = ( y%4==0 );
d = 365L*(y-1) + (y-1)/4 + 6;
}
for( i=1; i<m; ++i )
d += Days_Tbl[leap][i-1];
if ( y>1752 || (y==1752 && m>9) )
d -= 11;
return( d % 7 );
} /*--FirstDayOfMonth( )--------*/
firstdayofmonth = FirstDayOfMonth( m, y );
leap = ( y>1752 ) ? ( y%4==0&&y%100!=0 || y%400==0 ) : ( y%4==0 );
10.6 Memory-Swap
Normal Memory
head tail
Swapped Memory
tail head
Consider the situation in which you want to swap the contents of memory without using
much external storage space and one portion is larger than the other. In our example, the head
portion is larger than tail. It is really a tough job. The code by Ray Gardner efficiently solves
this problem.
/* memrev: reverse "count" bytes starting at "buf" */
void memrev( char *buf, size_t count )
{
char *r;
In order to swap the values of two variables we need a temporary variable and it needs a
name. In fact the name may be temp. But if someone passes a variable that has a name temp, like
SWAP( int, a, temp), everything will collapse! So, we use the preprocessor argument
concatenation operator ## to create the name (here we get ab) from the actual variable names in
the call. This guarantees that the result won't be either of the actual arguments.
Using XOR(^) operator also we can write the above SWAP macro. Here is the code…
#define SWAP(datatype, a, b) \
(unsigned char *)x=(unsigned char *)(&(a)); \
A to Z of C 31
Exercises
1. Write a program that use only bitwise operators to multiply any number by 2.
2. Find out the difference between Unix’s text file and DOS’s text file. Write a program that
converts Unix based text file into DOS based text file, and vice-versa.
3. Implement your own data type for very very long integer (i.e., it should accept any
number of digits say, 899999998998998998998998989989). Use that data type to find
out factorial for any number.
Suggested Projects
1. Write source code colorizer software. Source code colorizer formats the given C file into
HTML file with necessary syntax highlighting. (Hint: You may need to know the
syntaxes of HTML)
2. Write a utility that indents the given C file. That is it should align the C code properly for
better clarity.
3. Solve all the questions in K&R. It’s really a tough project as no one achieved it
successfully!
11
“The more we talk, the less sense we make.”
Pointers
Pointers are a gift to C programmers. One of the important uses of pointers is the
dynamic memory allocation. So pointers work with ‘memory’. It necessitates the need to
understand jargons related to ‘memory’ and pointer implementations.
Here, we can find that var2 (“Hello”) is not terminated with a Null terminator (‘\0’). So
when we copy var2 to var1 using strcpy( ), the strcpy( ) routine will copy all the
character to var2 until it finds ‘\0’ in memory. So array overflow may result in memory
overwrite!
Here, the variable ptr is first initialized with malloc( ) and once again with address of
x. So the value that was returned by malloc( ) is definitely lost. Now we have memory leak
even if we call free( ) function, because the free( ) function must be called with the exact
value of the pointer returned by malloc( ).
The remedy for memory leak is to declare pointer constant. That is,
int *const ptr = malloc( sizeof( int ) );
ptr = &x; /* compiler error */
Now, the compiler will generate error. So, we are in safe from memory leak problem.
12.4.3 Version 3
You may also simulate a two-dimensional array with a single, dynamically-allocated one-
dimensional array.
int *array = (int *)malloc(rows * columns * sizeof(int));
36 A to Z of C
12.4.4 Version 4
Here is another version which uses pointers to arrays.
int (*array)[NO_OF_COLUMNS] =
(int (*)[NO_OF_COLUMNS])malloc(rows * sizeof(*array));
struct node
{
int data;
LNKLIST *next;
};
switch( opt )
{
case '1': /* Create/append Linked List */
do
{
p = start;
/* Traverse upto the last node to append */
A to Z of C 37
while( p->next!=NULL )
p = p->next;
q = (LNKLIST*)malloc(sizeof(LNKLIST));
printf( "\nEnter the data: " );
scanf( "%d", &q->data );
q->next = NULL;
if ( start==NULL )
start = q;
else
p->next = q;
printf( "Wanna continue? " );
} while( tolower( getchar( ) )=='y' );
break;
Code Obfuscation
The word obfuscate means “to confuse”. Code Obfuscation refers to confusing others
with your code. In other words, Code Obfuscation is the technical term for crypting your code
and preventing others from reading the code (Just opposite to Readability). Code Obfuscation is
very interesting to most of the C programmers. Every year we have The International
Obfuscated C Code Contest. Throughout the world most of the C programmers participate in
this contest. As far as I know no Indian has yet received this prize. So in this chapter let’s see the
most interesting Code Obfuscation.
13.2 Guidelines
char a[ ] = "ABCD"; /* string representation */
char b[ ] = "\x41\x42\x43\x44"; /* hexadecimal representation */
char c[ ] = "\101\102\103\104"; /* octal representation */
char d[ ] = "A" "B" "C" "D"; /* using string properties */
char e[ ] = {'A', 'B', 'C', 'D', '\0'}; /* using char propery */
In C all the above strings a, b, c, d and e represent “ABCD”. This is one of the simple
tricks used in code obfuscation.
main(l
,a,n,d)char**a;{
for(d=atoi(a[1])/10*80-
atoi(a[2])/5-596;n="@NKA\
CLCCGZAAQBEAADAFaISADJABBA^\
SNLGAQABDAXIMBAACTBATAHDBAN\
ZcEMMCCCCAAhEIJFAEAAABAfHJE\
TBdFLDAANEfDNBPHdBcBBBEA_AL\
H E L L O, W O R L D! "
[l++-3];)for(;n-->64;)
putchar(!d+++33^
l&1);}
Any idea about the above code? It prints the world map! Quite amazing isn’t it?
Output: World Map - New Delhi marked with " (obtained by executing whereami 29 77)
13.3.2 Note
Following is a part of note added by Westley.
Run the program as whereami <lat> <long>
Where lat and long correspond to your latitude and longitude.
To find the approximate place where this entry (The International Obfuscated C Code
Contest) was judged, type:
Run the program with your latitude & longitude as integer arguments; it will produce a
map made up of '!' with the given position marked with either a '"' (if the position is over a '!') or a
'#' (if the position is over a space). Southern latitudes and western longitudes are entered as
negative numbers. For example, to find San Francisco, run with "whreami 38 -122". The
resolution of the map is five degrees horizontally, ten degrees vertically. The map is a Mercator
projection with equal spacing of the latitudes, so the areas near the poles are very distorted.
Latitudes near the poles and Antarctica are not shown.
40 A to Z of C
The program requires the ASCII character set, putchar( ), atoi( ), and a display
that auto-wraps at 80 characters(!). If your display does not work this way, you will have to
massage the output; for example, you can pipe it to a file and edit it with an editor, which will do
autowrap for you.
If you run it with fewer than 2 arguments, it will likely give you an exception, as it will
access arguments that don't exist and characters before a string constant.
Logic
The map is printed as one long string of ' ' and '!' characters, with the autowrap used to
stack up slices of 80. The map data is a string; the first character is how many '!'s are printed
('A'=1, 'B'=2, etc), the second character is how many ' 's, the third is how many '!'s, etc. ASCII
characters less than 'A' print no characters but still change the polarity, so any map of ' 's and '!'s
is possible. This is done in the putchar( ) argument as "33^l&1", where l is the character
position+4; if l is odd, ' ' is printed, if l is even, '!' is printed.
The position of latitude & longitude is changed into a single character position within the
one long string via the first expression "d = latitude/10*80 - longitude/5 - offset" The latitude is
divided by ten because the vertical resolution is ten degrees, then multiplied by 80 because of the
80 character wrap (i.e. each ten degrees moves the position up or down one entire row). The
longitude is divided by five and added, because five degrees of change moves the location one
character. The signs are opposite because latitude is decreasing and longitude is increasing as
you go from upper left to lower right. The offset is where the origin (latitude=0, longitude=0) is
found.
The position counting down to zero changes the putchar( ) from printing ('!' or ' ') to
printing ('"' or '#').
The "H E L L O, W O R L D!" string inside the data string prints the line of blanks past
Tierra del Fuego and the last blank line. It's just for show, really.
Since the resolution is coarse, a few costal cities are shown to be just off the map; this is
an unavoidable artifact. The map is reasonably accurate. Here are some cities you might like to
try:
City Lattitude Longitude
New York 41 -74
London 52 0
Moscow 56 38
New Delhi 29 77
Sydney - 34 151
Los Angeles 34 -118
Paris 45 2
Beijing 40 116
Rio de Janeiro -23 -43
Tokyo 36 140
Part II
DOS Programming
“writing BASIC for the Altair was exhausting…Paul and I
didn’t sleep much and lost track of night and day. When I did fall
asleep, it was usually at my desk or on the floor. Some days I didn’t
eat…But after five weeks…world’s first microcomputer software
company was born.”
—Bill Gates
Courtesy: The Road Ahead (ISBN 0-14-024351-8)
14
“If you love to sleep, you will be poor.”
DOS Secrets
To program well, you have to know more about your hardware and DOS internals. This
book is neither a hardware book nor a beginners’ book. So I would slightly touch the hardware
and DOS internals in this chapter. In many Institutions hardware & software are being taught as
different subjects. And people don’t know how both are related. For system programming you
must know the relationship between the two. This chapter will help you to understand why a
programmer should know hardware & DOS internals for DOS programming.
14.1 Prelude
DOS (Disk Operating System) is the widely used operating system. It is a single-user
operating system. DOS is designed to provide an easy way to use disks for storage. It is very
efficient in controlling, accessing and managing the data from disk drives. The basic operations
performed by DOS are regulate space allocation, keep track of files, save and retrieve files and
manage other control functions associated with disk storage. Thus using DOS an interface is
made between the user and the computer. This DOS is same for all the systems. For loading this
DOS to the memory BIOS, bootstrap program, diagnostic testing programs are very essential and
we will discuss it in the coming sections.
14.1.1 BIOS
It is a program that provides link between the hardware and the operating system. It is a
firmware (Firmware is a program or data stored in ROM. These are not altered by software, and
are not lost when the power is turned off). Since it is stored in ROM, it is usually called as ROM
BIOS. It contains many low level routines. It is responsible for basic hardware operations such as
interactions with disk drives and keyboards. It also has drivers and other software that manages
the peripheral devices.
The basic operations performed by BIOS are
• Keyboard routine
• Video routines
• Printer routines
This BIOS program differs from system to system. For getting good results we can use
BIOS functions along with the DOS functions.
44 A to Z of C
Bootstrap Program
Floppy / Hard Disk
! ROM
Diagnostic Testing
BIOS
Operating System
Operating System Executive
B8888
512KB
In this method overlapping is possible. For Conventional ( Base )
example, we can get the same physical address in 256KB Memory
various segments and offset combinations.
0KB
B080 B880 B008 B808
8008 0088 8808 0808
B8888 B8888 B8888 B8888
Traits of Turbo C
In the First Chapter itself I told you that Turbo C++3.0 is the IDE that is used throughout
this book. If you’ve got Turbo C 2.0 or latter version of Turbo C, please get version 3.0. Why I
prefer Version 3.0 is, it is being helpful to explain DOS programming than any other versions.
• Set the default extension to C by Options > Editor > Extension > C
• Set tab size to 8 by Options > Editor > Tab > 8
Compiler, Assembler & Linker are usually command line executable files, which requires
filename(s) and other information as parameters. What IDE does is, it saves our time by invoking
the proper utilities with proper parameters within the Editor.
Note
Even in TASM, the default instruction sets are x286. To call x386 instruction, you
have to add .386. We will see this later!
15.4.3 TLINK
TLINK is used to link object files and library files and produces the executable file.
15.4.4 TLIB
Turbo library or TLIB is useful to manage, create library files.
15.4.5 MAKE
MAKE file seems to be like a batch file. Real programmers very often use this useful
utility.
15.4.6 TCC
TCC is a command line compiler. It is an integrated compiler. Using this you can create
assembly files, object files, and you can also create executable files directly.
A to Z of C 49
15.5 main( )
In contradict to ANSI C, Turbo C supports three arguments: argc, argv & env. argc
holds number of arguments passed in command line. argv is the array of pointer to the string in
command line. Under 3.X versions of DOS, argv[0] points to the full path name of the program
(e.g., C:\WAR\CHKMAIN.EXE). Under versions of DOS before 3.0, argv[0] points to null string.
argv[1] points to first string typed on command line after the program name. argv[argc]
contains NULL. env is an array of pointers to the string of environment variables.
Let’s see an example:
/* chkmain.c */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *env[])
{
int i;
printf("argc = %d \n", argc);
for( i=0; i<=argc; ++i)
printf("argv["%d"] = %s \n", i, argc[i]);
for( i=0; env[i] != NULL; ++i)
printf("env["%d"] = %s \n", i, env[i]);
return(0);
}
See argv[2] and arg[4]. In order to embed blanks we have put it in double quotes.
Turbo C sends all the three arguments (argc, argv, env) to its programs. But using the third
argument env is not a standard way. For standard programming use environ.
15.5.1 int main( ) or void main( ) ?
Turbo C accepts both int and void main( ) and Turbo C programmers use both int
and void main( ) in their programs. But in my opinion, void main( ) is not a standard
usage. The reason is, whenever a program gets executed it returns an integer to the operating
system. If it returns '0' means, the program is executed successfully. Otherwise it means the
program has been terminated with error.
Using a sample program, I have found that void main( ) returns 20 even after
successful completion of program (which means it returns wrong status to the operating system!).
/* intmain0.c */
int main( void )
{
printf( "int main returns 0 \n" );
50 A to Z of C
return(0);
} /*--main( )-----*/
/* intmain5.c */
int main( void )
{
printf( "int main returns 5 \n" );
return(5);
} /*--main( )------*/
/* voidmain.c */
void main( void )
{
printf( "void main returns? \n" );
} /*--main( )----*/
@ECHO OFF
REM *** Batch file to check return code (Testmain.bat) ***
CLS
intmain0.exe
ECHO %errorlevel% Note
intmain5.exe As I am working on Windows NT, I used
ECHO %errorlevel% %errrorlevel% in a batch file. In other platforms, it
voidmain.exe may not work. You may have to try different
ECHO %errorlevel% techniques to display the “errorlevel”.
REM *** end ***
@ECHO ON
After compiling all the C files to exe files, test the return values with TESTMAIN.BAT.
It shows the error value or status.
Thus we have found that int main( ) is the appropriate usage.
Note
However void main( ) will be useful in certain circumstances like programming for embedded systems &
real time operating system, because there is no place to return the status value. We will see those things
later!
We can also get status of main( ) by using the menu option COMPILE>Information…
from IDE without using BATCH file.
15.6 Preprocessor
Preprocessor performs macro substitutions, conditional compilation and inclusion of
named files. All these are done with controls like: #define, #if, #ifdef, #ifndef,
A to Z of C 51
#elif, #else, #line, #error, #pragma, #include. We’ve got several predefined
identifiers and macros that expand to produce special information (__LINE__, __FILE__,
__DATE__, __TINY__, etc)
/* chkhead.c */
#include "badhead.h"
int main( void )
{
PrintHello( );
return(0);
} /*--main( )-----*/
When we include the Badhead.h file in chkhead.c, file gets expanded. And so it prints
the message "Hello!", which is wrong according to the definition of static functions. K&R page-
83 says, "If a function is declared static, however, its name is invisible outside of the file in which
it is declared".
Now let’s see the right declaration of a header file.
/* Head.h */
#ifndef __HEAD_H /* OR if !define(__HEAD_H) */
#define TRUE ( 1 )
#define FALSE ( 0 )
typedef int BOOLEAN;
#endif
52 A to Z of C
If head.h file is included in our program, the compile time variable __HEAD_H will be
created. We can use it as a flag to check whether the file is already included or not.
The #ifndef __HEAD_H or #if !defined(__HEAD_H) helps us to avoid multiple
inclusion error. That is, if we don't use the above preprocessor control line and if we include
head.h more than one time in our program, we will get error. Now you would ask me where to
write the function PrintHello1( ) and PrintHello2( ). Yes, you have to write them in a
separate file and you have to create a library file or object file.
15.8 Pragma
#pragma is used to control the compiler.
15.8.1 Example 1
#pragma inline
tells the compiler that the C file contains inline assembly and the compiler will
use TASM to assemble the inline codes.
15.8.2 Example 2
Sometimes we write code that will be specific to memory models. In such a case our code
must be compiled in that memory model only (We have 6 different memory models: Tiny, Small,
Medium, Compact, Large and Huge). So programmers use conditional compilation method.
That is,
#ifndef __SMALL__ /* or #if !defined(__SMALL__) */
#error compile with small memory model
#elif
:
:
/* Program Codes */
#endif
There is of course a simple method to do this. That is to use pragma and to force the
compiler to compile in specified memory model.
That is,
#pragma -ms /* forces compiler to compile in small memory
model */
:
:
/* Program Codes */
:
A to Z of C 53
Attention! you cannot link the library file that is created in one memory model with
another file that is created in another model. So it is advisable to create library file for each
memory model.
If you write a effective library file, you can sell it without the source code! (Only a
narrow-minded people do that!)
When you set the compiler to ANSI standard, you can use the above keywords as
identifiers.
If you run the above program from IDE, you will get the following message:
Error: Not enough memory
So running only the EXE file of respective program in DOS Box will be the remedy.
15.12.2 delay( )
The delay( ) function found in dos.h is processor dependent. And it won't work on all
systems. The reason is the delay function is implemented with clock speed.
15.12.2.1 Solution with BIOS tick
An easy solution for this is to implement our own delay with the help of BIOS tick as:
/* PC bios data area pointer to incrementing unsigned long int */
#define BIOSTICK (*(volatile unsigned long far *)(0x0040006CL))
The BIOSTICK get incremented for every 18.2 times per second. But this is not much
preferred by the professional programmers.
A to Z of C 55
void deinit_timer(void);
/*------------------------------------------------
new_timer
Logic:
You don't have to call the old timer, but if you don't
you have to write some code to cleanup in de-init that
fixes DOS's internal clock.
Its also considered 'good form' to call the old int.
If everyone does, then everything that other TSR's etc...
may have installed will also work.
If you skip the little chunk of ASM code- the out 20-
you WILL LOCKUP all interrupts, and your computer
Anyways, this test replacement just increments a couple of
long ints. */
/*--------------------------------------------
init_timer
Logic:
see that 1st line of inline asm!
to set whatever clock speed you want load
bx with 1193180/x where x is the
clock speed you want in Hz. */
void init_timer(void)
{
slow_tick=fast_tick=0l;
oldtimer=getvect(8); // save old timer
asm cli
// speed up clock
asm {
mov bx, 19886 /* set the clock speed to
60Hz (1193180/60) */
mov al, 00110110b
out 43h, al
mov al, bl
out 40h, al
mov al, bh
out 40h, al
}
setvect(8, new_timer);
asm sti
}
/*--------------------------------
deinit_timer */
void deinit_timer(void)
{
asm cli
asm {
xor bx, bx // min rate 18.2 Hz when set to zero
mov al, 00110110b
out 43h, al
mov al, bl
out 40h, al
A to Z of C 57
mov al, bh
out 40h, al
}
asm sti
}
Then we can use the following code in main( ) to get a machine independent delay.
next_time=fast_tick + 3; /* fast tick is incremented by
the int8 ISR (global)*/
while( next_time>=fast_tick )
; /* wait */
struct foo
{
float a;
int b;
};
Just include the above piece of code in your file. You don’t need to call the above
function. If the above function gets linked, with your code, it would automatically force floating
point formats to be linked.
15.12.4 Null pointer assignment
You will get this message when you assign a value through a pointer without first
assigning a value to the pointer. Normally it would happen if you use strcpy( ) or memcpy( )
with a pointer as its first argument.
Your program may look as if it runs correctly, but if you get this message, bug will be
somewhere inside. The actual reason for the cause is you might have written, via a Null or
uninitialized pointer, to location 0000. Whenever TC finds exit( ) or returns from main( ), it
would check whether the location 0000 in your data segment contains different values from what
you started with. If so, you might have used an uninitialized pointer. That is, you may get the
error message irrespective of where the error actually occurred.
The remedy for this problem is to watch the following expressions with Add Watch
(Ctrl+F7):
*(char *)0,4m
(char *)4
If the values at these locations get changed, it means that the line just executed is the one
causing the problem.
16
“Do to others what you want them to do to you.”
Nothing can beat the efficiency of Assembly language. A good optimizing C compiler
will convert C file to a better assembly code. But a good human Assembly programmer can write
much more tight and efficient code. If you are such an efficient-superb Assembly programmer,
fortunately there is a way to link those assembly codes with C and so you can improve your
program.
Here we have used interrupts to print message. We can see more about interrupt
programming later.
16.1.2 Example 2
We can also use inline assembly in functions. Anything that is present in AX register will
be returned.
60 A to Z of C
/* main program */
int main( void )
{
printf( "5+100 = %Ld\n", Add( 5, 100 ) );
return(0);
} /*--main( )-------*/
So the result in AX gets returned automatically. But here you will get a warning. If you
are allergic to warning, you can shut it off by adding return( _AX ); in the last line.
Let’s see another efficient version of Add( ).
int Add ( int _AX, int _BX )
{
asm ADD AX, BX;
} /*--Add( )-------*/
The result in AX(upper word), DX(lower word) gets returned as long. Here you must not
use return( _AX); to shut off warning!
A to Z of C 61
We can also set our TC IDE to use Pascal calling function by OPTION > COMPILER >
PASCAL. in the command line TCC -p. When you use such Pascal calling conventions, you
must explicitly declare main( ) with cdecl as
int cdecl main( void )
62 A to Z of C
Note
As the Pascal calling convention ensures ‘In Order’ pushing, it produces tight & efficient code.
However it is a good practice to stick onto the C’s standard calling convention.
Here you might have noticed that we have prefixed underscore (_ ) with the name of the
function. That is because of the C's naming convention as discussed in the previous section. You
have to note that we are mating two different language i.e. C and Assembly. As we discussed,
when we compile a C file to OBJ file all the function names and identifiers are automatically
prefixed with underscore (_ ) by the compiler. So if we don't put up an underscore (_ ) here in
Assembly, we cannot link these files. If you find it odd to use an underscore (_ ) in front of
function name, then there is another way of declaring function i.e. to use 'C' keywords with
assembly directive as:
;File name: Hello2.asm
.MODEL small, C ;'C' used to set the assembly to C
; calling & naming convention
.DATA
msg DB "Hello!$"
.CODE
A to Z of C 63
PUBLIC PrintHello
PrintHello PROC NEAR
MOV AH, 9
MOV DX, OFFSET msg
INT 21h
RET
PrintHello ENDP
END
The 'C' keyword sets the assembler to use C calling convention and it automatically
prefixes underscore( _ ) with all procedures that are declared as EXTERN or PUBLIC. Here we
find that Hello2.asm "looks better" than Hello1.asm! So let’s use Hello2.asm.
The next step is to assemble the Hello2.asm to OBJ file. When you assemble, you must
assemble it with the case sensitive switch on. The assembler makes all PUBLIC labels into capital
letters by default, unless we use case sensitive switch -mx. Case sensitive is important, because C
language is case sensitive and we need "PrintHello" to be case sensitive. We can assemble the
Hello2.asm as:
C:\WAR>TASM -mx Hello2.asm Note
You can even assemble the
Now you will get Hello2.OBJ which contains Hello2.asm from IDE by choosing
PrintfHello procedure. ≡>Turbo Assembler
Note
If you don’t have TASM, you can use the available assemblers such as MASM, NASM etc. For the details
regarding the switches, see your assembler’s documentation.
Now we have to compile chkasm1.c and link Hello2.obj in the same time as:
Note
C:\WAR> tcc chkasm1.c Hello2.obj To compile chkasm1.c and link
Hello2.obj, you can also use project
Now you will get chkasm1.exe that you can run file instead of command line compiler
it under DOS. tcc.
64 A to Z of C
16.2.4 Example 2
; File name: Addnum.asm
.MODEL small, C
.CODE
PUBLIC Addnum
Addnum PROC NEAR USES BX, x: WORD, y: WORD
MOV AX, x
ADD AX, y
RET
Addnum ENDP
END
/* Chkasm2.c */
extern Addnum( int x, int y ); /* Addnum is written in
Addnum.asm */
int main( void )
{
printf( "5+100 = %d \n", Addnum( 5, 100 ) );
return(0);
} /*--main( )---*/
For more information on the switch of TLIB, see the Turbo C documentation.
17
“Remaining calm solves great problems.”
Processor
“Processor” and CPU (Central Processing Unit) refers the same—the heart of the
computer. It is a chip that is responsible for processing instructions.
17.1 Processors
The computing world came across so many processors. Each of the processors has its
own merits and demerits. The following table shows few of the known processors and its
characteristics.
return!!(((int(far*)())p)
()&(( 0x88 + (( 286 | 386 )*4))<<4));
} /*--Test386( )--------*/
If the code is run on a machine that don’t have 386 or 486, you may get a wrong output.
For better results we must use Assembly. (We can call it as a limitation of C language!).
17.3.2 Assembly routine to find processor type
The following Assembly routine is by Alexander Russell. Using this routine, we can
find out our processor type and coprocessor support. This routine can be called from C i.e. you
can link the object code with C program.
17.3.2.1 Assembly routines
To understand this Assembly module, read the comments provided in comment line.
;------------------------------------------------------
; Hardware detection module
;
; Compile with Tasm.
; C callable.
;------------------------------------------------------
.model medium, c
LOCALS
.386
CPUID MACRO
db 0fh, 0A2h
ENDM
.code
i86 equ 0
i186 equ 1
i286 equ 2
68 A to Z of C
i386 equ 3
i486 equ 4
i586 equ 5
;---------------------------------------------
; PC Processor detection routine
;
; C callable as:
; unsigned int x_processor( );
;
;
x_processor PROC
.8086
pushf ; Save flags
@@not_86_186:
is_not_286:
pop eax
mov ebx, eax
xor eax, 040000h ; toggle bit 18
push eax
popfd
pushfd
pop eax
popfd
sti
and eax, 040000h ; clear all but bit 18
and ebx, 040000h ; same thing
cmp eax, ebx
jne @@moretest
mov ax, i386
jmp short @@done
@@moretest:
cli
pushfd
pushfd
pop eax
mov ebx, eax
xor eax, 0200000h ; toggle bit 21
push eax
popfd
pushfd
pop eax
popfd
sti
and eax, 0200000h ; clear all but bit 21
and ebx, 0200000h ; same thing
cmp eax, ebx
jne @@moretest2
mov ax, i486
jmp short @@done
@@moretest2:
mov eax, 1
70 A to Z of C
CPUID
and eax, 0f00h
shr eax, 8
.8086
is_286:
mov ax,i286 ; We have a 286
jmp short @@done
ret
x_processor endp
.386
.8086
;--------------------------------------------------
; PC Numeric coprocessor detection routine
;
A to Z of C 71
; C callable as:
; unsigned int x_coprocessor( );
;
; Returns 1 if coprocessor found, zero otherwise
x_coprocessor PROC
LOCAL control:word
x_coprocessor endp
end
;---------------------------
17.3.2.2 Calling C program
#pragma –mm /* force to medium memory model */
i = x_processor( );
72 A to Z of C
if ( i > 6 )
i = 6;
;
; using MASM 6.11 Ml /c /Fl CPUID.ASM
;
; using TASM 4.00 TASM CPUID.ASM
;
; using older assemblers, you may have to use the following equate
; and eliminate the .586 directive
;
;CPUID equ "dw 0a20fh"
;
; bit flags for high eight bits of return value
;
HAS_NPU equ 01h
IS386_287 equ 02h
IS386SX equ 04h
CYRIX equ 08h
A to Z of C 73
.code
cpu_id proc
push bx
push cx
push dx
push bp
mov bp,sp
xor dx,dx ; result = 0 (UNKNOWN)
;**********************************************************************
; The Cyrix test
;
; Cyrix processors do not alter the AF (Aux carry) bit when
; executing an XOR. Intel CPUs (and, I think, all the others)
; clear the AF flag while executing an XOR AL,AL.
;
;**********************************************************************
TestCyrix:
mov al,0fh ;
aas ; set AF flag
xor al,al ; only Cyrix leaves AF set
aas ;
jnc Test8086 ;
or dh,CYRIX ; it's at least an 80386 clone
jmp Test486 ;
;**********************************************************************
;
; The 80186 or under test
;
; On <80286 CPUs, the SP register was decremented *before* being
; pushed onto the stack. All later CPUs do it correctly.
;
;**********************************************************************
Test8086:
push sp ; Q: is it an 8086, 80188, or
pop ax ;
cmp ax,bp ;
je Test286 ; N: it's at least a 286
;**********************************************************************
; The V20/V30 test
;
; NEC's CPUs set the state of ZF (the Zero flag) correctly after
74 A to Z of C
;**********************************************************************
; The 286 test
; Bits 12-15 (the top four) of the flags register are all set to
; 0's on a 286 and can't be set to 1's.
;
;**********************************************************************
Test286:
.286
mov dl,2 ; it's at least a 286
pushf ; save the flags
pop ax ; fetch 'em into AX
or ah,0f0h ; try setting those high bits
push ax ;
popf ; run it through the flags reg
pushf ;
pop ax ; now check the results
and ah,0F0h ; Q: are bits clear?
jz longTestNpu ; Y: it's a 286
;**********************************************************************
; The 386 test
A to Z of C 75
;
; The AC (Alignment Check) bit was introduced on the 486. This
; bit can't be toggled on the 386.
;
;**********************************************************************
Test386:
.386
mov dl,3 ; it's at least a 386
pushfd ; assure enough stack space
cli
and sp, NOT 3 ; align stack to avoid AC fault
pushfd ;
pop cx ;
pop ax ;
mov bx,ax ; save a copy
xor al,4 ; flip AC bit
push ax ;
push cx ;
popfd ;
pushfd ;
pop cx ;
pop ax ;
and al,4 ;
sti
xor al,bl ; Q: did AC bit change?
jnz Test486 ; N: it's a 386
.386P
;**********************************************************************
; The 386SX test
;
; On the 386SX, the ET (Extension Type) bit of CR0 is permanently
; set to 1 and can't be toggled. On the 386DX this bit can be
; cleared.
;**********************************************************************
mov eax,cr0
mov bl,al ; save correct value
and al,not 10h ; try clearing ET bit
mov cr0,eax ;
mov eax,cr0 ; read back ET bit
xchg bl,al ; patch in the correct value
mov cr0,eax ;
test bl,10h ; Q: was bit cleared?
jz TestNpu ; Y: it's a DX
or dh,IS386SX ; N: it's probably an SX
;**********************************************************************
76 A to Z of C
;**********************************************************************
; The Pentium+ tests
;
; First, we issue a CPUID instruction with EAX=0 to get back the
; manufacturer's name string. (We only check the first letter.)
;
;**********************************************************************
PentPlus:
.586
push dx ;
xor eax,eax ;
cpuid ;
pop dx ;
cmp bl,'G' ; Q: GenuineIntel?
jz WhatPent ; Y: what kind?
or dh,CYRIX ; assume Cyrix for now
cmp bl,'C' ;
jz WhatPent ;
xor dh,(CYRIX OR AMD) ;
A to Z of C 77
cmp bl,'A' ;
jz WhatPent ;
xor dh,(AMD OR NEXGEN) ;
cmp bl,'N' ;
jz WhatPent ;
xor dh,(NEXGEN OR UMC) ; assume it's UMC
cmp bl,'U' ;
jz WhatPent ;
xor dh,UMC ; we don't know who made it!
;**********************************************************************
; The Pentium+ tests (part II)
;
; This test simply gets the family information via the CPUID
; instruction
;
;**********************************************************************
WhatPent:
push edx ;
xor eax,eax ;
inc al ;
cpuid ;
pop edx ;
and ah,0fh ;
mov dl,ah ; put family code in DL
;**********************************************************************
; The NPU test
;
; We reset the NPU (using the non-wait versions of the instruction,of
; course!), put a non-zero value on the stack, then write the NPU
; status word to that stack location. Then we check for zero, which
; is what would be there if there were an NPU.
;
;**********************************************************************
TestNpu:
.8087
.8086
mov sp,bp ; restore stack
fninit ; init but don't wait
mov ax,0EdEdh ;
push ax ; put non-zero value on stack
fnstsw word ptr [bp-2] ; save NPU status word
pop ax ;
or ax,ax ; Q: was status = 0?
jnz finish ; N: no NPu
or dh,HAS_NPU ; Y: has NPU
78 A to Z of C
;**********************************************************************
; The 386/287 combo test
;
; Since the 386 can be paired with either a 387 or 287, we check to
; see if the NPU believes that +infinity equals -infinity. The 387
; says they're equal, while the 287 doesn't.
;
;**********************************************************************
cmp dl,3 ; Q: is CPU a 386?
jnz finish ; N: no need to check
infinities
fld1 ; load 1
fldz ; load 0
fdiv ; calculate infinity! (1/0)
fld st ; duplicate it
fchs ; change signs of top inf
fcompp ; identical?
push ax ;
fstsw word ptr [bp-2] ;
pop ax ;
test ah,40h ; Q: does NPU say they're
equal?
jz finish ; N: it's a 387
or dh,IS386_287 ;
finish:
mov ax,dx ; put our return value in place
pop bp ; clean up stack
pop dx ;
pop cx ;
pop bx ;
ret ;
cpu_id endp
END
;---------------------------
Exercises
1. Write a program that can find the current mode of processor (i.e., Real / Protected /
Virtual Mode).
18
“Practice hospitality.”
File Format
All except the text file (with .txt extension) use their own standards to save and organize
their instruction. For example, the EXE file put up "MZ" in its first two bytes. Thus each file got
its own architecture or File Format. If we know the file format of a particular file, we can read or
create those files. For example if we know the file format of BMP file, we can read it or even we
can create it. We must understand that each and every file type uses its own file formats. Each file
format has its own advantages and drawbacks. The software that creates a file of specific type
should be aware of its file format. For example, the Linker must know the file format of EXE file,
Paintbrush must know the file format of BMP file and so on.
Usually all files contain what is called as file header and it is nothing but the first few
bytes of a file. Each file type uses specific size for the file header. For example, the size of File
Header for EXE is 28 bytes, for BMP file it is 14 bytes. The file Header contains many useful
information such as its file types i.e. whether EXE or BMP or GIF. The file type is identified by
what is known as signature. The signature of the EXE file is "MZ", the signature of BMP file is
19778 and so on. After the File Header, the files may contain instructions or some other header.
For example, most of the image files have got the file header in the beginning, then color table
and then instructions.
If you know the file format you can do miracles. Most of the software vendors document
the file format whenever they introduce a new file type. But certain narrow-minded vendors may
keep the file format as secret. In such a case you have to crack the file format with the help of
certain software (usually DEBUG & simple C programs).
In this chapter, I just introduce the concept. But in the following chapters and in CD
you can see some real examples. You can get almost all file formats from the File Format
Encyclopedia that is available in the CD .
80 A to Z of C
18.1 Example
The following shows the file format of EXE file format:
• relocation table and the program load module follow the header
• relocation entries are 32 bit values representing the offset into the load
module needing patched
• once the relocatable item is found, the CS register is added to the value found
at the calculated offset
Suggested Projects
After reading all the chapters of this book only, you will get thorough ideas about file
formats and its usage. Then you can try the following projects:
1. Write your own EXE2BIN utility.
2. Remove relocation found in EXE files.
3. Check out all the available file formats in the File Format Encyclopedia found in the
CD . Crack the file types for which file format is not yet available and try to document
the file format. (Of course it is illegal!) (Hint: Use DEBUG or simple C programs to read
byte by byte)
4. Write your own compression utility and thus develop your own file format for that.
Compare its efficiency with PKZIP.
5. Write software to split and join files. For the good quality, it needs that you have to use
your own file Header or file format.
6. Write a BMP file creator (i.e. Paintbrush) in high resolution VESA mode. The software
has to use both mouse and graphics stylus as input devices.
7. Write a PDF to TXT (text) conversion utility.
8. Write your own image creation utility that uses MP3 compression algorithm and thus
develop your own file format for that.
9. Add help (the one which always get invoked when we press Ctrl+F1) for the library that
you created. For example if you create a mouse library, and you have InitMouse( )
function, when you press Ctrl+F1, you should get the help for that function. (Hint: You
should know the file format of Turbo C's help file).
19
“Think before you speak.”
Interrupt Programming
Interrupt is the one which temporarily suspends the execution of the current program and
executes a specific subroutine called interrupt routine and then resumes the execution of actual
program. Many people think that the interrupt instruction 'INT' is one of the "basic" instructions
in assembly language. But it is not so. The 'INT' instruction just calls or invokes a specific routine
i.e., interrupt routine.
Here, you see that the behavior of the interrupt routine is determined by the argument that
passes through (Some book authors use the term input values instead of argument. But
professional programmers use the term argument). The value passed through the register AH is
referred as function value. In special cases, value is also passed through AL register to the sub-
function. Sometimes we would also pass values through other registers.
Some interrupt routines don’t take any argument, which means we don't need to pass
value through registers. For example, the interrupt for Print Screen int 5h doesn't take any
argument. The prototype of int 5h hence looks like:
int5h( void )
{
MOV ...
:
:
}
Usually interrupt numbers, function numbers and sub-function numbers are represented
in hexadecimal rather that in decimal.
Now we will get getvid.obj. We can link this obj file with the main program.
; File name: Getvid.asm
.MODEL small, C
.CODE
PUBLIC GetVideoMode
GetVideoMode PROC NEAR
MOV AH, 0Fh
INT 10h ; AL register holds current video mode
XOR AH, AH ; Set AH register to 0
; Now, AX holds value of AL
RET ; value in AX get returned
GetVideoMode ENDP
END
geninterrupt( 0x10 );
return(_AL);
} /*--GetVideoMode( )-------*/
Here you have to note that intr( ) functions doesn't return anything, there is no way to
represent AL or AH register separately.
19.3.6 Benchmarking
We can find that the inline assembly style and pure assembly style are faster than any
other above methods. Big software companies use "Pure Assembly Style". They create library file
with assembly language and link them wherever necessary. Inline assembly is my choice, because
it provides more readability, C style usage and flexibility. For example in C, we can directly enter
octal or hexadecimal or decimal number as
int a = \101 ; /* Octal */
int b = \x65 ; /* Hexa */
int c = 65 ; /* decimal */
But we cannot directly enter binary values in C (But it is possible in Assembly!). One
solution for this is to use strtol( ) as:
int a;
char str[] = "0000010"; /* binary */
86 A to Z of C
char *endptr;
/* radix should be 2 for binary in strol... */
a = strtol( str, &endptr, 2 );
Fortunately inline style provides more flexibility and an easy way for entering binary
values:
Exercises
1. Write a program that find out the life of battery found on your motherboard.
Suggested Projects
1. Write diagnostic software that finds the status of your peripherals and motherboard.
20
“Truth will continue forever.”
To get a display we have to add a component called video adapter with the motherboard.
Hardware engineers sometimes call this video adapter as video card. On the video card we can
see a group of video RAM chips. The video card may have upto
8MB in board, but most of them are used by circuits on the card Video
and cannot be directly accessed by processor. In the basic VGA RAM
mode (e.g., DOS mode, Windows safe mode ), the processor can
Video
directly access upto 128KB (i.e., A0000h to BFFFFh ) of video BIOS
RAM . Usually all video cards also have onboard video BIOS
normally addressed at C0000h TO C7FFFh.
Video Adapter
20.1 Memory map
Not all the memory is used for display purpose because, we have so many video modes
that support different resolutions. The video modes are usually set by the programs that are stored
in video ROM BIOS area. Note that it is ROM, which means you cannot write into it! Whereas in
video RAM, you can write! But you should know in which display mode, which memory area is
used. You can use far pointers to write into video RAM. Since VGA and SVGA adapters are
used almost everywhere, here I have given the memory map for VGA and SVGA. Other
adapters’ memory map will be slightly different. If
A0000 Graphics Mode
you use other adapters, refer its documentation.
bytes are being used namely character byte and attribute byte. The character byte contains the
ASCII value of the character. The attribute byte is organized as:
The following program fills the screen with ’C’ with given attributes.
#include <dos.h>
But programmers prefer the declaration, that we used in the above program, because it
provides good readability and helps us to clearly identify segment address and offset address.
20.2.1.1 Codes
#include <dos.h>
void WriteCh2VidRAM( int vdupage, int x, int y, char ch, int attribute )
{
FP_SEG( Vid_RAM ) = 0xb800;
FP_OFF( Vid_RAM ) = 0x0000;
You can use the above functions for normal use. For better programming, you should add
condition to check whether the character is on the last row of the screen. In such a case, you have
to scroll the screen upward by 1 row.
20.2.1.2 cprintf( )
We have written our functions to directly write into video RAM. But Turbo C also has
got inbuilt functions like cprintf() & cputs() (defined in conio.h) to directly write into
video RAM. The global variable directvideo determine whether the console output (by
cprintf, cputs… functions) go directly to video RAM (directvideo = 1;) or go via ROM
BIOS calls (directvideo = 0;). The default value is directvideo = 0. To use
directvideo = 1, the system’s video hardware must be be identical to IBM’s display adapter.
The functions of interest in this context are window(), clrscr(), textcolor(),
textbankground(), textattr(), gettextinfo(), highvideo(),
normalvideo().
Following is the example program:
#include <conio.h>
return(0);
} /*--main( )----------*/
Exercises
1. Write a program that finds number of video pages supported by your Video RAM for
each mode.
2. Find out the reason, why graphics mode occupies more video memory. (Why graphics
mode is slower than text mode?)
21
“Money that comes easily disappears quickly.”
Programming Ports
Ports can be thought of as hardware connection ports where devices with input/output
lines connect to a bus. The CPU has ports for each of its bus: at least ISA (Industry Standard
Architecture) and memory, for the simplest CPU. So using the port addresses we can access
hardware devices. For example CMOS is accessed via port 70h and 71h. The port can be Read &
Write (R/W), or Read only, or Write only.
21.5 Example
Here I am giving an example program to find the scan code of a key using port 60h.
Exercises
1. Find out the ports used by different peripherals. (Hint: Look into Ralf Brown’s Interrupt
List)
2. Find out the port used by your mouse. Use the details to write a mouse driver program.
22
“Pride leads only to shame.”
22.1 Secrets
22.1.1 Keyboard controller
Normally nobody uses PC/XT (8bit) systems, we use AT (16/32/64bit) systems. So I
think it is enough to explain the secrets of keyboard in AT systems.
In a typical AT system, the microcontroller(8048,6805 type) in the keyboard sends data
to the keyboard-controller (8042 type) on the motherboard. Controller found on the motherboard
can also send data back to the keyboard.
In detail, a keyboard consists of set of switches mounted in a grid (key matrix). When
you press a key on the keyboard the micro controller in keyboard reads the key switch location in
the key matrix, then it sends data to keyboard-controller on the motherboard. When the keyboard-
controller on the motherboard receives data, it signals the motherboard with an IRQ1 and sends
data to the main motherboard processor via I/O port address 60h. The function of the keyboard-
controller on the motherboard is to translate scan codes and perform other functions. We can use
I/O port 64h(R/W) to check the status of the keyboard-controller on the motherboard.
Note
Some people call the keyboard-controller on the motherboard as keyboard BIOS
Note
Scan code is different from ASCII code. The upper and lower case is determined by the state of shift keys,
not solely by which key is pressed
Keyboard Buffer
:
:
:
:
OFFSET=0080h :
kbufbegin
H (ASCII code)
35 (Scan code in decimal)
OFFSET=001Ah E
kbufhead 18
L
38
OFFSET=001Ch L
kbuftail 38
0
24
OFFSET=0082h
kbufend
The keyboard buffer is organized as a circular queue. It has four 2-byte wide pointers:
kbufbegin, kbufend, kbufhead and kbuftail. Here you should note one important thing:
these pointers are just 2-byte wide (not 4-byte wide), which means these pointers hold only the
OFFSET address (all are at segment 0040h). kbufbegin and kbufend points to the beginning
and end of the keyboard buffer and these pointers do not move. Whereas the kbufhead and
kbuftail points to the character on the keyboard buffer and so these pointers do move.
Keyboard buffer is a character (i.e., 1 byte wide) array. The size of the keyboard buffer
may vary from system to system. Some people say that the size of the keyboard buffer is 32
bytes, which is wrong, because the size of the keyboard buffer can be changed. Keyboard buffer
holds ASCII code and scan code on alternate bytes.
Whenever a key is been inputted through keyboard, it is being temporarily stored in
keyboard buffer, before it is processed by the BIOS. When we try to input more keystrokes, we
will get a beep sound indicating that the keyboard buffer is full. The pointer kbuftail points to
the recently inputted key and the pointer kbufhead points to the key that is being currently
processed. So when the keyboard buffer is empty, the pointer kbufhead and kbuftail holds
the same address (i.e., points to the same data).
A to Z of C 95
#define ON (1)
#define OFF (0)
The function SetKbdStatus( ) is used to change the status of the keyboard. The
status lights, on recent keyboards may not reflect the change. In that case you may call INT 16,
AH=2 (GetShiftFlags( )) to update the lights.
96 A to Z of C
/*------------------------------------------------
SendKeyControl - Receives the command
'cmd' and returns 1 for success */
do
{
byte = inportb( KEYSTATUS );
} while ( byte & OB_FULL );
byte = inportb( KEYDATA );
/*------------------------------------------------------------
Stuffkey.c
stuff chars into the BIOS keyboard buffer then exit
*----
*/
#include <string.h>
#include <dos.h>
/*------------------------------------------------------
Stuff - stuffs ch into BIOS keyboard buffer */
disable( );
*kbuffer++ = ch;
enable( );
} /*--Stuff( )---------------*/
char ch;
char temp[200];
if ( argc > 1 )
for ( i=1; i < argc; ++i )
{
strcpy( temp, argv[i] );
switch ( temp[0] )
{
case '0':
ch = atoi( temp );
Stuff( ch );
break;
default:
for ( j=0; temp[j] != '"' && temp[j]; ++j )
Stuff( temp[j] );
}
}
else
{
printf( "Use: STUFFKEY 027 013 a b \"hi there\"<ENTER>\n");
printf( "Parms that start with zero are ascii codes\n" );
printf("Generaly only useful called from inside a batch file\n");
}
return(0);
} /*--main( )----------*/
According to theory, keyboard buffer stores both ASCII and scan codes in alternate
bytes. But the above code stuffs only ASCII code. So the success of the above code depends upon
the reading program written in BIOS. For me the above code works fine. If it doesn’t work for
you, try to stuff scan code too and it should work.
char *Keys_Tbl[88] = {
/* 1..8 */ "Escape", "1", "2", "3", "4", "5", "6", "7",
/* 9..15 */ "8", "9", "0", "-", "=", "Backspace", "Tab",
/* 16..25 */ "q", "w", "e", "r", "t", "y", "u", "i", "o", "p",
100 A to Z of C
/* 26..29
*/ "[", "]", "Enter/KeypadEnter", "Left/RightCtrl",
/* 30..39
*/ "a", "s", "d", "f", "g", "h", "j", "k", "l", ";",
/* 40..42
*/ "'", "`", "LeftShift/PrintScreen",
/* 43..45
*/ "\\(101-keyOnly)/#(102-keyOnly)", "z", "x",
/* 46..53
*/ "c", "v", "b", "n", "m", ",", ".", "/",
/* 54..55
*/ "RightShift", "Keypad*/PrintScreen",
/* 56..59
*/ "Left/RightAlt", "Spacebar", "Caps Lock", "F1",
/* 60..67
*/ "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9",
/* 68..70
*/ "F10", "NumLock/Pause", "ScrollLock",
/* 71..72
*/ "Home/Keypad7", "UpArrow/Keypad8",
/* 73..74
*/ "PageUp/Keypad9", "Keypad-",
/* 75..76
*/ "LeftArrow/Keypad4", "Keypad5",
/* 77..78
*/ "RightArrow/Keypad6", "Keypad+",
/* 79..80
*/ "End/Keypad1", "DownArrow/Keypad2",
/* 81..82
*/ "PageDown/Keypad3", "Insert/Keypad0",
/* 83..85
*/ "Delete/Keypad.", "undefined", "undefined",
/* 86..88
*/ "\(102-keyOnly)", "F11", "F12"
};
BOOLEAN Key_Stat[88];
int main( void )
{
int i, key;
while( (key=inportb(0x60))!=ESC )
{
/* Store the status of keys... */
if ( key<128 )
Key_Stat[key-1] = PRESSED;
else
Key_Stat[key-1-128] = RELEASED;
/* Now, show the status... */
for ( i=0; i<88 ; ++i )
if ( Key_Stat[i]==PRESSED )
printf( "%s ", Keys_Tbl[i] );
printf( "\n" );
}
return(0);
} /*--main( )----------*/
Exercises
1. Write getch( ) and kbhit( ) functions without using any interrupt. (Hint: use
keyboard buffer)
2. Write a program that temporarily lock or freeze the system. (i.e. to lock keys)
3. Write a program to find out the size of the keyboard buffer.
A to Z of C 101
4. Write a function ASCII2Scan( ) that returns scan code for the given ASCII value using
system resources i.e., don’t pre-calculate the values. (It’s really a tough job! Hint: you
have to crack the driver file)
5. Write a “running lights” program using CAPSLOCK, NUMLOCK & SCROLLLOCK
LEDs.
Suggested Projects
1. Write software to increase or decrease the size of the keyboard buffer.
2. Use stuff key techniques and interfacing techniques to input keys from other devices.
23
“Let your gentleness be evident to all.”
Sound Programming
with PC Speaker
Sound programming can be classified as with PC speaker and with sound blaster card. In
this chapter, let’s see sound programming with PC speaker.
23.1 Introduction
Almost all systems have PC speaker. People who like to have digitized sound go for
MIDI card or sound blaster card. But for normal operations, it is enough to have PC speaker.
1.9 MHz
Formula to calculate N =
f
Now let’s write our own sound( ) and nosound( ) function to produce sound.
#define ON (1)
#define OFF (0)
/*------------------------------------------------
ChangeSpeaker - Turn speaker on or off. */
ChangeSpeaker( ON );
TC also has sound( ) and nosound( ) functions. If you don’t want to write your own
code, you can use those built-in functions.
104 A to Z of C
where Offset is the “distance” between note 'A' and the note in semitones.
Using the above formula, any part of the frequency table can be calculated. The following
program demonstrates this.
#include <math.h>
char *Note_Names[] =
{
"A",
"B Flat",
"B",
"C",
"C Sharp",
"D",
"E Flat",
"E",
"F",
"F Sharp",
"G",
"G Sharp"
};
int main( void )
{
double frequency;
int offset;
for( offset=0; offset<13; ++offset )
{
frequency = 440.0 * pow( 2.0, offset / 12.0 );
printf( "The Frequency of %s is %f Hz\n",
Note_Names[offset%12], frequency );
}
return(0);
} /*--main( )--------*/
#include <stdio.h>
#include <conio.h>
#include <dos.h>
void ClrKeyBrdBuffer(void)
{
outportb( 0x20, 0x20 ); /* reset PIC */
while( bioskey(1) ) /* read all chars until it empty */
bioskey( 0 );
} /*--ClrKeyBrd( )------*/
108 A to Z of C
Exercise
1. Using program find out the frequency and delay used for ordinary beep sound that is
produced by printf("\a");. Do not use any gadgets or Trial and Error Techniques.
Suggested Projects
1. Write software that plays MIDI files through PC speaker.
24
“Patience is better than strength.”
Sound Programming
with sound card
To have digitized sound, people install sound cards. Sound cards are necessary for music
software. Yet, sound cards don’t have any standard. Each manufacturing company produces
sound cards with its own standard. So programming for sound card won’t be unique. And we
must know the standards used by each and every manufacturer. If I start explaining all the sound
cards, it will really be boring. So I left the reader to program for his own sound card as an
exercise. Few example codes are available in CD . Hope that might be useful to you.
24.1 Idea
Normally, sound cards are accompanied with manuals. In that manual, you can find the
standards used by that particular sound card. The basic idea is that you have to load frequency
value to the register of the sound card. These registers are normally accessed via I/O ports. I/O
ports’ details are available on Ralf Brown’s Interrupt List.
Suggested Projects
1. Write software that plays WAV, MIDI files through sound card.
2. I have already explained multiple keys input concept. Yet I haven’t come across a
Piano software that can work with multiple keys and sound card. If you can write
such a software, it will be the world’s first one! (Hint: Use Ctrl or Alt key for
“sustain”)
25
“Show respect for all people.”
Mouse Programming
As everyone knows, mouse is one of the inputting devices. In this chapter, I explain
interrupts for mouse programming and a few concepts regarding mouse programming. In the
graphics programming, we can see more examples. To work with mouse, we must have mouse
driver file mouse.com.
#endif
25.2.2 Mouselib.c
#include "mouselib.h"
#pragma inline
/*-----------------------------------------------
InitMouse - Initializes Mouse.
Returns 0 for success. */
A to Z of C 111
/*-----------------------------------------------
ShowMousePtr - Shows Mouse Pointer. */
/*-----------------------------------------------
HideMousePtr - Hide Mouse Pointer. */
/*-----------------------------------------------
MoveMousePtr - Move Mouse Pointer
to (x, y). */
/*-----------------------------------------------
RestrictMousePtr - Restrict Mouse Pointer
to the specified coordinates */
/*-----------------------------------------------
GetMousePos - Gets Mouse position &
mouse button value. */
25.2.3 Mouselib.lib
When you compile the above Mouselib.c file to a library file for Small memory model,
you will get Mouselib.lib file. You can use the library – Mouselib.lib in your projects..
A to Z of C 113
;***************************************************************
;* *
;* Assembly language hook for CMOUSE library event handler *
;* *
;* Assemble with /Ml switch *
;* *
;***************************************************************
; real code for real men
; adjust for proper memory model
.MODEL SMALL,C
.CODE
PUBLIC mouse_event_func,mouse_int
mouse_event_func DD ?
END
;------------------------------------------------
#define ESC 27
typedef struct
{
unsigned int flags, x, y, button_flag;
} mouse_info_t;
#define MAX_MOUSE_EVENTS 10
#define MOUSE_MOVE 1
#define MOUSE_L_DN 2
#define MOUSE_L_UP 4
#define MOUSE_R_DN 8
#define MOUSE_R_UP 16
/*----------------------------------------------
mouse_handler - the low level interrupt
handler calls this */
mouse_info[tail].x = mouse_x;
mouse_info[tail].y = mouse_y;
mouse_info[tail].button_flag = button_stat;
mouse_info[tail].flags = flags;
tail++;
if ( tail == MAX_MOUSE_EVENTS )
tail=0;
if ( tail == head )
{
head++;
if ( head == MAX_MOUSE_EVENTS )
head=0;
}
/*------------------------------------------------
init_mouse - is there a mouse, install int
handlers */
short init_mouse( void )
{
unsigned short c_seg, c_off;
asm{
xor ax, ax
int 033h
if ( mouse_present )
{
/* install our own handler */
mouse_event_func = mouse_handler; /* global func pointer */
c_seg = FP_SEG(mouse_int);
c_off = FP_OFF(mouse_int);
asm{
mov ax, c_seg
mov es, ax
mov dx, c_off
mov ax, 0ch
116 A to Z of C
mov ax, 8
mov cx, 0
mov dx, 239
int 033h
mov mouse_x, cx
mov mouse_y, dx
}
}
return(mouse_present);
} /*--init_mouse( )---------*/
/*------------------------------------------------
deinit_mouse - deinstall our mouse handler
*/
void deinit_mouse( void )
{
if ( mouse_present )
{
/* deinstall our mouse handler by making int 33 never call it */
asm{
mov ax, 0ch
xor cx, cx /* mask == 0, handler never called */
int 033h
Exercise
1. Write all mouse functions using interrupt programming. Then find out each
function’s use. (Hint: Get into graphics mode for better results)
Suggested Projects
1. Write a mouse driver program.
2. Write mouse functions that doesn’t use interrupts or mouse.com. (Hint: You have to
use ports)
26
“Riches gotten by doing wrong have no value.”
Programmers so often praise C for its pointers. Pointers are more powerful! In this
chapter, let’s see some of the interesting programs that use pointers.
#undef MK_FP
#undef peekb
/* an array of characters */
char idbytes[10]={
'\x00', '\x9A', '\xFF', '\xFE', '\xFD',
'\xFC', '\xFB', '\xFA', '\xF9', '\xF8'};
/* an array of strings */
char *idstrings[]={
"Not In Our List",
"a COMPAQ plus",
"an IBM PC",
"a PC XT or Portable PC",
"a PC jr.",
"a Personal Computer AT or PS/2 Model 50 or 60",
"a PC XT after 1/10/86",
"a PS/2 Model 30",
120 A to Z of C
typedef struct{
unsigned char modelbyte;
char idinfo[66];
}MODELINFO;
char *captions[3]={
"\nGETMODEL.EXE by Bill Buckels 1990\n\n",
"This Computer is ",
"The BIOS release date is "};
void getmodelinfo(void)
{
/* a pointer to our MODELINFO's info */
MODELINFO *modelinfo;
for(byte=0;byte<num_records;byte++)
{
modelinfo[byte].modelbyte = idbytes[byte];
strcpy(modelinfo[byte].idinfo,
idstrings[byte]);
}
TSR Programming
TSR or “Terminate and Stay Resident” Programming is one of the interesting topics in
DOS Programming. TSR programs are the one which seems to terminate, but remains resident in
memory. So the resident program can be invoked at any time. Few TSR programs are written
with the characteristic of TCR (Terminate Continue Running) i.e., TSR program seems to
terminate, but continues to run in the background. TSR Programming is supposed to be an easy
one, if you know the DOS internals. In this chapter, I have tried to explain the tough TSR
Programming concept in a simpler manner.
can also use this flag in our TSR program to check whether DOS is busy or not. For that, we have
to use undocumented DOS function 34h.
Normally, TSR programmers capture Keyboard interrupt (int 9h), Control-C interrupt (int
23h), Control-break interrupt (int 1bh), Critical error interrupt (int 24h), BIOS disk interrupt (int
124 A to Z of C
13h), Timer interrupt (int 1ch) and DOS Idle interrupt (int 28h). Indian TSR programmers often
use int 8h as Timer interrupt. But other international TSR programmers use int 1ch as Timer
interrupt.
The idea is that we have to block Control-C interrupt, Control-break interrupt and Critical
error interrupt. Otherwise, there is a chance that the control will pass onto another program when
our TSR program is in action. And it will spoil everything!
We must also monitor other interrupts—Keyboard interrupt, BIOS disk interrupt, Timer
interrupt and DOS Idle interrupt, and we have to chain them. I hope by looking at the figure, you
can understand the concept better.
#include <stdio.h>
#include <dos.h>
#include <stdlib.h>
#include <conio.h>
#define TRUE 1
#define FALSE 0
typedef struct {
int bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,fl;
} IREGS;
unsigned scancode;
unsigned keymask;
oldtimer = getvect(TIMER);
old28 = getvect(INT28);
oldkb = getvect(KYBRD);
olddisk = getvect(DISK);
/* ------- attach vectors to resident program ------- */
setvect(TIMER, newtimer);
setvect(KYBRD, newkb);
setvect(INT28, new28);
setvect(DISK, newdisk);
/* -------- compute program's size -------- */
sizeprogram = myss + ((mysp+50) / 16) - _psp;
/* -------- terminate and stay resident -------- */
keep(0, sizeprogram);
}
return hotkey_flag;
}
if (test_hotkeys(inportb(0x60))) {
/* reset the keyboard */
kbval = inportb(0x61);
outportb(0x61, kbval | 0x80);
outportb(0x61, kbval);
outportb(0x20, 0x20);
}
else
(*oldkb)();
}
if (_osmajor < 3) {
/* --- save interrupted program's psp (DOS 2.x) ---- */
intpsp = peek(dosseg, *psps);
/* ------- set resident program's psp ------- */
for (pp = 0; pp < pspctr; pp++)
poke(dosseg, psps[pp], _psp);
}
else {
/* ----- save interrupted program's psp ------ */
intpsp = getpsp();
/* ------ set resident program's psp ------- */
_AH = 0x50;
_BX = _psp;
geninterrupt(DOS);
}
}
if (_osmajor < 3) {
/* --- reset interrupted psp (DOS 2.x) ---- */
for (pp = 0; pp < pspctr; pp++)
poke(dosseg, psps[pp], intpsp);
}
else {
/* ------ reset interrupted psp ------- */
_AH = 0x50;
_BX = intpsp;
geninterrupt(DOS);
}
}
_SP = mysp;
_SS = myss;
oldcrit = getvect(CRIT);
oldbreak = getvect(CTRLBRK);
oldctrlc = getvect(CTRLC);
setvect(CRIT, newcrit);
setvect(CTRLBRK, newbreak);
setvect(CTRLC, newbreak);
ctrl_break = getcbrk(); /* get ctrl break setting */
setcbrk(0); /* turn off ctrl break logic */
intdta = getdta(); /* get interrupted dta */
setdta(mydta); /* set resident dta */
resident_psp(); /* swap psps */
enable();
disable();
interrupted_psp(); /* reset interrupted psp */
setdta(intdta); /* reset interrupted dta */
setvect(CRIT, oldcrit); /* reset critical error */
setvect(CTRLBRK, oldbreak);
setvect(CTRLC, oldctrlc);
setcbrk(ctrl_break); /* reset ctrl break */
_SP = intsp; /* reset interrupted stack */
_SS = intss;
enable();
if (unloading)
unload();
running = FALSE;
}
df = _DS - _psp;
/* --- walk through mcb chain & search for TSR --- */
while (peekb(mcbs, 0) == 0x4d) {
blkseg = peek(mcbs, 1);
if (peek(blkseg, 0) == 0x20cd) {
/* ---- this is a psp ---- */
if (blkseg == _psp)
break; /* if the transient copy */
132 A to Z of C
disable();
/* ------- search for matches on the psp in dos -------- */
while (pspctr < 2 &&
(unsigned)((dosseg<<4) + adr) < (mcbseg<<4)) {
if (peek(dosseg, adr) == _psp) {
/* ------ matches psp, set phoney psp ------- */
_AH = 0x50;
_BX = _psp + 1;
geninterrupt(DOS);
/* ---- did matched psp change to the phoney? ----- */
if (peek(dosseg, adr) == _psp + 1)
/*---- this is a DOS 2.x psp placeholder ----*/
psps[pspctr++] = adr;
/* ----- reset the original psp ------ */
_AH = 0x50;
_BX = _psp;
geninterrupt(DOS);
}
adr++;
}
enable();
}
return;
}
/* --- another TSR is above us, cannot unload --- */
putch(7);
}
27.9 PC-PILOT
In the last section we have seen the TSR Template that will be very useful for writing any
TSR software. In this section, I just present the main program only. You can see how the TSR
template (Tsr.c) is used in Pcpilot main program.
/*
PCPILOT.C - This is the main( ) module for PCPILOT.EXE.
It should be compiled in the small or tiny memory model.
*/
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <scr.h>
#include <kbd.h>
void PcPilot(void);
void Initialize(void);
static int videomode(void);
void TitleScreen(void);
/* #define DEBUG */
char signature[] = "PC-PILOT";
extern unsigned _heaplen = 12288;
extern unsigned _stklen = 1024;
extern unsigned scancode[], keymask[];
extern int unloading; /* To UnInstall TSR */
void main(int argc, char *argv[])
{
while (--argc > 0) {
++argv;
if (**argv != '-')
break;
if (tolower(argv[0][1]) == 'x') {
Initialize();
PcPilot();
return;
}
}
Initialize();
*scancode= 76; /* Alt(8) - '5'(76) on the keypad */
*keymask = 8;
tsr(PcPilot, TitleScreen);
}
typedef struct {
char *str;
int y;
} MENU;
MENU m[] = {
" Ascii Table ", 8,
" Box Characters ", 9,
" Hex/Dec/Binary ", 10,
" Keyboard Codes ", 11,
" Ruler ", 12,
" Color Codes ", 13,
" Printer Setup ", 14,
A to Z of C 135
void PcPilot()
{
ScrGetCur(&oldx, &oldy, 0);
HideCur();
ScrPush();
DrawMenu();
for (;;) {
HighLight(1);
switch (K = KbdGetC()) {
case UP:
case LEFT:
HighLight(0);
if (--Idx < 0) Idx = 8;
break;
case DN:
case RIGHT:
HighLight(0);
if (++Idx > 8) Idx = 0;
break;
case PGUP:
case HOME:
HighLight(0);
Idx = 0;
break;
case PGDN:
case END:
HighLight(0);
Idx = 8;
break;
case RET:
if (Idx == 7 || Idx == 8) {
if (Idx == 7) unloading = 1;
ScrPop(1);
ScrSetCur(oldx, oldy, 0);
return;
}
136 A to Z of C
if (Idx == 4) {
ScrPop(1);
Ruler();
ScrPush();
DrawMenu();
}
ExecuteMenuOptions(Idx);
break;
case ESC:
ScrPop(1);
ScrSetCur(oldx, oldy, 0);
return;
default:
if ((K = K&0x00ff) != 0) {
if (!strchr("abhkrcpue", tolower(K)))
break;
HighLight(0);
switch (tolower(K)) {
case 'a': Idx = 0; break;
case 'b': Idx = 1; break;
case 'h': Idx = 2; break;
case 'k': Idx = 3; break;
case 'r': Idx = 4;
ScrPop(1);
Ruler();
ScrPush();
DrawMenu();
break;
case 'c': Idx = 5; break;
case 'p': Idx = 6; break;
case 'u': Idx = 7;
unloading = 1;
case 'e': Idx = 8;
ScrPop(1);
ScrSetCur(oldx, oldy, 0);
return;
default : continue;
}
HighLight(1);
ExecuteMenuOptions(Idx);
}
break;
}
}
}
A to Z of C 137
ShadowBox(31,5,48,19, 2, BorderClr);
PutStr(32,6, TitleClr, " PC - PILOT ");
PutStr(31,7, BorderClr,"╞════════════════╡");
PutStr(31,17,BorderClr,"╞════════════════╡");
PutStr(32,18,FooterClr," %c %c <Esc> exits", 24,25);
vmode = videomode();
if ((vmode != 2) && (vmode != 3) && (vmode != 7)) {
printf("Must be in 80 column text mode.\n");
exit(1);
}
InitScr();
if (VideoMode == MONO) {
BorderClr = 0x0f;
TitleClr = 0x0f;
TextClr = 0x07;
FooterClr = 0x0f;
HighlightClr = 0x70;
}
}
r.h.ah = 15;
return int86(0x10, &r, &r) & 255;
}
Suggested Projects
1. Write a Screen Thief utility. The Screen Thief will capture the screen, when a hot-
key is pressed. Depending upon the mode you set, when you load the TSR, Screen
Thief will store the screen into BMP or GIF or JPEG.
Part III
Advanced Graphics Programming
Graphics Programming can be classified into:
1. Graphics with BGI
2. Mode 13h Programming
3. VESA Programming
28
“Without leadership a nation falls.”
BGI stands for Borland Graphics Interface. Working with BGI refers to working with
driver files (with BGI extension). So we are in need of BGI files that are to be initialized with
initgraph( ) function. Programming with BGI is considered to be quite old. In my
experience, BGI is used only by Indian Programmers! Other International Programmers use mode
13h. Even though BGI is slow, we can do lots of graphics with it. It will be highly beneficial for
the beginners.
One of the common mistakes very often committed by Indian Programmers is to use
DETECT macro with initgraph( ) as shown above. First of all we must know what
DETECT will do in a program: it automatically detects the system’s graphics adapter and chooses
the mode that provides the highest resolution for that adapter. So we must understand that
DETECT may detect a mode, which we might not expect! And it will be a very serious problem!
If you write a program for 640x480 resolution, and if DETECT detects a mode that has only
320x200 resolution, you cannot see a part of the image. It is a costly mistake!
So the right declaration for a bug free program is:
int gdriver = VGA, gmode = VGAHI;
initgraph( &gdriver, &gmode, "c:\\tc\\bgi");
Another problem with DETECT is that even if you have SVGA it will detect VGA.
packages to support SVGA. Jordan Hargraphix Software’s SuperVGA/Tweak BGI drivers are the
widely used drivers.
• SuperVGA 16-color
0) Standard EGA/VGA 320x200x16
1) Standard EGA/VGA 640x200x16
2) Standard EGA/VGA 640x350x16
3) Standard VGA 640x480x16
4) SuperVGA/VESA 800x600x16
5) SuperVGA/VESA 1024x768x16
6) SuperVGA/VESA 1280x1024x16
• SuperVGA 256-color
0) Standard VGA/MCGA 320x200x256
1) 256k Svga/VESA 640x400x256
2) 512k Svga/VESA 640x480x256
3) 512k Svga/VESA 800x600x256
4) 1024k Svga/VESA 1024x768x256
5) 256k Svga 640x350x256
6) 1280k+ VESA 1280x1024x256
• SuperVGA 32768-color
0) 320x200x32768
1) 640x350x32768
2) 640x400x32768
3) 640x480x32768
4) 800x600x32768
5) 1024x768x32768
6) 1280x1024x32768
A to Z of C 143
• SuperVGA 65536-color
0) 320x200x65536
1) 640x350x65536
2) 640x400x65536
3) 640x480x65536
4) 800x600x65536
5) 1024x768x65536
6) 1280x1024x65536
• Tweaked 16-color
0) 704x528x16
1) 720x540x16
2) 736x552x16
3) 752x564x16
4) 768x576x16
5) 784x588x16
6) 800x600x16
• Tweaked 256-color
0) 320x400x256
1) 320x480x256
2) 360x480x256
3) 376x564x256
4) 400x564x256
5) 400x600x256
6) 320x240x256
7) 360x350x256
144 A to Z of C
• S3 Accelerator 16/256/32768-color
0) 640x480x256
1) 800x600x256
2) 1024x768x256
3) 800x600x16
4) 1024x768x16
5) 1280x960x16
6) 1280x1024x16
7) 640x480x32768
Turbo C++3.0’s setcolor( ) function was not written with upward compatibility.
setcolor( ) function receives ‘integer’ value as color value. So setcolor( ) function
cannot work if we provide a ‘long’ value (a value above 32767, say 50000). Inorder to make the
setcolor( ) function to work, Jordan Hargraphix Software’s graphics functions use certain
rules. More details and documentation are found on CD !
• Support for the mouse cursor in 16, 256, 32k, 64k and true color SuperVGA modes,
as well as tweaked 16 and 256 color modes.
• Support for a graphical text mode cursor (ala Norton)
• Support for the hardware cursor on systems that support it. (Cirrus 54xx, S3,
Paradise)
• Easy to use API so you can use the mouse cursor in your own programs. (without
needing to use Jordan Hargraphix Software’s SuperVGA/Tweak BGI drivers).
• Large cursor support (currently up to 32x32).
• Ability to set the cursor foreground and background colors.
• Bitmap cursor support (multicolored mouse cursors).
29
“People with understanding want more knowledge.”
VB Controls
Using graphics with BGI, we can create VB like controls: Forms, textboxes, command
buttons etc. In this chapter let us see how to create few VB like controls.
29.1 Paintbrush
The following program is a Demo Paintbrush program. This program uses: command
buttons, Windows and Frame. Paintbrush coders usually find difficulty in implementing mouse
drawings. Here, I give you few guidelines.
29.1.1 Restricting Mouse Pointer
When the mouse is clicked on the drawing area, you must restrict it so that outside of the
drawing should not be affected.
29.1.2 Hiding/Showing Mouse Pointer
You must properly hide/show mouse pointer. When you want to paint on the drawing box
using putpixel( ) or anything else, first of all hide the pointer, paint (using putpixel( ) )
and then do not forget to ‘show’ mouse pointer! I could see, even the commercial software—
Adobe’s Instant Artist fails to use this logic! So the logic is hide-paint-show.
29.1.3 Avoiding Flickering of Mouse Pointer
When you would hide and show the pointer repeatedly, it usually starts flickering. So use
‘hide-paint-show’ logic, only when the current mouse position is not equal to previous mouse
position. If the current mouse position is equal to previous mouse position, don’t do anything!
29.1.4 Using setwritemode( ) function
When you draw line with the so called ‘rubber-band technique’, you may find that the
existing images will get erased. We can avoid such ‘erasing’ with setwritemode(XOR_PUT).
As we know XOR is used for ‘toggling’, we can utilize it to avoid ‘erasing’.
146 A to Z of C
/*--------------------------------------------------------------------
Mini Paintbrush for VB Controls demo
*---
*/
#include <dos.h>
#include <graphics.h>
#include "mouselib.h"
struct RecButtonCoord
{
int x1;
int y1;
int x2;
int y2;
};
void far MyOuttextxy( int x, int y, char far *str, int color );
void MyRectangle( int x1, int y1, int x2, int y2, int upcolor, int
lowcolor );
void InitVB( void );
void InitScreen( void );
void VBForm( int x1, int y1, int x2, int y2, char *title );
void VBFrame( int x1, int y1, int x2, int y2 );
void VBDrawBox( int x1, int y1, int x2, int y2 );
void CmdButton( int cmdno, int status );
int CmdButtonVal( int x, int y );
void ShowStatus( int msgno );
/*-----------------------------------------------
MyOttextxy - Prints text with
specified color */
void far MyOuttextxy( int x, int y, char far *str, int color )
{
setcolor( color );
outtextxy( x, y, str );
} /*--MyOuttextxy( )-----------*/
/*-----------------------------------------------
MyRectangle - Rectangle with
upcolor for Ú, lowcolor for Ù.
It's for Command Button effect. */
void MyRectangle( int x1, int y1, int x2, int y2, int upcolor, int
lowcolor )
{
setcolor( upcolor );
line( x1, y1, x2, y1 );
line( x1, y1, x1, y2 );
setcolor( lowcolor );
line( x1, y2, x2, y2 );
line( x2, y1, x2, y2);
} /*--MyRectangle( )-------------*/
148 A to Z of C
/*-----------------------------------------------
InitVB - Initializes VB.
ie, Checks errors. */
/*-----------------------------------------------
InitScreen - Initializes Screen. */
/*-----------------------------------------------
VBForm - Creates a Window with the given title. */
void VBForm( int x1, int y1, int x2, int y2, char *title )
{
setfillstyle( SOLID_FILL, LIGHTGRAY );
bar( x1, y1, x2, y2 );
setfillstyle( SOLID_FILL, BLUE );
bar( x1+4, y1+3, x2-5, y1+22 );
MyOuttextxy( x1+13, y1+10, title, WHITE );
MyRectangle( x1+1, y1, x2-1, y2-1, WHITE, BLACK );
} /*--VBForm( )-----------*/
/*-----------------------------------------------
VBFrame - Creates VB like Frame. */
/*-----------------------------------------------
VBDrawBox - Creates Drawing Box. */
/*-----------------------------------------------
CmdButton - Draws Command Button for
specified status.
status are NORMAL, PRESS */
/*-----------------------------------------------
CmdButtonVal - Returns Command Button value. */
/*-----------------------------------------------
ShowStatus - Display messages. */
/*-----------------------------------------------
main - Main of VB */
while( stayin )
{
/* if ESC is pressed, then quit! */
if ( kbhit( ) )
stayin = ( getch( )!=ESC );
y2 = y1 = my;
/* Note! in XOR_PUT mode, you must
setcolor to 'WHITE-brushcolor'
*/
setwritemode( XOR_PUT );
setcolor( WHITE-brushcolor );
do
{
GetMousePos( &mbutton, &mx, &my );
if ( mx!=x2 || my!= y2 )
{
HideMousePtr( );
line( x1, y1, x2, y2 );
line( x1, y1, mx, my );
ShowMousePtr( );
x2 = mx;
y2 = my;
}
} while(mbutton==LFTCLICK);
setwritemode( COPY_PUT );
/* Note! in COPY_PUT mode, you must
setcolor to 'brushcolor'
*/
setcolor( brushcolor );
HideMousePtr( );
line( x1, y1, mx, my );
ShowMousePtr( );
}
RestrictMousePtr( 0, 0, 640, 480 );
}
}
}
closegraph( );
return( 0 );
} /*--main( )---------*/
29.2 Note
For mouse inputs, here I have used request mode and so it won’t be much efficient. If you
need more precision, use event mode to get mouse inputs.
A real VB control uses object-oriented concepts. So for the exact implementation, you
have to go for C++.
Suggested Projects
1. Yet I haven’t seen a full VB imitated controls library. If you could code all VB
controls, you can even sell that library!
30
“Plans fail without good advice.”
Scribble
Scribble is a CHR file creator developed with graphics with BGI. It will be a good
example of coding style, using mouse routines, graphics with BGI, library & project file creation
and file format.
30.1 Prelude
CHR files are used for generating fonts in Turbo C’s graphics programs. Except for
default font, we need the corresponding CHR file to display respective fonts. For example,
inorder to display ‘Gothic’ fonts, we need GOTH.CHR file. Scribble is a CHR (or font) file
creator. When I developed this utility, I thought that there is no utility to create CHR files. But
later I came to know that Borland also provides ‘Font Editor’ to create CHR file. When you
compare ‘Scribble’ and Borland’s ‘Font Editor’, you can find that the mouse support in Borland’s
Font Editor is worse! When I developed Scribble, I thought that CHR file format is
undocumented. And so I cracked the CHR file format. But later I came to know that it is
documented. So my view about CHR file format may slightly differ from Borland’s official
documentation. I suggest you to have a glance at the CHR file format on file format collection.
30.4 Mouselib.lib
30.4.1 Mouselib.h
#ifndef __MOUSELIB_H
A to Z of C 155
#endif
30.4.2 Mouselib.c
#include "mouselib.h"
#pragma inline
/*-----------------------------------------------
InitMouse - Initializes Mouse.
Returns 0 for success. */
/*-----------------------------------------------
ShowMousePtr - Shows Mouse Pointer. */
/*-----------------------------------------------
HideMousePtr - Hide Mouse Pointer. */
156 A to Z of C
/*-----------------------------------------------
MoveMousePtr - Move Mouse Pointer
to (x, y). */
/*-----------------------------------------------
RestrictMousePtr - Restrict Mouse Pointer
to the specified coordinates */
/*-----------------------------------------------
GetMousePos - Gets Mouse position & mouse button value. */
INT 33h;
30.4.3 Mouselib.lib
Using the above Mouselib.c file compile it to library file for Small memory model, you
will get Mouselib.lib file. You can use the library – Mouselib.lib in your projects.
30.5 Scribble.h
/*----------------------------------------------------------------------
Scribble Declarations
scribble.h
*----
*/
struct ButtonStatus
{
int x1;
int y1;
int x2;
int y2;
};
#define MAXBRUSH (10)
#define THANKS (1)
#define FSIZEERR (2)
"11100000",
"11100000",
"11100000",
"00000000",
"01100000",
"11110000",
"11110000",
"01100000",
"01111000",
160 A to Z of C
"11111100",
"11111100",
"01111000"
};
void far MyOuttextxy( int x, int y, char far *str, int color );
void MyRectangle( int x1, int y1, int x2, int y2, int upcolor, int
lowcolor );
void PutPoint( int x, int y, int btype );
void ScribbleLine ( int x1, int y1, int x2, int y2, int btype );
void ScribbleInfo( void );
void InitScribble( void );
void GWindow( int x1, int y1, int x2, int y2, char *title );
void SetScreen( void );
void GetFontName( char *str );
void FileSizeIndicator( void );
void Clear( void );
void CmdButton( int cmdno, int status );
int CmdButtonVal( int x, int y );
void BrushBox( int brushno, int status );
int BrushVal( int x, int y );
void MsgWindow( char *fontname, int msgno );
int X4CenteredMsg( char *str );
void MakeFontProcedure1( void );
void MakeFontProcedure2( void );
void MakeFontProcedure3( void );
void CloseScribbleFiles( void );
30.6 Scribble.c
/*---------------------------------------------------------------------
Scribble
( CHR file creator )
by
R. Rajesh Jeba Anbiah,
#include <dir.h>
#include <graphics.h>
#include "mouselib.h"
#include "scribble.h"
FONTINFO fInfo;
WORD charoffset;
BYTE charwidth;
FILE *chOffFp, *wthFp, *chInfoFp, *scriFp;
/*-----------------------------------------------
MyOttextxy - Prints text with
specified color */
void far MyOuttextxy( int x, int y, char far *str, int color )
{
setcolor( color );
outtextxy( x, y, str );
} /*--MyOuttextxy( )-----------*/
/*-----------------------------------------------
MyRectangle - Rectangle with
upcolor for ┌, lowcolor for ┘.
It's for Command Button effect. */
void MyRectangle( int x1, int y1, int x2, int y2, int upcolor, int
lowcolor )
{
setcolor( upcolor );
line( x1, y1, x2, y1 );
line( x1, y1, x1, y2 );
setcolor( lowcolor );
line( x1, y2, x2, y2 );
/*-----------------------------------------------
PutPoint - Point with a specified
pattern ( brush type ).
Pattern is stored in *Pixel_Mask[]
It's for Brush effect. */
/*-----------------------------------------------
ScribbleLine - Draws line a specified
pattern ( brush type ).
Logic: Bresenham's Line Algorithm.
It's for Brush effect. */
void ScribbleLine ( int x1, int y1, int x2, int y2, int btype )
{
int x, y, dx, dy, p, incrx, incry;
dx = abs(x2 - x1);
dy = abs(y2 - y1);
incrx = (x2 >= x1)? 1 : -1;
incry = (y2 >= y1)? 1 : -1;
else
{
y += incry;
p += 2 * (dy - dx);
}
PutPoint( x, y, btype );
}
}
else
{
A to Z of C 163
p = 2 * dx - dy;
while( y != y2 )
{
y += incry;
if ( p < 0 )
p += 2 * dx;
else
{
x += incrx;
p += 2 * ( dx - dy );
}
PutPoint( x, y, btype );
}
}
PutPoint( x2, y2, btype );
} /*--ScribbleLine( )---*/
/*-----------------------------------------------
ScribbleInfo - Prints the information
about Scribble. */
"╟───────────────────────────────────────────────────────────╢ \r\n"
);
textcolor( LIGHTGREEN );
cprintf(
"║ For any Suggestions Bug report ║ \r\n"
"║ Sending donations ║ \r\n"
"║ visit Scribble's official page: ║ \r\n"
"║ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ║ \r\n"
"╟───────────────────────────────────────────────────────────╢ \r\n"
"║ Copyright (c) April 2001, R. Rajesh Jeba Anbiah ║ \r\n"
"║ All Rights Reserved. ║ \r\n"
"╚═══════════════════════════════════════════════════════════╝ \r\n"
);
textcolor( LIGHTBLUE+BLINK );
cprintf(
" Press any Key... \r\n"
);
window( 31, 2, 65, 5 );
textcolor( WHITE );
cprintf( "▄" );
textcolor( GREEN );
cprintf( "\r\n ▀" );
getch( );
window( 10, 2, 75, 25 );
textcolor( GREEN );
_setcursortype( _NORMALCURSOR );
} /*--ScribbleInfo( )---------*/
/*-----------------------------------------------
InitScribble - Initializes Scribble.
ie, Checks errors. */
/*-----------------------------------------------
GWindow - Creates a Window with the given title. */
void GWindow( int x1, int y1, int x2, int y2, char *title )
{
setfillstyle( SOLID_FILL, LIGHTGRAY );
bar( x1, y1, x2, y2 );
setfillstyle( SOLID_FILL, BLUE );
bar( x1+4, y1+3, x2-5, y1+22 );
MyOuttextxy( x1+13, y1+10, title, WHITE );
MyRectangle( x1+1, y1, x2-1, y2-1, WHITE, BLACK );
} /*--GWindow( )-----------*/
/*-----------------------------------------------
SetScreen - Initializes Screen. */
if ( i == MAXBRUSH/2-1 )
{
y = 252;
x = 267-50;
}
}
setcolor( BLACK );
A to Z of C 167
setcolor( WHITE );
for ( i=0, x=290 ; i<5 ; x += 50, ++i )
PutPoint( x, 262, i );
But_Stat[0].x1 = 155;
But_Stat[0].y1 = 270;
But_Stat[0].x2 = 205;
But_Stat[0].y2 = 290;
CmdButton( 0, NORMAL );
But_Stat[3].x1 = 375;
But_Stat[3].y1 = 340;
But_Stat[3].x2 = 425;
But_Stat[3].y2 = 360;
CmdButton( 3, NORMAL );
But_Stat[4].x1 = 290;
But_Stat[4].y1 = 335;
But_Stat[4].x2 = 340;
But_Stat[4].y2 = 355;
But_Stat[5].x1 = 270;
But_Stat[5].y1 = 270;
But_Stat[5].x2 = 320;
But_Stat[5].y2 = 290;
But_Stat[6].x1 = 330;
But_Stat[6].y1 = 270;
But_Stat[6].x2 = 380;
But_Stat[6].y2 = 290;
/*-----------------------------------------------
GetFontName - Gets Font Name & checks
whether the file is already exist or not.
If exist, it prompt with Warning */
while( stayin )
{
GWindow( 190, 200, 450, 300, "Happy Scribbling!" );
MyOuttextxy( 213, 249, "Enter the Font Name", BLACK );
MyOuttextxy( 213, 269, "( 4 Characters )", BLACK );
setfillstyle( SOLID_FILL, WHITE );
bar( 375, 245, 420, 260 );
MyRectangle( 375, 245, 420, 260, BLACK, WHITE);
i = 0;
while( i<len )
{
if ( BIOSTICK > nexttick )
A to Z of C 169
{
MyOuttextxy( x, y, cursor, cursorcolor );
cursorcolor ^= ( BLACK ^ WHITE );
nexttick = BIOSTICK + 7L;
}
if ( kbhit( ) )
{
MyOuttextxy( x, y, cursor, WHITE );
ch[0] = toupper( getch( ) );
if ( ch[0]==0 ) /* Ignore special characters */
getch( );
if ( i!=0 && ch[0]=='\b' )
{
ch[0] = str[--i];
x -= textwidth( cursor );
MyOuttextxy( x, y, ch, WHITE );
}
else if ( ch[0]!=' ' && ch[0]!='*' && ch[0]!='+'
&& ch[0]!='=' && ch[0]!='[' && ch[0]!=']'
&& ch[0]!='|' && ch[0]!='\\' && ch[0]!='\"'
&& ch[0]!=':' && ch[0]!=';' && ch[0]!='<'
&& ch[0]!=',' && ch[0]!='>' && ch[0]!='.'
&& ch[0]!='?' && ch[0]!='/'
&& !(iscntrl(ch[0])) )
{
str[i++] = ch[0];
MyOuttextxy( x, y, ch, BLACK );
x += textwidth( cursor );
}
}
}
str[i] = '\0';
strcpy( filename, str );
strcat( filename, ".CHR" );
if ( findfirst( filename, &ffblk, 0 ) == 0 ) /* File already
exist! */
{
GWindow( 190, 200, 450, 300, "Warning!" );
strcpy( tmpmsg, filename );
strcat( tmpmsg, " already exist!" );
MyOuttextxy( 213, 234, tmpmsg, RED );
MyOuttextxy( 213, 248, "Overwrite existing file?", BLACK );
CmdButton( NOBUTTON, NORMAL );
CmdButton( YESBUTTON, NORMAL );
MyOuttextxy( 289, 277, "No", BLACK );
MyOuttextxy( 343, 277, "Yes", BLACK );
x -= len * textwidth( cursor );
170 A to Z of C
ShowMousePtr( );
do
{
cmdno = 0;
GetMousePos( &mbutton, &mx, &my );
if ( mbutton==LFTCLICK )
{
cmdno = CmdButtonVal( mx, my );
if ( cmdno==NOBUTTON || cmdno==YESBUTTON )
{
HideMousePtr( );
CmdButton( cmdno, PRESS );
ShowMousePtr( );
prevcmdno = cmdno;
do
{
GetMousePos( &mbutton, &mx, &my );
cmdno = CmdButtonVal( mx, my );
} while( mbutton==LFTCLICK&&cmdno==prevcmdno);
HideMousePtr( );
CmdButton( prevcmdno, NORMAL );
ShowMousePtr( );
}
}
} while( cmdno!=NOBUTTON && cmdno!=YESBUTTON );
stayin = ( cmdno==NOBUTTON );
HideMousePtr( );
}
else
stayin = FALSE;
}
for ( i=0; i<len ; ++i )
scriFh.fntName[i] = str[i];
putimage( 190, 200, buffer, COPY_PUT );
farfree( buffer );
} /*--GetFontName( )--------*/
/*-----------------------------------------------
FileSizeIndicator - Indicates the file
size limitation of 32KB */
void FileSizeIndicator( void )
{
int xmax = 269 + 0.007999 * (16 + 3*scriFh.noOfChars + ftell(
chInfoFp )) ;
if ( xmax > 420 )
A to Z of C 171
} /*--FileSizeIndicator( )--------*/
/*-----------------------------------------------
Clear - Clears the drawing box */
/*-----------------------------------------------
CmdButton - Draws Command Button for
specified status.
status are NORMAL, PRESS */
/*-----------------------------------------------
CmdButtonVal - Returns Command Button value. */
--i;
return( i );
} /*--CmdButtonVal( )----------*/
/*-----------------------------------------------
BrushBox - Draws Brush Box for
specified status.
status are NORMAL, PRESS */
/*-----------------------------------------------
BrushVal - Returns Brush value. */
/*-----------------------------------------------
MsgWindow - Prompts with messages "Thank you!",
"Error!", "About...". */
HideMousePtr( );
imgsize = imagesize( 150, 155, 490, 370 );
if ((buffer = farmalloc(imgsize)) == NULL)
{
CloseScribbleFiles( );
closegraph( );
cprintf( "\r\nError: Not enough memory!\r\n\a" );
exit(1);
}
getimage( 150, 155, 490, 370, buffer );
switch( msgno )
{
case FSIZEERR:
xx = X4CenteredMsg("Error:Cannot create more fonts!");
MyOuttextxy( xx, 281, "Error: Cannot create more fonts!", RED );
xx = X4CenteredMsg( "Reason: File size is limited" );
MyOuttextxy( xx, 294, "Reason: File size is limited", RED );
xx = X4CenteredMsg( "Suggestion: Try small fonts" );
MyOuttextxy( xx, 307, "Suggestion: Try small fonts", RED );
xx = X4CenteredMsg( "Quitting..." );
MyOuttextxy( xx, 320, "Quitting...", RED );
case THANKS:
xx = X4CenteredMsg( "Thanks for using Scribble!" );
MyOuttextxy( xx, 240, "Thanks for using Scribble!",
BLACK );
xx = X4CenteredMsg( "May God bless you!" );
174 A to Z of C
/*-----------------------------------------------
X4CenteredMsg - Returns X coordinate value
for the center justified message
in MsgWindow.
┌────────────┐
Logic:( 150, y ) │ msg │ ( 490, y )
└────────────┘
To have centered msg,
150 + ((490-150)-textwidth(msg))/2. */
/*-----------------------------------------------
MakeFontProcedure1 - Creates the first font
ie, ' ' ( space ) */
fInfo.x = 0;
fInfo.y = charwidth;
fInfo.op1 = CHARSCAN1;
fInfo.op2 = CHARSCAN2;
fwrite( &fInfo, sizeof( fInfo ), 1, chInfoFp );
fInfo.op1 = EOFCHAR1;
fInfo.op2 = EOFCHAR2;
fwrite( &charoffset, sizeof( charoffset ), 1, chOffFp );
fwrite( &charwidth, sizeof( charwidth ), 1, wthFp );
temporary file */
++scriFh.noOfChars;
/* Scans the drawing box...
To find character's xmin, xmax, ymin, ymax.
Steps: ( top to bottom )
top : ------>
: ---> ----->
...
...
bottom: ---->
*/
xmin = 209; xmax = 149;
ymin = 256; ymax = 221;
for ( y = 221 ; y<=256 ; ++y )
for ( x = 149 ; x<=209 ; ++x )
{
if ( getpixel( x, y ) == BLACK )
{
if ( x<xmin )
xmin = x;
if ( y<ymin )
ymin = y;
if ( x>xmax )
xmax = x;
if ( y>ymax )
ymax = y;
}
}
/* Drawing box empty?
( No character? )
check...
*/
if ( xmin==209 && xmax==149 ) /* if no character */
charwidth = 0;
else
charwidth = xmax - xmin + 4;
fwrite( &charwidth, sizeof( charwidth ), 1, wthFp );
if ( charwidth==0 )
charoffset = 0;
else
charoffset = ftell( chInfoFp );
fwrite( &charoffset, sizeof( charoffset ), 1, chOffFp );
A to Z of C 177
/*-----------------------------------------------
CloseScribbleFiles - Closes all Scribble
files and then deletes the
temporary files. */
remove( "~$scrib1.raj" );
remove( "~$scrib2.raj" );
remove( "~$scrib3.raj" );
} /*--CloseScribbleFiles( )-----------*/
/*-----------------------------------------------
main - Main of Scribble */
int main( void )
{
int mx, my, premx, premy,
mbutton, cmdno, prevcmdno, bno, prevbno = 0, msgno = THANKS;
long fontsize;
char ch[2] = "!", fontname[10];
BOOLEAN stayin = TRUE;
ScribbleInfo( );
A to Z of C 179
InitScribble( );
SetScreen( );
GetFontName( fontname );
strcat( fontname, ".CHR" );
if ( ( scriFp = fopen( fontname, "wb+" ) ) == NULL )
{
CloseScribbleFiles( );
closegraph( );
cprintf( "Fatal Error(04): File cannot be created \r\n\a" );
exit( 1 );
}
MakeFontProcedure1( );
FileSizeIndicator( );
ShowMousePtr( );
while( stayin )
{
GetMousePos( &mbutton, &mx, &my );
if ( mbutton==LFTCLICK )
{
if ( mx>=149 && mx<=209 && my>=223 && my<=256 ) /* drawing
box */
{
if ( prevbno>4 )
setcolor( WHITE );
else
setcolor( BLACK );
RestrictMousePtr( 150+(prevbno%5)/2,
221+(prevbno%5)/2, 208-(prevbno%5)/2,
255-((prevbno+1)%5)/2 );
premx = mx;
premy = my;
HideMousePtr( );
PutPoint( mx, my, prevbno%5 );
ShowMousePtr( );
do
{
GetMousePos( &mbutton, &mx, &my );
if ( premx!=mx || premy!=my )
{
HideMousePtr( );
ScribbleLine(premx,premy,mx, my, prevbno%5 );
ShowMousePtr( );
180 A to Z of C
premx = mx;
premy = my;
}
} while(mbutton==LFTCLICK);
RestrictMousePtr( 0, 0, 639, 479 );
}
bno = BrushVal( mx, my );
if ( bno!=MAXBRUSH && bno != prevbno )
{
HideMousePtr( );
BrushBox( prevbno, NORMAL );
BrushBox( bno, PRESS );
prevbno = bno;
ShowMousePtr( );
}
cmdno = CmdButtonVal( mx, my );
if ( cmdno!=MAXCMDBUTTON && cmdno!= OKBUTTON
&& cmdno!=NOBUTTON && cmdno!=YESBUTTON )
{
HideMousePtr( );
CmdButton( cmdno, PRESS );
ShowMousePtr( );
prevcmdno = cmdno;
do
{
GetMousePos( &mbutton, &mx, &my );
cmdno = CmdButtonVal( mx, my );
} while( mbutton==LFTCLICK && cmdno==prevcmdno );
HideMousePtr( );
CmdButton( prevcmdno, NORMAL );
ShowMousePtr( );
stayin = ( cmdno!=QUIT );
}
switch( cmdno )
{
case CLEAR:
Clear( );
break;
case NEXT:
HideMousePtr( );
MakeFontProcedure2( );
FileSizeIndicator( );
Clear( );
++ch[0];
fontsize = 16 + 3*scriFh.noOfChars +
ftell( chInfoFp );
if ( fontsize >= 30000 )
A to Z of C 181
{
msgno = FSIZEERR;
stayin = FALSE;
}
else if ( ch[0]==0 )
stayin = FALSE;
if ( ch[0]!=0 && fontsize<30000 )
{
settextstyle(DEFAULT_FONT, HORIZ_DIR, 4 );
MyOuttextxy( 150, 225, ch, BLACK );
settextstyle(DEFAULT_FONT, HORIZ_DIR, 1 );
}
ShowMousePtr( );
break;
case QUIT:
HideMousePtr( );
MakeFontProcedure2( );
ShowMousePtr( );
break;
case ABOUT:
MsgWindow( fontname, ABOUT );
}
}
}
MakeFontProcedure3( );
MsgWindow( fontname, msgno );
closegraph( );
return( 0 );
} /*--main( )---------*/
30.7 Scribble.prj
We use project (.PRJ) file to create standalone program. By the term standalone, we
mean the EXE file that doesn’t require any other (supporting) files for its execution.
Normally in BGI programming, we would supply the driver (BGI) files’ directory with
initgraph( ) function. If the corresponding BGI file is not found on that directory you would
get error message. We get this error message because, the driver files are not added with our
program. But if you have added the corresponding object (OBJ) file of the driver, to
graphics.lib library, you won’t get such error. You can use BGIOBJ utility to create object
file for the driver (BGI & CHR) files.
C:\>BGIOBJ /F egavga
Note
If you use other CHR files, just create object files for all the CHR files using BGIOBJ
utility, then register them using registerfarbgifont( ) function.
GIF stands for Graphics Interchange Format. GIF is a good file format introduced by
CompuServe Incorporated. GIF files can be classified into (i) Ordinary GIF files (ii) Animated
GIF files. GIF files are widely used in Internet. GIF took its popularity by the capacity to get
animated and by using the very efficient “one-pass” LZW compression algorithm.
31.2 GIFSAVE
GIFSAVE was developed by Sverre H. Huseby. It is a function to save the image in GIF
format. Sverre H. Huseby says that GIFSAVE is little bit slow and the reason is Borland’s
getpixel( )function and not the GIFSAVE functions.
GIFSAVE consists of four functions, all declared in GIFSAVE.H:
The functions should be called in the listed order for each GIF-file. One file must be closed
before a new one is created.
31.3 Gifsave.h
#ifndef GIFSAVE_H
#define GIFSAVE_H
enum GIF_Code {
GIF_OK,
GIF_ERRCREATE,
GIF_ERRWRITE,
GIF_OUTMEM
};
int GIF_Create(
char *filename,
int width, int height,
int numcolors, int colorres
);
void GIF_SetColor(
int colornum,
int red, int green, int blue
);
int GIF_CompressImage(
int left, int top,
int width, int height,
A to Z of C 185
#endif
31.4 Gifsave.c
/**********************************************************************
* FILE: GIFSAVE.C
*
* MODULE OF: GIFSAVE
*
* DESCRIPTION: Routines to create a GIF-file.
***********************************************************************
*/
#include <stdlib.h>
#include <stdio.h>
#include "gifsave.h"
/**********************************************************************
* P R I V A T E D A T A
**********************************************************************
*/
/*====================================================================
* I/O Routines
*====================================================================
*/
/*====================================================================
* Routines to write a bit-file
*====================================================================
*/
/*===================================================================
* Routines to maintain an LZW-string table
*===================================================================
*/
#define RES_CODES 2
#define MAXBITS 12
#define MAXSTR (1 << MAXBITS)
/*====================================================================
* Main routines
*====================================================================
*/
typedef struct {
Word LocalScreenWidth,
LocalScreenHeight;
Byte GlobalColorTableSize : 3,
SortFlag : 1,
ColorResolution : 3,
GlobalColorTableFlag : 1;
Byte BackgroundColorIndex;
Byte PixelAspectRatio;
} ScreenDescriptor;
typedef struct {
Byte Separator;
Word LeftPosition,
TopPosition;
Word Width,
Height;
Byte LocalColorTableSize : 3,
Reserved : 2,
A to Z of C 187
SortFlag : 1,
InterlaceFlag : 1,
LocalColorTableFlag : 1;
} ImageDescriptor;
/*********************************************************************
* P R I V A T E F U N C T I O N S
*********************************************************************
*/
/*==================================================================
* Routines to do file IO
*===================================================================
*/
/*-------------------------------------------------------------------
* NAME: Create()
*
* DESCRIPTION: Creates a new file, and enables referencing using
* the global variable OutFile. This variable is only
* used by these IO-functions, making it relatively
* simple to rewrite file IO.
*
* PARAMETERS: filename - Name of file to create
*
* RETURNS: GIF_OK - OK
* GIF_ERRWRITE - Error opening the file
*
*/
return GIF_OK;
}
/*--------------------------------------------------------------------
* NAME: Write()
*
* DESCRIPTION: Output bytes to the current OutFile.
*
* PARAMETERS: buf - Pointer to buffer to write
* len - Number of bytes to write
*
* RETURNS: GIF_OK - OK
* GIF_ERRWRITE - Error writing to the file
*/
return GIF_OK;
}
/*--------------------------------------------------------------------
* NAME: WriteByte()
*
* DESCRIPTION: Output one byte to the current OutFile.
*
* PARAMETERS: b - Byte to write
*
* RETURNS: GIF_OK - OK
* GIF_ERRWRITE - Error writing to the file
*/
return GIF_OK;
}
/*-------------------------------------------------------------------
* NAME: WriteWord()
*
* DESCRIPTION: Output one word (2 bytes with byte-swapping, like on
* the IBM PC) to the current OutFile.
A to Z of C 189
*
* PARAMETERS: w - Word to write
*
* RETURNS: GIF_OK - OK
* GIF_ERRWRITE - Error writing to the file
*/
return GIF_OK;
}
/*-------------------------------------------------------------------
* NAME: Close()
*
* DESCRIPTION: Close current OutFile.
*
* PARAMETERS: None
*
* RETURNS: Nothing
*/
/*===================================================================
* Routines to write a bit-file
*===================================================================
*/
/*-------------------------------------------------------------------
* NAME: InitBitFile()
*
* DESCRIPTION: Initiate for using a bitfile. All output is sent to
* the current OutFile using the I/O-routines above.
*
* PARAMETERS: None
* RETURNS: Nothing
*/
190 A to Z of C
/*-----------------------------------------------------------------
* NAME: ResetOutBitFile()
*
* DESCRIPTION: Tidy up after using a bitfile
*
* PARAMETERS: None
*
* RETURNS: 0 - OK, -1 - error
*/
/*
* Write whatever is in the buffer to the file
*/
if (numbytes) {
if (WriteByte(numbytes) != GIF_OK)
return -1;
Buffer[Index = 0] = 0;
BitsLeft = 8;
}
return 0;
}
/*------------------------------------------------------------------
* NAME: WriteBits()
*
* DESCRIPTION: Put the given number of bits to the outfile.
*
* PARAMETERS: bits - bits to write from (right justified)
* numbits - number of bits to write
A to Z of C 191
*
* RETURNS: bits written, or -1 on error.
*/
do {
/*
* If the buffer is full, write it.
*/
if ((Index == 254 && !BitsLeft) || Index > 254) {
if (WriteByte(numbytes) != GIF_OK)
return -1;
Buffer[Index = 0] = 0;
BitsLeft = 8;
}
/*
* Now take care of the two specialcases
*/
if (numbits <= BitsLeft) {
Buffer[Index] |= (bits & ((1 << numbits) - 1)) << (8 -
BitsLeft);
bitswritten += numbits;
BitsLeft -= numbits;
numbits = 0;
} else {
Buffer[Index] |= (bits & ((1 << BitsLeft) - 1)) << (8 -
BitsLeft);
bitswritten += BitsLeft;
bits >>= BitsLeft;
numbits -= BitsLeft;
Buffer[++Index] = 0;
BitsLeft = 8;
}
} while (numbits);
return bitswritten;
}
192 A to Z of C
/*==================================================================
* Routines to maintain an LZW-string table
*==================================================================
*/
/*------------------------------------------------------------------
* NAME: FreeStrtab()
*
* DESCRIPTION: Free arrays used in string table routines
*
* PARAMETERS: None
*
* RETURNS: Nothing
*/
if (StrNxt) {
free(StrNxt);
StrNxt = NULL;
}
if (StrChr) {
free(StrChr);
StrChr = NULL;
}
}
/*-----------------------------------------------------------------
* NAME: AllocStrtab()
*
* DESCRIPTION: Allocate arrays used in string table routines
*
* PARAMETERS: None
*
* RETURNS: GIF_OK - OK
* GIF_OUTMEM - Out of memory
*/
FreeStrtab();
return GIF_OK;
}
/*---------------------------------------------------------------------
* NAME: AddCharString()
*
* DESCRIPTION: Add a string consisting of the string of index plus
* the byte b.
*
* If a string of length 1 is wanted, the index should
* be 0xFFFF.
*
* PARAMETERS: index - Index to first part of string, or 0xFFFF is
* only 1 byte is wanted
* b - Last byte in new string
*
* RETURNS: Index to new string, or 0xFFFF if no more room
*
*/
static Word AddCharString(Word index, Byte b)
{
Word hshidx;
/*
* Check if there is more room
*/
if (NumStrings >= MAXSTR)
return 0xFFFF;
194 A to Z of C
/*
* Search the string table until a free position is found
*/
hshidx = HASH(index, b);
while (StrHsh[hshidx] != 0xFFFF)
hshidx = (hshidx + HASHSTEP) % HASHSIZE;
/*
* Insert new string
*/
StrHsh[hshidx] = NumStrings;
StrChr[NumStrings] = b;
StrNxt[NumStrings] = (index != 0xFFFF) ? index : NEXT_FIRST;
return NumStrings++;
}
/*--------------------------------------------------------------------
* NAME: FindCharString()
*
* DESCRIPTION: Find index of string consisting of the string of
* index plus the byte b.
*
* If a string of length 1 is wanted, the index should
* be 0xFFFF.
*
* PARAMETERS: index - Index to first part of string, or 0xFFFF is
* only 1 byte is wanted
* b - Last byte in string
*
* RETURNS: Index to string, or 0xFFFF if not found
*/
/*
* Check if index is 0xFFFF. In that case we need only
* return b, since all one-character strings has their
* bytevalue as their index
*/
if (index == 0xFFFF)
return b;
/*
* Search the string table until the string is found, or
A to Z of C 195
/*
* No match is found
*/
return 0xFFFF;
}
/*-------------------------------------------------------------------
* NAME: ClearStrtab()
*
* DESCRIPTION: Mark the entire table as free, enter the 2**codesize
* one-byte strings, and reserve the RES_CODES reserved
* codes.
*
* PARAMETERS: codesize - Number of bits to encode one pixel
*
* RETURNS: Nothing
*/
/*
* No strings currently in the table
*/
NumStrings = 0;
/*
* Mark entire hashtable as free
*/
wp = StrHsh;
for (q = 0; q < HASHSIZE; q++)
*wp++ = HASH_FREE;
/*
* Insert 2**codesize one-character strings, and reserved codes
*/
196 A to Z of C
/*===================================================================
* LZW compression routine
*===================================================================
*/
/*--------------------------------------------------------------------
* NAME: LZW_Compress()
*
* DESCRIPTION: Perform LZW compression as specified in the
* GIF-standard.
*
* PARAMETERS: codesize - Number of bits needed to represent
* one pixelvalue.
* inputbyte - Function that fetches each byte to
* compress.
* Must return -1 when no more bytes.
*
* RETURNS: GIF_OK - OK
* GIF_OUTMEM - Out of memory
*/
/*
* Set up variables and tables
*/
clearcode = 1 << codesize;
endofinfo = clearcode + 1;
numbits = codesize + 1;
limit = (1 << numbits) - 1;
ClearStrtab(codesize);
/*
* First send a code telling the unpacker to clear the stringtable.
*/
WriteBits(clearcode, numbits);
/*
* Pack image
*/
while ((c = inputbyte()) != -1) {
/*
* Now perform the packing.
* Check if the prefix + the new character is a string that
* exists in the table
*/
if ((index = FindCharString(prefix, c)) != 0xFFFF) {
/*
* The string exists in the table.
* Make this string the new prefix.
*/
prefix = index;
} else {
/*
* The string does not exist in the table.
* First write code of the old prefix to the file.
*/
WriteBits(prefix, numbits);
/*
* Add the new string (the prefix + the new character)
* to the stringtable.
*/
if (AddCharString(prefix, c) > limit) {
if (++numbits > 12) {
WriteBits(clearcode, numbits - 1);
ClearStrtab(codesize);
numbits = codesize + 1;
}
limit = (1 << numbits) - 1;
}
/*
* Set prefix to a string containing only the character
* read. Since all possible one-character strings exists
* int the table, there's no need to check if it is found.
*/
198 A to Z of C
prefix = c;
}
}
/*
* End of info is reached. Write last prefix.
*/
if (prefix != 0xFFFF)
WriteBits(prefix, numbits);
/*
* Write end of info -mark.
*/
WriteBits(endofinfo, numbits);
/*
* Flush the buffer
*/
ResetOutBitFile();
/*
* Tidy up
*/
FreeStrtab();
return GIF_OK;
}
/*====================================================================
* Other routines
*====================================================================
*/
/*--------------------------------------------------------------------
* NAME: BitsNeeded()
*
* DESCRIPTION: Calculates number of bits needed to store numbers
* between 0 and n - 1
*
* PARAMETERS: n - Number of numbers to store (0 to n - 1)
*
* RETURNS: Number of bits needed
*/
if (!n--)
return 0;
while (n >>= 1)
++ret;
return ret;
}
/*---------------------------------------------------------------------
* NAME: InputByte()
*
* DESCRIPTION: Get next pixel from image. Called by the
* LZW_Compress()-function
*
* PARAMETERS: None
*
* RETURNS: Next pixelvalue, or -1 if no more pixels
*/
return ret;
}
/*-------------------------------------------------------------------
* NAME: WriteScreenDescriptor()
*
* DESCRIPTION: Output a screen descriptor to the current GIF-file
*
* PARAMETERS: sd - Pointer to screen descriptor to output
*
* RETURNS: GIF_OK - OK
* GIF_ERRWRITE - Error writing to the file
*/
200 A to Z of C
if (WriteWord(sd->LocalScreenWidth) != GIF_OK)
return GIF_ERRWRITE;
if (WriteWord(sd->LocalScreenHeight) != GIF_OK)
return GIF_ERRWRITE;
tmp = (sd->GlobalColorTableFlag << 7)
| (sd->ColorResolution << 4)
| (sd->SortFlag << 3)
| sd->GlobalColorTableSize;
if (WriteByte(tmp) != GIF_OK)
return GIF_ERRWRITE;
if (WriteByte(sd->BackgroundColorIndex) != GIF_OK)
return GIF_ERRWRITE;
if (WriteByte(sd->PixelAspectRatio) != GIF_OK)
return GIF_ERRWRITE;
return GIF_OK;
}
/*--------------------------------------------------------------------
* NAME: WriteImageDescriptor()
*
* DESCRIPTION: Output an image descriptor to the current GIF-file
*
* PARAMETERS: id - Pointer to image descriptor to output
*
* RETURNS: GIF_OK - OK
* GIF_ERRWRITE - Error writing to the file
*/
if (WriteByte(id->Separator) != GIF_OK)
return GIF_ERRWRITE;
if (WriteWord(id->LeftPosition) != GIF_OK)
return GIF_ERRWRITE;
if (WriteWord(id->TopPosition) != GIF_OK)
return GIF_ERRWRITE;
if (WriteWord(id->Width) != GIF_OK)
return GIF_ERRWRITE;
if (WriteWord(id->Height) != GIF_OK)
return GIF_ERRWRITE;
A to Z of C 201
/********************************************************************
* P U B L I C F U N C T I O N S
*******************************************************************/
/*--------------------------------------------------------------------
* NAME: GIF_Create()
*
* DESCRIPTION: Create a GIF-file, and write headers for both screen
* and image.
*
* PARAMETERS: filename - Name of file to create (including
* extension)
* width - Number of horisontal pixels on screen
* height - Number of vertical pixels on screen
* numcolors - Number of colors in the colormaps
* colorres - Color resolution. Number of bits for
* each primary color
*
* RETURNS: GIF_OK - OK
* GIF_ERRCREATE - Couldn't create file
* GIF_ERRWRITE - Error writing to the file
* GIF_OUTMEM - Out of memory allocating color table
*/
int GIF_Create(char *filename, int width, int height,
int numcolors, int colorres)
{
int q, tabsize;
Byte *bp;
ScreenDescriptor SD;
/*
* Initiate variables for new GIF-file
*/
NumColors = numcolors ? (1 << BitsNeeded(numcolors)) : 0;
BitsPrPrimColor = colorres;
ScreenHeight = height;
ScreenWidth = width;
202 A to Z of C
/*
* Create file specified
*/
if (Create(filename) != GIF_OK)
return GIF_ERRCREATE;
/*
* Write GIF signature
*/
if ((Write("GIF87a", 6)) != GIF_OK)
return GIF_ERRWRITE;
/*
* Initiate and write screen descriptor
*/
SD.LocalScreenWidth = width;
SD.LocalScreenHeight = height;
if (NumColors) {
SD.GlobalColorTableSize = BitsNeeded(NumColors) - 1;
SD.GlobalColorTableFlag = 1;
} else {
SD.GlobalColorTableSize = 0;
SD.GlobalColorTableFlag = 0;
}
SD.SortFlag = 0;
SD.ColorResolution = colorres - 1;
SD.BackgroundColorIndex = 0;
SD.PixelAspectRatio = 0;
if (WriteScreenDescriptor(&SD) != GIF_OK)
return GIF_ERRWRITE;
/*
* Allocate color table
*/
if (ColorTable) {
free(ColorTable);
ColorTable = NULL;
}
if (NumColors) {
tabsize = NumColors * 3;
else {
bp = ColorTable;
A to Z of C 203
/*---------------------------------------------------------------------
* NAME: GIF_SetColor()
*
* DESCRIPTION: Set red, green and blue components of one of the
* colors. The color components are all in the range
* [0, (1 << BitsPrPrimColor) - 1]
*
* PARAMETERS: colornum - Color number to set. [0, NumColors - 1]
* red - Red component of color
* green - Green component of color
* blue - Blue component of color
*
* RETURNS: Nothing
*/
if (width < 0) {
width = ScreenWidth;
left = 0;
}
if (height < 0) {
height = ScreenHeight;
top = 0;
}
if (left < 0)
left = 0;
if (top < 0)
top = 0;
/*
* Write global colortable if any
*/
if (NumColors)
if ((Write(ColorTable, NumColors * 3)) != GIF_OK)
A to Z of C 205
return GIF_ERRWRITE;
/*
* Initiate and write image descriptor
*/
ID.Separator = ',';
ID.LeftPosition = ImageLeft = left;
ID.TopPosition = ImageTop = top;
ID.Width = ImageWidth = width;
ID.Height = ImageHeight = height;
ID.LocalColorTableSize = 0;
ID.Reserved = 0;
ID.SortFlag = 0;
ID.InterlaceFlag = 0;
ID.LocalColorTableFlag = 0;
if (WriteImageDescriptor(&ID) != GIF_OK)
return GIF_ERRWRITE;
/*
* Write code size
*/
codesize = BitsNeeded(NumColors);
if (codesize == 1)
++codesize;
if (WriteByte(codesize) != GIF_OK)
return GIF_ERRWRITE;
/*
* Perform compression
*/
RelPixX = RelPixY = 0;
GetPixel = getpixel;
if ((errcode = LZW_Compress(codesize, InputByte)) != GIF_OK)
return errcode;
/*
* Write terminating 0-byte
*/
if (WriteByte(0) != GIF_OK)
return GIF_ERRWRITE;
return GIF_OK;
}
/*---------------------------------------------------------------------
* NAME: GIF_Close()
206 A to Z of C
/*
* Initiate and write ending image descriptor
*/
ID.Separator = ';';
if (WriteImageDescriptor(&ID) != GIF_OK)
return GIF_ERRWRITE;
/*
* Close file
*/
Close();
/*
* Release color table
*/
if (ColorTable) {
free(ColorTable);
ColorTable = NULL;
}
return GIF_OK;
}
Compile the above Gifsave.c file to create the Gifsave.lib file. Using Gifsave.lib &
Gifsave.h files we can create GIF files quickly.
/***********************************************************************
* FILE: EXAMPLE.C
*
* MODULE OF: EXAMPLE
*
* DESCRIPTION: Example program using GIFSAVE.
*
* Produces output to an EGA-screen, then dumps it to
* a GIF-file.
**********************************************************************
*/
#ifndef __TURBOC__
#error This program must be compiled using a Borland C compiler
#endif
#include <stdlib.h>
#include <stdio.h>
#include <graphics.h>
#include "gifsave.h"
/*********************************************************************
* P R I V A T E F U N C T I O N S
*********************************************************************
*/
/*--------------------------------------------------------------------
* NAME: DrawScreen()
*
* DESCRIPTION: Produces some output on the graphic screen.
*
* PARAMETERS: None
*
* RETURNS: Nothing
*/
static void DrawScreen(void)
{
int color = 1, x, y;
char *text = "GIF-file produced by GIFSAVE";
/*
* Output some lines
*/
setlinestyle(SOLID_LINE, 0, 3);
for (x = 10; x < getmaxx(); x += 20) {
setcolor(color);
208 A to Z of C
line(x, 0, x, getmaxy());
if (++color > getmaxcolor())
color = 1;
}
for (y = 8; y < getmaxy(); y += 17) {
setcolor(color);
line(0, y, getmaxx(), y);
if (++color > getmaxcolor())
color = 1;
}
/*
* And then some text
*/
setfillstyle(SOLID_FILL, DARKGRAY);
settextstyle(TRIPLEX_FONT, HORIZ_DIR, 4);
bar(20, 10, textwidth(text) + 40, textheight(text) + 20);
setcolor(WHITE);
outtextxy(30, 10, text);
}
/*--------------------------------------------------------------------
* NAME: gpixel()
*
* DESCRIPTION: Callback function. Near version of getpixel()
*
* If this program is compiled with a model using
* far code, Borland's getpixel() can be used
* directly.
*
* PARAMETERS: As for getpixel()
*
* RETURNS: As for getpixel()
*/
/*--------------------------------------------------------------------
* NAME: GIF_DumpEga10()
*
* DESCRIPTION: Outputs a graphics screen to a GIF-file. The screen
* must be in the mode 0x10, EGA 640x350, 16 colors.
*
* No error checking is done! Probably not a very good
A to Z of C 209
int q, /* Counter */
color, /* Temporary color value */
red[NUMCOLORS], /* Red component for each color */
green[NUMCOLORS], /* Green component for each color */
blue[NUMCOLORS]; /* Blue component for each color */
struct palettetype pal;
/*
* Get the color palette, and extract the red, green and blue
* components for each color. In the EGA palette, colors are
* stored as bits in bytes:
*
* 00rgbRGB
*
* where r is low intensity red, R is high intensity red, etc.
* We shift the bits in place like
*
* 000000Rr
*
* for each component
*/
getpalette(&pal);
for (q = 0; q < NUMCOLORS; q++) {
color = pal.colors[q];
red[q] = ((color & 4) >> 1) | ((color & 32) >> 5);
green[q] = ((color & 2) >> 0) | ((color & 16) >> 4);
blue[q] = ((color & 1) << 1) | ((color & 8) >> 3);
}
/*
* Create and set up the GIF-file
*/
GIF_Create(filename, WIDTH, HEIGHT, NUMCOLORS, BITS_PR_PRIM_COLOR);
210 A to Z of C
/*
* Set each color according to the values extracted from
* the palette
*/
for (q = 0; q < NUMCOLORS; q++)
GIF_SetColor(q, red[q], green[q], blue[q]);
/*
* Store the entire screen as an image using the user defined
* callback function gpixel() to get pixel values from the screen
*/
GIF_CompressImage(0, 0, -1, -1, gpixel);
/*
* Finish it all and close the file
*/
GIF_Close();
}
/**********************************************************************
* P U B L I C F U N C T I O N S
**********************************************************************
*/
int main(void)
{
int gdr, gmd, errcode;
gdr = EGA;
gmd = EGAHI;
initgraph(&gdr, &gmd, "");
if ((errcode = graphresult()) != grOk) {
printf("Graphics error: %s\n", grapherrormsg(errcode));
exit(-1);
}
/* Put something on the screen */
DrawScreen();
Mode 13h
Programming
Mode 13h is considered to be the standard mode for graphics programming under
DOS. Mode 13h programming is also referred as VGA programming or VGA register
programming. Almost all DOS Game software uses this mode 13h.
Note
For the sake of simplicity, palette register is very
often referred as a single dimensional array :
palette[768].
Here the 3 bytes hold Red, Green & Blue values. For example { 0,0,0 } represents
White. Important note: VGA uses only 6 bits in each Red, Green & Blue bytes. So we can
use 26 combination of Red, 26 combination of Green, 26 combination of Blue values. And we
have the maximum of 26 x 26 x 26 = 262144 colors. Thus at a given time, the screen can have
maximum of 256 colors out of the possible 262144 combination.
The next question is how to set these palette registers? We can use BIOS interrupts
to set the palette registers. But it would be very slow and not good for professional
programming. So we directly use the palette registers found on our VGA card. Palette
registers are accessed via port 3C8h and 3C9h. First, we have to send 0 to port 3C8h and
then the corresponding pixel values to port 3C9h. The sequences of operations should be:
1. OUT 0 at port 3C8h
2. OUT all pixel values one by one at port 3C9h (There would be 768 OUTs)
Another important point I want to insist is: loading palette registers refers to
choosing 256 colors out of 262144 possible combinations and the screen holds just index or
pointer to the look up table.
32.1.2 Vertical Retrace
The electron gun in our monitor refreshes each pixel with their current and correct
values according to the refresh rate. The refresh rate may vary from system to system and
usually it is 60Hz i.e., each pixel is refreshed in 1/60th of a second. The electron gun fires
electron at each pixel, row by row. Horizontal retrace is the time the electron gun takes to
return from the right to left side of the screen after it has traced a row. For mode 13h
programming, we don’t bother about horizontal retrace.
Vertical retrace is the very short time in which the electron gun moves diagonally to
the upper-left corner from the bottom-right corner of the screen, after tracing the entire
screen. During the vertical retrace the screen is not being updated from video memory to
monitor. So during this time if we update the screen, it won’t result in flickering. In other
words, you may get flickering if you don’t consider vertical retrace. On the fast computers
available today, it is not a big problem. However it wise to consider vertical retrace for good
portability.
We can check the vertical retrace by noticing the value of the INPUT_STATUS
(0x3DA) port on the VGA card. This is a number that represents the VGA's current state.
Bit 3 tells if it is in a vertical blank. We first wait until it is not blanking; to make sure we
get a full vertical blank time for our copy. Then we wait for a vertical blank. Now that we
can update the whole screen. The following code fragment explains the concept.
void UpdateBuffer(void)
{
// wait for vertical re-trace
while ( inportb(INPUT_STATUS) & (1<<3) )
;
while ( !(inportb(INPUT_STATUS) & (1<<3)) )
;
When you look at the BMP file format closely, you can find that BMP stores palette
information in it. So in order to display BMP files, we must load that palette information. When
we read a BMP file in mode 13h we have two restrictions: maximum color of BMP must be 256
(BMP files can be of 16, 256 or 224 colors!) and file size must be less than 64KB. The following
program by Alexander Russell reads 256 colors BMP file. It clips images larger than 320x200. It
reads the whole thing into memory, and then displays it directly to video memory.
33.1 Programs
#include <stdio.h>
#include <io.h>
#include <conio.h>
#include <malloc.h>
#include <string.h>
#include <dos.h>
#pragma inline
#define BI_RGB 0L
#define BI_RLE8 1L
#define BI_RLE4 2L
DWORD bfOffBits;
} BITMAPFILEHEADER;
/* ----------------------------------------------
SaveVideoMode - save the vid mode so
we can restore it on exit */
/* ----------------------------------------------
SetGraph - set graphics mode to
mode BIOS 0x13, 320x200 256 color */
asm {
/* set new mode */
xor ah, ah
mov al, 013h
int 10h
}
return(0);
} /*--SetGraph( )--------*/
/* ----------------------------------------------
RestoreVideoMode - restore old video
mode */
/*-------------------------------------------------------
SetUpVGAPalette - set all 256 colours of the
palette, wait for vert sync to avoid flashing */
asm {
.386
/*------------------------------------------------
FarFread - returns number of bytes read
I compiled this in medium model, so fread
expects a near pointer.
This let’s me read the file into far memory. */
if ( i != 1024 )
break;
}
free(t);
}
else
read=0;
return(read);
} /*--FarFread( )-------*/
/*------------------------------------------------------------
DecompressOneLineBMP
decompress one line of a 256 colour bmp into line
returns where we ended up in rp which is the raw image
width is max line width, i_size is how much data we read in */
num--;
}
rp++;
size++;
}
else
{
// zero, either escape sequence, or string of random pixels
rp++;
size++;
switch ( *rp )
{
case 0: // end of line, we are done
rp++;
size++;
*i_size-=size;
return rp;
//break;
size++;
}
break;
}
}
}
// should never get here actually, as each line ends with a EOL
*i_size-=size;
return(rp);
} /*--DecompressOneLineBMP( )-----------*/
/*----------------------------------------
main - main of BMP */
if ( argc < 2 )
printf( "Usge: BMP <bmpfile> \n\a" );
else
{
fp=fopen(argv[1], "rb");
if ( fp )
{
size=filelength(fileno(fp));
if ( size > _64k )
{
printf( "DARN it! DOS SUCKS! file size greater"
"than %u bytes! - TRUNCATING!\n", _64k);
size=_64k;
}
buff=farmalloc(size);
if ( buff )
{
A to Z of C 221
SaveVideoMode();
SetGraph();
SetUpVGAPalette(pal);
// clip width
if ( info->biWidth <= 320 )
w_copy=info->biWidth;
else
w_copy=320;
if ( info->biCompression == BI_RLE8 )
{
// we will decompress one line at a time,
// then clip and display it
line=farmalloc(info->biWidth+4);
A to Z of C 223
if ( line )
{
for ( i=0; i < info->biHeight && i < 200
&& i_size > 0; i++ )
{
rp=DecompressOneLineBMP(rp, line, &i_size,
info->biWidth);
_fmemcpy(video, line, w_copy);
video-=320;
}
farfree(line);
}
}
else
{
// not compressed, simply copy to video mem
//pads to multiple of 4 bytes
adj=info->biWidth % 4;
if ( adj )
adj=4 - adj;
if ( info->biCompression == BI_RGB )
{
for ( i=0; i < info->biHeight && i < 200
&& i_size > 319; i++ )
{
_fmemcpy(video, rp, w_copy);
video-=320;
rp+=info->biWidth;
rp+=adj;
i_size-=info->biWidth;
i_size-=adj;
}
}
}
getch();
RestoreVideoMode();
}
else
printf("This code only does 256 colour BMP's\n");
}
}
farfree(buff);
}
224 A to Z of C
else
printf("OUT of mem!\n");
fclose(fp);
}
else
printf("ERROR opening file: %s\n", argv[1]);
}
return(0);
} /*--main( )--------*/
34
“Love never fails.”
Fire
Beginners of mode 13h programming will always try to do fire program. It is of course
an easy program. In order to set palette registers, we must know what are all the colors used by
‘Fire’. After setting palette registers and loading the screen values, we can generate a “firing”
screen with certain logic.
#include <stdio.h>
if ( argc < 3 )
{
printf( "Usage: PAL file.bmp palfile\n\a" );
exit( 1 );
}
bfp = fopen( argv[1], "rb" );
pfp = fopen( argv[2], "w" );
if ( bfp==NULL || pfp==NULL )
{
printf( "File Error!\n\a" );
exit( 1 );
}
fprintf( pfp, "/* Palette file created with PAL */\n"
"/* File name: %s */\n"
"BYTE pal[768] = { ", argv[2]
);
A to Z of C 227
I’ve got the following palette file from the known Fire.bmp file:
/* Palette file created with PAL */
/* File name: fire.pal */
BYTE pal[768] = {
0, 0, 0, 0, 0, 6, 0, 0, 6, 0, 0, 7,
0, 0, 8, 0, 0, 8, 0, 0, 9, 0, 0, 10,
228 A to Z of C
2, 0, 10, 4, 0, 9, 6, 0, 9, 8, 0, 8,
10, 0, 7, 12, 0, 7, 14, 0, 6, 16, 0, 5,
18, 0, 5, 20, 0, 4, 22, 0, 4, 24, 0, 3,
26, 0, 2, 28, 0, 2, 30, 0, 1, 32, 0, 0,
32, 0, 0, 33, 0, 0, 34, 0, 0, 35, 0, 0,
36, 0, 0, 36, 0, 0, 37, 0, 0, 38, 0, 0,
39, 0, 0, 40, 0, 0, 40, 0, 0, 41, 0, 0,
42, 0, 0, 43, 0, 0, 44, 0, 0, 45, 0, 0,
46, 1, 0, 47, 1, 0, 48, 2, 0, 49, 2, 0,
50, 3, 0, 51, 3, 0, 52, 4, 0, 53, 4, 0,
54, 5, 0, 55, 5, 0, 56, 6, 0, 57, 6, 0,
58, 7, 0, 59, 7, 0, 60, 8, 0, 61, 8, 0,
63, 9, 0, 63, 9, 0, 63, 10, 0, 63, 10, 0,
63, 11, 0, 63, 11, 0, 63, 12, 0, 63, 12, 0,
63, 13, 0, 63, 13, 0, 63, 14, 0, 63, 14, 0,
63, 15, 0, 63, 15, 0, 63, 16, 0, 63, 16, 0,
63, 17, 0, 63, 17, 0, 63, 18, 0, 63, 18, 0,
63, 19, 0, 63, 19, 0, 63, 20, 0, 63, 20, 0,
63, 21, 0, 63, 21, 0, 63, 22, 0, 63, 22, 0,
63, 23, 0, 63, 24, 0, 63, 24, 0, 63, 25, 0,
63, 25, 0, 63, 26, 0, 63, 26, 0, 63, 27, 0,
63, 27, 0, 63, 28, 0, 63, 28, 0, 63, 29, 0,
63, 29, 0, 63, 30, 0, 63, 30, 0, 63, 31, 0,
63, 31, 0, 63, 32, 0, 63, 32, 0, 63, 33, 0,
63, 33, 0, 63, 34, 0, 63, 34, 0, 63, 35, 0,
63, 35, 0, 63, 36, 0, 63, 36, 0, 63, 37, 0,
63, 38, 0, 63, 38, 0, 63, 39, 0, 63, 39, 0,
63, 40, 0, 63, 40, 0, 63, 41, 0, 63, 41, 0,
63, 42, 0, 63, 42, 0, 63, 43, 0, 63, 43, 0,
63, 44, 0, 63, 44, 0, 63, 45, 0, 63, 45, 0,
63, 46, 0, 63, 46, 0, 63, 47, 0, 63, 47, 0,
63, 48, 0, 63, 48, 0, 63, 49, 0, 63, 49, 0,
63, 50, 0, 63, 50, 0, 63, 51, 0, 63, 52, 0,
63, 52, 0, 63, 52, 0, 63, 52, 0, 63, 52, 0,
63, 53, 0, 63, 53, 0, 63, 53, 0, 63, 53, 0,
63, 54, 0, 63, 54, 0, 63, 54, 0, 63, 54, 0,
63, 54, 0, 63, 55, 0, 63, 55, 0, 63, 55, 0,
63, 55, 0, 63, 56, 0, 63, 56, 0, 63, 56, 0,
63, 56, 0, 63, 57, 0, 63, 57, 0, 63, 57, 0,
63, 57, 0, 63, 57, 0, 63, 58, 0, 63, 58, 0,
63, 58, 0, 63, 58, 0, 63, 59, 0, 63, 59, 0,
63, 59, 0, 63, 59, 0, 63, 60, 0, 63, 60, 0,
63, 61, 0, 63, 61, 0, 63, 61, 0, 63, 62, 0,
63, 62, 0, 63, 62, 0, 63, 62, 0, 63, 63, 0,
63, 63, 1, 63, 63, 2, 63, 63, 3, 63, 63, 4,
63, 63, 5, 63, 63, 6, 63, 63, 7, 63, 63, 8,
63, 63, 9, 63, 63, 10, 63, 63, 10, 63, 63, 11,
A to Z of C 229
63, 63, 12,63, 63, 13, 63, 63, 14, 63, 63, 15,
63, 63, 16,63, 63, 17, 63, 63, 18, 63, 63, 19,
63, 63, 20,63, 63, 21, 63, 63, 21, 63, 63, 22,
63, 63, 23,63, 63, 24, 63, 63, 25, 63, 63, 26,
63, 63, 27,63, 63, 28, 63, 63, 29, 63, 63, 30,
63, 63, 31,63, 63, 31, 63, 63, 32, 63, 63, 33,
63, 63, 34,63, 63, 35, 63, 63, 36, 63, 63, 37,
63, 63, 38,63, 63, 39, 63, 63, 40, 63, 63, 41,
63, 63, 42,63, 63, 42, 63, 63, 43, 63, 63, 44,
63, 63, 45,63, 63, 46, 63, 63, 47, 63, 63, 48,
63, 63, 49,63, 63, 50, 63, 63, 51, 63, 63, 52,
63, 63, 52,63, 63, 53, 63, 63, 54, 63, 63, 55,
63, 63, 56,63, 63, 57, 63, 63, 58, 63, 63, 59,
63, 63, 60,63, 63, 61, 63, 63, 62, 63, 63, 63,
63, 63, 60,63, 63, 61, 63, 63, 62, 63, 63, 63
};
/*_______EOF fire.pal _________________________*/
}
return( _DL );
} /*--Random( )---------*/
InitializeMCGA( );
SetUpPalette( );
while( !kbhit( ) )
{
AveragePixels( );
for ( i = BufferX*BufferY - 2*BufferX; i < BufferX*BufferY; ++i )
Buffer[i] = Random( );
for( i=k=0; k<BufferY-4; ++k, i+=320 )
for( j=0 ; j<320; ++i, ++j )
{
Video[i] = Buffer[320*k+j];
Video[i+320] = Buffer[320*k+j];
}
}
TextMode( );
return(0);
} /*--main( )---------*/
232 A to Z of C
Exercises
1. Replace the values of palette buffer pal[768] found at the palette file (Fire.pal) with
some random values. Now, execute the program. Observe the effect.
2. Write a program that generates ‘whirlpool’ or ‘lake’ effect.
3. Write a program that simulates ‘waving Indian Tricolor flag’.
Suggested Projects
1. Write a DOS based screen saver. (Hint: Use TSR concepts!)
35
“Have courage, and be strong.”
VESA Programming
35.1 Secrets
VESA programming is also sometimes referred as SVGA programming. According to the
documentations all windows based systems might have SVGA cards to provide better resolution
and more color. Even though VESA standard is introduced to reduce the burden of programming
complexity, programmers still face problem with VESA programming. One of the major
problems with VESA programming is compatibility. Few people say mode 98h is the standard
VESA mode and other say mode 101h & mode 103h are the standard modes! Another problem is
we must use interrupts to detect the modes supported by that particular SVGA card. So we cannot
have a single procedure, we must have different procedures for each mode! VESA people are
standardizing the existing VESA standards and come out with different versions. At present we
have VESA3.0. Thus VESA standard is not much standardized and people still go for mode 13h!
35.2 Program
The following program shows how to program for VESA. This is a pretty good example.
#include <dos.h>
} /*--PutPixel( )-------*/
{
asm{
MOV AX, 4F02h;
MOV BX, mode;
INT 10h;
}
return( _AX );
} /*--SetSVGAMode( )----------*/
oldmode = GetCurrentMode( );
printf( "Current Mode = %Xh \n", oldmode );
else
{
DemoDraw( );
getch( );
238 A to Z of C
SetSVGAMode( oldmode );
}
}
return(0);
} /*--main( )---------*/
36
“Anyone who loves learning accepts correction.”
3D Graphics
X * distance
́
X =
Z + distance
Y * distance
́
Y =
Z + distance
These equations may look easy. But these equations are not even available in so called
gem-books for graphics.
36.2 3D Rectangle
Here I present you a small program that plots a 3D Rectangle in 2D plane.
#include <graphics.h>
typedef struct
{
int x, y;
} COORD_2D;
typedef struct
240 A to Z of C
{
int x, y, z;
} COORD_3D;
Suggested Projects
1. Develop a CAD software.
2. Write a software that implements wire frame model.
37
“It’s better to go to a funeral than to attend a feast”
Fractal
37.1 Prelude
Fractal geometry was actually introduced by Benoit B.
Mandelbrot, a fellow of the Thomas J. Watson Research Center,
IBM Corporation. Mandelbrot coined the word fractal from the Latin A Simple Fractal
word frangere, which means, “to break”. Actually, a fractal object is constructed from simple
objects. Each part of the image will give you the overall structure. We, programmers view fractals
as recursively generated geometric patterns. In this figure each part of fractal can be viewed as a
circle.
37.2 Program
The following recursive program generates a fractal. I hope, from that you can come out
with more fractals!
#include <graphics.h>
Game Programming
38.2 Logic
It is advisable to develop a game’s outline or graphics output from its logic. Many people
often build game from graphics outline than from logic. It is a wrong practice. First of all your
game must be unique and should use faster algorithms. Your game should technically sound
good.
Chapter 1
• Quick overview of c
o pointers
o structs
o functions
o dynamic memory allocation
o include files
o file i/o
246 A to Z of C
o colour cycling
o reserved colours for common elements
o dynamic colours for various parts of a game
• Timing a game, and game design
o * separating drawing from logic *
o the PC timer
o too slow
o too fast
Chapter 6
• Games
o Break Out
• simple animation
• collision detection
• player control
Suggested Projects
1. Develop a Chess software.
2. Develop a Quake4 game.
39
“Don’t be afraid to invest. Someday it will pay off.”
Interfacing
Interfacing refers to connecting our PC with some external devices. Interfacing got so
many applications. In parcel service companies, weight gauge is been connected to the PC and so
the billing process becomes simple. Otherwise, we have to find the weight separately… we have
to enter the weight in the billing software… and then only it will produce the bill. In this chapter
let us see a simple interfacing example.
Now you may need to know how to connect the LCD with the parallel port. The
following diagram explains this.
A to Z of C 249
39.1.2 Logic
You can see there are 14 pins in the LCD chip and 25 pins in the parallel port. As control
port is an open collector/drain output, we connect it with LCD chip’s Enable (E) and Register
Select (RS) lines. We have added two 10K registers for safety measures. We just want to output
(i.e., write) our message on the LCD. So we force the Read/Write(R/W) line to Write Mode. The
contrast of the LCD display can be adjusted with the 10K potentiometer.
39.1.3 Program
#include <dos.h>
#include <string.h>
/* Initialize LCD... */
for ( i = 0; i < 3; ++i )
{
outportb( DATA, init[i] );
/* Delay */
delay( 20 );
/* Delay */
delay(20);
}
/* Set Strobe */
outportb( CONTROL, inportb( CONTROL ) | 0x01 );
delay(2);
/* Reset Strobe */
outportb( CONTROL, inportb( CONTROL ) & 0xFE );
delay(2);
}
return(0);
} /*--main( )---------*/
A to Z of C 251
In order to make our LCD panel work, first we have to initialize it. We can initialize it by
sending the instructions: initialize LCD, clear LCD & dual Line. After initializing the LCD, we
are supposed to clear the bit 3 of Control port. We did it by using
outportb(CONTROL, inportb(CONTROL) & 0xF7);
Then we sent our message to the LCD display using a for loop. If you have done
everything well, you can see our message “Hello world! A to Z of C ”
on the LCD display.
Suggested Projects
1. Write an Image Scanner program.
2. Activate a remote control toy car from keyboard.
3. Develop a new inputting device for your game (say, your own steering). Use it to
play your game or existing games.
40
“Charm can be deceiving, and beauty fades away.”
Embedded Systems
Our useful programs can be "embedded" in chips. These chips can be used in creating
different electronic devices. So programming for embedded systems is considered to be one of
the interesting topics for the people who are from Electronics background.
40.1 PROM
Our program can be embedded in PROM (Programmable ROM) category ROM. PROMs
are usually available in sizes 1KB to about 2MB. It is identified by part number. Usually PROM's
part number will be 27xxxx, where 27 denotes TL type PROM, xxxx denotes size in Kilo bits.
For example, the widely used PROM got part number 27512, which indicates that the size is
512K bits, or 64KB. The blank PROM is the one, which is preloaded with 1's (not 0’s). The 1's of
PROM corresponds to the fuses present in it. So if you burn the fuse, it represents 0. And so
programming ROM is very often referred as burning.
This burning is achieved with a hardware device known
as Device Programmer. Device Programmer is also
referred as PROM burner or PROM programmer. The
term “Programmer” in “PROM programmer” refers to PROM burner or PROM programmer
hardware device, not a person! PROM Programmer helps
us to embed our program in the PROM chip. PROMs are OTP (One Time Programmable).
Programmed or burned PROMS are widely used in electronic devices like billing machines,
washing machines, cell phones, etc.
40.2 EPROM
An Erasable PROM or EPROM is available with the same part numbering scheme of
PROM. EPROM has a clear quartz crystal window for
allowing UV rays to erase the contents. The UV rays erase EPROM
40.3 EEPROM
Electronically Erasable PROM or EEPROM is a kind of EPROM that doesn’t require UV
rays for erasing the contents. Instead it uses electricity for erasing. Nowadays we have Flash
A to Z of C 253
ROMs. Flash ROM is a type of EEPROM, which can be programmed without removing it from
the system and without any special devices.
40.7 Applications
Programming ROM has so many applications including the creation of chips used in
washing machine, creation of chips used in cars for monitoring the performance etc.
41
“Merely the thought of our favorite food makes our stomachs sick.”
Writing BIOS
BIOS (Basic Input Output System) is the one, which makes computer’s components
working together. BIOS are hence system specific. In this chapter, let’s see how to write our own
BIOS code.
db 0CBh
else
db 0CAh
dw x
endif
endm
;
LF equ 0Ah
CR equ 0Dh
;
.SALL ; Suppress Macro Expansions
.LFCOND ; List False Conditionals
;
ASSUME DS:code, SS:code, CS:code, ES:code
data SEGMENT at 40h ; IBM compatible data structure
dw 4 dup(?) ; 40:00 ; RS232 com. ports - up to four
dw 4 dup(?) ; 40:08 ; Printer ports - up to four
dw ? ; 40:10 ; Equipment present word
; + (1 iff floppies) * 1.
; + (# 64K sys ram ) * 4.
; + (init crt mode ) * 16.
; + (# of floppies ) * 64.
; + (# serial ports) * 512.
; + (1 iff toy port) * 4096.
; + (# parallel LPT) * 16384.
db ? ; 40:12 ; MFG test flags, unused by us
dw ? ; 40:13 ; Memory size, kilobytes
db ? ; 40:15 ; IPL errors<-table/scratchpad
db ? ; ...unused
;---------------[Keyboard data area]------------;
db ?,? ; 40:17 ; Shift/Alt/etc. keyboard flags
db ? ; 40:19 ; Alt-KEYPAD char. goes here
dw ? ; 40:1A ; --> keyboard buffer head
dw ? ; 40:1C ; --> keyboard buffer tail
dw 16 dup(?) ; 40:1E ; Keyboard Buffer (Scan,Value)
;---------------[Diskette data area]------------;
db ? ; 40:3E ; Drive Calibration bits 0 - 3
db ? ; 40:3F ; Drive Motor(s) on 0-3,7=write
db ? ; 40:40 ; Ticks (18/sec) til motor off
db ? ; 40:41 ; Floppy return code stat byte
; 1 = bad ic 765 command req.
; 2 = address mark not found
; 3 = write to protected disk
; 4 = sector not found
; 8 = data late (DMA overrun)
; 9 = DMA failed 64K page end
; 16 = bad CRC on floppy read
A to Z of C 257
code SEGMENT
ORG 0E000h
JS HALT
JNZ HALT
JPO HALT
ADD AX,1
JZ HALT
JPE HALT
SUB AX,8002h
JS HALT
INC AX
JNO HALT
SHL AX,1
JNB HALT
JNZ HALT
SHL AX,1
JB HALT
HALT: HLT
CPU_OK: CLD
MOV AL,0 ; Prepare to initialize
OUT 0A0h,AL ; ...no NMI interrupts
MOV DX,3D8h ; Load Color Graphic port
OUT DX,AL ; ...no video display
MOV DX,3B8h ; Load Monochrome port
INC AL ; ...no video display
OUT DX,AL ; ...write it out
MOV AL,10011001b ; Program 8255 PIA chip
260 A to Z of C
MOV AH,1
CALL CHKSUM ; ...for valid checksum
POP DS ; ...restore impure<-DS
JZ IC8259
OR Byte ptr DS:15h,ER_BIOS ; Checksum error BIOS eprom
PUSH DS
kosher: POP DS ; Setup special low vectors
MOV Word ptr ES:8,offset int_2 ; NMI interrupt
MOV Word ptr ES:14h,offset int_5 ; print screen interrupt
MOV Word ptr ES:7Ch,0 ; No special graphics chars.
MOV Word ptr ES:7Eh,0 ; ...so zero vector 1Fh
MOV DX,61h
IN AL,DX ; Read machine flags
OR AL,00110000b ; ...clear old parity error
OUT DX,AL ; Write them back to reset
AND AL,11001111b ; ...enable parity
OUT DX,AL ; Write back, parity enabled
MOV AL,80h ; ...allow NMI interrupts
OUT 0A0h,AL
MOV AX,0000000000110000b ; Assume monochrome video
264 A to Z of C
INC DI
NO_LPT: INC SI
INC SI
LOOP NXTPRT
MOV AX,DI ; Number of printers * 2
MOV CL,3 ; ...get shift count
ROR AL,CL ; ...divide by eight
MOV DS:11h,AL ; ...save in equip. flag
MOV AH,1
MOV CH,0F0h
INT 10h ; Set cursor type
CALL BLANK ; ...clear display
PUSH DS
PUSH CS
POP DS
POP ES
TEST Byte ptr ES:10h,1 ; Floppy disk present?
JZ FND_02 ; ...no
CMP Word ptr ES:72h,1234h ; Bios setup before?
JNZ CONFIG ; ...no
A to Z of C 269
NO_TIM: POP AX
MOV SI,offset STUF_6
ROR AL,1 ; Check for COM port
AND AL,3
JZ NO_COM ; ...skip if no com
XOR BP,BP
CALL FAO ; Formatted ascii output
MOV SI,2
MOV DX,SI
MOV AX,80h
MOV ES,AX
BADRAM: POP ES
OR Byte ptr ES:15h,ER_RAM ; Show "Bad Ram" error
JMP CONFIG
db 027h,060h,02Ch,02Eh
db 02Fh
KY_02: MOV BX,offset ASCII ; Table for ESC thru Scroll Lck
XLAT CS:[BX] ; ...translate to Ascii
280 A to Z of C
KY_CO3: TEST Byte ptr DS:17h,01000000b ; Test for "CAPS LOCK" state
JZ KY_BFR ; ...no, skip
TEST Byte ptr DS:17h,00000011b ; Test for SHIFT key
JZ KY_CO4 ; ...skip if no shift
CMP AL,'A' ; Check for alphabetic key
JB KY_BFR ; ...not SHIFT_able
CMP AL,'Z' ; Check for alphabetic key
JA KY_BFR ; ...not SHIFT_able
ADD AL,20h ; Else do the shift
JMP short KY_BFR
XOR AL,00000010b
OUT 61h,AL ; Toggle speaker position
TEST AL,00000010b ; Full cycle done yet?
JZ KY_BE2 ; ...no, do other half cycle
DEC BX ; Else show cycle sent
JNZ KY_BE1 ; ...more cycles to send
POP AX
OUT 61h,AL ; Restore flags
MOV CX,32h ; Silence counter
KY_BE4: LOOP KY_BE4 ; Send nothing for while
JMP KY_XIT
endif
MOV AH,ES:[SI+2] ; Get new motor count
MOV DS:40h,AH ; ...and save it
MOV AH,DS:41h ; Get completion status
CMP AH,1 ; ...check for write protect
CMC ; ...was write protect error
POP BX
POP CX
POP DX
XCHG DL,CL
XCHG CL,DH
POP BX ; Clean
POP BX ; ...up
POP BX ; ...stack
POP ES
POP DS
POP DI
POP SI
POP BP
RETF 2
TEST AL,01000000b
JNZ FD_RS2 ; Drive #2 active
TEST AL,10000000b
JZ FD_RS0 ; Drive #3 idle
FD_RS3: INC AL
FD_RS2: INC AL
FD_RS1: INC AL
FD_RS5: RET
OR AL,AL
JNS FD_003 ; ...skip if non-write
MOV AH,ES:[SI+0Ah] ; Motor start from param blk
OR AH,AH
JZ FD_003 ; ...none specified
TEST CH,DS:3Fh ; Was this drive motor running?
JNZ FD_003 ; ...skip if so
CALL FD_WT1 ; Else delay for motor start
NC_BS1: TEST Byte ptr DS:3Eh,10000000b ; Has interrupt set the flag?
CLC ; ...hack to slow CPU
JNZ NC_BS2 ; ...yes
LOOP NC_BS1 ; Else back for more
DEC AL
JNZ NC_BS1
NC_RD3: POP CX
STC
RET
IN AL,DX ; ...read it
PUSH AX
POP AX
OUT DX,AL ; ...write user's parameter
CLC
POP DX
POP CX
NEC_04: RET
DEC AH
JZ LP_STS ; Get the status , AH=2
LP_01: POP DX
POP CX
POP BX
POP DS
IRET
INC DX
INC DX
MOV AL,00001000b
OUT DX,AL ; Request initialize
MOV CX,5DCh ; ...delay
LP_DLY: LOOP LP_DLY
JMP LP_STR ; Strobe the line printer
TABMUL db 00h,00h,10h,10h,20h,20h,20h,30h
;Table lookup for multiply
C_02: CMP AH,0Fh ; Is AH a legal video command?
JBE C_03
RET ; ...error return if not
RET
SHL BX,CL
MOV CH,AL
SHL CH,1
MOV DL,AH
MOV DH,AL
SHR DH,1
SHR DH,1
CMP Byte ptr DS:49h,6 ; Mode 640 x 200 b/w?
JNZ C4_04 ; ...no, skip
SHL DL,1
SHL BX,1
JMP short C4_04
C4_04: MOV Byte ptr [BP+3],1 ; Return AH=1, light pen read
MOV [BP+8],DX ; ...row, column in user DX
MOV [BP+4],BX ; ...pixel column in user BX
MOV [BP+7],CH ; ...raster line in user CH
OR CL,CL
JZ SCR_07 ; ...nothing to do
ADD SI,AX
SUB DH,[BP+2]
SCR_09: RET
POP DX
SUB DX,[BP+6] ; Chars to copy over
ADD DX,101h ; ...width of one char
SHL DH,1
SHL DH,1
MOV AL,[BP+3] ; Get command type
CMP Byte ptr DS:49h,6 ; ...is this 640 x 200?
JZ SCG_03 ; ...skip if so
SHL DL,1 ; Else bigger characters
SHL DI,1
CMP AL,7 ; Is this scroll down?
JNZ SCG_03 ; ...skip if not so
INC DI
POP DI
POP SI
ADD SI,2000h ; Load other grafix
ADD DI,2000h ; ...video plane
MOV CL,DL
PUSH SI
PUSH DI
REPZ MOVSB ; Copy other plane
POP DI
POP SI
SUB SI,BX
SUB DI,BX
DEC DH ; One less row to scroll
JNZ SCG_06 ; ...loop if more to do
POP AX
MOV DH,AL ; Load rows to blank
JZ CG8_02 ; ...skip if so
SHL DI,1
MOV AL,[BP+4] ; Get char. attribute
AND AX,3
MOV BX,5555h
MUL BX
MOV DX,AX
MOV BL,[BP+4]
RET
CG8_02: MOV BL,[BP+4] ; Get display page
MOV DX,2000h ; ...size of grafix plane
MOV DL,0
CGR_03: TEST AX,CX ; Look for attributes
CLC
JZ CGR_04 ; ...set, skip
STC ; Else show not set
CGR_10: PUSH SI
PUSH DI
MOV CX,8 ; Bytes to compare for char
REPZ CMPSB ; ...do compare
312 A to Z of C
POP DI
POP SI
JZ CGR_11 ; Found grafix character
INC AL ; ...else show another char
ADD DI,8 ; ...advance one row
DEC DX ; ...one less char to scan
JNZ CGR_10 ; Loop if more char left
ROL AL,1
AND AL,3
MODCH1: POP AX
RET
RET
GRAMAP: PUSH BX ; Convert (row,col) -> col
MOV BL,AL ; ...save column
MOV AL,AH ; ...get row
MUL Byte ptr DS:4Ah ; Multiply by columns/row
SHL AX,1
SHL AX,1
MOV BH,0
ADD AX,BX ; Add in columns
POP BX
RET
SENDAX: PUSH DX
MOV DX,DS:63h ; Load active video port
XCHG AL,AH
OUT DX,AL ; Send hi order
XCHG AL,AH
INC DL
OUT DX,AL ; ... lo order
POP DX
SEND01: RET
IRET
ENTRY 0F84Dh ; IBM entry for equipment check
MOV AX,DS
INC AX ; ...next paragraph
MOV DS,AX
CMP AX,BX
JNZ PAR_02 ; More paragraphs to check
JMP short PAR_05 ; ...else flakey error
PAR_07: POP AX
IRET
CALL NUMBER
POP AX
CALL NUMBER
RET
OUTCHR: PUSH BX
PUSH AX
MOV AH,0Eh ; Teletype print service
MOV BL,7 ; ...normal intensity
INT 10h
POP AX
POP BX
RET
BEEP: PUSH AX
PUSH CX
MOV AL,10110110b ; Timer ic 8253 square waves
OUT 43h,AL ; ...channel 2, speaker
322 A to Z of C
LOCATE: PUSH DX
PUSH BX
MOV DX,AX ; Get position for cursor
A to Z of C 323
MOV AH,2
MOV BH,0 ; ...page 0
INT 10h
POP BX
POP DX
RET
db 010h,038h,07Ch,0FEh
db 07Ch,038h,010h,000h
db 038h,07Ch,038h,0FEh
db 0FEh,07Ch,038h,07Ch
db 010h,010h,038h,07Ch
db 0FEh,07Ch,038h,07Ch
db 000h,000h,018h,03Ch
db 03Ch,018h,000h,000h
db 0FFh,0FFh,0E7h,0C3h
db 0C3h,0E7h,0FFh,0FFh
db 000h,03Ch,066h,042h
db 042h,066h,03Ch,000h
db 0FFh,0C3h,099h,0BDh
db 0BDh,099h,0C3h,0FFh
A to Z of C 325
db 00Fh,007h,00Fh,07Dh
db 0CCh,0CCh,0CCh,078h
db 03Ch,066h,066h,066h
db 03Ch,018h,07Eh,018h
db 03Fh,033h,03Fh,030h
db 030h,070h,0F0h,0E0h
db 07Fh,063h,07Fh,063h
db 063h,067h,0E6h,0C0h
db 099h,05Ah,03Ch,0E7h
db 0E7h,03Ch,05Ah,099h
db 080h,0E0h,0F8h,0FEh
db 0F8h,0E0h,080h,000h
db 002h,00Eh,03Eh,0FEh
db 03Eh,00Eh,002h,000h
db 018h,03Ch,07Eh,018h
db 018h,07Eh,03Ch,018h
db 066h,066h,066h,066h
db 066h,000h,066h,000h
db 07Fh,0DBh,0DBh,07Bh
db 01Bh,01Bh,01Bh,000h
db 03Eh,063h,038h,06Ch
db 06Ch,038h,0CCh,078h
db 000h,000h,000h,000h
db 07Eh,07Eh,07Eh,000h
db 018h,03Ch,07Eh,018h
db 07Eh,03Ch,018h,0FFh
db 018h,03Ch,07Eh,018h
db 018h,018h,018h,000h
db 018h,018h,018h,018h
db 07Eh,03Ch,018h,000h
db 000h,018h,00Ch,0FEh
db 00Ch,018h,000h,000h
db 000h,030h,060h,0FEh
db 060h,030h,000h,000h
db 000h,000h,0C0h,0C0h
db 0C0h,0FEh,000h,000h
db 000h,024h,066h,0FFh
db 066h,024h,000h,000h
db 000h,018h,03Ch,07Eh
db 0FFh,0FFh,000h,000h
db 000h,0FFh,0FFh,07Eh
db 03Ch,018h,000h,000h
326 A to Z of C
db 000h,000h,000h,000h
db 000h,000h,000h,000h
db 030h,078h,078h,030h
db 030h,000h,030h,000h
db 06Ch,06Ch,06Ch,000h
db 000h,000h,000h,000h
db 06Ch,06Ch,0FEh,06Ch
db 0FEh,06Ch,06Ch,000h
db 030h,07Ch,0C0h,078h
db 00Ch,0F8h,030h,000h
db 000h,0C6h,0CCh,018h
db 030h,066h,0C6h,000h
db 038h,06Ch,038h,076h
db 0DCh,0CCh,076h,000h
db 060h,060h,0C0h,000h
db 000h,000h,000h,000h
db 018h,030h,060h,060h
db 060h,030h,018h,000h
db 060h,030h,018h,018h
db 018h,030h,060h,000h
db 000h,066h,03Ch,0FFh
db 03Ch,066h,000h,000h
db 000h,030h,030h,0FCh
db 030h,030h,000h,000h
db 000h,000h,000h,000h
db 000h,030h,030h,060h
db 000h,000h,000h,0FCh
db 000h,000h,000h,000h
db 000h,000h,000h,000h
db 000h,030h,030h,000h
db 006h,00Ch,018h,030h
db 060h,0C0h,080h,000h
db 07Ch,0C6h,0CEh,0DEh
db 0F6h,0E6h,07Ch,000h
db 030h,070h,030h,030h
db 030h,030h,0FCh,000h
db 078h,0CCh,00Ch,038h
db 060h,0CCh,0FCh,000h
db 078h,0CCh,00Ch,038h
db 00Ch,0CCh,078h,000h
db 01Ch,03Ch,06Ch,0CCh
A to Z of C 327
db 0FEh,00Ch,01Eh,000h
db 0FCh,0C0h,0F8h,00Ch
db 00Ch,0CCh,078h,000h
db 038h,060h,0C0h,0F8h
db 0CCh,0CCh,078h,000h
db 0FCh,0CCh,00Ch,018h
db 030h,030h,030h,000h
db 078h,0CCh,0CCh,078h
db 0CCh,0CCh,078h,000h
db 078h,0CCh,0CCh,07Ch
db 00Ch,018h,070h,000h
db 000h,030h,030h,000h
db 000h,030h,030h,000h
db 000h,030h,030h,000h
db 000h,030h,030h,060h
db 018h,030h,060h,0C0h
db 060h,030h,018h,000h
db 000h,000h,0FCh,000h
db 000h,0FCh,000h,000h
db 060h,030h,018h,00Ch
db 018h,030h,060h,000h
db 078h,0CCh,00Ch,018h
db 030h,000h,030h,000h
db 07Ch,0C6h,0DEh,0DEh
db 0DEh,0C0h,078h,000h
db 030h,078h,0CCh,0CCh
db 0FCh,0CCh,0CCh,000h
db 0FCh,066h,066h,07Ch
db 066h,066h,0FCh,000h
db 03Ch,066h,0C0h,0C0h
db 0C0h,066h,03Ch,000h
db 0F8h,06Ch,066h,066h
db 066h,06Ch,0F8h,000h
db 0FEh,062h,068h,078h
db 068h,062h,0FEh,000h
db 0FEh,062h,068h,078h
db 068h,060h,0F0h,000h
db 03Ch,066h,0C0h,0C0h
db 0CEh,066h,03Eh,000h
db 0CCh,0CCh,0CCh,0FCh
db 0CCh,0CCh,0CCh,000h
db 078h,030h,030h,030h
328 A to Z of C
db 030h,030h,078h,000h
db 01Eh,00Ch,00Ch,00Ch
db 0CCh,0CCh,078h,000h
db 0E6h,066h,06Ch,078h
db 06Ch,066h,0E6h,000h
db 0F0h,060h,060h,060h
db 062h,066h,0FEh,000h
db 0C6h,0EEh,0FEh,0FEh
db 0D6h,0C6h,0C6h,000h
db 0C6h,0E6h,0F6h,0DEh
db 0CEh,0C6h,0C6h,000h
db 038h,06Ch,0C6h,0C6h
db 0C6h,06Ch,038h,000h
db 0FCh,066h,066h,07Ch
db 060h,060h,0F0h,000h
db 078h,0CCh,0CCh,0CCh
db 0DCh,078h,01Ch,000h
db 0FCh,066h,066h,07Ch
db 06Ch,066h,0E6h,000h
db 078h,0CCh,0E0h,070h
db 01Ch,0CCh,078h,000h
db 0FCh,0B4h,030h,030h
db 030h,030h,078h,000h
db 0CCh,0CCh,0CCh,0CCh
db 0CCh,0CCh,0FCh,000h
db 0CCh,0CCh,0CCh,0CCh
db 0CCH,078h,030h,000h
db 0C6h,0C6h,0C6h,0D6h
db 0FEh,0EEh,0C6h,000h
db 0C6h,0C6h,06Ch,038h
db 038h,06Ch,0C6h,000h
db 0CCh,0CCh,0CCh,078h
db 030h,030h,078h,000h
db 0FEh,0C6h,08Ch,018h
db 032h,066h,0FEh,000h
db 078h,060h,060h,060h
db 060h,060h,078h,000h
db 0C0h,060h,030h,018h
db 00Ch,006h,002h,000h
db 078h,018h,018h,018h
db 018h,018h,078h,000h
db 010h,038h,06Ch,0C6h
A to Z of C 329
db 000h,000h,000h,000h
db 000h,000h,000h,000h
db 000h,000h,000h,0FFh
db 030h,030h,018h,000h
db 000h,000h,000h,000h
db 000h,000h,078h,00Ch
db 07Ch,0CCh,076h,000h
db 0E0h,060h,060h,07Ch
db 066h,066h,0DCh,000h
db 000h,000h,078h,0CCh
db 0C0h,0CCh,078h,000h
db 01Ch,00Ch,00Ch,07Ch
db 0CCh,0CCh,076h,000h
db 000h,000h,078h,0CCh
db 0FCh,0C0h,078h,000h
db 038h,06Ch,060h,0F0h
db 060h,060h,0F0h,000h
db 000h,000h,076h,0CCh
db 0CCh,07Ch,00Ch,0F8h
db 0E0h,060h,06Ch,076h
db 066h,066h,0E6h,000h
db 030h,000h,070h,030h
db 030h,030h,078h,000h
db 00Ch,000h,00Ch,00Ch
db 00Ch,0CCh,0CCh,078h
db 0E0h,060h,066h,06Ch
db 078h,06Ch,0E6h,000h
db 070h,030h,030h,030h
db 030h,030h,078h,000h
db 000h,000h,0CCh,0FEh
db 0FEh,0D6h,0C6h,000h
db 000h,000h,0F8h,0CCh
db 0CCh,0CCh,0CCh,000h
db 000h,000h,078h,0CCh
db 0CCh,0CCh,078h,000h
db 000h,000h,0DCh,066h
db 066h,07Ch,060h,0F0h
db 000h,000h,076h,0CCh
db 0CCh,07Ch,00Ch,01Eh
db 000h,000h,0DCh,076h
db 066h,060h,0F0h,000h
db 000h,000h,07Ch,0C0h
330 A to Z of C
db 078h,00Ch,0F8h,000h
db 010h,030h,07Ch,030h
db 030h,034h,018h,000h
db 000h,000h,0CCh,0CCh
db 0CCh,0CCh,076h,000h
db 000h,000h,0CCh,0CCh
db 0CCh,078h,030h,000h
db 000h,000h,0C6h,0D6h
db 0FEh,0FEh,06Ch,000h
db 000h,000h,0C6h,06Ch
db 038h,06Ch,0C6h,000h
db 000h,000h,0CCh,0CCh
db 0CCh,07Ch,00Ch,0F8h
db 000h,000h,0FCh,098h
db 030h,064h,0FCh,000h
db 01Ch,030h,030h,0E0h
db 030h,030h,01Ch,000h
db 018h,018h,018h,000h
db 018h,018h,018h,000h
db 0E0h,030h,030h,01Ch
db 030h,030h,0E0h,000h
db 076h,0DCh,000h,000h
db 000h,000h,000h,000h
db 000h,010h,038h,06Ch
db 0C6h,0C6h,0FEh,000h
PS_2: PUSH DX
334 A to Z of C
XOR DX,DX
MOV AH,DL ; Function=Print character
INT 17h
POP DX
TEST AH,00100101b ; Successful print
JZ PS_3
MOV Byte ptr DS:100h,0FFh ; No, error in Print Screen
JMP short PS_4
PS_5: POP DX
POP CX
POP BX
POP AX
POP DS
IRET
;*********************************************************************
ENTRY 0FFF0h ; Hardware power reset entry *
PUBLIC POWER ; ...ic "8088" or "V20" *
POWER: JMPF 0F000h,COLD ; ...begins here on power up *
A to Z of C 335
;*********************************************************************
ENTRY 0FFF5h ; Release date, Yankee style
db "08/23/87" ; ...MM/DD/YY (not logical)
ENTRY 0FFFEh
db 0FEh ; Computer type (XT)
; db ? ; Checksum byte
code ENDS
;
END
41.3 Uniflash
Uniflash is the famous BIOS code for Flash BIOSs. It was actually written in Pascal. It is
available on CD . (Few people think that Pascal got good readability over C. It won’t be a
tough process to convert a Pascal code to C as we have so many language-converters for that!)
42
“We humans are only a breath; none of us are truly great.”
Caution
Any write to port 70h should be followed by an action to port 71h, otherwise RTC will be left in an
unknown state.
42.1.2 Code
Following is the code to view contents of CMOS RAM. As I said earlier, CMOS RAM is
available in two sizes: 64 & 128 bytes. Here I assume that the size of my CMOS RAM is 128
bytes. You need not know the exact size of CMOS RAM for basic operations like viewing
contents. However you must know the exact size of CMOS RAM for hazardous operations like
clearing CMOS RAM.
#include <dos.h>
#define CMOS_ADDR (0x70) /* address port of CMOS */
#define CMOS_DATA (0x71) /* data port for CMOS */
42.2.2 Code
This is the C code to read the contents of CMOS setup registers and diagnose it. It
analyzes the power of battery, checksum etc through the contents of CMOS registers. Once I
received this code from someone else. I am not aware of the real author. The author assumes the
size of the CMOS to be 64 bytes.
#include <stdio.h>
#include <dos.h>
typedef struct
{ char seconds; /* AT Real Time Clock (RTC): Seconds */
char secalrm; /* AT RTC: Seconds Alarm */
char minutes; /* AT RTC: Minutes */
char minalrm; /* AT RTC: Minutes Alarm */
char hours; /* AT RTC: Hours */
char hrsalrm; /* AT RTC: Hours Alarm */
char dayofweek; /* AT RTC: day of week */
char dayofmon; /* AT RTC: day of month */
char month; /* AT RTC: month */
char year; /* AT RTC: year */
char aregister; /* STATUS REGISTER A */
char bregister; /* STATUS REGISTER B */
char cregister; /* STATUS REGISTER C */
char dregister; /* STATUS REGISTER D */
char diagnostic; /* Diagnostics status byte */
char shutdown; /* Shutdown status byte */
char diskettes; /* A & B diskette types */
char reserved1; /* undefined */
char harddrive; /* C & D hard drive types */
char reserved2; /* undefined */
char equipment; /* equipment byte */
char lowbyte; /* low byte of base memory */
char highbyte; /* high byte of base memory */
/* 100h = 256k, 200h = 512k, 280h = 640k */
char extlow; /* low byte of extended memory */
char exthigh; /* high byte of extended memory */
/* 200h=512k;400h=1024k;etc to 3c00h=15360k */
char drivec; /* more data on drive c */
char drived; /* more data on drive d */
char reserved[19]; /* reserved */
unsigned checksum;
char extlow1; /* same as extlow */
char exthigh1; /* same as exthigh */
char century; /* binary coded decimal value for century */
/* 19h = 1900 for example */
A to Z of C 341
"PwrOK"
};
static char *status[] = {
"OK",
"Not OK"
};
static char *hardtbl[] = {
"┌──────┬──────────┬───────┬──────┬──────┬─────────┬──────┐",
"│Drive │ Cylinder │ Heads/│ Pre- │ Land │ Sectors │ Size │",
"│ Type │ (Tracks) │ Sides │ Comp │ Zone │ Per Trk │ (MB) │",
"│──────┼──────────┼───────┼──────┼──────┼─────────┼──────│"
};
static char *harddisk[] = {
"│ None │ --- │ -- │ --- │ --- │ -- │ ---- │",
"│ 1 │ 306 │ 4 │ 128 │ 305 │ 17 │ 10.1 │",
"│ 2 │ 615 │ 4 │ 300 │ 615 │ 17 │ 20.4 │",
"│ 3 │ 615 │ 6 │ 300 │ 615 │ 17 │ 30.6 │",
"│ 4 │ 940 │ 8 │ 512 │ 940 │ 17 │ 62.4 │",
"│ 5 │ 940 │ 6 │ 512 │ 940 │ 17 │ 46.8 │",
"│ 6 │ 615 │ 4 │ NONE │ 615 │ 17 │ 20.4 │",
"│ 7 │ 462 │ 8 │ 256 │ 511 │ 17 │ 30.6 │",
"│ 8 │ 733 │ 5 │ NONE │ 733 │ 17 │ 30.4 │",
"│ 9 │ 900 │ 15 │ NONE │ 901 │ 17 │112.0 │",
"│ 10 │ 820 │ 3 │ NONE │ 820 │ 17 │ 20.4 │",
"│ 11 │ 855 │ 5 │ NONE │ 855 │ 17 │ 35.4 │",
"│ 12 │ 855 │ 7 │ NONE │ 855 │ 17 │ 49.6 │",
"│ 13 │ 306 │ 8 │ 128 │ 319 │ 17 │ 20.3 │",
"│ 14 │ 733 │ 7 │ NONE │ 733 │ 17 │ 42.5 │",
"│ 16 │ 612 │ 4 │ 0 │ 663 │ 17 │ 20.5 │",
"│ 17 │ 977 │ 5 │ 300 │ 977 │ 17 │ 40.5 │",
"│ 18 │ 977 │ 7 │ NONE │ 977 │ 17 │ 56.7 │",
"│ 19 │ 1024 │ 7 │ 512 │ 1023 │ 17 │ 59.5 │",
"│ 20 │ 733 │ 5 │ 300 │ 732 │ 17 │ 30.4 │",
"│ 21 │ 733 │ 7 │ 300 │ 732 │ 17 │ 42.5 │",
"│ 22 │ 733 │ 5 │ 300 │ 733 │ 17 │ 30.4 │",
"│ 23 │ 306 │ 4 │ 0 │ 336 │ 17 │ 10.1 │",
"│ 25 │ 615 │ 4 │ 0 │ 615 │ 17 │ 20.4 │",
"│ 26 │ 1024 │ 4 │ NONE │ 1023 │ 17 │ 34.0 │",
"│ 27 │ 1024 │ 5 │ NONE │ 1023 │ 17 │ 42.5 │",
"│ 28 │ 1024 │ 8 │ NONE │ 1023 │ 17 │ 68.0 │",
"│ 29 │ 512 │ 8 │ 256 │ 512 │ 17 │ 34.0 │",
"│ 30 │ 615 │ 2 │ 615 │ 615 │ 17 │ 10.2 │",
"│ 31 │ 989 │ 5 │ 0 │ 989 │ 17 │ 41.0 │",
"│ 32 │ 1020 │ 15 │ NONE │ 1024 │ 17 │127.0 │",
"│ 35 │ 1024 │ 9 │ 1024 │ 1024 │ 17 │ 76.5 │",
"│ 36 │ 1024 │ 5 │ 512 │ 1024 │ 17 │ 42.5 │",
"│ 37 │ 830 │ 10 │ NONE │ 830 │ 17 │ 68.8 │",
A to Z of C 343
CMOS cmosdata;
char *iptr = (char *)&cmosdata;
int j, k, drive;
k = (cmosdata.drived);
printf( "Drive %c: %s\n", drive++, harddisk[j] );
printf( "Drive %c: %s\n", drive, harddisk[k] );
printf( " └──────┴──────────┴────"
"───┴──────┴──────┴─────────┴──────┘\n" );
}
else
printf( "None\n" );
iptr = (char *)&cmosdata;
printf( "\nHex Dump of CMOS RAM:\n" );
for ( j=0,k=0 ; j<64; j++ )
{
printf( "%02x ", *iptr++ );
k++;
if ( k == 16 )
{
k = 0;
printf( "\n" );
}
}
} /*--ReadCMOS( )-----------*/
Device Driver
Programming
“Device driver” and “Driver” are interchangeably used in Programming world. Device
drivers are the programs that control the functioning of peripherals. According to me, writing
device driver is one of the easier things in programming. What all you need to know for device
driver programming is good knowledge of hardware components. You may also need to know,
how to access those hardware components through programs. In this chapter let’s see how to
write our own device driver.
43.1 Secrets
As I said earlier, device drivers are the programs that control the functioning of
peripherals like keyboard, printer, etc. More specifically, they are the modules of an operating
system.
MS DOS device drivers are with .SYS extensions. Since drivers drive peripheral devices,
they get loaded into the memory when we bootup the system. So obviously, they remain resident
in memory, but they are not considered as normal TSRs.
As drivers are the modules of an Operating System, one has to modify the OS whenever
he adds new device to his system. Fortunately the installable device drivers technology available
with MS DOS gives more flexibility to the user. It avoids direct operations or modifications of
Operating System. The user can simply install a new device in a system, copy the driver files to
boot disk and edit the system configuration file. Thus it clearly avoids complexity.
43.4 BUF160
BUF160 is a device driver for expanding the default keyboard buffer from 16 bytes to
160 bytes. 16 bytes restriction of default keyboard buffer might be strange to the people who are
unnoticingly using keyboard buffer expansion program. If you don’t use any keyboard buffer
expansion utility and if your keyboard buffer is still 16 bytes in size (i.e., it can hold only 16
character when you work under command prompt), you may try this BUF160.
BUF160 is a good device driver. The recent version is 1.6a. Many people including D J
Delorie, David Kirschbaum & Robert M. Ryan contributed to BUF160.
It works by installing itself as the standard keyboard buffer in the BIOS. It can only do
this if it is in the same segment as the BIOS, so you are advised to install it as the first device
driver. While it installs itself into the BIOS, it also installs a device driver called KBUFFER.
Anything written to KBUFFER ends up in the keyboard buffer. I suggest you to look into the
memory map found with Ralf Brown’s Interrupt List for understanding BIOS data area.
43.4.1 Source code
Following is the source code of BUF160. It is written in assembly. As the code is more
clear, I don’t want to port it to Turbo C. I hope this real code will help you to understand the
concepts behind device drivers. Refer the comment line for explanations.
title BUF160
page 58,132
;
; BUF160.ASM
;
;**********************************************************************
; Compilation flags
;**********************************************************************
A to Z of C 347
;**********************************************************************
; General equates
;**********************************************************************
;**********************************************************************
; Data structures
;**********************************************************************
dqq struc
ofs dw ?
segw dw ? ;changed from 'seg' to keep MASM 5.0 happy v1.4
dqq ends
;**********************************************************************
; Pointers to BIOS data segment, v1.4
348 A to Z of C
;**********************************************************************
BIOS_DATA_SEG equ 40H ;MASM had prob using BIOS_DATA in
calculations,
; so this typeless constant introduced. v1.6
;**********************************************************************
; The actual program
;**********************************************************************
IF USE286 ; v1.5
.286
%OUT Compiling 286 code ...
ELSE
%OUT Compiling generic 8086 code ...
ENDIF
IF PRIVATESTACK
%OUT Using private stack ...
ELSE
%OUT Not using private stack ...
ENDIF
IF TRANSFER
%OUT Including keyboard transfer code ...
ELSE
%OUT Not including keyboard transfer code ...
ENDIF
public header
header label near
dd -1 ;pointer to next device
dw 8000h ;type device
dw Strat ;strategy entry point
dw Intr ;interrupt entry point
db 'KBUFFER ' ;device name
A to Z of C 349
public req
req dd ? ;store request header vector here
public queue_start,queue_end
queue_start dw BUFSIZE dup (0) ;our expanded keyboard buffer
queue_end equ $ - start ;calculate offset as typeless
constant
IF PRIVATESTACK ; v1.6
stack_end db STACKSZ dup (0) ;use our own private data stack
stack_start equ $
oldss dw 0
oldsp dw 0
oldax dw 0
ENDIF
;*********************************************************************
; Strategy procedure
; Save the pointer to the request header for Intr in the req area.
; Enters with pointer in es:bx
;*********************************************************************
public Strat
Strat proc far
mov cs:[req].ofs,bx
mov cs:[req].segw,es ; v1.4
ret
Strat endp
;**********************************************************************
; The main interrupt (driver)
; This is the actual driver. Processes the command contained in the
; request header. (Remember, req points to the request header.)
;**********************************************************************
public Intr
ASSUME ds:Cseg, es:NOTHING ; v1.4
Intr proc far
mov cs:oldsp, sp
mov sp, offset stack_start
mov ax, cs
mov ss, ax
sti ; turn ints back on
mov ax, cs:oldax
ENDIF
mov ax,cs
mov ds,ax ;DS=code segment
IF PRIVATESTACK
mov ax, cs:oldss ; v1.6
cli ; turn ints off
mov ss, ax
mov sp, cs:oldsp
mov ax, cs:oldax
sti ; turn ints on
ENDIF
ret
public cmd_table
cmd_table: ;command routing table
dw Cmd_Init ;0=initialization (we do that)
dw Cmd_None ;1=media check (always SUCCESS)
dw Cmd_None ;2=build BIOS param block (ditto)
dw Cmd_None ;3=IO control input (ditto)
dw Cmd_None ;4=input from device (ditto)
dw Cmd_None ;5=nondest input no-wait (ditto)
dw Cmd_None ;6=input status (ditto)
dw Cmd_None ;7=flush input queue (ditto)
dw Cmd_Output ;8=output to device (we do that)
dw Cmd_Output ;9=output with verify (same thing)
dw Cmd_Output_Status ;A=output status (we do that)
dw Cmd_None ;B=flush output queue (always SUCCESS)
dw Cmd_None ;C=IO control output (ditto)
;*********************************************************************
; Cmd_Output procedure
;*********************************************************************
public Cmd_Output
Cmd_Output proc near
mov ax,BIOS_DATA
mov ds,ax ;BIOS data area
ASSUME ds:BIOS_DATA ;keep MASM happy v1.4
mov cx,es:[bx].count
les bx,es:[bx].trans
Output_Loop:
mov al,es:[bx]
inc bx
cli
mov di,BUFFER_PUT ;next free space v1.4
call Buf_Wrap ;add 2, check for wraparound
cmp di,BUFFER_GET ;is the buffer full? v1.4
sti ;ints back on v1.4
je Output_Error ;buffer is full, error v1.4
352 A to Z of C
Output_Error:
mov ax,ERROR
ret
Cmd_Output endp
;*********************************************************************
; Buf_Wrap procedure
;*********************************************************************
public Buf_Wrap
Buf_Wrap proc near
inc di
inc di
cmp di,BUFFER_END ;hit end yet? v1.4
je Wrap ;>=, wrap around v1.4
ret
Wrap:
mov di,BUFFER_START ;force ptr to start v1.4
ret
Buf_Wrap endp
;*********************************************************************
; Cmd_Output_Status procedure
;*********************************************************************
public Cmd_Output_Status
Cmd_Output_Status proc near
mov ax,BIOS_DATA
mov ds,ax
mov di,BUFFER_PUT ;ptr to next free space v1.4
call Buf_Wrap ;wraparound if necessary
cmp di,BUFFER_GET ;same as next char to get? v1.4
jne Cmd_None ;ok, return SUCCESS v1.4a
mov ax,BUSY
ret
Cmd_Output_Status endp
A to Z of C 353
public last_code
last_code label near
;*********************************************************************
; Initialization (installation) procedure
;*********************************************************************
public Cmd_Init
Cmd_Init proc near
mov ax,cs
mov ds,ax
mov es,ax ; v1.4a
ASSUME ds:Cseg,es:Cseg ; v1.4a
IF TRANSFER
public Transfer_Buffer
Transfer_Buffer:
mov si,BUFFER_GET ;next key to read v1.4
mov dx,BUFFER_PUT ;next empty space v1.4a
public Transfer_Done
Transfer_Done:
ENDIF
public Init_Error
ASSUME ds:Cseg,es:Cseg ; v1.4
Init_Error:
mov dx,offset msg_err ;'Buf160 too far...'
mov ah,9 ;display msg
int 21h
ELSE
mov es:[bx].trans.ofs,offset last_code
ENDIF
Stuff_Seg: ; v1.4a
mov es:[bx].trans.segw,cs ; v1.4
mov ax,SUCCESS
ret
Cmd_Init endp
Intr endp
Cseg ends
end
Network Programming
This chapter will be useful for the people who are working with LAN. Novell Netware
and Windows NT are the most widely used Network Operating Systems. These Network
Operating Systems help to link the computers present on LAN and support resource sharing.
#include "netware.h"
#include <stdio.h>
Note
This program would compile only in Tiny memory model.
/**********************************************************************/
/* File: ULIST.C */
/* */
/* Function: List all users that are currently logged into the*/
/* default server, and some useful stats (only if */
/* calling user has console operator rights). */
/* */
/* Usage: ulist */
/* */
/* Functions Called: GetConnectionNumber */
/* GetConnectionInformation */
/* GetConnectionsUsageStatistics */
/* */
/**********************************************************************/
#include <conio.h>
#include <dos.h>
#ifndef TURBOC
#include <search.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "netware.h"
#define FALSE 0
#define TRUE (!FALSE)
/**********************************************************************/
void main()
{
unsigned int station;
long object_id;
word object_type;
char object_name[OBJECT_LENGTH];
char logintime[7];
int thisone;
long systemelapsedtime;
double bytesread,byteswritten;
long totalrequestpackets;
char c;
360 A to Z of C
printf(" ---Login----");
printf(" -----file bytes------ request\n");
printf("conn User Name day time");
printf(" read written packets\n");
printf("==================================");
printf(" ===============================\n");
44.2 Windows NT
Windows NT is another famous Network Operating System. We cannot program it from
TC/DOS. The fact is Windows NT does not have DOS. The ‘command prompt’ of Windows NT
is just a DOS Emulator. Windows NT uses different technologies from other Windows versions
like 95/98. Windows 95 and Windows 98 are the GUIs (Graphical User Interface) running above
DOS. Whereas Windows NT is a pure 32 bit Operating System. And so programming Windows
NT from DOS is not possible.
45
“Kindness is rewarded.”
Writing Browser
First of all we must know that browser is the one, which reads the HTML file found over
net and formats the output according to the specification.
Programming Protocols
“Protocol” is defined as set of rules. So it is clear that if you know those “rules” defined
by someone, you won’t find any difficulty in programming protocols.
So, for writing protocol, you need the specification or the rules for that protocol.
Specifications for the important protocols are available on CD .
Writing Operating
System
Operating System is nothing but collection of programs for managing system resources
like CPU, memory, storage device etc. Study of the Operating System is one of the vastest areas.
This chapter does not deal with the details about Operating System. And in this chapter I would
like to show you how OS can be written in Turbo C. However you may not be able to code your
Operating System without depth knowledge of memory management, processor scheduling etc.
So I strongly recommend you to go through a good Operating System book for indepth
knowledge. According to me most of the people are not using Turbo C to write OS, because
Turbo C is 16bit. Also people mainly hangout with Assembly language for a better and tight
code.
47.1 EZOS_86
EZOS_86 is a simple multitasking kernel written in Turbo C by Scott A. Christensen for
x86 machines in 1996-97. Operating Systems are usually protected and licensed according to
GNU’s General Public License and so this EZOS_86! So if you modify or rewrite this source
code, you must acknowledge the author Scott A. Christensen and you are expected to keep the
name of the revised OS as EZOS_86, but you can change the version. Regarding OS and other
software, violation of copyright is treated as high offense. So beware of the licenses!
47.1.1 Notes
The author Scott A. Christensen added following note:
EZOS_86 is a simple multitasking kernel for the x86 family. It is written in 100% C
source (it uses Turbo C extensions to access the registers). If you need a tight, fast, hand-coded,
assembly kernel, forget this one!
The main emphasis here is to keep it simple: no linked lists, no dynamic allocation, no
complicated task scheduling, no assembly language, etc. Yes, this can be embedded!
The scheduler is very rudimentary. It is preemptive, but with a strictly prioritized order.
There is no protection from starvation; if a higher priority task spins the CPU, the lower priority
tasks will never execute. Programs for embedded applications are often event driven and
properly written will work fine. On the other hand, it wouldn't be that hard to change the
scheduler to a round robin method if desired.
The scheduler always traverses the Task Control Block (TCB) array from the beginning
(&tcb[0]). The first task encountered that is eligible to run is the one executed. At least one task
364 A to Z of C
MUST always be eligible to run; hence the "null" task, which is created as the lowest priority and
NEVER, sleeps.
The same task function can have multiple instances. For example you could call
OsTaskCreate( ) three times and pass task0 as the function all three times. Of course you
must specify a unique stack and tcb. The parameter passed to task0 can identify the particular
instance of the function.
Reentrancy issues:
• use the runtime library at your own risk (reason for direct video)
• floating point is not reentrant; use semaphore protection or only do floating point in
one task.
Semaphores:
• clearing semaphore does not cause task switch; call OsSchedule( ) to yield. This
can throttle throughput. One could have null task continuously scan TCBs for eligible
task and yield.
• OsSemClear( ) returns TRUE if higher priority task waiting on sem
• multiple tasks can sleep on same semaphore
• ok to clear semaphore from within interrupt routine
As written this code will run a demo on an IBM clones and even clean up upon exit
returning nicely backs to DOS. It creates the file "out" to dump the stack contents. Interrupt
routines use the current task's stack. Be careful not to exceed your allocated stack space; very
strange results can occur. Compile it with Turbo C with optimization off.
Wishlist:
• simple file functions to read/write directly to IDE HD with FAT16
• multitasking capable floating point support
• some sort of built in debugging capability (TBUG.ZIP looks like a good start)
• runtime calculation of cpu utilization
• a _simplified_ malloc for embedded applications
47.1.2 Kernel Source Code
/*
* ezos_86.c
*
* Copyright (c) 1996-7 Scott A. Christensen
* All Rights Reserved
*
* This file is part of the EZOS_86 multitasking kernel.
*
*
A to Z of C 365
* version description
* --------------------------------------------------------------
* 0.01.00 initial release
*
*/
#include <dos.h>
#include <stdio.h>
#include <conio.h>
#include <stdarg.h>
/*--------------------------------------------------------------*/
#define TRUE (0 == 0)
#define FALSE (0 != 0)
#define RUNNING 0
#define RUN_ASAP 1
#define SLEEPING 2
#define PENDING 3
#define SUSPENDED 4
#define KILLED 5
#define ALL_KILLED -2
#define NOT_STARTED -1
#define TICK_VECT 8
#define MAX_TASKS 10
#define STACKSIZE 1024
#define PENDING_SEM_REQUEST 0
#define PENDING_SEM_WAIT 1
#define schedule() \
{ \
int si; \
366 A to Z of C
/*--------------------------------------------------------------*/
typedef void (far cdecl *FUNCPTR)();
typedef struct
{
unsigned int r_bp;
unsigned int r_di;
unsigned int r_si;
unsigned int r_ds;
unsigned int r_es;
unsigned int r_dx;
unsigned int r_cx;
unsigned int r_bx;
unsigned int r_ax;
A to Z of C 367
FUNCPTR taskStartAddr;
unsigned int r_flags;
FUNCPTR taskExitReturn;
void * pTaskParam;
} STACK_REC;
typedef struct
{
unsigned int taskStatus;
unsigned int taskSP;
unsigned int taskSS;
long ticks;
int semState;
int * pSem;
} TCB_REC, *PTCB_REC;
/*--------------------------------------------------------------*/
void far interrupt OsTickIsr(void);
int far interrupt OsSchedule(void);
void far OsTaskKill(void);
void OsTaskCreate(PTCB_REC, FUNCPTR, void *,
unsigned char far *, int);
long OsTranslateMilsToTicks(long);
void OsInstall(void);
void OsRun(void);
void OsDeinstall(void);
void OsSleep(long);
void OsSleepTicks(long);
int OsSemClear(int *);
void OsSemSet(int *);
int OsSemWait(int *, long);
int OsSemSetWait(int *, long);
int OsSemRequest(int *, long);
int OsDisableStat(void);
/*--------------------------------------------------------------*/
void (far interrupt *oldTickIsr)(void);
368 A to Z of C
int numTasks = 0;
int killedTasks = 0;
int curTask = NOT_STARTED;
int mainSleep = TRUE;
unsigned int mainSP;
unsigned int mainSS;
TCB_REC tcb[MAX_TASKS];
unsigned int _stklen = (STACKSIZE * MAX_TASKS) + 1024;
int itick = 0;
unsigned int (far *screen)[80];
int row = 0;
int col = 0;
int tickSem = 1;
int goSem = 1;
int screenSem = 0;
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void main()
{
unsigned char stack0[STACKSIZE];
unsigned char stack1[STACKSIZE];
unsigned char stack2[STACKSIZE];
unsigned char stackNull[STACKSIZE];
FILE * f;
clrscr();
puts("\n\n EZOS_86 multitasking kernel");
puts(" Copyright (C) 1996-97 Scott A. Christensen");
delay(5000);
clrscr();
gotoxy(1, 24);
screen = MK_FP(0xB800, 0);
OsInstall();
OsRun();
OsDeinstall();
A to Z of C 369
f = fopen("out", "wb");
fclose(f);
/*--------------------------------------------------------------*/
void dumpStack(
FILE * f,
unsigned char * stack,
int size
)
{
int i;
char buf[80];
char string[80];
string[0] = 0;
for(i = 0; i < size; i++)
{
if(i % 16 == 0)
fprintf(f, "%04X:%04X ", FP_SEG(&stack[i]), FP_OFF(&stack[i]));
fprintf(f, "%02X ", stack[i]);
if(isalnum(stack[i]) || stack[i] == ' ')
{
buf[0] = stack[i];
buf[1] = 0;
strcat(string, buf);
}
else
strcat(string, ".");
if(i % 16 == 15)
{
fprintf(f, " %s\r\n", string);
string[0] = 0;
}
}
fprintf(f, "\r\n");
}
/*--------------------------------------------------------------*/
370 A to Z of C
void OsInstall()
{
oldTickIsr = getvect(TICK_VECT);
setvect(TICK_VECT, OsTickIsr);
}
/*--------------------------------------------------------------*/
void OsRun()
{
while(mainSleep);
}
/*--------------------------------------------------------------*/
void OsDeinstall()
{
setvect(TICK_VECT, oldTickIsr);
}
/*--------------------------------------------------------------*/
void far interrupt OsTickIsr()
{
int i;
static PTCB_REC pTCBi;
switch(curTask)
{
case ALL_KILLED:
break;
case NOT_STARTED:
mainSP = _SP;
mainSS = _SS;
pTCBi = tcb;
pTCBi->taskStatus = RUNNING;
_SP = pTCBi->taskSP;
_SS = pTCBi->taskSS;
curTask = 0;
break;
default:
itick++;
A to Z of C 371
oldTickIsr();
}
/*--------------------------------------------------------------*/
int far interrupt OsSchedule()
{
OsDisable();
schedule();
return _AX; /* dummy value */
}
/*--------------------------------------------------------------*/
void far OsTaskKill()
{
OsDisable();
killedTasks++;
tcb[curTask].taskStatus = KILLED;
OsSchedule();
}
/*--------------------------------------------------------------*/
void OsTaskCreate(
PTCB_REC pTCB,
FUNCPTR func,
void * pTaskParam,
unsigned char far * pStack,
int stackSize
)
{
STACK_REC far * pStackRec;
int i;
372 A to Z of C
pStackRec->r_bp = 0;
pStackRec->r_di = 0;
pStackRec->r_si = 0;
pStackRec->r_ds = _DS;
pStackRec->r_es = _DS;
pStackRec->r_dx = 0;
pStackRec->r_cx = 0;
pStackRec->r_bx = 0;
pStackRec->r_ax = 0;
pStackRec->taskStartAddr = func;
pStackRec->r_flags = 0x0200;
pStackRec->taskExitReturn = OsTaskKill;
pStackRec->pTaskParam = pTaskParam;
pTCB->taskStatus = RUN_ASAP;
pTCB->taskSP = FP_OFF(pStackRec);
pTCB->taskSS = FP_SEG(pStackRec);
numTasks++;
}
/*--------------------------------------------------------------*/
long OsTranslateMilsToTicks(
long mils
)
{
long x;
if(!mils)
return 0L;
return x;
}
/*--------------------------------------------------------------*/
void OsSleep(
A to Z of C 373
long mils
)
{
long ticks;
ticks = OsTranslateMilsToTicks(mils);
OsSleepTicks(ticks);
}
/*--------------------------------------------------------------*/
void OsSleepTicks(
long ticks
)
{
PTCB_REC pTCB;
OsDisable();
pTCB = &tcb[curTask];
pTCB->taskStatus = SLEEPING;
pTCB->ticks = ticks;
OsSchedule();
}
/*--------------------------------------------------------------*/
int OsSemClear(
int * pSem
)
{
int i;
STACK_REC far * pStackRec;
int processedRequest;
PTCB_REC pTCB;
int higherEligible;
int intsEnabled;
intsEnabled = OsDisableStat();
if(!*pSem)
{
if(intsEnabled)
OsEnable();
374 A to Z of C
return FALSE;
}
*pSem = 0;
processedRequest = FALSE;
higherEligible = FALSE;
case PENDING_SEM_WAIT:
pStackRec = MK_FP(pTCB->taskSS, pTCB->taskSP);
pStackRec->r_ax = 0;
pTCB->taskStatus = RUN_ASAP;
if(i < curTask)
higherEligible = TRUE;
break;
}
}
}
if(intsEnabled)
OsEnable();
return higherEligible;
}
/*--------------------------------------------------------------*/
void OsSemSet(
int * pSem
)
{
int intsEnabled;
intsEnabled = OsDisableStat();
*pSem = 1;
A to Z of C 375
if(intsEnabled)
OsEnable();
}
/*--------------------------------------------------------------*/
int OsSemWait(
int * pSem,
long mils
)
{
long ticks;
PTCB_REC pTCB;
OsDisable();
if(!*pSem)
{
OsEnable();
return 0;
}
ticks = OsTranslateMilsToTicks(mils);
if(!ticks)
{
OsEnable();
return TSK_ERR_TIMEOUT;
}
pTCB = &tcb[curTask];
pTCB->taskStatus = PENDING;
pTCB->semState = PENDING_SEM_WAIT;
pTCB->pSem = pSem;
pTCB->ticks = ticks;
_AX = TSK_ERR_TIMEOUT;
return OsSchedule();
}
/*--------------------------------------------------------------*/
int OsSemSetWait(
int * pSem,
376 A to Z of C
long mils
)
{
OsDisable();
OsSemSet(pSem);
/*--------------------------------------------------------------*/
int OsSemRequest(
int * pSem,
long mils
)
{
long ticks;
PTCB_REC pTCB;
OsDisable();
if(!*pSem)
{
*pSem = 1;
OsEnable();
return 0;
}
ticks = OsTranslateMilsToTicks(mils);
if(!ticks)
{
OsEnable();
return TSK_ERR_TIMEOUT;
}
pTCB = &tcb[curTask];
pTCB->taskStatus = PENDING;
pTCB->semState = PENDING_SEM_REQUEST;
pTCB->pSem = pSem;
pTCB->ticks = ticks;
_AX = TSK_ERR_TIMEOUT;
A to Z of C 377
return OsSchedule();
}
/*--------------------------------------------------------------*/
int OsDisableStat()
{
unsigned int flags;
flags = _FLAGS;
OsDisable();
/*--------------------------------------------------------------*/
void tprintf(
const char * format,
...
)
{
va_list argPtr;
char buf[100];
struct time t;
va_start(argPtr, format);
vsprintf(buf + 18, format, argPtr);
va_end(argPtr);
OsSemRequest(&screenSem, OS_INFINITE_WAIT);
gettime(&t);
sprintf(buf, "-T%02d(%02d:%02d:%02d.%02d)",
curTask, t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
sout(buf);
OsSemClear(&screenSem);
}
/*--------------------------------------------------------------*/
void tputs(
378 A to Z of C
OsSemRequest(&screenSem, OS_INFINITE_WAIT);
gettime(&t);
sout(buf);
OsSemClear(&screenSem);
}
/*--------------------------------------------------------------*/
void sout(
char * p
)
{
while(*p)
{
switch(*p)
{
case '\r':
col = 0;
break;
case '\n':
col = 0;
incRow();
break;
case '\t':
sout(" ");
break;
default:
screen[row][col] = ATTR | ((unsigned int) *p);
if(++col > 79)
{
col = 0;
A to Z of C 379
incRow();
}
break;
}
p++;
}
}
/*--------------------------------------------------------------*/
void incRow()
{
int r;
int c;
row = 24;
}
}
/*--------------------------------------------------------------*/
void far task0(
void * pTaskParam
)
{
int val = (int) pTaskParam;
int i;
long j;
int rc;
OsSemWait(&goSem, OS_INFINITE_WAIT);
tputs("OsSemWait successful");
OsSleep(150L);
break;
case TSK_ERR_TIMEOUT:
tputs("OsSemWait failed, error = TSK_ERR_TIMEOUT");
break;
default:
tprintf("OsSemWait failed, error = %d\n", rc);
break;
}
OsSleep(100L);
}
}
/*--------------------------------------------------------------*/
void far task1(
void * pTaskParam
)
{
int val = (int) pTaskParam;
int i;
OsSemWait(&goSem, OS_INFINITE_WAIT);
tputs("");
}
tputs("clearing tickSem");
OsSemClear(&tickSem);
OsSleep(1000L);
tputs("");
}
/*--------------------------------------------------------------*/
void far task2(
A to Z of C 381
void * pTaskParam
)
{
int val = (int) pTaskParam;
int i;
int j;
OsSleep(2000L);
OsSemClear(&goSem);
/*--------------------------------------------------------------*/
void far taskNull(
void * pTaskParam
)
{
48 Developing a new
language / writing
compiler
Believe it or not, developing a new language is one of the easiest things in programming
as we’ve got so many tools for developing compliers.
48.1 Secrets
Developing a new language refers to developing new grammar. Grammar refers to rules
of the language.
For example, following is the part of grammar for enum of C:
enum-specifier:
enum identifer { enumerator-list}
enum identifer
enumerator-list:
enumerator
enumerator-list, enumerator
enumerator:
identifier
identifer = constant-expression
So you need to write your new language's grammar first. By the way, you must decide
the data types, keywords and operators too. After preparing grammar you may need to produce a
complier for your language to emphasize the merits of your language.
• Linker is the one which links various object (.OBJ) files and produces executable files
(.EXE or .COM).
Nowadays, we have certain integrated compilers that are able to produce the executable
files directly for a given file in high-level language
Source code in
High level
language
Preprocessor Pass 1
Request tokens
Lexical analyzer Parser
“Useable” token
Pass 2
Intermediate Code
Optimizer Pass 3
Assembly code
Compile the hello.c program using command line compiler tcc with -S switch to get
assembly listing as
c:>tcc –S hello.c
sub sp,2
;
; {
; char *str = "Hello!\n";
;
mov word ptr [bp-2],offset DGROUP:s@
;
; printf("%s", str);
;
push word ptr [bp-2]
mov ax,offset DGROUP:s@+8
push ax
call near ptr _printf
pop cx
pop cx
;
; return( 0 );
;
xor ax,ax
jmp short @1@58
@1@58:
;
; }
;
mov sp,bp
pop bp
ret
_main endp
?debug C E9
_TEXT ends
_DATA segment word public 'DATA'
s@ label byte
db 'Hello!'
db 10
db 0
db '%s'
db 0
_DATA ends
_TEXT segment byte public 'CODE'
_TEXT ends
extrn _printf:near
public _main
_s@ equ s@
end
386 A to Z of C
Here you can see how each C statement has been converted to equivalent assembly. The
C statements are commented out with semicolon (;) in assembly file. I hope this might give you
an idea about how high level statements are converted to equivalent assembly by compiler.
Assembly file produced by the compiler can be assembled with the available assembler or with
your own assembler.
language.lex language.y
(scanner (parser/grammar
descriptions) descriptions)
lex yacc
C compiler
(e.g. cc)
lib1.lib, lib2.a
Other linker (Files to be linked in
libraries & objects Unix environment)
language.c
(Generated
Compiler)
With little bit of creativity and compiler-writing utilities, hope you might come out with a
new language!
49
“If you have lots of good advice, you will win.”
Writing YACC
YACC( Yet Another Compiler-Compiler) is a compiler writing tool. In this chapter, let’s
see how to write such a compiler writing tool.
49.1 Prelude
YACC was once available to Unix users only. Now we have
DOS versions too. When we discussed about writing compilers, we
File with
have seen the uses of YACC. YACC gets the grammar for a given
grammar
(new) language and generates a C file that can be compiled to work as descriptions
a compiler for that new language. More specifically YACC don’t
directly generate compiler but generates parser.
YACC uses certain syntax or grammer to represent the
grammar for new language. So one must be aware of the syntax used yacc
by YACC for its grammar file. As it has to output the compiler file,
writing YACC is similar to writing a compiler.
C file for
49.2 BYACC parser
From the above discussion, it is clear that writing a YACC is
really a tough job than writing a compiler! BYACC for DOS
(Berkeley YACC for MS-DOS) is one of the good implementations.
49.2.1 Brief History
The original YACC was developed by AT&T. YACC interested many other people in the
mean time. Later Berkeley University developed a open YACC and provided the source code to
all. So the Berkeley’s YACC was appreciated by all the people who are interested in writing
compiler. Both AT&T and Berkeley’s YACC was written for Unix environment. At that time,
DOS doesn’t have such utility. Stephen C. Trier used the source code provided by Berkeley and
modified it for DOS and DOS version of YACC came into existence.
49.2.2 Source code
Source code of BYACC is more useful to understand the techniques and tactics used by
real programmers. Many thanks to Jeff Jenness & Stephen C. Trier for providing such a good
YACC. Following are the set of files used for BYACC. In order to understand the following
A to Z of C 389
source code, you may need to know the syntax used by YACC for writing a grammar file. More
documentation can be found on CD .
When you look at the source code, you may find that the function prototype declarations
are in obsolete form. So you may get obsolete prototype declaration warning. That is because, the
source code provided by Berkeley is quite older.
49.2.2.1 Def.h
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#ifdef MSDOS
#include <alloc.h>
#endif
/* character names */
#ifdef MSDOS
#define CODE_SUFFIX "_code.c"
#define DEFINES_SUFFIX "_tab.h"
#define OUTPUT_SUFFIX "_tab.c"
#define VERBOSE_SUFFIX ".out"
#else
#define CODE_SUFFIX ".code.c"
#define DEFINES_SUFFIX ".tab.h"
#define OUTPUT_SUFFIX ".tab.c"
#define VERBOSE_SUFFIX ".output"
#endif
/* keyword codes */
#define TOKEN 0
#define LEFT 1
#define RIGHT 2
#define NONASSOC 3
#define MARK 4
#define TEXT 5
#define TYPE 6
#define START 7
#define UNION 8
#define IDENT 9
/* symbol classes */
#define UNKNOWN 0
#define TERM 1
#define NONTERM 2
/* action codes */
#define SHIFT 1
#define REDUCE 2
#define ERROR 3
/* character macros */
#define IS_IDENT(c) (isalnum(c)||(c) == '_' || (c) == '.' || (c) == '$')
#define IS_OCTAL(c) ((c) >= '0' && (c) <= '7')
#define NUMERIC_VALUE(c) ((c) - '0')
/* symbol macros */
#define ISTOKEN(s) ((s) < start_symbol)
#define ISVAR(s) ((s) >= start_symbol)
/* global variables */
/* global functions */
/* system variables */
/* system functions */
#ifndef MSDOS
extern void free();
A to Z of C 395
short *itemset;
short *itemsetend;
unsigned *ruleset;
set_EFF()
{
register unsigned *row;
register int symbol;
register short *sp;
register int rowsize;
register int i;
register int rule;
rowsize = WORDSIZE(nvars);
EFF = NEW2(nvars * rowsize, unsigned);
row = EFF;
for (i = start_symbol; i < nsyms; i++)
{
sp = derives[i];
for (rule = *sp; rule > 0; rule = *++sp)
{
symbol = ritem[rrhs[rule]];
if (ISVAR(symbol))
{
symbol -= start_symbol;
SETBIT(row, symbol);
}
}
row += rowsize;
}
reflexive_transitive_closure(EFF, nvars);
396 A to Z of C
#ifdef DEBUG
print_EFF();
#endif
}
set_first_derives()
{
register unsigned *rrow;
register unsigned *vrow;
register int j;
register unsigned mask;
register unsigned cword;
register short *rp;
int rule;
int i;
int rulesetsize;
int varsetsize;
rulesetsize = WORDSIZE(nrules);
varsetsize = WORDSIZE(nvars);
first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens *
rulesetsize;
set_EFF();
mask <<= 1;
A to Z of C 397
if (mask == 0)
{
cword = *vrow++;
mask = 1;
}
}
vrow += varsetsize;
rrow += rulesetsize;
}
#ifdef DEBUG
print_first_derives();
#endif
FREE(EFF);
}
closure(nucleus, n)
short *nucleus;
int n;
{
register int ruleno;
register unsigned word;
register unsigned mask;
register short *csp;
register unsigned *dsp;
register unsigned *rsp;
register int rulesetsize;
short *csend;
unsigned *rsend;
int symbol;
int itemno;
rulesetsize = WORDSIZE(nrules);
rsp = ruleset;
rsend = ruleset + rulesetsize;
for (rsp = ruleset; rsp < rsend; rsp++)
*rsp = 0;
csend = nucleus + n;
for (csp = nucleus; csp < csend; ++csp)
{
symbol = ritem[*csp];
if (ISVAR(symbol))
{
dsp = first_derives + symbol * rulesetsize;
398 A to Z of C
rsp = ruleset;
while (rsp < rsend)
*rsp++ |= *dsp++;
}
}
ruleno = 0;
itemsetend = itemset;
csp = nucleus;
for (rsp = ruleset; rsp < rsend; ++rsp)
{
word = *rsp;
if (word == 0)
ruleno += BITS_PER_WORD;
else
{
mask = 1;
while (mask)
{
if (word & mask)
{
itemno = rrhs[ruleno];
while (csp < csend && *csp < itemno)
*itemsetend++ = *csp++;
*itemsetend++ = itemno;
while (csp < csend && *csp == itemno)
++csp;
}
mask <<= 1;
++ruleno;
}
}
}
#ifdef DEBUG
print_closure(n);
#endif
}
finalize_closure()
{
FREE(itemset);
FREE(ruleset);
A to Z of C 399
#ifdef DEBUG
print_closure(n)
int n;
{
register short *isp;
print_EFF()
{
register int i, j, k;
register unsigned *rowp;
register unsigned word;
register unsigned mask;
mask = 1;
for (j = 0; j < nvars; j++)
{
if (word & mask)
printf(" %s", symbol_name[start_symbol + j]);
mask <<= 1;
if (mask == 0)
{
word = *rowp++;
mask = 1;
}
}
}
}
400 A to Z of C
print_first_derives()
{
register int i;
register int j;
register unsigned *rp;
register unsigned cword;
register unsigned mask;
printf("\n\n\nFirst Derives\n");
mask <<= 1;
if (mask == 0)
{
cword = *rp++;
mask = 1;
}
}
}
fflush(stdout);
}
#endif
49.2.2.3 Error.c
/* routines for printing error messages */
#include "defs.h"
fatal(msg)
char *msg;
{
fprintf(stderr, "%s: f - %s\n", myname, msg);
done(2);
}
A to Z of C 401
no_space()
{
fprintf(stderr, "%s: f - out of space\n", myname);
done(2);
}
open_error(filename)
char *filename;
{
fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename);
done(2);
}
unexpected_EOF()
{
fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-
file\n",
myname, lineno, input_file_name);
done(1);
}
print_pos(st_line, st_cptr)
char *st_line;
char *st_cptr;
{
register char *s;
if (st_line == 0) return;
for (s = st_line; *s != '\n'; ++s)
{
if (isprint(*s) || *s == '\t')
putc(*s, stderr);
else
putc('?', stderr);
}
putc('\n', stderr);
for (s = st_line; s < st_cptr; ++s)
{
if (*s == '\t')
putc('\t', stderr);
else
putc(' ', stderr);
}
putc('^', stderr);
putc('\n', stderr);
}
402 A to Z of C
char *u_cptr;
{
fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \
declaration\n", myname, u_lineno, input_file_name);
print_pos(u_line, u_cptr);
done(1);
}
over_unionized(u_cptr)
char *u_cptr;
{
fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \
declarations\n", myname, lineno, input_file_name);
print_pos(line, u_cptr);
done(1);
}
illegal_character(c_cptr)
char *c_cptr;
{
fprintf(stderr, "%s: e - line %d of \"%s\", illegal character\n",
myname, lineno, input_file_name);
print_pos(line, c_cptr);
done(1);
}
used_reserved(s)
char *s;
{
fprintf(stderr, "%s: e - line %d of \"%s\", illegal use of reserved
symbol \
%s\n", myname, lineno, input_file_name, s);
done(1);
}
tokenized_start(s)
404 A to Z of C
char *s;
{
fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s
cannot be \
declared to be a token\n", myname, lineno, input_file_name, s);
done(1);
}
retyped_warning(s)
char *s;
{
fprintf(stderr, "%s: w - line %d of \"%s\", the type of %s has been
\
redeclared\n", myname, lineno, input_file_name, s);
}
reprec_warning(s)
char *s;
{
fprintf(stderr, "%s: w - line %d of \"%s\", the precedence of %s has
been \
redeclared\n", myname, lineno, input_file_name, s);
}
revalued_warning(s)
char *s;
{
fprintf(stderr, "%s: w - line %d of \"%s\", the value of %s has been
\
redeclared\n", myname, lineno, input_file_name, s);
}
terminal_start(s)
char *s;
{
fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s is a
\
token\n", myname, lineno, input_file_name, s);
done(1);
}
restarted_warning()
{
fprintf(stderr, "%s: w - line %d of \"%s\", the start symbol has
been \
redeclared\n", myname, lineno, input_file_name);
}
A to Z of C 405
no_grammar()
{
fprintf(stderr, "%s: e - line %d of \"%s\", no grammar has been \
specified\n", myname, lineno, input_file_name);
done(1);
}
terminal_lhs(s_lineno)
int s_lineno;
{
fprintf(stderr, "%s: e - line %d of \"%s\", a token appears on the
lhs \
of a production\n", myname, s_lineno, input_file_name);
done(1);
}
prec_redeclared()
{
fprintf(stderr, "%s: w - line %d of \"%s\", conflicting %%prec \
specifiers\n", myname, lineno, input_file_name);
}
dollar_warning(a_lineno, i)
int a_lineno;
int i;
{
fprintf(stderr, "%s: w - line %d of \"%s\", $%d references beyond
the \
end of the current rule\n", myname, a_lineno, input_file_name, i);
}
char *a_cptr;
{
fprintf(stderr, "%s: e - line %d of \"%s\", illegal $-name\n",
myname, a_lineno, input_file_name);
print_pos(a_line, a_cptr);
done(1);
}
untyped_lhs()
{
fprintf(stderr, "%s: e - line %d of \"%s\", $$ is untyped\n",
myname, lineno, input_file_name);
done(1);
}
untyped_rhs(i, s)
int i;
char *s;
{
fprintf(stderr, "%s: e - line %d of \"%s\", $%d (%s) is untyped\n",
myname, lineno, input_file_name, i, s);
done(1);
}
unknown_rhs(i)
int i;
{
fprintf(stderr, "%s: e - line %d of \"%s\", $%d is untyped\n",
myname, lineno, input_file_name, i);
done(1);
}
default_action_warning()
{
fprintf(stderr, "%s: w - line %d of \"%s\", the default action
assigns an \
undefined value to $$\n", myname, lineno, input_file_name);
}
undefined_goal(s)
char *s;
{
fprintf(stderr, "%s: e - the start symbol %s is undefined\n",
myname, s);
done(1);
}
A to Z of C 407
undefined_symbol_warning(s)
char *s;
{
fprintf(stderr, "%s: w - the symbol %s is undefined\n", myname, s);
}
49.2.2.4 Lalr.c
#include "defs.h"
typedef
struct shorts
{
struct shorts *next;
short value;
}
shorts;
int tokensetsize;
short *lookaheads;
short *LAruleno;
unsigned *LA;
short *accessing_symbol;
core **state_table;
shifts **shift_table;
reductions **reduction_table;
short *goto_map;
short *from_state;
short *to_state;
short **transpose();
lalr()
{
tokensetsize = WORDSIZE(ntokens);
408 A to Z of C
set_state_table();
set_accessing_symbol();
set_shift_table();
set_reduction_table();
set_maxrhs();
initialize_LA();
set_goto_map();
initialize_F();
build_relations();
compute_FOLLOWS();
compute_lookaheads();
}
set_state_table()
{
register core *sp;
set_accessing_symbol()
{
register core *sp;
set_shift_table()
{
register shifts *sp;
set_reduction_table()
{
register reductions *rp;
reduction_table = NEW2(nstates, reductions *);
for (rp = first_reduction; rp; rp = rp->next)
reduction_table[rp->number] = rp;
}
A to Z of C 409
set_maxrhs()
{
register short *itemp;
register short *item_end;
register int length;
register int max;
length = 0;
max = 0;
item_end = ritem + nitems;
for (itemp = ritem; itemp < item_end; itemp++)
{
if (*itemp >= 0)
{
length++;
}
else
{
if (length > max) max = length;
length = 0;
}
}
maxrhs = max;
}
initialize_LA()
{
register int i, j, k;
register reductions *rp;
k = 0;
for (i = 0; i < nstates; i++)
{
lookaheads[i] = k;
rp = reduction_table[i];
if (rp)
k += rp->nreds;
}
lookaheads[nstates] = k;
k = 0;
for (i = 0; i < nstates; i++)
{
rp = reduction_table[i];
if (rp)
{
for (j = 0; j < rp->nreds; j++)
{
LAruleno[k] = rp->rules[j];
k++;
}
}
}
}
set_goto_map()
{
register shifts *sp;
register int i;
register int symbol;
register int k;
register short *temp_map;
register int state2;
register int state1;
ngotos = 0;
for (sp = first_shift; sp; sp = sp->next)
{
for (i = sp->nshifts - 1; i >= 0; i--)
{
symbol = accessing_symbol[sp->shift[i]];
if (ISTOKEN(symbol)) break;
if (ngotos == MAXSHORT)
fatal("too many gotos");
ngotos++;
goto_map[symbol]++;
}
}
k = 0;
A to Z of C 411
goto_map[nsyms] = ngotos;
temp_map[nsyms] = ngotos;
if (ISTOKEN(symbol)) break;
k = temp_map[symbol]++;
from_state[k] = state1;
to_state[k] = state2;
}
}
FREE(temp_map + ntokens);
}
low = goto_map[symbol];
412 A to Z of C
for (;;)
{
assert(low <= high);
middle = (low + high) >> 1;
s = from_state[middle];
if (s == state)
return (middle);
else if (s < state)
low = middle + 1;
else
high = middle - 1;
}
}
initialize_F()
{
register int i;
register int j;
register int k;
register shifts *sp;
register short *edge;
register unsigned *rowp;
register short *rp;
register short **reads;
register int nedges;
register int stateno;
register int symbol;
register int nwords;
rowp = F;
for (i = 0; i < ngotos; i++)
{
stateno = to_state[i];
sp = shift_table[stateno];
if (sp)
{
k = sp->nshifts;
A to Z of C 413
if (nedges)
{
reads[i] = rp = NEW2(nedges + 1, short);
rp[nedges] = -1;
nedges = 0;
}
}
rowp += tokensetsize;
}
SETBIT(F, 0);
digraph(reads);
FREE(reads);
FREE(edge);
}
build_relations()
{
register int i;
register int j;
414 A to Z of C
register int k;
register short *rulep;
register short *rp;
register shifts *sp;
register int length;
register int nedges;
register int done;
register int state1;
register int stateno;
register int symbol1;
register int symbol2;
register short *shortp;
register short *edge;
register short *states;
register short **new_includes;
states[length++] = stateno;
}
add_lookback_edge(stateno, *rulep, i);
A to Z of C 415
length--;
done = 0;
while (!done)
{
done = 1;
rp--;
if (ISVAR(*rp))
{
stateno = states[--length];
edge[nedges++] = map_goto(stateno, *rp);
if (nullable[*rp] && length > 0) done = 0;
}
}
}
if (nedges)
{
includes[i] = shortp = NEW2(nedges + 1, short);
for (j = 0; j < nedges; j++)
shortp[j] = edge[j];
shortp[nedges] = -1;
}
}
FREE(includes);
includes = new_includes;
FREE(edge);
FREE(states);
}
i = lookaheads[stateno];
k = lookaheads[stateno + 1];
416 A to Z of C
found = 0;
while (!found && i < k)
{
if (LAruleno[i] == ruleno)
found = 1;
else
++i;
}
assert(found);
sp = NEW(shorts);
sp->next = lookback[i];
sp->value = gotono;
lookback[i] = sp;
}
short **
transpose(R, n)
short **R;
int n;
{
register short **new_R;
register short **temp_R;
register short *nedges;
register short *sp;
register int i;
register int k;
sp = NEW2(k + 1, short);
new_R[i] = sp;
temp_R[i] = sp;
sp[k] = -1;
}
}
FREE(nedges);
return (new_R);
}
compute_FOLLOWS()
{
digraph(includes);
}
compute_lookaheads()
{
register int i, n;
register unsigned *fp1, *fp2, *fp3;
register shorts *sp, *next;
register unsigned *rowp;
rowp = LA;
n = lookaheads[nstates];
for (i = 0; i < n; i++)
{
fp3 = rowp + tokensetsize;
for (sp = lookback[i]; sp; sp = sp->next)
{
fp1 = rowp;
fp2 = F + tokensetsize * sp->value;
while (fp1 < fp3)
*fp1++ |= *fp2++;
}
418 A to Z of C
rowp = fp3;
}
FREE(lookback);
FREE(F);
}
digraph(relation)
short **relation;
{
register int i;
infinity = ngotos + 2;
INDEX = NEW2(ngotos + 1, short);
VERTICES = NEW2(ngotos + 1, short);
top = 0;
R = relation;
FREE(INDEX);
FREE(VERTICES);
}
traverse(i)
register int i;
{
register unsigned *fp1;
register unsigned *fp2;
register unsigned *fp3;
register int j;
register short *rp;
A to Z of C 419
int height;
unsigned *base;
VERTICES[++top] = i;
INDEX[i] = height = top;
base = F + i * tokensetsize;
fp3 = base + tokensetsize;
rp = R[i];
if (rp)
{
while ((j = *rp++) >= 0)
{
if (INDEX[j] == 0)
traverse(j);
if (INDEX[i] > INDEX[j])
INDEX[i] = INDEX[j];
fp1 = base;
fp2 = F + j * tokensetsize;
if (INDEX[i] == height)
{
for (;;)
{
j = VERTICES[top--];
INDEX[j] = infinity;
if (i == j)
break;
fp1 = base;
fp2 = F + j * tokensetsize;
int nstates;
core *first_state;
shifts *first_shift;
reductions *first_reduction;
int get_state();
core *new_state();
allocate_itemsets()
{
register short *itemp;
register short *item_end;
register int symbol;
register int i;
register int count;
register int max;
register short *symbol_count;
count = 0;
symbol_count = NEW2(nsyms, short);
symbol_count[symbol]++;
}
}
count = 0;
max = 0;
for (i = 0; i < nsyms; i++)
{
kernel_base[i] = kernel_items + count;
count += symbol_count[i];
if (max < symbol_count[i])
max = symbol_count[i];
}
shift_symbol = symbol_count;
kernel_end = NEW2(nsyms, short *);
}
allocate_storage()
{
allocate_itemsets();
append_states()
{
register int i;
register int j;
register int symbol;
#ifdef TRACE
fprintf(stderr, "Entering append_states\n");
#endif
j--;
}
shift_symbol[j] = symbol;
}
free_storage()
{
FREE(shift_symbol);
FREE(redset);
FREE(shiftset);
FREE(kernel_base);
FREE(kernel_end);
FREE(kernel_items);
FREE(state_set);
}
generate_states()
{
allocate_storage();
itemset = NEW2(nitems, short);
ruleset = NEW2(WORDSIZE(nrules), unsigned);
set_first_derives();
initialize_states();
while (this_state)
{
closure(this_state->items, this_state->nitems);
save_reductions();
new_itemsets();
append_states();
if (nshifts > 0)
save_shifts();
this_state = this_state->next;
}
finalize_closure();
free_storage();
}
A to Z of C 423
int
get_state(symbol)
int symbol;
{
register int key;
register short *isp1;
register short *isp2;
register short *iend;
register core *sp;
register int found;
int n;
#ifdef TRACE
fprintf(stderr, "Entering get_state, symbol = %d\n", symbol);
#endif
isp1 = kernel_base[symbol];
iend = kernel_end[symbol];
n = iend - isp1;
key = *isp1;
assert(0 <= key && key < nitems);
sp = state_set[key];
if (sp)
{
found = 0;
while (!found)
{
if (sp->nitems == n)
{
found = 1;
isp1 = kernel_base[symbol];
isp2 = sp->items;
if (!found)
{
if (sp->link)
{
sp = sp->link;
}
424 A to Z of C
else
{
sp = sp->link = new_state(symbol);
found = 1;
}
}
}
}
else
{
state_set[key] = sp = new_state(symbol);
}
return (sp->number);
}
initialize_states()
{
register int i;
register short *start_derives;
register core *p;
start_derives = derives[start_symbol];
for (i = 0; start_derives[i] >= 0; ++i)
continue;
p->next = 0;
p->link = 0;
p->number = 0;
p->accessing_symbol = 0;
p->nitems = i;
shiftcount = 0;
isp = itemset;
while (isp < itemsetend)
{
i = *isp++;
symbol = ritem[i];
if (symbol > 0)
{
ksp = kernel_end[symbol];
if (!ksp)
{
shift_symbol[shiftcount++] = symbol;
ksp = kernel_base[symbol];
}
*ksp++ = i + 1;
kernel_end[symbol] = ksp;
}
}
nshifts = shiftcount;
}
core *
new_state(symbol)
int symbol;
{
register int n;
register core *p;
register short *isp1;
register short *isp2;
register short *iend;
#ifdef TRACE
fprintf(stderr, "Entering new_state, symbol = %d\n", symbol);
#endif
if (nstates >= MAXSHORT)
fatal("too many states");
isp1 = kernel_base[symbol];
iend = kernel_end[symbol];
n = iend - isp1;
426 A to Z of C
isp2 = p->items;
while (isp1 < iend)
*isp2++ = *isp1++;
last_state->next = p;
last_state = p;
nstates++;
return (p);
}
show_cores()
{
core *p;
int i, j, k, n;
int itemno;
k = 0;
for (p = first_state; p; ++k, p = p->next)
{
if (k) printf("\n");
printf("state %d, number = %d, accessing symbol = %s\n",
k, p->number, symbol_name[p->accessing_symbol]);
n = p->nitems;
for (i = 0; i < n; ++i)
{
itemno = p->items[i];
printf("%4d ", itemno);
j = itemno;
while (ritem[j] >= 0) ++j;
printf("%s :", symbol_name[rlhs[-ritem[j]]]);
j = rrhs[-ritem[j]];
while (j < itemno)
printf(" %s", symbol_name[ritem[j++]]);
printf(" .");
while (ritem[j] >= 0)
printf(" %s", symbol_name[ritem[j++]]);
printf("\n");
A to Z of C 427
fflush(stdout);
}
}
}
show_ritems()
{
int i;
show_shifts()
{
shifts *p;
int i, j, k;
k = 0;
for (p = first_shift; p; ++k, p = p->next)
{
if (k) printf("\n");
printf("shift %d, number = %d, nshifts = %d\n", k, p->number,
p->nshifts);
j = p->nshifts;
for (i = 0; i < j; ++i)
printf("\t%d\n", p->shift[i]);
}
}
save_shifts()
{
register shifts *p;
register short *sp1;
register short *sp2;
register short *send;
428 A to Z of C
p->number = this_state->number;
p->nshifts = nshifts;
sp1 = shiftset;
sp2 = p->shift;
send = shiftset + nshifts;
if (last_shift)
{
last_shift->next = p;
last_shift = p;
}
else
{
first_shift = p;
last_shift = p;
}
}
save_reductions()
{
register short *isp;
register short *rp1;
register short *rp2;
register int item;
register int count;
register reductions *p;
short *rend;
count = 0;
for (isp = itemset; isp < itemsetend; isp++)
{
item = ritem[*isp];
if (item < 0)
{
redset[count++] = -item;
}
}
A to Z of C 429
if (count)
{
p = (reductions *) allocate((unsigned) (sizeof(reductions) +
(count - 1) * sizeof(short)));
p->number = this_state->number;
p->nreds = count;
rp1 = redset;
rp2 = p->rules;
rend = rp1 + count;
if (last_reduction)
{
last_reduction->next = p;
last_reduction = p;
}
else
{
first_reduction = p;
last_reduction = p;
}
}
}
set_derives()
{
register int i, k;
register int lhs;
register short *rules;
k = 0;
for (lhs = start_symbol; lhs < nsyms; lhs++)
{
derives[lhs] = rules + k;
for (i = 0; i < nrules; i++)
{
if (rlhs[i] == lhs)
{
rules[k] = i;
430 A to Z of C
k++;
}
}
rules[k] = -1;
k++;
}
#ifdef DEBUG
print_derives();
#endif
}
free_derives()
{
FREE(derives[start_symbol]);
FREE(derives);
}
#ifdef DEBUG
print_derives()
{
register int i;
register short *sp;
printf("\nDERIVES\n\n");
putchar('\n');
}
#endif
set_nullable()
{
register int i, j;
register int empty;
int done;
nullable = MALLOC(nsyms);
A to Z of C 431
if (nullable == 0) no_space();
done = 0;
while (!done)
{
done = 1;
for (i = 1; i < nitems; i++)
{
empty = 1;
while ((j = ritem[i]) >= 0)
{
if (!nullable[j])
empty = 0;
++i;
}
if (empty)
{
j = rlhs[-j];
if (!nullable[j])
{
nullable[j] = 1;
done = 0;
}
}
}
}
#ifdef DEBUG
for (i = 0; i < nsyms; i++)
{
if (nullable[i])
printf("%s is nullable\n", symbol_name[i]);
else
printf("%s is not nullable\n", symbol_name[i]);
}
#endif
}
free_nullable()
{
FREE(nullable);
}
lr0()
{
set_derives();
432 A to Z of C
set_nullable();
generate_states();
}
49.2.2.6 Mkpar.c
#include "defs.h"
action **parser;
int SRtotal;
int RRtotal;
short *SRconflicts;
short *RRconflicts;
short *defred;
short *rules_used;
short nunused;
short final_state;
static int SRcount;
static int RRcount;
make_parser()
{
register int i;
find_final_state();
remove_conflicts();
unused_rules();
if (SRtotal + RRtotal > 0) total_conflicts();
defreds();
}
action *
parse_actions(stateno)
register int stateno;
{
register action *actions;
actions = get_shifts(stateno);
actions = add_reductions(stateno, actions);
A to Z of C 433
return (actions);
}
action *
get_shifts(stateno)
int stateno;
{
register action *actions, *temp;
register shifts *sp;
register short *to_state;
register int i, k;
register int symbol;
actions = 0;
sp = shift_table[stateno];
if (sp)
{
to_state = sp->shift;
for (i = sp->nshifts - 1; i >= 0; i--)
{
k = to_state[i];
symbol = accessing_symbol[k];
if (ISTOKEN(symbol))
{
temp = NEW(action);
temp->next = actions;
temp->symbol = symbol;
temp->number = k;
temp->prec = symbol_prec[symbol];
temp->action_code = SHIFT;
temp->assoc = symbol_assoc[symbol];
actions = temp;
}
}
}
return (actions);
}
action *
add_reductions(stateno, actions)
int stateno;
register action *actions;
{
register int i, j, m, n;
register int ruleno, tokensetsize;
register unsigned *rowp;
tokensetsize = WORDSIZE(ntokens);
m = lookaheads[stateno];
434 A to Z of C
n = lookaheads[stateno + 1];
for (i = m; i < n; i++)
{
ruleno = LAruleno[i];
rowp = LA + i * tokensetsize;
for (j = ntokens - 1; j >= 0; j--)
{
if (BIT(rowp, j))
actions = add_reduce(actions, ruleno, j);
}
}
return (actions);
}
action *
add_reduce(actions, ruleno, symbol)
register action *actions;
register int ruleno, symbol;
{
register action *temp, *prev, *next;
prev = 0;
for (next = actions; next && next->symbol < symbol; next = next-
>next)
prev = next;
else
actions = temp;
return (actions);
}
find_final_state()
{
register int goal, i;
register short *to_state;
register shifts *p;
p = shift_table[0];
to_state = p->shift;
goal = ritem[1];
for (i = p->nshifts - 1; i >= 0; --i)
{
final_state = to_state[i];
if (accessing_symbol[final_state] == goal) break;
}
}
unused_rules()
{
register int i;
register action *p;
nunused = 0;
for (i = 3; i < nrules; ++i)
if (!rules_used[i]) ++nunused;
if (nunused)
436 A to Z of C
if (nunused == 1)
fprintf(stderr, "%s: 1 rule never reduced\n", myname);
else
fprintf(stderr, "%s: %d rules never reduced\n", myname,
nunused);
}
remove_conflicts()
{
register int i;
register int symbol;
register action *p, *q;
SRtotal = 0;
RRtotal = 0;
SRconflicts = NEW2(nstates, short);
RRconflicts = NEW2(nstates, short);
for (i = 0; i < nstates; i++)
{
SRcount = 0;
RRcount = 0;
for (p = parser[i]; p; p = q->next)
{
symbol = p->symbol;
q = p;
while (q->next && q->next->symbol == symbol)
q = q->next;
if (i == final_state && symbol == 0)
end_conflicts(p, q);
else if (p != q)
resolve_conflicts(p, q);
}
SRtotal += SRcount;
RRtotal += RRcount;
SRconflicts[i] = SRcount;
RRconflicts[i] = RRcount;
}
}
end_conflicts(p, q)
register action *p, *q;
{
for (;;)
{
SRcount++;
p->suppressed = 1;
if (p == q) break;
A to Z of C 437
p = p->next;
}
}
resolve_conflicts(first, last)
register action *first, *last;
{
register action *p;
register int count;
count = 1;
for (p = first; p != last; p = p->next)
++count;
assert(count > 1);
total_conflicts()
{
fprintf(stderr, "%s: ", myname);
if (SRtotal == 1)
fprintf(stderr, "1 shift/reduce conflict");
else if (SRtotal > 1)
fprintf(stderr, "%d shift/reduce conflicts", SRtotal);
if (RRtotal == 1)
fprintf(stderr, "1 reduce/reduce conflict");
else if (RRtotal > 1)
fprintf(stderr, "%d reduce/reduce conflicts", RRtotal);
fprintf(stderr, ".\n");
}
int
sole_reduction(stateno)
int stateno;
{
register int count, ruleno;
register action *p;
count = 0;
ruleno = 0;
for (p = parser[stateno]; p; p = p->next)
{
if (p->action_code == SHIFT && p->suppressed == 0)
return (0);
else if (p->action_code == REDUCE && p->suppressed == 0)
{
if (ruleno > 0 && p->number != ruleno)
return (0);
if (p->symbol != 1)
++count;
ruleno = p->number;
}
}
if (count == 0)
return (0);
return (ruleno);
}
A to Z of C 439
defreds()
{
register int i;
free_action_row(p)
register action *p;
{
register action *q;
while (p)
{
q = p->next;
FREE(p);
p = q;
}
}
free_parser()
{
register int i;
FREE(parser);
}
49.2.2.7 Output.c
#include "defs.h"
output()
{
free_itemsets();
free_shifts();
free_reductions();
output_stored_text();
output_defines();
output_rule_data();
output_yydefred();
output_actions();
free_parser();
output_debug();
output_stype();
if (rflag) write_section(tables);
write_section(header);
output_trailing_text();
write_section(body);
output_semantic_actions();
write_section(trailer);
}
output_rule_data()
{
register int i;
register int j;
j = 10;
for (i = 3; i < nrules; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
A to Z of C 441
j = 10;
for (i = 3; i < nrules; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
j++;
output_yydefred()
{
register int i, j;
j = 10;
for (i = 1; i < nstates; i++)
{
if (j < 10)
++j;
else
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
if (!rflag) outline += 2;
fprintf(output_file, "\n};\n");
}
output_actions()
{
nvectors = 2*nstates + nvars;
token_actions();
FREE(lookaheads);
FREE(LA);
FREE(LAruleno);
FREE(accessing_symbol);
goto_actions();
FREE(goto_map + ntokens);
FREE(from_state);
FREE(to_state);
sort_actions();
pack_table();
output_base();
output_table();
output_check();
}
token_actions()
{
register int i, j;
register int shiftcount, reducecount;
register int max, min;
register short *actionrow, *r, *s;
register action *p;
shiftcount = 0;
reducecount = 0;
for (p = parser[i]; p; p = p->next)
{
if (p->suppressed == 0)
{
if (p->action_code == SHIFT)
{
++shiftcount;
actionrow[p->symbol] = p->number;
}
else if (p->action_code == REDUCE && p->number !=
defred[i])
{
++reducecount;
actionrow[p->symbol + ntokens] = p->number;
}
}
}
tally[i] = shiftcount;
tally[nstates+i] = reducecount;
width[i] = 0;
width[nstates+i] = 0;
if (shiftcount > 0)
{
froms[i] = r = NEW2(shiftcount, short);
tos[i] = s = NEW2(shiftcount, short);
min = MAXSHORT;
max = 0;
for (j = 0; j < ntokens; ++j)
{
if (actionrow[j])
{
if (min > symbol_value[j])
min = symbol_value[j];
if (max < symbol_value[j])
max = symbol_value[j];
*r++ = symbol_value[j];
*s++ = actionrow[j];
}
}
width[i] = max - min + 1;
}
if (reducecount > 0)
{
froms[nstates+i] = r = NEW2(reducecount, short);
444 A to Z of C
goto_actions()
{
register int i, j, k;
k = default_goto(start_symbol + 1);
fprintf(output_file, "short yydgoto[] = {%40d,", k);
save_column(start_symbol + 1, k);
j = 10;
for (i = start_symbol + 2; i < nsyms; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
k = default_goto(i);
fprintf(output_file, "%5d,", k);
A to Z of C 445
save_column(i, k);
}
if (!rflag) outline += 2;
fprintf(output_file, "\n};\n");
FREE(state_count);
}
int
default_goto(symbol)
int symbol;
{
register int i;
register int m;
register int n;
register int default_state;
register int max;
m = goto_map[symbol];
n = goto_map[symbol + 1];
if (m == n) return (0);
max = 0;
default_state = 0;
register int m;
register int n;
register short *sp;
register short *sp1;
register short *sp2;
register int count;
register int symno;
m = goto_map[symbol];
n = goto_map[symbol + 1];
count = 0;
for (i = m; i < n; i++)
{
if (to_state[i] != default_state)
++count;
}
if (count == 0) return;
tally[symno] = count;
width[symno] = sp1[-1] - sp[0] + 1;
}
sort_actions()
{
register int i;
register int j;
register int k;
register int t;
register int w;
order[j + 1] = i;
nentries++;
}
}
}
pack_table()
{
register int i;
register int place;
register int state;
maxtable = 1000;
table = NEW2(maxtable, short);
check = NEW2(maxtable, short);
lowzero = 0;
high = 0;
if (state < 0)
place = pack_vector(i);
else
place = base[state];
pos[i] = place;
base[order[i]] = place;
}
FREE(froms);
FREE(tos);
FREE(pos);
}
int
matching_vector(vector)
int vector;
{
register int i;
register int j;
register int k;
register int t;
register int w;
A to Z of C 449
i = order[vector];
if (i >= 2*nstates)
return (-1);
t = tally[i];
w = width[i];
match = 1;
for (k = 0; match && k < t; k++)
{
if (tos[j][k] != tos[i][k] || froms[j][k] != froms[i][k])
match = 0;
}
if (match)
return (j);
}
return (-1);
}
int
pack_vector(vector)
int vector;
{
register int i, j, k, l;
register int t;
register int loc;
register int ok;
register short *from;
register short *to;
int newmax;
i = order[vector];
t = tally[i];
assert(t);
from = froms[i];
to = tos[i];
450 A to Z of C
j = lowzero - from[0];
for (k = 1; k < t; ++k)
if (lowzero - from[k] > j)
j = lowzero - from[k];
for (;; ++j)
{
if (j == 0)
continue;
ok = 1;
for (k = 0; ok && k < t; k++)
{
loc = j + from[k];
if (loc >= maxtable)
{
if (loc >= MAXTABLE)
fatal("maximum table size exceeded");
newmax = maxtable;
do { newmax += 200; } while (newmax <= loc);
table = (short *) REALLOC(table, newmax*sizeof(short));
if (table == 0) no_space();
check = (short *) REALLOC(check, newmax*sizeof(short));
if (check == 0) no_space();
for (l = maxtable; l < newmax; ++l)
{
table[l] = 0;
check[l] = -1;
}
maxtable = newmax;
}
if (check[loc] != -1)
ok = 0;
}
for (k = 0; ok && k < vector; k++)
{
if (pos[k] == j)
ok = 0;
}
if (ok)
{
for (k = 0; k < t; k++)
{
loc = j + from[k];
table[loc] = to[k];
check[loc] = from[k];
A to Z of C 451
return (j);
}
}
}
output_base()
{
register int i, j;
j = 10;
for (i = 1; i < nstates; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
if (!rflag) outline += 2;
fprintf(output_file, "\n};\nshort yyrindex[] = {%39d,",
base[nstates]);
j = 10;
for (i = nstates + 1; i < 2*nstates; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
452 A to Z of C
if (!rflag) outline += 2;
fprintf(output_file, "\n};\nshort yygindex[] = {%39d,",
base[2*nstates]);
j = 10;
for (i = 2*nstates + 1; i < nvectors - 1; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
if (!rflag) outline += 2;
fprintf(output_file, "\n};\n");
FREE(base);
}
output_table()
{
register int i;
register int j;
++outline;
fprintf(code_file, "#define YYTABLESIZE %d\n", high);
fprintf(output_file, "short yytable[] = {%40d,", table[0]);
j = 10;
for (i = 1; i <= high; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
A to Z of C 453
if (!rflag) outline += 2;
fprintf(output_file, "\n};\n");
FREE(table);
}
output_check()
{
register int i;
register int j;
j = 10;
for (i = 1; i <= high; i++)
{
if (j >= 10)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 1;
}
else
++j;
if (!rflag) outline += 2;
fprintf(output_file, "\n};\n");
FREE(check);
}
int
is_C_identifier(name)
char *name;
{
register char *s;
register int c;
s = name;
c = *s;
if (c == '"')
{
c = *++s;
454 A to Z of C
output_defines()
{
register int c, i;
register char *s;
if (is_C_identifier(s))
{
fprintf(code_file, "#define ");
if (dflag) fprintf(defines_file, "#define ");
c = *s;
if (c == '"')
{
while ((c = *++s) != '"')
{
putc(c, code_file);
if (dflag) putc(c, defines_file);
}
}
else
{
do
{
putc(c, code_file);
A to Z of C 455
++outline;
fprintf(code_file, "#define YYERRCODE %d\n", symbol_value[1]);
output_stored_text()
{
register int c;
register FILE *in, *out;
fclose(text_file);
text_file = fopen(text_file_name, "r");
if (text_file == NULL)
open_error(text_file_name);
in = text_file;
if ((c = getc(in)) == EOF)
return;
out = code_file;
if (c == '\n')
++outline;
putc(c, out);
while ((c = getc(in)) != EOF)
{
if (c == '\n')
++outline;
putc(c, out);
}
if (!lflag)
456 A to Z of C
output_debug()
{
register int i, j, k, max;
char **symnam, *s;
++outline;
fprintf(code_file, "#define YYFINAL %d\n", final_state);
outline += 3;
fprintf(code_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n",
tflag);
if (rflag)
fprintf(output_file, "#ifndef YYDEBUG\n#define YYDEBUG
%d\n#endif\n",
tflag);
max = 0;
for (i = 2; i < ntokens; ++i)
if (symbol_value[i] > max)
max = symbol_value[i];
++outline;
fprintf(code_file, "#define YYMAXTOKEN %d\n", max);
if (!rflag) ++outline;
fprintf(output_file, "#if YYDEBUG\nchar *yyname[] = {");
j = 80;
for (i = 0; i <= max; ++i)
{
if (s = symnam[i])
{
if (s[0] == '"')
{
k = 7;
A to Z of C 457
else
{
k = 5;
while (*++s != '\'')
{
++k;
if (*s == '\\')
{
k += 2;
if (*++s == '\\')
++k;
}
}
j += k;
if (j > 80)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = k;
}
fprintf(output_file, "\"'");
s = symnam[i];
while (*++s != '\'')
{
if (*s == '\\')
{
fprintf(output_file, "\\\\");
if (*++s == '\\')
fprintf(output_file, "\\\\");
else
putc(*s, output_file);
}
else
putc(*s, output_file);
}
fprintf(output_file, "'\",");
}
}
else
{
k = strlen(s) + 3;
j += k;
if (j > 80)
{
if (!rflag) ++outline;
putc('\n', output_file);
A to Z of C 459
j = k;
}
putc('"', output_file);
do { putc(*s, output_file); } while (*++s);
fprintf(output_file, "\",");
}
}
else
{
j += 2;
if (j > 80)
{
if (!rflag) ++outline;
putc('\n', output_file);
j = 2;
}
fprintf(output_file, "0,");
}
}
if (!rflag) outline += 2;
fprintf(output_file, "\n};\n");
FREE(symnam);
if (!rflag) ++outline;
fprintf(output_file, "char *yyrule[] = {\n");
for (i = 2; i < nrules; ++i)
{
fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]);
for (j = rrhs[i]; ritem[j] > 0; ++j)
{
s = symbol_name[ritem[j]];
if (s[0] == '"')
{
fprintf(output_file, " \\\"");
while (*++s != '"')
{
if (*s == '\\')
{
if (s[1] == '\\')
fprintf(output_file, "\\\\\\\\");
else
fprintf(output_file, "\\\\%c", s[1]);
++s;
}
else
putc(*s, output_file);
}
460 A to Z of C
fprintf(output_file, "\\\"");
}
else if (s[0] == '\'')
{
if (s[1] == '"')
fprintf(output_file, " '\\\"'");
else if (s[1] == '\\')
{
if (s[2] == '\\')
fprintf(output_file, " '\\\\\\\\");
else
fprintf(output_file, " '\\\\%c", s[2]);
s += 2;
while (*++s != '\'')
putc(*s, output_file);
putc('\'', output_file);
}
else
fprintf(output_file, " '%c'", s[1]);
}
else
fprintf(output_file, " %s", s);
}
if (!rflag) ++outline;
fprintf(output_file, "\",\n");
}
if (!rflag) outline += 2;
fprintf(output_file, "};\n#endif\n");
}
output_stype()
{
if (!unionized && ntags == 0)
{
outline += 3;
fprintf(code_file, "#ifndef YYSTYPE\ntypedef int
YYSTYPE;\n#endif\n");
}
}
output_trailing_text()
{
register int c, last;
register FILE *in, *out;
if (line == 0)
return;
A to Z of C 461
in = input_file;
out = code_file;
c = *cptr;
if (c == '\n')
{
++lineno;
if ((c = getc(in)) == EOF)
return;
if (!lflag)
{
++outline;
fprintf(out, line_format, lineno, input_file_name);
}
if (c == '\n')
++outline;
putc(c, out);
last = c;
}
else
{
if (!lflag)
{
++outline;
fprintf(out, line_format, lineno, input_file_name);
}
do { putc(c, out); } while ((c = *++cptr) != '\n');
++outline;
putc('\n', out);
last = '\n';
}
while ((c = getc(in)) != EOF)
{
if (c == '\n')
++outline;
putc(c, out);
last = c;
}
if (last != '\n')
{
++outline;
putc('\n', out);
}
if (!lflag)
fprintf(out, line_format, ++outline + 1, code_file_name);
}
462 A to Z of C
output_semantic_actions()
{
register int c, last;
register FILE *out;
fclose(action_file);
action_file = fopen(action_file_name, "r");
if (action_file == NULL)
open_error(action_file_name);
out = code_file;
last = c;
if (c == '\n')
++outline;
putc(c, out);
while ((c = getc(action_file)) != EOF)
{
if (c == '\n')
++outline;
putc(c, out);
last = c;
}
if (last != '\n')
{
++outline;
putc('\n', out);
}
if (!lflag)
fprintf(out, line_format, ++outline + 1, code_file_name);
}
free_itemsets()
{
register core *cp, *next;
FREE(state_table);
for (cp = first_state; cp; cp = next)
{
next = cp->next;
FREE(cp);
}
}
A to Z of C 463
free_shifts()
{
register shifts *sp, *next;
FREE(shift_table);
for (sp = first_shift; sp; sp = next)
{
next = sp->next;
FREE(sp);
}
}
free_reductions()
{
register reductions *rp, *next;
FREE(reduction_table);
for (rp = first_reduction; rp; rp = next)
{
next = rp->next;
FREE(rp);
}
}
49.2.2.8 Reader.c
#include "defs.h"
/* The line size must be a positive integer. One hundred was chosen */
/* because few lines in Yacc input grammars exceed 100 characters. */
/* Note that if a line exceeds LINESIZE characters, the line buffer */
/* will be expanded to accomodate it. */
char *cache;
int cinc, cache_size;
bucket *goal;
int prec;
464 A to Z of C
int gensym;
char last_was_action;
int maxitems;
bucket **pitem;
int maxrules;
bucket **plhs;
int name_pool_size;
char *name_pool;
cachec(c)
int c;
{
assert(cinc >= 0);
if (cinc >= cache_size)
{
cache_size += 256;
cache = REALLOC(cache, cache_size);
if (cache == 0) no_space();
}
cache[cinc] = c;
++cinc;
}
get_line()
{
register FILE *f = input_file;
register int c;
register int i;
if (line == 0) no_space();
}
i = 0;
++lineno;
for (;;)
{
line[i] = c;
if (c == '\n') { cptr = line; return; }
if (++i >= linesize)
{
linesize += LINESIZE;
line = REALLOC(line, linesize);
if (line == 0) no_space();
}
c = getc(f);
if (c == EOF)
{
line[i] = '\n';
saw_eof = 1;
cptr = line;
return;
}
}
}
char *
dup_line()
{
register char *p, *s, *t;
s = line;
t = p;
while ((*t++ = *s++) != '\n') continue;
return (p);
}
skip_comment()
{
register char *s;
466 A to Z of C
s = cptr + 2;
for (;;)
{
if (*s == '*' && s[1] == '/')
{
cptr = s + 2;
FREE(st_line);
return;
}
if (*s == '\n')
{
get_line();
if (line == 0)
unterminated_comment(st_lineno, st_line, st_cptr);
s = cptr;
}
else
++s;
}
}
int
nextc()
{
register char *s;
if (line == 0)
{
get_line();
if (line == 0)
return (EOF);
}
s = cptr;
for (;;)
{
switch (*s)
{
case '\n':
get_line();
if (line == 0) return (EOF);
s = cptr;
break;
case '\t':
case '\f':
case '\r':
case '\v':
case ',':
case ';':
++s;
break;
case '\\':
cptr = s;
return ('%');
case '/':
if (s[1] == '*')
{
cptr = s;
skip_comment();
s = cptr;
break;
}
else if (s[1] == '/')
{
get_line();
if (line == 0) return (EOF);
s = cptr;
break;
}
/* fall through */
default:
cptr = s;
return (*s);
}
}
}
int
keyword()
{
register int c;
char *t_cptr = cptr;
c = *++cptr;
if (isalpha(c))
{
cinc = 0;
468 A to Z of C
for (;;)
{
if (isalpha(c))
{
if (isupper(c)) c = tolower(c);
cachec(c);
}
else if (isdigit(c) || c == '_' || c == '.' || c == '$')
cachec(c);
else
break;
c = *++cptr;
}
cachec(NUL);
if (strcmp(cache, "ident") == 0)
return (IDENT);
}
else
{
++cptr;
if (c == '{')
return (TEXT);
if (c == '%' || c == '\\')
return (MARK);
if (c == '<')
return (LEFT);
if (c == '>')
return (RIGHT);
if (c == '0')
A to Z of C 469
return (TOKEN);
if (c == '2')
return (NONASSOC);
}
syntax_error(lineno, line, t_cptr);
/*NOTREACHED*/
}
copy_ident()
{
register int c;
register FILE *f = output_file;
c = nextc();
if (c == EOF) unexpected_EOF();
if (c != '"') syntax_error(lineno, line, cptr);
++outline;
fprintf(f, "#ident \"");
for (;;)
{
c = *++cptr;
if (c == '\n')
{
fprintf(f, "\"\n");
return;
}
putc(c, f);
if (c == '"')
{
putc('\n', f);
++cptr;
return;
}
}
}
copy_text()
{
register int c;
int quote;
register FILE *f = text_file;
int need_newline = 0;
int t_lineno = lineno;
char *t_line = dup_line();
char *t_cptr = t_line + (cptr - line - 2);
470 A to Z of C
if (*cptr == '\n')
{
get_line();
if (line == 0)
unterminated_text(t_lineno, t_line, t_cptr);
}
if (!lflag) fprintf(f, line_format, lineno, input_file_name);
loop:
c = *cptr++;
switch (c)
{
case '\n':
next_line:
putc('\n', f);
need_newline = 0;
get_line();
if (line) goto loop;
unterminated_text(t_lineno, t_line, t_cptr);
case '\'':
case '"':
{
int s_lineno = lineno;
char *s_line = dup_line();
char *s_cptr = s_line + (cptr - line - 1);
quote = c;
putc(c, f);
for (;;)
{
c = *cptr++;
putc(c, f);
if (c == quote)
{
need_newline = 1;
FREE(s_line);
goto loop;
}
if (c == '\n')
unterminated_string(s_lineno, s_line, s_cptr);
if (c == '\\')
{
c = *cptr++;
putc(c, f);
if (c == '\n')
{
A to Z of C 471
get_line();
if (line == 0)
unterminated_string(s_lineno, s_line,
s_cptr);
}
}
}
}
case '/':
putc(c, f);
need_newline = 1;
c = *cptr;
if (c == '/')
{
putc('*', f);
while ((c = *++cptr) != '\n')
{
if (c == '*' && cptr[1] == '/')
fprintf(f, "* ");
else
putc(c, f);
}
fprintf(f, "*/");
goto next_line;
}
if (c == '*')
{
int c_lineno = lineno;
char *c_line = dup_line();
char *c_cptr = c_line + (cptr - line - 1);
putc('*', f);
++cptr;
for (;;)
{
c = *cptr++;
putc(c, f);
if (c == '*' && *cptr == '/')
{
putc('/', f);
++cptr;
FREE(c_line);
goto loop;
}
if (c == '\n')
{
472 A to Z of C
get_line();
if (line == 0)
unterminated_comment(c_lineno, c_line, c_cptr);
}
}
}
need_newline = 1;
goto loop;
case '%':
case '\\':
if (*cptr == '}')
{
if (need_newline) putc('\n', f);
++cptr;
FREE(t_line);
return;
}
/* fall through */
default:
putc(c, f);
need_newline = 1;
goto loop;
}
}
copy_union()
{
register int c;
int quote;
int depth;
int u_lineno = lineno;
char *u_line = dup_line();
char *u_cptr = u_line + (cptr - line - 6);
if (!lflag)
fprintf(text_file, line_format, lineno, input_file_name);
depth = 0;
loop:
A to Z of C 473
c = *cptr++;
putc(c, text_file);
if (dflag) putc(c, union_file);
switch (c)
{
case '\n':
next_line:
get_line();
if (line == 0) unterminated_union(u_lineno, u_line, u_cptr);
goto loop;
case '{':
++depth;
goto loop;
case '}':
if (--depth == 0)
{
fprintf(text_file, " YYSTYPE;\n");
FREE(u_line);
return;
}
goto loop;
case '\'':
case '"':
{
int s_lineno = lineno;
char *s_line = dup_line();
char *s_cptr = s_line + (cptr - line - 1);
quote = c;
for (;;)
{
c = *cptr++;
putc(c, text_file);
if (dflag) putc(c, union_file);
if (c == quote)
{
FREE(s_line);
goto loop;
}
if (c == '\n')
unterminated_string(s_lineno, s_line, s_cptr);
if (c == '\\')
{
c = *cptr++;
474 A to Z of C
putc(c, text_file);
if (dflag) putc(c, union_file);
if (c == '\n')
{
get_line();
if (line == 0)
unterminated_string(s_lineno, s_line,
s_cptr);
}
}
}
}
case '/':
c = *cptr;
if (c == '/')
{
putc('*', text_file);
if (dflag) putc('*', union_file);
while ((c = *++cptr) != '\n')
{
if (c == '*' && cptr[1] == '/')
{
fprintf(text_file, "* ");
if (dflag) fprintf(union_file, "* ");
}
else
{
putc(c, text_file);
if (dflag) putc(c, union_file);
}
}
fprintf(text_file, "*/\n");
if (dflag) fprintf(union_file, "*/\n");
goto next_line;
}
if (c == '*')
{
int c_lineno = lineno;
char *c_line = dup_line();
char *c_cptr = c_line + (cptr - line - 1);
putc('*', text_file);
if (dflag) putc('*', union_file);
++cptr;
for (;;)
{
A to Z of C 475
c = *cptr++;
putc(c, text_file);
if (dflag) putc(c, union_file);
if (c == '*' && *cptr == '/')
{
putc('/', text_file);
if (dflag) putc('/', union_file);
++cptr;
FREE(c_line);
goto loop;
}
if (c == '\n')
{
get_line();
if (line == 0)
unterminated_comment(c_lineno, c_line, c_cptr);
}
}
}
goto loop;
default:
goto loop;
}
}
int
hexval(c)
int c;
{
if (c >= '0' && c <= '9')
return (c - '0');
if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
return (-1);
}
bucket *
get_literal()
{
register int c, quote;
register int i;
register int n;
register char *s;
register bucket *bp;
476 A to Z of C
quote = *cptr++;
cinc = 0;
for (;;)
{
c = *cptr++;
if (c == quote) break;
if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr);
if (c == '\\')
{
char *c_cptr = cptr - 1;
c = *cptr++;
switch (c)
{
case '\n':
get_line();
if (line == 0) unterminated_string(s_lineno, s_line,
s_cptr);
continue;
case 'x':
c = *cptr++;
n = hexval(c);
if (n < 0 || n >= 16)
illegal_character(c_cptr);
A to Z of C 477
for (;;)
{
c = *cptr;
i = hexval(c);
if (i < 0 || i >= 16) break;
++cptr;
n = (n << 4) + i;
if (n > MAXCHAR) illegal_character(c_cptr);
}
c = n;
break;
n = cinc;
s = MALLOC(n);
if (s == 0) no_space();
cinc = 0;
if (n == 1)
cachec('\'');
else
cachec('"');
cachec(c);
else
{
cachec('\\');
switch (c)
{
case 7: cachec('a'); break;
case '\b': cachec('b'); break;
case '\f': cachec('f'); break;
case '\n': cachec('n'); break;
case '\r': cachec('r'); break;
case '\t': cachec('t'); break;
case '\v': cachec('v'); break;
default:
cachec(((c >> 6) & 7) + '0');
cachec(((c >> 3) & 7) + '0');
cachec((c & 7) + '0');
break;
}
}
}
if (n == 1)
cachec('\'');
else
cachec('"');
cachec(NUL);
bp = lookup(cache);
bp->class = TERM;
if (n == 1 && bp->value == UNDEFINED)
bp->value = *(unsigned char *)s;
FREE(s);
return (bp);
}
int
is_reserved(name)
char *name;
{
char *s;
if (strcmp(name, ".") == 0 ||
strcmp(name, "$accept") == 0 ||
strcmp(name, "$end") == 0)
return (1);
A to Z of C 479
return (0);
}
bucket *
get_name()
{
register int c;
cinc = 0;
for (c = *cptr; IS_IDENT(c); c = *++cptr)
cachec(c);
cachec(NUL);
if (is_reserved(cache)) used_reserved(cache);
return (lookup(cache));
}
int
get_number()
{
register int c;
register int n;
n = 0;
for (c = *cptr; isdigit(c); c = *++cptr)
n = 10*n + (c - '0');
return (n);
}
char *
get_tag()
{
register int c;
register int i;
register char *s;
int t_lineno = lineno;
char *t_line = dup_line();
char *t_cptr = t_line + (cptr - line);
480 A to Z of C
++cptr;
c = nextc();
if (c == EOF) unexpected_EOF();
if (!isalpha(c) && c != '_' && c != '$')
illegal_tag(t_lineno, t_line, t_cptr);
cinc = 0;
do { cachec(c); c = *++cptr; } while (IS_IDENT(c));
cachec(NUL);
c = nextc();
if (c == EOF) unexpected_EOF();
if (c != '>')
illegal_tag(t_lineno, t_line, t_cptr);
++cptr;
s = MALLOC(cinc);
if (s == 0) no_space();
strcpy(s, cache);
tag_table[ntags] = s;
++ntags;
FREE(t_line);
return (s);
}
declare_tokens(assoc)
int assoc;
{
register int c;
register bucket *bp;
int value;
char *tag = 0;
A to Z of C 481
c = nextc();
if (c == EOF) unexpected_EOF();
if (c == '<')
{
tag = get_tag();
c = nextc();
if (c == EOF) unexpected_EOF();
}
for (;;)
{
if (isalpha(c) || c == '_' || c == '.' || c == '$')
bp = get_name();
else if (c == '\'' || c == '"')
bp = get_literal();
else
return;
if (tag)
{
if (bp->tag && tag != bp->tag)
retyped_warning(bp->name);
bp->tag = tag;
}
if (assoc != TOKEN)
{
if (bp->prec && prec != bp->prec)
reprec_warning(bp->name);
bp->assoc = assoc;
bp->prec = prec;
}
c = nextc();
if (c == EOF) unexpected_EOF();
value = UNDEFINED;
if (isdigit(c))
{
value = get_number();
if (bp->value != UNDEFINED && value != bp->value)
revalued_warning(bp->name);
bp->value = value;
c = nextc();
482 A to Z of C
if (c == EOF) unexpected_EOF();
}
}
}
declare_types()
{
register int c;
register bucket *bp;
char *tag;
c = nextc();
if (c == EOF) unexpected_EOF();
if (c != '<') syntax_error(lineno, line, cptr);
tag = get_tag();
for (;;)
{
c = nextc();
if (isalpha(c) || c == '_' || c == '.' || c == '$')
bp = get_name();
else if (c == '\'' || c == '"')
bp = get_literal();
else
return;
bp->tag = tag;
}
}
declare_start()
{
register int c;
register bucket *bp;
c = nextc();
if (c == EOF) unexpected_EOF();
if (!isalpha(c) && c != '_' && c != '.' && c != '$')
syntax_error(lineno, line, cptr);
bp = get_name();
if (bp->class == TERM)
terminal_start(bp->name);
if (goal && goal != bp)
restarted_warning();
A to Z of C 483
goal = bp;
}
read_declarations()
{
register int c, k;
cache_size = 256;
cache = MALLOC(cache_size);
if (cache == 0) no_space();
for (;;)
{
c = nextc();
if (c == EOF) unexpected_EOF();
if (c != '%') syntax_error(lineno, line, cptr);
switch (k = keyword())
{
case MARK:
return;
case IDENT:
copy_ident();
break;
case TEXT:
copy_text();
break;
case UNION:
copy_union();
break;
case TOKEN:
case LEFT:
case RIGHT:
case NONASSOC:
declare_tokens(k);
break;
case TYPE:
declare_types();
break;
case START:
declare_start();
484 A to Z of C
break;
}
}
}
initialize_grammar()
{
nitems = 4;
maxitems = 300;
pitem = (bucket **) MALLOC(maxitems*sizeof(bucket *));
if (pitem == 0) no_space();
pitem[0] = 0;
pitem[1] = 0;
pitem[2] = 0;
pitem[3] = 0;
nrules = 3;
maxrules = 100;
plhs = (bucket **) MALLOC(maxrules*sizeof(bucket *));
if (plhs == 0) no_space();
plhs[0] = 0;
plhs[1] = 0;
plhs[2] = 0;
rprec = (short *) MALLOC(maxrules*sizeof(short));
if (rprec == 0) no_space();
rprec[0] = 0;
rprec[1] = 0;
rprec[2] = 0;
rassoc = (char *) MALLOC(maxrules*sizeof(char));
if (rassoc == 0) no_space();
rassoc[0] = TOKEN;
rassoc[1] = TOKEN;
rassoc[2] = TOKEN;
}
expand_items()
{
maxitems += 300;
pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *));
if (pitem == 0) no_space();
}
expand_rules()
{
maxrules += 100;
plhs = (bucket **) REALLOC(plhs, maxrules*sizeof(bucket *));
if (plhs == 0) no_space();
A to Z of C 485
advance_to_start()
{
register int c;
register bucket *bp;
char *s_cptr;
int s_lineno;
for (;;)
{
c = nextc();
if (c != '%') break;
s_cptr = cptr;
switch (keyword())
{
case MARK:
no_grammar();
case TEXT:
copy_text();
break;
case START:
declare_start();
break;
default:
syntax_error(lineno, line, s_cptr);
}
}
c = nextc();
if (!isalpha(c) && c != '_' && c != '.' && c != '_')
syntax_error(lineno, line, cptr);
bp = get_name();
if (goal == 0)
{
if (bp->class == TERM)
terminal_start(bp->name);
goal = bp;
}
s_lineno = lineno;
486 A to Z of C
c = nextc();
if (c == EOF) unexpected_EOF();
if (c != ':') syntax_error(lineno, line, cptr);
start_rule(bp, s_lineno);
++cptr;
}
start_rule(bp, s_lineno)
register bucket *bp;
int s_lineno;
{
if (bp->class == TERM)
terminal_lhs(s_lineno);
bp->class = NONTERM;
if (nrules >= maxrules)
expand_rules();
plhs[nrules] = bp;
rprec[nrules] = UNDEFINED;
rassoc[nrules] = TOKEN;
}
end_rule()
{
register int i;
if (!last_was_action && plhs[nrules]->tag)
{
for (i = nitems - 1; pitem[i]; --i) continue;
if (pitem[i+1] == 0 || pitem[i+1]->tag != plhs[nrules]->tag)
default_action_warning();
}
last_was_action = 0;
if (nitems >= maxitems) expand_items();
pitem[nitems] = 0;
++nitems;
++nrules;
}
insert_empty_rule()
{
register bucket *bp, **bpp;
assert(cache);
sprintf(cache, "$$%d", ++gensym);
bp = make_bucket(cache);
last_symbol->next = bp;
last_symbol = bp;
A to Z of C 487
bp->tag = plhs[nrules]->tag;
bp->class = NONTERM;
add_symbol()
{
register int c;
register bucket *bp;
int s_lineno = lineno;
c = *cptr;
if (c == '\'' || c == '"')
bp = get_literal();
else
bp = get_name();
c = nextc();
if (c == ':')
{
end_rule();
start_rule(bp, s_lineno);
++cptr;
return;
}
if (last_was_action)
insert_empty_rule();
last_was_action = 0;
copy_action()
{
register int c;
register int i, n;
int depth;
int quote;
char *tag;
register FILE *f = action_file;
int a_lineno = lineno;
char *a_line = dup_line();
char *a_cptr = a_line + (cptr - line);
if (last_was_action)
insert_empty_rule();
last_was_action = 1;
n = 0;
for (i = nitems - 1; pitem[i]; --i) ++n;
depth = 0;
loop:
c = *cptr;
if (c == '$')
{
if (cptr[1] == '<')
{
int d_lineno = lineno;
char *d_line = dup_line();
char *d_cptr = d_line + (cptr - line);
++cptr;
tag = get_tag();
c = *cptr;
if (c == '$')
{
fprintf(f, "yyval.%s", tag);
++cptr;
FREE(d_line);
goto loop;
}
A to Z of C 489
else if (isdigit(c))
{
i = get_number();
if (i > n) dollar_warning(d_lineno, i);
fprintf(f, "yyvsp[%d].%s", i - n, tag);
FREE(d_line);
goto loop;
}
else if (c == '-' && isdigit(cptr[1]))
{
++cptr;
i = -get_number() - n;
fprintf(f, "yyvsp[%d].%s", i, tag);
FREE(d_line);
goto loop;
}
else
dollar_error(d_lineno, d_line, d_cptr);
}
else if (cptr[1] == '$')
{
if (ntags)
{
tag = plhs[nrules]->tag;
if (tag == 0) untyped_lhs();
fprintf(f, "yyval.%s", tag);
}
else
fprintf(f, "yyval");
cptr += 2;
goto loop;
}
else if (isdigit(cptr[1]))
{
++cptr;
i = get_number();
if (ntags)
{
if (i <= 0 || i > n)
unknown_rhs(i);
tag = pitem[nitems + i - n - 1]->tag;
if (tag == 0) untyped_rhs(i, pitem[nitems + i - n - 1]-
>name);
fprintf(f, "yyvsp[%d].%s", i - n, tag);
}
490 A to Z of C
else
{
if (i > n)
dollar_warning(lineno, i);
fprintf(f, "yyvsp[%d]", i - n);
}
goto loop;
}
else if (cptr[1] == '-')
{
cptr += 2;
i = get_number();
if (ntags)
unknown_rhs(-i);
fprintf(f, "yyvsp[%d]", -i - n);
goto loop;
}
}
if (isalpha(c) || c == '_' || c == '$')
{
do
{
putc(c, f);
c = *++cptr;
} while (isalnum(c) || c == '_' || c == '$');
goto loop;
}
putc(c, f);
++cptr;
switch (c)
{
case '\n':
next_line:
get_line();
if (line) goto loop;
unterminated_action(a_lineno, a_line, a_cptr);
case ';':
if (depth > 0) goto loop;
fprintf(f, "\nbreak;\n");
return;
case '{':
++depth;
goto loop;
A to Z of C 491
case '}':
if (--depth > 0) goto loop;
fprintf(f, "\nbreak;\n");
return;
case '\'':
case '"':
{
int s_lineno = lineno;
char *s_line = dup_line();
char *s_cptr = s_line + (cptr - line - 1);
quote = c;
for (;;)
{
c = *cptr++;
putc(c, f);
if (c == quote)
{
FREE(s_line);
goto loop;
}
if (c == '\n')
unterminated_string(s_lineno, s_line, s_cptr);
if (c == '\\')
{
c = *cptr++;
putc(c, f);
if (c == '\n')
{
get_line();
if (line == 0)
unterminated_string(s_lineno, s_line,
s_cptr);
}
}
}
}
case '/':
c = *cptr;
if (c == '/')
{
putc('*', f);
while ((c = *++cptr) != '\n')
{
if (c == '*' && cptr[1] == '/')
fprintf(f, "* ");
492 A to Z of C
else
putc(c, f);
}
fprintf(f, "*/\n");
goto next_line;
}
if (c == '*')
{
int c_lineno = lineno;
char *c_line = dup_line();
char *c_cptr = c_line + (cptr - line - 1);
putc('*', f);
++cptr;
for (;;)
{
c = *cptr++;
putc(c, f);
if (c == '*' && *cptr == '/')
{
putc('/', f);
++cptr;
FREE(c_line);
goto loop;
}
if (c == '\n')
{
get_line();
if (line == 0)
unterminated_comment(c_lineno, c_line, c_cptr);
}
}
}
goto loop;
default:
goto loop;
}
}
int
mark_symbol()
{
register int c;
register bucket *bp;
A to Z of C 493
c = cptr[1];
if (c == '%' || c == '\\')
{
cptr += 2;
return (1);
}
if (c == '=')
cptr += 2;
else if ((c == 'p' || c == 'P') &&
((c = cptr[2]) == 'r' || c == 'R') &&
((c = cptr[3]) == 'e' || c == 'E') &&
((c = cptr[4]) == 'c' || c == 'C') &&
((c = cptr[5], !IS_IDENT(c))))
cptr += 5;
else
syntax_error(lineno, line, cptr);
c = nextc();
if (isalpha(c) || c == '_' || c == '.' || c == '$')
bp = get_name();
else if (c == '\'' || c == '"')
bp = get_literal();
else
{
syntax_error(lineno, line, cptr);
/*NOTREACHED*/
}
rprec[nrules] = bp->prec;
rassoc[nrules] = bp->assoc;
return (0);
}
read_grammar()
{
register int c;
initialize_grammar();
advance_to_start();
for (;;)
{
c = nextc();
494 A to Z of C
if (c == EOF) break;
if (isalpha(c) || c == '_' || c == '.' || c == '$' || c == '\''
|| c == '"')
add_symbol();
else if (c == '{' || c == '=')
copy_action();
else if (c == '|')
{
end_rule();
start_rule(plhs[nrules-1], 0);
++cptr;
}
else if (c == '%')
{
if (mark_symbol()) break;
}
else
syntax_error(lineno, line, cptr);
}
end_rule();
}
free_tags()
{
register int i;
if (tag_table == 0) return;
pack_names()
{
register bucket *bp;
register char *p, *s, *t;
strcpy(name_pool, "$accept");
strcpy(name_pool+8, "$end");
t = name_pool + 13;
for (bp = first_symbol; bp; bp = bp->next)
{
p = t;
s = bp->name;
while (*t++ = *s++) continue;
FREE(bp->name);
bp->name = p;
}
}
check_symbols()
{
register bucket *bp;
if (goal->class == UNKNOWN)
undefined_goal(goal->name);
nsyms = 2;
ntokens = 1;
for (bp = first_symbol; bp; bp = bp->next)
{
++nsyms;
if (bp->class == TERM) ++ntokens;
}
start_symbol = ntokens;
nvars = nsyms - ntokens;
if (symbol_value == 0) no_space();
symbol_prec = (short *) MALLOC(nsyms*sizeof(short));
if (symbol_prec == 0) no_space();
symbol_assoc = MALLOC(nsyms);
if (symbol_assoc == 0) no_space();
v[0] = 0;
v[start_symbol] = 0;
i = 1;
j = start_symbol + 1;
for (bp = first_symbol; bp; bp = bp->next)
{
if (bp->class == TERM)
v[i++] = bp;
else
v[j++] = bp;
}
assert(i == ntokens && j == nsyms);
goal->index = start_symbol + 1;
k = start_symbol + 2;
while (++i < nsyms)
if (v[i] != goal)
{
v[i]->index = k;
++k;
}
goal->value = 0;
k = 1;
for (i = start_symbol + 1; i < nsyms; ++i)
{
if (v[i] != goal)
{
v[i]->value = k;
++k;
}
}
k = 0;
A to Z of C 497
j = 0;
n = 257;
for (i = 2; i < ntokens; ++i)
{
if (v[i]->value == UNDEFINED)
{
while (j < k && n == symbol_value[j])
{
while (++j < k && n == symbol_value[j]) continue;
++n;
}
v[i]->value = n;
++n;
}
}
symbol_name[0] = name_pool + 8;
symbol_value[0] = 0;
symbol_prec[0] = 0;
symbol_assoc[0] = TOKEN;
for (i = 1; i < ntokens; ++i)
{
symbol_name[i] = v[i]->name;
symbol_value[i] = v[i]->value;
symbol_prec[i] = v[i]->prec;
symbol_assoc[i] = v[i]->assoc;
}
symbol_name[start_symbol] = name_pool;
symbol_value[start_symbol] = -1;
symbol_prec[start_symbol] = 0;
symbol_assoc[start_symbol] = TOKEN;
for (++i; i < nsyms; ++i)
{
k = v[i]->index;
498 A to Z of C
symbol_name[k] = v[i]->name;
symbol_value[k] = v[i]->value;
symbol_prec[k] = v[i]->prec;
symbol_assoc[k] = v[i]->assoc;
}
FREE(v);
}
pack_grammar()
{
register int i, j;
int assoc, prec;
ritem[0] = -1;
ritem[1] = goal->index;
ritem[2] = 0;
ritem[3] = -2;
rlhs[0] = 0;
rlhs[1] = 0;
rlhs[2] = start_symbol;
rrhs[0] = 0;
rrhs[1] = 0;
rrhs[2] = 1;
j = 4;
for (i = 3; i < nrules; ++i)
{
rlhs[i] = plhs[i]->index;
rrhs[i] = j;
assoc = TOKEN;
prec = 0;
while (pitem[j])
{
ritem[j] = pitem[j]->index;
A to Z of C 499
if (pitem[j]->class == TERM)
{
prec = pitem[j]->prec;
assoc = pitem[j]->assoc;
}
++j;
}
ritem[j] = -i;
++j;
if (rprec[i] == UNDEFINED)
{
rprec[i] = prec;
rassoc[i] = assoc;
}
}
rrhs[i] = j;
FREE(plhs);
FREE(pitem);
}
print_grammar()
{
register int i, j, k;
int spacing;
register FILE *f = verbose_file;
if (!vflag) return;
k = 1;
for (i = 2; i < nrules; ++i)
{
if (rlhs[i] != rlhs[i-1])
{
if (i != 2) fprintf(f, "\n");
fprintf(f, "%4d %s :", i - 2, symbol_name[rlhs[i]]);
spacing = strlen(symbol_name[rlhs[i]]) + 1;
}
else
{
fprintf(f, "%4d ", i - 2);
j = spacing;
while (--j >= 0) putc(' ', f);
putc('|', f);
}
while (ritem[k] >= 0)
{
fprintf(f, " %s", symbol_name[ritem[k]]);
500 A to Z of C
++k;
}
++k;
putc('\n', f);
}
}
reader()
{
write_section(banner);
create_symbol_table();
read_declarations();
read_grammar();
free_symbol_table();
free_tags();
pack_names();
check_symbols();
pack_symbols();
pack_grammar();
free_symbols();
print_grammar();
}
49.2.2.9 Skeleton.c
#include "defs.h"
char *banner[] =
{
"#ifndef lint",
"static char yysccsid[] = \"@(#)yaccpar 1.7 (Berkeley)
09/09/90\";",
"#endif",
"#define YYBYACC 1",
0
};
char *tables[] =
{
"extern short yylhs[];",
"extern short yylen[];",
A to Z of C 501
char *header[] =
{
"#define yyclearin (yychar=(-1))",
"#define yyerrok (yyerrflag=0)",
"#ifdef YYSTACKSIZE",
"#ifndef YYMAXDEPTH",
"#define YYMAXDEPTH YYSTACKSIZE",
"#endif",
"#else",
"#ifdef YYMAXDEPTH",
"#define YYSTACKSIZE YYMAXDEPTH",
"#else",
"#define YYSTACKSIZE 600",
"#define YYMAXDEPTH 600",
"#endif",
"#endif",
"int yydebug;",
"int yynerrs;",
"int yyerrflag;",
"int yychar;",
"short *yyssp;",
"YYSTYPE *yyvsp;",
"YYSTYPE yyval;",
"YYSTYPE yylval;",
"short yyss[YYSTACKSIZE];",
"YYSTYPE yyvs[YYSTACKSIZE];",
"#define yystacksize YYSTACKSIZE",
0
};
char *body[] =
{
"#define YYABORT goto yyabort",
502 A to Z of C
" if (yydebug)",
" printf(\"yydebug: state %d, shifting to state
%d\\n\",",
" yystate, yytable[yyn]);",
"#endif",
" if (yyssp >= yyss + yystacksize - 1)",
" {",
" goto yyoverflow;",
" }",
" *++yyssp = yystate = yytable[yyn];",
" *++yyvsp = yylval;",
" yychar = (-1);",
" if (yyerrflag > 0) --yyerrflag;",
" goto yyloop;",
" }",
" if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&",
" yyn <= YYTABLESIZE && yycheck[yyn] == yychar)",
" {",
" yyn = yytable[yyn];",
" goto yyreduce;",
" }",
" if (yyerrflag) goto yyinrecovery;",
"#ifdef lint",
" goto yynewerror;",
"#endif",
"yynewerror:",
" yyerror(\"syntax error\");",
"#ifdef lint",
" goto yyerrlab;",
"#endif",
"yyerrlab:",
" ++yynerrs;",
"yyinrecovery:",
" if (yyerrflag < 3)",
" {",
" yyerrflag = 3;",
" for (;;)",
" {",
" if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0
&&",
" yyn <= YYTABLESIZE && yycheck[yyn] ==
YYERRCODE)",
" {",
"#if YYDEBUG",
" if (yydebug)",
" printf(\"yydebug: state %d, error recovery
shifting\\",
504 A to Z of C
char *trailer[] =
{
" }",
" yyssp -= yym;",
" yystate = *yyssp;",
" yyvsp -= yym;",
" yym = yylhs[yyn];",
" if (yystate == 0 && yym == 0)",
" {",
"#if YYDEBUG",
" if (yydebug)",
" printf(\"yydebug: after reduction, shifting from state
0 to\\",
" state %d\\n\", YYFINAL);",
"#endif",
" yystate = YYFINAL;",
" *++yyssp = YYFINAL;",
" *++yyvsp = yyval;",
" if (yychar < 0)",
" {",
" if ((yychar = yylex()) < 0) yychar = 0;",
"#if YYDEBUG",
" if (yydebug)",
" {",
" yys = 0;",
" if (yychar <= YYMAXTOKEN) yys = yyname[yychar];",
" if (!yys) yys = \"illegal-symbol\";",
" printf(\"yydebug: state %d, reading %d (%s)\\n\",",
" YYFINAL, yychar, yys);",
" }",
"#endif",
" }",
" if (yychar == 0) goto yyaccept;",
" goto yyloop;",
" }",
506 A to Z of C
write_section(section)
char *section[];
{
register int i;
register FILE *fp;
fp = code_file;
for (i = 0; section[i]; ++i)
{
++outline;
fprintf(fp, "%s\n", section[i]);
}
}
49.2.2.10 Symtab.c
#include "defs.h"
/* TABLE_SIZE is the number of entries in the symbol table. */
/* TABLE_SIZE must be a power of two. */
A to Z of C 507
bucket **symbol_table;
bucket *first_symbol;
bucket *last_symbol;
int
hash(name)
char *name;
{
register char *s;
register int c, k;
return (k);
}
bucket *
make_bucket(name)
char *name;
{
register bucket *bp;
assert(name);
bp = (bucket *) MALLOC(sizeof(bucket));
if (bp == 0) no_space();
bp->link = 0;
bp->next = 0;
bp->name = MALLOC(strlen(name) + 1);
if (bp->name == 0) no_space();
bp->tag = 0;
bp->value = UNDEFINED;
bp->index = 0;
bp->prec = 0;
bp-> class = UNKNOWN;
bp->assoc = TOKEN;
if (bp->name == 0) no_space();
strcpy(bp->name, name);
return (bp);
}
508 A to Z of C
bucket *
lookup(name)
char *name;
{
register bucket *bp, **bpp;
while (bp)
{
if (strcmp(name, bp->name) == 0) return (bp);
bpp = &bp->link;
bp = *bpp;
}
*bpp = bp = make_bucket(name);
last_symbol->next = bp;
last_symbol = bp;
return (bp);
}
create_symbol_table()
{
register int i;
register bucket *bp;
bp = make_bucket("error");
bp->index = 1;
bp->class = TERM;
first_symbol = bp;
last_symbol = bp;
symbol_table[hash("error")] = bp;
}
free_symbol_table()
{
FREE(symbol_table);
symbol_table = 0;
}
A to Z of C 509
free_symbols()
{
register bucket *p, *q;
for (p = first_symbol; p; p = q)
{
q = p->next;
FREE(p);
}
}
49.2.2.11 Verbose.c
#include "defs.h"
verbose()
{
register int i;
if (!vflag) return;
if (nunused)
log_unused();
if (SRtotal || RRtotal)
log_conflicts();
log_unused()
{
register int i;
register short *p;
510 A to Z of C
log_conflicts()
{
register int i;
fprintf(verbose_file, "\n\n");
for (i = 0; i < nstates; i++)
{
if (SRconflicts[i] || RRconflicts[i])
{
fprintf(verbose_file, "State %d contains ", i);
if (SRconflicts[i] == 1)
fprintf(verbose_file, "1 shift/reduce conflict");
else if (SRconflicts[i] > 1)
fprintf(verbose_file, "%d shift/reduce conflicts",
SRconflicts[i]);
if (SRconflicts[i] && RRconflicts[i])
fprintf(verbose_file, ", ");
if (RRconflicts[i] == 1)
fprintf(verbose_file, "1 reduce/reduce conflict");
else if (RRconflicts[i] > 1)
fprintf(verbose_file, "%d reduce/reduce conflicts",
RRconflicts[i]);
fprintf(verbose_file, ".\n");
}
}
}
print_state(state)
int state;
{
if (state)
fprintf(verbose_file, "\n\n");
if (SRconflicts[state] || RRconflicts[state])
print_conflicts(state);
A to Z of C 511
print_conflicts(state)
int state;
{
register int symbol;
register action *p, *q, *r;
symbol = p->symbol;
while (q->next && q->next->symbol == symbol)
q = q->next;
if (state == final_state && symbol == 0)
{
r = p;
for (;;)
{
fprintf(verbose_file, "%d: shift/reduce conflict \
(accept, reduce %d) on $end\n", state, r->number - 2);
if (r == q) break;
r = r->next;
}
}
else if (p != q)
{
r = p->next;
if (p->action_code == SHIFT)
{
for (;;)
{
if (r->action_code == REDUCE && p->suppressed != 2)
fprintf(verbose_file, "%d: shift/reduce conflict \
(shift %d, reduce %d) on %s\n", state, p->number, r->number - 2,
symbol_name[symbol]);
if (r == q) break;
r = r->next;
}
}
512 A to Z of C
else
{
for (;;)
{
if (r->action_code == REDUCE && p->suppressed != 2)
fprintf(verbose_file, "%d: reduce/reduce conflict \
(reduce %d, reduce %d) on %s\n", state, p->number - 2, r->number - 2,
symbol_name[symbol]);
if (r == q) break;
r = r->next;
}
}
}
}
}
print_core(state)
int state;
{
register int i;
register int k;
register int rule;
register core *statep;
register short *sp;
register short *sp1;
statep = state_table[state];
k = statep->nitems;
putc('.', verbose_file);
print_nulls(state)
int state;
{
register action *p;
register int i, j, k, nnulls;
nnulls = 0;
for (p = parser[state]; p; p = p->next)
{
if (p->action_code == REDUCE &&
(p->suppressed == 0 || p->suppressed == 1))
{
i = p->number;
if (rrhs[i] + 1 == rrhs[i+1])
{
for (j = 0; j < nnulls && i > null_rules[j]; ++j)
continue;
if (j == nnulls)
{
++nnulls;
null_rules[j] = i;
}
else if (i != null_rules[j])
{
++nnulls;
for (k = nnulls - 1; k > j; --k)
null_rules[k] = null_rules[k-1];
null_rules[j] = i;
}
}
}
}
print_actions(stateno)
int stateno;
{
register action *p;
register shifts *sp;
register int as;
if (stateno == final_state)
fprintf(verbose_file, "\t$end accept\n");
p = parser[stateno];
if (p)
{
print_shifts(p);
print_reductions(p, defred[stateno]);
}
sp = shift_table[stateno];
if (sp && sp->nshifts > 0)
{
as = accessing_symbol[sp->shift[sp->nshifts - 1]];
if (ISVAR(as))
print_gotos(stateno);
}
}
print_shifts(p)
register action *p;
{
register int count;
register action *q;
count = 0;
for (q = p; q; q = q->next)
{
if (q->suppressed < 2 && q->action_code == SHIFT)
++count;
}
if (count > 0)
{
for (; p; p = p->next)
{
if (p->action_code == SHIFT && p->suppressed == 0)
fprintf(verbose_file, "\t%s shift %d\n",
symbol_name[p->symbol], p->number);
}
}
}
A to Z of C 515
print_reductions(p, defred)
register action *p;
register int defred;
{
register int k, anyreds;
register action *q;
anyreds = 0;
for (q = p; q ; q = q->next)
{
if (q->action_code == REDUCE && q->suppressed < 2)
{
anyreds = 1;
break;
}
}
if (anyreds == 0)
fprintf(verbose_file, "\t. error\n");
else
{
for (; p; p = p->next)
{
if (p->action_code == REDUCE && p->number != defred)
{
k = p->number - 2;
if (p->suppressed == 0)
fprintf(verbose_file, "\t%s reduce %d\n",
symbol_name[p->symbol], k);
}
}
if (defred > 0)
fprintf(verbose_file, "\t. reduce %d\n", defred - 2);
}
}
print_gotos(stateno)
int stateno;
{
register int i, k;
register int as;
register short *to_state;
register shifts *sp;
putc('\n', verbose_file);
sp = shift_table[stateno];
to_state = sp->shift;
516 A to Z of C
transitive_closure(R, n)
unsigned *R;
int n;
{
register int rowsize;
register unsigned mask;
register unsigned *rowj;
register unsigned *rp;
register unsigned *rend;
register unsigned *ccol;
register unsigned *relend;
register unsigned *cword;
register unsigned *rowi;
rowsize = WORDSIZE(n);
relend = R + n*rowsize;
cword = R;
mask = 1;
rowi = R;
while (rowi < relend)
{
ccol = cword;
rowj = R;
else
{
rowj += rowsize;
}
ccol += rowsize;
}
mask <<= 1;
if (mask == 0)
{
mask = 1;
cword++;
}
rowi += rowsize;
}
}
reflexive_transitive_closure(R, n)
unsigned *R;
int n;
{
register int rowsize;
register unsigned mask;
register unsigned *rp;
register unsigned *relend;
transitive_closure(R, n);
rowsize = WORDSIZE(n);
relend = R + n*rowsize;
mask = 1;
rp = R;
while (rp < relend)
{
*rp |= mask;
mask <<= 1;
if (mask == 0)
{
mask = 1;
rp++;
}
rp += rowsize;
}
}
518 A to Z of C
49.2.2.13 Main.c
#include <signal.h>
#include "defs.h"
char dflag;
char lflag;
char rflag;
char tflag;
char vflag;
int lineno;
int outline;
char *action_file_name;
char *defines_file_name;
char *input_file_name = "";
char *output_file_name;
char *code_file_name;
char *text_file_name;
char *union_file_name;
char *verbose_file_name;
int nitems;
int nrules;
int nsyms;
A to Z of C 519
int ntokens;
int nvars;
int start_symbol;
char **symbol_name;
short *symbol_value;
short *symbol_prec;
char *symbol_assoc;
short *ritem;
short *rlhs;
short *rrhs;
short *rprec;
char *rassoc;
short **derives;
char *nullable;
done(k)
int k;
{
if (action_file) { fclose(action_file); unlink(action_file_name); }
if (text_file) { fclose(text_file); unlink(text_file_name); }
if (union_file) { fclose(union_file); unlink(union_file_name); }
exit(k);
}
set_signals()
{
#ifdef SIGINT
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, onintr);
#endif
#ifdef SIGTERM
if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
signal(SIGTERM, onintr);
#endif
#ifdef SIGHUP
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, onintr);
520 A to Z of C
#endif
}
usage()
{
fprintf(stderr, "Yacc (Berkeley) 09/09/90\n");
fprintf(stderr, "Usage: %s [-dlrtv] [-b file_prefix] filename\n\n",
myname);
fprintf(stderr, "\t-b file_prefix change the default file prefix
\"y.\"\n");
fprintf(stderr, "\t-d\t\twrite the header file \"y.tab.h\"\n");
fprintf(stderr, "\t-l\t\texclude the #line directives in files\n");
fprintf(stderr, "\t-r\t\tseperate code and tables into \"y.code.c\"
and \"y.tab.c\"\n");
fprintf(stderr, "\t-t\t\tinclude the debugging code in files\n");
getargs(argc, argv)
int argc;
char *argv[];
{
register int i;
register char *s;
case '-':
++i;
goto no_more_options;
case 'b':
if (*++s)
file_prefix = s;
A to Z of C 521
case 'd':
dflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'r':
rflag = 1;
break;
case 't':
tflag = 1;
break;
case 'v':
vflag = 1;
break;
default:
usage();
}
for (;;)
{
switch (*++s)
{
case '\0':
goto end_of_option;
case 'd':
dflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'r':
rflag = 1;
522 A to Z of C
break;
case 't':
tflag = 1;
break;
case 'v':
vflag = 1;
break;
default:
usage();
}
}
end_of_option:;
}
no_more_options:;
if (i + 1 != argc) usage();
input_file_name = argv[i];
}
char *
allocate(n)
unsigned n;
{
register char *p;
p = NULL;
if (n)
{
p = CALLOC(1, n);
if (!p) no_space();
}
return (p);
}
create_file_names()
{
int i, len;
char *tmpdir;
#ifdef MSDOS
(tmpdir = getenv("TMPDIR")) ||
(tmpdir = getenv("TMP")) ||
(tmpdir = ".");
#else
A to Z of C 523
tmpdir = getenv("TMPDIR");
if (tmpdir == 0) tmpdir = "/tmp";
#endif
len = strlen(tmpdir);
i = len + 13;
if (len && tmpdir[len-1] != '/')
++i;
action_file_name = MALLOC(i);
if (action_file_name == 0) no_space();
text_file_name = MALLOC(i);
if (text_file_name == 0) no_space();
union_file_name = MALLOC(i);
if (union_file_name == 0) no_space();
strcpy(action_file_name, tmpdir);
strcpy(text_file_name, tmpdir);
strcpy(union_file_name, tmpdir);
action_file_name[len + 5] = 'a';
text_file_name[len + 5] = 't';
union_file_name[len + 5] = 'u';
mktemp(action_file_name);
mktemp(text_file_name);
mktemp(union_file_name);
len = strlen(file_prefix);
if (rflag)
{
code_file_name = MALLOC(len + 8);
if (code_file_name == 0)
no_space();
strcpy(code_file_name, file_prefix);
strcpy(code_file_name + len, CODE_SUFFIX);
}
else
code_file_name = output_file_name;
if (dflag)
{
/* the number 7 below is the size of ".tab.h"; sizeof is not
used */
/* because of a C compiler that thinks sizeof(".tab.h") == 6 */
defines_file_name = MALLOC(len + 7);
if (defines_file_name == 0)
no_space();
strcpy(defines_file_name, file_prefix);
strcpy(defines_file_name + len, DEFINES_SUFFIX);
}
if (vflag)
{
verbose_file_name = MALLOC(len + 8);
if (verbose_file_name == 0)
no_space();
strcpy(verbose_file_name, file_prefix);
strcpy(verbose_file_name + len, VERBOSE_SUFFIX);
}
}
open_files()
{
create_file_names();
if (input_file == 0)
{
input_file = fopen(input_file_name, "r");
if (input_file == 0)
open_error(input_file_name);
}
if (vflag)
{
verbose_file = fopen(verbose_file_name, "w");
if (verbose_file == 0) open_error(verbose_file_name);
}
if (dflag)
{
defines_file = fopen(defines_file_name, "w");
if (defines_file == 0) open_error(defines_file_name);
union_file = fopen(union_file_name, "w");
if (union_file == 0) open_error(union_file_name);
}
if (rflag)
{
code_file = fopen(code_file_name, "w");
if (code_file == 0)
open_error(code_file_name);
}
else
code_file = output_file;
}
int
main(argc, argv)
int argc;
char *argv[];
{
set_signals();
getargs(argc, argv);
open_files();
reader();
lr0();
lalr();
make_parser();
verbose();
output();
done(0);
/*NOTREACHED*/
}
526 A to Z of C
Developing a Database
Package
DBMS (Database Management System) is a vast area. In DBMS we have many theories
and algorithms for managing data. This book does not deal the DBMS basics. So I recommend
you to go through a good book on DBMS for indepth knowledge in that area. Indepth knowledge
on DBMS is necessary for developing our own Database Package. In this chapter I won’t describe
the DBMS fundamentals instead I am going to present the file organization of database files.
BYTES DESCRIPTION
29 dBaseIV language driver ID
30-31 Reserved fill with 0x00
32-n Field Descriptor array
N+1 Header Record Terminator - 0x0D
50.3 Security
Applying security to the database file is considered to be hard. Oracle came out with a
very good security system. So we cannot look into the database file created from Oracle! And
thus stealing of data is restricted. This is considered to be a tough task. By the way, you won’t
find any difficulty in creating FoxPro like Database Package. I hope this information would help
you to develop your own Database Package.
51
“Wisdom is better than weapons of war.”
Decompilation / EXE to C
Decompilation is the reverse of compilation. That is, we can get a C file from EXE file!
The most important problem in converting back C file from EXE file is loss of variable names
and loss of function names. Machine code won’t store variable names. So it is not at all possible
to get back the original C code.
51.2 DCC
51.2.1 Disclaimer
DCC is a decompiler written by Cristina Cifuentes and Mike Van Emmerik while at
the Queensland University of Technology, Australia. Copyright is owned by Cristina Cifuentes
and the Queensland University of Technology. DCC is merely a prototype tool and more work
needs to be done in order to have a fully working decompiler.
Important Notice
I have received permission to use the article about DCC from the authors (Cristina Cifuentes and Mike Van
Emmerik) with the condition of including the above disclaimer note. As Cristina Cifuentes and Mike Van
Emmerik are not currently involving in decompililation, it seems they don’t like to receive any request or
correspondence regarding their decompilation work. So the reader is requested not to disturb them.
51.2.2 Notice
Decompilation is a technique that allows you to recover lost source code. It is also needed
in some cases for computer security, interoperability and error correction. dcc, and any
decompiler in general, should not be used for "cracking" other programs, as programs are
protected by copyright. Cracking of programs is not only illegal but it rides on other's creative
effort.
530 A to Z of C
55 8B EC 83 EC 04 56 57 1E B8 94 00 50 9A
0E 00 3C 17 59 59 16 8D 46 FC 50 1E B8 B1 00 50
9A 07 00 F0 17 83 C4 08 BE 01 00 EB 3B 1E B8 B4
00 50 9A 0E 00 3C 17 59 59 16 8D 46 FE 50 1E B8
C3 00 50 9A 07 00 F0 17 83 C4 08 FF 76 FE 9A 7C
00 3B 16 59 8B F8 57 FF 76 FE 1E B8 C6 00 50 9A
0E 00 3C 17 83 C4 08 46 3B 76 FC 7E C0 33 C0 50
9A 0A 00 49 16 59 5F 5E 8B E5 5D CB 55 8B EC 56
8B 76 06 83 FE 02 7E 1E 8B C6 48 50 0E E8 EC FF
59 50 8B C6 05 FE FF 50 0E E8 E0 FF 59 8B D0 58
03 C2 EB 07 EB 05 B8 01 00 EB 00 5E 5D CB
Figure 1 - Machine Code for Fibonacci.exe
A to Z of C 531
/*
* Input file : fibo.exe
* File type : EXE
*/
int proc_1 (int arg0)
/* Takes 2 bytes of parameters.
* High-level language prologue code.
* C calling convention.
*/
{
int loc1;
int loc2; /* ax */
loc1 = arg0;
if (loc1 > 2) {
loc2 = (proc_1 ((loc1 - 1)) + proc_1 ((loc1 + 0xFFFE)));
}
else {
loc2 = 1;
}
return (loc2);
}
void main ( )
/* Takes no parameters.
* High-level language prologue code.
*/
{
int loc1;
int loc2;
int loc3;
int loc4;
#include <stdio.h>
int main( )
{ int i, numtimes, number;
unsigned value, fib();
Writing Disassembler
Disassembler is the one which produces Assembly code for a given binary (EXE /
COM)file. In this chapter let’s see how to write a disassembler.
52.1 Prelude
We have already seen about assembler, linker and compiler. While we were discussing
about decompilation (converting EXE file to C), we used disassembler to
EXE/COM file
convert a binary file to assembly file. Thus disassembler provides a way
to view the binary file with certain readability. In otherwords,
disassembler can be used to read or edit a binary file in a better way.
Debugger is a tool to edit binary files. DOS’s DEBUG is one Disassembler
such readily available Debugger. We also have other efficient Debuggers
like TD (Turbo Debugger) etc. All debuggers use disassembler to provide
assembly listing.
Assembly Code
52.2 Secrets
In binary files the machine instructions are stored. Each binary code represents certain
assembly instruction. So for writing disassembler, you need to know machine codes and
corresponding assembly instructions. Disassembling is simply the reverse of assembling.
52.3 2asm
2asm is a disassembler utility that converts binary files to 80x86 assembler. The code was
originally from the GNU C++ debugger, as ported to DOS by DJ Delorie and Kent Williams.
Later Robin Hilliard modified it. This code was licensed under GNU’s GPL. This disassembler
is entirely table driven so one can easily change the instructions. When I checked this code it
worked better than DOS’s DEBUG. According to me it is really good as it uses tough logic.
The emulated coprocessor instructions on interrupts 34--3E are disassembled if the "-e"
command line option is specified.
Command line switches (case sensitive):
-e : Disassemble (unoverridden) emulated 80*87 instructions (not default)
-3 : Assume code is 32 bit (default==16)
-x : Output all numbers in pure hex (no leading zeros or trailing "h"s.)
536 A to Z of C
-s : Don't specify operand size (ie omit "byte ptr", "word ptr" and "dword ptr" from
instruction output
-d : Don't specify distance of calls and jumps (near/far/short) (not default)
52.3.1 Table.c
Following is the table implementation for the disassembler. By the term table we mean
array. It is wise to place the corresponding instructions in the array, so that we can fetch it for the
given opcode.
/* Percent tokens in strings:
First char after '%':
A - direct address
C - reg of r/m picks control register
D - reg of r/m picks debug register
E - r/m picks operand
F - flags register
G - reg of r/m picks general register
I - immediate data
J - relative IP offset
+ K - call/jmp distance
M - r/m picks memory
O - no r/m, offset only
R - mod of r/m picks register only
S - reg of r/m picks segment register
T - reg of r/m picks test register
X - DS:ESI
Y - ES:EDI
2 - prefix of two-byte opcode
+ e - put in 'e' if use32 (second char is part of reg name)
+ put in 'w' for use16 or 'd' for use32 (second char is 'w')
+ j - put in 'e' in jcxz if prefix==0x66
f - floating point (second char is esc value)
g - do r/m group 'n', n==0..7
p - prefix
s - size override (second char is a,o)
+ d - put d if double arg, nothing otherwise (pushfd, popfd &c)
+ w - put w if word, d if double arg, nothing otherwise
(lodsw/lodsd)
+ P - simple prefix
+ n - near call/jmp
p - 32 or 48 bit pointer
+ q - byte/word thingy
s - six byte pseudo-descriptor
v - word or dword
w - word
+ x - sign extended byte
F - use floating regs in mod/rm
1-8 - group number, esc value, etc
*/
char *opmap1[256] = {
/* 0 */
"add %Eb,%Gb", "add %Ev,%Gv", "add %Gb,%Eb", "add %Gv,%Ev",
"add al,%Ib", "add %eax,%Iv", "push es", "pop es",
"or %Eb,%Gb", "or %Ev,%Gv", "or %Gb,%Eb", "or %Gv,%Ev",
"or al,%Ib", "or %eax,%Iv", "push cs", "%2 ",
/* 1 */
"adc %Eb,%Gb", "adc %Ev,%Gv", "adc %Gb,%Eb", "adc %Gv,%Ev",
"adc al,%Ib", "adc %eax,%Iv", "push ss", "pop ss",
"sbb %Eb,%Gb", "sbb %Ev,%Gv", "sbb %Gb,%Eb", "sbb %Gv,%Ev",
"sbb al,%Ib", "sbb %eax,%Iv", "push ds", "pop ds",
/* 2 */
"and %Eb,%Gb", "and %Ev,%Gv", "and %Gb,%Eb", "and %Gv,%Ev",
"and al,%Ib", "and %eax,%Iv", "%pe", "daa",
"sub %Eb,%Gb", "sub %Ev,%Gv", "sub %Gb,%Eb", "sub %Gv,%Ev",
"sub al,%Ib", "sub %eax,%Iv", "%pc", "das",
/* 3 */
"xor %Eb,%Gb", "xor %Ev,%Gv", "xor %Gb,%Eb", "xor %Gv,%Ev",
"xor al,%Ib", "xor %eax,%Iv", "%ps", "aaa",
"cmp %Eb,%Gb", "cmp %Ev,%Gv", "cmp %Gb,%Eb", "cmp %Gv,%Ev",
"cmp al,%Ib", "cmp %eax,%Iv", "%pd", "aas",
/* 4 */
"inc %eax", "inc %ecx", "inc %edx", "inc %ebx",
"inc %esp", "inc %ebp", "inc %esi", "inc %edi",
"dec %eax", "dec %ecx", "dec %edx", "dec %ebx",
"dec %esp", "dec %ebp", "dec %esi", "dec %edi",
/* 5 */
"push %eax", "push %ecx", "push %edx", "push %ebx",
"push %esp", "push %ebp", "push %esi", "push %edi",
"pop %eax", "pop %ecx", "pop %edx", "pop %ebx",
"pop %esp", "pop %ebp", "pop %esi", "pop %edi",
/* 6 */
"pusha%d ", "popa%d ", "bound %Gv,%Ma","arpl %Ew,%Rw",
538 A to Z of C
char *second[] = {
/* 0 */
"%g5", "%g6", "lar %Gv,%Ew", "lsl %Gv,%Ew",
0, "loadall", "clts", "loadall",
"invd", "wbinvd", 0, 0,
0, 0, 0, 0,
/* 1 */
"mov %Eb,%Gb", "mov %Ev,%Gv", "mov %Gb,%Eb", "mov %Gv,%Ev",
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
/* 2 */
"mov %Rd,%Cd", "mov %Rd,%Dd", "mov %Cd,%Rd", "mov %Dd,%Rd",
"mov %Rd,%Td", 0, "mov %Td,%Rd", 0,
0, 0, 0, 0,
0, 0, 0, 0,
/* 3 */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* 4 */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* 5 */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* 6 */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* 7 */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* 8 */
"jo %Jv", "jno %Jv", "jb %Jv", "jnb %Jv",
"jz %Jv", "jnz %Jv", "jbe %Jv", "ja %Jv",
"js %Jv", "jns %Jv", "jp %Jv", "jnp %Jv",
"jl %Jv", "jge %Jv", "jle %Jv", "jg %Jv",
540 A to Z of C
/* 9 */
"seto %Eb", "setno %Eb", "setc %Eb", "setnc %Eb",
"setz %Eb", "setnz %Eb", "setbe %Eb", "setnbe %Eb",
"sets %Eb", "setns %Eb", "setp %Eb", "setnp %Eb",
"setl %Eb", "setge %Eb", "setle %Eb", "setg %Eb",
/* a */
"push fs", "pop fs", 0, "bt %Ev,%Gv",
"shld %Ev,%Gv,%Ib", "shld %Ev,%Gv,cl", 0, 0,
"push gs", "pop gs", 0, "bts %Ev,%Gv",
"shrd %Ev,%Gv,%Ib", "shrd %Ev,%Gv,cl", 0, "imul %Gv,%Ev",
/* b */
"cmpxchg %Eb,%Gb", "cmpxchg %Ev,%Gv", "lss %Mp", "btr %Ev,%Gv",
"lfs %Mp", "lgs %Mp", "movzx %Gv,%Eb","movzx %Gv,%Ew",
0, 0, "%g7 %Ev,%Ib", "btc %Ev,%Gv",
"bsf %Gv,%Ev", "bsr %Gv,%Ev", "movsx %Gv,%Eb","movsx %Gv,%Ew",
/* c */
"xadd %Eb,%Gb", "xadd %Ev,%Gv", 0, 0,
0, 0, 0, 0,
"bswap eax", "bswap ecx", "bswap edx", "bswap ebx",
"bswap esp", "bswap ebp", "bswap esi", "bswap edi",
/* d */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* e */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* f */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
/* 4 */
{ "inc %Ev", "dec %Ev", "call %Kn%Ev", "call %Kf%Ep",
"jmp %Kn%Ev", "jmp %Kf%Ep", "push %Ev", 0
},
/* 5 */
{ "sldt %Ew", "str %Ew", "lldt %Ew", "ltr %Ew",
"verr %Ew", "verw %Ew", 0, 0
},
/* 6 */
{ "sgdt %Ms", "sidt %Ms", "lgdt %Ms", "lidt %Ms",
"smsw %Ew", 0, "lmsw %Ew", 0
},
/* 7 */
{ 0, 0, 0, 0,
"bt", "bts", "btr", "btc"
}
};
/* zero here means invalid. If first entry starts with '*', use st(i)
*/
/* no assumed %EFs here. Indexed by RM(modrm())
*/
char *f0[] = { 0, 0, 0, 0, 0, 0, 0, 0};
char *fop_9[] = { "*fxch st,%GF" };
char *fop_10[] = { "fnop", 0, 0, 0, 0, 0, 0, 0 };
char *fop_12[] = { "fchs", "fabs", 0, 0, "ftst", "fxam", 0, 0 };
char *fop_13[] = { "fld1", "fldl2t", "fldl2e", "fldpi",
"fldlg2", "fldln2", "fldz", 0 };
char *fop_14[] = { "f2xm1", "fyl2x", "fptan", "fpatan",
"fxtract", "fprem1", "fdecstp", "fincstp" };
char *fop_15[] = { "fprem", "fyl2xp1", "fsqrt", "fsincos",
"frndint", "fscale", "fsin", "fcos" };
char *fop_21[] = { 0, "fucompp", 0, 0, 0, 0, 0, 0 };
char *fop_28[] = { 0, 0, "fclex", "finit", 0, 0, 0, 0 };
char *fop_32[] = { "*fadd %GF,st" };
char *fop_33[] = { "*fmul %GF,st" };
char *fop_36[] = { "*fsubr %GF,st" };
char *fop_37[] = { "*fsub %GF,st" };
char *fop_38[] = { "*fdivr %GF,st" };
char *fop_39[] = { "*fdiv %GF,st" };
char *fop_40[] = { "*ffree %GF" };
char *fop_42[] = { "*fst %GF" };
char *fop_43[] = { "*fstp %GF" };
char *fop_44[] = { "*fucom %GF" };
char *fop_45[] = { "*fucomp %GF" };
char *fop_48[] = { "*faddp %GF,st" };
char *fop_49[] = { "*fmulp %GF,st" };
542 A to Z of C
52.3.2 Disasm.c
Following is the main routine for the disassembler.
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
A to Z of C 543
#include <stdlib.h>
typedef union {
struct {
word16 ofs;
word16 seg;
} w;
word32 dword;
} WORD32;
/* prototypes */
strcpy(hexstr, do_hex?"h":"");
adr.dword = addr;
if (splitup) {
if (adr.w.seg==0 || adr.w.seg==0xffff) /* 'coz of wraparound */
sprintf(buffer, "%04X%s", adr.w.ofs, hexstr);
else
sprintf(buffer, "%04X%s:%04X%s", adr.w.seg, hexstr, adr.w.ofs,
hexstr);
} else {
if (adr.w.seg==0 || adr.w.seg==0xffff) /* 'coz of wraparound */
sprintf(buffer, "%04X%s", adr.w.ofs, hexstr);
else
sprintf(buffer, "%08lX%s", addr, hexstr);
}
return buffer;
}
c = fgetc(infile);
if (c==EOF)
longjmp(reached_eof, 1);
printf("%02X", c); /* print out byte */
col+=2;
if (patch87) {
546 A to Z of C
/*
only one modrm or sib byte per instruction, tho' they need to be
returned a few times...
*/
static modrm(void)
{
if (modrmv == -1)
modrmv = getbyte();
return modrmv;
}
static sib(void)
{
if (sibv == -1)
sibv = getbyte();
return sibv;
}
/*-----------------------------------------------------------------*/
static void uprintf(char *s, ...)
{
vsprintf(ubufp, s, ...);
while (*ubufp)
ubufp++;
}
A to Z of C 547
/*----------------------------------------------------------------*/
static int bytes(char c)
{
switch (c) {
case 'b':
return 1;
case 'w':
return 2;
case 'd':
return 4;
case 'v':
if (opsize == 32)
return 4;
else
return 2;
}
return 0;
}
/*-----------------------------------------------------------------*/
static void outhex(char subtype, int extend, int optional, int defsize,
int sign)
{
int n=0, s=0, i;
int32 delta;
unsigned char buff[6];
char *name;
char signchar;
switch (subtype) {
case 'q':
548 A to Z of C
if (wordop) {
if (opsize==16) {
n = 2;
} else {
n = 4;
}
} else {
n = 1;
}
break;
case 'a':
break;
case 'x':
extend = 2;
n = 1;
break;
case 'b':
n = 1;
break;
case 'w':
n = 2;
break;
case 'd':
n = 4;
break;
case 's':
n = 6;
break;
case 'c':
case 'v':
if (defsize == 32)
n = 4;
else
n = 2;
break;
case 'p':
if (defsize == 32)
n = 6;
else
n = 4;
s = 1;
break;
}
for (i=0; i<n; i++)
buff[i] = getbyte();
for (; i<extend; i++)
A to Z of C 549
if (sign)
uprintf(do_hex?"%c%02X":"%c%03Xh",signchar,(unsigned
char)delta);
else
uprintf(do_hex?"%02X":"%03Xh", (unsigned char)delta);
break;
case 2:
if (sign && (int)delta<0) {
signchar = '-';
delta = -delta;
} else
signchar = '+';
if (sign)
uprintf(do_hex?"%c%04X":"%c%05Xh", signchar,(int)delta);
else
uprintf(do_hex?"%04X":"%05Xh", (unsigned int)delta);
break;
case 4:
if (sign && (long)delta<0) {
delta = -delta;
signchar = '-';
} else
signchar = '+';
if (sign)
uprintf(do_hex?"%c%08X":"%c%09lXh", signchar, (unsigned
long)delta);
else
uprintf(do_hex?"%08X":"%09lXh", (unsigned long)delta);
break;
}
}
/*---------------------------------------------------------------*/
static void reg_name(int regnum, char size)
{
if (size == 'F') { /* floating point register? */
uprintf("st(%d)", regnum);
return;
}
if (((size == 'v') && (opsize == 32)) || (size == 'd'))
uputchar('e');
if ((size=='q' || size == 'b' || size=='c') && !wordop) {
uputchar("acdbacdb"[regnum]);
uputchar("llllhhhh"[regnum]);
} else {
A to Z of C 551
uputchar("acdbsbsd"[regnum]);
uputchar("xxxxppii"[regnum]);
}
}
/*-----------------------------------------------------------------*/
static void do_sib(int m)
{
int s, i, b;
s = SCALE(sib());
i = INDEX(sib());
b = BASE(sib());
switch (b) { /* pick base */
case 0: ua_str("%p:[eax"); break;
case 1: ua_str("%p:[ecx"); break;
case 2: ua_str("%p:[edx"); break;
case 3: ua_str("%p:[ebx"); break;
case 4: ua_str("%p:[esp"); break;
case 5:
if (m == 0) {
ua_str("%p:[");
outhex('d', 4, 0, addrsize, 0);
} else {
ua_str("%p:[ebp");
}
break;
case 6: ua_str("%p:[esi"); break;
case 7: ua_str("%p:[edi"); break;
}
switch (i) { /* and index */
case 0: uprintf("+eax"); break;
case 1: uprintf("+ecx"); break;
case 2: uprintf("+edx"); break;
case 3: uprintf("+ebx"); break;
case 4: break;
case 5: uprintf("+ebp"); break;
case 6: uprintf("+esi"); break;
case 7: uprintf("+edi"); break;
}
if (i != 4) {
switch (s) { /* and scale */
case 0: uprintf(""); break;
case 1: uprintf("*2"); break;
case 2: uprintf("*4"); break;
552 A to Z of C
/*-------------------------------------------------------------------*/
static void do_modrm(char subtype)
{
int mod = MOD(modrm());
int rm = RM(modrm());
int extend = (addrsize == 32) ? 4 : 2;
/*-------------------------------------------------------------------*/
static void floating_point(int e1)
{
int esc = e1*8 + REG(modrm());
if (MOD(modrm()) == 3) {
if (fspecial[esc]) {
if (fspecial[esc][0][0] == '*') {
ua_str(fspecial[esc][0]+1);
} else {
ua_str(fspecial[esc][RM(modrm())]);
}
} else {
ua_str(floatops[esc]);
ua_str(" %EF");
}
} else {
ua_str(floatops[esc]);
554 A to Z of C
ua_str(" %EF");
}
}
/*--------------------------------------------------------------------*/
/* Main table driver
*/
static void percent(char type, char subtype)
{
int32 vofs;
char *name;
int extend = (addrsize == 32) ? 4 : 2;
char c;
start:
switch (type) {
case 'A': /* direct address */
outhex(subtype, extend, 0, addrsize, 0);
break;
case 'K':
if (do_distance==0)
break;
switch (subtype) {
case 'f':
ua_str("far ");
break;
case 'n':
ua_str("near ");
break;
case 's':
ua_str("short ");
break;
}
break;
ua_str("%p:");
break;
case 'j':
if (addrsize==32 || opsize==32) /* both of them?! */
uputchar('e');
break;
c = getbyte();
wordop = c & 1;
ua_str(opmap1[c]);
break;
}
break;
if (str == 0) {
uprintf("<invalid>");
return;
}
if (strpbrk(str, "CDFGRST")) /* specifiers for registers=>no size 2b
specified */
must_do_size = 0;
while ((c = *str++) != 0) {
if (c == '%') {
c = *str++;
percent(c, *str++);
} else {
if (c == ' ') {
uputchar('\t');
} else {
A to Z of C 559
uputchar(c);
}
}
}
}
printf("%s\n", ubuf);
return instruction_length;
}
#if defined(DEBUG)
clrscr();
#endif
*infilename = 0;
while (--argc) {
argv++;
if (**argv=='?') {
hlp: fprintf(stderr,
"2ASM Version 1.01 (C) Copyright 1992, Robin Hilliard\n"
"Converts binary files to 80*86 assembly\n"
"Usage:\n"
"\t2asm <file> [-e] [-3] [-x] [-s] [-d]\n"
"Switches:\n"
"\t-e :\tDisassemble (unoverridden) emulated 80*87 instructions\n"
"\t-3 :\tAssume code is 32 bit (default==16)\n"
"\t-x :\tOutput numbers in pure hex (default is reassemblable)\n"
"\t-s :\tDon't specify operand size, even where necessary\n"
"\t-d :\tDon't specify distance short/near/far jmps and calls"
);
exit(1);
}
if (isoption(**argv)) {
while (isoption(**argv)) {
nextflag:
switch (c = *(++*argv)) {
case 'e':
do_emul87 = 1;
break;
case '3':
seg_size = 32;
A to Z of C 561
break;
case 'x':
do_hex = 1;
break;
case 's':
do_size = 0;
break;
case 'd':
do_distance = 0;
break;
case '?':
case 'H':
goto hlp;
case '#': /* hidden flag in the Loft's programs! */
fprintf(stderr,"Last compiled on " __TIME__ ", " __DATE__);
exit(1);
default:
fprintf(stderr, "Unknown option: `-%c'", c);
exit(1);
}
++*argv;
}
} else { /* assume that its a file name */
if (*infilename) {
fprintf(stderr,"Unknown file argument: \"%s\"", *argv);
exit(1);
}
strcpy(infilename,*argv);
}
}
if ((infile=fopen(infilename,"rb"))==NULL) {
printf("Unable to open %s",infilename);
exit(2);
}
offset = 0;
strlwr(infilename);
if (strstr(infilename, ".com")) /* not perfect, fix later */
instruction_offset = offset = 0x100;
if (!setjmp(reached_eof)) {
do {
instr_len = unassemble(offset);
offset += instr_len;
} while (instr_len); /* whoops, no files > 64k */
}
}
562 A to Z of C
52.3.3 2asm.prj
Add the above two programs: Table.c and Disasm.c in project file 2asm.prj and
compile. You will get an EXE file 2asm.exe that you can use as disassembler.
53
“Blessed are the pure in heart.”
Printer Programming
As everyone knows, Printers help us to produce hard copies. The quality of the printer is
referred by the term ‘resolution’. Dots per inch (dpi) is the unit of resolution.
Note
Since the Escape Codes are mostly ‘printer’ and ‘vendor’ specific, the Escape Sequences I have used here
will mostly work only on Epson 9 pin Dot Matrix Printers.
Epson Epson
ASCII Extended ASCII Extended
Character Character
value Character value Character
Set Value Set Value
┌ 218 135 ─ 196 133
┐ 191 136 │ 179 134
└ 192 137 ♠ 6 145
┘ 217 138 ♥ 3 146
├ 195 132 ♦ 4 147
┤ 180 131 ♣ 5 148
┬ 194 130 ÷ 246 158
┴ 193 129 ± 241 159
┼ 197 128
To set the printer to Epson Extended Character Set, we have to send ESC ‘m’ 4. For
that we can use the biosprint( ) function. As this mode uses ‘character set’, it will be faster
than graphics mode.
53.3.2 Graphics Mode
Graphics mode is the slowest one. To set the printer to graphics mode, we have to send:
ESC ‘*’ n1 n2 n3.
where n1 is the resolution (n1 = 4 means 80 dpi),
n2 = number of bytes to print % 256,
n3 = number of bytes to print / 256.
Pin
Pin Command Let’s see how to program the pins of printer head. To
No. to be sent activate the bottommost pin 0 we have to send 1 as a command, to
● 7 128 (27) activate pin 1 we have to send 2 as a command…
● 6 64 (26)
● 5 32 (25) So to activate pins 0, 1 and 7 at a given time, we have to
● 4 16 (24) send 1+2+128 = 131 to the printer. Before that, it is necessary to set
● 3 8 (23) the printer to graphics mode with the command:
ESC ‘*’ 4 8%256 8/256 (or ESC ‘*’ 4 8 0).
● 2 4 (22)
● 1 2 (21)
● 0 1 (20)
Note
At a given time you can program up to 8 pins only. So if you sent 256, all
pins will be activated. You cannot program the 9th pin (i.e., pin 8).
566 A to Z of C
● ●
● ●
For example, to print the character ‘H’ in graphics mode, the ● ●
command to be sent to the printer will be: ●●●●●●●●
ESC ‘*’ 4 8 0 (then pin values) 256 16 16 16 16 16 16
● 16 ●
256
● ●
● ●
● ●
256 256
53.3.3 Font Map
Note
If you prefer 8x14 font, you have to print the part of the font (with height 8) in one line and then you
have to print the remaining part of the font (with height 6) in another line.
The returned pointer by int 10h will point to the font map of first character of the
ASCII set (i.e., NULL or ASCII-0). The font map of the letter ‘H’ will be at the offset ‘H’
(ASCII-72). Similarly font map of every letter of the ASCII set (including non-printable
characters) will be at the offset of its ASCII value. So with the help of the pointer and a simple
program, we can find out the pin values easily.
53.3.4 Optimization Tip
We must understand that graphics mode is the slowest one. Printing with Epson
Extended Character Set is faster than graphics mode. So it is wiser to use Epson extended
character set’s all available characters. For all other non-printable characters use graphics mode.
A to Z of C 567
53.3.5 Program
The following is the code to print non-printable characters on Epson 9 pin Dot Matrix
Printers.
/*-------------------------------------------
PR – To print non-printable characters
File name: Pr.c
*-----
*/
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <bios.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define PRINTER_WRITE ( 0 )
#define PRINTER_STATUS ( 2 )
#define ESC ( 27 )
#define LPT1 ( 0 )
#define FNTHEIGHT ( 8 )
/*-----------------------------------------------------
Send2LPT1 - Send 'num' characters to LPT1 */
/*---------------------------------------------------------------
SetLineSpacingTo1by8 - Sets line spacing to 1/8 inch */
568 A to Z of C
/*---------------------------------------------------------------
PrintWithEpsonCharSet - Initializes printer to Epson Extended
Printer Character Set and print a single character 'ch'.
Epson Character Set contains all printable characters, single
line box characters and few other ASCII characters.
(It is faster than Graphics mode.) */
/* call the bios interrupt to get the address of the desired font */
regs.r_ax = 0x1130;
regs.r_bx = 0x0300;
intr( 0x10, ®s );
/*make a far pointer font8x8 point to info returned by the bios call*/
offset = regs.r_bp;
segment = regs.r_es;
font8x8 = (char far*) MK_FP( segment, offset );
/*---Check For any Errors-----> */
if ( argc < 2 )
{
cprintf(
" Syntax: PR filename [ -bb | -nbb ] \a\r\n"
A to Z of C 569
" -bb Box Better. Box characters will appear better. But \r\n"
" characters of adjacent lines may touch each other. \r\n"
" (default) \r\n"
while ( !feof( fp ) )
{
fread( &ch, 1, 1, fp );
if ( ch=='\r' || ch=='\n' || ch=='\a'|| ch=='\t'|| ch=='\v'
|| ch=='\f'|| ch=='\b'|| ch==0
|| ch==255 || (ch>=' '&&ch<='~') )
PrintWithEpsonCharSet( ch );
else
{
switch( ch )
{
/* Box Characters adjust */
/* upper left corner */
case 218: /* '┌' */
PrintWithEpsonCharSet( 135 );
break;
570 A to Z of C
case 4: /* diamond */
PrintWithEpsonCharSet( 147 );
break;
case 5: /* club */
PrintWithEpsonCharSet( 148 );
break;
case 246: /* 'Ў' */
PrintWithEpsonCharSet( 158 );
break;
case 241: /* 'ё' */
PrintWithEpsonCharSet( 159 );
break;
default:
mask = 128;
SetPrinter2GraphicsMode( );
for ( i=0; i<8; ++i )
{
/* make ptr point the start of the letter in
the rom font each character is FNTHEIGHT
bytes with each bit in a byte being a
pixel on/off for that scan line of the
charater
*/
ptr = font8x8 + ( ch * FNTHEIGHT );
fntval = 0;
powof2 = 128;
for ( j=0 ; j<8 ; ++j )
{
fntval += (*ptr&mask) ? powof2 : 0;
++ptr; /* ptr points to the next scanline
of the current character */
powof2 >>= 1;
}
biosprint( PRINTER_WRITE, fntval, LPT1 );
mask >>= 1; /* or dividing by 2 */
}
}
}
}
fclose( fp );
return(0);
} /*--main( )------------*/
572 A to Z of C
Suggested Projects
1. As far as I know, there is no function library for printing purposes. So develop your own
PRINTER.LIB. The library should contain similar functions like
Set2GraphicsMode(), SetLineSpacingTo1by8(), etc. It is very easy to do so! No
programming skill is necessary! The only thing you need is Escape Codes.
2. Write a program that prints the given text in Braille characters. (Hint: You may need to
alter your dot-matrix printer. That is, you have to remove the ribbons, replace the existing
soft pins with hard pins. For programming, use graphics mode)
Part V
Mathematics & C
“Standing alone needs courage”
- Ramesh Krishnan
54
“Blessed are the meek.”
Implementing Math
Functions
For a quite long time, I was wondering how to implement mathematic functions like
sin(), cos(), log() etc. I knew the implementation of the easy functions like
IntergerPower( ):
But how to implement the functions like sin( ), cos( )? Let’s see!
Differentiation
The differentiation problem is actually a tangent problem or slope problem. Finding out
tangent for circle at a given point is very easy. But finding out tangent for a curve, which got
irregular slope is little bit difficult. So now we can define the
tangent as a line, which is drawn from a point to its nearest
y
point. That is dx should be very small.
tangent
The definition of derivation says
y=f(x)
lim f(x+ ∆x)- f(x)
f′(x)=
∆x →0 ∆x
55.1 Program x
#define x ( 3 )
#define dx ( 0.00000001 )
Integration
The basic problem of integral calculus is actually a problem of areas. We calculate the
area of graph between the points x = a and x = b.
In order to find out the area, the definition of
Integration suggests us to divide the entire area into pieces of
y
rectangles. When the width dx of the rectangle becomes
smaller and smaller, we get the area of a graph with good
y=f(x)
accuracy.
∫ F(x) dx
a a b x
56.1 Program
The following program finds out the integration of y = 4 – x2
#include <math.h>
a x
dx b
PI
π is an irrational number. To find out π with enough precision, many people have
contributed since 2000BC. Before the invention of computers, the calculation of π was really
hard. Even with the computers, the calculation of π is really a tough job. The problem with π is
that it is defined as the ratio between perimeter and diameter of a circle. The value of π is not
exactly 22/7, but it is approximately 22/7. And so you need more precision. First computer
calculation of π was carried on ENIAC (Electronic Numerical Integrator and Computer) at
Ballistic Research labs in September 1949. It took about 70 hours to calculate π to 2,037 decimal
places! It was programmed to use Machine’s formula π = 16arctan(1/5) – 4arctan(1/239). It took
almost 4000 years to find out π with good precision. Yes, in 1981AD only Kazunori Miyoshi and
Kazuhiko Nakayama in Japan calculated π to 20,00,000 decimal places. They used an efficient
portable program from the formula π = 32 arctan(1/10) – 4 arctan(1/239) – 16 arctan(1/515).
57.1 π
Officially accepted value of π to 3,200 decimal places is listed below. This listing would
be very useful, if you want to work on this research-oriented program!
57.2 Program
The following C program is one of the implementations to find π. Once someone else
provided me this program. I don’t know who is the real author of this program. On Pentium III
machine, it just took fraction of seconds to calculate π! I have compared the output of this
program with official-accepted value of π. This program gives right π value upto 3199 decimal
places; from 3200th decimal place onwards the accuracy is lost. Anyhow this is a good program!
#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>
void Shift( long far *l1, long far *l2, long lp, long lmod )
{
long k;
k = (*l2) > 0 ? (*l2) / lmod: -(-(*l2) / lmod) - 1;
*l2 -= k * lmod;
*l1 += k * lp;
} /*--Shift( )---------*/
wk += arr[(int)wk1];
arr[(int)wk1] = wk % 10;
wk /= 10;
}
}
arr[(int)(++loc)] = m;
} /*--XPrint( )---------*/
{
for( i = 0; ++i <= n - cnt; )
{
mf[i] *= 10;
ms[i] *= 10;
}
for( i = (int)(n - cnt + 1); --i >= 2; )
{
temp = 2 * i - 1;
Shift( &mf[i - 1], &mf[i], temp - 2, temp * kf );
Shift( &ms[i - 1], &ms[i], temp - 2, temp * ks );
}
nd = 0;
Shift( (long far *)&nd, &mf[1], 1L, 5L );
Shift( (long far *)&nd, &ms[1], 1L, 239L );
XPrint( nd );
}
printf( "\n\nCalculations Completed!\n" );
farfree( ms );
farfree( mf );
return(0);
} /*--main( )----------*/
58
“Whoever makes himself great will be made humble.”
Easter Day
Easter is one of the important celebrations for Christians. Easter day is always a Sunday.
So it is not celebrated on certain date like Christmas or New Year. In the Gregorian calendar, the
date of Easter is defined to occur on the Sunday following the ecclesiastical Full Moon that falls
on or next after March 21.
58.1 Oudin’s Algorithm
Oudin has developed an algorithm to find out the ‘Easter day’. Perhaps it is one of the
greatest ‘mysterious’ algorithms.
CORDIC
59.2 Advantages
This algorithm uses only minimal hardware (adder and shift) for computation of all
trigonometric and other function values. It consumes fewer resources than any other techniques
and so the performance is high. Thus, almost all scientific calculators use the CORDIC algorithm
in their calculations.
59.3 Principle
CORDIC works by rotating the coordinate system through constant angles until the angle
is reduced to zero. So with this principle we are changing the given angle each time to reduce to
zero. Here we are using addition, subtraction and shift to calculate the function values.
Now let us see, how we can calculate sine and cosine values using CORDIC. Consider a
vector C with coordinate (X, Y) that is to be rotated through an angle σ. The new coordinate
(X′,Y′) after rotation is
X′ X cos(σ) – Y sin(σ)
C′ = =
Y′ Y cos(σ) – X sin(σ)
590 A to Z of C
59.4 Algorithm
The steps for CORDIC algorithm are:
1. Get the angle and store it in Angle. Store the pre-calculated arctan values in
an array
2. Assign X = 0.607252935 (i.e., X=T), Y=0
3. Find X′ and Y′
4. If sign of Angle is positive then
X = X - Y′
Y = Y + X′
else ( If sign of Angle is negative )
X = X + Y′
Y = Y - X′
5. Repeat steps (3) and (4) till the Angle approaches 0
6. Print “Value of cos = ” X
7. Print “Value of sin = ” Y
8. Exit
A to Z of C 591
59.5 Program
Following is the program for calculating sine and cosine value for a given angle. In this
program the variable Angle holds the supplied angle (for which we have to find the cosine and
sine values). Arctans[i] holds the precalculated angle of arctan’s. Then in each iteration the
value of Arctans[i] is subtracted from or added to Angle according to the sign of the Angle
value. We can finish the iteration when Angle becomes 0 or to a nearer value (say, 0.00001). The
value of X and Y will also incremented or decremented according to Angle value.
After the completion of this program, cosine value will be stored in X and sine value will
be stored in Y for a given Angle.
#define T (0.60725293500888)
#define SIZE (50)
#define ZERO (0.00000001) /* approximation for zero */
#include <math.h>
int main( void )
{
int i = 0;
double X = T, Y = 0.0, Angle;
double dx, dy;
double Arctans[SIZE] =
{
45.0000000000000, 26.5650511770780, 14.0362434679265,
7.1250163489018, 3.5763343749974, 1.7899106082461,
0.8951737102111, 0.4476141708606, 0.2238105003685,
0.1119056770662, 0.0559528918938, 0.0279764526170,
0.0139882271423, 0.0069941136754, 0.0034970568507,
0.0017485284270, 0.0008742642137, 0.0004371321069,
0.0002185660534, 0.0001092830267, 0.0000546415134,
0.0000273207567, 0.0000136603783, 0.0000068301892,
0.0000034150946, 0.0000017075473, 0.0000008537736,
0.0000004268868, 0.0000002134434, 0.0000001067217,
0.0000000533609, 0.0000000266804, 0.0000000133402,
0.0000000066701, 0.0000000033351, 0.0000000016675,
0.0000000008338, 0.0000000004169, 0.0000000002084,
0.0000000001042, 0.0000000000521, 0.0000000000261,
0.0000000000130, 0.0000000000065, 0.0000000000033,
0.0000000000016, 0.0000000000008, 0.0000000000004,
0.0000000000002, 0.0000000000001
};
printf( "Enter the Angle : " );
scanf( "%lf", &Angle );
printf("I\tX\t\tY\t\tAngle\t\tPreCal arctan()\n");
592 A to Z of C
The value of cos(3) is stored in X and sin(3) is stored in Y. Thus, according to the
precision we use for T, the accuracy of the cosine and sine values can be increased or decreased.
60
“All who use swords will be killed with swords.”
Every programmer may have the knowledge about data compression. Data compression
is the process of reducing the size of the data file. One method of achieving this is by eliminating
redundant data. There are many other methods for data compression. In this chapter let’s see
LZW (Lempel Ziv Welch) algorithm. This algorithm is not much known to people as many books
on algorithms ignore this neat algorithm.
elements in the table. So when we store each element in the table it is to be converted to a 12-bit
number.
For example, when you want to store A(dec-65, hex -41), T(dec-84, hex-54),
O(dec-79, hex-4F) and Z(dec-90, hex-5A), you have to store it in bytes as 04, 10,
54, 04, F0, 5A . The reason is, we have allotted only 12-bits for each character.
Consider a string ‘ATOZOFC’. It takes 7x8(56) bits. Suppose if a code is assigned to it as
400(190h), it will take only 12-bits instead of 56-bits!
60.3.1 Compression Algorithm
1. Specify the number of bits to which you have to extend
2. read the first character from the file and store it in ch
3. repeat steps (4) to (7) till there is no character in the file
4. read the next character and store it in ch2
5. if ch+ch2 is in the table
get the code from the table
otherwise
output the code for ch+ch2
add to the table
6. Store it to the Output file in the specified number of bits
7. ch = ch2
8. output the last character ch
9. exit
60.3.2 Example
Input string: ATOZOFCATOZOFCATOZOFC
In this example-string, the first character ‘A’ is read and then the second character ‘T’.
Both the characters are concatenated as ‘AT’ and a code is assigned to it. The code is stored in the
Code table. Since this is the first string that is new to the table, it is assigned 256(100h). Then the
second and the third characters are concatenated to form another new string ‘TO’. This string is
also new to the Code table and the table expands to accommodate this new string and it is
assigned the next code 257(101h). Thus whenever a new string is read after concatenation it is
assigned a relevant code and the Code table is build. The table expands till the code reaches 4096
(since we have assigned 12-bits) or it reaches the end of file.
When the same set of characters that is stored in the table is again read it is assigned to
the code in the Code table. Thus according to the number of bits specified by the program the
output code is stored. In other words, if we have extended the bits from 8 to 12 then the
characters that is stored in 8-bits should be adjusted so as to store it in 12-bit format.
Here each byte is read one by one as hexadecimal code and 3 of the bytes are combined
so as to convert them from a 12-bit format to a 8-bit character (ASCII) format.
Thus the bytes 04, 10 & 84 are combined as 041084. The combined code is split to get
A(041) and T(084). The table is also built concurrently when each new string is read. When we
read 100, 102 etc., we can refer to the relevant code in the table and output the relevant code to
the file. For example, when we reach the 4th set of characters and read 04, 31 and 00 they must be
converted to 12-bit form as 043 and 100 will refer to the code in the table and outputs the string C
and AT respectively. Thus we can get all the characters without knowing the previous Code table.
Suggested Projects
1. Write your own compression utility using LZW algorithm.
61
“What comes out of a man is what makes him ‘unclean’.”
Backtracking Algorithms
Have you ever seen poor blind people walking in roads? If they find any obstacles in their
way, they would just move backward. Then they will proceed in other direction. How a blind
person could move backward when he finds obstacles? Simple answer…by intelligence!
Similarly, if a algorithm backtracks with intelligence, wonderful isn’t it?
61.2 Maze
Maze is an area surrounded by walls; in between you have a path from starting position to
ending position. We have to start from the starting point and travel towards the ending point
61.4 Program
The following program finds whether there is a path available between the two positions
in maze or not. Here I have used (1, 1) and (8, 10) as the two positions.
/*---------------------------------------------------------------------
Maze
by
K. Joseph Wesley
http://JosephWesley.itgo.com
---*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int cell[10][12] = {
{1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,1,1,1,1,1,1,1},
{1,1,0,0,0,1,1,1,0,1,1,1},
{1,1,0,1,0,0,0,0,0,1,1,1},
{1,1,0,1,0,0,0,0,0,1,1,1},
{1,1,0,1,0,0,0,0,0,0,1,1},
{1,1,0,1,0,0,0,0,0,0,0,1},
{1,1,0,0,0,0,0,0,0,0,0,1},
{1,1,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1}
};
void Traverse( BOOLEAN *pathavailable, int x1, int y1, int x2, int y2 )
{
if ( x1 == x2 && y1 == y2 )
*pathavailable = TRUE;
if( cell[x1][y1] == 0 )
{
cell[x1][y1] = 1;
Traverse( pathavailable, x1, y1+1, x2, y2 );
Traverse( pathavailable, x1+1, y1, x2, y2 );
Traverse( pathavailable, x1, y1-1, x2, y2 );
Traverse( pathavailable, x1-1, y1, x2, y2 );
}
} /*--Traverse( )------*/
Exercises
1. Find out other backtracking problems.
2. Solve 8 Queen problem.
Part VII
Illegal Codes
Important Notice
The purpose of Illegal codes is to provide the reader with the loopholes in
existing security measures. The main idea is to initiate the reader to develop
a good security mechanism. Hence the readers are requested not to use
these codes for illegal purposes.
62
“Whoever wants to be first must be slave to all.”
BIOS security system provides us two types of passwords mechanism namely: system
password and setup password. If your system has system password, you cannot use it, unless you
provide the right password. If your system has setup password, you need to provide the right
password to change the contents of CMOS setup.
Compaq Daytek
Compaq Daytec
Dell Hewlett-Packard
Dell Hewlpack
IBM Phoenix
IBM Phoenix
MBIUO
merlin
sertafu
Toshiba Zenith
Toshiba 3098z
toshy99 Zenith
A to Z of C 605
#include <dos.h>
606 A to Z of C
Note
For more security some smart BIOS vendors store BIOS data in NVRAM or SMM memory instead of storing
it in same CMOS location. In those cases, clearing BIOS passwords through program won’t work. But only
a few BIOS vendors do this!
63
“He who stands firm to the end will be saved.”
Cracking Techniques
char
Valid_Chars[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
" 1234567890!@#$%^&*()-=_+`~[]\\{}|;':\",./<>?";
if ( argc<2 )
{
printf( "Syntax: BRUTE <output file name> \n\a" );
exit(1);
}
A to Z of C 609
You would get about 90 thousand words! All the words are with length 2. So it is more
time consuming. You can add more for loops to get words of length other than 2. But it won’t be
an efficient implementation, you need to try another method. Optimized implementation of
generating words list using brute force technique is left to the reader as an exercise. You may
change the Valid_Chars table if you don’t require all the characters.
64.2 CrackIt
64.2.1 Logic
The following code is very popular among crackers. Let’s call it as CrackIt utility.
CrackIt uses dictionary attack technique. So you need to provide a Words list file that is
preloaded with all the passwords you suspect. For example, if you suspect that the password
would be any one of the words “KING”, ”QUEEN”, “JACK”, you have to load the Words list
file as:
KING
QUEEN
JACK
The CrackIt would first take the “KING” and it would check whether it is the right
password or not. If not, it would check “QUEEN” and if it is the right password, it would print it.
The validation of password is done with dictionary attack.
The encryption algorithm uses case sensitive passwords. So you have to load the Words
list file with enough words list. A clever idea is to use brute force for preparing words list that are
to be used in Words list file.
CrackIt has few drawbacks:
1. Success of the cracking depends upon the Words list file
2. Dictionary attack won’t be faster if you use large Word list
A to Z of C 611
#include <stdio.h>
int extra_field_length;
char file_name[1024];
int file_name_length;
int file_num;
int flags;
unsigned char header[3][12];
unsigned long key[3];
int num_enciphered;
char password[255];
char *password_ptr;
long signature;
unsigned char target[3];
int tem;
if ( argc < 3 )
{
printf( "Syntax: CRACKIT <zipfile> <wordslistfile> \a\n " );
exit(1);
}
/* Check for file errors....*/
if ( (zip_fp=fopen(argv[1], "rb")) == NULL )
{
printf( "Error: Couldn't open %s \a\n", argv[1] );
exit(1);
}
if ( (wordlist_fp=fopen(argv[2], "r") ) == NULL )
{
printf( "Error: Couldn't open %s \a\n", argv[2] );
exit(1);
}
/* <- checked ok */
if (num_enciphered == 0)
printf( "Nothing is enciphered in %s \n", argv[1] );
else if (num_enciphered < 3)
{
printf( "Less than 3 files are enciphered in %s \a\n",
argv[1] );
printf( "CRACKIT requires atleast 3 enciphered files \n" );
}
else /* Crack using wordlist....*/
{
found = FALSE;
byte_num = 0;
while (fgets(&password[0],255,wordlist_fp) != NULL)
{
password[strlen(&password[0])-1] = '\0';
tried_all = TRUE;
file_num = 0;
while (tried_all && (file_num<num_enciphered))
{
key[0] = 305419896L;
A to Z of C 615
key[1] = 591751049L;
key[2] = 878082192L;
password_ptr = &password[0];
while (*password_ptr != '\0')
{
byte = *(password_ptr++);
key[0] = CRC32( key[0], byte );
key[1] += key[0] & 0xff;
key[1] = key[1]*134775813L + 1;
key[2] = CRC32( key[2], key[1] >> 24);
}
for ( byte_num=0; byte_num < 12; ++byte_num )
{
tem = key[2] | 2;
byte = header[file_num][byte_num]
^(((tem*(tem^1)) >> 8) & 0xff);
key[0] = CRC32( key[0], byte );
key[1] += key[0] & 0xff;
key[1] = key[1]*134775813L + 1;
key[2] = CRC32( key[2], key[1] >> 24 );
}
if ( byte == target[file_num] )
++file_num;
else
tried_all = FALSE;
}
if ( tried_all )
{
if (!found)
{
found = TRUE;
printf( "Passwords migh be: \n" );
}
printf( "\t %s \n", &password[0] );
}
}
if (!found)
printf( "%s don't hold the right Password \a\n",
argv[2] );
fclose(wordlist_fp);
}
return(0);
} /*--main( )------*/
65
“Correction and punishment make children wise.”
Network Passwords
Novell Netware and Windows NT are the famous Network Operating Systems. Now,
Novell Netware is quite obsolete. This Network Operating System provides security to files of
each user in the network. So accessing another user’s file in network is restricted. In order to
access another user’s files, we need access privilege or his password.
Another innocent user will enter his user name and password as:
F:\LOGIN>LOGIN JACK
Enter your password: ****
Now the ‘fake prompt’ program will save the username and password in your area. And it
will restart the system. The innocent user may not be aware of the cause. The following code does
this:
#include <stdio.h>
#include <conio.h>
This method has got drawbacks. The user may not enter the right username and right
password always. Another thing is if somebody switches off the system, your ‘fake prompt’
program will no more be alive.
65.1.2 TSR program
Another technique preferred is to use a TSR program to trap the key press. Crackers
usually use a buffer with enough size (say 50), to store the key presses. The cracker will execute
the TSR program and will logoff. But the TSR program will still be active. The innocent user will
now login, his key presses will be trapped in a buffer. When the innocent user logoff or goes off,
the cracker will silently come and use his hot-key to see the trapped keys and so his password.
This method is better than the previous method because even if the innocent user enters wrong
user name or password, it silently traps them. The following code does this:
#include <dos.h>
char Key_String[SIZE],
Space_Bar = ' ',
Spl_Row[] = "!@#$%^&*()_+",
Spl_1 = '~',
Spl_2 = '|',
Q_Row[] = "qwertyuiop[]",
A_Row[] = "asdfghjkl;'",
Z_Row[] = "zxcvbnm,./",
Num_Row1[] = "789",
Num_Row2[] = "456",
Num_Row3[] = "123",
Num_Row4[] = "0.",
Enter_Symbol[] = " Ù";
char far *Vid_RAM;
int i=0, Key_Val, Last_Pos = 0;
void WriteCh2VidRAM( int vdupage, int x, int y, char ch, int attribute )
{
FP_SEG( Vid_RAM ) = 0xb800;
FP_OFF( Vid_RAM ) = 0x0000;
setvect( 9, MyInt9 );
keep( 0, 500 );
return(0);
} /*--main( )----*/
65.2 Windows NT
Windows NT’s passwords are stored in specific password database but in cryptic form. If
you know the hash values and have access to password database, it won’t be a tough job to crack
the passwords. Because of certain reasons, I avoid dealing the Windows NT password cracking.
Anyhow it is not a tough job for crackers.
66
“Stirring up anger causes trouble.”
I have already explained about file format. Each file got its own standards for storing the
contents. So for cracking or retrieving a particular type of file, we need to know its file format.
Few of the software vendors don’t document the file format that are used by their software. So to
know file format, we need to perform some illegal operations. We must understand the fact that
most of the software vendors use the file format that were proposed by some research scholars
and non-profit organizations.
66.1 DEBUG
Using DEBUG we can find which character is stored in which location. That is, in which
offset (distance) which character is stored can be viewed. To find that, you can even write your
own program!
66.3 Algorithms
Most of the software might use certain specific algorithms. Mostly these algorithms
might be documented. So you need to checkout different algorithms.
Virus Programming
Everybody is scared of computer ‘virus’ as it does harmful actions on our computer. But
when we look into the virus programming, we may certainly come out with the conclusion that it
requires intelligence to code a virus.
67.1 Logic
It is easy to mess-up the right program. For example, if you remove even a single byte
from an EXE file, that EXE file won’t be usable! Virus program don’t have any specific rules.
But it’s a common practice to include ‘signatures’ by virus creators. The main idea is to force the
innocent user to run the programs. So certain viruses come along with so called ‘programmer
utilities’ or ‘free tools’. Another thing is, it is easy to hang-up a working system using some ‘bad’
interrupts. Viruses use this logic too!
#include <dos.h>
int i = 1;
char far *Vid_RAM = (char far *)0xb8000000;
if ( i>4000 )
i = 1;
else
i += 2;
(*Int9)( );
} /*--interrupt MyInt9-----*/
int main(void)
{
Int9 = getvect( 9 );
setvect( 9, MyInt9 );
keep( 0, 500 );
return(0);
} /*--main( )----*/
#endif
67.4 Anti-Viruses
As I said earlier, many virus programmers add signature to their program. So by
checking the signature, we can find the name of the virus. Most of the anti-virus packages use this
logic! The following table shows few viruses and their signatures.
Virus Signature
Einstein 0042CD217231B96E0333D2B440CD2172193BC17515B80042
Phoenix 927 E800005E81C6????BF0001B90400F3A4E8
Spanz E800005E81EE????8D94????B41ACD21C784
Necropolis 50FCAD33C2AB8BD0E2F8
Trivial-25 B44EFEC6CD21B8??3DBA??00CD2193B440CD
Trivial-46 B44EB120BA????CD21BA????B80?3DCD21%2BA0001%4B440CD
SK CD20B80300CD1051E800005E83EE09
So you can find that writing anti-virus package is not a tough job. But understand the fact
that checking out the signature is not 100% foolproof. You may find many of the buggy anti-
virus packages even point out the right programs as virus programs and vice-versa.
Part VIII
Next Step
What do you think of C#?
I have no comments on C# as a language. It will take a lot to
persuade me that the world needs yet another proprietary language
(YAPL). It will be especially hard to persuade me that it needs a
language that is geared for a specific proprietary operating system…
—Bjarne Stroustrup, Creator of C++
Courtesy: Bjarne Stroustrup’s FAQ
68
“Rulers should not desire beer.”
32-bit Compiler
Today, 32-bit applications and Operating Systems have replaced the existing 16-bit
applications and Operating Systems. So people refer the 16-bit environment as obsolete!
CD . Allegro provides so many functions to access graphics cards and sound cards. So Allegro
reduces the programming effort by enormous amount. Following are the important features of
Allegro as by their documentations:
1. Supports VGA mode 13h, mode-X (twenty three tweaked VGA resolutions plus
unchained 640x400 Xtended mode), and SVGA modes with 8, 15, 16, 24, and
32 bit color depths, taking full advantage of VBE 2.0 linear framebuffers and the
VBE/AF hardware accelerator API if they are available.
2. Drawing functions including putpixel, getpixel, lines, rectangles, flat shaded,
gouraud shaded, and texture mapped polygons, circles, floodfill, bezier splines,
patterned fills, masked, run length encoded, and compiled sprites, blitting,
bitmap scaling and rotation, translucency/lighting, and text output with
proportional fonts. Supports clipping, and can draw directly to the screen or to
memory bitmaps of any size.
3. Hardware scrolling, mode-X split screens, and palette manipulation.
4. FLI/FLC animation player.
5. Plays background MIDI music and up to 64 simultaneous sound effects, and can
record sample waveforms and MIDI input. Samples can be looped (forwards,
backwards, or bidirectionally), and the volume, pan, pitch, etc, can be adjusted
while they are playing. The MIDI player responds to note on, note off, main
volume, pan, pitch bend, and program change messages, using the General MIDI
patch set and drum mappings. Currently supports Adlib, SB, SB Pro, SB16,
AWE32, MPU-401, ESS AudioDrive, Ensoniq Soundscape, and software
wavetable MIDI.
6. Easy access to the mouse, keyboard, joystick, and high resolution timer
interrupts, including a vertical retrace interrupt simulator.
7. Routines for reading and writing LZSS compressed files.
8. Multi-object data files and a grabber utility.
9. Math functions including fixed point arithmetic, lookup table trig, and 3d
vector/matrix manipulation.
10. GUI dialog manager and file selector.
69
“Speak up for those who cannot speak for themselves.”
Descendents of C
The development of C language has marked a wide difference in the computing world.
The grammar and structure of C language has influenced the development of other languages.
Many languages are claiming that they are ‘descendent’ of C. Let’s see some of those languages!
69.1 C++
C++ don’t differ much with C, except for its object-oriented concepts. In fact, C++ was
developed as ‘C with classes’. C++ claims that its codes are easily maintainable and readable than
C codes. But in reality, it is not much true. As C++ supports both procedural and object-oriented
approach, it may lead to more complexity when programmer uses both procedural and object
oriented approach in his program.
69.2 Java
Java is a pure object-oriented language. Java was introduced as a language for Embedded
applications, later it is known to be ‘internet-language’. Java claims that it is multi-platform. But
certain people argue that Java is not exactly multi-platform, because there are platforms for which
no JVM emulator is available, and we cannot run Java programs on those platforms.
69.3 C#
C# is a product from Microsoft. People say that C# will be a good rival for SUN’s Java.
But it is a proprietary language.
69.4 D
D language claims that it is the right descendent of C language. I don’t know much about
D language. But I am sure that it is still used by certain people.
Part IX
Smart Dictionary
People are often unreasonable, illogical
And self-centred,
Forgive them anyway.
Programmers often use certain uncommon words. To get into the world of programming,
we must also know these jargons. In CD you have so many utilities and source codes by
many International programmers. So to cope up with the programming world, we are supposed to
know these jargons and slang.
Slang Meaning
Shit [used to express disbelief,
disappointment, imitation etc.]
Darn / Dern / Durn ,,
Darn it / Dern it / Durn it ,,
Damn ,,
Heck / Hell ,,
Fuck ,, (More offensive)
Oops! [used to express surprise,
apology, etc.]
the hell actually, really
the heck ,,
the fuck ,,
beat it leave, depart
fuck off ,,
bitch unrespectable woman, prostitute
it sucks it fails
screw up spoil
bullshit nonsense
mess up to disarrange, to get into trouble
aka Also known as (pronounced as
separate letters a-k-a)
Happy hacking farewell
patch quick fix to the bug in a program
tweak adjust or refine a program
twiddle small change in a program
netiquette etiquette that should be followed
over the net
Pronounciation
Linux li-nucks
GUI goo-ee
GNU gu-new
634 A to Z of C
Pronounciation
URL earl
Bjarne Stroustrup bi-yaa-ne stroov-strup
71.1 Notations
To save spaces, RBIL (Ralf Brown’s Interrupt List) uses few notations. So we have to
understand those notations before using RBIL.
INT 4E - TI/Z100
INT 4F - Common Access Method SCSI
INT 50 - IRQ0 relocated by software
INT 51 - IRQ1 relocated by software
INT 52 - IRQ2 relocated by software
INT 53 - IRQ3 relocated by software
INT 54 - IRQ4 relocated by software
INT 55 - IRQ5 relocated by software
INT 56 - IRQ6 relocated by software
INT 57 - IRQ7 relocated by software
INT 58 - IRQ8/0 relocated by software
INT 59 - IRQ9/1 relocated by software; GSS Computer Graphics Interface
INT 5A - IRQ10/2 relocated by software
INT 5B - IRQ11/3 relocated by software; Network
INT 5C - IRQ12/4 relocated by software; Network Interface
INT 5D - IRQ13/5 relocated by software
INT 5E - IRQ14/6 relocated by software
INT 5F - IRQ15/7 relocated by software; HP 95LX GRAPHICS PRIMITIVES
INT 60 - reserved for user interrupt; multiple purposes
INT 61 - reserved for user interrupt; multiple purposes
INT 62 - reserved for user interrupt; multiple purposes
INT 63 - reserved for user interrupt; multiple purposes
INT 64 - reserved for user interrupt; multiple purposes
INT 65 - reserved for user interrupt; multiple purposes
INT 66 - reserved for user interrupt; multiple purposes
INT 67 - reserved for user interrupt; LIM EMS; multiple purposes
INT 68 - multiple purposes
INT 69 - multiple purposes
INT 6A - multiple purposes
INT 6B - multiple purposes
INT 6C - CONVERTIBLE; DOS 3.2; DECnet DOS network scheduler
INT 6D - VGA - internal
INT 6E - DECnet DOS - DECnet NETWORK PROCESS API
INT 6F - Novell NetWare; 10NET; MS Windows 3.0
INT 70 - IRQ8 - CMOS REAL-TIME CLOCK
INT 71 - IRQ9 - REDIRECTED TO INT 0A BY BIOS
INT 72 - IRQ10 - RESERVED
INT 73 - IRQ11 - RESERVED
INT 74 - IRQ12 - POINTING DEVICE (PS)
INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION (AT and up)
INT 76 - IRQ14 - HARD DISK CONTROLLER (AT and later)
INT 77 - IRQ15 - RESERVED (AT,PS); POWER CONSERVATION (Compaq)
INT 78 - DOS extenders; multiple purposes
INT 79 - multiple purposes
INT 7A - Novell NetWare; IBM 3270; multiple purposes
INT 7B - multiple purposes
INT 7C - multiple purposes
INT 7D - multiple purposes
640 A to Z of C
71.2.2 Listing
Because of space constraint, here I provide only a few interrupts that I use much. The
reader is however suggested to check out the CD for complete information. As everyone
should be aware of the RBIL format, I present here without formatting it!
(Table 00006)
Values for keyboard make/break (scan) code:
01h Esc 31h N
02h 1! 32h M
03h 2@ 33h ,< 63h F16
04h 3# 34h .> 64h F17
05h 4$ 35h /? 65h F18
06h 5% 36h Right Shift 66h F19
07h 6^ 37h Grey* 67h F20
08h 7& 38h Alt 68h F21 (Fn) [*]
09h 8* 39h SpaceBar 69h F22
0Ah 9( 3Ah CapsLock 6Ah F23
0Bh 0) 3Bh F1 6Bh F24
0Ch -_ 3Ch F2 6Ch --
0Dh =+ 3Dh F3 6Dh EraseEOF
0Eh Backspace 3Eh F4
0Fh Tab 3Fh F5 6Fh Copy/Play
10h Q 40h F6
11h W 41h F7
12h E 42h F8 72h CrSel
13h R 43h F9 73h <delta> [*]
14h T 44h F10 74h ExSel
15h Y 45h NumLock 75h --
16h U 46h ScrollLock 76h Clear
17h I 47h Home 77h [Note2] Joyst But1
18h O 48h UpArrow 78h [Note2] Joyst But2
19h P 49h PgUp 79h [Note2] Joyst Right
1Ah [{ 4Ah Grey- 7Ah [Note2] Joyst Left
1Bh ]} 4Bh LeftArrow 7Bh [Note2] Joyst Up
1Ch Enter 4Ch Keypad 5 7Ch [Note2] Joyst Down
1Dh Ctrl 4Dh RightArrow 7Dh [Note2] right mouse
1Eh A 4Eh Grey+ 7Eh [Note2] left mouse
1Fh S 4Fh End
20h D 50h DownArrow
646 A to Z of C
the "Preh Commander AT" keyboard with additional F11-F22 keys treats
F11-F20 as Shift-F1..Shift-F10 and F21/F22 as Ctrl-F1/Ctrl-F2; the
Eagle PC-2 keyboard with F11-F24 keys treated those additional keys
in the same way
[Note1] the "Cherry G80-0777" keyboard has additional F11-F15 keys
which generate make codes 55h-59h; some other extended keyboards
generate codes 55h and 56h for F11 and F12, which cannot be managed
by standard DOS keyboard drivers
[Note2] the Schneider/Amstrad PC1512 PC keyboards contain extra keys,
a mouse, and a digital joystick, which are handled like extra keys.
The joystick's motion scancodes are converted into standard arrow
keys by the BIOS, and the joystick and mouse button scan codes are
converted to FFFFh codes in the BIOS keyboard buffer
(see CMOS 15h"AMSTRAD").
In addition to the keys listed in the table above, there are
Del-> (delete forward) 70h
Enter 74h
SeeAlso: #00602 at INT 16/AX=6F07h,#03214 at INT 4A/AH=05h
--------H-0A---------------------------------
INT 0A - IRQ2 - ROLAND MPU MIDI INTERFACE
Note: newer Roland cards and MIDI interfaces by other manufacturers use
a jumper-selectable IRQ, but software and hardware generally defaults
to IRQ2
SeeAlso: INT 52"DESQview",INT 5A"DoubleDOS",INT 71,INT 7A"GO32"
--------V-1000-------------------------------
INT 10 - VIDEO - SET VIDEO MODE
AH = 00h
AL = desired video mode (see #00010)
Return: AL = video mode flag (Phoenix, AMI BIOS)
20h mode > 7
30h modes 0-5 and 7
3Fh mode 6
AL = CRT controller mode byte (Phoenix 386 BIOS v1.10)
Desc: specify the display mode for the currently active display adapter
--------V-1001-------------------------------
INT 10 - VIDEO - SET TEXT-MODE CURSOR SHAPE
AH = 01h
CH = cursor start and options (see #00013)
CL = bottom scan line containing cursor (bits 0-4)
Return: nothing
Desc: specify the starting and ending scan lines to be occupied by the
hardware cursor in text modes
Notes: buggy on EGA systems--BIOS remaps cursor shape in 43 line modes, but
returns unmapped cursor shape
UltraVision scales size to the current font height by assuming 14-line
monochrome and 8-line color fonts; this call is not valid if cursor
emulation has been disabled
applications which wish to change the cursor by programming the
648 A to Z of C
(Table 00015)
A to Z of C 651
Note: the supported font sizes are 8x16 single-byte, 8x19 single-byte,
16x16 double-byte, and 24x24 double-byte
SeeAlso: AH=19h,INT 16/AH=14h
--------V-101E08-----------------------------
INT 10 - VIDEO - FLAT-PANEL - CONTRAST SETTING
AX = 1E08h
BH = function
bit 7: =1 set contrast control, =0 query contrast
bit 6: use standard contrast
bits 5-0: reserved (0)
---if BH bits 7,6=10---
BL = contrast (00h = minimum, FFh = maximum)
Return: AL = 1Eh if function supported
BH = results
bit 7: query/set (copied from input)
bit 6: standard/custom (copied from input)
bits 5-2: reserved (0)
bit 1: software contrast control is supported
bit 0: set operation was succesful (always clear on get)
BL = contrast (00h = minimum, FFh = maximum)
Note: this function operates independently of AX=1E06h
SeeAlso: AX=1E00h,AX=1E06h,AX=1E07h
--------V-104F00-----------------------------
INT 10 - VESA SuperVGA BIOS (VBE) - GET SuperVGA INFORMATION
AX = 4F00h
ES:DI -> buffer for SuperVGA information (see #00077)
Return: AL = 4Fh if function supported
AH = status
00h successful
ES:DI buffer filled
01h failed
---VBE v2.0---
02h function not supported by current hardware configuration
03h function invalid in current video mode
Desc: determine whether VESA BIOS extensions are present and the capabilities
supported by the display adapter
SeeAlso: AX=4E00h,AX=4F01h,AX=7F00h"SOLLEX",AX=A00Ch
Index: installation check;VESA SuperVGA
0Eh DWORD pointer to list of supported VESA and OEM video modes
(list of words terminated with FFFFh)
12h WORD total amount of video memory in 64K blocks
---VBE v1.x ---
14h 236 BYTEs reserved
---VBE v2.0 ---
14h WORD OEM software version (BCD, high byte = major, low byte = minor)
16h DWORD pointer to vendor name
1Ah DWORD pointer to product name
1Eh DWORD pointer to product revision string
22h WORD (if capabilities bit 3 set) VBE/AF version (BCD)
0100h for v1.0P
24h DWORD (if capabilities bit 3 set) pointer to list of supported
accelerated video modes (list of words terminated with FFFFh)
28h 216 BYTEs reserved for VBE implementation
100h 256 BYTEs OEM scratchpad (for OEM strings, etc.)
Notes: the list of supported video modes is stored in the reserved portion of
the SuperVGA information record by some implementations, and it may
thus be necessary to either copy the mode list or use a different
buffer for all subsequent VESA calls
not all of the video modes in the list of mode numbers may be
supported, e.g. if they require more memory than currently installed
or are not supported by the attached monitor. Check any mode you
intend to use through AX=4F01h first.
the 1.1 VESA document specifies 242 reserved bytes at the end, so the
buffer should be 262 bytes to ensure that it is not overrun; for
v2.0, the buffer should be 512 bytes
the S3 specific video modes will most likely follow the FFFFh
terminator at the end of the standard modes. A search must then
be made to find them, FFFFh will also terminate this second list
in some cases, only a "stub" VBE may be present, supporting only
AX=4F00h; this case may be assumed if the list of supported video
modes is empty (consisting of a single word of FFFFh)
(Table 00082)
Values for VESA SuperVGA memory model type:
00h text
01h CGA graphics
02h HGC graphics
03h 16-color (EGA) graphics
04h packed pixel graphics
05h "sequ 256" (non-chain 4) graphics
06h direct color (HiColor, 24-bit color)
07h YUV (luminance-chrominance, also called YIQ)
08h-0Fh reserved for VESA
10h-FFh OEM memory models
SeeAlso: #00079
--------V-104F02-----------------------------
INT 10 - VESA SuperVGA BIOS - SET SuperVGA VIDEO MODE
AX = 4F02h
BX = new video mode (see #04082,#00083,#00084)
ES:DI -> (VBE 3.0+) CRTC information block, bit mode bit 11 set
(see #04083)
Return: AL = 4Fh if function supported
AH = status
00h successful
01h failed
Notes: bit 13 may only be set if the video mode is present in the list of
accelerated video modes returned by AX=4F00h
if the DAC supports both 8 bits per primary color and 6 bits, it will
be reset to 6 bits after a mode set; use AX=4F08h to restore 8 bits
SeeAlso: AX=4E03h,AX=4F00h,AX=4F01h,AX=4F03h,AX=4F08h
(Table 00083)
Values for VESA video mode:
658 A to Z of C
(Table 00084)
Values for S3 OEM video mode:
201h 640x480x256
A to Z of C 659
202h 800x600x16
203h 800x600x256
204h 1024x768x16
205h 1024x768x256
206h 1280x960x16
207h 1152x864x256 (Diamond Stealth 64)
208h 1280x1024x16
209h 1152x864x32K
20Ah 1152x864x64K (Diamond Stealth 64)
20Bh 1152x864x4G
211h 640x480x64K (Diamond Stealth 24)
211h 640x400x4G (Diamond Stealth64 Video / Stealth64 Graphics)
212h 640x480x16M (Diamond Stealth 24)
301h 640x480x32K
Note: these modes are only available on video cards using S3's VESA driver
SeeAlso: #00083,#00191,#00732 at INT 1A/AX=B102h
Index: video modes;S3
(Table 00115)
Values for Sample data size:
01h 8bit play
02h 16bit play
10h 8bit record
20h 16bit record
(Table 00117)
Values for type of compression:
01h IMA play
02h ALAW play
03h ULAW play
11h IMA record
12h ALAW record
13h ULAW record
(Table 00121)
Values for MIDI Registered Patch Types:
10h OPL2
11h OPL3
cylinder number reportedly is three less than the count in the fixed
disk parameter table.
for BIOSes which reserve the last cylinder for testing purposes, the
cylinder count is automatically decremented
on PS/1s with IBM ROM DOS 4, nonexistent drives return CF clear,
BX=CX=0000h, and ES:DI = 0000h:0000h
machines with lost CMOS memory may return invalid data for floppy
drives. In this situation CF is cleared, but AX,BX,CX,DX,DH,DI,
and ES contain only 0. At least under some circumstances, MS-DOS/
PC DOS IO.SYS/IBMBIO.COM just assumes a 360 KB floppy if it sees
CH to be zero for a floppy.
the PC-Tools PCFORMAT program requires that AL=00h before it will
proceed with the formatting
if this function fails, an alternative way to retrieve the number
of floppy drives installed in the system is to call INT 11h.
In fact, the MS-DOS/PC-DOS IO.SYS/IBMBIO.COM attempts to get the
number of floppy drives installed from INT 13/AH=08h, when INT 11h
AX bit 0 indicates there are no floppy drives installed. In addition
to testing the CF flag, it only trusts the result when the number of
sectors (CL preset to zero) is non-zero after the call.
BUGS: several different Compaq BIOSes incorrectly report high-numbered
drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
same geometry as drive 80h; as a workaround, scan through disk
numbers, stopping as soon as the number of valid drives encountered
equals the value in 0040h:0075h
a bug in Leading Edge 8088 BIOS 3.10 causes the DI,SI,BP,DS, and ES
registers to be destroyed
some Toshiba BIOSes (at least before 1995, maybe some laptops???
with 1.44 MB floppies) have a bug where they do not set the ES:DI
vector even for floppy drives. Hence these registers should be
preset with zero before the call and checked to be non-zero on
return before using them. Also it seems these BIOSes can return
wrong info in BL and CX, as S/DOS 1.0 can be configured to preset
these registers as for an 1.44 MB floppy.
the PS/2 Model 30 fails to reset the bus after INT 13/AH=08h and
INT 13/AH=15h. A workaround is to monitor for these functions
and perform a transparent INT 13/AH=01h status read afterwards.
This will reset the bus. The MS-DOS 6.0 IO.SYS takes care of
this by installing a special INT 13h interceptor for this purpose.
AD-DOS may leave interrupts disabled on return from this function.
Some Microsoft software explicitly sets STI after return.
SeeAlso: AH=06h"Adaptec",AH=13h"SyQuest",AH=48h,AH=15h,INT 1E
SeeAlso: INT 41"HARD DISK 0"
(Table 00242)
Values for diskette drive type:
01h 360K
02h 1.2M
A to Z of C 669
03h 720K
04h 1.44M
05h ??? (reportedly an obscure drive type shipped on some IBM machines)
2.88M on some machines (at least AMI 486 BIOS)
06h 2.88M
10h ATAPI Removable Media Device
--------b-1584-------------------------------
INT 15 - V20-XT-BIOS - JOYSTICK SUPPORT
AH = 84h
DX = subfunction
0000h read joystick switches
Return: AL bits 7-4 = switch settings
other: read positions of joysticks as indicated by bits 0-3
Return: AX = X position of joystick A (if DX bit 0 set)
BX = Y position of joystick A (if DX bit 1 set)
CX = X position of joystick B (if DX bit 2 set)
DX = Y position of joystick B (if DX bit 3 set)
Return: CF set on error
AH = status (see #00496)
CF clear if successful
Program: V20-XT-BIOS is a ROM BIOS replacement with extensions by Peter
Koehlmann / c't magazine
SeeAlso: AH=84h"PS",INT 10/AH=0Eh/CX=ABCDh
--------B-1B---------------------------------
INT 1B C - KEYBOARD - CONTROL-BREAK HANDLER
Desc: this interrupt is automatically called when INT 09 determines that
Control-Break has been pressed
Note: normally points to a short routine in DOS which sets the Ctrl-C flag,
thus invoking INT 23h the next time DOS checks for Ctrl-C.
SeeAlso: INT 23,MEM 0040h:0071h
--------B-1C---------------------------------
INT 1C - TIME - SYSTEM TIMER TICK
Desc: this interrupt is automatically called on each clock tick by the INT 08
handler
Notes: this is the preferred interrupt to chain when a program needs to be
invoked regularly
not available on NEC 9800-series PCs
SeeAlso: INT 08,INT E2"PC Cluster"
--------D-2100-------------------------------
INT 21 - DOS 1+ - TERMINATE PROGRAM
AH = 00h
CS = PSP segment
Notes: Microsoft recommends using INT 21/AH=4Ch for DOS 2+
this function sets the program's return code (ERRORLEVEL) to 00h
execution continues at the address stored in INT 22 after DOS performs
whatever cleanup it needs to do (restoring the INT 22,INT 23,INT 24
vectors from the PSP assumed to be located at offset 0000h in the
segment indicated by the stack copy of CS, etc.)
670 A to Z of C
if the PSP is its own parent, the process's memory is not freed; if
INT 22 additionally points into the terminating program, the
process is effectively NOT terminated
not supported by MS Windows 3.0 DOSX.EXE DOS extender
SeeAlso: AH=26h,AH=31h,AH=4Ch,INT 20,INT 22
--------D-2101-------------------------------
INT 21 - DOS 1+ - READ CHARACTER FROM STANDARD INPUT, WITH ECHO
AH = 01h
Return: AL = character read
Notes: ^C/^Break are checked, and INT 23 executed if read
^P toggles the DOS-internal echo-to-printer flag
^Z is not interpreted, thus not causing an EOF if input is redirected
character is echoed to standard output
standard input is always the keyboard and standard output the screen
under DOS 1.x, but they may be redirected under DOS 2+
SeeAlso: AH=06h,AH=07h,AH=08h,AH=0Ah
--------v-21010F-----------------------------
INT 21 - VIRUS - "Susan" - INSTALLATION CHECK
AX = 010Fh
Return: AX = 7553h ("Su") if resident
SeeAlso: INT 16/AH=DDh"VIRUS",INT 21/AX=0B56h
--------D-2102-------------------------------
INT 21 - DOS 1+ - WRITE CHARACTER TO STANDARD OUTPUT
AH = 02h
DL = character to write
Return: AL = last character output (despite the official docs which state
nothing is returned) (at least DOS 2.1-7.0)
Notes: ^C/^Break are checked, and INT 23 executed if pressed
standard output is always the screen under DOS 1.x, but may be
redirected under DOS 2+
the last character output will be the character in DL unless DL=09h
on entry, in which case AL=20h as tabs are expanded to blanks
if standard output is redirected to a file, no error checks (write-
protected, full media, etc.) are performed
SeeAlso: AH=06h,AH=09h
--------D-2103-------------------------------
INT 21 - DOS 1+ - READ CHARACTER FROM STDAUX
AH = 03h
Return: AL = character read
Notes: keyboard checked for ^C/^Break, and INT 23 executed if detected
STDAUX is usually the first serial port
SeeAlso: AH=04h,INT 14/AH=02h,INT E0/CL=03h
--------D-2104-------------------------------
INT 21 - DOS 1+ - WRITE CHARACTER TO STDAUX
AH = 04h
DL = character to write
Notes: keyboard checked for ^C/^Break, and INT 23 executed if detected
STDAUX is usually the first serial port
A to Z of C 671
--------D-215D0B-----------------------------
INT 21 OU - DOS 4.x only - internal - GET DOS SWAPPABLE DATA AREAS
AX = 5D0Bh
Return: CF set on error
AX = error code (see #01680)
CF clear if successful
DS:SI -> swappable data area list (see #01689)
Notes: copying and restoring the swappable data areas allows DOS to be
reentered unless it is in a critical section delimited by calls to
INT 2A/AH=80h and INT 2A/AH=81h,82h
SHARE and other DOS utilities consult the byte at offset 04h in the
DOS data segment (see INT 2F/AX=1203h) to determine the SDA format
in use: 00h = DOS 3.x, 01h = DOS 4.0-6.0, other = error.
DOS 5+ use the SDA format listed below, but revert back to the DOS 3.x
call for finding the SDA (see #01687); Novell DOS 7 does not support
this function, either.
SeeAlso: AX=5D06h,INT 2A/AH=80h,INT 2A/AH=81h,INT 2A/AH=82h,INT 2F/AX=1203h
72h 14 BYTEs device driver request header for disk status check (also
includes following eight bytes for some calls)
80h DWORD pointer to device I/O buffer
84h WORD part of request header at 72h
86h WORD part of request header at 72h (0)
88h BYTE type of PSP copy (00h=simple for INT 21/AH=26h, FFh=make child)
89h DWORD start offset of file region to lock/unlock
8Dh DWORD length of file region to lock/unlock
91h BYTE padding (unused)
92h 3 BYTEs 24-bit user number (see AH=30h)
95h BYTE OEM number (see #01394 at AH=30h)
96h 6 BYTEs CLOCK$ transfer record (see #01688 at AX=5D06h)
9Ch BYTE device I/O buffer for single-byte I/O functions
9Dh BYTE padding
9Eh 128 BYTEs buffer for filename
11Eh 128 BYTEs buffer for filename (rename destination name)
19Eh 21 BYTEs findfirst/findnext search data block (see #01626 at AH=4Eh)
1B3h 32 BYTEs directory entry for found file (see #01394 at AH=11h)
1D3h 88 BYTEs copy of current directory structure for drive being accessed
22Bh 11 BYTEs FCB-format filename for device name comparison
236h BYTE terminating NUL for above filename
237h 11 BYTEs wildcard destination specification for rename (FCB format)
242h BYTE terminating NUL for above filespec
243h BYTE padding???
244h WORD destination starting sector (cluster???)
246h 5 BYTEs extra space to allow a directory entry to be stored starting
at offset 22Bh
24Bh BYTE extended FCB file attributes
24Ch BYTE type of FCB (00h regular, FFh extended)
24Dh BYTE directory search attributes
24Eh BYTE file open/access mode
24Fh BYTE flag: nonzero if file was deleted
250h BYTE flag: device name found on rename, or file not found
251h BYTE flag: splice file name and directory name together
252h BYTE flag indicating how DOS function was invoked
(00h = direct INT 20/INT 21, FFh = server call AX=5D00h)
253h BYTE sector position within cluster
254h BYTE flag: translating sector/cluster
255h BYTE flag: 00h if read, 01h if write
256h BYTE current working drive number
257h BYTE cluster factor
258h BYTE "sda_CLUSSPLIT" flag: cluster split between two FAT sectors
259h BYTE line edit (AH=0Ah) insert mode flag (nonzero = on)
25Ah BYTE canonicalized filename referred to existing file/dir if FFh
25Bh BYTE volume ID flag
25Ch BYTE type of process termination (00h-03h) (see AH=4Dh)
25Dh BYTE unused (padding for alignment)
25Eh BYTE file create flag (00h = no, search only)
676 A to Z of C
25Fh BYTE value for deleted file's first byte: 00h to delete all, else E5
260h DWORD pointer to Drive Parameter Block for critical error invocation
264h DWORD pointer to stack frame containing user registers on INT 21
268h WORD stores SP across INT 24
26Ah DWORD pointer to DOS Drive Parameter Block for ???
26Eh WORD segment of disk buffer
270h DWORD saving partial cluster number
274h WORD "sda_PREREAD" 00h if preread, 01h if optional
276h WORD temporary used in allocating disk space
278h BYTE Media ID byte returned by AH=1Bh,1Ch
279h BYTE unused
27Ah DWORD pointer to device header if filename is character device
27Eh DWORD pointer to current SFT
282h DWORD pointer to current directory structure for drive being accessed
286h DWORD pointer to caller's FCB
28Ah WORD SFT index to which file being opened will refer
28Ch WORD temporary storage for file handle
28Eh DWORD pointer to JFT entry (for file being opened) in process handle
table (see #01378 at AH=26h)
292h WORD "sda_WFP_START" offset in DOS DS of first filename argument
294h WORD "sda_REN_WFP" offset in DOS DS of second filename argument
296h WORD offset of last component in pathname or FFFFh
298h WORD offset of transfer address to add
29Ah WORD last relative cluster within file being accessed
29Ch WORD temp: absolute cluster number being accessed
29Eh DWORD directory sector number
2A2h WORD directory cluster number
2A4h DWORD current relative sector number within file
2A8h DWORD current sector number (number of previously written sectors)
2ACh WORD current byte offset within sector
2AEh DWORD current offset in file
2B2h WORD number of bytes in first sector
2B4h WORD bytes in partial last sector
2B6h WORD number of whole sectors
2B8h WORD free file cluster entry
2BAh WORD last file cluster entry
2BCh WORD next file cluster number
2BEh DWORD number of bytes appended to file
2C2h DWORD pointer to current work disk buffer
2C6h DWORD pointer to working SFT
2CAh WORD used by INT 21 dispatcher to store caller's BX
2CCh WORD used by INT 21 dispatcher to store caller's DS
2CEh WORD temporary storage while saving/restoring caller's registers
2D0h DWORD pointer to prev call frame (offset 264h) if INT 21 reentered
also switched to for duration of INT 24
2D4h WORD open mode/action for INT 21/AX=6C00h
2D6h BYTE extended open conditional flag
set to 00h by INT 21h dispatcher, 02h when a read is
A to Z of C 677
--------D-2171-------------------------------
INT 21 - Windows95 - LONG FILENAME FUNCTIONS
AH = 71h
AL = function
0Dh reset drive (see AX=710Dh)
39h create directory (see AX=7139h)
3Ah remove directory (see AX=713Ah)
3Bh set current directory (see AX=713Bh)
41h delete file (see AX=7141h)
43h get/set file attributes (see AX=7143h)
47h get current directory (see AX=7147h)
4Eh find first file (see AX=714Eh)
4Fh find next file (see AX=714Fh)
56h move (rename) file (see AX=7156h)
60h truename (see AX=7160h/CL=00h,AX=7160h/CL=02h)
6Ch create/open file (see AX=716Ch)
A0h get volume information (see AX=71A0h)
A1h terminate FindFirst/FindNext (see AX=71A1h)
A6h get file information (see AX=71A6h)
A7h time conversion (see AX=71A7h/BL=00h,AX=71A7h/BL=01h)
A8h generate short filename (see AX=71A8h)
A9h server create/open file (see AX=71A9h)
AAh create/terminate SUBST (see AX=71AAh/BH=00h,AX=71AAh/BH=02h)
Return: CF set on error
AX = error code (see #01680)
A to Z of C 679
(Table 03169)
Values for mouse button number:
0000h left
0001h right
0002h middle (Mouse Systems/Logitech/Genius mouse)
--------M-330006-----------------------------
INT 33 - MS MOUSE v1.0+ - RETURN BUTTON RELEASE DATA
AX = 0006h
BX = button number (see #03169)
684 A to Z of C
AX = 000Ah
BX = hardware/software text cursor
0000h software
CX = screen mask
DX = cursor mask
0001h hardware
CX = start scan line
DX = end scan line
Note: when the software cursor is selected, the character/attribute data at
the current screen position is ANDed with the screen mask and then
XORed with the cursor mask
SeeAlso: AX=0009h,INT 62/AX=007Eh
--------M-33000B-----------------------------
INT 33 - MS MOUSE v1.0+ - READ MOTION COUNTERS
AX = 000Bh
Return: CX = number of mickeys mouse moved horizontally since last call
DX = number of mickeys mouse moved vertically
Notes: a mickey is the smallest increment the mouse can sense
positive values indicate down/right
SeeAlso: AX=0003h,AX=001Bh,AX=0027h
--------M-33000C-----------------------------
INT 33 - MS MOUSE v1.0+ - DEFINE INTERRUPT SUBROUTINE PARAMETERS
AX = 000Ch
CX = call mask (see #03171)
ES:DX -> FAR routine (see #03172)
SeeAlso: AX=0018h
(Table 03172)
Values interrupt routine is called with:
AX = condition mask (same bit assignments as call mask)
BX = button state
CX = cursor column
DX = cursor row
SI = horizontal mickey count
DI = vertical mickey count
686 A to Z of C
PPPPw RW description
where: PPPP is the four-digit hex port number or a plus sign and three hex
digits to indicate an offset from a base port address
w is blank for byte-size port, 'w' for word, and 'd' for dword
R is dash (or blank) if not readable, 'r' if sometimes readable,
'R' if "always" readable, '?' if readability unknown
W is dash (or blank) if not writable, 'w' if sometimes writable,
A to Z of C 687
71.3.2 Listing
----------P0000001F--------------------------
PORT 0000-001F - DMA 1 - FIRST DIRECT MEMORY ACCESS CONTROLLER (8237)
SeeAlso: PORT 0080h-008Fh"DMA",PORT 00C0h-00DFh
followed by the high byte. In 8254 read back modes, all selected
counters and status are latched and must be read out completely
before normal operation is valid again. Each counter switches back
to normal operation after read out. In 'get status and counter'
mode the first byte read is the status, followed by one or two
counter values. (see #P0379) Note that 16-bit reads performed
without using the "latch" command will get the current high/low
portion of the counter at the instant of the port read, so it is
possible for the low part of the counter to wrap around before the
high part gets read, resulting in a significant measurement error
0043 RW PIT mode port, control word register for counters 0-2 (see #P0380)
Once a control word has been written (43h), it must be followed
immediately by performing the corresponding action to the counter
registers (40h-42h), else the system may hang!!
(Table P0409)
Values for Real-Time Clock register number (see also CMOS.LST):
00h-0Dh clock registers
0Eh diagnostics status byte
0Fh shutdown status byte
10h diskette drive type for A: and B:
11h reserved / IBM fixed disk / setup options
12h fixed disk drive type for drive 0 and drive 1
13h reserved / AMI Extended CMOS setup (AMI Hi-Flex BIOS)
14h equipment byte
15h LSB of system base memory in Kb
16h MSB of system base memory in Kb
17h LSB of total extended memory in Kb
18h MSB of total extended memory in Kb
19h drive C extension byte
1Ah drive D extension byte
1Bh-2Dh reserved
20h-27h commonly used for first user-configurable drive type
2Eh CMOS MSB checksum over 10-2D
2Fh CMOS LSB checksum over 10-2D
30h LSB of extended memory found above 1Mb at POST
31h MSB of extended memory found above 1Mb at POST
32h date century in BCD
33h information flags
34h-3Fh reserved
35h-3Ch commonly used for second user-configurable drive type
3Dh-3Eh word to 82335 MCR memory config register at [22] (Phoenix)
42h-4Ch AMI 1990 Hyundai super-NB368S notebook
???
54h-57h AMI 1990 Hyundai super-NB368S notebook
???
5Ch-5Dh AMI 1990 Hyundai super-NB368S notebook
???
60h-61h AMI 1990 Hyundai super-NB368S notebook
???
--------V-P03C603C9--------------------------
694 A to Z of C
(Table P0698)
Values for EDSUN CEG (Continuous Edge Graphics) modes::
x: mode: colors: mix: pixel depth: effective colors:
0 = disabled 256 - 8 256
1=A 16 16 8 1920
2 = A+REP 16 16 8 dblscn 1920
3 = A+EDP 15 16 truecolor
4 = reserved - - - -
5=B 16 8 8 960
6 = B+REP 16 8 8 dblscn 960
7 = B+EDP 15 8 truecolor
8 = reserved - - - -
9=C 8 8 4 224
10 = C+REP 8 8 4 dblscn 224
11 = C+EDP 7 8 truecolor
12 = reserved - - - -
13 = D 223 32 8 792096
14 = D+REP 223 32 8 dblscn 792096
15 = D+EDP 223 32 truecolor
696 A to Z of C
SeeAlso: #P0699
(Table P0699)
Values for EDSUN CEG mixing codes:
Mode A: | Mode C:
mix: new: old: | mix: new: old: colorcode:
0 = 32/32 0/32 | 0 = - - 0
1 = 30/32 2/32 | 1 = - - 1
2 = 28/32 4/32 | 2 = - - 2
3 = 26/32 6/32 | 3 = - - 3
4 = 24/32 8/32 | 4 = - - 4
5 = 22/32 10/32 | 5 = - - 5
6 = 20/32 12/32 | 6 = - - 6
7 = 18/32 14/32 | 7 = - - 7/EDP
8 = 16/32 16/32 | 8 = 30/32 2/32 -
9 = 14/32 18/32 | 9 = 28/32 4/32 -
10 = 12/32 20/32 | 10 = 26/32 6/32 -
11 = 10/32 22/32 | 11 = 24/32 8/32 -
12 = 8/32 24/32 | 12 = 22/32 10/32 -
13 = 6/32 26/32 | 13 = 20/32 12/32 -
14 = 4/32 28/32 | 14 = 18/32 14/32 -
15 = 2/32 30/32 | 15 = 16/32 16/32 -
---Mode B: | Mode D:
mix: new: old: | mix: new: old: description:
0 = 30/32 2/32 | 00h..BEh = - - normal color
1 = 26/32 6/32 | BFh = - - EDP
2 = 22/32 10/32 | C0h = 32/32 0/32
3 = 18/32 14/32 | C1h = 31/32 1/32
4 = 14/32 18/32 | C2h = 30/32 2/32
5 = 10/32 22/32 | ... = ... ...
6 = 6/32 26/32 | DFh = 0/32 32/32
7 = 2/32 30/32 | E0h-FFh = - - normal color
SeeAlso: #P0698
--------H-M00000000--------------------------
MEM 0000h:0000h R - INTERRUPT VECTOR TABLE
Size: 1024 BYTEs
Note: see also the main interrupt list
A to Z of C 697
--------b-M0000031D--------------------------
MEM 0000h:031Dh - 1989 AMI 386sx BIOS - USER-DEFINABLE TYPE 47 HARD DISK PARMS
Size: 16 BYTEs
Note: these fields are used if the AMI BIOS setup is set to use the top of
the interrupt table for the extended BIOS data area
SeeAlso: MEM 0000h:032Dh,INT 41
--------b-M0000032D--------------------------
MEM 0000h:032Dh - 1989 AMI 386sx BIOS - USER-DEFINABLE TYPE 48 HARD DISK PARMS
Size: 16 BYTEs
Note: these fields are used if the AMI BIOS setup is set to use the top of
the interrupt table for the extended BIOS data area
SeeAlso: MEM 0000h:031Dh,INT 46
--------B-M00000400--------------------------
MEM 0000h:0400h - BIOS DATA AREA
Size: 256 BYTEs
Note: see also the MEM 0040h:xxxxh entries
----------M00000500--------------------------
MEM 0000h:0500h - DATA AREA
Size: 256 BYTEs
--------D-M00000600--------------------------
MEM 0000h:0600h - MS-DOS 1.x LOAD ADDRESS
--------D-M00000700--------------------------
MEM 0000h:0700h - MS-DOS 2+ LOAD ADDRESS
--------S-M00400000--------------------------
MEM 0040h:0000h - BASE I/O ADDRESS OF FIRST SERIAL I/O PORT
Size: WORD
Notes: the BIOS sets this word to zero if is unable to find any serial ports
at the addresses it is programmed to check at boot
DOS and BIOS serial device numbers may be redefined by re-assigning
these values of the base I/O addresses stored here
Under DR-OpenDOS 7.02+ this setting can be changed with the
undocumented CONFIG.SYS COM1=[port_address|logical_no][,[timeout]]
directive, whereby port_address = 200h..3F8h, logical_no = 0 or 1..4,
timeout=0..255 (default 1).
SeeAlso: MEM 0040h:0002h,MEM 0040h:0004h,MEM 0040h:0006h,MEM 0040h:0008h
SeeAlso: MEM 0040h:007Ch,INT 14/AH=00h,PORT 03F8h"SERIAL"
--------S-M00400002--------------------------
MEM 0040h:0002h - BASE I/O ADDRESS OF SECOND SERIAL I/O PORT
Size: WORD
Notes: the BIOS sets this word to zero if is unable to find more than one
serial port at the addresses it is programmed to check at boot
DOS and BIOS serial device numbers may be redefined by re-assigning
these values of the base I/O addresses stored here
Under DR-OpenDOS 7.02+ this setting can be changed with the
undocumented CONFIG.SYS COM2=[port_address|logical_no][,[timeout]]
directive, whereby port_address = 200h..3F8h, logical_no = 0 or 1..4,
timeout=0..255 (default 1).
SeeAlso: MEM 0040h:0000h,MEM 0040h:0004h,MEM 0040h:0006h,MEM 0040h:000Ah
698 A to Z of C
Size: BYTE
SeeAlso: MEM 0040h:003Eh,MEM 0040h:0042h,INT 13/AH=01h
1 bad cylinder
0 unable to find address mark
SeeAlso: #M0016
--------V-M00400049--------------------------
MEM 0040h:0049h - VIDEO - CURRENT VIDEO MODE
Size: BYTE
SeeAlso: MEM 0040h:004Ah,INT 10/AH=00h
--------V-M0040004A--------------------------
MEM 0040h:004Ah - VIDEO - COLUMNS ON SCREEN
Size: WORD
SeeAlso: MEM 0040h:0049h,MEM 0040h:004Ch,MEM 0040h:004Eh,INT 10/AH=0Fh
--------V-M0040004C--------------------------
MEM 0040h:004Ch - VIDEO - PAGE (REGEN BUFFER) SIZE IN BYTES
Size: WORD
SeeAlso: MEM 0040h:004Ah,MEM 0040h:004Eh,MEM 0040h:0050h
--------V-M0040004E--------------------------
MEM 0040h:004Eh - VIDEO - CURRENT PAGE START ADDRESS IN REGEN BUFFER
Size: WORD
SeeAlso: MEM 0040h:004Ch,MEM 0040h:0050h,MEM 0040h:0062h,INT 10/AH=05h
--------V-M00400050--------------------------
MEM 0040h:0050h - VIDEO - CURSOR POSITIONS
Size: 8 WORDs
Desc: contains row and column position for the cursors on each of eight
video pages
SeeAlso: MEM 0040h:004Eh,MEM 0040h:0060h,INT 10/AH=02h
--------V-M00400060--------------------------
MEM 0040h:0060h - VIDEO - CURSOR TYPE
Size: WORD (big-endian)
Desc: contains cursor start scan line and cursor end scan line
SeeAlso: MEM 0040h:0050h,MEM 0040h:0062h,INT 10/AH=03h
--------V-M00400062--------------------------
MEM 0040h:0062h - VIDEO - CURRENT PAGE NUMBER
Size: BYTE
SeeAlso: MEM 0040h:004Eh,MEM 0040h:0063h,INT 10/AH=05h
--------V-M00400063--------------------------
MEM 0040h:0063h - VIDEO - CRT CONTROLLER BASE I/O PORT ADDRESS
Size: WORD
Note: normally 03B4h for mono and 03D4h for color video boards
SeeAlso: MEM 0040h:0065h,MEM 0040h:0066h
--------V-M00400065--------------------------
MEM 0040h:0065h - VIDEO - CURRENT MODE SELECT REGISTER
Size: BYTE
Desc: contains last value written to I/O port 03B8h / 03D8h
SeeAlso: MEM 0040h:0063h,MEM 0040h:0066h
----------M0040006B--------------------------
MEM 0040h:006Bh - POST LAST UNEXPECTED INTERRUPT (XT and later)
Size: BYTE
Desc: this is a bitmask of IRQs which have occurred while the corresponding
interrupt vector points at the default system BIOS handler
(bit 0 = IRQ0 to bit 7 = IRQ7; bit 2 = IRQ8-15 on AT and later)
SeeAlso: INT 0F"IRQ7",INT 70"IRQ8",INT 77"IRQ15"
----------M0040006C--------------------------
MEM 0040h:006Ch - TIMER TICKS SINCE MIDNIGHT
Size: DWORD
Desc: updated approximately every 55 milliseconds by the BIOS INT 08 handler
SeeAlso: MEM 0040h:0070h,INT 08"IRQ0",INT 1A/AH=00h
----------M00400070--------------------------
MEM 0040h:0070h - TIMER OVERFLOW
Size: BYTE
Desc: non-zero if timer has counted past midnight since last call to
INT 1A/AH=00h
Note: the original IBM BIOS, and thus most other BIOSes, sets this byte to
01h at midnight; a few (such as the Eagle PC-2) increment it each
time midnight is passed. The former behavior results in lost days
if multiple midnights pass between "get-time" calls while the machine
is powered up.
SeeAlso: MEM 0040h:006Ch,INT 1A/AH=00h
--------K-M00400071--------------------------
MEM 0040h:0071h - Ctrl-Break FLAG
Size: BYTE
Desc: bit 7 is set when Ctrl-Break has been pressed
SeeAlso: INT 1B
----------M00400072--------------------------
MEM 0040h:0072h - POST RESET FLAG
Size: WORD
Desc: specify the action the BIOS should take at the beginning of the
power-on self-test when the machine is reset
SeeAlso: INT 19,MEM F000h:FFF0h
(Table M0021)
Values for POST reset flag:
0000h cold boot
0064h Burn-in mode
1234h to bypass memory test (warm boot)
4321h [PS/2 except Mod 25,30] to preserve memory
5678h [Conv] system suspended
9ABCh [Conv] manufacturing test mode
ABCDh [Conv] POST loop mode
--------B-M00400074--------------------------
MEM 0040h:0074h - FIXED DISK LAST OPERATION STATUS (except ESDI drives)
Size: BYTE
SeeAlso: INT 13/AH=01h,INT 13h/AH=0Ah,MEM 0040h:0041h
A to Z of C 711
(Table M0022)
Values for fixed disk last operation status:
00h no error
01h invalid function request
02h address mark not found
03h write protect error
04h sector not found
05h reset failed
06h diskette removed
07h drive parameter activity failed
08h DMA overrun
09h DMA data boundary error
0Ah bad sector flag detected
0Bh bad track detected
0Ch requested diskette media type not found
(PS/2 or extended BIOS only) unsupported track
0Dh invalid number of sectors for Format
0Eh control data address mark detected
0Fh DMA arbitration level out of range
10h uncorrectable ECC or CRC error
11h ECC corrected data error
20h general controller failed
40h seek failed
80h time out
AAh drive not ready
B0h volume not locked in drive (INT 13 extensions)
B1h volume locked in drive (INT 13 extensions)
B2h volume not removable (INT 13 extensions)
B3h volume in use (INT 13 extensions)
B4h lock count exceeded (INT 13 extensions)
B5h valid eject request failed (INT 13 extensions)
BBh undefined error
CCh write fault on selected drive
E0h status error/error register is zero
FFh sense failed
SeeAlso: #00234
--------d-M00400074--------------------------
MEM 0040h:0074h - WD1002-27X SuperBIOS - TOTAL DRIVES, FIRST CONTROLLER ONLY
Size: BYTE
SeeAlso: MEM 0040h:0075h"SuperBIOS",MEM 0040h:0076h"SuperBIOS"
--------B-M00400075--------------------------
MEM 0040h:0075h - FIXED DISK - NUMBER OF FIXED DISK DRIVES
Size: BYTE
SeeAlso: MEM 0040h:0076h"FIXED DISK",MEM 0040h:0077h"FIXED DISK"
--------d-M00400075--------------------------
MEM 0040h:0075h - WD1002-27X SuperBIOS - TOTAL FIXED DRIVES, BOTH CONTROLLERS
Size: BYTE
712 A to Z of C
(Table M0026)
Values for EGA/VGA configuration switches:
00h Pri MDA, Sec EGA+old color display 40 x 25
01h Pri MDA, Sec EGA+old color display 80 x 25
02h Pri MDA, Sec EGA+ECD normal mode (CGA emul)
03h Pri MDA, Sec EGA+ECD enhanced mode
04h Pri CGA 40 x 25, Sec EGA mono display
05h Pri CGA 80 x 25, Sec EGA mono display
06h Pri EGA+old color display 40 x 25, Sec MDA
07h Pri EGA+old color display 80 x 25, Sec MDA
08h Pri EGA+ECD normal mode (CGA emul), Sec MDA
09h Pri EGA+ECD enhanced mode, Sec MDA
0Ah Pri EGA mono display, Sec CGA 40 x 25
0Bh Pri EGA mono display, Sec CGA 80 x 25
SeeAlso: #M0025
--------b-M00400088--------------------------
MEM 0040h:0088h - Olivetti EGA capabilities???
Size: BYTE???
Note: EHD BIOS sets this byte to 01h and never reads it back
--------B-M0040008C--------------------------
MEM 0040h:008Ch - FIXED DISK - CONTROLLER STATUS [not XT]
Size: BYTE
SeeAlso: MEM 0040h:008Dh,MEM 0040h:008Eh
--------B-M0040008D--------------------------
MEM 0040h:008Dh - FIXED DISK - CONTROLLER ERROR STATUS [not XT]
Size: BYTE
SeeAlso: MEM 0040h:008Ch,MEM 0040h:008Eh
--------B-M0040008E--------------------------
MEM 0040h:008Eh - FIXED DISK - INTERRUPT CONTROL [not XT]
Size: BYTE
Note: cleared to 00h at start of disk operation, set to FFh by IRQ14
handler when hard disk controller completes command
SeeAlso: MEM 0040h:008Ch,MEM 0040h:008Dh,MEM 0040h:008Fh
--------B-M0040008F--------------------------
MEM 0040h:008Fh U - DISKETTE CONTROLLER INFORMATION [not XT]
Size: BYTE
SeeAlso: MEM 0040h:008Ch,MEM 0040h:008Dh,MEM 0040h:008Eh
(Table M0035)
Values for HP 100LX/200LX video zoom mode:
02h 80x25 mono
03h 80x25 color
80h 64x18 mono
81h 64x18 color
82h 40x25 mono
83h 40x25 color
84h 40x16 mono
85h 40x16 color
SeeAlso: INT 10/AH=D0h
--------B-M004000A0--------------------------
MEM 0040h:00A0h - TIMER2 (AT, PS exc Mod 30) - WAIT ACTIVE FLAG
Size: BYTE
SeeAlso: MEM 0040h:009Ch,INT 15/AH=83h,INT 15/AH=86h
(Table M0040)
Values for Display Combination display type:
00h no display
01h MDA with mono display
02h CGA with color display
03h reserved
04h EGA with color display
05h EGA with mono display
06h Professional Graphics Controller
07h VGA with mono display
08h VGA with color display
09h reserved
0Ah MCGA with digital color display
0Bh MCGA with analog mono display
0Ch MCGA with analog color display
FFh unrecognised video system
SeeAlso: #M0039
Note: this byte contains FFh if controller at 03Fxh and 7Fh if at 037xh; the
value is ANDed with DL prior to using IN A?,DX or OUT DX,A?
instructions
--------K-M004000D6--------------------------
MEM 0040h:00D6h - Digiboard MV/4 - NUMBER OF KEYBOARDS FOUND
Size: WORD
SeeAlso: MEM 0040h:00D8h"Digiboard"
--------b-M004000D6--------------------------
MEM 0040h:00D6h - AMI BIOS v1.00.12.AX1T - EPP - PARALLEL PORT 0 IRQ
Size: BYTE
SeeAlso: MEM 0040h:00D2h"AMI",MEM 0040h:00D5h"AMI",MEM 0040h:00D8h"AMI"
SeeAlso: MEM 0040h:00DDh"AMI"
--------d-M004000D7--------------------------
MEM 0040h:00D7h - EHD floppy - DRIVE 0 DISKETTE MEDIA STATE
Size: BYTE
Note: the value in this byte is copied into 0040h:0090h (diskette 0 status)
SeeAlso: MEM 0040h:00D8h"EHD",MEM 0040h:00D9h"EHD",MEM 0040h:00DAh"EHD"
Size: BYTE
SeeAlso: MEM 0040h:00D2h"AMI",MEM 0040h:00D6h"AMI",MEM 0040h:00D7h"AMI"
SeeAlso: MEM 0040h:00DDh"AMI"
--------b-M004000D8--------------------------
MEM 0040h:00D8h U - Phoenix BIOS 4.0 Rel 6.0 - POWER MANAGEMENT FLAGS
Size: BYTE
SeeAlso: INT 15/AX=5300h
--------d-M004000D9--------------------------
MEM 0040h:00D9h - EHD floppy - DRIVE 2 DISKETTE MEDIA STATE
Size: BYTE
SeeAlso: MEM 0040h:00D7h"EHD",MEM 0040h:00D8h"EHD",MEM 0040h:00DAh"EHD"
--------S-M004000DA--------------------------
MEM 0040h:00DAh - Digiboard MV/4 - CURRENT PORT (used by VGA initializatn only)
Size: BYTE
SeeAlso: MEM 0040h:00D8h"Digiboard"
--------d-M004000DA--------------------------
MEM 0040h:00DAh - EHD floppy - DRIVE 3 DISKETTE MEDIA STATE
Size: BYTE
SeeAlso: MEM 0040h:00D7h"EHD",MEM 0040h:00D8h"EHD",MEM 0040h:00D9h"EHD"
--------S-M004000DB--------------------------
MEM 0040h:00DBh - Digiboard MV/4 - MASTER 8259 MASK (used by VGA init only)
Size: BYTE
SeeAlso: MEM 0040h:00DCh"Digiboard"
--------d-M004000DB--------------------------
MEM 0040h:00DBh - EHD floppy - DRIVE 0 NEEDS RECALIBARATION
Size: BYTE
SeeAlso: MEM 0040h:00DCh"EHD",MEM 0040h:00DDh"EHD",MEM 0040h:00DEh"EHD"
--------S-M004000DC--------------------------
MEM 0040h:00DCh - Digiboard MV/4 - SLAVE 8259 MASK (used by VGA init only)
Size: BYTE
SeeAlso: MEM 0040h:00DBh"Digiboard"
--------b-M004000DC--------------------------
MEM 0040h:00DCh - AMI BIOS v1.00.12.AX1T - EPP - PARALLEL PORT 0 MODE
Size: BYTE
SeeAlso: MEM 0040h:00D2h"AMI",MEM 0040h:00D5h"AMI",MEM 0040h:00DDh"AMI"
SeeAlso: INT 17/AX=0200h/BX=5050h
(Table M0053)
Values for AMI Enhanced Parallel Port mode:
01h compatibility mode
02h bi-directional mode
04h EPP mode
SeeAlso: #00637
--------d-M004000DC--------------------------
MEM 0040h:00DCh - EHD floppy - DRIVE 1 NEEDS RECALIBARATION
Size: BYTE
SeeAlso: MEM 0040h:00DBh"EHD",MEM 0040h:00DDh"EHD",MEM 0040h:00DEh"EHD"
--------b-M004000DC--------------------------
A to Z of C 729
(Table M0054)
Values for EHD floppy diskette type:
01h undefined by diskette change (360K)
02h 1.2M
03h 720K
04h 1.44M
05h 2.88M
--------d-M004000E4--------------------------
MEM 0040h:00E4h - EHD floppy - DRIVE 1 DISKETTE TYPE (from jumpers)
Size: BYTE
SeeAlso: MEM 0040h:00E3h,MEM 0040h:00E5h"EHD",MEM 0040h:00E6h"EHD"
--------d-M004000E5--------------------------
MEM 0040h:00E5h - EHD floppy - DRIVE 2 DISKETTE TYPE (from jumpers)
Size: BYTE
SeeAlso: MEM 0040h:00E3h,MEM 0040h:00E4h"EHD",MEM 0040h:00E6h"EHD"
730 A to Z of C
---------------------------------------------
MEM 0040h:00E5h - AWARD v4.51PG - ASSOC DRIVE NUMBERS TO PHYSICAL INTERFACES
Size: BYTE
SeeAlso: MEM 0040h:00B5h"Gigabyte"
--------A-M00500010--------------------------
MEM 0050h:0010h - POINTER TO BASIC DATA SEGMENT
Size: WORD
--------A-M00500012--------------------------
MEM 0050h:0012h - INT 08 VECTOR AT START OF BASICA.COM EXECUTION
Size: DWORD
--------A-M00500016--------------------------
MEM 0050h:0016h - INT 1B VECTOR AT START OF BASICA.COM EXECUTION
Size: DWORD
--------A-M0050001A--------------------------
MEM 0050h:001Ah - INT 24 VECTOR AT START OF BASICA.COM EXECUTION
Size: DWORD
--------D-M00600000--------------------------
MEM 0060h:0000h - DOS 2+ SCRATCH SPACE
Size: 256 BYTEs
Note: used during DOS 2+ boot process
--------D-M00600000--------------------------
MEM 0060h:0000h - DOS 1.x IO.SYS LOAD ADDRESS
--------D-M00700000--------------------------
MEM 0070h:0000h - DOS 2+ IO.SYS LOAD ADDRESS
--------D-M00700100--------------------------
MEM 0070h:0100h - DOS 5+ - ORIGINAL INTERRUPT VECTORS 10h,13h,15h,19h,1Bh
Size: 25 BYTEs
Note: each value is stored as a BYTE for the interrupt number followed by
a DWORD for the vector
these values are restored on INT 19 by recent versions of
DR/Novell/PC/MS-DOS (MS-DOS 3.x used this area to support HIMEM.SYS)
not supported by OS/2 MDOS
SeeAlso: MEM 0080h:0000h,INT 2F/AH=13
--------d-M0070016C--------------------------
MEM 0070h:016Ch - DR-DOS 7.02-7.03 - "DEVNO" AUX/PRN PORT ASSIGNMENTS
Size: 2 BYTEs
016Ch BYTE PRN: assignment (0..2 for LPT1:..LPT3: (3 for LPT4:); default: 1)
016Dh BYTE AUX: assignment (0..3 for COM1:..COM4:; default: 1)
Notes: As long as the built-in AUX: or PRN: drivers are in effect, these
settings can be transparently reassigned at the DR-OpenDOS 7.02 /
DR-DOS 7.03 DOS BIOS device driver level (that is below DOS
redirection etc., but above ROM BIOS) using the undocumented
CONFIG.SYS AUX=0|1..4 and PRN=0|1..3|4 directive, where 1..4
specifies COM1:..COM4: or LPT1:..LPT4: and the high speed bypass 0
selects the previous hardwired equivalence of AUX: with COM1: and
PRN: with LPT1: at this level, saving a few clock cycles. The system
defaults to AUX=1 and PRN=1 (that is 0 in the internal variables).
If the high speed bypass was not enabled, the assigment can be changed
anytime later by updating these bytes, e.g. by a future issue of the
MODE utility. If the highspeed bypass has been enabled, changes have
732 A to Z of C
no effect.
The LPT4 setting (or corresponding value 3) is valid for
DR-OpenDOS 7.02 and DR-DOS 7.02, but due to a bug introduced with the
partial removal of the LPT4: device, it must not be used under
DR-DOS 7.03.
The address 0070h:016Ch is only valid for DR-OpenDOS 7.02 up to
DR-DOS 7.03 (BDOS 73h), and will most probably change with future
releases of DR-DOS!
These bytes are local for each task running.
SeeAlso: INT 21h/03h, INT 21h/04h, INT 21h/05h, MEM 0040h:0000h etc.
--------H-M00800000--------------------------
MEM 0080h:0000h - 80286 CPU - LOADALL WORKSPACE
Size: 102 BYTEs
Desc: on the 80286 (unlike 80386), the state buffer from which the LOADALL
instruction loads all internal registers is hardwired to physical
address 000800h
Note: several versions 3.x of MS-DOS leave an empty space at offset 100h in
IO.SYS (which is loaded at 0070h:0000h) so that HIMEM.SYS can use
LOADALL on 80286 machines without having to save/restore the area
of memory that LOADALL uses
SeeAlso: MEM 0070h:0100h
--------m-m80C00000--------------------------
MEM 80C00000h - Compaq Deskpro 386 system memory board register
Size: BYTE
frame buffer
additional setup may be required to access these registers via memory
the DWORDs at 8080h,8088h,808Ch,8090h,8094h,8098h,809Ch are used by
STLTH64.DRV
the DWORDs at 18080h,18088h,18090h,18094h,18098h,1809Ch are written
by S3_32.DLL
--------V-MA0008100--------------------------
MEM A000h:8100h - S3 - MEMORY-MAPPED PACKED REGISTERS
Size: 80 BYTEs
Access: Write-Only
Desc: these registers pack two 16-bit I/O registers into a single DWORD
for faster writes
Note: the S3 graphics processor registers can be mapped at either
linear 000A0000h or at offset 16M from the start of the linear
frame buffer
SeeAlso: MEM A000h:8180h
81E8h DWORD DDA vertical accumulator (bits 11-0 only) (lines out)
set to (#lines_out) - 1
81ECh DWORD streams FIFO and RAS control (see #M0067)
81F0h DWORD primary start coordinate (see #M0068)
81F4h DWORD primary window size (see #M0069)
81F8h DWORD secondary start coordinate (see #M0068)
81FCh DWORD secondary window size (see #M0069)
Note: changes to registers 81E0h-81E8h do not take effect until the next
VSYNC
SeeAlso: #M0073,#M0057,#M0070
(Table M0060)
Values for S3 Streams Processor color mode:
000b eight bits per pixel
001b YCrCb 4:2:2 unsigned, range 10h-F0h (secondary stream only)
010b YUV 4:2:2, range 00h-FFh (secondary stream only)
011b keyed high-color (1-5-5-5)
100b YUV 2:1:1 two's complement (secondary stream only)
101b high-color (5-6-5)
110b reserved
111b true-color (32bpp, high byte ignored)
SeeAlso: #M0059,#M0061
27 reserved
26-24 color mode (see #M0060,#M0074)
23-12 reserved
11-0 initial value of DDA horizontal accumulator
set to 2*(inwidth-1)-(outwidth-1)
Notes: the secondary stream is typically live video, but can be pointed at
any part of video memory
changes to this register do not take effect until the next VSYNC
SeeAlso: #M0058,#M0059,#M0062
Bitfields for S3 Streams Processor streams FIFO and RAS control register:
Bit(s) Description (Table M0067)
31-22 reserved (0)
21 skip 0.5 MCLK delay of PD[63:0] output (default = 0)
20 skip memory arbitration for ROM cycles (default = 0)
19 do not tristate PD[63:16] during ROM cycles (default = 0)
(set by Win95 driver when using ISA bus)
18 EDO wait state control (LPB memory cycles only)
=0 two-cycle accesses
=1 one-cycle EDO accesses
17 reserved
16 RAS# pre-charge control
=0 use CR68(bit3) setting (2.5/3.5 MCLKs)
=1 1.5 MCLKs
15 RAS# low control
=0 use CR68(bit2) setting (3.5/4.5 MCLKs)
=1 2.5 MCLKs
14-10 primary stream FIFO threshold
number of filled quadword slots at which to request refilling
9-5 secondary stream FIFO threshold
number of filled quadword slots at which to request refilling
4-0 FIFO allocation, in quadword slots
00000 primary stream = 24, secondary = 0
01000 primary stream = 16, secondary = 8
01100 primary stream = 12, secondary = 12
10000 primary stream = 8, secondary = 16
11000 primary stream = 0, secondary = 24
else reserved
SeeAlso: #M0058
SeeAlso: #M0058,#M0068
--------V-MA0008200--------------------------
MEM A000h:8200h - S3 ViRGE - MEMORY-MAPPED MEMORY-PORT CONTROL REGISTERS
Size: 40 BYTEs
Note: the S3 graphics processor registers can be mapped at either
linear 000A0000h or at offset 16M from the start of the linear
frame buffer
--------V-MA0008580--------------------------
MEM A000h:8580h - S3 - MEMORY-MAPPED DMA REGISTERS
Size: 32 BYTEs
Note: the S3 graphics processor registers can be mapped at either
linear 000A0000h or at offset 16M from the start of the linear
frame buffer
bit 4 is set
FF14h DWORD "direct address" = index for FF18h (see #M0077)
FF18h DWORD "direct data" (see #M0077)
Note: the direct address/direct data registers presumably rely
on the attached device inserting data into the digital video
stream, as on a Diamond Stealth64 Video, the "direct data"
appears to reflect the video stream data (i.e. it varies, but
with a pattern that depends on the video image, and stops
varying when video is frozen)
FF1Ch DWORD general purpose I/O (see #M0078)
FF20h DWORD LPB serial port -- I2C/DDC access (see #M0079)
FF24h DWORD input window size (high word = rows, low word = columns)
FF28h DWORD data offsets
(video alignment; high word = rows ; low word = columns)
FF2Ch DWORD horizontal decimation
bits 0-31 set indicate that bytes 0-31 (mod 32)
of each line should be dropped (in Video16 mode, each bit
controls a WORD); decimation is aligned with the start of
line as specified by the data offsets at FF28h
FF30h DWORD vertical decimation
bits 0-31 set indicate that lines 0-31 (mod 32) should be
dropped, i.e. setting this DWORD to 55555555h will drop
every other line; decimation starts with VSYNC regardless
of the data offsets specified at FF28h
FF34h DWORD line stride (number of bytes between starts of successive lines
of video data)
must be multiple of 4 -- lowest two bits forced to 0
FF38h 3 DWORDs unused??? (seem to echo FF34h)
FF40h 8 DWORDs LPB output FIFO - data transfer
writing to ANY of these DWORDs transfers a value to the FIFO;
this organization allows use of a REP MOVSD instruction to
fill the FIFO
on ISA bus, there must be a delay between successive writes
SeeAlso: #M0058
4 LBP Reset
pulse this bit before changing operational mode
5 skip every other frame
=0 write all received frames to memory
6 disable byte-swapping
=0 incoming 8-bit video is in order U, Y0, V, Y1 (CL-480)
=1 incoming 8-bit video is in order Y0, U, Y1, V (SAA711x)
(refer to bit 26 below)
8-7 officially reserved
7 ??? messes up video image when set
9 LPB vertical sync input polarity
=0 active low
=1 active high
10 LPB horizontal sync input polarity
=0 active low
=1 active high
11 (write-only) CPU VSYNC
writing a 1 makes Trio act as if LPB VSYNC had been received
12 (write-only) CPU HSYNC
writing a 1 makes Trio act as if LPB HSYNC had been received
13 (write-only) load base address
writing a 1 causes an immediate load of the currently active base
address
15-14 reserved
17-16 maximum compressed data burst, LPB to Scenic/MX2
00 one DWORD
01 two DWORDs
10 three DWORDs
11 burst until empty (must ensure that MX2's 8-entry FIFO is not
overrun)
20-18 reserved
22-21 video FIFO threshold
number of filled slots at which to request that Trio's memory manager
begin to empty the FIFO (00 = one slot, 01 = two slots, 10 = four
slots, 11 = six slots)
23 reserved (read-only)
24 LPB clock source
=0 driven by SCLK (Pin194) (for Trio64-compatibility mode)
=1 driven by LCLK (Pin148) (default)
25 don't add line stride after first HSYNC within VSYNC
must be set if first HSYNC occurs before VSYNC goes active
26 invert LCLK (only has effect if bit 24 set)
27 reserved
28 (not yet on Trio64V+) current odd/even video field status
29 (not yet on Trio64V+) field inversion - when set, the LPB's FIELD pin
state is inverted before being reported in bit 28
30 reserved
31 (read-only) current state of CFLEVEL input (Pin182) in Video In/Out
A to Z of C 745
(Table M0077)
Values for S3 Local Peripheral Bus "direct address" index:
0000h CP3 installation (FF18h reads 00C3h if installed)
0001h ?
0002h ?
0003h ?
bit 7: ???
bits 6-0: ???
0004h ?
0005h ?
746 A to Z of C
SeeAlso: #M0080
Size: 20 BYTEs
Range: starting at any byte within the first 32K of segment C000h
--------H-MF000FFF0--------------------------
MEM F000h:FFF0h - RESET JUMP
Size: 5 BYTEs
--------B-MF000FFF5--------------------------
MEM F000h:FFF5h - ASCII BIOS DATE
Size: 8 BYTEs
--------B-MF000FFFD--------------------------
MEM F000h:FFFDh - OFTEN USED TO ENSURE CORRECT BIOS CHECKSUM
Size: BYTE
--------B-MF000FFFE--------------------------
MEM F000h:FFFEh - MACHINE TYPE CODE
Size: BYTE
SeeAlso: INT 15/AH=C0h
--------X-MF000xxx0--------------------------
MEM F000h:xxx0h - PCI IRQ Routing Table Specification v1.0
Size: N paragraphs (N >= 2)
InstallCheck: scan for the signature string "$PIR" followed by a valid
PCI IRQ Routing Table
Range: any paragraph boundary within the range F0000h to FFFFFh
SeeAlso: #M0094
00h processor
01h bus
02h I/O APIC
03h I/IO interrupt assignment
04h local interrupt assignment
80h system address space mapping
81h bus hierarchy descriptor
82h compatibility bus address space modifier
---processor---
01h BYTE local APIC identifier
02h BYTE local APIC version
03h BYTE CPU flags
bit 0: processor usable
bit 1: bootstrap processor
04h WORD CPU type
bits 11-8: CPU family
bits 7-4: CPU model
bits 3-0: stepping
(bits 11-0 all set indicate non-Intel-compatible CPU)
06h 2 BYTEs unused
08h DWORD feature flags (as returned by Pentium CPUID instruction)
0Ch 8 BYTEs reserved
---bus---
01h BYTE bus ID (assigned sequentially from 00h by BIOS)
02h 6 BYTEs bus type (blank-padded ASCII string) (see #M0116)
---I/O APIC---
01h BYTE APIC identifier
02h BYTE APIC version
03h BYTE I/O APIC flags
bit 0: enabled
bits 7-1: reserved
04h DWORD base address for APIC
---I/O,local interrupt assignment---
01h BYTE interrupt type
00h vectored interrupt (from APIC)
01h NMI
02h system management interrupt
03h vectored interrupt (from external PIC)
02h BYTE APIC control (see #M0117)
03h BYTE unused
04h BYTE source bus identifier
05h BYTE source bus IRQ
06h BYTE destination I/O APIC identifier
07h BYTE destination I/O APIC interrupt pin number
---system address space mapping---
01h BYTE entry length (14h)
02h BYTE bus ID
03h BYTE address type (00h I/O, 01h memory, 02h prefetch)
764 A to Z of C
(Table M0116)
Values for Multiprocessor Specification bus name:
"CBUS" Corollary CBus
"CBUSII" Corollary CBus II
"EISA"
"FUTURE" IEEE FutureBus
"INTERN" internal bus
"ISA"
"MBI" Multibus I
"MBII" Multibus II
"MCA" Microchannel
"MPI"
"MPSA"
"NUBUS" Apple Macintosh NuBus
"PCI"
"PCMCIA"
"TC" DEC TurboChannel
"VL" VESA Local Bus
"VME" VMEbus
"XPRESS" Express System Bus
SeeAlso: #M0115
11 active low
3-2 trigger mode
00 conforms to bus specification
01 edge-triggered
10 reserved
11 level-triggered
SeeAlso: #M0115
--------H-mFEC00000--------------------------
MEM FEC00000h - Pentium - 82379AB I/O APIC - I/O REGISTER SELECT
Size: DWORD
Desc: bits 7-0 of the I/O Register Select memory location specify which
of the APIC's registers appears in the I/O Window at FExxx010h
Range: the Multiprocessor Specification calls for I/O APICs to be memory-
mapped on 4K boundaries between FEC00000h and FEDFC000h; the Intel
82379AB I/O APIC can be memory-mapped on any 1K boundary within
FEC0000h-FEC0F800h
Note: this memory-mapped register is also supported by the Intel 82093AA
I/O APIC
SeeAlso: MEM FEC00010h,MEM FEE00000h,MEM xxxxh:xxx0h"Multiprocessor"
--------H-mFEC00010--------------------------
MEM FEC00010h - Pentium - 82379AB I/O APIC - I/O WINDOW
Size: DWORD
Range: the Multiprocessor Specification calls for I/O APICs to be memory-
mapped on 4K boundaries between FEC00000h and FEDFC000h
Note: this memory-mapped register is also supported by the Intel 82093AA
I/O APIC
SeeAlso: MEM FEC00010h
(Table M0118)
Values for Intel 82379AB/82093AA I/O APIC registers:
00h APIC ID
01h APIC version (read-only)
bits 31-24: reserved
bits 23-16: maximum redirection entry
bits 15-8: reserved
bits 7-0: APIC version (11h for 82093AA)
02h APIC arbitration ID (read-only)
bits 31-28: reserved
bits 27-24: arbitration ID
bits 23-0: reserved
10h-11h redirection table entry 0 (10h=low DWORD, 11h=high DWORD)
12h-13h redirection table entry 1 (see !!!)
...
2Eh-2Fh redirection table entry 15
---82093AA only---
30h-31h redirection table entry 16
...
3Eh-3Fh redirection table entry 23
766 A to Z of C
(Table M0122)
Values for Pentium APIC delivery mode:
000b fixed
001b lowest-priority
010b SMI
011b remote read
100b NMI
101b INIT
110b start up
111b reserved
SeeAlso: #M0121
--------H-mFEE00310--------------------------
MEM FEE00310h - Pentium + - LOCAL APIC - INTERRUPT COMMAND REGISTER (ICR)
Size: DWORD
Note: this is the high half of the 64-bit ICR
SeeAlso: MEM FEE00300h,#M0121
--------H-mFEE00320--------------------------
MEM FEE00320h - Pentium + - LOCAL APIC - LOCAL VECTOR TABLE ENTRY 0 (TIMER)
Size: DWORD
SeeAlso: MEM FEE00350h,MEM FEE00370h,MEM FEE003E0h,INT 70h
Size: DWORD
SeeAlso: MEM FEE00320h,MEM FEE00360h
File formats are usually represented in record/structure format. Almost all documents use
Assembly language’s record format or C’s structure format or sometimes Pascal’s record format.
In file formats, mostly we would come across the jargons: BYTE, WORD & DWORD. BYTE
can be viewed as signed or unsigned char; WORD can be viewed as signed or unsigned int;
DWORD can be viewed as signed or unsigned long.
72.2 ARJ
72.2.1 Glimpse
Following documentation gives you overall picture about ARJ file format.
The ARJ program by Robert K. Jung is a "newcomer" which compares well to PKZip
and LhArc in both compression and speed. An ARJ archive contains two types of header
blocks, one archive main header at the head of the archive and local file headers before each
archived file.
OFFSET Count TYPE Description
0000h 1 word ID=0EA60h
0002h 1 word Basic header size (0 if end of archive)
0004h 1 byte Size of header including extra data
0005h 1 byte Archiver version number
0006h 1 byte Minimum version needed to extract
0007h 1 byte Host OS (see table 0002)
0008h 1 byte Internal flags, bitmapped :
0 - no password / password
1 - reserved
2 - file continues on next disk
3 - file start position field is available
4 - path translation ( "\" to "/" )
A to Z of C 773
(Table 0002)
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
<---- year-1980 ---> <- month -> <--- day ---->
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
<--- hour ---> <---- minute ---> <- second/2 ->
72.3 BMP
Windows bitmap files are stored in a device-independent bitmap (DIB) format that
allows Windows to display the bitmap on any type of display device. The term "device
independent" means that the bitmap specifies pixel color in a form independent of the method
used by a display to represent color. The default filename extension of a Windows DIB file is
.BMP.
Bitmap-File Structures
Each bitmap file contains a bitmap-file header, a bitmap-information header,a color table, and
an array of bytes that defines the bitmap bits. The file has the following form:
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors[];
BYTE aBitmapBits[];
The bitmap-file header contains information about the type, size, and layout of a device-
independent bitmap file. The header is defined as a
BITMAPFILEHEADER structure.
The color table, defined as an array of RGBQUAD structures, contains as many elements as
there are colors in the bitmap. The color table is not present for bitmaps with 24 color bits
because each pixel is represented by 24-bitred-green-blue (RGB) values in the actual bitmap
data area. The colors in the table should appear in order of importance. This helps a display
driver render a bitmap on a device that cannot display as many colors as there are in the
bitmap. If the DIB is in Windows version 3.0 or later format, the driver can use the
biClrImportant member of the BITMAPINFOHEADER structure to determine which colors are
important.
of the bitmap. If necessary, a scan line must be zero-padded to end on a 32-bit boundary.
However, segment boundaries can appear anywhere in the bitmap. The scan lines in the
bitmap are stored from bottom up. This means that the first byte in the array represents the
pixels in the lower-left corner of the bitmap and the last byte represents the pixels in the
upper-right corner.
The biBitCount member of the BITMAPINFOHEADER structure determines the number of bits
that define each pixel and the maximum number of colors in the bitmap. These members can
have any of the following values:
Value Meaning
1 Bitmap is monochrome and the color table contains two entries. Each bit in the
bitmap array represents a pixel. If the bit is clear, the pixel is displayed with
the color of the first entry in the color table. If the bit is set, the pixel has the
color of the second entry in the table.
4 Bitmap has a maximum of 16 colors. Each pixel in the bitmap is represented by
a 4-bit index into the color table. For example, if the first byte in the bitmap is
0x1F, the byte represents two pixels. The first pixel contains the color in the
second table entry, and the second pixel contains the color in the sixteenth table
entry.
8 Bitmap has a maximum of 256 colors. Each pixel in the bitmap is
represented by a 1-byte index into the color table. For example,
if the first byte in the bitmap is 0x1F, the first pixel has the
color of the thirty-second table entry.
24 Bitmap has a maximum of 2^24 colors. The bmiColors (or bmciColors) member
is NULL, and each 3-byte sequence in the bitmap array represents the relative
intensities of red, green, and blue, respectively, for a pixel.
The biClrUsed member of the BITMAPINFOHEADER structure specifies the number of color
indexes in the color table actually used by the bitmap. If the biClrUsed member is set to zero,
the bitmap uses the maximum number of colors corresponding to the value of the biBitCount
member. An alternative form of bitmap file uses the BITMAPCOREINFO,
BITMAPCOREHEADER, and RGBTRIPLE structures.
Bitmap Compression
Windows versions 3.0 and later support run-length encoded (RLE) formats for compressing
bitmaps that use 4 bits per pixel and 8 bits per pixel.
Compression reduces the disk and memory storage required for a bitmap.
When the biCompression member of the BITMAPINFOHEADER structure is set to BI_RLE8, the
DIB is compressed using a run-length encoded format for a 256-color bitmap. This format
uses two modes: encoded mode and absolute mode. Both modes can occur anywhere
throughout a single bitmap.
778 A to Z of C
Encoded Mode
A unit of information in encoded mode consists of two bytes. The first byte specifies the
number of consecutive pixels to be drawn using the color index contained in the second byte.
The first byte of the pair can be set to zero to indicate an escape that denotes the end of a
line, the end of the bitmap, or a delta. The interpretation of the escape depends on the value
of the second byte of the pair, which must be in the range 0x00 through 0x02.
Following are the meanings of the escape values that can be used in the second byte:
Absolute Mode
Absolute mode is signaled by the first byte in the pair being set to zero and the second byte to
a value between 0x03 and 0xFF. The second byte represents the number of bytes that follow,
each of which contains the color index of a single pixel. Each run must be aligned on a word
boundary.
Following is an example of an 8-bit RLE bitmap (the two-digit hexadecimal values in the
second column represent a color index for a single pixel):
When the biCompression member of the BITMAPINFOHEADER structure is set to BI_RLE4, the
DIB is compressed using a run-length encoded format for a 16-color bitmap. This format uses
two modes: encoded mode and absolute mode.
Encoded Mode
A unit of information in encoded mode consists of two bytes. The first byte of the pair contains
the number of pixels to be drawn using the color indexes in the second byte.
A to Z of C 779
The second byte contains two color indexes, one in its high-order nibble (that is, its low-order
4 bits) and one in its low-order nibble.
The first pixel is drawn using the color specified by the high-order nibble, the second is drawn
using the color in the low-order nibble, the third is drawn with the color in the high-order
nibble, and so on, until all the pixels specified by the first byte have been drawn.
The first byte of the pair can be set to zero to indicate an escape that denotes the end of a
line, the end of the bitmap, or a delta. The interpretation of the escape depends on the value
of the second byte of the pair. In encoded mode, the second byte has a value in the range
0x00 through 0x02. The meaning of these values is the same as for a DIB with 8 bits per
pixel.
Absolute Mode
In absolute mode, the first byte contains zero, the second byte contains the number of color
indexes that follow, and subsequent bytes contain color indexes in their high- and low-order
nibbles, one color index for each pixel. Each run must be aligned on a word boundary.
Following is an example of a 4-bit RLE bitmap (the one-digit hexadecimal values in the second
column represent a color index for a single pixel):
Bitmap Example
The following example is a text dump of a 16-color bitmap (4 bits per pixel):
Win3DIBFile
BitmapFileHeader
Type 19778
Size 3118
Reserved1 0
Reserved2 0
OffsetBits 118
BitmapInfoHeader
Size 40
Width 80
Height 75
780 A to Z of C
Planes 1
BitCount 4
Compression 0
SizeImage 3000
XPelsPerMeter 0
YPelsPerMeter 0
ColorsUsed 16
ColorsImportant 16
Win3ColorTable
Blue Green Red Unused
[00000000] 84 252 84 0
[00000001] 252 252 84 0
[00000002] 84 84 252 0
[00000003] 252 84 252 0
[00000004] 84 252 252 0
[00000005] 252 252 252 0
[00000006] 0 0 0 0
[00000007] 168 0 0 0
[00000008] 0 168 0 0
[00000009] 168 168 0 0
[0000000A] 0 0 168 0
[0000000B] 168 0 168 0
[0000000C] 0 168 168 0
[0000000D] 168 168 168 0
[0000000E] 84 84 84 0
[0000000F] 252 84 84 0
Image
.
. Bitmap data
.
72.4 CHR
Following is the official documentation of CHR file format.
The structure of Borland .CHR (stroke) files is as follows:
db 'PK',8,8
db 'BGI ',descr,' V'
A to Z of C 781
db MajorVersion+'0'
db (MinorVersion / 10)+'0',(MinorVersion mod 10)+'0'
db ' - 19 October 1987',0DH,0AH
db 'Copyright (c) 1987 Borland International', 0dh,0ah
db 0,1ah ; null & ctrl-Z = end
Byte 1 7 6 5 4 3 2 1 0 bit #
op1 <seven bit signed X coord>
Byte 2 7 6 5 4 3 2 1 0 bit #
op2 <seven bit signed Y coord>
72.5 COM
The COM files are raw binary executables and are a leftover from the old CP/M
machines with 64K RAM. A COM program can only have a size of less than one segment
(64K), including code and static data since no fixups for segment relocation or anything else
is included. One method to check for a COM file is to check if the first byte in the file could be
782 A to Z of C
a valid jump or call opcode, but this is a very weak test since a COM file is not required to
start with a jump or a call. In principle, a COM file is just loaded at offset 100h in the segment
and then executed.
72.6 CUR
A cursor-resource file contains image data for cursors used by Windows applications.
The file consists of a cursor directory identifying the number and types of cursor images in the
file, plus one or more cursor images. The default filename extension for a cursor-resource file
is .CUR.
Cursor Directory
Each cursor-resource file starts with a cursor directory. The cursor directory, defined as a
CURSORDIR structure, specifies the number of cursors in the file and the dimensions and color
format of each cursor image. The CURSORDIR structure has the following form:
A CURSORDIRENTRY structure specifies the dimensions and color format of a cursor image.
The structure has the following form:
A to Z of C 783
Cursor Image
Each cursor-resource file contains one cursor image for each image identified in the cursor
directory. A cursor image consists of a cursor-image header, a color table, an XOR mask, and
an AND mask. The cursor image has the following form:
BITMAPINFOHEADER crHeader;
RGBQUAD crColors[];
BYTE crXOR[];
BYTE crAND[];
The cursor hot spot is a single pixel in the cursor bitmap that Windows uses to track the
cursor. The crXHotspot and crYHotspot members specify the x- and y-coordinates of the
cursor hot spot. These coordinates are 16-bit integers.
The color table, defined as an array of RGBQUAD structures, specifies the colors used in the
XOR mask. For a cursor image, the table contains exactly two structures, since the biBitCount
member in the cursor-image header is always 1.
The XOR mask, immediately following the color table, is an array of BYTE values representing
consecutive rows of a bitmap. The bitmap defines the basic shape and color of the cursor
image. As with the bitmap bits in a bitmap file, the bitmap data in a cursor-resource file is
organized in scan lines, with each byte representing one or more pixels, as defined by the
color format. For more information about these bitmap bits, see Section "Bitmap-File
Formats."
The AND mask, immediately following the XOR mask, is an array of BYTE values representing
a monochrome bitmap with the same width and height as the XOR mask. The array is
organized in scan lines, with each byte representing 8 pixels.
When Windows draws a cursor, it uses the AND and XOR masks to combine the cursor image
with the pixels already on the display surface. Windows first applies the AND mask by using a
bitwise AND operation; this preserves or removes existing pixel color. Window then applies
the XOR mask by using a bitwise XOR operation. This sets the final color for each pixel.
The following illustration shows the XOR and the AND masks that create a cursor (measuring 8
pixels by 8 pixels) in the form of an arrow:
Following are the bit-mask values necessary to produce black, white, inverted, and
transparent results:
If a cursor-resource file contains more than one cursor image, Windows determines the best
match for a particular display by examining the width and height of the cursor images.
1. DBF Structure
Byte Description
0..n .dbf header (see 2 for size, byte 8)
n+1 1st record of fixed length (see 2&3)
2nd record (see 2 for size, byte 10) if dbf is
... not empty
last record
last optional: 0x1a (eof byte)
2a. Field descriptor array in dbf header (fix 32 bytes for each field)
Byte Size Contents Description Applies for
(supported by)
0 11 ASCI field name, 0x00 termin. all
11 1 ASCI field type (see 2b) all
12 4 n,n,n,n fld address in memory D3
n,n,0,0 offset from record begin Fp
0,0,0,0 ignored FS, D4, D5, Fb, CL
16 1 byte Field length, bin (see 2b) all \ FS,CL: for C field type,
17 1 byte decimal count, bin all / both used for fld lng
18 2 0,0 reserved all
20 1 byte Work area ID D4, D5
0x00 unused FS, D3, Fb, Fp, CL
21 2 n,n multi-user dBase D3, D4, D5
0,0 ignored FS, Fb, Fp, CL
23 1 0x01 Set Fields D3, D4, D5
0x00 ignored FS, Fb, Fp, CL
24 7 0..0 reserved all
31 1 0x01 Field is in .mdx index D4, D5
0x00 ignored FS, D3, Fb, Fp, CL
2b. Field type and size in dbf header, field descriptor (1 byte)
Size Type Description/Storage Applies for
(supported by)
C 1..n Char ASCII (OEM code page chars) all
rest= space, not \0 term.
n = 1..64kb (using deci count) FS
n = 1..32kb (using deci count) Fp, CL
n = 1..254 all
D8 Date 8 Ascii digits (0..9) in the all
YYYYMMDD format
F 1..n Numeric Ascii digits (-.0123456789) FS, D4, D5, Fp
variable pos. of float.point
n = 1..20
N 1..n Numeric Ascii digits (-.0123456789) all
fix posit/no float.point
n = 1..20 FS, Fp, CL
n = 1..18 D3, D4, D5, Fb
L1 Logical Ascii chars (YyNnTtFf space) FS, D3, Fb, Fp, CL
Ascii chars (YyNnTtFf ?) D4, D5 (FS)
M 10 Memo 10 digits repres. the start all
block posit. in .dbt file, or
10spaces if no entry in memo
A to Z of C 787
72.8 EXE
72.8.1 Old EXE format (EXE MZ)
.EXE - DOS EXE File Structure
Offset Size Description
00 word "MZ" or “ZM”- Link file .EXE signature (Mark Zbikowski?)
02 word length of image mod 512
04 word size of file in 512 byte pages
06 word number of relocation items following header
08 word size of header in 16 byte paragraphs, used to locate
the beginning of the load module
0A word min # of paragraphs needed to run program
0C word max # of paragraphs the program would like
0E word offset in load module of stack segment (in paras)
10 word initial SP value to be loaded
12 word negative checksum of pgm used while by EXEC loads pgm
14 word program entry point, (initial IP value)
16 word offset in load module of the code segment (in paras)
18 word offset in .EXE file of first relocation item overlay number (0 for
1A word root program)
• relocation table and the program load module follow the header
• relocation entries are 32 bit values representing the offset into the load module
needing patched
• once the relocatable item is found, the CS register is added to the value found at the
calculated offset
Information Block
The information block in the Windows header contains the linker version number, the lengths
of various tables that further describe the executable file, the offsets from the beginning of the
header to the beginning of these tables, the heap and stack sizes, and so on. The following list
summarizes the contents of the header information block (the locations are relative to the
beginning of the block):
Location Description
00h Specifies the signature word. The low byte contains "N" (4Eh) and the high byte
contains "E" (45h).
02h Specifies the linker version number.
03h Specifies the linker revision number.
04h Specifies the offset to the entry table (relative to the beginning of the header).
Bit Meaning
0 The linker sets this bit if the executable-file format is SINGLEDATA. An executable
file with this format contains one data segment. This bit is set if the file is a
dynamic-link library (DLL).
1 The linker sets this bit if the executable-file format is MULTIPLEDATA. An executable
file with this format contains multiple data segments. This bit is set if the file is a
Windows application.
If neither bit 0 nor bit 1 is set, the executable-file format is NOAUTODATA. An
executable file with this format does not contain an automatic data segment.
2 Reserved.
3 Reserved.
8 Reserved.
9 Reserved.
11 If this bit is set, the first segment in the executable file contains code that loads the
application.
13 If this bit is set, the linker detects errors at link time but still creates an executable
file.
14 Reserved.
15 If this bit is set, the executable file is a library module.
If bit 15 is set, the CS:IP registers point to an initialization procedure called with the
value in the AX register equal to the module handle. The initialization procedure must execute
a far return to the caller. If the procedure is successful, the value in AX is nonzero. Otherwise,
the value in AX is zero. The value in the DS register is set to the library's data segment if
SINGLEDATA is set. Otherwise, DS is set to the data segment of the application that loads the
library.
790 A to Z of C
0Eh Specifies the automatic data segment number. (0Eh is zero if the SINGLEDATA and
MULTIPLEDATA bits are cleared.)
10h Specifies the initial size, in bytes, of the local heap. This value is zero if there is no local
allocation.
12h Specifies the initial size, in bytes, of the stack. This value is zero if the SS register value
does not equal the DS register value.
14h Specifies the segment:offset value of CS:IP.
18h Specifies the segment:offset value of SS:SP.
The value specified in SS is an index to the module's segment table. The first entry
in the segment table corresponds to segment number 1. If SS addresses the automatic data
segment and SP is zero, SP is set to the address obtained by adding the size of the automatic
data segment to the size of the stack.
Bit Meaning
0 Operating system format is unknown.
1 Reserved.
2 Operating system is Microsoft Windows.
3 Reserved.
4 Reserved.
A to Z of C 791
37h Specifies additional information about the executable file. It can be one or more of the
following values:
Bit Meaning
1 If this bit is set, the executable file contains a Windows 2.x application that runs in
version 3.x protected mode.
2 If this bit is set, the executable file contains a Windows 2.x application that supports
proportional fonts.
3 If this bit is set, the executable file contains a fast-load area.
38h Specifies the offset, in sectors, to the beginning of the fast-load area. (Only Windows
uses this value.)
3Ah Specifies the length, in sectors, of the fast-load area. (Only Windows uses this value.)
3Ch Reserved.
3Eh Specifies the expected version number for Windows. (Only Windows uses this value.)
Segment Table
The segment table contains information that describes each segment in an executable file.
This information includes the segment length, segment type, and segment-relocation data.
The following list summarizes the values found in the segment table (the locations are relative
to the beginning of each entry):
Location Description
00h Specifies the offset, in sectors, to the segment data (relative to the beginning of
the file). A value of zero means no data exists.
02h Specifies the length, in bytes, of the segment, in the file. A value of zero indicates
that the segment length is 64K, unless the selector offset is also zero.
04h Specifies flags that describe the contents of the executable file. This value can be
one or more of the following:
Bit Meaning
0 If this bit is set, the segment is a data segment. Otherwise, the segment is a code
segment.
1 If this bit is set, the loader has allocated memory for the segment.
2 If this bit is set, the segment is loaded.
3 Reserved.
4 If this bit is set, the segment type is MOVABLE. Otherwise, the segment type is FIXED.
5 If this bit is set, the segment type is PURE or SHAREABLE. Otherwise, the segment type
is IMPURE or NONSHAREABLE.
6 If this bit is set, the segment type is PRELOAD. Otherwise, the segment type is
LOADONCALL.
7 If this bit is set and the segment is a code segment, the segment type is
EXECUTEONLY.
If this bit is set and the segment is a data segment, the segment type is READONLY.
792 A to Z of C
Bit Meaning
8 If this bit is set, the segment contains relocation data.
9 Reserved.
10 Reserved.
11 Reserved.
12 If this bit is set, the segment is discardable.
13 Reserved.
14 Reserved.
15 Reserved.
06h Specifies the minimum allocation size of the segment, in bytes. A value of zero
indicates that the minimum allocation size is 64K.
Resource Table
The resource table describes and identifies the location of each resource in the executable file.
The table has the following form:
WORD rscAlignShift;
TYPEINFO rscTypes[];
WORD rscEndTypes;
BYTE rscResourceNames[];
BYTE rscEndNames;
rscAlignShift Specifies the alignment shift count for resource data. When the shift
count is used as an exponent of 2, the resulting value specifies the
factor, in bytes, for computing the location of a resource in the
executable file.
rscTypes Specifies an array of TYPEINFO structures containing information about
resource types. There must be one TYPEINFO structure for each type of
resource in the executable file.
rscEndTypes Specifies the end of the resource type definitions. This member must be
zero.
RscResourceNames Specifies the names (if any) associated with the resources in this table.
Each name is stored as consecutive bytes; the first byte specifies the
number of characters in the name.
rscEndNames Specifies the end of the resource names and the end of the resource
table.
This member must be zero.
Type Information
rtTypeID Specifies the type identifier of the resource. This integer value is either a resource-
type value or an offset to a resource-type name. If the high bit in this member is
set (0x8000), the value is one of the following resource-type values:
If the high bit of the value in this member is not set, the value represents an offset, in bytes
relative to the beginning of the resource table, to a name in the rscResourceNames member.
rtResourceCount Specifies the number of resources of this type in the executable file.
rtReserved Reserved.
rtNameInfo Specifies an array of NAMEINFO structures containing information about
individual resources.
The rtResourceCount member specifies the number of structures in the array.
Name Information
Value Meaning
0x0010 Resource is movable (MOVEABLE). Otherwise, it is fixed.
0x0020 Resource can be shared (PURE).
0x0040 Resource is preloaded (PRELOAD). Otherwise, it is loaded on demand.
Resident-Name Table
The resident-name table contains strings that identify exported functions in the executable
file. As the name implies, these strings are resident in system memory and are never
discarded. The resident-name strings are case-sensitive and are not null-terminated. The
following list summarizes the values found in the resident-name table (the locations are
relative to the beginning of each entry):
Location Description
00h Specifies the length of a string. If there are no more strings in the table, this
value is zero.
01h - xxh Specifies the resident-name text. This string is case-sensitive and is not null-
terminated.
xxh + 01h Specifies an ordinal number that identifies the string. This number is an index
into the entry table.
Module-Reference Table
The module-reference table contains offsets for module names stored in the imported-name
table. Each entry in this table is 2 bytes long.
Imported-Name Table
The imported-name table contains the names of modules that the executable file imports.
Each entry contains two parts: a single byte that specifies the length of the string and the
string itself. The strings in this table are not null-terminated.
Entry Table
The entry table contains bundles of entry points from the executable file (the linker generates
each bundle). The numbering system for these ordinal values is 1-based--that is, the ordinal
value corresponding to the first entry point is 1. The linker generates the densest possible
bundles under the restriction that it cannot reorder the entry points. This restriction is
necessary because other executable files may refer to entry points within a given bundle by
their ordinal values. The entry-table data is organized by bundle, each of which begins with a
2-byte header. The first byte of the header specifies the number of entries in the bundle (a
value of 00h designates the end of the table). The second byte specifies whether the
corresponding segment is movable or fixed. If the value in this byte is 0FFh, the segment is
movable. If the value in this byte is 0FEh, the entry does not refer to a segment but refers,
instead, to a constant defined within the module. If the value in this byte is neither 0FFh nor
0FEh, it is a segment index.
For movable segments, each entry consists of 6 bytes and has the following form:
Location Description
00h Specifies a byte value. This value can be a combination of the following bits:
Bit(s) Meaning
0 If this bit is set, the entry is exported.
1 If this bit is set, the segment uses a global (shared) data segment.
3-7 If the executable file contains code that performs ring transitions, these bits specify
the number of words that compose the stack. At the time of the ring transition, these
words must be copied from one ring to the other.
For fixed segments, each entry consists of 3 bytes and has the following form:
Location Description
00h Specifies a byte value. This value can be a combination of the following bits:
796 A to Z of C
Bit(s) Meaning
0 If this bit is set, the entry is exported.
1 If this bit is set, the entry uses a global (shared) data segment. (This may be set only
for SINGLEDATA library modules.)
3-7 If the executable file contains code that performs ring transitions, these bits specify
the number of words that compose the stack. At the time of the ring transition, these
words must be copied from one ring to the other.
Nonresident-Name Table
The nonresident-name table contains strings that identify exported functions in the executable
file. As the name implies, these strings are not always resident in system memory and are
discardable. The nonresident-name strings are case-sensitive; they are not null-terminated.
The following list summarizes the values found in the nonresident-name table (the specified
locations are relative to the beginning of each entry):
Location Description
00h Specifies the length, in bytes, of a string. If this byte is 00h, there are no more
strings in the table.
01h - xxh Specifies the nonresident-name text. This string is case-sensitive and is not null-
terminated.
xx + 01h Specifies an ordinal number that is an index to the entry table.
The first name that appears in the nonresident-name table is the module description string
(which was specified in the module-definition file).
Code and data segments follow the Windows header. Some of the code segments
may contain calls to functions in other segments and may, therefore, require relocation data
to resolve those references. This relocation data is stored in a relocation table that appears
immediately after the code or data in the segment. The first 2 bytes in this table specify the
number of relocation items the table contains. A relocation item is a collection of bytes
specifying the following information:
Each relocation item contains 8 bytes of data, the first byte of which specifies one of the
following relocation-address types:
Value Meaning
0 Low byte at the specified offset
2 16-bit selector
3 32-bit pointer
5 16-bit offset
11 48-bit pointer
13 32-bit offset
Value Meaning
0 Internal reference
1 Imported ordinal
2 Imported name
3 OSFIXUP
The third and fourth bytes specify the offset of the relocation item within the segment.
If the relocation type is imported ordinal, the fifth and sixth bytes specify an index to a
module's reference table and the seventh and eighth bytes specify a function ordinal value.
If the relocation type is imported name, the fifth and sixth bytes specify an index to a
module's reference table and the seventh and eighth bytes specify an offset to an imported-
name table. If the relocation type is internal reference and the segment is fixed, the fifth byte
specifies the segment number, the sixth byte is zero, and the seventh and eighth bytes specify
an offset to the segment. If the relocation type is internal reference and the segment is
movable, the fifth byte specifies 0FFh, the sixth byte is zero; and the seventh and eighth bytes
specify an ordinal value found in the segment's entry table.
72.9 GIF
The Graphics Interchange Format (tm) was created by Compuserve Inc. as a standard
for the storage and transmission of raster-based graphics information, i.e. images. A GIF
file may contain several images, which are to be displayed overlapping and without any
delay betwenn the images. The image data itself is compressed using a LZW scheme. Please
note that the LZW algorithm is patented by UniSys and that since Jan.1995 royalties to
Compuserve are due for every software that implements GIF images. The GIF file consists of
a global GIF header, one or more image blocks and optionally some GIF extensions.
798 A to Z of C
The global color map immediately follows the screen descriptor and has the size
(2**BitsPerPixel), and has the RGB colors for each color index. 0 is none, 255 is full intensity.
The bytes are stored in the following format :
After the first picture, there may be more pictures attached in the file whic overlay
the first picture or parts of the first picture. The Image Descriptor defines the actual
placement and extents of the following image within the space defined in the Screen
Descriptor. Each Image Descriptor is introduced by an image separator character. The role
of the Image Separator is simply to provide a synchronization character to introduce an
Image Descriptor, the image separator is defined as ",", 02Ch, Any characters encountered
between the end of a previous image and the image separator character are to be ignored.
The format of the Image descriptor looks like this :
To provide for some possibility of an extension of the GIF files, a special extension
block introducer can be added after the GIF data block. The block has the following
structure :
A to Z of C 799
72.10 ICO
An icon-resource file contains image data for icons used by Windows applications. The file
consists of an icon directory identifying the number and types of icon images in the file, plus
one or more icon images. The default filename extension for an icon-resource file is .ICO.
Icon Directory
Each icon-resource file starts with an icon directory. The icon directory, defined as an ICONDIR
structure, specifies the number of icons in the resource and the dimensions and color format
of each icon image. The ICONDIR structure has the following form:
struct IconDirectoryEntry {
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
};
800 A to Z of C
Icon Image
Each icon-resource file contains one icon image for each image identified in the icon directory.
An icon image consists of an icon-image header, a color table, an XOR mask, and an AND
mask. The icon image has the following form:
BITMAPINFOHEADER icHeader;
RGBQUAD icColors[];
BYTE icXOR[];
BYTE icAND[];
The XOR mask, immediately following the color table, is an array of BYTE values representing
consecutive rows of a bitmap. The bitmap defines the basic shape and color of the icon image.
As with the bitmap bits in a bitmap file, the bitmap data in an icon-resource file is organized in
scan lines, with each byte representing one or more pixels, as defined by the color format. For
more information about these bitmap bits, see Section "Bitmap-File Formats."
The AND mask, immediately following the XOR mask, is an array of BYTE values, representing
a monochrome bitmap with the same width and height as the XOR mask. The array is
organized in scan lines, with each byte representing 8 pixels.
When Windows draws an icon, it uses the AND and XOR masks to combine the icon image with
the pixels already on the display surface. Windows first applies the AND mask by using a
A to Z of C 801
bitwise AND operation; this preserves or removes existing pixel color. Windows then applies
the XOR mask by using a bitwise XOR operation. This sets the final color for each pixel.
The following illustration shows the XOR and AND masks that create a monochrome icon
(measuring 8 pixels by 8 pixels) in the form of an uppercase K:
Windows detects the resolution of the current display and matches it against the width and
height specified for each version of the icon image. If Windows determines that there is an
exact match between an icon image and the current device, it uses the matching image.
Otherwise, it selects the closest match and stretches the image to the proper size.
If an icon-resource file contains more than one image for a particular resolution, Windows
uses the icon image that most closely matches the color capabilities of the current display. If
no image matches the device capabilities exactly, Windows selects the image that has the
greatest number of colors without exceeding the number of display colors. If all images
exceed the color capabilities of the current display, Windows uses the icon image with the
least number of colors.
72.11 JPEG
Format of a JPEG block (all data is in Motorola byte order) :
72.12 LZH
The LHArc/LHA archiver is a multi platform archiver made by Haruyasu Yoshizaki,
which has a relatively good compression. It uses more or less the same technology like the
ZIP programs by Phil Katz. There was a hack named "ICE", which had only the graphic
characters displayed on decompression changed.
(Table 0005)
LHArc compression types
"0" - No compression
"1" - LZW, 4K buffer, Huffman for upper 6 bits of position
"2" - unknown
"3" - unknown
"4" - LZW, Arithmetic Encoding
"5" - LZW, Arithmetic Encoding
"s" - LHa 2.x archive?
"\" - LHa 2.x archive?
"d" - LHa 2.x archive?
72.13 MIDI
The MIDI file format is used to store MIDI song data on disk. The discussed
version of the MIDI file spec is the approved MIDI Manufacturers' Associations format
version 0.06 of (3/88). The contact address is listed in the adresses file. Version 1.0 is
technically identical but the description has been rewritten. The description was made by
Dave Oppenheim, most of the text was taken right out of his document.
MIDI files contain one or more MIDI streams, with time information for each event. Song,
sequence, and track structures, tempo and time signature information, are all
A to Z of C 803
supported. Track names and other descriptive information may be stored with the MIDI
data. This format supports multiple tracks and multiple sequences so that if the user of a
program which supports multiple tracks intends to move a file to another one, this format can
allow that to happen.
The MIDI files are block oriented files, currently only 2 block types are defined, header and
track data. Opposed to the IFF and RIFF formats, no global header is given, so that the
validation must be done by adding the different block sizes.
A MIDI file always starts with a header block, and is followed by one or more track block.
Some numbers in MTrk blocks are represented in a form called a variable-length quantity.
These numbers are represented 7 bits per byte, most significant bits first. All bytes except
the last have bit 7 set, and the last byte has bit 7 clear. If the number is between 0 and 127,
it is thus represented exactly as one byte. Since this explanation might not be too clear,
some examples :
Number (hex) Representation (hex)
00000000 00
00000040 40
0000007F 7F
00000080 81 00
00002000 C0 00
00003FFF FF 7F
001FFFFF FF FF 7F
08000000 C0 80 80 00
0FFFFFFF FF FF FF 7F
804 A to Z of C
The largest number which is allowed is 0FFFFFFF so that the variable-length representation
must fit in 32 bits in a routine to write variable-length numbers.
Each track block contains one or more MIDI events, each event consists of a delta-time and
the number of the event. The delta-time is stored as a variable-length quantity and
represents the time to delay before the following event. A delta-time of 0 means, that
the event occurs simultaneous with the previous event or occurs right at the start of a
track. The delta-time unit is specified in the header block.
Three types of events are defined, MIDI event, system exclusive event and meta event.
The first event in a file must specify status; delta-time itself is not an event. Meta
events are non-MIDI informations.
A few meta-events are defined. It is not required for every program to support every meta-
event. Meta-events initially defined include:
it will be at the beginning of the file. This event should be the first event in the first track
block, at time 0.
FF 2F 00 End of Track
This event is not optional. It is included so that an exact ending point may be specified for the
track, so that it has an exact length, which is necessary for tracks which are looped or
concatenated.
FF 54 05 hr mn se fr ff SMPTE Offset
This event, if present, designates the SMPTE time at which the track block is supposed to
start. It should be present at the beginning of the track, that is, before any nonzero
delta-times, and before any transmittable MIDI events. The hour must be encoded with
the SMPTE format, just as it is in MIDI Time Code. In a format 1 file, the SMPTE Offset must
be stored with the tempo map, and has no meaning in any of the other tracks. The ff field
contains fractional frames, in 100ths of a frame, even in SMPTE-based tracks which specify
a different frame subdivision for delta-times.
806 A to Z of C
FF 58 04 nn dd cc bb Time Signature
The time signature is expressed as four numbers. nn and dd represent the numerator and
denominator of the time signature as it would be notated. The denominator is a negative
power of two: 2 represents a quarter-note, 3 represents an eighth-note, etc. The cc
parameter expresses the number of MIDI clocks in a metronome click. The bb parameter
expresses the number of notated 32nd-notes in a MIDI quarter- note (24 MIDI Clocks).
FF 59 02 sf mi Key Signature
sf = -7: 7 flats
sf = -1: 1 flat
sf = 0: key of C
sf = 1: 1 sharp
sf = 7: 7 sharps
mi = 0: major key
mi = 1: minor key
72.14 PCX
The PCX files are created by the programs of the ZSoft Paintbrush family and the
FRIEZE package by the same manufacturer. A PCX file contains only one image, the data for
this image and possibly palette information for this image. The encoding scheme used for
PCX encoding is a simple RLE mechanism, see ALGRTHMS.txt for further information. A PCX
image is stored from the upper scan line to the lower scan line.
The size of a decoded scan line is always an even number, thus one additional
byte should always be allocated for the decoding buffer.
The header has a fixed size of 128 bytes and looks like this :
A to Z of C 807
The space needed to decode a single scan line is "NCP"*"NBS" bytes, the last byte may be a
junk byte which is not displayed. After the image data, if the version number is 5 (or
greater?) there possibly is a VGA color palette. The color ranges from 0 to 255, 0 is zero
intensity, 255 is full intensity. The palette has the following format :
72.15 PIF
The Program Information Files have stayed a long time with the PC. They origi- nated
from IBMs Topview, were carried on by DoubleView and DesqView, and today they are
used by Windows and Windows NT. The PIF files store additional information about
executables that are foreign to the running multitasking system such as ressource usage,
keyboard and mouse virtualization and hotkeys. The original (Topview) PIF had a size of
808 A to Z of C
171h bytes, after that, there come the various extensions for the different operating
environments. The different extensions are discussed in their own sections.
72.16 RTF
RTF text is a form of encoding of various text formatting properties, document
structures, and document properties, using the printable ASCII character set. Special
characters can be also thus encoded, although RTF does not prevent the utilization of
character codes outside the ASCII printable set. The main encoding mechanism of "control
words" provides a name space that may be later used to expand the realm of RTF with
macros, programming, etc.
1. BASIC INGREDIENTS
. a digit or - means that a parameter follows. The following digit sequence is then delimited by
a space or any other non-letter-or-digit as for control words.
A to Z of C 809
. any other non-letter-or digit: terminates the control word, but is not a part of the control
word.
By "letter:, here we mean just the upper and lower case ASCII letters.
Notes: control symbols are compact, but there are not too many of them. The number of
possible control words are not limited.
The parameter is partially incorporated in control symbols, so that a program that does not
understand a control symbol can recognize and ignore the corresponding parameter as well.
In addition to control words and control symbols, there are also the braces:
and to delineate document structure - such as the footnotes, headers, title, and so on. The
control words, control symbols, and braces constitute control information. All other characters
in RTF text constitute "plain text".
Since the characters \, {, and } have specific uses in RTF, the control symbols \\,\{, and \}
are provided to express the corresponding plain characters.
1. the "destination" or part of the document that the plain text is building up.
2. the character formatting properties - such as bold or italic.
3. the paragraph formatting properties - such as justified.
4. the section formatting properties - such as number of columns.
Collecting and properly disposing of the remaining "plain text" as directed by the current
group state.
1. if ={
2. if =}
unstack current state from stack. this will change the state in general.
3. if =\
A. Change destination:
change destination to the destination described in the entry.
Most destination changes are legal only immediately after a {.
Other restrictions may also apply (for example, footnotes may not be nested.)
B. Change formatting property:
The symbol table entry will describe the property and whether the parameter
is required.
C. Special character:
The symbol table entry will describe the character code..
goto 4.
D. End of paragraph
This could be viewed as just a special character.
E. End of section
This could be viewed as just a special character.
F. Ignore
3. SPECIAL CHARACTERS
The special characters are explained as they exist in Mac Word. Clearly, other characters may
be added for interchange with other programs. If a character name is not recognized by a
reader, according to the rules described above, it will be simply ignored.
A to Z of C 811
For simplicity of operation, the ASCII codes 9 and 10 will be accepted as \tab and \par
respectively. ASCII 13 will be ignored. The control code \<10> will be ignored. It may be used
to include "soft" carriage returns for easier readability but which will have no effect on the
interpretation.
4. DESTINATIONS
The change of destination will reset all properties to default. Changes are legal only at the
beginning of a group (by group here we mean the text and controls enclosed in braces.)
\rtf<param> The destination is the document. The parameter is the version number of the
writer. This destination preceded by { the beginnings of RTF documents and
the corresponding } marks the end. Legal only once after the initial {. Small
scale interchange of RTF where other methods for marking the end of string
are available, as in a string constant, need not include this identification but
will start with this destination as the default.
\pict The destination is a picture. The group must immediately follow a \chpict
character. The plain text describes the picture as a hex dump (string of
characters 0,1,...9, a, ..., e, f.)
\footnote The destination is a footnote text. The group must immediately follow the
footnote reference character(s).
\header The destination is the header text for the current section. The group must
precede the first plain text character in the section.
\headerl Same as above, but header for left-hand pages.
\headerr Same as above, but header for right-hand pages.
\headerf Same as above, but header for first page.
\footer Same as above, but footer.
\footerl Same as above, but footer for left-hand pages.
\footerr Same as above, but footer for right-hand pages.
\footerf Same as above, but header for first page.
\ftnsep Same as above, but text is footnote separator
\ftnsepc Same as above, but text is separator for continued footnotes.
812 A to Z of C
9. INFO GROUP
info block.
72.17 SCR
SCR files are Windows EXE files (EXE NE) with the extension SCR. Windows calls the
.SCR file with two command-line options:
For the windows control panel to recognise the screensaver, the program's module
description string must begin with SCRNSAVE: (in uppercase). So, if writing a Visual Basic
screensaver, simply set the application title to something like "SCRNSAVE:My Screensaver"
To create a new screen saver simply write a program that checks the command-line
option when starting and performs the appropriate action. The display should use a full-
screen window (usually with a black background) and should end when any key is pressed or
when the mouse is moved.
Compile the program to .SCR.
72.18 WAV
The Windows .WAV files are RIFF format files. Some programs expect the fmt block
right behind the RIFF header itself, so your programs should write out this block as the first
block in the RIFF file.
This block contains the raw sample data. The necessary information for playback is contained
in the [fmt ] block.
RiffBLOCK [fmt ]
This block contains the data necessary for playback of the sound files. Note the blank after fmt
!
OFFSET Count TYPE Description
0000h 1 word Format tag
1 = PCM (raw sample data)
2 etc. for APCDM, a-Law, u-Law ...
0002h 1 word Channels (1=mono,2=stereo,...)
0004h 1 dword Sampling rate
0008h 1 dword Average bytes per second (=sampling
rate*channels)
000Ch 1 word Block alignment / reserved ??
000Eh 1 word Bits per sample (8/12/16-bit samples)
RiffBLOCK [loop]
This block is for looped samples. Very few programs support this block, but if your program
changes the wave file, it should preserve any unknown blocks.
72.19 ZIP
Following is the official documenation of PKZIP.
PKZIP® Application Note
File: APPNOTE.TXT - .ZIP File Format Specification
Version: 4.0
Revised: 11/01/2000
I. Disclaimer
B. File data
C. Data descriptor
E. Explanation of fields
A to Z of C 817
F. General notes
V. Imploding - Method 6
VIII. Decryption
I. Disclaimer
Although PKWARE will attempt to supply current and accurate information relating to
its file formats, algorithms, and the subject programs, the possibility of error can not
be eliminated. PKWARE therefore expressly disclaims any warranty that the
information contained in the associated materials relating to the subject programs
and/or the format of the files created or accessed by the subject programs and/or the
algorithms used by the subject programs, or any other matter, is current, correct or
accurate as delivered. Any risk of damage due to any possible inaccurate information
is assumed by the user of the information. Furthermore, the information relating to
the subject programs and/or the file formats created or accessed by the subject
programs and/or the algorithms used by the subject programs is subject to change
without notice.
II. General Format of a ZIP file
Files stored in arbitrary order. Large zipfiles can span multiple diskette media or be
split into user-defined segment sizes. The minimum user-defined segment size for a
split .ZIP file is 64K..
4 - VM/CMS
5 - Atari ST
6 - OS/2 H.P.F.S.
7 - Macintosh
8 - Z-System
9 - CP/M
10 - Windows NTFS
11 thru 255 - unused
The lower byte indicates the version number of the software
used to encode the file. The value/10 indicates the major
version number, and the value mod 10 is the minor version
number.
version needed to extract (2 bytes)
The minimum software version needed to extract the file,
mapped as above.
general purpose bit flag: (2 bytes)
Bit 0: If set, indicates that the file is encrypted.
(For Method 6 - Imploding)
Bit 1: If the compression method used was type 6, Imploding, then
this bit, if set, indicates an 8K sliding dictionary was used. If
clear, then a 4K sliding dictionary was used.
Bit 2: If the compression method used was type 6, Imploding, then
this bit, if set, indicates an 3 Shannon-Fano trees were used to
encode the sliding dictionary output. If clear, then 2 Shannon-
Fano trees were used.
(For Methods 8 and 9 - Deflating)
Bit 2 Bit 1
0 0 Normal (-en) compression option was used.
0 1 Maximum (-exx/-ex) compression option was used.
1 0 Fast (-ef) compression option was used.
1 1 Super Fast (-es) compression option was used.
Note: Bits 1 and 2 are undefined if the compression method is any
other.
Bit 3: If this bit is set, the fields crc-32, compressed size and
uncompressed size are set to zero in the local header. The
correct values are put in the data descriptor immediately
following the compressed data. (Note: PKZIP version 2.04g for
DOS only recognizes this bit for method 8 compression, newer
versions of PKZIP recognize this bit for any compression
method.)
Bit 4: Reserved for use with method 8, for enhanced deflating.
Bit 5: If this bit is set, this indicates that the file is compressed
patched data.(Note: Requires PKZIP version 2.70 or greater)
A to Z of C 821
header1+data1 + header2+data2 . . .
0xfd4a SMS/QDOS
The Data Size field indicates the size of the following data
block. Programs can use this value to skip to the next header
block, passing over any data blocks that are not of interest.
Note: As stated above, the size of the entire .ZIP file header,
including the filename, comment, and extra field should not
exceed 64K in size.
Note: At this time, the Mtime, Atime and Ctime values may be
used on any Win32 system.
Value Size Description
826 A to Z of C
SData
Value Size Description
Version 2 bytes Version number, 0x0001 for now
StoreD (variable) Actual store data
The StoreD member is suitable for passing as the pbData
member of a CRYPT_DATA_BLOB to the CertOpenStore()
function in Microsoft's CryptoAPI. The SSize member above will
be cbData + 6, where cbData is the cbData member of the
same CRYPT_DATA_BLOB. The encoding type to pass to
CertOpenStore() should be
PKCS_7_ANS_ENCODING | X509_ASN_ENCODING.
-X.509 Certificate ID and Signature for individual file
828 A to Z of C
Method
Value Size Description
Version 2 bytes Version number, 0x0001 for now
AlgID 2 bytes Algorithm ID used for signing
IDSize 2 bytes Size of Certificate ID data
CertID (variable) Certificate ID data
SigSize 2 bytes Size of Signature data
Sig (variable) Signature data
CertID
Value Size Description
Size1 4 bytes Size of CertID, should be (IDSize - 4)
A bug in version one causes this value to
Size1 4 bytes
appear twice.
IssSize 4 bytes Issuer data size
Issuer (variable) Issuer data
SerSize 4 bytes Serial Number size
Serial (variable) Serial Number data
The Issuer and IssSize members are suitable for creating a
CRYPT_DATA_BLOB to be the Issuer member of a CERT_INFO
struct. The Serial and SerSize members would be the
SerialNumber member of the same CERT_INFO struct. This
struct would be used to find the certificate in the store the file
was signed with. Those structures are from the MS CryptoAPI.
Sig and SigSize are the actual signature data and size
generated by signing the file with the MS CryptoAPI using a
hash created with the given AlgID.
-X.509 Certificate ID and Signature for central directory
This field contains the information about which certificate in
the PKCS#7 Store was used to sign the central directory. It
should only appear with the first central directory record, along
A to Z of C 829
with the store. The data structure is the same as the CID,
except that SigSize will be 0, and there will be no Sig member.
This field is also kept after the last central directory record, as
the signature data (ID 0x05054b50, it looks like a central
directory record of a different type). This second copy of the
data is the Signature Data member of the record, and will have
a SigSize that is non-zero, and will have Sig data.
2. String fields are not null terminated, since the length is given
explicitly.
3. Local headers should not span disk boundaries. Also, even though the
central directory can span disk boundaries, no single record in the
central directory should be split across disks.
4. The entries in the central directory may not necessarily be in the same
order that files appear in the .ZIP file.
b. When the table becomes full, total clearing is not performed. Rather, when the
compressor emits the code sequence 256,2 (decimal), the decompressor
should clear all leaf nodes from the Ziv-Lempel tree, and continue to use the
current code size. The nodes that are cleared from the Ziv-Lempel tree are
then re-used, with the lowest code value re-used first, and the highest code
value re-used last. The compressor can emit the sequence 256,2 at any time.
IV. Expanding - Methods 2-5
The Reducing algorithm is actually a combination of two distinct algorithms. The first
algorithm compresses repeated byte sequences, and the second algorithm takes the
compressed stream from the first algorithm and applies a probabilistic compression
method.
The probabilistic compression stores an array of 'follower sets' S(j), for j=0 to 255,
corresponding to each possible ASCII character. Each set contains between 0 and 32
characters, to be denoted as S(j)[0],...,S(j)[m], where m<32. The sets are stored at
the beginning of the data area for a Reduced file, in reverse order, with S(255) first,
and S(0) last.
The sets are encoded as { N(j), S(j)[0],...,S(j)[N(j)-1] }, where N(j) is the size of set
S(j). N(j) can be 0, in which case the follower set for S(j) is empty. Each N(j) value is
encoded in 6 bits, followed by N(j) eight bit character values corresponding to S(j)[0]
to S(j)[N(j)-1] respectively. If N(j) is 0, then no values for S(j) are stored, and the
value for N(j-1) immediately follows.
Immediately after the follower sets, is the compressed data stream. The compressed
data stream can be interpreted for the probabilistic decompression as follows:
let Last-Character <- 0.
832 A to Z of C
The decompressed stream from above can then be expanded to re-create the original
file as follows:
let State <- 0.
1: if C is non-zero then
let V <- C.
let Len <- L(V)
let State <- F(Len).
otherwise if C is zero then
copy the value 144 (decimal) to the output stream.
let State <- 0
end case
end loop
The functions F,L, and D are dependent on the 'compression factor', 1 through 4, and
are defined as follows:
The Imploding algorithm is actually a combination of two distinct algorithms. The first
algorithm compresses repeated byte sequences using a sliding dictionary. The second
algorithm is used to compress the encoding of the sliding dictionary output, using
multiple Shannon-Fano trees.
The Imploding algorithm can use a 4K or 8K sliding dictionary size. The dictionary size
used can be determined by bit 1 in the general purpose flag word; a 0 bit indicates a
4K dictionary while a 1 bit indicates an 8K dictionary.
The Shannon-Fano trees are stored at the start of the compressed file. The number of
trees stored is defined by bit 2 in the general purpose flag word; a 0 bit indicates two
trees stored, a 1 bit indicates three trees are stored. If 3 trees are stored, the first
Shannon-Fano tree represents the encoding of the Literal characters, the second tree
represents the encoding of the Length information, the third represents the encoding
of the Distance information. When 2 Shannon-Fano trees are stored, the Length tree is
stored first, followed by the Distance tree.
834 A to Z of C
The Literal Shannon-Fano tree, if present is used to represent the entire ASCII
character set, and contains 256 values. This tree is used to compress any data not
compressed by the sliding dictionary algorithm. When this tree is present, the
Minimum Match Length for the sliding dictionary is 3. If this tree is not present, the
Minimum Match Length is 2.
The Length Shannon-Fano tree is used to compress the Length part of the
(length,distance) pairs from the sliding dictionary output. The Length tree contains 64
values, ranging from the Minimum Match Length, to 63 plus the Minimum Match
Length.
The Distance Shannon-Fano tree is used to compress the Distance part of the
(length,distance) pairs from the sliding dictionary output. The Distance tree contains
64 values, ranging from 0 to 63, representing the upper 6 bits of the distance value.
The distance values themselves will be between 0 and the sliding dictionary size,
either 4K or 8K.
The Shannon-Fano trees themselves are stored in a compressed format. The first byte
of the tree data represents the number of bytes of data representing the (compressed)
Shannon-Fano tree minus 1. The remaining bytes represent the Shannon-Fano tree
data encoded as:
High 4 bits: Number of values at this bit length + 1. (1 - 16)
Low 4 bits: Bit Length needed to represent value + 1. (1 - 16)
The Shannon-Fano codes can be constructed from the bit lengths using the following
algorithm:
a. Sort the Bit Lengths in ascending order, while retaining the order of the
original lengths stored in the file.
p. Restore the order of Shannon-Fano codes as originally stored within the file.
Example:
The first byte indicates 3 values in this table. Decoding the bytes:
This would generate the original bit length array of: (3, 3, 3, 3, 3, 2,
4, 4)
There are 8 codes in this table for the values 0 thru 7. Using the
algorithm to obtain the Shannon-Fano codes produces:
Reversed Order Original
Val Sorted Constructed Code Value Restored Length
0: 2 1100000000000000 11 101 3
1: 3 1010000000000000 101 001 3
2: 3 1000000000000000 001 110 3
3: 3 0110000000000000 110 010 3
4: 3 0100000000000000 010 100 3
5: 3 0010000000000000 100 11 2
6: 4 0001000000000000 1000 1000 4
7: 4 0000000000000000 0000 0000 4
The values in the Val, Order Restored and Original Length columns now represent the
Shannon-Fano encoding tree that can be used for decoding the Shannon-Fano
encoded data. How to parse the variable length Shannon-Fano values from the data
stream is beyond the scope of this document. (See the references listed at the end of
this document for more information.) However, traditional decoding schemes used for
Huffman variable length decoding, such as the Greenlaw algorithm, can be
successfully applied.
The compressed data stream begins immediately after the compressed Shannon-Fano
data. The compressed data stream can be interpreted as follows:
loop until done
read 1 bit from input stream.
The Deflate algorithm is similar to the Implode algorithm using a sliding dictionary of
up to 32K with secondary compression from Huffman/Shannon-Fano codes.
The compressed data is stored in blocks with a header describing the block and the
Huffman codes used in the data block. The header format is as follows:
Bit 0: Last Block bit This bit is set to 1 if this is the last compressed block in the
data.
Bits 1-2: Block type
00 (0) - Block is stored - All stored data is byte aligned. Skip bits until next byte, then
next word = block length, followed by the ones compliment of the block
length word. Remaining data in block is the stored data.
01 (1) - Use fixed Huffman codes for literal and distance codes.
Lit Code Bits Dist Code Bits
0 - 143 8 0 - 31 5
A to Z of C 837
144 - 255 9
256 - 279 7
280 - 287 8
Literal codes 286-287 and distance codes 30-31 are never used but
participate in the huffman construction.
10 (2) - Dynamic Huffman codes. (See expanding Huffman codes)
11 (3) - Reserved - Flag a "Error in compressed data" if seen.
Expanding Huffman Codes
If the data block is stored with dynamic Huffman codes, the Huffman codes are sent in
the following compressed format:
5 Bits: # of Literal codes sent - 256 (256 - 286)
All other codes are never sent
5 Bits: # of Dist codes - 1 (1 - 32)
4 Bits: # of Bit Length codes - 3 (3 - 19)
The Huffman codes are sent as bit lengths and the codes are built as described in the
implode algorithm. The bit lengths themselves are compressed with Huffman codes.
There are 19 bit length codes:
0 - 15: Represent bit lengths of 0 - 15
16: Copy the previous bit length 3 - 6 times.
The next 2 bits indicate repeat length (0 = 3, ... ,3 = 6)
Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will
expand to 12 bit lengths of 8 (1 + 6 + 5)
17: Repeat a bit length of 0 for 3 - 10 times. (3 bits of length)
18: Repeat a bit length of 0 for 11 - 138 times (7 bits of length)
The lengths of the bit length codes are sent packed 3 bits per value (0 - 7) in the
following order:
The Huffman codes should be built as described in the Implode algorithm except codes
are assigned starting at the shortest bit length, i.e. the shortest code should be all 0's
rather than all 1's. Also, codes with a bit length of zero do not participate in the tree
construction. The codes are then used to decode the bit lengths for the literal and
distance tables.
The bit lengths for the literal tables are sent first with the number of entries sent
described by the 5 bits sent earlier. There are up to 286 literal characters; the first
256 represent the respective 8 bit character, code 256 represents the End-Of-Block
code, the remaining 29 codes represent copy lengths of 3 thru 258. There are up to 30
distance codes representing distances from 1 thru 32k as described below.
Length Codes
Code Extra Length Code Extra Lengths Code Extra Lengths Code Extra Length(s)
838 A to Z of C
Distance Codes
Co Extra Extra Extra Co Extra
de Bits Distance Code Bits Distance Code Bits Distance de Bits Distance
0 0 1 8 3 17-24 16 7 257-384 24 11 4097-6144
1 0 2 9 3 25-32 17 7 385-512 25 11 6145-8192
2 0 3 10 4 33-48 18 8 513-768 26 12 8193-12288
3 0 4 11 4 49-64 19 8 769-1024 27 12 12289-16384
4 1 5,6 12 5 65-96 20 9 1025-1536 28 13 16385-24576
5 1 7,8 13 5 97-128 21 9 1537-2048 29 13 24577-32768
6 2 9-12 14 6 129-192 22 10 2049-3072
7 2 13-16 15 6 193-256 23 10 3073-4096
The compressed data stream begins immediately after the compressed header data.
The compressed data stream can be interpreted as follows:
do
read header from input stream.
if stored block
skip bits until byte aligned
read count and 1's compliment of count
copy count bytes data block
otherwise
loop until end of block code sent
decode literal character from input stream
if literal < 256
copy character to the output stream
otherwise
if literal = end of block
break from loop
otherwise
decode distance from input stream
The encryption used in PKZIP was generously supplied by Roger Schlafly. PKWARE is
grateful to Mr. Schlafly for his expert help and advice in the field of data encryption.
PKZIP encrypts the compressed data stream. Encrypted files must be decrypted before
they can be extracted.
Each encrypted file has an extra 12 bytes stored at the start of the data area defining
the encryption header for that file. The encryption header is originally set to random
values, and then itself encrypted, using three, 32-bit keys. The key values are
initialized using the supplied encryption password. After each byte is encrypted, the
keys are then updated using pseudo-random number generation techniques in
combination with the same CRC-32 algorithm used in PKZIP and described elsewhere
in this document.
b. Read and decrypt the 12-byte encryption header, further initializing the
encryption keys.
c. Read and decrypt the compressed data stream using the encryption keys.
Step 1 - Initializing the encryption keys
Key(0) <- 305419896
Key(1) <- 591751049
Key(2) <- 878082192
Where crc32(old_crc,char) is a routine that given a CRC value and a character, returns
an updated CRC value after applying the CRC-32 algorithm described elsewhere in this
document.
Step 2 - Decrypting the encryption header
The purpose of this step is to further initialize the encryption keys, based on random
data, to render a plaintext attack on the data ineffective.
Read the 12-byte encryption header into Buffer, in locations
Buffer(0) thru Buffer(11).
Ziv, J. and Lempel, A., "A universal algorithm for sequential data
compression", Communications of the ACM, Volume 30, Number 6,
June 1987, pages 520-540.
72.20 ZOO
The ZOO archive program by Raoul Dhesi is a file compression program now
superceeded in both compression and speed by most other compression programs. The
archive header looks like this :
n = 0 Italic set ! !
n = 1 Graphic set ! !
n = 2 User-Defined Set !
Remap to 80h-FFh
846 A to Z of C
n = 0 USA
n = 1 France
n = 2 Germany
! !
n = 3 United Kingdom
n = 4 Denmark I
! !
n = 5 Sweden
! !
n = 6 Italy ! !
n = 7 Spain ! !
n = 8 Japan ! !
n = 9 Norway ! !
n = 10 Denmark II ! !
n = 11 Spain II ! !
n = 12 Latin America !
n = 13 Korea !
n = 64 Legal
Other Control Codes
Set Absolute Print Position ESC $ n1 n2 ! !
ASCII Table
Scan Code
Test in C
Important Notice
Most of the teachers ask the “undefined” patterns as questions. As they get certain output for their
undefined patterns or programs, they think that their question is right. But it is not so. “Undefined” is not
an exception to Turbo C. So anything undefined means, it applies to both ANSI C and Turbo C.
76.1 ANSI C
1. Which are the valid C identifiers among the following?
(i) a
(ii) a_
(iii) _a
(iv) _
(v) __
(vi) _1
(vii) 1_
(viii) 1
(ix) $s
(x) a-z
2. Comment on the validity of following C code.
int _;
_ = 10;
--_;
3. Comment on the following C code.
int i = 7;
printf( “%d”, ++i * ++i );
4. Comment on the following C code.
int *ptr, a;
ptr = malloc( 5 );
ptr = & a;
76.3 Windows
1. Only one directory of Windows can hold multiple files with same name. What is the
name of that directory?
2. The files we try to store in C:\TEMP> get disappeared when we reboot our system.
Why?
77
“Consider carefully what you hear.”
C Resources
After reading this book, you may want to develop yourself further. The CD
accompanying this book will be a good resource for you. In CD you have a number of
utilities and source code of various utilities.
77.1 Magazine
C / C++ user Journal
It is the most popular Journal for C programmers. It is a must for every C programmer. In
India, a single copy costs Rs.450/-. If you find any difficulty in getting the copy, you may contact
them at www.cuj.com
77.2 Books
1. The C programming Language by Brian W. Kernighan & Dennis M. Ritchie (Second
Edition, PHI)
This book is from the creator of C language. It is often nicked as ‘K&R’ and
‘White book’. This book covers ANSI C. Even though it is small in size, it is rich
with many concepts and ideas. It is a must for all C programmers! It costs only
Rs.95/-
2. Algorithms in C by Robert Sedgewick (Addition Wesley) ISBN 0-201-51425-7
This book explains almost all algorithms through C programs.
3. Calculus with Analytical Geometry by George F. Simmons (Mc Graw Hill) ISBN 0-07-
057419-7
This book is of course a ‘pure’ Mathematics book. But I have never seen such a
well-explained and a neat book in my life. This book will really help you to build
your mathematical skills. It includes Bibliographical notes of famous
Mathematicians.
77.3 Jobs
www.JustCJobs.com
If you are searching for C/C++ jobs, you can try this site. As far as I know
this is the only Job site that is restricted to C/C++ programmers.
860 A to Z of C
77.4 Associations
ACM (Association for Computing Machinery)
This association was established in 1947. It is the oldest computing association.
ACM is known for having many computing researchers as its members. Dennis M. Ritchie,
creator of C language is one of the ACM members. For more details visit www.acm.org
77.5 Websites
Many websites are discontinuing their services. So one cannot give assurance for the
websites and its contents. I suggest you to visit the official website of this book.
www.guideme.itgo.com
There you can find frequently updated useful links.
78
“Remember the LORD in all you do.”
Yet we have seen so many things in C programming! But we didn’t look into the social
aspects. I think, Education without social concern is merely a waste! So let’s look into the pitfalls
in the Education System and Society!
78.4.2 GPL
GNU’s General Public Licence protects the author of the programs. According to the
licence if one provides the binary file, he has to give the source code too. Thus the person who
receives the binary may find the details about the real author. One can modify the program, but he
may not remove the original author as GPL protects the first author of the source. Linux is
appreciated worldwide as it is licensed under GNU’s GPL. So I suggest you to consider GPL, if
you write any new code.
Last Chapter
Alas!…You are on the last chapter of this book! Yes, you have completed this book! It
took about 1½ year for me to complete this book. Writing book is really a marathon running. I
have sacrificed many things because of this book project. I received lots of good and bad
criticisms during the course of this project. All the criticisms really helped me to “tune” this book.
I would like to hear from you too! Now what do you think about this book?
79.2 Errata
This book might contain some errors. I would appreciate you if you notify me any kind of
errors or omissions in this book. For the errata, please visit
http://guideme.itgo.com