3 THE IBM PC PROGRAMMER'S GUIDE TO C
\r
18 This publication remains the property of Matthew Probert. License is
\r
19 hereby given for this work to be freely distibuted in whole under the
\r
20 proviso that credit is given to the author. Sections of this work may be
\r
21 used and distributed without payment under the proviso that credit is
\r
22 given to both this work and the author. Source code occuring in this work
\r
23 may be used within commercial and non-commercial applications without
\r
24 charge and without reference to the author.
\r
29 Matthew Probert is a software consultant working for his own firm,
\r
30 Servile Software. He has been involved with micro-computer software
\r
31 design and programming since the age of eighteen and has been involved
\r
32 with the C programming language for the past ten years.
\r
34 His educational background lies in the non-too distinguished honour of
\r
35 having a nervous break down during his last year of secondary school
\r
36 which resulted in a lack of formal qualifications. However, Matthew has
\r
37 made up for it by achieving a complete recovery and has been studying
\r
38 Psychology with particular attention to behaviourism and conversation
\r
41 His chequered career spans back twelve years during which time he has
\r
42 trained people in the design and use of database management applications,
\r
43 been called upon to design and implement structured methodologies and has
\r
44 been a "good old fashioned" analyst programmer.
\r
46 Matthew Probert is currently researching Artificial Intelligence with
\r
47 particular reference to the application of natural language processing,
\r
48 whereby a computer software package may decode written human language and
\r
49 respond to it in an intelligent manner. He is also monitoring the
\r
50 progress of facilitated communication amongst autistic and children with
\r
51 severe learning and challenging behaviour and hopes one day to be able to
\r
52 develope a computer based mechanism for true and reliable communication
\r
53 between autistic people and the rest of society.
\r
56 Matthew Probert can be contacted via
\r
64 Telephone 01256 478576
\r
69 In 1992, an English software house, Servile Software published a paper
\r
70 entitled "HOW TO C", which sought to introduce computer programmers to
\r
71 the C programming language. That paper was written by Matthew Probert. A
\r
72 follow up effort was "HOW TO CMORE", a document that was also published
\r
73 by Servile Software. Now those two documents have been amalgamated and
\r
74 thoroughly revamped to create this book. I have included loads of new
\r
75 source code that can be lifted directly out of the text.
\r
77 All the program listings have been typed in to the Turbo C compiler,
\r
78 compiled and executed successfully before being imported into this
\r
81 I hope you enjoy my work, and more I hope that you learn to program in C.
\r
82 It really is a great language, there can be no other language that gives
\r
83 the computer the opportunity to live up to the old saying;
\r
86 "To err is human, to make a complete balls up requires a computer!"
\r
92 This document is the result of over ten years experience as a software
\r
93 engineer. This document contains professional source code that is not
\r
94 intended for beginers.
\r
101 The major distinguishing features of the C programming language are;
\r
103 ú block-structured flow-control constructs (typical of most high-level
\r
105 ú freedom to manipulate basic machine objects (eg: bytes) and to refer
\r
106 to them using any particular object view desired (typical of assembly-
\r
108 ú both high-level operations (eg: floating-point arithmetic) and low-
\r
109 level operations (which map closely onto machine-language instructions,
\r
110 thereby offering the means to code in an optimal, yet portable, manner).
\r
112 This book sets out to describe the C programming language, as commonly
\r
113 found with compilers for the IBM PC, to enable a computer programmer with
\r
114 no previous knowledge of the C programming language to program in C using
\r
115 the IBM PC including the ROM facilities provided by the PC and facilities
\r
118 It is assumed that the reader has access to a C compiler, and to the
\r
119 documentation that accompanies it regarding library functions.
\r
121 The example programs were written with Borland's Turbo C, most of the non-
\r
122 standard facilities provided by Turbo C should be found in later releases
\r
127 Differences Between the Various Versions of C
\r
129 The original C (prior to the definitive book by K&R) defined the
\r
130 combination assignment operators (eg: +=, *=, etc.) backwards (ie: they
\r
131 were written =+, =*, etc.). This caused terrible confusion when a
\r
135 was compiled - it could have meant
\r
141 Ritchie soon spotted this ambiguity and changed the language to have
\r
142 these operators written in the now-familiar manner (+=, *=, etc.).
\r
144 The major variations, however, are between K&R C and ANSI C. These can
\r
145 be summarized as follows:
\r
147 ú introduction of function prototypes in declarations; change of
\r
148 function definition preamble to match the style of prototypes;
\r
149 ú introduction of the ellipsis ("...") to show variable-length
\r
150 function argument lists;
\r
151 ú introduction of the keyword `void' (for functions not returning a
\r
152 value) and the type `void *' for generic pointer variables;
\r
153 ú addition of string-merging, token-pasting and stringizing functions
\r
154 in the preprocessor;
\r
155 ú addition of trigraph translation in the preprocessor;
\r
156 ú addition of the `#pragma' directive and formalization of the
\r
157 `declared()' pseudofunction in the preprocessor;
\r
158 ú introduction of multi-byte strings and characters to support non-
\r
160 ú introduction of the `signed' keyword (to complement the `unsigned'
\r
161 keyword when used in integer declarations) and the unary plus (`+')
\r
166 C is a medium level language
\r
168 The powerful facilities offered by C to allow manipulation of direct
\r
169 memory addresses and data, even down to the bit level, along with C's
\r
170 structured approach to programming cause C to be classified as a "medium
\r
171 level" programming language. It possesses fewer ready made facilities
\r
172 than a high level language, such as BASIC, but a higher level of
\r
173 structure than low level Assembler.
\r
178 The original C language as described in; "The C programming language", by
\r
179 Kernighan and Ritchie, provided 27 key words. To those 27 the ANSI
\r
180 standards committee on C have added five more. This confusingly results
\r
181 in two standards for the C language. However, the ANSI standard is
\r
182 quickly taking over from the old K & R standard.
\r
185 The 32 C key words are;
\r
187 auto double int struct
\r
188 break else long switch
\r
189 case enum register typedef
\r
190 char extern return union
\r
191 const float short unsigned
\r
192 continue for signed void
\r
193 default goto sizeof volatile
\r
196 Some C compilers offer additional key words specific to the hardware
\r
197 environment that they operate on. You should be aware of your own C
\r
198 compilers additional key words. Most notably on the PC these are;
\r
207 C programs are written in a structured manner. A collection of code
\r
208 blocks are created that call each other to comprise the complete program.
\r
209 As a structured language C provides various looping and testing commands
\r
213 do-while, for, while, if
\r
215 and the use of jumps, while provided for, are rarely used.
\r
217 A C code block is contained within a pair of curly braces "{ }", and may
\r
218 be a complete procedure, in C terminology called a "function", or a
\r
219 subset of code within a function. For example the following is a code
\r
220 block. The statements within the curly braces are only executed upon
\r
221 satisfaction of the condition that "x < 10";
\r
230 while this, is a complete function code block containing a sub code block
\r
231 as a do-while loop;
\r
239 printf("\nEnter a number between 0 and 10 ");
\r
242 while(x < 0 || x > 10);
\r
246 Notice how every statement line is terminated in a semicolon, unless that
\r
247 statement marks the start of a code block, in which case it is followed
\r
248 by a curly brace. C is a case sensitive but free flow language, spaces
\r
249 between commands are ignored, and therefore the semicolon delimiter is
\r
250 required to mark the end of the command line.
\r
252 Having a freeflow structure the following commands are recognised as the
\r
253 same by the C compiler;
\r
261 The general form of a C program is as follows;
\r
263 compiler preprocessor statements
\r
264 global data declarations
\r
269 return-type main(parameter list)
\r
274 return-type f1(parameter list)
\r
279 return-type f2(parameter list)
\r
286 return-type fn(parameter list)
\r
295 C allows comments to be included in the program. A comment line is
\r
296 defined by being enclosed within "/*" and "*/". Thus the following is a
\r
300 /* This is a legitimate C comment line */
\r
305 C programs are compiled and combined with library functions provided with
\r
306 the C compiler. These libraries are of generally standard functions, the
\r
307 functionality of which are defined in the ANSI standard of the C
\r
308 language, but are provided by the individual C compiler manufacturers to
\r
309 be machine dependant. Thus, the standard library function "printf()"
\r
310 provides the same facilities on a DEC VAX as on an IBM PC, although the
\r
311 actual machine language code in the library is quite different for each.
\r
312 The C programmer however, does not need to know about the internals of
\r
313 the libraries, only that each library function will behave in the same
\r
314 way on any computer.
\r
321 There are four basic types of data in the C language; character, integer,
\r
322 floating point, and valueless that are referred to by the C key words;
\r
324 "char", "int", "float" and "void" respectively.
\r
326 To the basic data types may be added the type modifiers; signed,
\r
327 unsigned, long and short to produce further data types. By default data
\r
328 types are assumed signed, and the signed modifier is rarely used, unless
\r
329 to overide a compiler switch defaulting a data type to unsigned.
\r
331 The size of each data type varies from one hardware platform to another,
\r
332 but the least range of values that can be held is described in the ANSI
\r
333 standard as follows;
\r
339 unsigned char 8 0 to 255
\r
340 int 16 -32767 to 32767
\r
341 unsigned int 16 0 to 65535
\r
342 long int 32 -2147483647 to
\r
344 unsigned long int 32 0 to 4294967295
\r
345 float 32 Six digit precision
\r
346 double 64 Ten digit precision
\r
347 long double 80 Ten digit precision
\r
350 In practice, this means that the data type `char' is particularly
\r
351 suitable for storing flag type variables, such as status codes, which
\r
352 have a limited range of values. The `int' data type can be used, but if
\r
353 the range of values does not exceed 127 (or 255 for an unsigned char),
\r
354 then each declared variable would be wasting storage space.
\r
356 Which real number data type to use: `float', `double' or `long double' is
\r
357 another tricky question. When numeric accuracy is required, for example
\r
358 in an accounting application, the instinct would be to use the `long
\r
359 double', but this requires at least 10 bytes of storage space for each
\r
360 variable. And real numbers are not as precise as integers anyway, so
\r
361 perhaps one should use integer data types instead and work around the
\r
362 problem. The data type `float' is worse than useless since its six digit
\r
363 precision is too inaccurate to be relied upon. Generally, then, you
\r
364 should use integer data types where ever possible, but if real numbers
\r
365 are required use at least a `double'.
\r
368 Declaring a variable
\r
370 All variables in a C program must be declared before they can be used.
\r
371 The general form of a variable definition is;
\r
376 So, for example to declare a variable "x", of data type "int" so that it
\r
377 may store a value in the range -32767 to 32767, you use the statement;
\r
382 Character strings may be declared, which are in reality arrays of
\r
383 characters. They are declared as follows;
\r
386 char name[number_of_elements];
\r
388 So, to declare a string thirty characters long, and called `name' you
\r
389 would use the declaration;
\r
395 Arrays of other data types also may be declared in one, two or more
\r
396 dimensions in the same way. For example to declare a two dimensional
\r
402 The elements of this array are then accessed as;
\r
408 There are three levels of access to variable; local, module and global. A
\r
409 variable declared within a code block is only known to the statements
\r
410 within that code block. A variable declared outside any function code
\r
411 blocks but prefixed with the storage modifier "static" is known only to
\r
412 the statements within that source module. A variable declared outside any
\r
413 functions and not prefixed with the static storage type modifier may be
\r
414 accessed by any statement within any source module of the program.
\r
431 /* Test variable 'a' for equality with 0 */
\r
435 for(b = 0; b < 20; b++)
\r
436 printf("\nHello World");
\r
442 In this example the variable `error' is accessible by all source code
\r
443 modules compiled together to form the finished program. The variable `a'
\r
444 is accessible by statements in both functions `main()' and `funca()', but
\r
445 is invisible to any other source module. Variables `x' and `y' are only
\r
446 accessible by statements within function `main()'. The variable `b' is
\r
447 only accessible by statements within the code block following the `if'
\r
450 If a second source module wished to access the variable `error' it would
\r
451 need to declare `error' as an `extern' global variable thus;
\r
460 C will quite happily allow you, the programmer, to assign different data
\r
461 types to each other. For example, you may declare a variable to be of
\r
462 type `char' in which case a single byte of data will be allocated to
\r
463 store the variable. To this variable you can attempt to allocate larger
\r
464 values, for example;
\r
474 In this example the variable `x' can only store a value between -127 and
\r
475 128, so the figure 5000 will NOT be assigned to the variable `x'. Rather
\r
476 the value 136 will be assigned!
\r
479 Often you may wish to assign different data types to each other, and to
\r
480 prevent the compiler from warning you of a possible error you can use a
\r
481 cast to tell the compiler that you know what you're doing. A cast
\r
482 statement is a data type in parenthesis preceding a variable or
\r
496 In this example the (int) cast tells the compiler to convert the value of
\r
497 the floating point variable x to an integer before assigning it to the
\r
504 A C function may receive parameters from a calling function. These
\r
505 parameters are declared as variables within the parentheses of the
\r
506 function name, thus;
\r
509 int MULT(int x, int y)
\r
511 /* Return parameter x multiplied by parameter y */
\r
525 printf("%d multiplied by %d equals %d\n",a,b,c);
\r
531 There are two access modifiers; `const' and `volatile'. A variable
\r
532 declared to be `const' may not be changed by the program, whereas a
\r
533 variable declared as type as type `volatile' may be changed by the
\r
534 program. In addition, declaring a variable to be volatile prevents the C
\r
535 compiler from allocating the variable to a register, and reduces the
\r
536 optimization carried out on the variable.
\r
540 Storage class types
\r
541 C provides four storage types; `extern', `static', `auto' and `register'.
\r
543 The extern storage type is used to allow a source module within a C
\r
544 program to access a variable declared in another source module.
\r
546 Static variables are only accessible within the code block that declared
\r
547 them, and additionally if the variable is local, rather than global, they
\r
548 retain their old value between subsequent calls to the code block.
\r
550 Register variables are stored within CPU registers where ever possible,
\r
551 providing the fastest possible access to their values.
\r
553 The auto type variable is only used with local variables, and declares
\r
554 the variable to retain its value locally only. Since this is the default
\r
555 for local variables the auto storage type is very rarely used.
\r
561 Operators are tokens that cause a computation to occur when applied to
\r
562 variables. C provides the following operators;
\r
569 ~ Bitwise compliment
\r
594 %= Assign remainder
\r
597 -= Assign difference
\r
598 <<= Assign left shift
\r
599 >>= Assign right shift
\r
600 &= Assign bitwise AND
\r
601 |= Assign bitwise OR
\r
602 ^= Assign bitwise XOR
\r
605 <= Less than or equal
\r
613 -> Indirect component
\r
615 a ? x:y "If a is true then
\r
621 ... Ellipsis are used
\r
632 To illustrate some more commonly used operators consider the following
\r
641 a = 5; /* Assign a value of 5 to variable 'a' */
\r
642 b = a / 2; /* Assign the value of 'a' divided by two to
\r
644 c = b * 2; /* Assign the value of 'b' multiplied by two
\r
647 if (a == c) /* Test if 'a' holds the same value as 'c' */
\r
649 puts("Variable 'a' is an even number");
\r
651 puts("Variable 'a' is an odd number");
\r
655 Normally when incrementing the value of a variable you would write
\r
660 C provides the incremental operator '++' as well so that you can write;
\r
664 Similarly you can decrement the value of a variable using '--' as;
\r
668 All the other mathematical operators may be used the same, so in a C
\r
669 program you can write in shorthand;
\r
686 Functions are the source code procedures that comprise a C program. They
\r
687 follow the general form;
\r
689 return_type function_name(parameter_list)
\r
695 The return_type specifies the data type that will be returned by the
\r
696 function; char, int, double, void &c.
\r
698 The code within a C function is invisible to any other C function, and
\r
699 jumps may not be made from one function into the middle of another,
\r
700 although functions may call other functions. Also, functions cannot be
\r
701 defined within functions, only within source modules.
\r
703 Parameters may be passed to a function either by value, or by reference.
\r
704 If a parameter is passed by value, then only a copy of the current value
\r
705 of the parameter is passed to the function. A parameter passed by
\r
706 reference however, is a pointer to the actual parameter that may then be
\r
707 changed by the function.
\r
710 The following example passes two parameters by value to a function,
\r
711 funca(), which attempts to change the value of the variables passed to
\r
712 it. And then passes the same two parameters by reference to funcb() which
\r
713 also attempts to modify their values.
\r
718 int funca(int x, int y)
\r
720 /* This function receives two parameters by value, x and y */
\r
725 printf("\nValue of x in funca() %d value of y in funca()
\r
732 int funcb(int *x, int *y)
\r
734 /* This function receives two parameters by reference, x and y
\r
740 printf("\nValue of x in funcb() %d value of y in funcb()
\r
758 printf("\nValue of x %d value of y %d value of z %d",x,y,z);
\r
762 Actually funcb() does not change the values of the parameters it
\r
763 receives. Rather it changes the contents of the memory addresses pointed
\r
764 to by the received parameters. While funca() receives the values of
\r
765 variables `x' and `y' from function main(), funcb() receives the memory
\r
766 addresses of the variables `x' and `y' from function main().
\r
770 Passing an array to a function
\r
772 The following program passes an array to a function, funca(), which
\r
773 initialises the array elements;
\r
777 void funca(int x[])
\r
781 for(n = 0; n < 100; n++)
\r
792 for(counter = 0; counter < 100; counter++)
\r
793 printf("\nValue of element %d is
\r
794 %d",counter,array[counter]);
\r
797 The parameter of funca() `int x[]' is declared to be an array of any
\r
798 length. This works because the compiler passes the address of the start
\r
799 of the array parameter to the function, rather than the value of the
\r
800 individual elements. This does of course mean that the function can
\r
801 change the value of the array elements. To prevent a function from
\r
802 changing the values you can specify the parameter as type `const';
\r
805 funca(const int x[])
\r
809 This will then generate a compiler error at the line that attempts to
\r
810 write a value to the array. However, specifying a parameter to be const
\r
811 does not protect the parameter from indirect assignment as the following
\r
812 program illustrates;
\r
817 int funca(const int x[])
\r
822 /* This line gives a 'suspicious pointer conversion warning' */
\r
823 /* because x is a const pointer, and ptr is not */
\r
826 for(n = 0; n < 100; n++)
\r
840 for(counter = 0; counter < 100; counter++)
\r
841 printf("\nValue of element %d is
\r
842 %d",counter,array[counter]);
\r
847 Passing parameters to main()
\r
849 C allows parameters to be passed from the operating system to the program
\r
850 when it starts executing through two parameters; argc and argv[], as
\r
856 main(int argc, char *argv[])
\r
860 for(n = 0; n < argc; n++)
\r
861 printf("\nParameter %d equals %s",n,argv[n]);
\r
865 Parameter argc holds the number of parameters passed to the program, and
\r
866 the array argv[] holds the addresses of each parameter passed. argv[0] is
\r
867 always the program name. This feature may be put to good use in
\r
868 applications that need to access system files. Consider the following
\r
871 A simple database application stores its data in a single file called
\r
872 "data.dat". The application needs to be created so that it may be stored
\r
873 in any directory on either a floppy diskette or a hard disk, and executed
\r
874 both from within the host directory and through a DOS search path. To
\r
875 work correctly the application must always know where to find the data
\r
876 file; "data.dat". This is solved by assuming that the data file will be
\r
877 in the same directory as the executable module, a not unreasonable
\r
878 restriction to place upon the operator. The following code fragment then
\r
879 illustrates how an application may apply this algorithm into practice to
\r
880 be always able to locate a desired system file:
\r
883 #include <string.h>
\r
885 char system_file_name[160];
\r
887 void main(int argc,char *argv[])
\r
889 char *data_file = "DATA.DAT";
\r
892 strcpy(system_file_name,argv[0]);
\r
893 p = strstr(system_file_name,".EXE");
\r
896 /* The executable is a .COM file */
\r
897 p = strstr(system_file_name,".COM");
\r
900 /* Now back track to the last '\' character in the file name */
\r
901 while(*(p - 1) != '\\')
\r
904 strcpy(p,data_file);
\r
907 In practice this code creates a string in system_file_name that is
\r
908 comprised of path\data.dat, so if for example the executable file is
\r
909 called "test.exe" and resides in the directory \borlandc, then
\r
910 system_file_name will be assigned with: \borlandc\data.dat
\r
914 Returning from a function
\r
916 The command `return' is used to return immediately from a function. If
\r
917 the function was declared with a return data type, then return should be
\r
918 used with a parameter of the same data type.
\r
922 Function prototypes
\r
924 Prototypes for functions allow the C compiler to check that the type of
\r
925 data being passed to and from functions is correct. This is very
\r
926 important to prevent data overflowing its allocated storage space into
\r
927 other variables areas.
\r
929 A function prototype is placed at the beginning of the program, after any
\r
930 preprocessor commands, such as #include <stdio.h>, and before the
\r
931 declaration of any functions.
\r
935 C allows for commands to the compiler to be included in the source code.
\r
936 These commands are then called preprocessor commands and are defined by
\r
937 the ANSI standard to be;
\r
952 All preprocessor commands start with a hash symbol, "#", and must be on a
\r
953 line on their own (although comments may follow).
\r
959 The #define command specifies an identifier and a string that the
\r
960 compiler will substitute every time it comes accross the identifier
\r
961 within that source code module. For example;
\r
965 #define TRUE !FALSE
\r
967 The compiler will replace any subsequent occurence of `FALSE' with `0'
\r
968 and any subsequent occurence of `TRUE' with `!0'. The substitution does
\r
969 NOT take place if the compiler finds that the identifier is enclosed by
\r
970 quotation marks, so
\r
975 would NOT be replaced, but
\r
977 printf("%d",FALSE);
\r
981 The #define command also can be used to define macros that may include
\r
982 parameters. The parameters are best enclosed in parenthesis to ensure
\r
983 that correct substitution occurs.
\r
986 This example declares a macro `larger()' that accepts two parameters and
\r
987 returns the larger of the two;
\r
991 #define larger(a,b) (a > b) ? (a) : (b)
\r
995 printf("\n%d is largest",larger(5,7));
\r
1002 The #error command causes the compiler to stop compilation and to display
\r
1003 the text following the #error command. For example;
\r
1006 #error REACHED MODULE B
\r
1008 will cause the compiler to stop compilation and display;
\r
1016 The #include command tells the compiler to read the contents of another
\r
1017 source file. The name of the source file must be enclosed either by
\r
1018 quotes or by angular brackets thus;
\r
1021 #include "module2.c"
\r
1022 #include <stdio.h>
\r
1024 Generally, if the file name is enclosed in angular brackets, then the
\r
1025 compiler will search for the file in a directory defined in the
\r
1026 compiler's setup. Whereas if the file name is enclosed in quotes then the
\r
1027 compiler will look for the file in the current directory.
\r
1031 #if, #else, #elif, #endif
\r
1033 The #if set of commands provide conditional compilation around the
\r
1036 #if constant_expression
\r
1042 #elif stands for '#else if' and follows the form;
\r
1054 These two commands stand for '#if defined' and '#if not defined'
\r
1055 respectively and follow the general form;
\r
1063 #ifndef macro_name
\r
1069 where 'macro_name' is an identifier declared by a #define statement.
\r
1075 Undefines a macro previously defined by #define.
\r
1080 Changes the compiler declared global variables __LINE__ and __FILE__. The
\r
1081 general form of #line is;
\r
1083 #line number "filename"
\r
1085 where number is inserted into the variable '__LINE__' and 'filename' is
\r
1086 assigned to '__FILE__'.
\r
1091 This command is used to give compiler specific commands to the compiler.
\r
1092 The compiler's manual should give you full details of any valid options
\r
1093 to go with the particular implementation of #pragma that it supports.
\r
1095 PROGRAM CONTROL STATEMENTS
\r
1097 As with any computer language, C includes statements that test the
\r
1098 outcome of an expression. The outcome of the test is either TRUE or
\r
1099 FALSE. The C language defines a value of TRUE as non-zero, and FALSE as
\r
1104 Selection statements
\r
1106 The general purpose selection statement is "if" that follows the general
\r
1114 Where "statement" may be a single statement, or a code block enclosed in
\r
1115 curly braces. The "else" is optional. If the result of the expression
\r
1116 equates to TRUE, then the statement(s) following the if() will be
\r
1117 evaluated. Otherwise the statement(s) following the else, if there is
\r
1118 one, will be evaluated.
\r
1121 An alternative to the if....else combination is the ?: command that takes
\r
1125 expression ? true_expression : false_expression
\r
1127 Where if the expression evaluates to TRUE, then the true_expression will
\r
1128 be evaluated, otherwise the false_expression will be evaluated. Thus we
\r
1132 #include <stdio.h>
\r
1140 printf("\nx is an %s number", x % 2 == 0 ? "even" : "odd");
\r
1143 C also provides a multiple branch selection statement, switch, which
\r
1144 successively tests a value of an expression against a list of values and
\r
1145 branches program execution to the first match found. The general form of
\r
1149 switch (expression)
\r
1151 case value1 : statements
\r
1153 case value2 : statements
\r
1159 case valuen : statements
\r
1161 default : statements
\r
1164 The break statement is optional, but if omitted, program execution will
\r
1165 continue down the list.
\r
1167 #include <stdio.h>
\r
1177 case 0 : printf("\nx equals zero");
\r
1179 case 1 : printf("\nx equals one");
\r
1181 case 2 : printf("\nx equals two");
\r
1183 case 3 : printf("\nx equals three");
\r
1185 default : printf("\nx is larger than three");
\r
1189 Switch statements may be nested within one another. This is a
\r
1190 particularly useful feature for confusing people who read your source
\r
1194 Iteration statements
\r
1195 C provides three looping or iteration statements; for, while, and do-
\r
1196 while. The for loop has the general form;
\r
1199 for(initialization;condition;increment)
\r
1201 and is useful for counters such as in this example that displays the
\r
1202 entire ascii character set;
\r
1204 #include <stdio.h>
\r
1210 for(x = 32; x < 128; x++)
\r
1211 printf("%d\t%c\t",x,x);
\r
1214 An infinite for loop is also quite valid;
\r
1221 Also, C allows empty statements. The following for loop removes leading
\r
1222 spaces from a string;
\r
1224 for(; *str == ' '; str++)
\r
1227 Notice the lack of an initializer, and the empty statement following the
\r
1230 The while loop is somewhat simpler than the for loop and follows the
\r
1236 The statement following the condition, or statements enclosed in curly
\r
1237 braces will be executed until the condition is FALSE. If the condition is
\r
1238 false before the loop commences, the loop statements will not be
\r
1239 executed. The do-while loop on the other hand is always executed at least
\r
1240 once. It takes the general form;
\r
1252 The "return" statement is used to return from a function to the calling
\r
1253 function. Depending upon the declared return data type of the function it
\r
1254 may or may not return a value;
\r
1257 int MULT(int x, int y)
\r
1266 printf("\nHello World");
\r
1270 The "break" statement is used to break out of a loop or from a switch
\r
1271 statement. In a loop it may be used to terminate the loop prematurely, as
\r
1275 #include <stdio.h>
\r
1281 for(x = 0; x < 256; x++)
\r
1290 In contrast to "break" is "continue", which forces the next iteration of
\r
1291 the loop to occur, effectively forcing program control back to the loop
\r
1295 C provides a function for terminating the program prematurely, "exit()".
\r
1296 Exit() may be used with a return value to pass back to the calling
\r
1300 exit(return_value);
\r
1306 A powerful, but often misunderstood feature of the C programming language
\r
1307 is ?:. This is an operator that acts upon a boolean expression, and
\r
1308 returns one of two values dependant upon the result of the expression;
\r
1311 <boolean expression> ? <value for true> : <value for false>
\r
1313 It can be used almost anywhere, for example it was used in the binary
\r
1314 search demonstration program;
\r
1316 printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");
\r
1318 Here it passes either "Not found" or "Located okay" to the printf()
\r
1319 function dependant upon the outcome of the boolean expression `result ==
\r
1320 0'. Alternatively it can be used for assigning values to a variable;
\r
1322 x = (a == 0) ? (b) : (c);
\r
1324 Which will assign the value of b to variable x if a is equal to zero,
\r
1325 otherwise it will assign the value of c to variable x.
\r
1327 This example returns the name of the executing program, without any path
\r
1330 #include <stdio.h>
\r
1331 #include <stddef.h>
\r
1332 #include <string.h>
\r
1334 char *progname(char *pathname)
\r
1336 /* Return name of running program */
\r
1340 static char bnbuf[256];
\r
1342 return pathname? p = strrchr (pathname, '\\'),
\r
1343 q = strrchr (pathname, '.'),
\r
1344 l = (q == NULL? strchr (pathname, '\0'): q)
\r
1345 - (p == NULL? p = pathname: ++p),
\r
1346 strncpy (bnbuf, p, l),
\r
1352 void main(int argc, char *argv[])
\r
1354 printf("\n%s",progname(argv[0]));
\r
1361 The continue keyword forces control to jump to the test statement of the
\r
1362 innermost loop (while, do...while()). This can be useful for terminating
\r
1363 a loop gracefuly as this program that reads strings from a file until
\r
1364 there are no more illustrates;
\r
1367 #include <stdio.h>
\r
1375 fp = fopen("data.txt","r");
\r
1378 fprintf(stderr,"Unable to open file data.txt");
\r
1384 p = fgets(buff,100,fp);
\r
1386 /* Force exit from loop */
\r
1393 With a for() loop however, continue passes control back to the third
\r
1401 Input to a C program may occur from the console, the standard input
\r
1402 device (unless otherwise redirected this is the console), from a file or
\r
1405 The general input command for reading data from the standard input stream
\r
1406 `stdin' is scanf(). Scanf() scans a series of input fields, one character
\r
1407 at a time. Each field is then formatted according to the appropriate
\r
1408 format specifier passed to the scanf() function as a parameter. This
\r
1409 field is then stored at the ADDRESS passed to scanf() following the
\r
1410 format specifiers list.
\r
1412 For example, the following program will read a single integer from the
\r
1423 Notice the address operator & prefixing the variable name `x' in the
\r
1424 scanf() parameter list. This is because scanf() stores values at
\r
1425 ADDRESSES rather than assigning values to variables directly.
\r
1427 The format string is a character string that may contain three types of
\r
1430 whitespace characters (space, tab and newline), non-whitespace characters
\r
1431 (all ascii characters EXCEPT %) and format specifiers.
\r
1433 Format specifiers have the general form;
\r
1436 %[*][width][h|l|L]type_character
\r
1438 After the % sign the format specifier is comprised of:
\r
1440 an optional assignment suppression character, *, which suppresses
\r
1441 assignment of the next input field.
\r
1443 an optional width specifier, width, which declares the maximum number
\r
1444 of characters to be read.
\r
1446 an optional argument type modifier, h or l or L, where:
\r
1447 h is a short integer
\r
1449 L is a long double
\r
1451 the data type character that is one of;
\r
1455 D Decimal long integer
\r
1457 O Octal long integer
\r
1458 i Decimal, octal or hexadecimal integer
\r
1459 I Decimal, octal or hexadecimal long
\r
1461 u Decimal unsigned integer
\r
1462 U Decimal unsigned long integer
\r
1463 x Hexadecimal integer
\r
1464 X Hexadecimal long integer
\r
1468 s Character string
\r
1472 An example using scanf();
\r
1475 #include <stdio.h>
\r
1482 printf("\nEnter your name and age ");
\r
1483 scanf("%30s%d",name,&age);
\r
1484 printf("\n%s %d",name,age);
\r
1487 Notice the include line, "#include <stdio.h>", this is to tell the
\r
1488 compiler to also read the file stdio.h that contains the function
\r
1489 prototypes for scanf() and printf().
\r
1492 If you type in and run this sample program you will see that only one
\r
1493 name can be entered, that is you can't enter;
\r
1498 because scanf() detects the whitespace between "JOHN" and "SMITH" and
\r
1499 moves on to the next input field, which is age, and attempts to assign
\r
1500 the value "SMITH" to the age field! The limitations of scanf() as an
\r
1501 input function are obvious.
\r
1503 An alternative input function is gets() that reads a string of characters
\r
1504 from the stream stdin until a newline character is detected. The newline
\r
1505 character is replaced by a null (0 byte) in the target string. This
\r
1506 function has the advantage of allowing whitespace to be read in. The
\r
1507 following program is a modification to the earlier one, using gets()
\r
1508 instead of scanf().
\r
1511 #include <stdio.h>
\r
1512 #include <stdlib.h>
\r
1513 #include <string.h>
\r
1522 printf("\nEnter your name and age ");
\r
1523 /* Read in a string of data */
\r
1527 /* P is a pointer to the last character in the input string */
\r
1528 p = &data[strlen(data) - 1];
\r
1530 /* Remove any trailing spaces by replacing them with null bytes
\r
1537 /* Locate last space in the string */
\r
1538 p = strrchr(data,' ');
\r
1540 /* Read age from string and convert to an integer */
\r
1543 /* Terminate data string at start of age field */
\r
1546 /* Copy data string to name variable */
\r
1547 strcpy(name,data);
\r
1549 /* Display results */
\r
1550 printf("\nName is %s age is %d",name,age);
\r
1555 The most common output function is printf() that is very similar to
\r
1556 scanf() except that it writes formatted data out to the standard output
\r
1557 stream stdout. Printf() takes a list of output data fields and applies
\r
1558 format specifiers to each and outputs the result. The format specifiers
\r
1559 are the same as for scanf() except that flags may be added to the format
\r
1560 specifiers. These flags include;
\r
1563 - Which left justifies the output padding to the right with spaces.
\r
1564 + Which causes numbers to be prefixed by their sign
\r
1566 The width specifier is also slightly different for printf(). In its most
\r
1567 useful form is the precision specifier;
\r
1572 So, to print a floating point number to three decimal places you would
\r
1577 And to pad a string with spaces to the right or truncate the string to
\r
1578 twenty characters if it is longer, to form a fixed twenty character
\r
1581 printf("%-20.20s",data);
\r
1583 Special character constants may appear in the printf() parameter list.
\r
1587 \r Carriage return
\r
1589 \b Sound the computer's bell
\r
1592 \\ Backslash character
\r
1597 \x Hexadecimal string
\r
1599 Thus, to display "Hello World", surrounded in quotation marks and
\r
1600 followed by a newline you would use;
\r
1602 printf("\"Hello World\"\n");
\r
1604 The following program shows how a decimal integer may be displayed as a
\r
1605 decimal, hexadecimal or octal integer. The 04 following the % in the
\r
1606 printf() format tells the compiler to pad the displayed figure to a width
\r
1607 of at least four digits padded with leading zeroes if required.
\r
1610 /* A simple decimal to hexadecimal and octal conversion program */
\r
1612 #include <stdio.h>
\r
1620 printf("\nEnter a number, or 0 to end ");
\r
1622 printf("%04d %04X %04o",x,x,x);
\r
1628 There are associated functions to printf() that you should be aware of.
\r
1629 fprintf() has the prototype;
\r
1631 fprintf(FILE *fp,char *format[,argument,...]);
\r
1633 This variation on printf() simply sends the formatted output to the
\r
1634 specified file stream.
\r
1636 sprintf() has the function prototype;
\r
1638 sprintf(char *s,char *format[,argument,...]);
\r
1640 and writes the formatted output to a string. You should take care using
\r
1641 sprintf() that the target string has been declared long enough to hold
\r
1642 the result of the sprintf() output. Otherwise other data will be
\r
1643 overwritten in memory.
\r
1645 An example of using sprintf() to copy multiple data to a string;
\r
1653 sprintf(buffer,"7 * 5 == %d\n",7 * 5);
\r
1659 An alternative to printf() for outputting a simple string to the stream
\r
1660 stdout is puts(). This function sends a string to the stream stdout
\r
1661 followed by a newline character. It is faster than printf(), but far less
\r
1662 flexible. Instead of;
\r
1664 printf("Hello World\n");
\r
1668 puts("Hello World");
\r
1672 Direct Console I/O
\r
1673 Data may be sent to, and read directly from the console (keyboard and
\r
1674 screen) using the direct console I/O functions. These functions are
\r
1675 prefixed `c'. The direct console I/O equivalent of printf() is then
\r
1676 cprintf(), and the equivalent of puts() is cputs(). Direct console I/O
\r
1677 functions differ from standard I?o functions:
\r
1679 1. They do not make use of the predefined streams, and hence may not be
\r
1681 2. They are not portable across operating systems (for example you cant
\r
1682 use direct console I/O functions in a Windows programme).
\r
1683 3. They are faster than their standard I/O equivalents
\r
1684 4. They may not work with all video modes (especially VESA display
\r
1691 A pointer is a variable that holds the memory address of an item of data.
\r
1692 Therefore it points to another item. A pointer is declared like an
\r
1693 ordinary variable, but its name is prefixed by `*', thus;
\r
1698 This declares the variable `p' to be a pointer to a character variable.
\r
1699 Pointers are very powerful, and similarly dangerous! If only because a
\r
1700 pointer can be inadvertently set to point to the code segment of a
\r
1701 program and then some value assigned to the address of the pointer!
\r
1703 The following program illustrates a simple, though fairly useless
\r
1704 application of a pointer;
\r
1707 #include <stdio.h>
\r
1714 /* x is a pointer to an integer data type */
\r
1719 printf("\nVariable 'a' holds the value %d at memory address
\r
1723 Here is a more useful example of a pointer illustrating how because the
\r
1724 compiler knows the type of data pointed to by the pointer, when the
\r
1725 pointer is incremented it is incremented the correct number of bytes for
\r
1726 the data type. In this case two bytes;
\r
1728 #include <stdio.h>
\r
1736 /* x is a pointer to an integer data type */
\r
1738 /* Assign x to point to array element zero */
\r
1741 /* Assign values to the array */
\r
1742 for(n = 0; n < 25; n++)
\r
1746 /* Now print out all array element values */
\r
1747 for(n = 0; n < 25; n++ , x++)
\r
1748 printf("\nElement %d holds %d",n,*x);
\r
1751 To read or assign a value to the address held by a pointer you use the
\r
1752 indirection operator `*'. Thus in the above example, to print the value
\r
1753 at the memory address pointed to by variable x I have used `*x'.
\r
1755 Pointers may be incremented and decremented and have other mathematics
\r
1756 applied to them also. For example in the above program to move variable x
\r
1757 along the array one element at a time we put the statement `x++' in the
\r
1758 for loop. We could move x along two elements by saying `x += 2'. Notice
\r
1759 that this doesn't mean "add 2 bytes to the value of x", but rather it
\r
1760 means "add 2 of the pointer's data type size units to the value of x".
\r
1762 Pointers are used extensively in dynamic memory allocation. When a
\r
1763 program is running it is often necessary to temporarily allocate a block
\r
1764 of data, say a table, in memory. C provides the function malloc() for
\r
1765 this purpose that follows the general form;
\r
1768 any pointer type = malloc(number_of_bytes);
\r
1770 malloc() actually returns a void pointer type, which means it can be any
\r
1771 type; integer, character, floating point or whatever. This example
\r
1772 allocates a table in memory for 1000 integers;
\r
1774 #include <stdio.h>
\r
1775 #include <stdlib.h>
\r
1782 /* x is a pointer to an integer data type */
\r
1784 /* Create a 1000 element table, sizeof() returns the compiler
\r
1786 /* specific number of bytes used to store an integer */
\r
1788 x = malloc(1000 * sizeof(int));
\r
1791 /* Check to see if the memory allocation succeeded */
\r
1794 printf("\nUnable to allocate a 1000 element integer
\r
1799 /* Assign values to each table element */
\r
1800 for(n = 0; n < 1000; n++)
\r
1806 /* Return x to the start of the table */
\r
1809 /* Display the values in the table */
\r
1810 for(n = 0; n < 1000; n++){
\r
1811 printf("\nElement %d holds a value of %d",n,*x);
\r
1814 /* Deallocate the block of memory now it's no longer required
\r
1819 Pointers are also extensively used with character arrays, called strings.
\r
1820 Since all C program strings are terminated by a zero byte we can count
\r
1821 the letters in a string using a pointer;
\r
1823 #include <stdio.h>
\r
1824 #include <string.h>
\r
1832 /* Initialise variable 'text' with some writing */
\r
1833 strcpy(text,"This is a string of data");
\r
1835 /* Set variable p to the start of variable text */
\r
1838 /* Initialise variable len to zero */
\r
1841 /* Count the characters in variable text */
\r
1848 /* Display the result */
\r
1849 printf("\nThe string of data has %d characters in it",len);
\r
1853 The 8088/8086 group of CPUs, as used in the IBM PC, divide memory into
\r
1854 64K segments. To address all 1Mb of memory a 20 bit number is used
\r
1855 comprised of an `offset' to and a 64K `segment'. The IBM PC uses special
\r
1856 registers called "segment registers" to record the segments of addresses.
\r
1858 This leads the C language on the IBM PC to have three new keywords; near,
\r
1861 near pointers are 16 bits wide and access only data within the current
\r
1864 far pointers are comprised of an offset and a segment address allowing
\r
1865 them to access data any where in memory, but arithmetic with far pointers
\r
1866 is fraught with danger! When a value is added or subtracted from a far
\r
1867 pointer it is only actualy the segment part of the pointer that is
\r
1868 affected, thus leading to the situation where a far pointer being
\r
1869 incremented wraps around on its own offset 64K segment.
\r
1871 huge pointers are a variation on the far pointer that can be successfuly
\r
1872 incremented and decremented through the entire 1Mb range since the
\r
1873 compiler generates code to amend the offset as applicable.
\r
1875 It will come as no surprise that code using near pointers is executed
\r
1876 faster than code using far pointers, which in turn is faster than code
\r
1877 using huge pointers.
\r
1879 to give a literal address to a far pointer IBM PC C compilers provide a
\r
1880 macro MK-FP() that has the prototype;
\r
1883 void far *MK_FP(unsigned segment, unsigned offset);
\r
1885 For example, to create a far pointer to the start of video memory on a
\r
1886 colour IBM PC system you could use;
\r
1888 screen = MK_FP(0xB800,0);
\r
1890 Two coressponding macros provided are;
\r
1892 FP_SEG() and FP_OFF()
\r
1894 Which return the segment and offset respectively of a far pointer. The
\r
1895 following example uses FP_SEG() and FP_OFF() to send segment and offset
\r
1896 addresses to a DOS call to create a new directory path;
\r
1901 int makedir(char *path)
\r
1903 /* Make sub directory, returning non zero on success */
\r
1905 union REGS inreg,outreg;
\r
1906 struct SREGS segreg;
\r
1908 inreg.h.ah = 0x39;
\r
1909 segreg.ds = FP_SEG(path);
\r
1910 inreg.x.dx = FP_OFF(path);
\r
1911 intdosx(&inreg,&outreg,&segreg);
\r
1913 return(1 - outreg.x.cflag);
\r
1917 Finally, the CPU's segment registers can be read with the function
\r
1918 `segread()'. This is a function unique to C compilers for the the 8086
\r
1919 family of processors. segread() has the function prototype:
\r
1922 void segread(struct SREGS *segp);
\r
1924 It returns the current values of the segment registers in the SREGS type
\r
1925 structure pointed to by the pointer `segp'. For example:
\r
1931 struct SREGS sregs;
\r
1934 printf("\nCode segment is currently at %04X, Data segment is at
\r
1935 %04X",sregs.cs,sregs.ds);
\r
1936 printf("\nExtra segment is at %04X, Stack segment is at
\r
1937 %04X",sregs.es,sregs.ss);
\r
1943 C provides the means to group together variables under one name providing
\r
1944 a convenient means of keeping related information together and providing
\r
1945 a structured approach to data.
\r
1947 The general form for a structure definition is;
\r
1952 variable_type variable_name;
\r
1953 variable_type variable_name;
\r
1958 When accessing data files, with a fixed record structure, the use of a
\r
1959 structure variable becomes essential. The following example shows a
\r
1960 record structure for a very simple name and address file. It declares a
\r
1961 data structure called `data', and comprised of six fields; `name',
\r
1962 `address', `town', `county' , `post' and `telephone'.
\r
1972 char telephone[15];
\r
1976 The structure 'data' may then be used in the program as a variable data
\r
1977 type for declaring variables;
\r
1981 Thus declares a variable called 'record' to be of type 'data'.
\r
1983 The individual fields of the structure variable are accessed by the
\r
1986 structure_variable.field_name;
\r
1988 Thus, the name field of the structure variable record is accessed with;
\r
1992 There is no limit to the number of fields that may comprise a structure,
\r
1993 nor do the fields have to be of the same types, for example;
\r
2004 Declares a structure 'dp' that is comprised of a character array field,
\r
2005 an integer field and a character pointer field.
\r
2007 A structure variable may be passed as a parameter by passing the address
\r
2008 of the variable as the parameter with the & operator thus;
\r
2013 The following is an example program that makes use of a structure to
\r
2014 provide the basic access to the data in a simple name and address file;
\r
2017 /* A VERY simple address book written in ANSI C */
\r
2019 #include <stdio.h>
\r
2020 #include <stdlib.h>
\r
2022 #include <string.h>
\r
2023 #include <fcntl.h>
\r
2024 #include <sys\stat.h>
\r
2026 /* num_lines is the number of screen display lines */
\r
2027 #define num_lines 25
\r
2037 char telephone[15];
\r
2045 /* Function prototypes */
\r
2047 void ADD_REC(void);
\r
2049 void DISPDATA(void);
\r
2050 void FATAL(char *);
\r
2051 void GETDATA(void);
\r
2053 void OPENDATA(void);
\r
2060 for(n = 0; n < num_lines; n++)
\r
2064 void FATAL(char *error)
\r
2066 printf("\nFATAL ERROR: %s",error);
\r
2072 /* Check for existence of data file and if not create it */
\r
2073 /* otherwise open it for reading/writing at end of file */
\r
2075 handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);
\r
2079 handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);
\r
2081 FATAL("Unable to create data file");
\r
2087 /* Get address data from operator */
\r
2092 gets(record.name);
\r
2093 printf("\nAddress ");
\r
2094 gets(record.address);
\r
2095 printf("\nTown ");
\r
2096 gets(record.town);
\r
2097 printf("\nCounty ");
\r
2098 gets(record.county);
\r
2099 printf("\nPost Code ");
\r
2100 gets(record.post);
\r
2101 printf("\nTelephone ");
\r
2102 gets(record.telephone);
\r
2107 /* Display address data */
\r
2112 printf("Name %s",record.name);
\r
2113 printf("\nAddress %s",record.address);
\r
2114 printf("\nTown %s",record.town);
\r
2115 printf("\nCounty %s",record.county);
\r
2116 printf("\nPost Code %s",record.post);
\r
2117 printf("\nTelephone %s\n\n",record.telephone);
\r
2119 puts("Press RETURN to continue");
\r
2125 /* Insert or append a new record to the data file */
\r
2128 result = write(handle,&record,sizeof(data));
\r
2131 FATAL("Unable to write to data file");
\r
2139 printf("Enter data to search for ");
\r
2144 /* Locate start of file */
\r
2145 lseek(handle,0,SEEK_SET);
\r
2149 /* Read record into memory */
\r
2150 result = read(handle,&record,sizeof(data));
\r
2153 /* Scan record for matching data */
\r
2154 if (strstr(record.name,text) != NULL)
\r
2156 if (strstr(record.address,text) != NULL)
\r
2158 if (strstr(record.town,text) != NULL)
\r
2160 if (strstr(record.county,text) != NULL)
\r
2162 if (strstr(record.post,text) != NULL)
\r
2164 if (strstr(record.telephone,text) != NULL)
\r
2168 while(result > 0);
\r
2180 puts("\n\t\t\tSelect Option");
\r
2181 puts("\n\n\t\t\t1 Add new record");
\r
2182 puts("\n\n\t\t\t2 Search for data");
\r
2183 puts("\n\n\t\t\t3 Exit");
\r
2184 puts("\n\n\n\n\n");
\r
2186 option = atoi(text);
\r
2190 case 1 : GETDATA();
\r
2191 /* Go to end of file to append new record */
\r
2192 lseek(handle,0,SEEK_END);
\r
2196 case 2 : if (SEARCH())
\r
2200 puts("NOT FOUND!");
\r
2201 puts("Press RETURN to continue");
\r
2209 while(option != 3);
\r
2221 C allows the inclusion of variables with a size of less than eight bits
\r
2222 to be included in structures. These variables are known as bit fields,
\r
2223 and may be any declared size from one bit upwards.
\r
2225 The general form for declaring a bit field is;
\r
2228 type name : number_of_bits;
\r
2231 For example, to declare a set of status flags, each occupying one bit;
\r
2235 unsigned carry : 1;
\r
2236 unsigned zero : 1;
\r
2237 unsigned over : 1;
\r
2238 unsigned parity : 1;
\r
2244 The variable `flags' then occupies only four bits in memory, and yet is
\r
2245 comprised of four variables that may be accessed like any other structure
\r
2252 Another facility provided by C for efficient use of available memory is
\r
2253 the union structure. A union structure is a collection of variables that
\r
2254 all share the same memory storage address. As such only one of the
\r
2255 variables is ever accessible at a time.
\r
2257 The general form of a union definition is;
\r
2262 type variable_name;
\r
2263 type variable_name;
\r
2267 type variable_name;
\r
2270 Thus, to declare a union structure for two integer variables;
\r
2278 and to declare a variable of type 'data';
\r
2282 The variable 'my_var' is then comprised of the two variables 'vara' and
\r
2283 'varb' that are accessed like with any form of structure, eg;
\r
2287 Assigns a value of 5 to the variable 'vara' of union 'my_var'.
\r
2292 An enumeration assigns ascending integer values to a list of symbols. An
\r
2293 enumeration declaration takes the general form;
\r
2296 enum name { enumeration list } variable_list;
\r
2298 Thus to define a symbol list of colours, you can say;
\r
2320 Then, the number zero may be referred to by the symbol BLACK, the number
\r
2321 one by the symbol BLUE, the number two by the symbol GREEN and so on.
\r
2323 The following program illustrates the use of an enumeration list to
\r
2324 symbolise integers;
\r
2326 #include <stdio.h>
\r
2355 printf("\nVariable 'x' holds %d",x);
\r
2362 C provides buffered file streams for file access. Some C platforms, such
\r
2363 as Unix and DOS provide unbuffered file handles as well.
\r
2369 Buffered streams are accessed through a variable of type 'file pointer'.
\r
2370 The data type FILE is defined in the header file stdio.h. Thus to declare
\r
2373 #include <stdio.h>
\r
2377 To open a stream C provides the function fopen(), which accepts two
\r
2378 parameters, the name of the file to be opened, and the access mode for
\r
2379 the file to be opened with. The access mode may be any one of;
\r
2384 r Open for reading
\r
2385 w Create for writing, destroying any
\r
2387 a Open for append, creating a new file
\r
2390 r+ Open an existing file for reading
\r
2392 w+ Create for reading and writing,
\r
2395 a+ Open for append, creating a new file
\r
2396 if it doesn't exist.
\r
2399 Optionaly either `b' or `t' may be appended for binary or text mode. If
\r
2400 neither is appended, then the file stream will be opened in the mode
\r
2401 described by the global variable _fmode. Data read or written from file
\r
2402 streams opened in text mode undergoes conversion, that is the characters
\r
2403 CR and LF are converted to CR LF pairs on writing, and the CR LF pair is
\r
2404 converted to a single LF on reading. File streams opened in binary mode
\r
2405 do not undergo conversion.
\r
2408 If fopen() fails to open the file, it returns a value of NULL (defined in
\r
2409 stdio.h) to the file pointer.
\r
2411 Thus, the following program will create a new file called "data.txt" and
\r
2412 open it for reading and writing;
\r
2414 #include <stdio.h>
\r
2420 fp = fopen("data.txt","w+");
\r
2424 To close a stream C provides the function fclose(), which accepts the
\r
2425 stream's file pointer as a parameter;
\r
2429 If an error occurs in closing the file stream, fclose() returns non zero.
\r
2431 There are four basic functions for receiving and sending data to and from
\r
2432 streams; fgetc(), fputc(), fgets() and fputs().
\r
2434 fgetc() simply reads a single character from the specified input stream;
\r
2436 char fgetc(FILE *fp);
\r
2438 Its opposite number is fputc(), which simply writes a single character to
\r
2439 the specified input stream;
\r
2441 char fputc(char c, FILE *fp);
\r
2443 fgets() reads a string from the input stream;
\r
2445 char *fgets(char s, int numbytes, FILE *fp);
\r
2447 It stops reading when either numbytes - 1 bytes have been read, or a
\r
2448 newline character is read in. A null terminating byte is appended to the
\r
2449 read string, s. If an error occurs, fgets() returns NULL.
\r
2452 fputs() writes a null terminated string to a stream;
\r
2454 int fputs(char *s, FILE *fp);
\r
2456 Excepting fgets(), which returns a NULL pointer if an error occurs, all
\r
2457 the other functions described above return EOF (defined in stdio.h) if an
\r
2458 error occurs during the operation.
\r
2461 The following program creates a copy of the file "data.dat" as "data.old"
\r
2462 and illustrates the use of fopen(), fgetc(), fputc() and fclose();
\r
2465 #include <stdio.h>
\r
2472 in = fopen("data.dat","r");
\r
2476 puts("\nUnable to open file data.dat for reading");
\r
2480 out = fopen("data.old","w+");
\r
2484 puts("\nUnable to create file data.old");
\r
2488 /* Loop reading and writing one byte at a time until end-of-
\r
2491 fputc(fgetc(in),out);
\r
2493 /* Close the file streams */
\r
2500 Example program using fputs() to copy text from stream stdin (usually
\r
2501 typed in at the keyboard) to a new file called "data.txt".
\r
2503 #include <stdio.h>
\r
2510 fp = fopen("data.txt","w+");
\r
2523 Random access using streams
\r
2525 Random file access for streams is provided for by the fseek() function
\r
2526 that has the following prototype;
\r
2528 int fseek(FILE *fp, long numbytes, int fromwhere);
\r
2530 fseek() repositions a file pointer associated with a stream previously
\r
2531 opened by a call to fopen(). The file pointer is positioned `numbytes'
\r
2532 from the location `fromwhere', which may be the file beginning, the
\r
2533 current file pointer position, or the end of the file, symbolised by the
\r
2534 constants SEEK_SET, SEEK_CUR and SEEK_END respectively. If a call to
\r
2535 fseek() succeeds, a value of zero is returned.
\r
2538 Associated with fseek() is ftell(), which reports the current file
\r
2539 pointer position of a stream, and has the following function prototype;
\r
2542 long int ftell(FILE *fp);
\r
2544 ftell() returns either the position of the file pointer, measured in
\r
2545 bytes from the start of the file, or -1 upon an error occurring.
\r
2551 File handles are opened with the open() function that has the prototype;
\r
2553 int open(char *filename,int access[,unsigned mode]);
\r
2555 If open() is successful, the number of the file handle is returned.
\r
2556 Otherwise open() returns -1.
\r
2558 The access integer is comprised from bitwise oring together of the
\r
2559 symbolic constants declared in fcntl.h. These vary from compiler to
\r
2560 compiler but may be;
\r
2563 O_APPEND If set, the file pointer will be set to the end of
\r
2565 file prior to each write.
\r
2566 O_CREAT If the file does not exist it is created.
\r
2567 O_TRUNC Truncates the existing file to a length of zero
\r
2569 O_EXCL Used with O_CREAT
\r
2570 O_BINARY Opens the file in binary mode
\r
2571 O_TEXT Opens file in text mode
\r
2573 The optional mode parameter is comprised by bitwise oring of the symbolic
\r
2574 constants defined in stat.h. These vary from C compiler to C compiler but
\r
2577 S_IWRITE Permission to write
\r
2578 S_IREAD Permission to read
\r
2581 Once a file handle has been assigned with open(), the file may be
\r
2582 accessed with read() and write().
\r
2584 Read() has the function prototype;
\r
2586 int read(int handle, void *buf, unsigned num_bytes);
\r
2588 It attempts to read 'num_bytes' and returns the number of bytes actually
\r
2589 read from the file handle 'handle', and stores these bytes in the memory
\r
2590 block pointed to by 'buf'.
\r
2592 Write() is very similar to read() and has the same function prototype and
\r
2593 return values, but writes 'num_bytes' from the memory block pointed to by
\r
2596 Files opened with open() are closed using close() that has the function
\r
2599 int close(int handle);
\r
2601 close() returns zero on success, and -1 if an error occurs trying to
\r
2604 Random access is provided by lseek(), which is very similar to fseek(),
\r
2605 except that it accepts an integer file handle as the first parameter
\r
2606 rather than a stream FILE pointer.
\r
2608 This example uses file handles to read data from stdin (usually the
\r
2609 keyboard) and copy the text to a new file called "data.txt".
\r
2612 #include <fcntl.h>
\r
2613 #include <sys\stat.h>
\r
2620 handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);
\r
2625 write(handle,&text,strlen(text));
\r
2636 The ANSI standard on C defines file IO as by way of file streams, and
\r
2637 defines various functions for file access;
\r
2640 fopen() has the prototype;
\r
2642 FILE *fopen(const char *name,const char *mode);
\r
2644 fopen() attempts to open a stream to a file name in a specified mode. If
\r
2645 successful a FILE type pointer is returned to the file stream, if the
\r
2646 call fails NULL is returned. The mode string can be one of the following;
\r
2650 r Open for reading only
\r
2651 w Create for writing, overwriting any existing file with the same
\r
2653 a Open for append (writing at end of file) or create the file if
\r
2656 r+ Open an existing file for reading and writing.
\r
2657 w+ Create a new file for reading and writing.
\r
2658 a+ Open for append with read and write access.
\r
2661 fclose() is used to close a file stream previously opened by a call to
\r
2662 fopen(). It has the prototype;
\r
2664 int fclose(FILE *fp);
\r
2666 When a call to fclose() is successful, all buffers to the stream are
\r
2667 flushed and a value of zero is returned. If the call fails fclose()
\r
2670 Many host computers, and the IBM PC is no exception, use buffered file
\r
2671 access, that is when writing to a file stream the data is stored in
\r
2672 memory and only written to the stream when it exceeds a predefined number
\r
2673 of bytes. A power failure occurring before the data has been written to
\r
2674 the stream will result in the data never being written, so the function
\r
2675 fflush() can be called to force all pending data to be written. fflush()
\r
2676 has the prototype;
\r
2678 int fflush(FILE *fp);
\r
2680 When a call to fflush() is successful, the buffers connected with the
\r
2681 stream are flushed and a value of zero is returned. On failure fflush()
\r
2684 The location of the file pointer connected with a stream can be
\r
2685 determined with the function ftell(). ftell() has the prototype;
\r
2688 long int ftell(FILE *fp);
\r
2690 and returns the offset of the file pointer in bytes from the start of the
\r
2691 file, or -1L if the call fails.
\r
2693 Similarly, you can move the file pointer to a new position with fseek().
\r
2694 fseek() has the prototype;
\r
2696 int fseek(FILE *fp, long offset, int from_what_place);
\r
2698 fseek() attempts to move the file pointer, 'fp' 'offset' bytes from the
\r
2699 position 'from_what_place'. 'from_what_place' is predefined as one of;
\r
2701 SEEK_SET The file's beginning
\r
2702 SEEK_CUR The file pointer's current position
\r
2703 SEEK_END End of file
\r
2705 The offset may be a positive value to move the file pointer on through
\r
2706 the file, or negative to move backwards.
\r
2708 To move a file pointer quickly back to the start of a file, and clear any
\r
2709 references to errors that have occurred C provides the function rewind()
\r
2710 that has the prototype;
\r
2712 void rewind(FILE *fp);
\r
2714 rewind(fp) is similar to fseek(fp,0L,SEEK_SET) in that they both set the
\r
2715 file pointer to the start of the file, but whereas fseek() clears the EOF
\r
2716 error marker, rewind() clears all error indicators.
\r
2718 Errors occurring with file functions can be checked with the function
\r
2719 ferror() that has the prototype;
\r
2721 int ferror(FILE *fp);
\r
2723 ferror() returns a nonzero value if an error has occurred on the
\r
2724 specified stream. After checking ferror() and reporting any errors you
\r
2725 should clear the error indicators, and this can be done by a call to
\r
2726 clearerr() that has the prototype;
\r
2728 void clearerr(FILE *fp);
\r
2730 The condition of reaching end of file (EOF) can be tested for with the
\r
2731 predefined macro feof() that has the prototype;
\r
2733 int feof(FILE *fp);
\r
2735 feof() returns a nonzero value if an end of file error indicator was
\r
2736 detected on the specified file stream, and zero if the end of file has
\r
2737 not yet been reached.
\r
2739 Reading data from a file stream can be achieved using several functions;
\r
2740 A single character can be read with fgetc() that has the prototype;
\r
2743 int fgetc(FILE *fp);
\r
2745 fgetc() returns either the character read converted to an integer, or EOF
\r
2746 if an error occurred.
\r
2748 Reading a string of data is achieved with fgets(). fgets() attempts to
\r
2749 read a string terminated by a newline character and of no more than a
\r
2750 specified number of bytes from the file stream. It has the prototype;
\r
2753 char *fgets(char s, int n, FILE *fp);
\r
2755 A successful call to fgets() results in a string being stored in `s' that
\r
2756 is either terminated by a newline character, or that is `n' - 1
\r
2757 characters long, which ever came first. The newline character is retained
\r
2758 by fgets(), and a null bytes is appended to the string. If the call fails
\r
2759 a NULL pointer is returned.
\r
2762 Strings may be written to a stream using fputs() that has the prototype;
\r
2764 int fputs(const char *s,FILE *fp);
\r
2766 fputs() writes all the characters in the string `s' to the stream `fp'
\r
2767 except the null terminating byte. On success, fputs() returns the last
\r
2768 character written, on failure it returns EOF.
\r
2770 To write a single character to a stream use fputc() that has the
\r
2773 int fputc(int c,FILE *fp);
\r
2775 If successful, fputc() returns the character written, otherwise it
\r
2778 To read a large block of data, or a record from a stream you can use
\r
2779 fread() that has the prototype;
\r
2781 size_t fread(void *ptr,size_t size, size_t n, FILE *fp);
\r
2783 fread() attempts to read 'n' items, each of length 'size' from the file
\r
2784 stream 'fp' into the block of memory pointed to by 'ptr'. To check the
\r
2785 success or failure status of fread() use ferror().
\r
2787 The sister function to fread() is fwrite() that has the prototype;
\r
2789 size_t fwrite(const void *ptr,size_t size, size_t n,FILE *fp);
\r
2791 that writes 'n' items each of length 'size' from the memory area pointed
\r
2792 to by 'ptr' to the specified stream 'fp'.
\r
2794 Formatted input from a stream is achieved with fscanf() that has the
\r
2797 int fscanf(FILE *fp, const char *format[,address ...]);
\r
2799 fscanf() returns the number of fields successfully stored, and EOF on end
\r
2800 of file. This short example shows how fscanf() is quite useful for
\r
2801 reading numbers from a stream, but hopeless when it comes to strings!
\r
2803 #include <stdio.h>
\r
2815 fp = fopen("data.txt","w+");
\r
2819 perror("Unable to create file");
\r
2823 fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");
\r
2829 fputs("Error flushing stream",stderr);
\r
2836 fputs("Error rewind stream",stderr);
\r
2840 fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);
\r
2843 fputs("Error reading from stream",stderr);
\r
2847 printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);
\r
2850 As you can see from the example, fprintf() can be used to write formatted
\r
2853 If you wish to store the position of a file pointer on a stream, and then
\r
2854 later restore it to the same position you can use the functions fgetpos()
\r
2855 and fsetpos(). fgetpos() reads the current location of the file pointer
\r
2856 and has the prototype;
\r
2858 int fgetpos(FILE *fp, fpos_t *pos);
\r
2860 fsetpos() repositions the file pointer and has the prototype;
\r
2862 int fsetpos(FILE *fp, const fpos_t *fpos);
\r
2864 fpos_t is defined in stdio.h.
\r
2866 These functions are more convenient than doing an ftell() followed by an
\r
2869 An open stream can have a new file associated with it in place of the
\r
2870 existing file by using the function freopen() that has the prototype;
\r
2872 FILE *freopen(const char *name,const char *mode,FILE *fp);
\r
2874 freopen() closes the existing stream and then attempts to reopens it with
\r
2875 the specified file name. This is useful for redirecting the predefined
\r
2876 streams stdin, stdout and stderr to a file or device.
\r
2878 For example; if you wish to redirect all output intended to stdout
\r
2879 (usually the host computer's display device) to a printer you might use;
\r
2881 freopen("LPT1","w",stdout);
\r
2883 where LPT1 is the name of the printer device (on a PC host, LPT1 is the
\r
2884 name of the parallel port).
\r
2887 Predefined I/O Streams
\r
2889 There are three predefined I/O streams; stdin, stdout, and stderr. The
\r
2890 streams stdin and stdout default to the keyboard and display
\r
2891 respectively, but can be redirected on some hardware platforms, such as
\r
2892 the PC and under UNIX. The stream stderr defaults to the display and is
\r
2893 not usually redirected by the operator. Thus it can be used for the
\r
2894 display of error messages even when program output has been redirected,
\r
2897 fputs("Error message",stderr);
\r
2899 The functions printf() and puts(), output data to the stream stdout, and
\r
2900 can therefore be redirected by the operator of the program. scanf() and
\r
2901 gets() accept input from the stream stdin.
\r
2903 As an example of file i/o with the PC consider the following short
\r
2904 program that does a hex dump of a specified file to the predefined stream
\r
2905 stdout, which may be redirected to a file using;
\r
2907 dump filename.ext > target.ext
\r
2909 #include <stdio.h>
\r
2910 #include <fcntl.h>
\r
2912 #include <string.h>
\r
2914 main(int argc, char *argv[])
\r
2917 unsigned char v1[20];
\r
2924 fputs("\nERROR: Syntax is dump f1\n",stderr);
\r
2928 f1 = open(argv[1],O_RDONLY);
\r
2932 fprintf(stderr,"\nERROR: Unable to open %s\n",argv[1]);
\r
2936 fprintf(stdout,"\nDUMP OF FILE %s\n\n",strupr(argv[1]));
\r
2942 /* Set buffer to zero bytes */
\r
2945 /* Read buffer from file */
\r
2946 x = _read(f1,&v1,16);
\r
2948 /* x will be 0 on EOF or -1 on error */
\r
2952 /* Print file offset to stdout */
\r
2953 fprintf(stdout,"%06d(%05x) ",counter,counter);
\r
2957 /* print hex values of buffer to stdout */
\r
2958 for(n = 0; n < 16; n++)
\r
2959 fprintf(stdout,"%02x ",v1[n]);
\r
2961 /* Print ascii values of buffer to stdout */
\r
2962 for(n = 0; n < 16; n++)
\r
2964 if ((v1[n] > 31) && (v1[n] < 128))
\r
2965 fprintf(stdout,"%c",v1[n]);
\r
2967 fputs(".",stdout);
\r
2970 /* Finish the line with a new line */
\r
2971 fputs("\n",stdout);
\r
2974 /* successful termination */
\r
2980 The C language has one of the most powerful string handling capabilities
\r
2981 of any general purpose computer language.
\r
2983 A string is a single dimension array of characters terminated by a zero
\r
2986 Strings may be initialised in two ways. Either in the source code where
\r
2987 they may be assigned a constant value, as in;
\r
2991 char *p = "System 5";
\r
2992 char name[] = "Test Program" ;
\r
2995 or at run time by the function strcpy() that has the function prototype;
\r
2997 char *strcpy(char *destination, char *source);
\r
2999 strcpy() copies the string pointed to by source into the location pointed
\r
3000 to by destination as in the following example;
\r
3009 strcpy(name,"Servile Software");
\r
3011 printf("\nName equals %s",name);
\r
3014 C also allows direct access to each individual byte of the string, so the
\r
3015 following is quite permissible;
\r
3023 strcpy(name,"Servile Software");
\r
3025 printf("\nName equals %s",name);
\r
3027 /* Replace first byte with lower case 's' */
\r
3030 printf("\nName equals %s",name);
\r
3033 The ANSI standard on the C programming language defines the following
\r
3034 functions for use with strings;
\r
3036 char *strcat(char *dest, char *source) Appends string source
\r
3037 to the end of string destination.
\r
3039 char *strchr(char *s, int c) Returns a pointer to the first
\r
3040 occurence of character 'c' within s.
\r
3042 int strcmp(char *s1, char *s2) Compares strings s1 and s2
\r
3043 returning < 0 if s1 is less than s2
\r
3049 int strcoll(char *s1, char *s2) Compares strings s1 and s2
\r
3050 according to the collating sequence set by
\r
3051 setlocale() returning < 0 if s1 is less
\r
3053 == 0 if s1 and s2 are the
\r
3055 > 0 if s1 is greater than s2
\r
3057 char *strcpy(char *dest, char *src) Copies string src into
\r
3060 unsigned strcspn(char *s1, char *s2) Returns the length of string
\r
3061 s1 that consists entirely of characters not in
\r
3064 unsigned strlen(char *s) Returns the length of string s.
\r
3066 char *strncat(char *dest, char *src, unsigned len) Copies at most
\r
3067 'len' characters from string src into string dest.
\r
3069 int strncmp(char *s1, char *s2, unsigned len) Compares at most 'len'
\r
3071 string s1 with string s2 returning < 0
\r
3072 if s1 is less than s2
\r
3073 == 0 if s1 and s2 are
\r
3075 > 0 if s1 is greater
\r
3078 char *strncpy(char *dest, char *src, unsigned len) Copies 'len'
\r
3079 characters from string src into string dest, truncating or
\r
3080 padding with zero bytes as required.
\r
3082 char *strpbrk(char *s1, char *s2) Returns a pointer to the
\r
3083 first character in string s1 that occurs in
\r
3086 char *strrchr(char *s, int c) Returns a pointer to the last
\r
3087 occurence of 'c' within string s.
\r
3089 unsigned strspn(char *s1, char *s2) Returns the length of the
\r
3090 initial segment of string s1 that consists
\r
3091 entirely of characters in string s2.
\r
3093 char *strstr(char *s1, char *s2) Returns a pointer to the
\r
3094 first occurence of string s2 within string
\r
3095 s1, or NULL if string s2 is not found in
\r
3098 char *strtok(char *s1, char *s2) Returns a pointer to the
\r
3099 token found in string s1 that is defined by
\r
3100 delimiters in string s2. Returns NULLif no
\r
3103 The ANSI standard also defines various functions for converting strings
\r
3104 into numbers and numbers into strings.
\r
3106 Some C compilers include functions to convert strings to upper and lower
\r
3107 case, but these functions are not defined in the ANSI standard. However,
\r
3108 the ANSI standard does define the functions; toupper() and tolower() that
\r
3111 integer parameter converted to upper and lowercase respectively. By using
\r
3112 these functions we can create our own ANSI compatible versions;
\r
3117 void strupr(char *source)
\r
3129 void strlwr(char *source)
\r
3146 strcpy(name,"Servile Software");
\r
3148 printf("\nName equals %s",name);
\r
3152 printf("\nName equals %s",name);
\r
3156 printf("\nName equals %s",name);
\r
3159 C does not impose a maximum length that a string may be, unlike other
\r
3160 computer languages. However, some CPUs impose restrictions on the maximum
\r
3161 size a block of memory can be. For example, the 8088 family of CPUs, as
\r
3162 used by the IBM PC, impose a limit of 64K bytes on a segment of memory.
\r
3164 An example program to reverse all the characters in a string.
\r
3166 #include <stdio.h>
\r
3167 #include <string.h>
\r
3169 char *strrev(char *s)
\r
3171 /* Reverses the order of all characters in a string except the
\r
3173 /* terminating byte */
\r
3179 /* Set pointer 'end' to last character in string */
\r
3180 end = s + strlen(s) - 1;
\r
3182 /* Preserve pointer to start of string */
\r
3185 /* Swop characters */
\r
3203 strcpy(text,"This is a string of data");
\r
3213 The function strtok() is a very powerful standard C feature for
\r
3214 extracting substrings from within a single string. It is used where the
\r
3215 substrings are separated by known delimiters, such as commas in the
\r
3216 following example;
\r
3218 #include <stdio.h>
\r
3219 #include <string.h>
\r
3226 strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");
\r
3228 p = strtok(data,",");
\r
3232 p = strtok(NULL,",");
\r
3236 Or this program can be written with a for() loop thus;
\r
3238 #include <stdio.h>
\r
3239 #include <string.h>
\r
3246 strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");
\r
3248 for(strtok(data,","); p; p = strtok(NULL,","))
\r
3254 They both compile to the same code but follow different programming
\r
3257 Initially, you call strtok() with the name of the string variable to be
\r
3258 parsed, and a second string that contains the known delimiters. Strtok()
\r
3259 then returns a pointer to the start of the first substring and replaces
\r
3260 the first token with a zero delimiter. Subsequent calls to strtok() can
\r
3261 be made in a loop passing NULL as the string to be parsed, and strtok()
\r
3262 will return the subsequent substrings.
\r
3265 Since strtok() can accept many delimiter characters in the second
\r
3266 parameter string we can use it as the basis of a simple word counting
\r
3269 #include <stdio.h>
\r
3270 #include <stdlib.h>
\r
3271 #include <string.h>
\r
3273 void main(int argc, char *argv[])
\r
3282 fputs("\nERROR: Usage is wordcnt <file>\n",stderr);
\r
3286 /* Open file for reading */
\r
3287 fp = fopen(argv[1],"r");
\r
3289 /* Check the open was okay */
\r
3292 fputs("\nERROR: Cannot open source file\n",stderr);
\r
3296 /* Initialise word count */
\r
3301 /* Read a line of data from the file */
\r
3302 fgets(buffer,255,fp);
\r
3304 /* check for an error in the read or EOF */
\r
3305 if (ferror(fp) || feof(fp))
\r
3308 /* count words in received line */
\r
3309 /* Words are defined as separated by the characters */
\r
3310 /* \t(tab) \n(newline) , ; : . ! ? ( ) - and [space] */
\r
3311 p = strtok(buffer,"\t\n,;:.!?()- ");
\r
3315 p = strtok(NULL,"\t\n,;:.!?()- ");
\r
3318 while(!ferror(fp) && !feof(fp));
\r
3320 /* Finished reading. Was it due to an error? */
\r
3323 fputs("\nERROR: Reading source file\n",stderr);
\r
3328 /* Reading finished due to EOF, quite valid so print count */
\r
3329 printf("\nFile %s contains %ld words\n",argv[1],count);
\r
3336 Converting Numbers To And From Strings
\r
3338 All C compilers provide a facility for converting numbers to strings.
\r
3339 This being sprintf(). However, as happens sprintf() is a multi-purpose
\r
3340 function that is therefore large and slow. The following function ITOS()
\r
3341 accepts two parameters, the first being a signed integer and the second
\r
3342 being a pointer to a character string. It then copies the integer into
\r
3343 the memory pointed to by the character pointer. As with sprintf() ITOS()
\r
3344 does not check that the target string is long enough to accept the result
\r
3345 of the conversion. You should then ensure that the target string is long
\r
3348 Example function for copying a signed integer into a string;
\r
3350 void ITOS(long x, char *ptr)
\r
3352 /* Convert a signed decimal integer to a string */
\r
3354 long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,
\r
3355 1000, 100, 10, 1 };
\r
3362 /* Convert x to absolute */
\r
3366 for(n = 0; n < 9; n++)
\r
3370 *ptr++ = '0' + x / pt[n];
\r
3377 To convert a string to a floating point number, C provides two functions;
\r
3378 atof() and strtod(). atof() has the prototype;
\r
3380 double atof(const char *s);
\r
3382 strtod has the prototype;
\r
3384 double strtod(const char *s,char **endptr);
\r
3386 Both functions scan the string and convert it as far as they can, until
\r
3387 they come across a character they don't understand. The difference
\r
3388 between the two functions is that if strtod() is passed a character
\r
3389 pointer for parameter `endptr', it sets that pointer to the first
\r
3390 character in the string that terminated the conversion. Because of its
\r
3391 better error reporting, by way of endptr, strtod() is often preferred to
\r
3394 To convert a string to an integer use atoi() that has the prototype;
\r
3397 int atoi(const char *s);
\r
3399 atoi() does not check for an overflow, and the results are undefined!
\r
3401 atol() is a similar function but returns a long. Alternatively, you can
\r
3402 use strtol() and stroul() instead that have better error checking.
\r
3407 Human languages write information down as `text'. This is comprised of
\r
3408 words, figures and punctuation. The words being made up of upper case and
\r
3409 lower case letters. Processing text with a computer is a commonly
\r
3410 required task, and yet quite a difficult one.
\r
3412 The ANSI C definitions include string processing functions that are by
\r
3413 their nature sensitive to case. That is the letter `A' is seen as
\r
3414 distinct from the letter `a'. This is the first problem that must be
\r
3415 overcome by the programmer. Fortunately both Borland's Turbo C compilers
\r
3416 and Microsoft's C compilers include case insensitive forms of the string
\r
3419 stricmp() for example is the case insensitive form of strcmp(), and
\r
3420 strnicmp() is the case insensitive form of strncmp().
\r
3422 If you are concerned about writing portable code, then you must restrict
\r
3423 yourself to the ANSI C functions, and write your own case insensitive
\r
3424 functions using the tools provided.
\r
3426 Here is a simple implementation of a case insensitive version of
\r
3427 strstr(). The function simply makes a copy of the parameter strings,
\r
3428 converts those copies both to upper case and then does a standard
\r
3429 strstr() on the copies. The offset of the target string within the
\r
3430 source string will be the same for the copy as the original, and so it
\r
3431 can be returned relative to the parameter string.
\r
3434 char *stristr(char *s1, char *s2)
\r
3446 p = strstr(c1,c2);
\r
3448 return s1 + (p - c1);
\r
3452 This function scans a string, s1 looking for the word held in s2. The
\r
3453 word must be a complete word, not simply a character pattern, for the
\r
3454 function to return true. It makes use of the stristr() function described
\r
3457 int word_in(char *s1,char *s2)
\r
3459 /* return non-zero if s2 occurs as a word in s1 */
\r
3469 /* Locate character occurence s2 in s1 */
\r
3470 p = stristr(q,s2);
\r
3478 /* Check previous character */
\r
3479 if (*(p - 1) >= 'A' && *(p - 1) <= 'z')
\r
3483 /* Move p to end of character set */
\r
3487 /* Check character following */
\r
3488 if (*p >= 'A' && *p <= 'z')
\r
3499 Some more useful functions for dealing with text are truncstr() that
\r
3500 truncates a string;
\r
3502 void truncstr(char *p,int num)
\r
3504 /* Truncate string by losing last num characters */
\r
3505 if (num < strlen(p))
\r
3506 p[strlen(p) - num] = 0;
\r
3509 trim() that removes trailing spaces from the end of a string;
\r
3511 void trim(char *text)
\r
3513 /* remove trailing spaces */
\r
3516 p = &text[strlen(text) - 1];
\r
3517 while(*p == 32 && p >= text)
\r
3521 strlench() that changes the length of a string by adding or deleting
\r
3524 void strlench(char *p,int num)
\r
3526 /* Change length of string by adding or deleting characters */
\r
3529 memmove(p + num,p,strlen(p) + 1);
\r
3533 memmove(p,p + num,strlen(p) + 1);
\r
3537 strins() that inserts a string into another string;
\r
3539 void strins(char *p, char *q)
\r
3541 /* Insert string q into p */
\r
3542 strlench(p,strlen(q));
\r
3543 strncpy(p,q,strlen(q));
\r
3546 strchg() that replaces all occurences of one sub-string with another
\r
3547 within a target string;
\r
3549 void strchg(char *data, char *s1, char *s2)
\r
3551 /* Replace all occurences of s1 with s2 */
\r
3558 p = strstr(data,s1);
\r
3561 /* Delete original string */
\r
3562 strlench(p,0 - strlen(s1));
\r
3564 /* Insert replacement string */
\r
3574 C provides a function, time(), which reads the computer's system clock to
\r
3575 return the system time as a number of seconds since midnight on January
\r
3576 the first, 1970. However, this value can be converted to a useful string
\r
3577 by the function ctime() as illustrated in the following example;
\r
3579 #include <stdio.h>
\r
3584 /* Structure to hold time, as defined in time.h */
\r
3587 /* Get system date and time from computer */
\r
3589 printf("Today's date and time: %s\n",ctime(&t));
\r
3592 The string returned by ctime() is comprised of seven fields;
\r
3595 Month of the year,
\r
3596 Date of the day of the month,
\r
3600 century of the year
\r
3602 terminated by a newline character and null terminating byte. Since the
\r
3603 fields always occupy the same width, slicing operations can be carried
\r
3604 out on the string with ease. The following program defines a structure
\r
3605 `time' and a function gettime() that extracts the hours, minutes and
\r
3606 seconds of the current time and places them in the structure;
\r
3609 #include <stdio.h>
\r
3614 int ti_min; /* Minutes */
\r
3615 int ti_hour; /* Hours */
\r
3616 int ti_sec; /* Seconds */
\r
3619 void gettime(struct time *now)
\r
3625 /* Get system date and time from computer */
\r
3628 /* Translate dat and time into a string */
\r
3629 strcpy(temp,ctime(&t));
\r
3631 /* Copy out just time part of string */
\r
3635 /* Scan time string and copy into time structure */
\r
3636 sscanf(ts,"%2d:%2d:%2d",&now->ti_hour,&now->ti_min,&now-
\r
3646 printf("\nThe time is
\r
3647 %02d:%02d:%02d",now.ti_hour,now.ti_min,now.ti_sec);
\r
3651 The ANSI standard on C does actually provide a function ready made to
\r
3652 convert the value returned by time() into a structure;
\r
3654 #include <stdio.h>
\r
3662 /* Get time into t */
\r
3665 /* Convert time value t into structure pointed to by tb */
\r
3666 tb = localtime(&t);
\r
3668 printf("\nTime is %02d:%02d:%02d",tb->tm_hour,tb->tm_min,tb-
\r
3672 The structure 'tm' is defined in time.h as;
\r
3689 Often a program must determine the date and time from the host computer's
\r
3690 non-volatile RAM. There are several time functions provided by the ANSI
\r
3691 standard on C that allow a program to retrieve, from the host computer,
\r
3692 the current date and time;
\r
3694 time() returns the number of seconds that have elapsed since midnight on
\r
3695 January the 1st 1970. It has the prototype;
\r
3697 time_t time(time_t *timer);
\r
3699 time() fills in the time_t variable sent as a parameter and returns the
\r
3700 same value. You can call time() with a NULL parameter and just collect
\r
3701 the return value thus;
\r
3712 asctime() converts a time block to a twenty six character string of the
\r
3715 Wed Oct 14 10:23:45 1992\n\0
\r
3717 asctime() has the prototype;
\r
3719 char *asctime(const struct tm *tblock);
\r
3721 ctime() converts a time value (as returned by time()) into a twenty six
\r
3722 chracter string of the same format as asctime(). For example;
\r
3724 #include <stdio.h>
\r
3733 strcpy(date,ctime(&now));
\r
3736 difftime() returns the difference, in seconds, between two values (as
\r
3737 returned by time()). This can be useful for testing the elapsed time
\r
3738 between two events, the time a function takes to execute, and for
\r
3739 creating consistent delays that are irrelevant of the host computer.
\r
3741 An example delay program;
\r
3743 #include <stdio.h>
\r
3747 void DELAY(int period)
\r
3751 start = time(NULL);
\r
3752 while(time(NULL) < start + period)
\r
3758 printf("\nStarting delay now....(please wait 5 seconds)");
\r
3762 puts("\nOkay, I've finished!");
\r
3765 gmtime() converts a local time value (as returned by time()) to the GMT
\r
3766 time and stores it in a time block. This function depends upon the global
\r
3767 variable timezone being set.
\r
3770 The time block is a predefined structure (declared in time.h) as follows;
\r
3785 tm_mday records the day of the month, ranging from 1 to 31; tm_wday is
\r
3786 the day of the week with Sunday being represented by 0; the year is
\r
3787 recorded less 1900; tm_isdst is a flag to show whether daylight saving
\r
3788 time is in effect. The actual names of the structure and its elements may
\r
3789 vary from compiler to compiler, but the structure should be the same in
\r
3792 mktime() converts a time block to a calendar format. It follows the
\r
3795 time_t mktime(struct tm *t);
\r
3797 The following example allows entry of a date, and uses mktime() to
\r
3798 calculate the day of the week appropriate to that date. Only dates from
\r
3799 the first of January 1970 are recognisable by the time functions.
\r
3801 #include <stdio.h>
\r
3803 #include <string.h>
\r
3811 char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
\r
3812 "Thursday", "Friday", "Saturday" ,
\r
3813 "prior to 1970, thus not known" };
\r
3817 printf("\nEnter a date as dd/mm/yy ");
\r
3818 p = fgets(data,8,stdin);
\r
3819 p = strtok(data,"/");
\r
3822 tsruct.tm_mday = atoi(p);
\r
3826 p = strtok(NULL,"/");
\r
3828 tsruct.tm_mon = atoi(p);
\r
3832 p = strtok(NULL,"/");
\r
3835 tsruct.tm_year = atoi(p);
\r
3842 tsruct.tm_hour = 0;
\r
3843 tsruct.tm_min = 0;
\r
3844 tsruct.tm_sec = 1;
\r
3845 tsruct.tm_isdst = -1;
\r
3847 /* Now get day of the week */
\r
3848 if (mktime(&tsruct) == -1)
\r
3849 tsruct.tm_wday = 7;
\r
3851 printf("That was %s\n",wday[tsruct.tm_wday]);
\r
3854 mktime() also makes the neccessary adjustments for values out of range,
\r
3855 this can be utilised for discovering what the date will be in n number of
\r
3859 #include <stdio.h>
\r
3861 #include <string.h>
\r
3865 struct tm *tsruct;
\r
3868 today = time(NULL);
\r
3869 tsruct = localtime(&today);
\r
3871 tsruct->tm_mday += 10;
\r
3874 printf("In ten days it will be %02d/%02d/%2d\n", tsruct-
\r
3875 >tm_mday,tsruct->tm_mon + 1,tsruct->tm_year);
\r
3880 This program uses Julian Dates to decide any day of the week since the
\r
3881 1st of October 1582 when the Gregorian calendar was introduced.
\r
3883 char *WDAY(int day, int month, int year)
\r
3885 /* Returns a pointer to a string representing the day of the
\r
3888 static char *cday[] = { "Saturday","Sunday","Monday","Tuesday",
\r
3889 "Wednesday","Thursday","Friday" };
\r
3909 j = day + (int)(365.25*year);
\r
3911 j += (int)(30.6001 * (month + 1 + x)) - y1 + y4;
\r
3913 if (yy == y1 && yt != y4 && month < 3)
\r
3916 j = 1 + j - 7 * (int)(j/7);
\r
3925 With time() and difftime() we can create a timer for testing the
\r
3926 execution times of functions thus;
\r
3928 #include <stdio.h>
\r
3941 /* This loop is adjustable for multiple passes */
\r
3942 for(n = 0; n < 5000; n++)
\r
3943 /* Call the function to test */
\r
3946 then = time(NULL);
\r
3948 elapsed = difftime(then,now);
\r
3949 printf("\nElapsed seconds==%lf\n",elapsed);
\r
3952 By way of time() and ctime() the current system date and time can be
\r
3953 retrieved from the host computer thus;
\r
3955 #include <stdio.h>
\r
3964 /* Get system time */
\r
3967 /* Convert system time to a string */
\r
3968 date = ctime(&now);
\r
3970 /*Display system time */
\r
3971 printf("\nIt is %s",date);
\r
3974 time_t is a type defined in time.h as the type of variable returned by
\r
3975 time(). This type may vary from compiler to compiler, and therefore is
\r
3976 represented by the type "time_t".
\r
3980 Function prototypes for library functions supplied with the C compiler,
\r
3981 and standard macros are declared in header files.
\r
3983 The ANSI standard on the C programming language lists the following
\r
3986 Header file Description
\r
3988 assert.h Defines the assert debugging macro
\r
3989 ctype.h Character classification and
\r
3991 errno.h Constant mnemonics for error codes
\r
3992 float.h Defines implementation specific
\r
3993 macros for dealing with floating
\r
3995 limits.h Defines implementation specific
\r
3996 limits on type values
\r
3997 locale.h Country specific parameters
\r
3998 math.h Prototypes for mathematics functions
\r
3999 setjmp.h Defines typedef and functions for
\r
4001 signal.h Constants and declarations for use by
\r
4002 signal() and raise()
\r
4003 stdarg.h Macros for dealing with argument
\r
4005 stddef.h Common data types and macros
\r
4006 stdio.h Types and macros required for
\r
4008 stdlib.h Prototypes of commonly used functions
\r
4010 string.h String manipulation function
\r
4012 time.h Structures for time conversion
\r
4017 The ANSI standard on C includes a macro function for debugging called
\r
4018 assert(). This expands to an if() statement, which if it returns true
\r
4019 terminates the program and outputs to the standard error stream a message
\r
4023 Assertion failed: <test>, file <module>, line <line number>
\r
4024 Abnormal program termination
\r
4025 For example, the following program accidentally assigns a zero value to a
\r
4028 #include <stdio.h>
\r
4029 #include <assert.h>
\r
4033 /* Demonstration of assert */
\r
4040 /* Whoops! error in this line! */
\r
4043 assert(ptr != NULL);
\r
4046 When run, this program terminates with the message:
\r
4048 Assertion failed: ptr != 0, file TEST.C, line 16
\r
4049 Abnormal program termination
\r
4051 When a program is running okay, the assert() functions can be removed
\r
4052 from the compiled program by simply adding the line;
\r
4056 before the #include <assert.h> line. Effectively the assert functions are
\r
4057 commented out in the preprocessed source before compilation, this means
\r
4058 that the assert expressions are not evaluated, and thus cannot cause any
\r
4063 Floating point numbers are decimal fractions, decimal fractions do not
\r
4064 accurately equate to normal fractions as not every number will divide
\r
4065 precisely by ten. This creates the potential for rounding errors in
\r
4066 calculations that use floating point numbers. The following program
\r
4067 illustrates one such example of rounding error problems;
\r
4070 #include <stdio.h>
\r
4076 for(number = 1; number > 0.4; number -= 0.01)
\r
4077 printf("\n%f",number);
\r
4080 At about 0.47 (depending upon the host computer and compiler) the program
\r
4081 starts to store an inaccurate value for 'number'.
\r
4083 This problem can be minimised by using longer floating point numbers,
\r
4084 doubles or long doubles that have larger storage space allocated to them.
\r
4085 For really accurate work though, you should use integers and only convert
\r
4086 to a floating point number for display. You also should notice that most
\r
4087 C compilers default floating point numbers to `doubles' and when using
\r
4088 `float' types have to convert the double down to a float!
\r
4094 When a system error occurs within a program, for example when an attempt
\r
4095 to open a file fails, it is helpful to the program's user to display a
\r
4096 message reporting the failure. Equally it is useful to the program's
\r
4097 developer to know why the error occurred, or at least as much about it as
\r
4098 possible. To this end the ANSI standard on C describes a function,
\r
4099 perror(), which has the prototype;
\r
4102 void perror(const char *s);
\r
4104 and is used to display an error message. The program's own prefixed error
\r
4105 message is passed to perror() as the string parameter. This error message
\r
4106 is displayed by perror() followed by the host's system error separated by
\r
4107 a colon. The following example illustrates a use for perror();
\r
4110 #include <stdio.h>
\r
4115 char fname[] = "none.xyz";
\r
4117 fp = fopen(fname,"r");
\r
4124 If the fopen() operation fails, a message similar to;
\r
4126 none.xyz: No such file or directory
\r
4130 You should note, perror() sends its output to the predefined stream
\r
4131 `stderr', which is usually the host computer's display unit.
\r
4134 perror() finds its message from the host computer via the global variable
\r
4135 'errno' that is set by most, but not all system functions.
\r
4137 Unpleasant errors might justify the use of abort(). abort() is a function
\r
4138 that terminates the running program with a message;
\r
4140 "Abnormal program termination"
\r
4142 and returns an exit code of 3 to the parent process or operating system.
\r
4146 Critical Error Handling With The IBM PC AND DOS
\r
4148 The IBM PC DOS operating system provides a user amendable critical error
\r
4149 handling function. This function is usually discovered by attempting to
\r
4150 write to a disk drive that does not have a disk in it, in which case the
\r
4153 Not ready error writing drive A
\r
4154 Abort Retry Ignore?
\r
4156 Message is displayed on the screen. Fine when it occurs from within a DOS
\r
4157 program, not so fine from within your own program!
\r
4159 The following example program shows how to redirect the DOS critical
\r
4160 error interrupt to your own function;
\r
4163 /* DOS critical error handler test */
\r
4165 #include <stdio.h>
\r
4168 void interrupt new_int();
\r
4169 void interrupt (*old_int)();
\r
4177 old_int = getvect(0x24);
\r
4179 /* Set critical error handler to my function */
\r
4180 setvect(0x24,new_int);
\r
4182 /* Generate an error by not having a disc in drive A */
\r
4183 fp = fopen("a:\\data.txt","w+");
\r
4185 /* Display error status returned */
\r
4186 printf("\nStatus == %d",status);
\r
4190 void interrupt new_int()
\r
4192 /* set global error code */
\r
4195 /* ignore error and return */
\r
4199 When the DOS critical error interrupt is called, a status message is
\r
4200 passed in the low byte of the DI register. This message is one of;
\r
4204 00 Write-protect error
\r
4206 02 Drive not ready
\r
4207 03 Unknown command
\r
4208 04 Data error, bad CRC
\r
4209 05 Bad request structure
\r
4212 07 Unknown media type
\r
4213 08 Sector not found
\r
4214 09 Printer out of paper
\r
4217 0C General failure
\r
4219 Your critical error interrupt handler can transfer this status message
\r
4220 into a global variable, and then set the result message held in register
\r
4228 02 Terminate program
\r
4229 03 Fail (Available with
\r
4230 DOS 3.3 and above)
\r
4233 If you choose to set AL to 02, terminate program, you should ensure ALL
\r
4234 files are closed first since DOS will terminate the program abruptly,
\r
4235 leaving files open and memory allocated, not a pretty state to be in!
\r
4238 The example program shown returns an ignore status from the critical
\r
4239 error interrupt, and leaves the checking of any errors to the program
\r
4240 itself. So, in this example after the call to fopen() we could check the
\r
4241 return value in fp, and if it reveals an error (NULL in this case) we
\r
4242 could then check the global variable status and act accordingly, perhaps
\r
4243 displaying a polite message to the user to put a disk in the floppy drive
\r
4244 and ensure that the door is closed.
\r
4246 The following is a practical function for checking whether a specified
\r
4247 disc drive can be accessed. It should be used with the earlier critical
\r
4248 error handler and global variable `status'.
\r
4250 int DISCOK(int drive)
\r
4252 /* Checks for whether a disc can be read */
\r
4253 /* Returns false (zero) on error */
\r
4254 /* Thus if(!DISCOK(drive)) */
\r
4257 unsigned char buffer[25];
\r
4262 /* If already logged to disc, return okay */
\r
4263 if ('A' + drive == diry[0])
\r
4266 /* Attempt to read disc */
\r
4267 memset(buffer,0,20);
\r
4268 sprintf(buffer,"%c:$$$.$$$",'A'+drive);
\r
4270 _open(buffer,O_RDONLY);
\r
4272 /* Check critical error handler status */
\r
4276 /* Disc cannot be read */
\r
4283 Casting tells the compiler what a data type is, and can be used to change
\r
4284 a data type. For example, consider the following;
\r
4286 #include <stdio.h>
\r
4296 printf("\n%lf",x / y);
\r
4299 The printf() function has been told to expect a double. However, the
\r
4300 compiler sees the variables `x' and `y' as integers, and an error occurs!
\r
4301 To make this example work we must tell the compiler that the result of
\r
4302 the expression x / y is a double, this is done with a cast thus;
\r
4305 #include <stdio.h>
\r
4315 printf("\n%lf",(double)(x / y));
\r
4318 Notice the data type `double' is enclosed by parenthesis, and so is the
\r
4319 expression to convert. But now, the compiler knows that the result of the
\r
4320 expression is a double, but it still knows that the variables `x' and `y'
\r
4321 are integers and so an integer division will be carried out. We have to
\r
4322 cast the constants thus;
\r
4324 #include <stdio.h>
\r
4334 printf("\n%lf",(double)(x) / (double)(y));
\r
4337 Because both of the constants are doubles, the compiler knows that the
\r
4338 outcome of the expression will also be a double.
\r
4343 THE IMPORTANCE OF PROTOTYPING
\r
4345 Prototyping a function involves letting the compiler know in advance what
\r
4346 type of values a function will receive and return. For example, lets look
\r
4347 at strtok(). This has the prototype;
\r
4350 char *strtok(char *s1, const char *s2);
\r
4352 This prototype tells the compiler that strtok() will return a character
\r
4353 pointer, the first received parameter will be a pointer to a character
\r
4354 string, and that string can be changed by strtok(), and the last
\r
4355 parameter will be a pointer to a character string that strtok() cannot
\r
4358 The compiler knows how much space to allocate for the return parameter,
\r
4359 sizeof(char *), but without a prototype for the function the compiler
\r
4360 will assume that the return value of strtok() is an integer, and will
\r
4361 allocate space for a return type of int, that is sizeof(int). If an
\r
4362 integer and a character pointer occupy the same number of bytes on the
\r
4363 host computer no major problems will occur, but if a character pointer
\r
4364 occupies more space than an integer, then the compiler wont have
\r
4365 allocated enough space for the return value and the return from a call to
\r
4366 strtok() will overwrite some other bit of memory. If, as so often happens
\r
4367 the return value is returned via the stack, the results of confusing the
\r
4368 compiler can be disastrous!
\r
4370 Thankfully most C compilers will warn the programmer if a call to a
\r
4371 function has been made without a prototype, so that you can add the
\r
4372 required function prototypes.
\r
4374 Consider the following example that will not compile on most modern C
\r
4375 compilers due to the nasty error in it;
\r
4378 #include <stdio.h>
\r
4380 int FUNCA(int x, int y)
\r
4382 return(MULT(x,y));
\r
4385 double MULT(double x, double y)
\r
4393 printf("\n%d",FUNCA(5,5));
\r
4396 When the compiler first encounters the function MULT() it is as a call
\r
4397 from within FUNCA(). In the absence of any prototype for MULT() the
\r
4398 compiler assumes that MULT() returns an integer. When the compiler finds
\r
4399 the definition for function MULT() it sees that a return of type double
\r
4400 has been declared. The compiler then reports an error in the compilation
\r
4401 saying something like;
\r
4404 "Type mismatch in redclaration of function 'MULT'"
\r
4406 What the compiler is really trying to say is, prototype your functions
\r
4407 before using them! If this example did compile, and was then run it
\r
4408 probably would crash the computer's stack and cause a system hang.
\r
4411 POINTERS TO FUNCTIONS
\r
4413 C allows a pointer to point to the address of a function, and this
\r
4414 pointer to be called rather than specifying the function. This is used by
\r
4415 interrupt changing functions and may be used for indexing functions
\r
4416 rather than using switch statements. For example;
\r
4418 #include <stdio.h>
\r
4421 double (*fp[7])(double x);
\r
4439 printf("\nResult %lf",x);
\r
4442 This example program defines an array of pointers to functions, (*fp[])()
\r
4443 that are then called dependant upon the value in the indexing variable p.
\r
4444 This program could also be written;
\r
4446 #include <stdio.h>
\r
4458 case 0 : x = sin(1.5);
\r
4460 case 1 : x = cos(1.5);
\r
4462 case 2 : x = acos(1.5);
\r
4464 case 3 : x = asin(1.5);
\r
4466 case 4 : x = tan(1.5);
\r
4468 case 5 : x = atan(1.5);
\r
4470 case 6 : x = ceil(1.5);
\r
4473 puts("\nResult %lf",x);
\r
4476 The first example, using pointers to the functions, compiles into much
\r
4477 smaller code and executes faster than the second example.
\r
4479 The table of pointers to functions is a useful facility when writing
\r
4480 language interpreters, the program compares an entered instruction
\r
4481 against a table of key words that results in an index variable being set
\r
4482 and then the program simply needs to call the function pointer indexed by
\r
4483 the variable, rather than wading through a lengthy switch() statement.
\r
4486 DANGEROUS PITFALLS
\r
4488 One of the most dangerous pitfalls can occur with the use of gets(). This
\r
4489 function accepts input from the stream stdin until it receives a newline
\r
4490 character, which it does not pass to the program. All the data it
\r
4491 receives is stored in memory starting at the address of the specified
\r
4492 string, and quite happily overflowing into other variables! This danger
\r
4493 can be avoided by using fgets() that allows a maximum number of
\r
4494 characters to be specified, so you can avoid overflow problems. Notice
\r
4495 though that fgets() does retain the newline character scanf() is another
\r
4496 function best avoided. It accepts input from stdin and stores the
\r
4497 received data at the addresses provided to it. If those addresses are not
\r
4498 really addresses where the data ends up is anybodys guess!
\r
4500 This example is okay, since scanf() has been told to store the data at
\r
4501 the addresses occupied by the two variables `x' and `y'.
\r
4509 scanf("%d%d",&x,&y);
\r
4512 But in this example scanf() has been told to store the data at the
\r
4513 addresses suggested by the current values of `x' and `y'! An easy and
\r
4514 common mistake to make, and yet one that can have very peculiar effects.
\r
4522 scanf("%d%d",x,y);
\r
4525 The answer is, don't use scanf(), use fgets() and parse your string
\r
4526 manually using the standard library functions strtod(), strtol() and
\r
4529 Here is the basis of a simple input string parser that returns the
\r
4530 individual input fields from an entered string;
\r
4532 #include <stdio.h>
\r
4533 #include <string.h>
\r
4540 puts("\nEnter a string ");
\r
4541 fgets(input,79,stdin);
\r
4543 /* now parse string for input fields */
\r
4544 puts("The fields entered are:");
\r
4545 p = strtok(input,", ");
\r
4549 p = strtok(NULL,", ");
\r
4556 A preprocessor instruction, `sizeof', returns the size of an item, be it
\r
4557 a structure, pointer, string or whatever. However! take care when using
\r
4558 `sizeof'. Consider the following program;
\r
4561 #include <stdio.h>
\r
4564 char string1[80]; char *text = "This is a string of data" ;
\r
4568 /* Initialise string1 correctly */
\r
4569 memset(string1,0,sizeof(string1));
\r
4571 /* Copy some text into string1 ? */
\r
4572 memcpy(string1,text,sizeof(text));
\r
4574 /* Display string1 */
\r
4575 printf("\nString 1 = %s\n",string1);
\r
4578 What it is meant to do is initialise all 80 elements of string1 to
\r
4579 zeroes, which it does alright, and then copy the constant string `text'
\r
4580 into the variable `string1'. However, variable text is a pointer, and so
\r
4581 the sizeof(text) instruction returns the size of the character pointer
\r
4582 (perhaps two bytes) rather than the length of the string pointed to by
\r
4583 the pointer! If the length of the string pointed to by `text' happened
\r
4584 to be the same as the size of a character pointer then no error would be
\r
4590 The IBM PC BIOS and DOS contain functions that may be called by a program
\r
4591 by way of the function's interrupt number. The address of the function
\r
4592 assigned to each interrupt is recorded in a table in RAM called the
\r
4593 interrupt vector table. By changing the address of an interrupt vector a
\r
4594 program can effectively disable the original interrupt function and
\r
4595 divert any calls to it to its own function. This was done by the critical
\r
4596 error handler described in the section on error handling.
\r
4598 Borland's Turbo C provides two library functions for reading and changing
\r
4599 an interrupt vector. These are: setvect() and getvect(). The
\r
4600 corresponding Microsoft C library functions are: _dos_getvect() and
\r
4603 getvect() has the function prototype;
\r
4605 void interrupt(*getvect(int interrupt_no))();
\r
4607 setvect() has the prototype;
\r
4609 void setvect(int interrupt_no, void interrupt(*func)());
\r
4611 To read and save the address of an existing interrupt a program uses
\r
4614 /* Declare variable to record old interrupt */
\r
4615 void interrupt(*old)(void);
\r
4619 /* get old interrupt vector */
\r
4620 old = getvect(0x1C);
\r
4626 Where 0x1C is the interrupt vector to be retrieved.
\r
4628 To then set the interrupt vector to a new address, our own function, we
\r
4629 use setvect() thus;
\r
4631 void interrupt new(void)
\r
4635 /* New interrupt function */
\r
4646 setvect(0x1C,new);
\r
4653 There are two important points to note about interrupts;
\r
4655 First, if the interrupt is called by external events then before changing
\r
4656 the vector you MUST disable the interrupt callers using disable() and
\r
4657 then re-enable the interrupts after the vector has been changed using
\r
4658 enable(). If a call is made to the interrupt while the vector is being
\r
4659 changed ANYTHING could happen!
\r
4661 Secondly, before your program terminates and returns to DOS you must
\r
4662 reset any changed interrupt vectors! The exception to this is the
\r
4663 critical error handler interrupt vector that is restored automatically by
\r
4664 DOS, so your program needn't bother restoring it.
\r
4666 This example program hooks the PC clock timer interrupt to provide a
\r
4667 background clock process while the rest of the program continues to run.
\r
4668 If included with your own program that requires a constantly displayed
\r
4669 clock on screen, you need only amend the display coordinates in the call
\r
4670 to puttext(). Sincle the closk display code is called by a hardware
\r
4671 issued interrupt, your program can start the clock and forget it until it
\r
4675 /* Compile in LARGE memory model */
\r
4677 #include <stdio.h>
\r
4680 #include <conio.h>
\r
4681 #include <stdlib.h>
\r
4683 enum { FALSE, TRUE };
\r
4685 #define COLOUR (BLUE << 4) | YELLOW
\r
4687 #define BIOS_TIMER 0x1C
\r
4689 static unsigned installed = FALSE;
\r
4690 static void interrupt (*old_tick) (void);
\r
4692 static void interrupt tick (void)
\r
4698 static time_t last_time = 0L;
\r
4699 static char video_buf[20] =
\r
4701 ' ', COLOUR, '0', COLOUR, '0', COLOUR, ':', COLOUR, '0',
\r
4703 '0', COLOUR, ':', COLOUR, '0', COLOUR, '0', COLOUR, ' ',
\r
4709 if (time (&this_time) != last_time)
\r
4711 last_time = this_time;
\r
4713 now = localtime(&this_time);
\r
4715 sprintf(time_buf, "%02d:%02d.%02d",now->tm_hour,now-
\r
4716 >tm_min,now->tm_sec);
\r
4718 for (i = 0; i < 8; i++)
\r
4720 video_buf[(i + 1) << 1] = time_buf[i];
\r
4723 puttext (71, 1, 80, 1, video_buf);
\r
4729 void stop_clock (void)
\r
4733 setvect (BIOS_TIMER, old_tick);
\r
4734 installed = FALSE;
\r
4738 void start_clock (void)
\r
4740 static unsigned first_time = TRUE;
\r
4746 atexit (stop_clock);
\r
4747 first_time = FALSE;
\r
4750 old_tick = getvect (BIOS_TIMER);
\r
4751 setvect (BIOS_TIMER, tick);
\r
4758 Interrupts raised by the host computer can be trapped and diverted in
\r
4759 several ways. A simple method is to use signal().
\r
4761 Signal() takes two parameters in the form;
\r
4763 void (*signal (int sig, void (*func) (int))) (int);
\r
4765 The first parameter, `sig' is the signal to be caught. These are often
\r
4766 predefined by the header file `signal.h'.
\r
4768 The second parameter is a pointer to a function to be called when the
\r
4769 signal is raised. This can either be a user function, or a macro defined
\r
4770 in the header file `signal.h' to do some arbitrary task, such as ignore
\r
4771 the signal for example.
\r
4773 On a PC platform, it is often useful to disable the `ctrl-break' key
\r
4774 combination that is used to terminate a running program by the user. The
\r
4775 following PC signal() call replaces the predefined signal `SIGINT', which
\r
4776 equates to the ctrl-break interrupt request, with the predefined macro
\r
4777 `SIG-IGN', ignore the request;
\r
4780 signal(SIGINT,SIG_IGN);
\r
4782 This example catches floating point errors on a PC, and zero divisions!
\r
4784 #include <stdio.h>
\r
4785 #include <signal.h>
\r
4787 void (*old_sig)();
\r
4789 void catch(int sig)
\r
4791 printf("Catch was called with: %d\n",sig);
\r
4800 old_sig = signal(SIGFPE,catch);
\r
4805 /* Restore original handler before exiting! */
\r
4806 signal(SIGFPE,old_sig);
\r
4810 SORTING AND SEARCHING
\r
4812 The ANSI C standard defines qsort(), a function for sorting a table of
\r
4813 data. The function follows the format;
\r
4815 qsort(void *base,size_t elements,size_t width,int (*cmp)(void *,
\r
4818 The following short program illustrates the use of qsort() with a
\r
4821 #include <string.h>
\r
4826 char data[10][20];
\r
4828 /* Initialise some arbirary data */
\r
4830 strcpy(data[0],"RED");
\r
4831 strcpy(data[1],"BLUE");
\r
4832 strcpy(data[2],"GREEN");
\r
4833 strcpy(data[3],"YELLOW");
\r
4834 strcpy(data[4],"INDIGO");
\r
4835 strcpy(data[5],"BROWN");
\r
4836 strcpy(data[6],"BLACK");
\r
4837 strcpy(data[7],"ORANGE");
\r
4838 strcpy(data[8],"PINK");
\r
4839 strcpy(data[9],"CYAN");
\r
4841 /* Sort the data table */
\r
4842 qsort(data[0],10,20,strcmp);
\r
4844 /* Print the data table */
\r
4845 for(n = 0; n < 10; n++)
\r
4850 Here is a program that implements the shell sort algorithm (this one is
\r
4851 based on the routine in K & R), which sorts arrays of pointers based upon
\r
4852 the data pointed to by the pointers;
\r
4854 #include <stdio.h>
\r
4855 #include <stdlib.h>
\r
4856 #include <string.h>
\r
4858 #define LINELEN 80
\r
4859 #define MAXLINES 2000
\r
4861 char *lines[MAXLINES];
\r
4868 /* SHELL Sort Courtesy of K & R */
\r
4873 char temp[LINELEN];
\r
4875 for(gap = lastone / 2; gap > 0; gap /= 2)
\r
4876 for(i = gap; i < lastone; i++)
\r
4877 for(j = i - gap; j >= 0 && strcmp(lines[j] , lines[j +
\r
4881 strcpy(temp,lines[j]);
\r
4882 strcpy(lines[j] , lines[j + gap]);
\r
4883 strcpy(lines[j + gap] , temp);
\r
4888 void main(int argc, char *argv[])
\r
4894 /* Check command line parameter has been given */
\r
4897 printf("\nError: Usage is SERVSORT file");
\r
4901 /* Attempt to open file for updating */
\r
4902 fp = fopen(argv[1],"r+");
\r
4905 printf("\nError: Unable to open %s",argv[1]);
\r
4909 /* Initialise element counter to zero */
\r
4912 /* Read file to be sorted */
\r
4913 while((fgets(buff,100,fp)) != NULL)
\r
4915 /* Allocate memory block*/
\r
4916 lines[lastone] = malloc(LINELEN);
\r
4917 if (lines[lastone] == NULL)
\r
4919 printf("\nError: Unable to allocate memory");
\r
4923 strcpy(lines[lastone],buff);
\r
4926 if (lastone > MAXLINES)
\r
4928 printf("\nError: Too many lines in source file");
\r
4932 /* Call sort function */
\r
4938 /* Reopen file in create mode */
\r
4939 fp = fopen(argv[1],"w+");
\r
4941 /* Copy sorted data from memory to disk */
\r
4942 for(n = 0; n < lastone; n++)
\r
4943 fputs(lines[n],fp);
\r
4945 /* Close file finally */
\r
4948 /* Return to calling program */
\r
4953 If we want to use qsort() with a table of pointers we have to be a bit
\r
4954 more clever than usual.
\r
4956 This example uses the colours again, but this time they are stored in
\r
4957 main memory and indexed by a table of pointers. Because we have a table
\r
4958 of pointers to sort there are two differences between this program's
\r
4959 qsort() and the previous one;
\r
4961 First we can't use strcmp() as the qsort() comparison function, secondly
\r
4962 the width of the table being sorted is sizeof(char *), that is the size
\r
4963 of a character pointer.
\r
4965 Notice the comparison function cmp() that receives two parameters, both
\r
4966 are pointers to a pointer. qsort() sends to this function the values held
\r
4967 in data[], which are in turn pointers to the data. So we need to use this
\r
4968 indirection to locate the data, otherwise we would be comparing the
\r
4969 addresses at which the data is held rather than the data itself!
\r
4971 #include <alloc.h>
\r
4972 #include <string.h>
\r
4974 /* Function prototype for comparison function */
\r
4975 int (cmp)(char **,char **);
\r
4977 int cmp(char **s1, char **s2)
\r
4979 /* comparison function using pointers to pointers */
\r
4980 return(strcmp(*s1,*s2));
\r
4988 for(n = 0; n < 10; n++)
\r
4989 data[n] = malloc(20);
\r
4991 strcpy(data[0],"RED");
\r
4992 strcpy(data[1],"BLUE");
\r
4993 strcpy(data[2],"GREEN");
\r
4994 strcpy(data[3],"YELLOW");
\r
4995 strcpy(data[4],"INDIGO");
\r
4996 strcpy(data[5],"BROWN");
\r
4997 strcpy(data[6],"BLACK");
\r
4998 strcpy(data[7],"ORANGE");
\r
4999 strcpy(data[8],"PINK");
\r
5000 strcpy(data[9],"CYAN");
\r
5002 /* The data table is comprised of pointers */
\r
5003 /* so the call to qsort() must reflect this */
\r
5004 qsort(data,10,sizeof(char *),cmp);
\r
5006 for(n = 0; n < 10; n++)
\r
5010 The quick sort is a fast sorting algorithm that works by subdividing the
\r
5011 data table into two sub-tables and then subdividing the sub-tables. As it
\r
5012 subdivides the table, so it compares the elements in the table and swaps
\r
5015 The following program implements the quick sort algorithm, which is
\r
5016 usually already used by qsort();
\r
5019 #include <string.h>
\r
5021 #define MAXELE 2000
\r
5023 char data[10][20];
\r
5028 /* Implementation of QUICKSORT algorithm */
\r
5037 static int sl[MAXELE][2];
\r
5039 /* sl[] is an index to the sub-table */
\r
5055 if (strcmp(data[i],data[j]) > 0)
\r
5057 strcpy(temp,data[i]);
\r
5058 strcpy(data[i],data[j]);
\r
5059 strcpy(data[j],temp);
\r
5091 /* Initialise arbitrary data */
\r
5092 strcpy(data[0],"RED");
\r
5093 strcpy(data[1],"BLUE");
\r
5094 strcpy(data[2],"GREEN");
\r
5095 strcpy(data[3],"YELLOW");
\r
5096 strcpy(data[4],"INDIGO");
\r
5097 strcpy(data[5],"BROWN");
\r
5098 strcpy(data[6],"BLACK");
\r
5099 strcpy(data[7],"ORANGE");
\r
5100 strcpy(data[8],"PINK");
\r
5101 strcpy(data[9],"CYAN");
\r
5103 /* Set last element indicator */
\r
5106 /* Call quick sort function */
\r
5109 /* Display sorted list */
\r
5110 for(n = 0; n < 10; n++)
\r
5115 A table sorted into ascending order can be searched with bsearch(), this
\r
5118 bsearch(key,base,num_elements,width,int (*cmp)(void *, void *));
\r
5120 bsearch() returns a pointer to the first element in the table that
\r
5121 matches the key, or zero if no match is found.
\r
5123 Or you can write your own binary search function thus;
\r
5125 int BSRCH(char *key, void *data, int numele, int width)
\r
5127 /* A binary search function returning one if found */
\r
5128 /* Zero if not found */
\r
5138 mp = (tp + bp) / 2;
\r
5140 /* Locate element mp in table by assigning pointer to start */
\r
5141 /* and incrementing it by width * mp */
\r
5145 while((result = strcmp(p,key)) != 0)
\r
5155 mp = (bp + tp) / 2;
\r
5165 char data[10][20];
\r
5167 /* Initialise some arbirary data */
\r
5169 strcpy(data[0],"RED");
\r
5170 strcpy(data[1],"BLUE");
\r
5171 strcpy(data[2],"GREEN");
\r
5172 strcpy(data[3],"YELLOW");
\r
5173 strcpy(data[4],"INDIGO");
\r
5174 strcpy(data[5],"BROWN");
\r
5175 strcpy(data[6],"BLACK");
\r
5176 strcpy(data[7],"ORANGE");
\r
5177 strcpy(data[8],"PINK");
\r
5178 strcpy(data[9],"CYAN");
\r
5180 /* Sort the data table */
\r
5181 qsort(data[0],10,20,strcmp);
\r
5183 result = BSRCH("CYAN",data[0],10,20);
\r
5185 printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");
\r
5188 There are other sorting algorithms as well. This program incorporates the
\r
5189 QUICK SORT, BUBBLE SORT, FAST BUBBLE SORT, INSERTION SORT and SHELL SORT
\r
5190 for comparing how each performs on a random 1000 item string list;
\r
5192 #include <stdio.h>
\r
5193 #include <stdlib.h>
\r
5194 #include <string.h>
\r
5196 char data[1000][4];
\r
5197 char save[1000][4];
\r
5201 void INITDATA(void);
\r
5202 void QKSORT(void);
\r
5204 void BUBBLE(void);
\r
5205 void FBUBBLE(void);
\r
5206 void INSERTION(void);
\r
5207 void MKDATA(void);
\r
5211 /* Implementation of QUICKSORT algorithm */
\r
5220 static int sl[1000][2];
\r
5236 if (strcmp(data[i],data[j]) > 0)
\r
5238 strcpy(temp,data[i]);
\r
5239 strcpy(data[i],data[j]);
\r
5240 strcpy(data[j],temp);
\r
5270 /* SHELL Sort Courtesy of K & R */
\r
5277 for(gap = lastone / 2; gap > 0; gap /= 2)
\r
5278 for(i = gap; i < lastone; i++)
\r
5279 for(j = i - gap; j >= 0 && strcmp(data[j] , data[j + gap])
\r
5283 strcpy(temp,data[j]);
\r
5284 strcpy(data[j] , data[j + gap]);
\r
5285 strcpy(data[j + gap] , temp);
\r
5295 for(a = lastone; a >= 0; a--)
\r
5297 for(b = 0; b < a; b++)
\r
5299 if(strcmp(data[b],data[b + 1]) > 0)
\r
5301 strcpy(temp,data[b]);
\r
5302 strcpy(data[b] , data[b + 1]);
\r
5303 strcpy(data[b + 1] , temp);
\r
5311 /* bubble sort with swap flag*/
\r
5320 for(a = lastone; a >= 0 && s == 1; a--)
\r
5323 for(b = 0; b < a; b++)
\r
5325 if(strcmp(data[b],data[b + 1]) > 0)
\r
5327 strcpy(temp,data[b]);
\r
5328 strcpy(data[b] , data[b + 1]);
\r
5329 strcpy(data[b + 1] , temp);
\r
5342 for(a = 0; a < lastone; a++)
\r
5345 strcpy(temp,data[a + 1]);
\r
5348 if (strcmp(temp,data[b]) < 0)
\r
5350 strcpy(data[b+1],data[b]);
\r
5356 strcpy(data[b+1],temp);
\r
5362 /* Initialise arbitrary data */
\r
5363 /* Uses random(), which is not ANSI C */
\r
5364 /* Returns a random number between 0 and n - 1 */
\r
5367 for(n = 0; n < 1000; n++)
\r
5368 sprintf(save[n],"%d",random(1000));
\r
5375 for(n = 0 ; n < 1000; n++)
\r
5376 strcpy(data[n],save[n]);
\r
5383 /* Initialise arbitrary data */
\r
5386 /* Set last element indicator */
\r
5389 /* Call quick sort function */
\r
5393 /* Initialise arbitrary data */
\r
5396 /* Set last element indicator */
\r
5399 /* Call shell sort function */
\r
5402 /* Initialise arbitrary data */
\r
5405 /* Set last element indicator */
\r
5408 /* Call bubble sort function */
\r
5411 /* Initialise arbitrary data */
\r
5414 /* Set last element indicator */
\r
5417 /* Call bubble sort with swap flag function */
\r
5420 /* Initialise arbitrary data */
\r
5423 /* Set last element indicator */
\r
5426 /* Call insertion sort function */
\r
5430 Here are the profiler results of the above test program run on 1000 and
\r
5431 5000 random items;
\r
5433 STRING SORT - 1000 RANDOM ITEMS
\r
5435 FBUBBLE 26.436 sec 41%
\r
5436 |********************************************
\r
5437 BUBBLE 26.315 sec 41%
\r
5438 |*******************************************
\r
5439 INSERTION 10.210 sec 15% |***************
\r
5440 SHELL 0.8050 sec 1% |*
\r
5441 QKSORT 0.3252 sec <1% |
\r
5443 STRING SORT - 5000 RANDOM ITEMS
\r
5445 FBUBBLE 563.70 sec 41%
\r
5446 |********************************************
\r
5447 BUBBLE 558.01 sec 41%
\r
5448 |********************************************
\r
5449 INSERTION 220.61 sec 16% |***************
\r
5450 SHELL 5.2531 sec <1% |
\r
5451 QKSORT 0.8379 sec <1% |
\r
5453 Here is the same test program amended for sorting tables of integers;
\r
5455 /* Integer sort test program */
\r
5457 #include <stdio.h>
\r
5458 #include <stdlib.h>
\r
5460 void INITDATA(void);
\r
5461 void QKSORT(void);
\r
5463 void BUBBLE(void);
\r
5464 void FBUBBLE(void);
\r
5465 void INSERTION(void);
\r
5466 void MKDATA(void);
\r
5475 /* Implementation of QUICKSORT algorithm */
\r
5484 static int sl[1000][2];
\r
5500 if (data[i] > data[j])
\r
5503 data[i] = data[j];
\r
5534 /* SHELL Sort Courtesy of K & R */
\r
5541 for(gap = lastone / 2; gap > 0; gap /= 2)
\r
5542 for(i = gap; i < lastone; i++)
\r
5543 for(j = i - gap; j >= 0 && data[j] > data[j + gap];
\r
5547 data[j] = data[j + gap];
\r
5548 data[j + gap] = temp;
\r
5558 for(a = lastone; a >= 0; a--)
\r
5560 for(b = 0; b < a; b++)
\r
5562 if(data[b] > data[b + 1])
\r
5565 data[b] = data[b + 1];
\r
5566 data[b + 1] = temp;
\r
5574 /* bubble sort with swap flag */
\r
5583 for(a = lastone; a >= 0 && s == 1; a--)
\r
5586 for(b = 0; b < lastone - a; b++)
\r
5588 if(data[b] > data[b + 1])
\r
5591 data[b] = data[b + 1];
\r
5592 data[b + 1] = temp;
\r
5605 for(a = 0; a < lastone; a++)
\r
5608 temp = data[a + 1];
\r
5611 if (temp < data[b])
\r
5613 data[b+1] = data[b];
\r
5627 for(n = 0; n < 1000; n++)
\r
5628 save[n] = random(1000);
\r
5635 for(n = 0; n < 1000; n++)
\r
5636 data[n] = save[n];
\r
5643 /* Create 1000 random elements */
\r
5646 /* Initialise arbitrary data */
\r
5649 /* Set last element indicator */
\r
5652 /* Call quick sort function */
\r
5655 /* Initialise arbitrary data */
\r
5658 /* Set last element indicator */
\r
5661 /* Call shell sort function */
\r
5664 /* Initialise arbitrary data */
\r
5667 /* Set last element indicator */
\r
5670 /* Call bubble sort function */
\r
5673 /* Initialise arbitrary data */
\r
5676 /* Set last element indicator */
\r
5679 /* Call bubble sort with swap flag function */
\r
5682 /* Initialise arbitrary data */
\r
5685 /* Set last element indicator */
\r
5688 /* Call insertion sort function */
\r
5692 And here are the profiler results for this program;
\r
5694 INTEGER SORTS - 1000 RANDOM NUMBERS (0 - 999)
\r
5696 FBUBBLE 3.7197 sec 41%
\r
5697 |********************************************
\r
5698 BUBBLE 3.5981 sec 39%
\r
5699 |******************************************
\r
5700 INSERTION 1.4258 sec 15% |***************
\r
5701 SHELL 0.1207 sec 1% |*
\r
5702 QKSORT 0.0081 sec <1% |
\r
5704 INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 999)
\r
5706 FBUBBLE 92.749 sec 42%
\r
5707 |********************************************
\r
5708 BUBBLE 89.731 sec 41%
\r
5709 |********************************************
\r
5710 INSERTION 35.201 sec 16% |***************
\r
5711 SHELL 0.9838 sec <1% |
\r
5712 QKSORT 0.0420 sec <1% |
\r
5714 INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 99)
\r
5716 FBUBBLE 92.594 sec 42% |*****************************************
\r
5717 BUBBLE 89.595 sec 40% |****************************************
\r
5718 INSERTION 35.026 sec 16% |***************
\r
5719 SHELL 0.7563 sec <1% |
\r
5720 QKSORT 0.6018 sec <1% |
\r
5722 INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 9)
\r
5724 FBUBBLE 89.003 sec 41%
\r
5725 |*******************************************
\r
5726 BUBBLE 86.921 sec 40%
\r
5727 |*******************************************
\r
5728 INSERTION 31.544 sec 14% |***************
\r
5729 QKSORT 6.0358 sec 2% |**
\r
5730 SHELL 0.5424 sec <1% |
\r
5732 INTEGER SORTS - 5000 DESCENDING ORDERED NUMBERS
\r
5734 FBUBBLE 122.99 sec 39%
\r
5735 |******************************************
\r
5736 BUBBLE 117.22 sec 37% |****************************************
\r
5737 INSERTION 70.595 sec 22% |**********************
\r
5738 SHELL 0.6438 sec <1% |
\r
5739 QKSORT 0.0741 sec <1% |
\r
5741 INTEGER SORTS - 5000 ORDERED NUMBERS
\r
5743 BUBBLE 62.908 sec 99%
\r
5744 |******************************************
\r
5745 SHELL 0.3971 sec <1% |
\r
5746 INSERTION 0.0510 sec <1% |
\r
5747 QKSORT 0.0382 sec <1% |
\r
5748 FBUBBLE 0.0251 sec <1% |
\r
5750 INTEGER SORTS - 10000 RANDOM NUMBERS (0 - 999)
\r
5752 FBUBBLE 371.18 sec 42% |****************************************
\r
5753 BUBBLE 359.06 sec 41% |***************************************
\r
5754 INSERTION 140.88 sec 16% |**************
\r
5755 SHELL 2.0423 sec <1% |
\r
5756 QKSORT 0.6183 sec <1% |
\r
5758 Theory has it that the performance of a sorting algorithm is dependant
\r
5761 a) the number of items to be sorted and
\r
5762 b) how unsorted the list is to start with.
\r
5764 With this in mind it is worth testing the various sorting routines
\r
5765 described here to determine which one will best suit your particular
\r
5766 application. If you examine the above profiler results you will see that:
\r
5768 1) With an already sorted list FBUBBLE() executes fastest
\r
5770 2) With a random list of small variations between the values SHELL()
\r
5773 3) With a random list of large variations between the values
\r
5775 executes the fastest
\r
5777 What the profiler does not highlight is that when the comparison aspect
\r
5778 of a sort function takes a disproportionately long time to execute in
\r
5779 relation to the rest of the sort function, then the bubble sort with a
\r
5780 swap flag will execute faster than the bubble sort with out a swap flag.
\r
5782 When considering a sort routine take into consideration memory
\r
5783 constraints and the type of data to be sorted as well as the relative
\r
5784 performances of the sort functions. Generally, the faster a sort
\r
5785 operates, the more memory it requires. Compare the simple bubble sort
\r
5786 with the quick sort, and you will see that the quick sort requires far
\r
5787 more memory than the bubble sort.
\r
5790 DYNAMIC MEMORY ALLOCATION
\r
5793 If a program needs a table of data, but the size of the table is
\r
5794 variable, perhaps for a list of all file names in the current directory,
\r
5795 it is inefficient to waste memory by declaring a data table of the
\r
5796 maximum possible size. Rather it is better to dynamically allocate the
\r
5797 table as required.
\r
5799 Turbo C allocates RAM as being available for dynamic allocation into an
\r
5800 area called the "heap". The size of the heap varies with memory model.
\r
5801 The tiny memory model defaults to occupy 64K of RAM. The small memory
\r
5802 model allocates upto 64K for the program/code and heap with a far heap
\r
5803 being available within the remainder of conventional memory. The other
\r
5804 memory models make all conventional memory available to the heap. This is
\r
5805 significant when programming in the tiny memory model when you want to
\r
5806 reduce the memory overhead of your program to a minimum. The way to do
\r
5807 this is to reduce the heap to a minimum size. The smallest is 1 byte.
\r
5809 C provides a function malloc() which allocates a block of free memory of
\r
5810 a specified size and returns a pointer to the start of the block; it also
\r
5811 provides free() which deallocates a block of memory previously allocated
\r
5812 by malloc(). Notice, however, that the IBM PC doesnot properly free
\r
5813 blocks of memory, and contiuous use of malloc() and free() will
\r
5814 fragmentise memory, eventually causing no memory to be available untill
\r
5815 the program terminates.
\r
5817 This program searches a specified file for a specified string (with case
\r
5818 sensitivity). It uses malloc() to allocate just enough memory for the
\r
5819 file to be read into memory.
\r
5821 #include <stdio.h>
\r
5822 #include <stdlib.h>
\r
5826 void main(int argc, char *argv[])
\r
5831 /* Check number of parameters */
\r
5834 fputs("Usage is sgrep <text> <file spec>",stderr);
\r
5838 /* Open stream fp to file */
\r
5839 fp = fopen(argv[2],"r");
\r
5842 perror("Unable to open source file");
\r
5846 /* Locate file end */
\r
5847 if(fseek(fp,0L,SEEK_END))
\r
5849 fputs("Unable to determine file length",stderr);
\r
5854 /* Determine file length */
\r
5857 /* Check for error */
\r
5860 fputs("Unable to determine file length",stderr);
\r
5865 /* Set file pointer to start of file */
\r
5868 /* Allocate memory buffer */
\r
5869 buffer = malloc(flen);
\r
5873 fputs("Unable to allocate memory",stderr);
\r
5878 /* Read file into buffer */
\r
5879 fread(buffer,flen,1,fp);
\r
5881 /* Check for read error */
\r
5884 fputs("Unable to read file",stderr);
\r
5886 /* Deallocate memory block */
\r
5893 printf("%s %s in %s",argv[1],(strstr(buffer,argv[1])) ? "was
\r
5894 found" : "was not found",argv[2]);
\r
5896 /* Deallocate memory block before exiting */
\r
5901 VARIABLE ARGUMENT LISTS
\r
5904 Some functions, such as printf(), accept a variable number and type of
\r
5905 arguments. C provides a mechanism to write your own functions which can
\r
5906 accept a variable argument list. This mechanism is the va_ family defined
\r
5907 in the header file `stdarg.h'.
\r
5909 There are three macros which allow implementation of variable argument
\r
5910 lists; va_start(), va_arg() and va_end() and a variable type va_list
\r
5911 which defines an array which holds the information required by the
\r
5914 va_start() takes two parameters, the first is the va_list variable and
\r
5915 the second is the last fixed parameter sent to the function. va_start()
\r
5916 must be called before attempting to access the variable argument list as
\r
5917 it sets up pointers required by the other macros.
\r
5919 va_arg() returns the next variable from the argument list. It is called
\r
5920 with two parameters, the first is the va_list variable and the second is
\r
5921 the type of the argument to be extracted. So, if the next variable in the
\r
5922 argument list is an integer, it can be extracted with;
\r
5925 <int> = va_arg(<va_list>,int);
\r
5927 va_end() is called after extracting all required variables from the
\r
5928 argument list, and simply tidies up the internal stack if appropriate.
\r
5929 va_end() accepts a single parameter, the va_list variable.
\r
5931 The following simple example program illustrates the basis for a printf()
\r
5932 type implementation where the types of the arguments is not known, but
\r
5933 can be determined from the fixed parameter. This example only caters for
\r
5934 integer, string and character types, but could easily by extended to
\r
5935 cater for other variable types as well by following the method
\r
5938 #include <stdarg.h>
\r
5940 char *ITOS(long x, char *ptr)
\r
5942 /* Convert a signed decimal integer to a string */
\r
5944 long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,
\r
5945 1000, 100, 10, 1 };
\r
5952 /* Convert x to absolute */
\r
5956 for(n = 0; n < 9; n++)
\r
5960 *ptr++ = 48 + x / pt[n];
\r
5967 void varfunc(char *format, ...)
\r
5970 char output[1000];
\r
5977 /* Initialise pointer to argument list */
\r
5978 va_start(arg_ptr, format);
\r
5980 /* loop format string */
\r
5985 /* locate next argument */
\r
5986 while(*format != '%')
\r
5988 *ptr++ = *format++;
\r
5991 /* A % has been located */
\r
5995 case '%' : *ptr++ = '%';
\r
5998 case 'd' : /* integer expression follows */
\r
5999 x = va_arg(arg_ptr,int);
\r
6000 ptr = ITOS(x,ptr);
\r
6003 bytes += strlen(output) - bytes;
\r
6006 case 's' : /* String expression follows */
\r
6007 y = va_arg(arg_ptr,char *);
\r
6015 case 'c' : /* Char expression follows */
\r
6016 z = va_arg(arg_ptr,char);
\r
6025 /* Clean stack just in case! */
\r
6028 /* Null terminate output string */
\r
6031 /* Display what we got */
\r
6032 printf("\nOUTPUT==%s",output);
\r
6037 varfunc("%d %s %c",5,"hello world",49);
\r
6041 A simpler variation is to use vsprintf() rather than implementing our own
\r
6042 variable argument list access. However, it is beneficial to understand
\r
6043 how variable argument lists behave. The following is a simplification of
\r
6044 the same program, but leaving the dirty work to the compiler;
\r
6046 #include <stdio.h>
\r
6047 #include <stdarg.h>
\r
6049 void varfunc(char *format, ...)
\r
6052 char output[1000];
\r
6054 va_start(arg_ptr, format);
\r
6056 vsprintf(output,format,arg_ptr);
\r
6060 /* Display what we got */
\r
6061 printf("\nOUTPUT==%s",output);
\r
6066 varfunc("%d %s %c",5,"hello world",49);
\r
6069 TRIGONOMETRY FUNCTIONS
\r
6071 The ANSI standard on C defines a number of trigonometry functions, all of
\r
6072 which accept an angle parameter in radians;
\r
6075 FUNCTION PROTOTYPE DESCRIPTION
\r
6076 acos double acos(double x) arc cosine of x
\r
6077 asin double asin(double x) arc sine of x
\r
6078 atan double atan(double x) arc tangent of x
\r
6079 atan2 double atan2(double x, double y) arc tangent of y/x
\r
6080 cos double cos(double x) cosine of x
\r
6081 cosh double cosh(double x) hyperbolic cosine of x
\r
6082 sin double sin(double x) sine of x
\r
6083 sinh double sinh(double x) hyperbolic sine of x
\r
6084 tan double tan(double x) tangent of x
\r
6085 tanh double tanh(double x) hyperbolic tangent of x
\r
6087 There are 2PI radians in a circle, therefore 1 radian is equal to 360/2PI
\r
6088 degrees or approximately 57 degrees in 1 radian. The calculation of any
\r
6089 of the above functions requires large floating point numbers to be used
\r
6090 which is a very slow process. If you are going to use calls to a trig'
\r
6091 function, it is a good idea to use a lookup table of values rather than
\r
6092 keep on calling the function. This approach is used in the discussion on
\r
6093 circle drawing later in this book.
\r
6099 When ever a program terminates, it should close any open files (this is
\r
6100 done for you by the C compiler's startup/termination code which it
\r
6101 surrounds your program with), and restore the host computer to some
\r
6102 semblance of order. Within a large program where exit may occur from a
\r
6103 number of locations it is a pain to have to keep on writing calls to the
\r
6104 cleanup routine. Fortunately we don't have to!
\r
6106 The ANSI standard on C describes a function, atexit(), which registers
\r
6107 the specified function, supplied as a parameter to atexit(), as a
\r
6108 function which is called immediately before terminating the program. This
\r
6109 function is called automatically, so the following program calls
\r
6110 `leave()' whether an error occurs or not;
\r
6112 #include <stdio.h>
\r
6116 puts("\nBye Bye!");
\r
6131 fp = fopen("data.txt","w");
\r
6135 perror("Unable to create file");
\r
6139 fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");
\r
6145 fputs("Error flushing stream",stderr);
\r
6152 fputs("Error rewind stream",stderr);
\r
6156 fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);
\r
6159 /* Unless you noticed the deliberate bug earlier */
\r
6160 /* The program terminates here */
\r
6161 fputs("Error reading from stream",stderr);
\r
6165 printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);
\r
6171 In order to reduce the time your program spends executing it is essential
\r
6172 to know your host computer. Most computers are very slow at displaying
\r
6173 information on the screen. And the IBM PC is no exception to this. C
\r
6174 offers various functions for displaying data, printf() being one of the
\r
6175 most commonly used and also the slowest. Whenever possible try to use
\r
6176 puts(varname) in place of printf("%s\n",varname). Remembering that puts()
\r
6177 appends a newline to the string sent to the screen.
\r
6179 When multiplying a variable by a constant which is a factor of 2 many C
\r
6180 compilers will recognise that a left shift is all that is required in the
\r
6181 assembler code to carry out the multiplication rapidly. When multiplying
\r
6182 by other values it is often faster to do a multiple addition instead, so;
\r
6185 'x * 3' becomes 'x + x + x'
\r
6187 Don't try this with variable multipliers in a loop because it becomes
\r
6188 very slow! But, where the multiplier is a constant it can be faster.
\r
6189 (Sometimes!) Another way to speed up multiplication and division is with
\r
6190 the shift commands, << and >>.
\r
6192 The instruction x /= 2 can equally well be written x >>= 1, shift the
\r
6193 bits of x right one place. Many compilers actually convert integer
\r
6194 divisions by 2 into a shift right instruction. You can use the shifts for
\r
6195 multiplying and dividing by 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 &c.
\r
6196 If you have difficulty understanding the shift commands consider the
\r
6197 binary form of a number;
\r
6200 01001101 equal to 77
\r
6202 shifted right one place it becomes;
\r
6204 00100110 equal to 38
\r
6206 Try to use integers rather than floating point numbers where ever
\r
6207 possible. Sometimes you can use integers where you didn't think you
\r
6208 could! For example, to convert a fraction to a decimal one would normally
\r
6211 percentage = x / y * 100
\r
6213 This requires floating point variables. However, it can also be written
\r
6217 percentage = z / y
\r
6219 Which works fine with integers, so long as you don't mind the percentage
\r
6220 being truncated. eg;
\r
6222 5 / 7 * 100 is equal to 71.43 with floating point
\r
6224 but with integers;
\r
6226 5 * 100 / 7 is equal to 71
\r
6228 (Assuming left to right expression evaluation. You may need to force the
\r
6229 multiplication to be done first as with `z = x * 100').
\r
6231 Here is a test program using this idea;
\r
6233 float funca(double x, double y)
\r
6235 return(x / y * 100);
\r
6238 int funcb(int x,int y)
\r
6240 return(x * 100 / y);
\r
6249 for(n = 0; n < 5000; n++)
\r
6256 And here is the results of the test program fed through a profiler;
\r
6258 funca 1.9169 sec 96%
\r
6259 |**********************************************
\r
6260 funcb 0.0753 sec 3% |*
\r
6262 You can clearly see that the floating point function is 25 times slower
\r
6263 than the integer equivalent!
\r
6265 NB: Although it is normal practice for expressions to be evaluated left
\r
6266 to right, the ANSI standard on C does not specify an order of preference
\r
6267 for expression evaluation, and as such you should check your compiler
\r
6270 Another way of increasing speed is to use pointers rather than array
\r
6271 indexing. When you access an array through an index, for example with;
\r
6275 the compiler has to calculate the offset of data[i] from the beginning of
\r
6276 the array. A slow process. Using pointers can often improve things as the
\r
6277 following two bubble sorts, one with array indexing and one with pointers
\r
6282 /* Bubble sort using array indexing */
\r
6288 for(a = lastone; a >= 0; a--)
\r
6290 for(b = 0; b < a; b++)
\r
6292 if(data[b] > data[b + 1])
\r
6295 data[b] = data[b + 1];
\r
6296 data[b + 1] = temp;
\r
6304 /* Bubble sort using pointers */
\r
6310 for(ptr = &data[lastone]; ptr >= data; ptr--)
\r
6312 for(ptr2 = data; ptr2 < ptr; ptr2++)
\r
6314 if(*ptr2 > *(ptr2 + 1))
\r
6317 *ptr2 = *(ptr2 + 1);
\r
6318 *(ptr2 + 1) = temp;
\r
6324 Here are the profiler results for the two versions of the same bubble
\r
6325 sort operating on the same 1000 item, randomly sorted list;
\r
6327 BUBBLE 3.1307 sec 59% |******************************************
\r
6328 PTRBUBBLE 2.1686 sec 40% |***************************
\r
6331 Here is another example of how to initialise an array using first the
\r
6332 common indexing approach, and secondly the pointer approach;
\r
6334 /* Index array initialisation */
\r
6337 for(n = 0; n < 1000; n++)
\r
6338 data[n] = random(1000);
\r
6341 /* Pointer array initialisation */
\r
6344 for(n = data; n < &data[1000]; n++)
\r
6345 *n = random(1000);
\r
6348 Needless to say, the pointer approach is faster than the index. The
\r
6349 pointer approach is only really of benefit when an array is going to be
\r
6350 traversed, as in the above examples. In the case of say a binary search
\r
6351 where a different and non-adjacent element is going to be tested each
\r
6352 pass then the pointer approach is no better than using array indexing.
\r
6354 The exception to this rule of using pointers rather than indexed access,
\r
6355 comes with pointer to pointers. Say your program has declared a table of
\r
6356 static data, such as:
\r
6358 static char *colours[] = { "Black", "Blue", "Green", "Yellow", "Red",
\r
6361 It is faster to access the table with colours[n] than it is with a
\r
6362 pointer, since each element in the table colours[], is a pointer. If you
\r
6363 need to scan a string table for a value you can use this very fast
\r
6366 First the table is changed into a single string, with some delimiter
\r
6367 between the elements.
\r
6369 static char *colours = "Black/Blue/Green/Yellow/Red/White";
\r
6371 Then to confirm that a value is held in the table you can use strstr();
\r
6373 result = strstr(colours,"Cyan");
\r
6375 Using in-line assembler code can provide the greatest speed increase.
\r
6376 Care must be taken however not to interfere with the compiled C code. It
\r
6377 is usually safe to write a complete function with in-line assembler code,
\r
6378 but mixing in-line assembler with C code can be hazardous. As a rule of
\r
6379 thumb, get your program working without assembler code, and then if you
\r
6380 want to use in-line assembler, convert small portions of the code at a
\r
6381 time, testing the program at each stage. Video I/O is a very slow process
\r
6382 with C, and usually benefits from in-line assembler, and we have used
\r
6383 this principle quite widely in the example programs which follow later.
\r
6388 When programming graphics you should bear in mind that they are a machine
\r
6389 dependant subject. Code to produce graphics on an IBM PC will not port to
\r
6390 an Amiga, or VAX or any other type of computer.
\r
6394 Introduction To PC Graphics
\r
6396 The IBM PC and compatible range of computers display information on a
\r
6397 visual display unit (VDU). To enable the computer to send information to
\r
6398 the VDU a component is included within the computer called a "display
\r
6399 card". There are various display cards and VDUs which have been produced
\r
6400 for the IBM PC range of computers; monochrome display adapter (MDA),
\r
6401 colour graphics adapter (CGA), Hercules graphics card (HGC), Enhanced
\r
6402 graphics adapter (EGA), video graphics array (VGA), super video graphics
\r
6403 array (SVGA), memory controller gate array (MCGA), 8514/A and the Txas
\r
6404 Instruments Graphics Architecture (TIGA). For simplicity, this section
\r
6405 will concern itself only with the three more common types of display;
\r
6409 Information about the VGA display is also relevant to the SVGA display
\r
6410 which in simple terms can do the same and more. This section will not
\r
6411 concern itself with monochrome displays since they are of limited use in
\r
6417 When an IBM PC computer is first switched on is set to display 80 columns
\r
6418 by 25 rows of writing. This measurement, 80 x 25 is called the
\r
6419 "resolution". A display mode which is intended for displaying writing is
\r
6420 called a "text" mode. Where as a display mode which is intended for
\r
6421 displaying pictures is called a "graphics" mode.
\r
6423 If you look closely at the display you will see that each displayed
\r
6424 character is comprised of dots. In reality the entire display is
\r
6425 comprised of dots, called "pixels", which may be set to different
\r
6426 colours. In text display modes these pixels are not very relevant,
\r
6427 however, in graphics display modes each pixel may be selected and set by
\r
6428 a program. The size of the pixels varies with the display resolution. In
\r
6429 80 x 25 text mode the pixels are half the width they are in 40 x 25 text
\r
6432 Depending upon the display card installed in the computer, there are a
\r
6433 number of display modes which may be used;
\r
6436 MODE TYPE RESOLUTION COLOURS
\r
6438 0 Text 40 x 25 4 (CGA), 16 (EGA,
\r
6441 1 Text 40 x 25 4 (CGA), 16 (EGA,
\r
6443 2 Text 80 x 25 4 (CGA), 16 (EGA,
\r
6446 3 Text 80 x 25 4 (CGA), 16 (EGA,
\r
6448 4 Graphics 320 x 200 4
\r
6449 5 Graphics 320 x 200 4 (grey on CGA
\r
6451 6 Graphics 640 x 200 2
\r
6452 7 Text 80 x 25 Mono (EGA, VGA)
\r
6453 13 Graphics 320 x 200 16 (EGA, VGA)
\r
6454 14 Graphics 640 x 200 16 (EGA, VGA)
\r
6455 15 Graphics 640 x 350 Mono (EGA, VGA)
\r
6456 16 Graphics 640 x 350 16 (EGA, VGA)
\r
6457 17 Graphics 640 x 480 2 (VGA)
\r
6458 18 Graphics 640 x 480 16 (VGA)
\r
6459 19 Graphics 320 x 200 256 (VGA)
\r
6461 The term resolution in graphics modes refers to the number of pixels
\r
6462 across and down the VDU. The larger the number of pixels, the smaller
\r
6463 each is and the sharper any displayed image appears. As you can see from
\r
6464 the table, the VGA display can produce a higher resolution than the other
\r
6465 display cards, resulting in sharper images being produced.
\r
6468 The CGA display card can produce a maximum resolution of 320 x 200
\r
6469 pixels, where as the VGA display card can produce a resolution of 640 x
\r
6470 480 pixels. This is why writing on a VGA VDU looks so much sharper than
\r
6471 the writing displayed on a CGA VDU.
\r
6475 Accessing The Display
\r
6477 Inside the IBM PC computer is a silicon chip called the BIOS ROM, this
\r
6478 chip contains functions which may be called by an external computer
\r
6479 program to access the display card, which in turn passes the information
\r
6480 on to the VDU. The BIOS display functions are all accessed by generating
\r
6481 interrupt 10 calls, with the number of the appropriate function stored in
\r
6482 the assembly language AH register.
\r
6484 A programmer interested in creating graphics displays must first be able
\r
6485 to switch the display card to an appropriate graphics mode. This is
\r
6486 achieved by calling the BIOS display function 0, with th number of the
\r
6487 desired display mode from the table stored in the assembly language AL
\r
6488 register thus the following assembly language code segment will switch
\r
6489 the display card to CGA graphics mode 4, assuming that is that the
\r
6490 display card is capable of this mode;
\r
6497 A C function for selecting video display modes can be written;
\r
6501 void setmode(unsigned char mode)
\r
6503 /* Sets the video display mode */
\r
6505 union REGS inregs outreg;
\r
6508 inreg.h.al = mode;
\r
6509 int86(0x10,&inreg,&outregs);
\r
6512 Any graphics are created by setting different pixels to different
\r
6513 colours, this is termed "plotting", and is achieved by calling BIOS
\r
6514 display function 12 with the pixel's horizontal coordinate in the
\r
6515 assembly language CX register and it's vertical coordinate in the
\r
6516 assembly language DX register and the required colour in the assembly
\r
6517 language AL register thus;
\r
6527 The corresponding C function is;
\r
6531 void plot(int x_coord, int y_coord, unsigned char colour)
\r
6533 /* Sets the colour of a pixel */
\r
6538 regs.h.al = colour;
\r
6540 regs.x.cx = x_coord;
\r
6541 regs.x.dx = y_coord;
\r
6542 int86(0x10,®s,®s);
\r
6545 The inverse function of plot is to read the existing colour setting of a
\r
6546 pixel. This is done by calling BIOS ROM display function 13, again with
\r
6547 the pixel's horizontal coordinate in the assembly language CX register
\r
6548 and it's vertical coordinate in the assembly language DX register. This
\r
6549 function then returns the pixel's colour in the assembly language AL
\r
6555 unsigned char get_pixel(int x_coord, int y_coord)
\r
6557 /* Reads the colour of a pixel */
\r
6559 union REGS inreg, outreg;
\r
6563 inreg.x.cx = x_coord;
\r
6564 inreg.x.dx = y_coord;
\r
6565 int86(0x10,&inreg,&outreg);
\r
6566 return(outreg.h.al);
\r
6570 Colour And The CGA
\r
6572 The CGA display card can display a maximum of 4 colours simultaneously at
\r
6573 any time. However, the display card can generate a total of 8 colours.
\r
6574 There are two sets of colours, called "palettes". The first palette
\r
6575 contains the colours;
\r
6577 background, cyan, magenta and white.
\r
6579 the second palette contains the colours;
\r
6581 background, green, red and yellow.
\r
6583 Colour 0 is always the same as the background colour.
\r
6585 The pixels displayed on the VDU take their colours from the currently
\r
6586 active palette, and are continuously being refreshed. So, if the active
\r
6587 palette changes, so to do the colours of the displayed pixels on the VDU.
\r
6589 Selection of the active CGA palette is achieved by calling the BIOS
\r
6590 display function 11 with the number of the desired palette (either 0 or
\r
6591 1) in the assembly language BH register;
\r
6598 The C function for selecting the CGA palette is;
\r
6600 void palette(unsigned char palette)
\r
6602 union REGS inreg, outreg;
\r
6605 inreg.h.bh = palette;
\r
6606 int86(0x10,&inreg,&outreg);
\r
6609 The background colour may be selected independantly from any of the eight
\r
6610 available colours by calling the same BIOS display function with a value
\r
6611 of 0 stored in the assembly language BH register and the desired
\r
6612 background colour in the assembly language BL register;
\r
6620 In C this function can be written;
\r
6622 void background(unsigned char colour)
\r
6624 union REGS inreg, outreg;
\r
6628 inreg.h.bl = colour;
\r
6629 int86(0x10,&inreg,&outreg);
\r
6632 The background colours available are;
\r
6644 Colour And The EGA
\r
6646 The EGA display card can display a maximum of 16 colours simultaneously
\r
6647 at any time. The colour of all pixels is continuously refreshed by the
\r
6648 display card by reading the colour from the EGA palette. Unlike the CGA
\r
6649 display card, the EGA display card allows you to redefine any or all of
\r
6650 the colours in the palette. Unfortunately only the first 8 colours may be
\r
6651 loaded into other palette colours.
\r
6672 Changing a palette colour is achieved by calling the BIOS display
\r
6673 function 16 with a value of 0 in the assembly language AL register, the
\r
6674 colour value (0 to 7) in the assembly language BH register and the number
\r
6675 of the palette colour (0 to 15) in the assembly language BL register
\r
6685 In C this function may be written;
\r
6687 void ega_palette(unsigned char colour, unsigned char palette)
\r
6689 union REGS inreg,outreg;
\r
6693 inreg.h.bl = palette;
\r
6694 inreg.h.bh = colour;
\r
6696 int86(0x10,&inreg,&outreg);
\r
6701 Colour And The VGA
\r
6703 The VGA display card can display a maximum of 256 colours on the VDU at
\r
6704 any one time, these colours are defined by information held in 256
\r
6705 special registers called "DAC" registers. As with the CGA and EGA
\r
6706 displays, the colour of displayed pixels is continuously being refreshed,
\r
6707 and as such any change to a DAC register is immediately visible. Each DAC
\r
6708 register has three component data parts which record the amount of green,
\r
6709 blue and red colours which make up the displayed colour. Each of these
\r
6710 seperate data components can hold a value between 0 and 63 giving the VGA
\r
6711 display card the ability to display 262,144 colours! Although only a
\r
6712 small subset of them can be displayed at any one time.
\r
6714 Setting the value of a DAC register is achieved by calling the BIOS
\r
6715 display function 16 with a value of 16 stored in the assembly language AL
\r
6716 register, the green value stored in the assembly language CH register,
\r
6717 the blue value stored in the assembly language CL register and the red
\r
6718 value stored in the assembly language DH register and the number of the
\r
6719 DAC register to be set stored in the assembly language BX register;
\r
6731 The C function to set a DAC register looks lik this;
\r
6733 void set_dac(int dac, unsigned char green, unsigned char blue,
\r
6734 unsigned char red)
\r
6741 regs.h.ch = green;
\r
6744 int86(0x10,®s,®s);
\r
6751 The BIOS ROM provides three functions for displaying a single character.
\r
6752 The first function to consider is the one used extensively by DOS for
\r
6753 displaying messages, this is function 14 called "write text in teletype
\r
6754 mode". This function interprets some control characters; bell (ascii 7),
\r
6755 backspace (ascii 8), carriage return (ascii 10) and line feed (ascii 13)
\r
6756 but all other ascii codes are displayed, and the current cursor position
\r
6757 updated accordingly, moving down a row when a character is displayed in
\r
6758 the far right column. To call this function the assembly language
\r
6759 register AL holds the ascii code of the character to be displayed and
\r
6760 assembly language register BL holds the foreground colour for the
\r
6761 character to be displayed in if a graphics mode is active;
\r
6771 A C function for accessing the write text in teletype mode may be written
\r
6776 void teletype(unsigned char character, unsigned char foreground)
\r
6778 union REGS inreg, outreg;
\r
6781 inreg.h.al = character;
\r
6783 inreg.h.bl = foreground;
\r
6784 int86(0x10,&inrg,&outreg);
\r
6787 The second BIOS ROM display function for displaying a character allows
\r
6788 the foreground and background colours of the displayed character to be
\r
6789 defined. It also allows multiple copies of the character to be displayed
\r
6790 one after another automatically displaying subsequent characters at the
\r
6791 next display position, although the current cursor position is not
\r
6792 changed by this function.
\r
6794 This function is called "write character and attribute", and is BIOS ROM
\r
6795 display function number 9. It is called with the ascii code of the
\r
6796 character to be displayed in the assembly language AL register, the
\r
6797 display page in assembly language register BH, the foreground colour in
\r
6798 the first four bits of the assembly language register BL and the
\r
6799 background colour in the last four bits of the assembly language register
\r
6800 BL, the number of times the character is to be displayed is stored in the
\r
6801 assembly language CX register thus;
\r
6807 mov bl,foreground + 16 * background
\r
6815 void char_attrib(unsigned char character, unsigned char foreground,
\r
6816 unsigned char background, int number)
\r
6818 union REGS inreg,outreg;
\r
6821 inreg.h.al = character;
\r
6823 inreg.h.bl = (background << 4) + foreground;
\r
6824 inreg.x.cx = number;
\r
6825 int86(0x10,&inreg,&outreg);
\r
6828 The last BIOS ROM display function for displaying a character retains the
\r
6829 foreground and background colours of the display position.
\r
6831 This function is called "write character", and is BIOS ROM display
\r
6832 function number 10. It is identical to BIOS ROM display function 9 except
\r
6833 that the colours of the displayed character are those which are prevalent
\r
6834 at the display position, except in graphics modes when the foreground
\r
6835 colour of the character is determined by the value in the assembly
\r
6836 language BL register. Its use is as follows;
\r
6842 mov bl,foreground ; For graphics modes ONLY
\r
6850 void char_attrib(unsigned char character, unsigned char foreground,
\r
6853 union REGS inreg,outreg;
\r
6856 inreg.h.al = character;
\r
6858 inreg.h.bl = foreground; /* For graphics modes ONLY */
\r
6859 inreg.x.cx = number;
\r
6860 int86(0x10,&inreg,&outreg);
\r
6864 Positioning of the text cursor is provided for by the ROM BIOS display
\r
6865 function number 2. It is called with the row number in the assembly
\r
6866 language register DH and the column number in the assembly language
\r
6875 The corresponding function in C looks like this;
\r
6879 void at(unsigned char row, unsigned char column)
\r
6886 regs.h.dl = column;
\r
6887 int86(0x10,®s,®s);
\r
6890 From these basic functions a more useful replacement for the C language's
\r
6891 "printf()" function can be written which allows data to be displayed at
\r
6892 the current cursor position, previously set by a call to "at()", with
\r
6893 prescribed attributes;
\r
6897 #include <stdarg.h>
\r
6899 void at(unsigned char row, unsigned char column)
\r
6906 regs.h.dl = column;
\r
6907 int86(0x10,®s,®s);
\r
6910 void xprintf(unsigned char foreground, unsigned char background,
\r
6913 union REGS inreg,outreg;
\r
6916 static char output[1000];
\r
6917 unsigned char col;
\r
6918 unsigned char row;
\r
6921 unsigned char text;
\r
6922 unsigned char attr;
\r
6924 /* Convert foreground and background colours into a single
\r
6926 attr = (background << 4) + foreground;
\r
6928 /* Copy data into a single string */
\r
6929 va_start(arg_ptr, format);
\r
6930 vsprintf(output, format, arg_ptr);
\r
6932 /* Determine number of display columns */
\r
6934 int86(0x10,&inreg,&outreg);
\r
6937 /* Determine current cursor position */
\r
6940 int86(0x10,&inreg,&outreg);
\r
6941 row = outreg.h.dh;
\r
6942 col = outreg.h.dl;
\r
6944 /* Now display data */
\r
6948 /* Display this character */
\r
6950 inreg.h.bl = attr;
\r
6953 inreg.h.al = output[p++];
\r
6954 int86(0x10,&inreg,&outreg);
\r
6956 /* Update cursor position */
\r
6957 /* moving down a row if required */
\r
6959 if (col < (n - 1))
\r
6969 This function, "xprintf()" illustrates two more functions of the BIOS
\r
6970 ROM. The first is the call to function 15 which returns the number of
\r
6971 text display columns for the currently active display mode.
\r
6973 The other function illustrated, but not yet discussed, is BIOS ROM
\r
6974 function 3 which returns information about the cursor. The cursor's row
\r
6975 is returned in the assembly language register DH, and it's column in the
\r
6976 assembly language register DL.
\r
6980 ADVANCED GRAPHICS ON THE IBM PC
\r
6982 This section aims to reveal more about the graphics facilities offered by
\r
6983 the IBM PC, in particular topics which are of a more complex nature than
\r
6984 those addressed in the previous section.
\r
6990 The information for display by the display card is stored in an area of
\r
6991 memory called the "video RAM". The size of the video RAM varies from one
\r
6992 display card to another, and the amount of video RAM required for a
\r
6993 display varies with the selected display mode. Display modes which do not
\r
6994 require all of the video RAM use the remaining video RAM for additional
\r
7001 2 4 (CGA) 8 (EGA, VGA)
\r
7002 3 4 (CGA) 8 (EGA, VGA)
\r
7015 Many of the BIOS ROM display functions allow selection of the display
\r
7016 page to be written to, regardless of which page is currently being
\r
7019 The display card continuously updates the VDU from the information in the
\r
7020 active display page. Changing the active display page instantaneously
\r
7021 changes the display.
\r
7023 This provides the graphics programmer with the means to build a display
\r
7024 on an undisplayed page, and to then change the active display page so
\r
7025 that the viewer does not see the display being drawn.
\r
7027 Selection of the active display page is achieved by calling BIOS ROM
\r
7028 display function 5 with the number of the required display page stored in
\r
7029 the assembly language register AL;
\r
7035 Or, from C this function becomes;
\r
7039 void set_page(unsigned char page)
\r
7041 union REGS inreg, outreg;
\r
7044 inreg.h.al = page;
\r
7045 int86(0x10,&inreg,&outreg);
\r
7048 The display page to which BIOS ROM display functions write is decided by
\r
7049 the value stored in the assembly language BH register. The functions for
\r
7050 setting a pixel's colour may be amended thus;
\r
7059 And the corresponding C function becomes;
\r
7063 void plot(int x_coord, int y_coord, unsigned char colour, unsigned
\r
7066 /* Sets the colour of a pixel */
\r
7068 union REGS inreg, outreg;
\r
7071 inreg.h.al = colour;
\r
7072 inreg.h.bh = page;
\r
7073 inreg.x.cx = x_coord;
\r
7074 inreg.x.dx = y_coord;
\r
7075 int86(0x10,&inreg,&outreg);
\r
7078 The currently active display page can be determined by calling BIOS ROM
\r
7079 display function 15. This function returns the active display page in the
\r
7080 assembly language register BH;
\r
7084 ; BH now holds active page number
\r
7087 Advanced Text Routines
\r
7089 When the IBM PC display is in a text mode a blinking cursor is displayed
\r
7090 at the current cursor position. This cursor is formed of a rectangle
\r
7091 which is one complete character width, but it's top and bottom pixel
\r
7092 lines are definable within the limits of the character height by calling
\r
7093 BIOS ROM display function 1. A CGA display has a character height of 8
\r
7094 pixel lines, an EGA display has a character height of 14 lines and a VGA
\r
7095 display has a character height of 16 lines.
\r
7097 BIOS ROM function 1 is called with the top pixel line number of the
\r
7098 desired cursor shape in assembly language register CH and the bottom
\r
7099 pixel line number in assembly language register CL;
\r
7107 A C function to set the cursor shape may be be written thus;
\r
7111 void setcursor(unsigned char top, unsigned char bottom)
\r
7113 union REGS inreg, outreg;
\r
7115 inreg.h.ch = start;
\r
7118 int86(0x10, &inreg, &outreg);
\r
7121 If the top pixel line is defined as larger than the bottom line, then the
\r
7122 cursor will appear as a pair of parallel rectangles.
\r
7124 The cursor may be removed from view by calling BIOS ROM display function
\r
7125 1 with a value of 32 in the assembly language CH register.
\r
7127 The current shape of the cursor can be determined by calling BIOS ROM
\r
7128 function 3, which returns the top pixel line number of the cursor shape
\r
7129 in the assembly language CH register, and the bottom line number in the
\r
7130 assembly language register CL.
\r
7132 Two functions are provided by the BIOS ROM for scrolling of the currently
\r
7133 active display page. These are function 6, which scrolls the display up
\r
7134 and function 7 which scrolls the display down.
\r
7136 Both functions accept the same parameters, these being the number of
\r
7137 lines to scroll in the assembly language register AL, the colour
\r
7138 attribute for the resulting blank line in the assembly language BH
\r
7139 register, the top row of the area to be scrolled in the assembly language
\r
7140 CH register, the left column of the area to be scrolled in the assembly
\r
7141 language CL register, the bottom row to be scrolled in the assembly
\r
7142 language DH register and the right most column to be scrolled in the
\r
7143 assembly language DL register.
\r
7145 If the number of lines being scrolled is greater than the number of lines
\r
7146 in the specified area, then the result is to clear the specified area,
\r
7147 filling it with spaces in the attribute specified in the assembly
\r
7148 language BH register.
\r
7153 A C function to scroll the entire screen down one line can be written
\r
7158 void scroll_down(unsigned char attr)
\r
7160 union REGS inreg, outreg;
\r
7166 inreg.h.dh = 24; /* Assuming a 25 line display */
\r
7167 inreg.h.bh = attr;
\r
7169 int86(0x10, &inreg, &outreg);
\r
7174 A simple clear screen function can be written in C based upon the
\r
7175 "scroll_down()" function simply by changing the value assigned to
\r
7180 void cls(unsigned char attr)
\r
7182 union REGS inreg, outreg;
\r
7188 inreg.h.dh = 24; /* Assuming a 25 line display */
\r
7189 inreg.h.bh = attr;
\r
7191 int86(0x10, &inreg, &outreg);
\r
7197 Windowing functions need to preserve the display they overwrite, and
\r
7198 restore it when the window is removed from display. The BIOS ROM provides
\r
7199 a display function which enables this to be done.
\r
7201 Function 8 requires the appropriate display page number to be stored in
\r
7202 assembly language register BH, and then when called it returns the ascii
\r
7203 code of the character at the current cursor position of that display page
\r
7204 in the assembly language AL register, and the display attribute of the
\r
7205 character in the assembly language AH register.
\r
7207 The following C functions allow an area of the display to be preserved,
\r
7208 and later restored;
\r
7212 void at(unsigned char row, unsigned char column, unsigned char page)
\r
7214 /* Position the cursor */
\r
7216 union REGS inreg,outreg;
\r
7219 inreg.h.bh = page;
\r
7221 inreg.h.dl = column;
\r
7222 int86(0x10,&inreg,&outreg);
\r
7225 void get_win(unsigned char left, unsigned char top, unsigned char
\r
7226 right,unsigned char bottom, unsigned char page, char *buffer)
\r
7228 /* Read a text window into a variable */
\r
7230 union REGS inreg,outreg;
\r
7232 unsigned char old_left;
\r
7233 unsigned char old_row;
\r
7234 unsigned char old_col;
\r
7236 /* save current cursor position */
\r
7238 inreg.h.bh = page;
\r
7239 int86(0x10,&inreg,&outreg);
\r
7240 old_row = outreg.h.dh;
\r
7241 old_col = outreg.h.dl;
\r
7243 while(top <= bottom)
\r
7246 while(left <= right)
\r
7248 at(top,left,page);
\r
7249 inreg.h.bh = page;
\r
7251 int86(0x10,&inreg,&outreg);
\r
7252 *buffer++ = outreg.h.al;
\r
7253 *buffer++ = outreg.h.ah;
\r
7261 /* Restore cursor to original location */
\r
7262 at(old_row,old_col,page);
\r
7265 void put_win(unsigned char left, unsigned char top, unsigned char
\r
7266 right, unsigned char bottom, unsigned char page, char
\r
7269 /* Display a text window from a variable */
\r
7271 union REGS inreg,outreg;
\r
7273 unsigned char old_left;
\r
7274 unsigned char chr;
\r
7275 unsigned char attr;
\r
7276 unsigned char old_row;
\r
7277 unsigned char old_col;
\r
7279 /* save current cursor position */
\r
7281 inreg.h.bh = page;
\r
7282 int86(0x10,&inreg,&outreg);
\r
7283 old_row = outreg.h.dh;
\r
7284 old_col = outreg.h.dl;
\r
7286 while(top <= bottom)
\r
7289 while(left <= right)
\r
7291 at(top,left,page);
\r
7294 inreg.h.bh = page;
\r
7297 inreg.h.bl = attr;
\r
7299 int86(0x10,&inreg,&outreg);
\r
7306 /* Restore cursor to original location */
\r
7307 at(old_row,old_col,page);
\r
7310 DIRECT VIDEO ACCESS WITH THE IBM PC
\r
7312 Accessing video RAM directly is much faster than using the BIOS ROM
\r
7313 display functions. There are problems however. Different video modes
\r
7314 arrange their use of video RAM in different ways so a number of functions
\r
7315 are required for plotting using direct video access, where as only one
\r
7316 function is required if use is made of the BIOS ROM display function.
\r
7318 The following C function will set a pixel in CGA display modes 4 and 5
\r
7322 void dplot4(int y, int x, int colour)
\r
7324 /* Direct plotting in modes 4 & 5 ONLY! */
\r
7338 char far *ptr = (char far *) 0xB8000000;
\r
7340 bit_mask.i = 0xFF3F;
\r
7342 if ( y < 0 || y > 319 || x < 0 || x > 199)
\r
7345 xor = colour & 128;
\r
7347 colour = colour & 127;
\r
7349 bit_position = y % 4;
\r
7351 colour <<= 2 * (3 - bit_position);
\r
7353 bit_mask.i >>= 2 * bit_position;
\r
7355 index = x * 40 + (y / 4);
\r
7365 t = *(ptr + index) & bit_mask.c[0];
\r
7366 *(ptr + index) = t | colour;
\r
7370 t = *(ptr + index) | (char)0;
\r
7371 *(ptr + index) = t ^ colour;
\r
7376 Direct plotting in VGA mode 19 is very much simpler;
\r
7378 void dplot19(int x, int y, unsigned char colour)
\r
7380 /* Direct plot in mode 19 ONLY */
\r
7383 video = MK_FP(0xA000,0);
\r
7384 video[x + y * 320] = colour;
\r
7387 ADVANCED GRAPHICS TECHNIQUES WITH THE IBM PC
\r
7392 Increasing Colours
\r
7394 The EGA display is limited displaying a maximum of 16 colours, however in
\r
7395 high resolution graphics modes (such as mode 16) the small physical size
\r
7396 of the pixels allows blending of adjacent colours to produce additional
\r
7399 If a line is drawn straight across a black background display in blue,
\r
7400 and then a subsequent line is drawn beneath it also in blue but only
\r
7401 plotting alternate pixels, the second line will appear in a darker shade
\r
7402 of the same colour.
\r
7404 The following C program illustrates this idea;
\r
7409 union REGS inreg, outreg;
\r
7411 void setvideo(unsigned char mode)
\r
7413 /* Sets the video display mode */
\r
7415 inreg.h.al = mode;
\r
7417 int86(0x10, &inreg, &outreg);
\r
7420 void plot(int x, int y, unsigned char colour)
\r
7422 /* Sets a pixel at the specified coordinates */
\r
7424 inreg.h.al = colour;
\r
7428 inreg.h.ah = 0x0C;
\r
7429 int86(0x10, &inreg, &outreg);
\r
7432 void line(int a, int b, int c, int d, unsigned char colour)
\r
7434 /* Draws a straight line from (a,b) to (c,d) */
\r
7491 for (i = 0; i <= m; i++)
\r
7509 void dot_line(int a, int b, int c, int d, int colour)
\r
7511 /* Draws a dotted straight line from (a,b) to (c,d) */
\r
7568 for (i = 0; i <= m; i+=2)
\r
7591 /* Display different colour bands */
\r
7595 for(n = 1; n < 16; n++)
\r
7597 line(0,n * 20,639,n * 20,n);
\r
7598 line(0,1 + n * 20,639,1 + n * 20,n);
\r
7599 line(0,2 + n * 20,639,2 + n * 20,n);
\r
7601 dot_line(0,4 + n * 20,639,4 + n * 20,n);
\r
7602 dot_line(1,5 + n * 20,639,5 + n * 20,n);
\r
7603 dot_line(0,6 + n * 20,639,6 + n * 20,n);
\r
7605 dot_line(0,8 + n * 20,639,8 + n * 20,n);
\r
7606 dot_line(1,9 + n * 20,639,9 + n * 20,n);
\r
7607 dot_line(0,10 + n * 20,639,10 + n * 20,n);
\r
7609 dot_line(1,8 + n * 20,639,8 + n * 20,7);
\r
7610 dot_line(0,9 + n * 20,639,9 + n * 20,7);
\r
7611 dot_line(1,10 + n * 20,639,10 + n * 20,7);
\r
7613 dot_line(1,12 + n * 20,639,12 + n * 20,n);
\r
7614 dot_line(0,13 + n * 20,639,13 + n * 20,n);
\r
7615 dot_line(1,14 + n * 20,639,14 + n * 20,n);
\r
7617 dot_line(0,12 + n * 20,639,12 + n * 20,14);
\r
7618 dot_line(1,13 + n * 20,639,13 + n * 20,14);
\r
7619 dot_line(0,14 + n * 20,639,14 + n * 20,14);
\r
7623 This technique can be put to good use for drawing three dimensional
\r
7626 void box3d(int xa,int ya, int xb, int yb, int col)
\r
7628 /* Draws a box for use in 3d histogram graphs etc */
\r
7635 xd = (xb - xa) / 2;
\r
7638 /* First draw the solid face */
\r
7639 for(n = xa; n < xb; n++)
\r
7640 line(n,ya,n,yb,col);
\r
7642 /* Now "shaded" top and side */
\r
7643 for(n = 0; n < xd; n++)
\r
7645 dotline(xa + n,yb - n ,xc + n,yb - n,col);
\r
7646 dotline(xa + xd + n,yb - n ,xc + xd + n,yb - n,col);
\r
7647 dotline(xb +n ,ya - n ,xb + n,yb - n,col);
\r
7653 Displaying Text At Pixel Coordinates
\r
7655 When using graphics display modes it is useful to be able to display text
\r
7656 not at the fixed character boundaries, but at pixel coordinates. This can
\r
7657 be achieved by implementing a print function which reads the character
\r
7658 definition data from the BIOS ROM and uses it to plot pixels to create
\r
7659 the shapes of the desired text. The following C function, "gr_print()"
\r
7660 illustrates this idea using the ROM CGA (8x8) character set;
\r
7662 void gr_print(char *output, int x, int y, unsigned char colour)
\r
7664 unsigned char far *ptr;
\r
7665 unsigned char chr;
\r
7666 unsigned char bmask;
\r
7673 /* The height of the characters in the font being accessed */
\r
7676 /* Set pointer to start of font definition in the ROM */
\r
7677 ptr = getfontptr(3);
\r
7682 /* Loop output string */
\r
7685 /* Get first character to be displayed */
\r
7688 /* Loop pixel lines in character definition */
\r
7689 for(i = 0; i < height; i++)
\r
7691 /* Get pixel line definition from the ROM */
\r
7692 bmask = *(ptr + (chr * height) + i);
\r
7694 /* Loop pixel columns */
\r
7695 for (k = 0; k < 8; ++k, bmask <<= 1)
\r
7697 /* Test for a set bit */
\r
7699 /* Plot a pixel if appropriate */
\r
7705 /* Down to next row and left to start of character */
\r
7709 /* Next character to be displayed */
\r
7712 /* Back to top row of the display position */
\r
7715 /* Right to next character display position */
\r
7720 The following assembly language support function is required to retrieve
\r
7721 the address of the ROM font by calling the BIOS ROM display function
\r
7722 which will return the address;
\r
7725 ; GET FONT POINTER
\r
7726 ; Small memory model
\r
7727 ; compile with tasm /mx
\r
7729 _TEXT segment byte public 'CODE'
\r
7730 assume cs:_TEXT,ds:NOTHING
\r
7732 _getfontptr proc near
\r
7736 mov bh, [bp+4] ; Number for font to be retrieved
\r
7746 public _getfontptr
\r
7749 The font number supplied to "getfontptr()" can be one of;
\r
7751 2 ROM EGA 8 x 14 font
\r
7752 3 ROM CGA 8 x 8 font
\r
7753 6 ROM VGA 8 x 16 font
\r
7755 A Graphics Function Library For Turbo C
\r
7757 /* Graphics library for 'Turbo C' (V2.01) */
\r
7759 /* (C)1992 Copyright Servile Software */
\r
7761 #include <stdio.h>
\r
7763 #include <stdlib.h>
\r
7764 #include <stdarg.h>
\r
7765 #include <string.h>
\r
7768 /* Global variables */
\r
7769 static unsigned char attribute;
\r
7774 /* Maximum coordinates for graphics screen */
\r
7778 /* Sprite structure */
\r
7786 typedef struct SP SPRITE;
\r
7789 /* Sine and cosine tables for trig' operations */
\r
7791 static double sintable[360] =
\r
7792 {0.000000000000000001,0.017452406437283512,
\r
7793 0.034899496702500969,0.052335956242943835,
\r
7794 0.069756473744125302,0.087155742747658166,
\r
7795 0.104528463267653457,0.121869343405147462,
\r
7796 0.139173100960065438,0.156434465040230869,
\r
7797 0.173648177666930331,0.190808995376544804,
\r
7798 0.207911690817759315,0.224951054343864976,
\r
7799 0.241921895599667702,0.258819045102520739,
\r
7800 0.275637355816999163,0.292371704722736769,
\r
7801 0.309016994374947451,0.325568154457156755,
\r
7802 0.342020143325668824,0.358367949545300379,
\r
7803 0.374606593415912181,0.390731128489273882,
\r
7804 0.406736643075800375,0.422618261740699608,
\r
7805 0.438371146789077626,0.453990499739547027,
\r
7806 0.469471562785891028,0.484809620246337225,
\r
7807 0.500000000000000222,0.515038074910054489,
\r
7808 0.529919264233205234,0.544639035015027417,
\r
7809 0.559192903470747127,0.573576436351046381,
\r
7810 0.587785252292473470,0.601815023152048600,
\r
7811 0.615661475325658625,0.629320391049837835,
\r
7812 0.642787609686539696,0.656059028990507720,
\r
7813 0.669130606358858682,0.681998360062498921,
\r
7814 0.694658370458997698,0.707106781186548017,
\r
7815 0.719339800338651636,0.731353701619170904,
\r
7816 0.743144825477394688,0.754709580222772458,
\r
7817 0.766044443118978569,0.777145961456971346,
\r
7818 0.788010753606722458,0.798635510047293384,
\r
7819 0.809016994374947895,0.819152044288992243,
\r
7820 0.829037572555042179,0.838670567945424494,
\r
7821 0.848048096156426512,0.857167300702112778,
\r
7822 0.866025403784439152,0.874619707139396296,
\r
7823 0.882947592858927432,0.891006524188368343,
\r
7824 0.898794046299167482,0.906307787036650381,
\r
7825 0.913545457642601311,0.920504853452440819,
\r
7826 0.927183854566787868,0.933580426497202187,
\r
7827 0.939692620785908761,0.945518575599317179,
\r
7828 0.951056516295153975,0.956304755963035880,
\r
7829 0.961261695938319227,0.965925826289068645,
\r
7830 0.970295726275996806,0.974370064785235579,
\r
7831 0.978147600733805911,0.981627183447664198,
\r
7832 0.984807753012208353,0.987688340595137992,
\r
7833 0.990268068741570473,0.992546151641322205,
\r
7834 0.994521895368273512,0.996194698091745656,
\r
7835 0.997564050259824309,0.998629534754573944,
\r
7836 0.999390827019095762,0.999847695156391270,
\r
7837 1.000000000000000000,0.999847695156391159,
\r
7838 0.999390827019095651,0.998629534754573833,
\r
7839 0.997564050259824087,0.996194698091745323,
\r
7840 0.994521895368273179,0.992546151641321761,
\r
7841 0.990268068741570029,0.987688340595137437,
\r
7842 0.984807753012207687,0.981627183447663532,
\r
7843 0.978147600733805245,0.974370064785234802,
\r
7844 0.970295726275996029,0.965925826289067757,
\r
7845 0.961261695938318228,0.956304755963034880,
\r
7846 0.951056516295152865,0.945518575599316069,
\r
7847 0.939692620785907651,0.933580426497200966,
\r
7848 0.927183854566786536,0.920504853452439486,
\r
7849 0.913545457642599978,0.906307787036649048,
\r
7850 0.898794046299166149,0.891006524188367122,
\r
7851 0.882947592858926211,0.874619707139395186,
\r
7852 0.866025403784438042,0.857167300702111778,
\r
7853 0.848048096156425624,0.838670567945423717,
\r
7854 0.829037572555041513,0.819152044288991688,
\r
7855 0.809016994374947451,0.798635510047293051,
\r
7856 0.788010753606722236,0.777145961456971346,
\r
7857 0.766044443118978569,0.754709580222772680,
\r
7858 0.743144825477395132,0.731353701619171459,
\r
7859 0.719339800338652302,0.707106781186548794,
\r
7860 0.694658370458998808,0.681998360062500142,
\r
7861 0.669130606358860014,0.656059028990509274,
\r
7862 0.642787609686541472,0.629320391049839833,
\r
7863 0.615661475325660845,0.601815023152051043,
\r
7864 0.587785252292476135,0.573576436351049268,
\r
7865 0.559192903470750236,0.544639035015030637,
\r
7866 0.529919264233208676,0.515038074910058152,
\r
7867 0.500000000000004219,0.484809620246341444,
\r
7868 0.469471562785895413,0.453990499739551634,
\r
7869 0.438371146789082455,0.422618261740704715,
\r
7870 0.406736643075805704,0.390731128489279489,
\r
7871 0.374606593415918010,0.358367949545306430,
\r
7872 0.342020143325675152,0.325568154457163306,
\r
7873 0.309016994374954279,0.292371704722743819,
\r
7874 0.275637355817006491,0.258819045102528289,
\r
7875 0.241921895599675502,0.224951054343872997,
\r
7876 0.207911690817767558,0.190808995376553270,
\r
7877 0.173648177666939019,0.156434465040239751,
\r
7878 0.139173100960074542,0.121869343405156802,
\r
7879 0.104528463267663005,0.087155742747667922,
\r
7880 0.069756473744135267,0.052335956242954007,
\r
7881 0.034899496702511350,0.017452406437294093,
\r
7882 0.000000000000010781,-0.017452406437272534,
\r
7883 -0.034899496702489805,-0.052335956242932476,
\r
7884 -0.069756473744113756,-0.087155742747646453,
\r
7885 -0.104528463267641564,-0.121869343405135402,
\r
7886 -0.139173100960053198,-0.156434465040218462,
\r
7887 -0.173648177666917786,-0.190808995376532092,
\r
7888 -0.207911690817746464,-0.224951054343851986,
\r
7889 -0.241921895599654574,-0.258819045102507472,
\r
7890 -0.275637355816985785,-0.292371704722723225,
\r
7891 -0.309016994374933796,-0.325568154457142933,
\r
7892 -0.342020143325654891,-0.358367949545286335,
\r
7893 -0.374606593415898026,-0.390731128489259616,
\r
7894 -0.406736643075785997,-0.422618261740685175,
\r
7895 -0.438371146789063082,-0.453990499739532427,
\r
7896 -0.469471562785876373,-0.484809620246322570,
\r
7897 -0.499999999999985512,-0.515038074910039723,
\r
7898 -0.529919264233190468,-0.544639035015012540,
\r
7899 -0.559192903470732361,-0.573576436351031616,
\r
7900 -0.587785252292458593,-0.601815023152033834,
\r
7901 -0.615661475325643859,-0.629320391049823069,
\r
7902 -0.642787609686525041,-0.656059028990493065,
\r
7903 -0.669130606358844027,-0.681998360062484377,
\r
7904 -0.694658370458983265,-0.707106781186533584,
\r
7905 -0.719339800338637314,-0.731353701619156804,
\r
7906 -0.743144825477380699,-0.754709580222758580,
\r
7907 -0.766044443118964691,-0.777145961456957690,
\r
7908 -0.788010753606709025,-0.798635510047280062,
\r
7909 -0.809016994374934795,-0.819152044288979364,
\r
7910 -0.829037572555029412,-0.838670567945412060,
\r
7911 -0.848048096156414188,-0.857167300702100676,
\r
7912 -0.866025403784427272,-0.874619707139384750,
\r
7913 -0.882947592858916108,-0.891006524188357352,
\r
7914 -0.898794046299156713,-0.906307787036639945,
\r
7915 -0.913545457642591208,-0.920504853452430938,
\r
7916 -0.927183854566778320,-0.933580426497192972,
\r
7917 -0.939692620785899990,-0.945518575599308742,
\r
7918 -0.951056516295145871,-0.956304755963028108,
\r
7919 -0.961261695938311900,-0.965925826289061651,
\r
7920 -0.970295726275990256,-0.974370064785229362,
\r
7921 -0.978147600733800138,-0.981627183447658869,
\r
7922 -0.984807753012203468,-0.987688340595133552,
\r
7923 -0.990268068741566587,-0.992546151641318764,
\r
7924 -0.994521895368270514,-0.996194698091743103,
\r
7925 -0.997564050259822310,-0.998629534754572390,
\r
7926 -0.999390827019094763,-0.999847695156390714,
\r
7927 -1.000000000000000000,-0.999847695156391714,
\r
7928 -0.999390827019096761,-0.998629534754575388,
\r
7929 -0.997564050259826307,-0.996194698091748099,
\r
7930 -0.994521895368276398,-0.992546151641325647,
\r
7931 -0.990268068741574470,-0.987688340595142433,
\r
7932 -0.984807753012213349,-0.981627183447669860,
\r
7933 -0.978147600733812128,-0.974370064785242240,
\r
7934 -0.970295726276004022,-0.965925826289076417,
\r
7935 -0.961261695938327665,-0.956304755963044872,
\r
7936 -0.951056516295163523,-0.945518575599327393,
\r
7937 -0.939692620785919530,-0.933580426497213511,
\r
7938 -0.927183854566799748,-0.920504853452453253,
\r
7939 -0.913545457642614411,-0.906307787036664148,
\r
7940 -0.898794046299181804,-0.891006524188383331,
\r
7941 -0.882947592858942976,-0.874619707139412506,
\r
7942 -0.866025403784455916,-0.857167300702130208,
\r
7943 -0.848048096156444498,-0.838670567945443146,
\r
7944 -0.829037572555061497,-0.819152044289012227,
\r
7945 -0.809016994374968434,-0.798635510047314479,
\r
7946 -0.788010753606744219,-0.777145961456993772,
\r
7947 -0.766044443119001550,-0.754709580222796106,
\r
7948 -0.743144825477418891,-0.731353701619195773,
\r
7949 -0.719339800338677060,-0.707106781186574107,
\r
7950 -0.694658370459024455,-0.681998360062526232,
\r
7951 -0.669130606358886548,-0.656059028990536253,
\r
7952 -0.642787609686568784,-0.629320391049867478,
\r
7953 -0.615661475325688934,-0.601815023152079465,
\r
7954 -0.587785252292504890,-0.573576436351078467,
\r
7955 -0.559192903470779767,-0.544639035015060502,
\r
7956 -0.529919264233238985,-0.515038074910088794,
\r
7957 -0.500000000000035083,-0.484809620246372641,
\r
7958 -0.469471562785926888,-0.453990499739583386,
\r
7959 -0.438371146789114541,-0.422618261740737022,
\r
7960 -0.406736643075838289,-0.390731128489312296,
\r
7961 -0.374606593415951039,-0.358367949545339737,
\r
7962 -0.342020143325708625,-0.325568154457197001,
\r
7963 -0.309016994374988196,-0.292371704722777903,
\r
7964 -0.275637355817040797,-0.258819045102562761,
\r
7965 -0.241921895599710085,-0.224951054343907719,
\r
7966 -0.207911690817802419,-0.190808995376588242,
\r
7967 -0.173648177666974129,-0.156434465040274973,
\r
7968 -0.139173100960109847,-0.121869343405192190,
\r
7969 -0.104528463267698463,-0.087155742747703435,
\r
7970 -0.069756473744170822,-0.052335956242989604,
\r
7971 -0.034899496702546981,-0.017452406437329739
\r
7974 static double costable[360] = {
\r
7975 1.000000000000000000,0.999847695156391270,
\r
7976 0.999390827019095762,0.998629534754573833,
\r
7977 0.997564050259824198,0.996194698091745545,
\r
7978 0.994521895368273290,0.992546151641321983,
\r
7979 0.990268068741570362,0.987688340595137770,
\r
7980 0.984807753012208020,0.981627183447663976,
\r
7981 0.978147600733805689,0.974370064785235246,
\r
7982 0.970295726275996473,0.965925826289068312,
\r
7983 0.961261695938318894,0.956304755963035436,
\r
7984 0.951056516295153531,0.945518575599316735,
\r
7985 0.939692620785908317,0.933580426497201743,
\r
7986 0.927183854566787313,0.920504853452440264,
\r
7987 0.913545457642600867,0.906307787036649826,
\r
7988 0.898794046299166927,0.891006524188367788,
\r
7989 0.882947592858926766,0.874619707139395630,
\r
7990 0.866025403784438486,0.857167300702112112,
\r
7991 0.848048096156425846,0.838670567945423828,
\r
7992 0.829037572555041513,0.819152044288991576,
\r
7993 0.809016994374947229,0.798635510047292607,
\r
7994 0.788010753606721681,0.777145961456970569,
\r
7995 0.766044443118977680,0.754709580222771681,
\r
7996 0.743144825477393911,0.731353701619170127,
\r
7997 0.719339800338650748,0.707106781186547129,
\r
7998 0.694658370458996810,0.681998360062498032,
\r
7999 0.669130606358857682,0.656059028990506721,
\r
8000 0.642787609686538697,0.629320391049836836,
\r
8001 0.615661475325657626,0.601815023152047601,
\r
8002 0.587785252292472471,0.573576436351045382,
\r
8003 0.559192903470746128,0.544639035015026307,
\r
8004 0.529919264233204124,0.515038074910053378,
\r
8005 0.499999999999999112,0.484809620246336115,
\r
8006 0.469471562785889862,0.453990499739545861,
\r
8007 0.438371146789076460,0.422618261740698442,
\r
8008 0.406736643075799154,0.390731128489272661,
\r
8009 0.374606593415910960,0.358367949545299158,
\r
8010 0.342020143325667547,0.325568154457155479,
\r
8011 0.309016994374946230,0.292371704722735493,
\r
8012 0.275637355816997887,0.258819045102519463,
\r
8013 0.241921895599666398,0.224951054343863643,
\r
8014 0.207911690817757955,0.190808995376543389,
\r
8015 0.173648177666928888,0.156434465040229398,
\r
8016 0.139173100960063939,0.121869343405145950,
\r
8017 0.104528463267651903,0.087155742747656584,
\r
8018 0.069756473744123679,0.052335956242942190,
\r
8019 0.034899496702499304,0.017452406437281822,
\r
8020 -0.000000000000001715,-0.017452406437285253,
\r
8021 -0.034899496702502732,-0.052335956242945618,
\r
8022 -0.069756473744127107,-0.087155742747659998,
\r
8023 -0.104528463267655317,-0.121869343405149350,
\r
8024 -0.139173100960067325,-0.156434465040232784,
\r
8025 -0.173648177666932274,-0.190808995376546747,
\r
8026 -0.207911690817761285,-0.224951054343866974,
\r
8027 -0.241921895599669701,-0.258819045102522793,
\r
8028 -0.275637355817001217,-0.292371704722738768,
\r
8029 -0.309016994374949450,-0.325568154457158698,
\r
8030 -0.342020143325670822,-0.358367949545302322,
\r
8031 -0.374606593415914124,-0.390731128489275825,
\r
8032 -0.406736643075802318,-0.422618261740701329,
\r
8033 -0.438371146789079125,-0.453990499739548303,
\r
8034 -0.469471562785892083,-0.484809620246338169,
\r
8035 -0.500000000000000999,-0.515038074910054933,
\r
8036 -0.529919264233205567,-0.544639035015027528,
\r
8037 -0.559192903470747127,-0.573576436351046159,
\r
8038 -0.587785252292473026,-0.601815023152048045,
\r
8039 -0.615661475325657848,-0.629320391049836947,
\r
8040 -0.642787609686538697,-0.656059028990506499,
\r
8041 -0.669130606358857238,-0.681998360062497477,
\r
8042 -0.694658370458996033,-0.707106781186546240,
\r
8043 -0.719339800338649749,-0.731353701619168906,
\r
8044 -0.743144825477392579,-0.754709580222770238,
\r
8045 -0.766044443118976237,-0.777145961456968903,
\r
8046 -0.788010753606719905,-0.798635510047290720,
\r
8047 -0.809016994374945231,-0.819152044288989578,
\r
8048 -0.829037572555039404,-0.838670567945421719,
\r
8049 -0.848048096156423625,-0.857167300702109891,
\r
8050 -0.866025403784436265,-0.874619707139393410,
\r
8051 -0.882947592858924435,-0.891006524188365345,
\r
8052 -0.898794046299164484,-0.906307787036647494,
\r
8053 -0.913545457642598424,-0.920504853452437932,
\r
8054 -0.927183854566784982,-0.933580426497199412,
\r
8055 -0.939692620785906096,-0.945518575599314515,
\r
8056 -0.951056516295151311,-0.956304755963033326,
\r
8057 -0.961261695938316785,-0.965925826289066314,
\r
8058 -0.970295726275994586,-0.974370064785233358,
\r
8059 -0.978147600733803912,-0.981627183447662310,
\r
8060 -0.984807753012206577,-0.987688340595136327,
\r
8061 -0.990268068741569030,-0.992546151641320873,
\r
8062 -0.994521895368272291,-0.996194698091744657,
\r
8063 -0.997564050259823532,-0.998629534754573389,
\r
8064 -0.999390827019095318,-0.999847695156391048,
\r
8065 -1.000000000000000000,-0.999847695156391381,
\r
8066 -0.999390827019096095,-0.998629534754574499,
\r
8067 -0.997564050259825086,-0.996194698091746544,
\r
8068 -0.994521895368274622,-0.992546151641323537,
\r
8069 -0.990268068741572027,-0.987688340595139658,
\r
8070 -0.984807753012210241,-0.981627183447666418,
\r
8071 -0.978147600733808353,-0.974370064785238244,
\r
8072 -0.970295726275999804,-0.965925826289071865,
\r
8073 -0.961261695938322669,-0.956304755963039654,
\r
8074 -0.951056516295157972,-0.945518575599321509,
\r
8075 -0.939692620785913424,-0.933580426497207072,
\r
8076 -0.927183854566793086,-0.920504853452446370,
\r
8077 -0.913545457642607195,-0.906307787036656598,
\r
8078 -0.898794046299173921,-0.891006524188375226,
\r
8079 -0.882947592858934649,-0.874619707139403846,
\r
8080 -0.866025403784447034,-0.857167300702120993,
\r
8081 -0.848048096156435061,-0.838670567945433487,
\r
8082 -0.829037572555051505,-0.819152044289001902,
\r
8083 -0.809016994374957998,-0.798635510047303709,
\r
8084 -0.788010753606733227,-0.777145961456982559,
\r
8085 -0.766044443118990004,-0.754709580222784449,
\r
8086 -0.743144825477407012,-0.731353701619183672,
\r
8087 -0.719339800338664737,-0.707106781186561450,
\r
8088 -0.694658370459011576,-0.681998360062513242,
\r
8089 -0.669130606358873337,-0.656059028990522708,
\r
8090 -0.642787609686555128,-0.629320391049853711,
\r
8091 -0.615661475325674945,-0.601815023152065254,
\r
8092 -0.587785252292490457,-0.573576436351063812,
\r
8093 -0.559192903470765002,-0.544639035015045625,
\r
8094 -0.529919264233223886,-0.515038074910073473,
\r
8095 -0.500000000000019651,-0.484809620246357043,
\r
8096 -0.469471562785911123,-0.453990499739567510,
\r
8097 -0.438371146789098498,-0.422618261740720869,
\r
8098 -0.406736643075822024,-0.390731128489295920,
\r
8099 -0.374606593415934497,-0.358367949545323083,
\r
8100 -0.342020143325691917,-0.325568154457180181,
\r
8101 -0.309016994374971266,-0.292371704722760861,
\r
8102 -0.275637355817023644,-0.258819045102545497,
\r
8103 -0.241921895599692793,-0.224951054343890372,
\r
8104 -0.207911690817784989,-0.190808995376570756,
\r
8105 -0.173648177666956560,-0.156434465040257376,
\r
8106 -0.139173100960092194,-0.121869343405174496,
\r
8107 -0.104528463267680741,-0.087155742747685686,
\r
8108 -0.069756473744153044,-0.052335956242971805,
\r
8109 -0.034899496702529162,-0.017452406437311916,
\r
8110 -0.000000000000028605,0.017452406437254715,
\r
8111 0.034899496702471985,0.052335956242914670,
\r
8112 0.069756473744095979,0.087155742747628689,
\r
8113 0.104528463267623842,0.121869343405117708,
\r
8114 0.139173100960035545,0.156434465040200865,
\r
8115 0.173648177666900216,0.190808995376514606,
\r
8116 0.207911690817729033,0.224951054343834611,
\r
8117 0.241921895599637282,0.258819045102490264,
\r
8118 0.275637355816968632,0.292371704722706127,
\r
8119 0.309016994374916809,0.325568154457126058,
\r
8120 0.342020143325638126,0.358367949545269682,
\r
8121 0.374606593415881484,0.390731128489243240,
\r
8122 0.406736643075769733,0.422618261740669021,
\r
8123 0.438371146789047095,0.453990499739516551,
\r
8124 0.469471562785860608,0.484809620246306972,
\r
8125 0.499999999999970080,0.515038074910024402,
\r
8126 0.529919264233175369,0.544639035014997663,
\r
8127 0.559192903470717484,0.573576436351016961,
\r
8128 0.587785252292444271,0.601815023152019624,
\r
8129 0.615661475325629759,0.629320391049809191,
\r
8130 0.642787609686511385,0.656059028990479520,
\r
8131 0.669130606358830815,0.681998360062471387,
\r
8132 0.694658370458970387,0.707106781186521038,
\r
8133 0.719339800338624991,0.731353701619144592,
\r
8134 0.743144825477368709,0.754709580222746812,
\r
8135 0.766044443118953255,0.777145961456946477,
\r
8136 0.788010753606698033,0.798635510047269292,
\r
8137 0.809016994374924359,0.819152044288969150,
\r
8138 0.829037572555019531,0.838670567945402290,
\r
8139 0.848048096156404752,0.857167300702091572,
\r
8140 0.866025403784418391,0.874619707139376090,
\r
8141 0.882947592858907782,0.891006524188349247,
\r
8142 0.898794046299148941,0.906307787036632395,
\r
8143 0.913545457642583991,0.920504853452423943,
\r
8144 0.927183854566771659,0.933580426497186644,
\r
8145 0.939692620785893884,0.945518575599302968,
\r
8146 0.951056516295140320,0.956304755963022890,
\r
8147 0.961261695938306904,0.965925826289056988,
\r
8148 0.970295726275985926,0.974370064785225365,
\r
8149 0.978147600733796474,0.981627183447655538,
\r
8150 0.984807753012200360,0.987688340595130776,
\r
8151 0.990268068741564034,0.992546151641316543,
\r
8152 0.994521895368268627,0.996194698091741548,
\r
8153 0.997564050259821089,0.998629534754571502,
\r
8154 0.999390827019094097,0.999847695156390381
\r
8156 int dtoi(double x)
\r
8158 /* Rounds a floating point number to an integer */
\r
8163 if (y < (x + 0.5))
\r
8168 int getmode(int *ncols)
\r
8170 /* Returns current video mode and number of columns in ncols */
\r
8172 union REGS inreg,outreg;
\r
8174 inreg.h.ah = 0x0F;
\r
8175 int86(0x10, &inreg, &outreg);
\r
8176 *ncols = outreg.h.ah;
\r
8177 return(outreg.h.al);
\r
8180 void setvideo(unsigned char mode)
\r
8182 /* Sets the video display mode and clears the screen */
\r
8183 /* (modes above 127 on VGA monitors do not clear the screen) */
\r
8185 asm mov ax , mode;
\r
8188 /* Set global variables */
\r
8199 case 5: maxx = 320;
\r
8204 case 6: maxx = 640;
\r
8207 case 7: maxx = 720;
\r
8210 case 8: maxx = 160;
\r
8214 case 16: maxx = 640;
\r
8218 case 18: maxx = 640;
\r
8226 void getcursor(int *row, int *col)
\r
8228 /* Returns the cursor position and size for the currently
\r
8230 video display page */
\r
8232 union REGS inreg,outreg;
\r
8235 inreg.h.ah = 0x03;
\r
8236 int86(0x10, &inreg, &outreg);
\r
8237 *row = outreg.h.dh;
\r
8238 *col = outreg.h.dl;
\r
8241 void setcursor(char status)
\r
8243 /* Set cursor size, if status is 0x20 the cursor is not
\r
8252 void at(int row, int col)
\r
8254 /* Position text cursor on current screen */
\r
8263 void clsc(unsigned char attrib)
\r
8265 /* Clear display and fill with new attribute */
\r
8267 asm mov ax , 073dh;
\r
8268 asm mov bh , attrib;
\r
8270 asm mov dx , 3c4fh;
\r
8273 /* Now move text cursor to origin (0,0) */
\r
8280 void clrwindow(int x1, int y1,int x2, int y2, unsigned char attrib)
\r
8282 /* Clear a text window */
\r
8284 union REGS inreg,outreg;
\r
8286 inreg.h.al = (unsigned char)(y2 - y1 + 1);
\r
8287 inreg.h.bh = attrib;
\r
8288 inreg.h.cl = (unsigned char)x1;
\r
8289 inreg.h.ch = (unsigned char)y1;
\r
8290 inreg.h.dl = (unsigned char)x2;
\r
8291 inreg.h.dh = (unsigned char)y2;
\r
8292 inreg.h.ah = 0x06;
\r
8293 int86(0x10, &inreg, &outreg);
\r
8296 void scrollsc(void)
\r
8298 /* Scroll the text screen */
\r
8303 asm mov bh,attribute;
\r
8308 void winscr(unsigned char left,unsigned char top, unsigned char
\r
8309 right,unsigned char bottom,
\r
8310 unsigned char direction, unsigned char numlines)
\r
8312 /* Scroll a text window */
\r
8314 asm mov al , numlines;
\r
8315 asm mov cl , left;
\r
8317 asm mov dl , right;
\r
8318 asm mov dh , bottom;
\r
8319 asm mov bh , attribute;
\r
8320 asm mov ah , direction;
\r
8324 unsigned char attr(int foregrnd, int backgrnd)
\r
8326 /* Convert a colour pair into a single attribute */
\r
8328 return((char)((backgrnd << 4) + foregrnd));
\r
8331 void shadewin(unsigned char left, unsigned char top, unsigned char
\r
8332 right, unsigned char bottom)
\r
8334 /* Shade a text window */
\r
8341 /* Preserve existing coords */
\r
8342 getcursor(&oldrow,&oldcol);
\r
8345 for (row = top + 1; row <= bottom + 1; row++)
\r
8347 /* Move to row,col */
\r
8353 /* Get character */
\r
8359 /* Write in attribute 7 */
\r
8366 for (col = left + 1; col <= right + 1; col++)
\r
8368 /* Move to row,col */
\r
8370 asm mov dh , bottom;
\r
8375 /* Get character */
\r
8381 /* Write in attribute 7 */
\r
8388 /* Return to original position */
\r
8389 /* Move to row,col */
\r
8391 asm mov dh , oldrow;
\r
8392 asm mov dl , oldcol;
\r
8397 void bprintf(char *format, ...)
\r
8399 /* print text to graphics screen correctly */
\r
8402 char output[1000];
\r
8403 int c, r, n, p = 0;
\r
8404 unsigned char text;
\r
8405 unsigned char page;
\r
8407 va_start(arg_ptr, format);
\r
8408 vsprintf(output, format, arg_ptr);
\r
8410 if (strcmp(output,"\r\n") == 0)
\r
8411 fputs(output,stdout);
\r
8424 text = output[p++];
\r
8425 asm mov bh , page;
\r
8426 asm mov bl , attribute;
\r
8429 asm mov al , text;
\r
8444 void wrtstr(char *output)
\r
8446 /* TTY text output. The original screen attributes remain
\r
8450 unsigned char page;
\r
8451 unsigned char text;
\r
8459 text = output[p++];
\r
8460 asm mov bh , page;
\r
8462 asm mov al , text;
\r
8467 void getwin(int left, int top, int right, int bottom, char *buffer)
\r
8469 /* Read a text window into a variable */
\r
8472 unsigned char page;
\r
8478 while(top <= bottom)
\r
8481 while(left <= right)
\r
8484 asm mov bh , page;
\r
8496 void putwin(int left, int top, int right, int bottom,char *buffer)
\r
8498 /* Display a text window from a variable */
\r
8501 unsigned char chr;
\r
8502 unsigned char attr;
\r
8503 unsigned char page;
\r
8509 while(top <= bottom)
\r
8512 while(left <= right)
\r
8517 asm mov bh , page;
\r
8530 void setpalette(unsigned char palno)
\r
8532 /* Sets the video palette */
\r
8540 void setborder(unsigned char x)
\r
8542 /* Set border colour */
\r
8545 asm mov ax ,1001h;
\r
8549 void setlines(unsigned char x)
\r
8551 /* Set text display number of lines */
\r
8559 void graphbackground(unsigned char colour)
\r
8561 /* Selects the background colour for a graphics mode */
\r
8564 asm mov bl,colour;
\r
8569 void plot(int x, int y, unsigned char colour)
\r
8571 /* Sets a pixel at the specified coordinates to the specified
\r
8573 The variables _lastx and _lasty are updated. */
\r
8575 unsigned char far *video;
\r
8582 video = MK_FP(0xA000,0);
\r
8583 video[x+y*320] = colour;
\r
8587 asm mov al , colour;
\r
8596 int pixset(int x, int y)
\r
8598 /* Returns the colour of the specified pixel */
\r
8607 void move(int x, int y)
\r
8609 /* Sets the value of the variables _lastx and _lasty */
\r
8615 void line(int a, int b, int c, int d, int col)
\r
8617 /* Draws a straight line from (a,b) to (c,d) in colour col */
\r
8699 for (i = 0; i <= m; i++)
\r
8725 void ellipse(int x, int y, int xrad, int yrad,double incline,int
\r
8728 /* Draws an ellipse */
\r
8738 incline = 1 / sintable[(int)incline];
\r
8740 if (getmode(&cols) == 6)
\r
8745 /* Compensate for pixel shape */
\r
8748 b = y + sintable[0] * yrad + xrad/incline / div;
\r
8750 for(f = 5; f < 360; f += 5)
\r
8752 c = x + costable[f] * xrad;
\r
8753 d = y + sintable[f] * yrad + (costable[f] *
\r
8754 xrad)/incline/div;
\r
8756 line(a,b,c,d,col);
\r
8762 line(a,b,x + xrad,y + sintable[0] * yrad + xrad/incline /
\r
8766 void polygon(int x, int y, int rad, int col, int sides, int start)
\r
8768 /* Draws a regular polygon */
\r
8781 step = 360 / sides;
\r
8783 if (getmode(&cols) == 6)
\r
8787 aa = a = x + costable[start] * rad;
\r
8788 bb = b = y + sintable[start] * rad / div;
\r
8790 for(f = start + step; f < start + 360; f += step)
\r
8792 c = x + costable[(int)f] * rad;
\r
8793 d = y + sintable[(int)f] * rad / div;
\r
8794 line(a,b,c,d,col);
\r
8798 line(a,b,aa,bb,col);
\r
8801 void arc(int x, int y, int rad, int start, int end,int col)
\r
8813 if (getmode(&cols) == 6)
\r
8817 a = x + costable[start] * rad;
\r
8818 b = y + sintable[start] * rad / div;
\r
8820 for(f = start; f <= end; f ++)
\r
8822 c = x + costable[f] * rad;
\r
8823 d = y + sintable[f] * rad / div;
\r
8824 line(a,b,c,d,col);
\r
8830 void segm(int x, int y, int rad, int start, int end,int col)
\r
8832 /* Draw a segment of a circle */
\r
8842 if (getmode(&cols) == 6)
\r
8846 a = x + costable[start] * rad;
\r
8847 b = y + sintable[start] * rad / div;
\r
8849 line(x,y,a,b,col);
\r
8851 for(f = start; f <= end ; f ++)
\r
8853 c = x + costable[f] * rad;
\r
8854 d = y + sintable[f] * rad / div;
\r
8855 line(a,b,c,d,col);
\r
8859 line(x,y,a,b,col);
\r
8862 void box(int xa,int ya, int xb, int yb, int col)
\r
8864 /* Draws a box for use in 2d histogram graphs etc */
\r
8866 line(xa,ya,xa,yb,col);
\r
8867 line(xa,yb,xb,yb,col);
\r
8868 line(xb,yb,xb,ya,col);
\r
8869 line(xb,ya,xa,ya,col);
\r
8872 void tri(int xa,int ya, int xb, int yb, int xc, int yc,int col)
\r
8874 /* Draw a triangle */
\r
8876 line(xa,ya,xb,yb,col);
\r
8877 line(xb,yb,xc,yc,col);
\r
8878 line(xc,yc,xa,ya,col);
\r
8881 void fill(int x, int y, int col,int pattern)
\r
8883 /* Fill a boundered shape using a hatch pattern */
\r
8890 int hatch[10][8] = { 255,255,255,255,255,255,255,255,
\r
8891 128,64,32,16,8,4,2,1,
\r
8892 1,2,4,8,16,32,64,128,
\r
8894 238,238,238,238,238,238,238,238,
\r
8895 170,85,170,85,170,85,170,85,
\r
8896 192,96,48,24,12,6,3,1,
\r
8897 62,62,62,0,227,227,227,0,
\r
8898 129,66,36,24,24,36,66,129,
\r
8899 146,36,146,36,146,36,146,36
\r
8902 /* Patterns for fill, each integer describes a row of dots */
\r
8905 ya = y; /* Save Origin */
\r
8915 if (hatch[pattern][byn] != 0)
\r
8917 /* If blank ignore */
\r
8920 if ((bn & hatch[pattern][byn]) == bn)
\r
8934 while(!pixset(x,y) && (x > -1));
\r
8941 if ((bn & hatch[pattern][byn]) == bn)
\r
8955 while((!pixset(x,y)) && (x <= maxx));
\r
8965 while(!pixset(x,y) && ( y > -1));
\r
8967 /* Now travel downwards */
\r
8976 if (hatch[pattern][byn] !=0)
\r
8980 if ( (bn & hatch[pattern][byn]) == bn)
\r
8995 while(!pixset(x,y) && (x > -1));
\r
8997 /* Back to x origin */
\r
9001 /* Travel right */
\r
9004 if ((bn & hatch[pattern][byn]) == bn)
\r
9018 while((!pixset(x,y)) && (x <= maxx));
\r
9027 while((!pixset(x,y)) && (y <= maxy));
\r
9030 void invert(int xa,int ya, int xb, int yb, int col)
\r
9032 /* Invert a pixel window */
\r
9034 union REGS inreg,outreg;
\r
9036 inreg.h.al = (unsigned char)(128 | col);
\r
9037 inreg.h.ah = 0x0C;
\r
9038 for(inreg.x.cx = (unsigned int)xa; inreg.x.cx <= (unsigned
\r
9039 int)xb; inreg.x.cx++)
\r
9040 for(inreg.x.dx = (unsigned)ya; inreg.x.dx <= (unsigned)yb;
\r
9042 int86(0x10, &inreg, &outreg);
\r
9045 void circle(int x_centre , int y_centre, int radius, int colour)
\r
9048 int startx,endx,x1,starty,endy,y1;
\r
9057 delta = 3 - 2 * radius;
\r
9058 for(x = 0; x < y; )
\r
9060 starty = y * asp_ratio / 10;
\r
9061 endy = (y + 1) * asp_ratio / 10;
\r
9062 startx = x * asp_ratio / 10;
\r
9063 endx = (x + 1) * asp_ratio / 10;
\r
9064 for(x1 = startx; x1 < endx; ++x1)
\r
9066 plot(x1+x_centre,y+y_centre,colour);
\r
9067 plot(x1+x_centre,y_centre - y,colour);
\r
9068 plot(x_centre - x1,y_centre - y,colour);
\r
9069 plot(x_centre - x1,y + y_centre,colour);
\r
9072 for(y1 = starty; y1 < endy; ++y1)
\r
9074 plot(y1+x_centre,x+y_centre,colour);
\r
9075 plot(y1+x_centre,y_centre - x,colour);
\r
9076 plot(x_centre - y1,y_centre - x,colour);
\r
9077 plot(x_centre - y1,x + y_centre,colour);
\r
9081 delta += 4 * x + 6;
\r
9084 delta += 4*(x-y)+10;
\r
9092 starty = y * asp_ratio / 10;
\r
9093 endy = (y + 1) * asp_ratio / 10;
\r
9094 startx = x * asp_ratio / 10;
\r
9095 endx = (x + 1) * asp_ratio / 10;
\r
9096 for(x1 = startx; x1 < endx; ++x1)
\r
9098 plot(x1+x_centre,y+y_centre,colour);
\r
9099 plot(x1+x_centre,y_centre - y,colour);
\r
9100 plot(x_centre - x1,y_centre - y,colour);
\r
9101 plot(x_centre - x1,y + y_centre,colour);
\r
9104 for(y1 = starty; y1 < endy; ++y1)
\r
9106 plot(y1+x_centre,x+y_centre,colour);
\r
9107 plot(y1+x_centre,y_centre - x,colour);
\r
9108 plot(x_centre - y1,y_centre - x,colour);
\r
9109 plot(x_centre - y1,x + y_centre,colour);
\r
9114 void draw(int x, int y, int colour)
\r
9116 /* Draws a line from _lastx,_lasty to x,y */
\r
9118 line(_lastx,_lasty,x,y,colour);
\r
9121 void psprite(SPRITE *sprite,int x,int y)
\r
9129 unsigned char far *video;
\r
9133 /* Super fast direct video write in mode 19 for sprites */
\r
9135 video = MK_FP(0xA000,0);
\r
9140 if (sprite->x != -1)
\r
9142 /* This sprite has been displayed before */
\r
9143 /* replace background */
\r
9144 /* This must be done first in case the sprite
\r
9145 overlaps itself */
\r
9149 pos = x + y * 320;
\r
9150 for(count = 0; count < 256; count++)
\r
9152 video[pos] = sprite->save[count];
\r
9168 pos = x + y * 320;
\r
9170 for(count = 0; count < 256; count++)
\r
9172 sprite->save[count] = video[pos];
\r
9173 z = sprite->data[count];
\r
9186 sprite->x = origx;
\r
9187 sprite->y = origy;
\r
9195 if (sprite->x != -1)
\r
9197 /* This sprite has been displayed before */
\r
9198 /* replace background */
\r
9199 /* This must be done first in case the sprite overlaps
\r
9204 for(count = 0; count < 256; count++)
\r
9206 if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))
\r
9208 z = sprite->save[count];
\r
9232 for(count = 0; count < 256; count++)
\r
9234 if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))
\r
9241 sprite->save[count] = z;
\r
9242 z = sprite->data[count];
\r
9264 sprite->x = origx;
\r
9265 sprite->y = origy;
\r
9271 Displaying A PCX File
\r
9273 The following program is offered as a practical example of graphics with
\r
9274 the IBM PC. It reads a file of the `PCX' format and displays the image on
\r
9278 /* Read a PCX file and display image */
\r
9282 #include <fcntl.h>
\r
9286 unsigned char man;
\r
9287 unsigned char version;
\r
9288 unsigned char encoding;
\r
9289 unsigned char bpp;
\r
9298 unsigned char planes;
\r
9307 PCX_HEADER header;
\r
9312 union REGS inreg,outreg;
\r
9314 void setvideo(unsigned char mode)
\r
9316 /* Sets the video display mode and clears the screen */
\r
9318 inreg.h.al = mode;
\r
9319 inreg.h.ah = 0x00;
\r
9320 int86(0x10, &inreg, &outreg);
\r
9323 void plot(int x, int y, unsigned char colour)
\r
9326 if (x < header.xmax && y < header.ymax)
\r
9329 /* Direct video plot in modes 16 & 18 only! */
\r
9350 asm mov ax,(02h shl 8) + 5;
\r
9353 asm mov ax,0A000h;
\r
9356 asm mov al,es:[bx];
\r
9357 asm mov al,byte ptr colour;
\r
9358 asm mov es:[bx],al;
\r
9360 asm mov ax,(0FFh shl 8 ) + 8;
\r
9363 asm mov ax,(00h shl 8) + 5;
\r
9368 void DISPLAY(unsigned char data)
\r
9375 for (n = 0; n < 6; n++)
\r
9386 main(int argc, char *argv[])
\r
9391 unsigned char data;
\r
9397 puts("USAGE IS getpcx <filename>");
\r
9406 fp = open(argv[1],O_RDONLY|O_BINARY);
\r
9408 _read(fp,&header,128);
\r
9410 total_bytes = header.planes * header.bpl;
\r
9412 for(scan = 0; scan <= header.ymax; scan++)
\r
9416 /* First scan line */
\r
9418 for(n = 0; n < total_bytes; n++)
\r
9421 _read(fp,&data,1);
\r
9423 count = data & 192;
\r
9427 count = data & 63;
\r
9429 _read(fp,&data,1);
\r
9449 What has drawing circles got to do with advanced C programming? Well
\r
9450 quite a lot, it is a task which is often desired by modern programmers,
\r
9451 and it is a task which can be attacked from a number of angles. This
\r
9452 example illustrates some of the ideas already discussed for replacing
\r
9453 floating point numbers with integers, and using lookup tables rather than
\r
9454 repeat calls to maths functions.
\r
9456 A circle may be drawn by plotting each point on its circumference. The
\r
9457 location of any point is given by;
\r
9460 Xp = Xo + Sine(Angle) * Radius
\r
9461 Yp = Yo + Cosine(Angle) * Radius
\r
9463 Where Xp,Yp is the point to be plotted, and Xo,Yo is the centre of the
\r
9466 Thus, the simplest way to draw a circle is to calculate Xp and Yp for
\r
9467 each angle between 1 and 360 degrees and to plot these points. There is
\r
9468 however one fundamental error with this. As the radius of the circle
\r
9469 increases, so also does the length of the arc between each angular point.
\r
9470 Thus a circle of sufficient radius will be drawn with a dotted line
\r
9471 rather than a solid line.
\r
9473 The problem of the distance between the angular points may be tackled in
\r
9476 1)The number of points to be plotted can be increased, to say every 30
\r
9478 2)A straight line may be drawn between each angular point.
\r
9480 The simplest circle drawing pseudo-code may then look like this;
\r
9483 FOR angle = 1 TO 360
\r
9484 PLOT Xo + SINE(angle) * radius, Yo + COSINE(angle) * radius
\r
9487 This code has two major disadvantages;
\r
9489 1) It uses REAL numbers for the sine and cosine figures
\r
9490 2) It makes numerous calculations of sine and cosine values
\r
9492 Both of these disadvantages result in a very slow piece of code. Since a
\r
9493 circle is a regular figure with two axis of symmetry, one in both the X
\r
9494 and Y axis, one only needs to calculate the relative offsets of points in
\r
9495 one quadrant of the circle and then these offsets may be applied to the
\r
9496 other three quadrants to produce a faster piece of code. Faster because
\r
9497 the slow sine and cosine calculations are only done 90 times instead of
\r
9501 FOR angle = 1 TO 90
\r
9502 Xa = SINE(angle) * radius
\r
9503 Ya = COSINE(angle) * radius
\r
9504 PLOT Xo + Xa, Yo + Ya
\r
9505 PLOT Xo + Xa, Yo - Ya
\r
9506 PLOT Xo - Xa, Yo + Ya
\r
9507 PLOT Xo - Xa, Yo - Ya
\r
9510 A further enhancement may be made by making use of sine and cosine lookup
\r
9511 tables instead of calculating them. This means calculating the sine and
\r
9512 cosine values for each required angle and storing them in a table. Then,
\r
9513 instead of calculating the values for each angle the circle drawing code
\r
9514 need only retrieve the values from a table;
\r
9517 FOR angle = 1 TO 90
\r
9518 Xa = SINE[angle] * radius
\r
9519 Ya = COSINE[angle] * radius
\r
9520 PLOT Xo + Xa, Yo + Ya
\r
9521 PLOT Xo + Xa, Yo - Ya
\r
9522 PLOT Xo - Xa, Yo + Ya
\r
9523 PLOT Xo - Xa, Yo - Ya
\r
9527 Most computer languages work in RADIANS rather than DEGREES. There being
\r
9528 approximately 57 degrees in one radian, 2 * PI radians in one circle.
\r
9529 This implies that to calculate sine and cosine values of sufficient
\r
9530 points to draw a reasonable circle using radians one must again use real
\r
9531 numbers, that is numbers which have figures following a decimal point.
\r
9532 Real number arithmetic, also known as floating point arithmetic, is
\r
9533 horrendously slow to calculate. Integer arithmetic on the other hand is
\r
9534 very quick to calculate, but less precise.
\r
9536 To use integer arithmetic in circle drawing code requires ingenuity. If
\r
9537 one agrees to use sine and cosine lookup tables for degrees, rather than
\r
9538 radians. Then the sine value of an angle of 1 degree is;
\r
9542 Which, truncated to an integer becomes zero! To overcome this the sine
\r
9543 and cosine values held in the table should be multiplied by some factor,
\r
9544 say 10000. Then, the integer value of the sine of an angle of 1 degree
\r
9549 If the sine and cosine values have been multiplied by a factor, then when
\r
9550 the calculation of the point's offset is carried out one must remember to
\r
9551 divide the result by the same factor. Thus the calculation becomes;
\r
9553 Xa = SINE[angle] * radius / factor
\r
9554 Ya = COSINE[angle] * radius / factor
\r
9556 The final obstacle to drawing circles on a computer is the relationship
\r
9557 between the width of the display screen and its height. This ratio
\r
9558 between width and height is known as the "aspect ratio" and varies upon
\r
9559 video display mode. The IBM VGA 256 colour mode for example can display
\r
9560 320 pixels across and 200 up the screen. This equates to an aspect ratio
\r
9561 of 1:1.3. If the circle drawing code ignores the aspect ratio, then the
\r
9562 shape displayed will often be ovalar to a greater or lesser degree due to
\r
9563 the rectangular shape of the display pixels. Thus in order to display a
\r
9564 true circle, the formulae to calculate each point on the circumference
\r
9565 must be amended to calculate a slight ellipse in compensation of the
\r
9566 distorting factor of the display.
\r
9568 The offset formulae then become;
\r
9570 Xa = SINE[angle] * radius / factor
\r
9571 Ya = COSINE[angle] * radius / (factor * aspect ratio)
\r
9573 The following short C program illustrates a practical circle drawing code
\r
9574 segment, in a demonstrable form;
\r
9577 /* Circles.c A demonstration circle drawing program for the IBM PC
\r
9581 #include <stdlib.h>
\r
9583 int sintable[91] = {0,175,349,523,698,
\r
9584 872,1045,1219,1392,
\r
9585 1564,1736,1908,2079,
\r
9586 2250,2419,2588,2756,
\r
9587 2924,3090,3256,3420,
\r
9588 3584,3746,3907,4067,
\r
9589 4226,4384,4540,4695,
\r
9590 4848,5000,5150,5299,
\r
9591 5446,5592,5736,5878,
\r
9592 6018,6157,6293,6428,
\r
9593 6561,6691,6820,6947,
\r
9594 7071,7193,7314,7431,
\r
9595 7547,7660,7771,7880,
\r
9596 7986,8090,8192,8290,
\r
9597 8387,8480,8572,8660,
\r
9598 8746,8829,8910,8988,
\r
9599 9063,9135,9205,9272,
\r
9600 9336,9397,9455,9511,
\r
9601 9563,9613,9659,9703,
\r
9602 9744,9781,9816,9848,
\r
9603 9877,9903,9925,9945,
\r
9604 9962,9976,9986,9994,
\r
9608 int costable[91] = { 10000,9998,9994,9986,9976,
\r
9609 9962,9945,9925,9903,
\r
9610 9877,9848,9816,9781,
\r
9611 9744,9703,9659,9613,
\r
9612 9563,9511,9455,9397,
\r
9613 9336,9272,9205,9135,
\r
9614 9063,8988,8910,8829,
\r
9615 8746,8660,8572,8480,
\r
9616 8387,8290,8192,8090,
\r
9617 7986,7880,7771,7660,
\r
9618 7547,7431,7314,7193,
\r
9619 7071,6947,6820,6691,
\r
9620 6561,6428,6293,6157,
\r
9621 6018,5878,5736,5592,
\r
9622 5446,5299,5150,5000,
\r
9623 4848,4695,4540,4384,
\r
9624 4226,4067,3907,3746,
\r
9625 3584,3420,3256,3090,
\r
9626 2924,2756,2588,2419,
\r
9627 2250,2079,1908,1736,
\r
9628 1564,1392,1219,1045,
\r
9633 void setvideo(unsigned char mode)
\r
9635 /* Sets the video display mode for an IBM PC */
\r
9637 asm mov al , mode;
\r
9642 void plot(int x, int y, unsigned char colour)
\r
9644 /* Code for IBM PC BIOS ROM */
\r
9645 /* Sets a pixel at the specified coordinates to a specified
\r
9648 /* Return if out of range */
\r
9649 if (x < 0 || y < 0 || x > 320 || y > 200)
\r
9652 asm mov al , colour;
\r
9660 void Line(int a, int b, int c, int d, int col)
\r
9662 /* Draws a straight line from point a,b to point c,d in colour
\r
9673 double s; /* The only real number variable, but it's essential
\r
9722 for (i = 0; i <= m; i++)
\r
9741 void Circle(int x, int y, int rad, int col)
\r
9743 /* Draws a circle about origin x,y */
\r
9744 /* With a radius of rad */
\r
9745 /* The col parameter defines the colour for plotting */
\r
9760 /* Calculate first point in each segment */
\r
9762 a1 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
\r
9763 b1 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
\r
9765 a2 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
\r
9766 b2 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
\r
9768 a3 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
\r
9769 b3 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
\r
9771 a4 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
\r
9772 b4 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
\r
9774 /* Start loop at second point */
\r
9775 for(f = 1; f <= 90; f++)
\r
9777 /* Calculate offset to new point */
\r
9778 xa = ((long)(costable[f]) * (long)(rad) + 5000) / 10000;
\r
9779 ya = ((long)(sintable[f]) * (long)(rad) + 5000) / 13000;
\r
9781 /* Draw a line from the previous point to the new point in
\r
9783 Line(a1,b1,x + xa, y + ya,col);
\r
9784 Line(a2,b2,x - xa, y + ya,col);
\r
9785 Line(a3,b3,x - xa, y - ya,col);
\r
9786 Line(a4,b4,x + xa, y - ya,col);
\r
9788 /* Update the previous point in each segment */
\r
9804 /* Select VGA 256 colour 320 x 200 video mode */
\r
9807 /* Draw some circles */
\r
9808 for(n = 0; n < 100; n++)
\r
9809 Circle(160,100,n,n + 20);
\r
9816 The VESA BIOS provides a number of new, and exciting video modes not
\r
9817 supported by the standard BIOS. These modes vary from one video card to
\r
9818 another, but most support the following modes:
\r
9822 0x54 Text 16 colours 132 x 43
\r
9823 0x55 Text 16 colours 132 x 25
\r
9824 0x58 Graphics 16 colours 800 x 600
\r
9825 0x5C Graphics 256 colours 800 x 600
\r
9826 0x5D Graphics 16 colours 1024 x 768
\r
9827 0x5F Graphics 256 colours 640 x 480
\r
9828 0x60 Graphics 256 colours 1024 x 768
\r
9829 0x64 Graphics 64k colours 640 x 480
\r
9830 0x65 Graphics 64k colours 800 x 600
\r
9831 0x6A Graphics 16 colours 800 x 600
\r
9832 0x6C Graphics 16 colours 1280 x 1024
\r
9833 0x70 Graphics 16m colours 320 x 200
\r
9834 0x71 Graphics 16m colours 640 x 480
\r
9836 These modes are in addition to the standard BIOS video modes described
\r
9839 Setting a VESA video mode requires a call to a different BIOS function
\r
9840 than the standard BIOS, as illustrated in the following example which
\r
9841 enables any VESA or standard display mode to be selected from the DOS
\r
9845 #include <ctype.h>
\r
9847 void setvideo(int mode)
\r
9849 /* Sets the video display to a VESA or normal mode and clears
\r
9852 union REGS inreg,outreg;
\r
9854 inreg.h.ah = 0x4f;
\r
9855 inreg.h.al = 0x02;
\r
9856 inreg.x.bx = mode;
\r
9857 int86(0x10, &inreg, &outreg);
\r
9860 main(int argc, char *argv[])
\r
9862 setvideo(atoi(argv[1]));
\r
9866 Plotting pixels in a VESA mode graphics display can be acgieved with the
\r
9867 standard BIOS plot functiona call, as illustrated here;
\r
9870 void plot(int x, int y, unsigned char colour)
\r
9872 asm mov al , colour;
\r
9881 Or, in a 800 x 600 resolution mode you can use this fast direct video
\r
9882 access plot function;
\r
9884 void plot( int x, int y, unsigned char pcolor)
\r
9887 Fast 800 x 600 mode (0x58 or 0x102) plotting
\r
9910 asm mov ax,(02h shl 8) + 5;
\r
9913 asm mov ax,0A000h;
\r
9916 asm mov al,es:[bx];
\r
9917 asm mov al,byte ptr pcolor;
\r
9918 asm mov es:[bx],al;
\r
9920 asm mov ax,(0FFh shl 8 ) + 8;
\r
9923 asm mov ax,(00h shl 8) + 5;
\r
9927 There are lots more functions supported by the VESA BIOS, but this will
\r
9928 get you going with the basic operations. Remember though that when using
\r
9929 VESA display modes, that the direct console I/O functions in the C
\r
9930 compiler library may not function correctly.
\r
9932 DIRECTORY SEARCHING WITH THE IBM PC AND DOS
\r
9934 Amongst the many functions provided by DOS for programmers, are a pair of
\r
9935 functions; "Find first" and "Find next" which are used to search a DOS
\r
9936 directory for a specified file name or names. The first function, "Find
\r
9937 first" is accessed via DOS interrupt 21, function 4E. It takes an ascii
\r
9938 string file specification, which can include wildcards, and the required
\r
9939 attribute for files to match. Upon return the function fills the disk
\r
9940 transfer area (DTA) with details of the located file and returns with the
\r
9941 carry flag clear. If an error occurs, such as no matching files are
\r
9942 located, the function returns with the carry flag set.
\r
9944 Following a successful call to "Find first", a program can call "Find
\r
9945 next", DOS interrupt 21, function 4F, to locate the next file matching
\r
9946 the specifications provided by the initial call to "Find first". If this
\r
9947 function succeeds, then the DTA is filled in with details of the next
\r
9948 matching file, and the function returns with the carry flag clear.
\r
9949 Otherwise a return is made with the carry flag set.
\r
9951 Most C compilers for the IBM PC provide non standard library functions
\r
9952 for accessing these two functions. Turbo C provides "findfirst()" and
\r
9953 "findnext()". Making use of the supplied library functions shields the
\r
9954 programmer from the messy task of worrying about the DTA. Microsoft C
\r
9955 programmers should substitue findfirst() with _dos_findfirst() and
\r
9956 findnext() with _dos_findnext().
\r
9958 The following Turbo C example imitates the DOS directory command, in a
\r
9961 #include <stdio.h>
\r
9967 /* Display directory listing of current directory */
\r
9976 struct ffblk ffblk;
\r
9979 /* First display sub directory entries */
\r
9980 done = findfirst("*.",&ffblk,16);
\r
9984 year = (ffblk.ff_fdate >> 9) + 80;
\r
9985 month = (ffblk.ff_fdate >> 5) & 0x0f;
\r
9986 day = ffblk.ff_fdate & 0x1f;
\r
9987 hour = (ffblk.ff_ftime >> 11);
\r
9988 min = (ffblk.ff_ftime >> 5) & 63;
\r
9998 printf("%-11.11s <DIR> %02d-%02d-%02d %2d:%02d%c\n",
\r
9999 ffblk.ff_name,day,month,year,hour,min,amflag);
\r
10000 done = findnext(&ffblk);
\r
10003 /* Now all files except directories */
\r
10004 done = findfirst("*.*",&ffblk,231);
\r
10008 year = (ffblk.ff_fdate >> 9) + 80;
\r
10009 month = (ffblk.ff_fdate >> 5) & 0x0f;
\r
10010 day = ffblk.ff_fdate & 0x1f;
\r
10011 hour = (ffblk.ff_ftime >> 11);
\r
10012 min = (ffblk.ff_ftime >> 5) & 63;
\r
10022 parsfnm(ffblk.ff_name,&fcb,1);
\r
10024 printf("%-8.8s %-3.3s %8ld %02d-%02d-%02d %2d:%02d%c\n",
\r
10025 fcb.fcb_name,fcb.fcb_ext,ffblk.ff_fsize,
\r
10026 day,month,year,hour,min,amflag);
\r
10027 done = findnext(&ffblk);
\r
10032 The function "parsfnm()" is a Turbo C library command which makes use of
\r
10033 the DOS function for parsing an ascii string containing a file name, into
\r
10034 its component parts. These component parts are then put into a DOS file
\r
10035 control block (fcb), from where they may be easily retrieved for
\r
10036 displaying by printf().
\r
10039 The DOS DTA is comprised as follows;
\r
10041 Offset Length Contents
\r
10044 15 Byte Attribute of matched
\r
10046 16 Word File time
\r
10047 18 Word File date
\r
10049 1E 0D File name and extension
\r
10052 The file time word contains the time at which the file was last written
\r
10053 to disc and is comprised as follows;
\r
10057 0 - 4 Seconds divided by 2
\r
10061 The file date word holds the date on which the file was last written to
\r
10062 disc and is comprised of;
\r
10068 9 - 15 Years since 1980
\r
10070 To extract these details from the DTA requires a little manipulation, as
\r
10071 illustrated in the above example.
\r
10073 The DTA attribute flag is comprised of the following bits being set or
\r
10085 ACCESSING EXPANDED MEMORY
\r
10087 Memory (RAM) in an IBM PC comes in three flavours; Conventional, Expanded
\r
10088 and Extended. Conventional memory is the 640K of RAM which the operating
\r
10089 system DOS can access. This memory is normally used. However, it is often
\r
10090 insufficient for todays RAM hungry systems. Expanded memory is RAM which
\r
10091 is addressed outside of the area of conventional RAM not by DOS but by a
\r
10092 second program called a LIM EMS driver. Access to this device driver is
\r
10093 made through interrupt 67h.
\r
10095 The main problem with accessing expanded memory is that no matter how
\r
10096 much expanded memory is fitted to the computer, it can only be accessed
\r
10097 through 16K blocks refered to as pages. So for example. If you have 2mB
\r
10098 of expanded RAM fitted to your PC then that is comprised of 128 pages
\r
10099 (128 * 16K = 2mB).
\r
10101 A program can determine whether a LIM EMS driver is installed by
\r
10102 attempting to open the file `EMMXXXX0' which is guarranteed by the LIM
\r
10103 standard to be present as an IOCTL device when the device driver is
\r
10106 The following source code illustrates some basic functions for testing
\r
10107 for and accessing expanded memory.
\r
10110 Various functions for using Expanded memory
\r
10116 char far *emmbase;
\r
10120 Tests for the presence of expnaded memory by attempting to
\r
10121 open the file EMMXXXX0.
\r
10125 struct SREGS sregs;
\r
10129 /* Attempt to open the file device EMMXXXX0 */
\r
10130 regs.x.ax = 0x3d00;
\r
10131 regs.x.dx = (int)"EMMXXXX0";
\r
10133 intdosx(®s,®s,&sregs);
\r
10134 handle = regs.x.ax;
\r
10135 error = regs.x.cflag;
\r
10139 regs.h.ah = 0x3e;
\r
10140 regs.x.bx = handle;
\r
10141 intdos(®s,®s);
\r
10149 Checks whether the expanded memory manager responds correctly
\r
10154 regs.h.ah = 0x40;
\r
10155 int86(EMM,®s,®s);
\r
10160 regs.h.ah = 0x41;
\r
10161 int86(EMM,®s,®s);
\r
10166 emmbase = MK_FP(regs.x.bx,0);
\r
10173 Returns the number of available (free) 16K pages of expanded
\r
10175 or -1 if an error occurs.
\r
10180 regs.h.ah = 0x42;
\r
10181 int86(EMM,®s,®s);
\r
10183 return regs.x.bx;
\r
10187 long emmalloc(int n)
\r
10190 Requests 'n' pages of expanded memory and returns the file
\r
10192 assigned to the pages or -1 if there is an error
\r
10197 regs.h.ah = 0x43;
\r
10199 int86(EMM,®s,®s);
\r
10202 return regs.x.dx;
\r
10205 emmmap(long handle, int phys, int page)
\r
10208 Maps a physical page from expanded memory into the page frame
\r
10210 conventional memory 16K window so that data can be transfered
\r
10212 the expanded memory and conventional memory.
\r
10217 regs.h.ah = 0x44;
\r
10218 regs.h.al = page;
\r
10219 regs.x.bx = phys;
\r
10220 regs.x.dx = handle;
\r
10221 int86(EMM,®s,®s);
\r
10222 return (regs.h.ah == 0);
\r
10225 void emmmove(int page, char *str, int n)
\r
10228 Move 'n' bytes from conventional memory to the specified
\r
10235 ptr = emmbase + page * 16384;
\r
10240 void emmget(int page, char *str, int n)
\r
10243 Move 'n' bytes from the specified expanded memory page into
\r
10250 ptr = emmbase + page * 16384;
\r
10255 emmclose(long handle)
\r
10258 Release control of the expanded memory pages allocated to
\r
10264 regs.h.ah = 0x45;
\r
10265 regs.x.dx = handle;
\r
10266 int86(EMM,®s,®s);
\r
10267 return (regs.h.ah == 0);
\r
10271 Test function for the EMM routines
\r
10278 char teststr[80];
\r
10283 printf("Expanded memory is not present\n");
\r
10289 printf("Expanded memory manager is not present\n");
\r
10293 avail = emmavail();
\r
10296 printf("Expanded memory manager error\n");
\r
10299 printf("There are %ld pages available\n",avail);
\r
10301 /* Request 10 pages of expanded memory */
\r
10302 if((emmhandle = emmalloc(10)) < 0)
\r
10304 printf("Insufficient pages available\n");
\r
10308 for (i = 0; i < 10; i++)
\r
10310 sprintf(teststr,"%02d This is a test string\n",i);
\r
10311 emmmap(emmhandle,i,0);
\r
10312 emmmove(0,teststr,strlen(teststr) + 1);
\r
10315 for (i = 0; i < 10; i++)
\r
10317 emmmap(emmhandle,i,0);
\r
10318 emmget(0,teststr,strlen(teststr) + 1);
\r
10319 printf("READING BLOCK %d: %s\n",i,teststr);
\r
10322 emmclose(emmhandle);
\r
10325 ACCESSING EXTENDED MEMORY
\r
10328 Extended memory has all but taken over from Expanded Memory now (1996).
\r
10329 It is faster and more useable than expanded memory. As with Expanded
\r
10330 memory, Extended memory cannot be directly accessed through the standard
\r
10331 DOS mode, and so a transfer buffer in conventional or "real-mode" memory
\r
10332 needs to be used. The process to write data to Extended memory then
\r
10333 involves copying the data to the transfer buffer in conventional memory,
\r
10334 and from there copying it to Extended memory.
\r
10336 Before any use may be made of Extended memory, a program should test to
\r
10337 see if Extended memory is available. The following function, XMS_init(),
\r
10338 tests for the presence of Extended memory, and if available calls another
\r
10339 function, GetXMSEntry() to initialise the program for using Extended
\r
10340 Memory. The function also allocates a conventional memory transfer
\r
10346 BLOCKSIZE will be the size of our real-memory buffer that
\r
10347 we'll swap XMS through (must be a multiple of 1024, since
\r
10348 XMS is allocated in 1K chunks.)
\r
10352 #define BLOCKSIZE (16L * 1024L)
\r
10356 #ifdef __MEDIUM__
\r
10357 #define BLOCKSIZE (16L * 1024L)
\r
10361 #ifdef __COMPACT__
\r
10362 #define BLOCKSIZE (64L * 1024L)
\r
10366 #define BLOCKSIZE (64L * 1024L)
\r
10373 returns 0 if XMS present,
\r
10375 2 if unable to allocate conventional memory transfer
\r
10378 unsigned char status;
\r
10380 geninterrupt(0x2F);
\r
10385 XMSBuf = (char far *) farmalloc(BLOCKSIZE);
\r
10386 if (XMSBuf == NULL)
\r
10393 void GetXMSEntry(void)
\r
10396 GetXMSEntry sets XMSFunc to the XMS Manager entry point
\r
10397 so we can call it later
\r
10401 geninterrupt(0x2F);
\r
10402 XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);
\r
10406 Once the presence of Extended memory has been confirmed, a program can
\r
10407 find out how much Extended memory is available;
\r
10409 void XMSSize(int *kbAvail, int *largestAvail)
\r
10412 XMSSize returns the total kilobytes available, and the
\r
10414 in kilobytes of the largest available block
\r
10419 *largestAvail=_DX;
\r
10424 The following function may be called to allocate a block of Extended
\r
10425 memory, like you would allocate a block of conventional memory.
\r
10427 char AllocXMS(unsigned long numberBytes)
\r
10430 Allocate a block of XMS memory numberBytes long
\r
10431 Returns 1 on success
\r
10435 _DX = (int)(numberBytes / 1024);
\r
10447 Allocated Extended memory is not freed by DOS. A program using Extended
\r
10448 memory must release it before terminating. This function frees a block of
\r
10449 extended memory previously allocated by AllocXMS. Note, XMSHandle is a
\r
10450 global variable of type int.
\r
10452 void XMS_free(void)
\r
10463 Two functions are now given. One for writing data to Extended memory, and
\r
10464 one for reading data from Extended memory into conventional memory.
\r
10467 XMSParms is a structure for copying information to and from
\r
10468 real-mode memory to XMS memory
\r
10471 struct parmstruct
\r
10474 blocklength is the size in bytes of block to copy
\r
10476 unsigned long blockLength;
\r
10479 sourceHandle is the XMS handle of source; 0 means that
\r
10480 sourcePtr will be a 16:16 real-mode pointer, otherwise
\r
10481 sourcePtr is a 32-bit offset from the beginning of the
\r
10482 XMS area that sourceHandle points to
\r
10485 unsigned int sourceHandle;
\r
10486 far void *sourcePtr;
\r
10489 destHandle is the XMS handle of destination; 0 means that
\r
10490 destPtr will be a 16:16 real-mode pointer, otherwise
\r
10491 destPtr is a 32-bit offset from the beginning of the XMS
\r
10492 area that destHandle points to
\r
10495 unsigned int destHandle;
\r
10496 far void *destPtr;
\r
10501 char XMS_write(unsigned long loc, char far *val, unsigned length)
\r
10504 Round length up to next even value
\r
10506 length += length % 2;
\r
10508 XMSParms.sourceHandle=0;
\r
10509 XMSParms.sourcePtr=val;
\r
10510 XMSParms.destHandle=XMSHandle;
\r
10511 XMSParms.destPtr=(void far *) (loc);
\r
10512 XMSParms.blockLength=length; /* Must be an even number! */
\r
10513 _SI = FP_OFF(&XMSParms);
\r
10524 void *XMS_read(unsigned long loc,unsigned length)
\r
10527 Returns pointer to data
\r
10532 Round length up to next even value
\r
10534 length += length % 2;
\r
10536 XMSParms.sourceHandle=XMSHandle;
\r
10537 XMSParms.sourcePtr=(void far *) (loc);
\r
10538 XMSParms.destHandle=0;
\r
10539 XMSParms.destPtr=XMSBuf;
\r
10540 XMSParms.blockLength=length; /* Must be an even number */
\r
10541 _SI=FP_OFF(&XMSParms);
\r
10552 And now putting it all together is a demonstration program.
\r
10554 /* A sequential table of variable length records in XMS */
\r
10557 #include <stdio.h>
\r
10558 #include <stdlib.h>
\r
10559 #include <alloc.h>
\r
10560 #include <string.h>
\r
10566 BLOCKSIZE will be the size of our real-memory buffer that
\r
10567 we'll swap XMS through (must be a multiple of 1024, since
\r
10568 XMS is allocated in 1K chunks.)
\r
10572 #define BLOCKSIZE (16L * 1024L)
\r
10576 #ifdef __MEDIUM__
\r
10577 #define BLOCKSIZE (16L * 1024L)
\r
10581 #ifdef __COMPACT__
\r
10582 #define BLOCKSIZE (64L * 1024L)
\r
10586 #define BLOCKSIZE (64L * 1024L)
\r
10591 XMSParms is a structure for copying information to and from
\r
10592 real-mode memory to XMS memory
\r
10595 struct parmstruct
\r
10598 blocklength is the size in bytes of block to copy
\r
10600 unsigned long blockLength;
\r
10603 sourceHandle is the XMS handle of source; 0 means that
\r
10604 sourcePtr will be a 16:16 real-mode pointer, otherwise
\r
10605 sourcePtr is a 32-bit offset from the beginning of the
\r
10606 XMS area that sourceHandle points to
\r
10609 unsigned int sourceHandle;
\r
10610 far void *sourcePtr;
\r
10613 destHandle is the XMS handle of destination; 0 means that
\r
10614 destPtr will be a 16:16 real-mode pointer, otherwise
\r
10615 destPtr is a 32-bit offset from the beginning of the XMS
\r
10616 area that destHandle points to
\r
10619 unsigned int destHandle;
\r
10620 far void *destPtr;
\r
10624 void far (*XMSFunc) (void); /* Used to call XMS manager
\r
10626 char GetBuf(void);
\r
10627 void GetXMSEntry(void);
\r
10629 char *XMSBuf; /* Conventional memory buffer for transfers */
\r
10631 unsigned int XMSHandle; /* handle to allocated XMS block */
\r
10637 returns 0 if XMS present,
\r
10639 2 if unable to allocate transfer buffer
\r
10641 unsigned char status;
\r
10643 geninterrupt(0x2F);
\r
10648 XMSBuf = (char far *) farmalloc(BLOCKSIZE);
\r
10649 if (XMSBuf == NULL)
\r
10656 void GetXMSEntry(void)
\r
10659 GetXMSEntry sets XMSFunc to the XMS Manager entry point
\r
10660 so we can call it later
\r
10664 geninterrupt(0x2F);
\r
10665 XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);
\r
10669 void XMSSize(int *kbAvail, int *largestAvail)
\r
10672 XMSSize returns the total kilobytes available, and the
\r
10674 in kilobytes of the largest available block
\r
10679 *largestAvail=_DX;
\r
10683 char AllocXMS(unsigned long numberBytes)
\r
10686 Allocate a block of XMS memory numberBytes long
\r
10689 _DX = (int)(numberBytes / 1024);
\r
10700 void XMS_free(void)
\r
10710 char XMS_write(unsigned long loc, char far *val, unsigned length)
\r
10713 Round length up to next even value
\r
10715 length += length % 2;
\r
10717 XMSParms.sourceHandle=0;
\r
10718 XMSParms.sourcePtr=val;
\r
10719 XMSParms.destHandle=XMSHandle;
\r
10720 XMSParms.destPtr=(void far *) (loc);
\r
10721 XMSParms.blockLength=length; /* Must be an even number! */
\r
10722 _SI = FP_OFF(&XMSParms);
\r
10733 void *XMS_read(unsigned long loc,unsigned length)
\r
10736 Returns pointer to data
\r
10741 Round length up to next even value
\r
10743 length += length % 2;
\r
10745 XMSParms.sourceHandle=XMSHandle;
\r
10746 XMSParms.sourcePtr=(void far *) (loc);
\r
10747 XMSParms.destHandle=0;
\r
10748 XMSParms.destPtr=XMSBuf;
\r
10749 XMSParms.blockLength=length; /* Must be an even number */
\r
10750 _SI=FP_OFF(&XMSParms);
\r
10762 Demonstration code
\r
10763 Read various length strings into a single XMS block (EMB)
\r
10764 and write them out again
\r
10769 int kbAvail,largestAvail;
\r
10775 if (XMS_init() == 0)
\r
10776 printf("XMS Available ...\n");
\r
10779 printf("XMS Not Available\n");
\r
10783 XMSSize(&kbAvail,&largestAvail);
\r
10784 printf("Kilobytes Available: %d; Largest block:
\r
10785 %dK\n",kbAvail,largestAvail);
\r
10787 if (!AllocXMS(2000 * 1024L))
\r
10795 p = fgets(buffer,1000,stdin);
\r
10798 XMS_write(pos,buffer,strlen(buffer) + 1);
\r
10799 pos += strlen(buffer) + 1;
\r
10802 while(p != NULL);
\r
10810 memcpy(buffer,XMS_read(pos,100),70);
\r
10811 printf("%s",buffer);
\r
10812 pos += strlen(buffer) + 1;
\r
10814 while(pos < end);
\r
10817 It is VERY important to free any XMS before exiting!
\r
10829 Programs which remain running and resident in memory while other programs
\r
10830 are running are the most exciting line of programming for many PC
\r
10831 developers. This type of program is known as a "Terminate and Stay
\r
10832 Resident" or "TSR" program and they are very difficult to program
\r
10835 The difficulties in programming TSRs comes from the limitations of DOS
\r
10836 which is not a multi-tasking operating system, and does not react well to
\r
10837 re-enterant code. That is it's own functions (interrupts) calling
\r
10840 In theory a TSR is quite simple. It is an ordinary program which
\r
10841 terminates not through the usual DOS terminate function, but through the
\r
10842 DOS "keep" function - interrupt 27h. This function reserves an area of
\r
10843 memory, used by the program so that no other programs will overwrite it.
\r
10844 This in itself is not a very difficult task, excepting that the program
\r
10845 needs to tell DOS how much memory to leave it!
\r
10847 The problems stem mainly from not being able to use DOS function calls
\r
10848 within the TSR program once it has "gone resident".
\r
10850 There are a few basic rules which help to clarify the problems
\r
10851 encountered in programming TSRs:
\r
10853 1.Avoid DOS function calls
\r
10854 2.Monitor the DOS busy flag, when this flag is nonzero, DOS is
\r
10855 executing an interrupt 21h function and MUST NOT be disturbed!
\r
10856 3.Monitor interrupt 28h. This reveals when DOS is busy waiting for
\r
10857 console input. At this time you can disturb DOS regardless of the
\r
10858 DOS busy flag setting.
\r
10859 4.Provide some way of checking whether the TSR is already loaded to
\r
10860 prevent multiple copies occuring in memory.
\r
10861 5.Remember that other TSR programs may be chained to interrupts, and
\r
10862 so you must chain any interrupt vectors that your program needs.
\r
10863 6.Your TSR program must use its own stack, and NOT that of the running
\r
10865 7.TSR programs must be compiled in a small memory model with stack
\r
10866 checking turned off.
\r
10867 8.When control passes to your TSR program, it must tell DOS that the
\r
10868 active process has changed.
\r
10871 The following three source code modules describe a complete TSR program.
\r
10872 This is a useful pop-up address book database which can be activated
\r
10873 while any other program is running by pressing the key combination `Alt'
\r
10874 and `.'. If the address book does not respond to the key press, it is
\r
10875 probably because DOS cannot be disturbed, and you should try to pop-it-up
\r
10880 A practical TSR program (a pop-up address book database)
\r
10881 Compile in small memory model with stack checking OFF
\r
10885 #include <stdio.h>
\r
10886 #include <string.h>
\r
10889 static union REGS rg;
\r
10892 Size of the program to remain resident
\r
10893 experimentation is required to make this as small as possible
\r
10895 unsigned sizeprogram = 28000/16;
\r
10897 /* Activate with Alt . */
\r
10898 unsigned scancode = 52; /* . */
\r
10899 unsigned keymask = 8; /* ALT */
\r
10901 char signature[]= "POPADDR";
\r
10905 Function prototypes
\r
10908 void curr_cursor(int *x, int *y);
\r
10909 int resident(char *, void interrupt(*)());
\r
10910 void resinit(void);
\r
10911 void terminate(void);
\r
10912 void restart(void);
\r
10914 void resident_psp(void);
\r
10918 Entry point from DOS
\r
10921 main(int argc, char *argv[])
\r
10923 void interrupt ifunc();
\r
10927 For simplicity, assume the data file is in the root
\r
10931 strcpy(fpath,"C:\\ADDRESS.DAT");
\r
10933 if ((ivec = resident(signature,ifunc)) != 0)
\r
10935 /* TSR is resident */
\r
10939 if (strcmp(argv[1],"quit") == 0)
\r
10941 else if (strcmp(argv[1],"restart") == 0)
\r
10943 else if (strcmp(argv[1],"wait") == 0)
\r
10947 int86(ivec,&rg,&rg);
\r
10951 printf("\nPopup Address Book is already resident");
\r
10955 /* Initial load of TSR program */
\r
10956 printf("Popup Address Book Resident.\nPress Alt . To
\r
10957 Activate....\n");
\r
10962 void interrupt ifunc(bp,di,si,ds,es,dx,cx,bx,ax)
\r
10976 curr_cursor(&x,&y);
\r
10978 /* Call the TSR C program here */
\r
10984 Second source module
\r
10988 #include <stdio.h>
\r
10990 static union REGS rg;
\r
10991 static struct SREGS seg;
\r
10992 static unsigned mcbseg;
\r
10993 static unsigned dosseg;
\r
10994 static unsigned dosbusy;
\r
10995 static unsigned enddos;
\r
10996 char far *intdta;
\r
10997 static unsigned intsp;
\r
10998 static unsigned intss;
\r
10999 static char far *mydta;
\r
11000 static unsigned myss;
\r
11001 static unsigned stack;
\r
11002 static unsigned ctrl_break;
\r
11003 static unsigned mypsp;
\r
11004 static unsigned intpsp;
\r
11005 static unsigned pids[2];
\r
11006 static int pidctr = 0;
\r
11008 static void interrupt (*oldtimer)();
\r
11009 static void interrupt (*old28)();
\r
11010 static void interrupt (*oldkb)();
\r
11011 static void interrupt (*olddisk)();
\r
11012 static void interrupt (*oldcrit)();
\r
11014 void interrupt newtimer();
\r
11015 void interrupt new28();
\r
11016 void interrupt newkb();
\r
11017 void interrupt newdisk();
\r
11018 void interrupt newcrit();
\r
11020 extern unsigned sizeprogram;
\r
11021 extern unsigned scancode;
\r
11022 extern unsigned keymask;
\r
11024 static int resoff = 0;
\r
11025 static int running = 0;
\r
11026 static int popflg = 0;
\r
11027 static int diskflag = 0;
\r
11028 static int kbval;
\r
11029 static int cflag;
\r
11031 void dores(void);
\r
11032 void pidaddr(void);
\r
11042 dosbusy = rg.x.bx;
\r
11044 mydta = getdta();
\r
11046 oldtimer = getvect(0x1c);
\r
11047 old28 = getvect(0x28);
\r
11048 oldkb = getvect(9);
\r
11049 olddisk = getvect(0x13);
\r
11051 setvect(0x1c,newtimer);
\r
11052 setvect(9,newkb);
\r
11053 setvect(0x28,new28);
\r
11054 setvect(0x13,newdisk);
\r
11056 stack = (sizeprogram - (seg.ds - seg.cs)) * 16 - 300;
\r
11057 rg.x.ax = 0x3100;
\r
11058 rg.x.dx = sizeprogram;
\r
11062 void interrupt newdisk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)
\r
11072 void interrupt newcrit(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)
\r
11078 void interrupt newkb()
\r
11080 if (inportb(0x60) == scancode)
\r
11082 kbval = peekb(0,0x417);
\r
11083 if (!resoff && ((kbval & keymask) ^ keymask) == 0)
\r
11085 kbval = inportb(0x61);
\r
11086 outportb(0x61,kbval | 0x80);
\r
11087 outportb(0x61,kbval);
\r
11089 outportb(0x20,0x20);
\r
11099 void interrupt newtimer()
\r
11102 if (popflg && peekb(dosseg,dosbusy) == 0)
\r
11103 if(diskflag == 0)
\r
11105 outportb(0x20,0x20);
\r
11111 void interrupt new28()
\r
11114 if (popflg && peekb(dosseg,dosbusy) != 0)
\r
11123 intpsp = peek(dosseg,*pids);
\r
11124 for(pp = 0; pp < pidctr; pp++)
\r
11125 poke(dosseg,pids[pp],mypsp);
\r
11128 interrupted_psp()
\r
11130 for(pp = 0; pp < pidctr; pp++)
\r
11131 poke(dosseg,pids[pp],intpsp);
\r
11143 oldcrit = getvect(0x24);
\r
11144 setvect(0x24,newcrit);
\r
11145 rg.x.ax = 0x3300;
\r
11147 ctrl_break = rg.h.dl;
\r
11148 rg.x.ax = 0x3301;
\r
11151 intdta = getdta();
\r
11155 interrupted_psp();
\r
11157 setvect(0x24,oldcrit);
\r
11158 rg.x.ax = 0x3301;
\r
11159 rg.h.dl = ctrl_break;
\r
11168 static int avec = 0;
\r
11170 unsigned resident(char *signature,void interrupt(*ifunc)())
\r
11177 df = seg.ds-seg.cs;
\r
11178 for(vec = 0x60; vec < 0x68; vec++)
\r
11180 if (getvect(vec) == NULL)
\r
11186 for(sg = signature; *sg; sg++)
\r
11187 if (*sg != peekb(peek(0,2+vec*4)+df,(unsigned)sg))
\r
11193 setvect(avec,ifunc);
\r
11197 static void pidaddr()
\r
11199 unsigned adr = 0;
\r
11207 enddos = peek(enddos,rg.x.bx-2);
\r
11208 while(pidctr < 2 && (unsigned)((dosseg<<4) + adr) < (enddos
\r
11211 if (peek(dosseg,adr) == mypsp)
\r
11214 rg.x.bx = mypsp + 1;
\r
11216 if (peek(dosseg,adr) == mypsp + 1)
\r
11217 pids[pidctr++] = adr;
\r
11228 setvect(0x1c,oldtimer);
\r
11229 setvect(9,oldkb);
\r
11230 setvect(0x28,old28);
\r
11231 setvect(0x13,olddisk);
\r
11232 setvect(avec,(void interrupt (*)()) 0);
\r
11236 mcbseg = peek(mcbseg,rg.x.bx-2);
\r
11238 while(peekb(mcbseg,0) == 0x4d)
\r
11240 if(peek(mcbseg,1) == mypsp)
\r
11243 seg.es = mcbseg+1;
\r
11244 intdosx(&rg,&rg,&seg);
\r
11246 mcbseg += peek(mcbseg,3) + 1;
\r
11252 if (getvect(0x13) == (void interrupt (*)()) newdisk)
\r
11253 if (getvect(9) == newkb)
\r
11254 if(getvect(0x28) == new28)
\r
11255 if(getvect(0x1c) == newtimer)
\r
11273 void cursor(int y, int x)
\r
11275 rg.x.ax = 0x0200;
\r
11277 rg.x.dx = ((y << 8) & 0xff00) + x;
\r
11278 int86(16,&rg,&rg);
\r
11281 void curr_cursor(int *y, int *x)
\r
11283 rg.x.ax = 0x0300;
\r
11285 int86(16,&rg,&rg);
\r
11291 Third module, the simple pop-up address book
\r
11292 with mouse support
\r
11295 #include <stdio.h>
\r
11296 #include <stdlib.h>
\r
11298 #include <string.h>
\r
11299 #include <fcntl.h>
\r
11300 #include <sys\stat.h>
\r
11302 #include <conio.h>
\r
11303 #include <graphics.h>
\r
11304 #include <bios.h>
\r
11306 /* left cannot be less than 3 */
\r
11309 /* Data structure for records */
\r
11313 char company[31];
\r
11314 char address[31];
\r
11319 char telephone[16];
\r
11324 extern char fpath[];
\r
11326 static char scr[4000];
\r
11328 static char sbuff[2000];
\r
11333 union REGS inreg,outreg;
\r
11336 Function prototypes
\r
11338 void FATAL(char *);
\r
11339 void OPENDATA(void);
\r
11340 void CONTINUE(void);
\r
11341 void EXPORT_MULTI(void);
\r
11342 void GETDATA(int);
\r
11343 int GETOPT(void);
\r
11344 void DISPDATA(void);
\r
11345 void ADD_REC(void);
\r
11346 void PRINT_MULTI(void);
\r
11347 void SEARCH(void);
\r
11350 int GET_MOUSE(int *buttons)
\r
11353 int86(0x33,&inreg,&outreg);
\r
11354 *buttons = outreg.x.bx;
\r
11355 return outreg.x.ax;
\r
11358 void MOUSE_CURSOR(int status)
\r
11360 /* Status = 0 cursor off */
\r
11361 /* 1 cursor on */
\r
11363 inreg.x.ax = 2 - status;
\r
11364 int86(0x33,&inreg,&outreg);
\r
11367 int MOUSE_LOCATION(int *x, int *y)
\r
11370 int86(0x33,&inreg,&outreg);
\r
11372 *x = outreg.x.cx / 8;
\r
11373 *y = outreg.x.dx / 8;
\r
11375 return outreg.x.bx;
\r
11388 result = MOUSE_LOCATION(&x,&y);
\r
11391 if (x >= 52 && x <= 53 && y >= 7 && y <= 15)
\r
11393 if (x >= 4 && x <= 40 && y >= 7 && y <= 14)
\r
11396 if (x >= 4 && x <= 40 && y == 15)
\r
11400 while(!bioskey(1));
\r
11402 result = bioskey(0);
\r
11403 x = result & 0xff;
\r
11406 result = result >> 8;
\r
11410 while(result < 0 || result > 8);
\r
11414 void setvideo(unsigned char mode)
\r
11416 /* Sets the video display mode and clears the screen */
\r
11418 inreg.h.al = mode;
\r
11419 inreg.h.ah = 0x00;
\r
11420 int86(0x10, &inreg, &outreg);
\r
11424 int activepage(void)
\r
11426 /* Returns the currently selected video display page */
\r
11428 union REGS inreg,outreg;
\r
11430 inreg.h.ah = 0x0F;
\r
11431 int86(0x10, &inreg, &outreg);
\r
11432 return(outreg.h.bh);
\r
11435 void print(char *str)
\r
11438 Prints characters only directly to the current display page
\r
11439 starting at the current cursor position. The cursor is not
\r
11441 This function assumes a colour display card. For use with a
\r
11442 monochrome display card change 0xB800 to read 0xB000
\r
11451 page = activepage();
\r
11452 curr_cursor(&row,&col);
\r
11454 offset = page * 4000 + row * 160 + col * 2;
\r
11456 ptr = MK_FP(0xB800,offset);
\r
11466 void TRUESHADE(int lef, int top, int right, int bottom)
\r
11470 /* True Shading of a screen block */
\r
11472 gettext(lef,top,right,bottom,sbuff);
\r
11473 for(n = 1; n < 2000; n+= 2)
\r
11475 puttext(lef,top,right,bottom,sbuff);
\r
11478 void DBOX(int l, int t, int r, int b)
\r
11480 /* Draws a double line box around the described area */
\r
11486 for(n = 1; n < r - l; n++)
\r
11494 for (n = t + 1; n < b; n++)
\r
11503 for(n = 1; n < r - l; n++)
\r
11512 int INPUT(char *text,unsigned length)
\r
11514 /* Receive a string from the operator */
\r
11516 unsigned key_pos;
\r
11518 unsigned start_row;
\r
11519 unsigned start_col;
\r
11524 curr_cursor(&start_row,&start_col);
\r
11527 end = strlen(text);
\r
11530 key = bioskey(0);
\r
11531 if ((key & 0xFF) == 0)
\r
11536 while(key_pos < end)
\r
11538 cursor(start_row,start_col + key_pos);
\r
11544 cursor(start_row,start_col);
\r
11547 if ((key == 75) && (key_pos > 0))
\r
11550 cursor(start_row,start_col + key_pos);
\r
11553 if ((key == 77) && (key_pos < end))
\r
11556 cursor(start_row,start_col + key_pos);
\r
11561 p = text + key_pos;
\r
11570 cursor(start_row,start_col);
\r
11573 if ((key_pos > 0) && (key_pos == end))
\r
11575 cursor(start_row,start_col + key_pos);
\r
11580 key = key & 0xFF;
\r
11581 if (key == 13 || key == 27)
\r
11584 if ((key == 8) && (key_pos > 0))
\r
11588 text[key_pos--] = '\0';
\r
11589 strcpy(temp,text);
\r
11590 p = text + key_pos + 2;
\r
11592 strcpy(text,temp);
\r
11593 cursor(start_row,start_col);
\r
11594 cprintf("%-*.*s",length,length,text);
\r
11596 cursor(start_row,start_col + key_pos);
\r
11599 if ((key > 31) && (key_pos < length) &&
\r
11600 (start_col + key_pos < 80))
\r
11602 if (key_pos <= end)
\r
11604 p = text + key_pos;
\r
11605 memmove(p+1,p,end - key_pos);
\r
11606 if (end < length)
\r
11608 text[end] = '\0';
\r
11610 text[key_pos++] = (char)key;
\r
11611 if (key_pos > end)
\r
11614 text[end] = '\0';
\r
11616 cursor(start_row,start_col);
\r
11617 cprintf("%-*.*s",length,length,text);
\r
11618 cursor(start_row,start_col + key_pos);
\r
11622 text[end] = '\0';
\r
11626 void FATAL(char *error)
\r
11628 /* A fatal error has occured */
\r
11630 printf("\nFATAL ERROR: %s",error);
\r
11636 /* Check for existence of data file and if not create it */
\r
11637 /* otherwise open it for reading/writing at end of file */
\r
11639 handle = open(fpath,O_RDWR,S_IWRITE);
\r
11641 if (handle == -1)
\r
11643 handle = open(fpath,O_RDWR|O_CREAT,S_IWRITE);
\r
11644 if (handle == -1)
\r
11645 FATAL("Unable to create data file");
\r
11647 /* Read in first rec */
\r
11648 read(handle,&rec,recsize);
\r
11656 void GETDATA(int start)
\r
11658 /* Get address data from operator */
\r
11660 textcolor(BLACK);
\r
11661 textbackground(GREEN);
\r
11665 print("Company ");
\r
11667 print("Address ");
\r
11673 print("County ");
\r
11675 print("Post Code ");
\r
11677 print("Telephone ");
\r
11683 case 0: gotoxy(left + 10,8);
\r
11684 if(INPUT(rec.name,30) == 27)
\r
11686 case 1: gotoxy(left + 10,9);
\r
11687 if(INPUT(rec.company,30) == 27)
\r
11689 case 2: gotoxy(left + 10,10);
\r
11690 if(INPUT(rec.address,30) == 27)
\r
11692 case 3: gotoxy(left + 10,11);
\r
11693 if(INPUT(rec.area,30) == 27)
\r
11695 case 4: gotoxy(left + 10,12);
\r
11696 if(INPUT(rec.town,30) == 27)
\r
11698 case 5: gotoxy(left + 10,13);
\r
11699 if(INPUT(rec.county,30) == 27)
\r
11701 case 6: gotoxy(left + 10,14);
\r
11702 if(INPUT(rec.post,12) == 27)
\r
11704 case 7: gotoxy(left + 10,15);
\r
11705 if(INPUT(rec.telephone,15) == 27)
\r
11707 case 8: gotoxy(left + 10,16);
\r
11708 INPUT(rec.fax,15);
\r
11711 textcolor(WHITE);
\r
11712 textbackground(RED);
\r
11713 gotoxy(left + 23,21);
\r
11719 /* Display address data */
\r
11720 textcolor(BLACK);
\r
11721 textbackground(GREEN);
\r
11723 cprintf("Name %-30.30s",rec.name);
\r
11725 cprintf("Company %-30.30s",rec.company);
\r
11727 cprintf("Address %-30.30s",rec.address);
\r
11729 cprintf("Area %-30.30s",rec.area);
\r
11731 cprintf("Town %-30.30s",rec.town);
\r
11733 cprintf("County %-30.30s",rec.county);
\r
11735 cprintf("Post Code %-30.30s",rec.post);
\r
11737 cprintf("Telephone %-30.30s",rec.telephone);
\r
11739 cprintf("Fax %-30.30s",rec.fax);
\r
11742 int LOCATE(char *text)
\r
11748 /* Read rec into memory */
\r
11749 result = read(handle,&rec,recsize);
\r
11752 /* Scan rec for matching data */
\r
11753 if (strstr(strupr(rec.name),text) != NULL)
\r
11755 if (strstr(strupr(rec.company),text) != NULL)
\r
11757 if (strstr(strupr(rec.address),text) != NULL)
\r
11759 if (strstr(strupr(rec.area),text) != NULL)
\r
11761 if (strstr(strupr(rec.town),text) != NULL)
\r
11763 if (strstr(strupr(rec.county),text) != NULL)
\r
11765 if (strstr(strupr(rec.post),text) != NULL)
\r
11767 if (strstr(strupr(rec.telephone),text) != NULL)
\r
11769 if (strstr(strupr(rec.fax),text) != NULL)
\r
11773 while(result > 0);
\r
11782 textcolor(WHITE);
\r
11783 textbackground(RED);
\r
11784 cprintf("Enter data to search for ");
\r
11785 strcpy(stext,"");
\r
11790 cprintf("%70c",32);
\r
11794 textcolor(WHITE);
\r
11795 textbackground(RED);
\r
11796 cprintf("Searching for %s Please Wait....",stext);
\r
11798 /* Locate start of file */
\r
11799 lseek(handle,0,SEEK_SET);
\r
11800 result = LOCATE(stext);
\r
11804 cprintf("%70c",32);
\r
11805 gotoxy(left + 27,21);
\r
11806 cprintf("NO MATCHING RECORDS");
\r
11807 gotoxy(left + 24,22);
\r
11808 cprintf("Press RETURN to Continue");
\r
11811 cprintf("%70c",32);
\r
11813 cprintf("%70c",32);
\r
11817 lseek(handle,0 - recsize,SEEK_CUR);
\r
11818 read(handle,&rec,recsize);
\r
11821 textcolor(WHITE);
\r
11822 textbackground(RED);
\r
11824 cprintf("%70c",32);
\r
11825 textcolor(BLACK);
\r
11826 textbackground(GREEN);
\r
11834 curpos = tell(handle) - recsize;
\r
11836 result = LOCATE(stext);
\r
11837 textcolor(WHITE);
\r
11838 textbackground(RED);
\r
11841 gotoxy(left + 24,21);
\r
11842 cprintf("NO MORE MATCHING RECORDS");
\r
11843 gotoxy(left + 24,22);
\r
11844 cprintf("Press RETURN to Continue");
\r
11847 cprintf("%70c",32);
\r
11849 cprintf("%70c",32);
\r
11850 lseek(handle,curpos,SEEK_SET);
\r
11851 read(handle,&rec,recsize);
\r
11856 lseek(handle,0 - recsize,SEEK_CUR);
\r
11857 read(handle,&rec,recsize);
\r
11860 textcolor(WHITE);
\r
11861 textbackground(RED);
\r
11863 cprintf("%70c",32);
\r
11866 textcolor(BLACK);
\r
11867 textbackground(GREEN);
\r
11870 void PRINT_MULTI()
\r
11873 char destination[60];
\r
11883 textcolor(WHITE);
\r
11884 textbackground(RED);
\r
11885 gotoxy(left + 23,21);
\r
11886 cprintf("Enter selection criteria");
\r
11888 /* Clear existing rec details */
\r
11889 memset(&rec,0,recsize);
\r
11894 textcolor(WHITE);
\r
11895 textbackground(RED);
\r
11897 cprintf("Enter report destination PRN");
\r
11898 strcpy(destination,"PRN");
\r
11900 cprintf("Enter Address length in lines 18");
\r
11901 strcpy(text,"18");
\r
11902 gotoxy(left + 25,21);
\r
11903 INPUT(destination,40);
\r
11904 gotoxy(left +30,22);
\r
11907 cprintf("%72c",32);
\r
11909 cprintf("%72c",32);
\r
11911 total_lines = atoi(text) - 6;
\r
11912 if (total_lines < 0)
\r
11915 fp = fopen(destination,"w+");
\r
11919 cprintf("Unable to print to %s",destination);
\r
11921 cprintf("Press RETURN to Continue");
\r
11924 cprintf("%78c",32);
\r
11929 /* Locate start of file */
\r
11930 lseek(handle,0,SEEK_SET);
\r
11934 /* Read rec into memory */
\r
11935 result = read(handle,&buffer,recsize);
\r
11939 /* Scan rec for matching data */
\r
11941 if (stricmp(buffer.name,rec.name))
\r
11943 if (*rec.company)
\r
11944 if (stricmp(buffer.company,rec.company))
\r
11946 if (*rec.address)
\r
11947 if (stricmp(buffer.address,rec.address))
\r
11950 if (stricmp(buffer.area,rec.area))
\r
11953 if (stricmp(buffer.town,rec.town))
\r
11956 if (stricmp(buffer.county,rec.county))
\r
11959 if (stricmp(buffer.post,rec.post))
\r
11961 if (*rec.telephone)
\r
11962 if (stricmp(buffer.telephone,rec.telephone))
\r
11965 if (stricmp(buffer.fax,rec.fax))
\r
11969 blanks = total_lines;
\r
11984 fprintf(fp,"%s\n",buffer.name);
\r
11985 p = buffer.company;
\r
11999 fprintf(fp,"%s\n",buffer.company);
\r
12000 p = buffer.address;
\r
12015 fprintf(fp,"%s\n",buffer.address);
\r
12030 fprintf(fp,"%s\n",buffer.area);
\r
12045 fprintf(fp,"%s\n",buffer.town);
\r
12046 p = buffer.county;
\r
12061 fprintf(fp,"%s\n",buffer.county);
\r
12076 fprintf(fp,"%s\n",buffer.post);
\r
12079 fprintf(fp,"\n");
\r
12085 while(result > 0);
\r
12087 lseek(handle,0,SEEK_SET);
\r
12088 read(handle,&rec,recsize);
\r
12092 void EXPORT_MULTI()
\r
12095 char destination[60];
\r
12100 textcolor(WHITE);
\r
12101 textbackground(RED);
\r
12102 gotoxy(left + 23,21);
\r
12103 cprintf("Enter selection criteria");
\r
12105 /* Clear existing rec details */
\r
12106 memset(&rec,0,recsize);
\r
12111 textcolor(WHITE);
\r
12112 textbackground(RED);
\r
12114 cprintf("Enter export file address.txt");
\r
12115 strcpy(destination,"address.txt");
\r
12116 gotoxy(left + 18,21);
\r
12117 INPUT(destination,59);
\r
12119 cprintf("%70c",32);
\r
12121 fp = fopen(destination,"w+");
\r
12125 cprintf("Unable to print to %s",destination);
\r
12127 cprintf("Press RETURN to Continue");
\r
12130 cprintf("%78c",32);
\r
12134 /* Locate start of file */
\r
12135 lseek(handle,0,SEEK_SET);
\r
12139 /* Read rec into memory */
\r
12140 result = read(handle,&buffer,recsize);
\r
12144 /* Scan rec for matching data */
\r
12146 if (stricmp(buffer.name,rec.name))
\r
12148 if (*rec.company)
\r
12149 if (stricmp(buffer.company,rec.company))
\r
12151 if (*rec.address)
\r
12152 if (stricmp(buffer.address,rec.address))
\r
12155 if (stricmp(buffer.area,rec.area))
\r
12158 if (stricmp(buffer.town,rec.town))
\r
12161 if (stricmp(buffer.county,rec.county))
\r
12164 if (stricmp(buffer.post,rec.post))
\r
12166 if (*rec.telephone)
\r
12167 if (stricmp(buffer.telephone,rec.telephone))
\r
12170 if (stricmp(buffer.fax,rec.fax))
\r
12174 fprintf(fp,"\"%s\",",buffer.name);
\r
12175 fprintf(fp,"\"%s\",",buffer.company);
\r
12176 fprintf(fp,"\"%s\",",buffer.address);
\r
12177 fprintf(fp,"\"%s\",",buffer.area);
\r
12178 fprintf(fp,"\"%s\",",buffer.town);
\r
12179 fprintf(fp,"\"%s\",",buffer.county);
\r
12180 fprintf(fp,"\"%s\",",buffer.post);
\r
12181 fprintf(fp,"\"%s\",",buffer.telephone);
\r
12182 fprintf(fp,"\"%s\"\n",buffer.fax);
\r
12188 while(result > 0);
\r
12190 lseek(handle,0,SEEK_SET);
\r
12191 read(handle,&rec,recsize);
\r
12205 print("Select option (F2 - F10)");
\r
12207 print("F2 Next record");
\r
12209 print("F3 Previous record");
\r
12211 print("F4 Amend record");
\r
12213 print("F5 Add new record");
\r
12215 print("F6 Search");
\r
12217 print("F7 Continue search");
\r
12219 print("F8 Print address labels");
\r
12221 print("F9 Export records");
\r
12223 print("F10 Exit");
\r
12225 option = GETOPT();
\r
12230 case 0 : /* Next rec */
\r
12231 result = read(handle,&rec,recsize);
\r
12234 lseek(handle,0,SEEK_SET);
\r
12235 result = read(handle,&rec,recsize);
\r
12240 case 1 : /* Previous rec */
\r
12241 result = lseek(handle,0 - recsize * 2,SEEK_CUR);
\r
12242 if (result <= -1)
\r
12243 lseek(handle,0 - recsize,SEEK_END);
\r
12244 result = read(handle,&rec,recsize);
\r
12248 case 3 : /* Add rec */
\r
12249 lseek(handle,0,SEEK_END);
\r
12250 memset(&rec,0,recsize);
\r
12253 case 2 : /* Amend current rec */
\r
12258 if (*rec.company)
\r
12261 if (*rec.address)
\r
12276 if (*rec.telephone)
\r
12281 result = tell(handle);
\r
12282 lseek(handle,0,SEEK_END);
\r
12283 end = tell(handle);
\r
12285 /* Back to original position */
\r
12286 lseek(handle,result,SEEK_SET);
\r
12288 /* If not at end of file, && !new rewind one
\r
12290 if (result != end || ! new)
\r
12291 result = lseek(handle,0 -
\r
12292 recsize,SEEK_CUR);
\r
12293 result = tell(handle);
\r
12294 gotoxy(left + 22,21);
\r
12295 print(" Enter address details ");
\r
12297 if (*rec.name || *rec.company)
\r
12298 result = write(handle,&rec,recsize);
\r
12301 case 4 : /* Search */
\r
12302 gotoxy(left + 22,21);
\r
12307 case 5 : /* Continue */
\r
12308 gotoxy(left + 22,21);
\r
12313 case 6 : /* Print */
\r
12314 gotoxy(left + 22,21);
\r
12319 case 7 : /* Export */
\r
12320 gotoxy(left + 22,21);
\r
12325 case 8 : /* Exit */
\r
12328 default: /* Amend current rec */
\r
12333 if (*rec.company)
\r
12336 if (*rec.address)
\r
12351 if (*rec.telephone)
\r
12356 result = tell(handle);
\r
12357 lseek(handle,0,SEEK_END);
\r
12358 end = tell(handle);
\r
12360 /* Back to original position */
\r
12361 lseek(handle,result,SEEK_SET);
\r
12363 /* If not at end of file, && !new rewind one
\r
12365 if (result != end || ! new)
\r
12366 result = lseek(handle,0 -
\r
12367 recsize,SEEK_CUR);
\r
12368 result = tell(handle);
\r
12369 gotoxy(left + 22,21);
\r
12370 print(" Enter address details ");
\r
12371 GETDATA(option - 17);
\r
12372 if (*rec.name || *rec.company)
\r
12373 result = write(handle,&rec,recsize);
\r
12380 while(option != 8);
\r
12385 gettext(1,1,80,25,scr);
\r
12387 textbackground(WHITE);
\r
12388 textcolor(BLACK);
\r
12390 recsize = sizeof(data);
\r
12394 TRUESHADE(left,3,79,5);
\r
12395 window(left - 2,2 ,78, 4);
\r
12396 textcolor(YELLOW);
\r
12397 textbackground(MAGENTA);
\r
12399 DBOX(left - 3, 1, 77, 3);
\r
12401 print("Servile Software PC ADDRESS BOOK 5.2
\r
12404 TRUESHADE(left,8,left + 43,18);
\r
12405 window(left - 2,7 , left + 42, 17);
\r
12406 textcolor(BLACK);
\r
12407 textbackground(GREEN);
\r
12409 DBOX(left - 3, 6, left + 41, 16);
\r
12411 TRUESHADE(left + 48,8,79,18);
\r
12412 window(left + 46, 7 , 78, 17);
\r
12413 textbackground(BLUE);
\r
12414 textcolor(YELLOW);
\r
12416 DBOX(left + 45,6,77,16);
\r
12418 TRUESHADE(left ,21,79,24);
\r
12419 window(left - 2, 20 , 78, 23);
\r
12420 textbackground(RED);
\r
12421 textcolor(WHITE);
\r
12423 DBOX(left - 3,19,77,22);
\r
12425 window(1,1,80,25);
\r
12426 textcolor(BLACK);
\r
12427 textbackground(GREEN);
\r
12433 puttext(1,1,80,25,scr);
\r
12438 INTERFACING C WITH CLIPPER
\r
12440 The Clipper programming language is a popular xBase environment for the
\r
12441 PC. However, it lacks many of the facilities available to programmers of
\r
12442 other languages, and it is quite slow compared to C. Because of this
\r
12443 there are a large number of third party add-on libraries available for
\r
12444 Clipper which provide the facilities lacked.
\r
12446 As a programmer you probably want to write your own library for Clipper,
\r
12447 or perhaps individual functions to cater for circumstances which Clipper
\r
12448 cannot handle, such as high resolution graphics.
\r
12450 Throughout this section, Clipper refers to the Summer `87 Clipper
\r
12451 compiler, although initial tests show that the functions described here
\r
12452 work perfectly well with the new Clipper 5 compiler also, we are not in a
\r
12453 position to guarrantee success!
\r
12456 COMPILING AND LINKING
\r
12457 The Clipper extend functions allow user defined functions to be written
\r
12458 in C, linked with and used by the Clipper application. The first
\r
12459 problem a programmer must address when writing functions in C to link
\r
12460 with a Clipper application is that of the C compiler's run time
\r
12463 If one is writing functions with Microsoft C, then most of the required
\r
12464 run time library functions will be found in the Clipper.lib and
\r
12465 Extend.lib libraries which are part of Clipper.
\r
12467 If, however, one is using a different C compiler, such as Borland's
\r
12468 Turbo C then the run time library routines must be supplied on the link
\r
12471 All C functions must be compiled using the large memory model the
\r
12472 following line is used with Microsoft C
\r
12475 cl /c /AL /Zl /Oalt /FPa /Gs <program.c>
\r
12477 and this compile line may be used with Turbo C
\r
12479 tcc -c -ml <program>
\r
12481 simply substitute <program> for the program name to be compiled.
\r
12483 Having compiled a C function it must be linked in with the application.
\r
12484 If the C function was compiled with Microsoft C then the link line will
\r
12485 look a little like this;
\r
12488 LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend
\r
12490 If the C function was linked with another C compiler you will also need
\r
12491 to link in the C run time libraries, for example to link in the Turbo C
\r
12492 large memory mode library use the following link line;
\r
12495 LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend cl
\r
12497 If one is using a number of separately compiled C functions it is a good
\r
12498 idea to collect them in a library. If you are using Microsoft C then
\r
12499 you can simply create the library by using Microsoft Lib.exe with
\r
12500 the following command line;
\r
12503 LIB mylib +prog1 +prog2, NUL, NUL
\r
12505 This tells the librarian to add prog1.obj and prog2.obj to a library
\r
12506 called mylib.lib, creating it if it does not exist. The NUL
\r
12507 parameter is for supressing the listing file.
\r
12510 If you have been using another C compiler you should copy the C large
\r
12511 memory model run time library before adding your functions to it for
\r
12515 COPY C:\TURBOC\LIB\cl.lib mylib.lib
\r
12516 LIB mylib +prog1 +prog2, NUL, NUL
\r
12518 Then when you link your Clipper application you will use a link line
\r
12521 LINK /SE:500 /NOE myprog,,,Clipper Extend Mylib
\r
12523 Often when linking C functions with Clipper applications link errors
\r
12524 will occur such as those shown below;
\r
12527 Microsoft (R) Overlay Linker Version 3.65
\r
12528 Copyright (C) Microsoft Corp 1983-1988. All rights reserved.
\r
12531 LINK : error L2029: Unresolved externals:
\r
12534 FIWRQQ in file(s):
\r
12536 FIDRQQ in file(s):
\r
12539 There were 2 errors detected
\r
12542 Example Link Errors
\r
12544 The errors shown here are `Unresolved externals', that is they
\r
12545 are references to functions which are not found in any of the object
\r
12546 modules or libraries specified on the link line. These occur because
\r
12547 the C compilers often scatter functions and variables through a number
\r
12548 of libraries. In tracking these functions down use may be made of
\r
12549 the Microsoft librarian list file option. If you run Lib.Exe on the
\r
12550 Turbo C `emu.lib' library file and specify a listing file as follows;
\r
12555 The librarian will create an ascii file which contains the names of
\r
12556 each object module contained in the specified library file, and the names
\r
12557 of each function and public variable declared in each object module, as
\r
12558 shown in this listing of Borland's EMU.LIB library.
\r
12561 e086_Entry........EMU086 e086_Shortcut.....EMU086
\r
12562 e087_Entry........EMU087 e087_Shortcut.....EMU087
\r
12563 FIARQQ............EMUINIT FICRQQ............EMUINIT
\r
12564 FIDRQQ............EMUINIT FIERQQ............EMUINIT
\r
12565 FISRQQ............EMUINIT FIWRQQ............EMUINIT
\r
12566 FJARQQ............EMUINIT FJCRQQ............EMUINIT
\r
12567 FJSRQQ............EMUINIT __EMURESET........EMUINIT
\r
12570 EMUINIT Offset: 00000010H Code and data size: 1a2H
\r
12571 FIARQQ FICRQQ FIDRQQ FIERQQ
\r
12572 FISRQQ FIWRQQ FJARQQ FJCRQQ
\r
12573 FJSRQQ __EMURESET
\r
12575 EMU086 Offset: 00000470H Code and data size: 2630H
\r
12576 e086_Entry e086_Shortcut
\r
12578 EMU087 Offset: 00003200H Code and data size: 417H
\r
12579 e087_Entry e087_Shortcut
\r
12583 Receiving Parameters
\r
12585 Clipper provides six different functions for receiving parameters in a C
\r
12586 function. These functions are;
\r
12589 Receive a string char * _parc(int,[int])
\r
12590 Receive a Date string char * _pards(int,[int])
\r
12591 Receive a logical int _parl(int,[int])
\r
12592 Receive an integer int _parni(int,[int])
\r
12593 Receive a long long _parnl(int,[int])
\r
12594 Receive a double double _parnd(int,[int])
\r
12598 To illustrate simple parameter receiving in a C function I offer
\r
12599 the following simple C function which receives two numeric parameters
\r
12600 from the calling Clipper program, and uses these two numeric
\r
12601 parameters to set the size of the cursor.
\r
12604 #include <nandef.h> /* Clipper header files */
\r
12605 #include <extend.h>
\r
12606 #include <dos.h> /* Header file to define REGS */
\r
12608 CLIPPER s_curset()
\r
12610 /* Demonstration function to set cursor shape */
\r
12612 union REGS inreg,outreg;
\r
12614 inreg.h.ah = 0x01;
\r
12615 inreg.h.ch = _parni(1); /* Get integer parameter 1 */
\r
12616 inreg.h.cl = _parni(2); /* Get integer parameter 2 */
\r
12617 int86(0x10,&inreg,&outreg);
\r
12618 _ret(); /* Return to Clipper */
\r
12621 Clipper provides four more functions for dealing with received
\r
12624 _parclen(int,[int]) which returns the length of a string including
\r
12625 imbedded `\0's, _parcsiz(int[int]) which returns the length of a
\r
12626 character string passed by reference from Clipper, _parinfa(int,[int])
\r
12627 which returns the type of a specified array element or the length of
\r
12628 an array, and finally _parinfo(int) whic returns the type of a
\r
12631 The following example function uses _parinfa() to determine both the
\r
12632 length of an array passed from a Clipper program, and the type of each
\r
12633 element in the array. The function then returns to Clipper an integer
\r
12634 representing the number of defined elements in the array.
\r
12638 #include <nandef.h>
\r
12639 #include <extend.h>
\r
12648 /* Return the number of defined elements in an array */
\r
12649 /* From Clipper use defined = s_alen(arr) */
\r
12651 total = _parinfa(1,0); /* Get declared number of elements in
\r
12656 for (n = 1; n <= total; n++){
\r
12657 type = _parinfa(1,n); /* Get array parameter type */
\r
12661 _retni(defined); /* Return an integer to Clipper
\r
12666 This function goes one step further to return the mean average of
\r
12667 all numeric values in an array. Notice the use of _parnd() to
\r
12668 retrieve the numeric values as doubles. You may find that because of
\r
12669 the floating point arithmetic in this function that it will only
\r
12670 work if compiled with Microsoft C.
\r
12673 #include <nandef.h>
\r
12674 #include <extend.h>
\r
12684 /* Return the mean average value of numbers in array */
\r
12685 /* From Clipper use mean = s_aave(arr)
\r
12688 total = _parinfa(1,0); /* Get declared number of
\r
12693 for (n = 1; n <= total; n++){ /* Determine number of defined
\r
12695 type = _parinfa(1,n); /* elements */
\r
12702 for (n = 1; n <= total; n++){
\r
12703 type = _parinfa(1,n);
\r
12704 if (type == 2) /* Only sum numeric values
\r
12706 sum += _parnd(1,n);
\r
12708 _retnd(sum / defined); /* Return a double to
\r
12716 The Clipper manual lists seven functions for returning from a
\r
12717 function written in another language. These return functions for C are as
\r
12720 character _retc(char *)
\r
12721 date _retds(char *)
\r
12722 logical _retl(int)
\r
12723 numeric (int) _retni(int)
\r
12724 numeric (long) _retnl(long)
\r
12725 numeric (double) _retnd(double)
\r
12726 nothing _ret(void)
\r
12728 Omitted from the Clipper manual is the information that you may
\r
12729 return different types of value back from a function! For example, you
\r
12730 may wish to return a character string under normal circumstances, fine
\r
12731 use _retc(). On error occurences however you can return a logical using
\r
12732 _retl(). The Clipper program will assign the received value to the
\r
12733 receiving variable in the correct manner.
\r
12735 The following simple C function returns a random number. Notice the use
\r
12736 of integers which limits the range of the function to +-32767. For
\r
12737 larger values you should use longs instead of integers.
\r
12740 #include <nandef.h>
\r
12741 #include <extend.h>
\r
12744 CLIPPER s_random()
\r
12746 /* Returns a random number between 0 and param1 - 1 */
\r
12747 /* From Clipper use x = s_random(param1) */
\r
12752 param1 = _parni(1);
\r
12754 x = rand() % param1;
\r
12759 This function receives a string from Clipper, and passes back an upper
\r
12760 case copy of the string, leaving the original unchanged. The maximum
\r
12761 length of the string which can be processed is determined by the size
\r
12762 of target[], here set to 5000 characters.
\r
12765 #include <nandef.h>
\r
12766 #include <extend.h>
\r
12768 CLIPPER s_upper()
\r
12770 /* Returns an upper case copy of string */
\r
12771 /* From Clipper use ? s_upper("this is a string")
\r
12776 char target[5000];
\r
12779 string = _parc(1);
\r
12785 *q++ = toupper(*string);
\r
12794 This function may be used to change the current DOS directory. If it
\r
12795 is successful it returns .T. to the calling Clipper program,
\r
12796 otherwise it returns .F.
\r
12798 #include <nandef.h>
\r
12799 #include <extend.h>
\r
12802 CLIPPER s_chdir()
\r
12804 /* Attempts to change the current DOS directory */
\r
12805 /* From Clipper use result = s_chdir(path) */
\r
12807 union REGS inreg,outreg;
\r
12808 struct SREGS segreg;
\r
12813 path = _parc(1); /* Retrieve string from Clipper
\r
12816 inreg.h.ah = 0x3b;
\r
12817 segreg.ds = FP_SEG(path);
\r
12818 inreg.x.dx = FP_OFF(path);
\r
12819 intdosx(&inreg,&outreg,&segreg);
\r
12824 _retl(0); /* Return logical .F. back to Clipper */
\r
12826 _retl(1); /* Return logical .T. back to Clipper */
\r
12831 Avoiding Unresolved Externals
\r
12833 As we have already seen, a common problem plaguing the programmer
\r
12834 interfacing C functions with Clipper programs is Unresolved Externals.
\r
12836 The following example C function called s_print() will not link
\r
12839 #include <nandef.h>
\r
12840 #include <extend.h>
\r
12841 #include <stdio.h>
\r
12843 CLIPPER s_print()
\r
12849 printf("\nI received %s from Clipper.\n",x);
\r
12855 The linker gives you the following reply;
\r
12857 Microsoft (R) Overlay Linker Version 3.65
\r
12858 Copyright (C) Microsoft Corp 1983-1988. All rights reserved.
\r
12860 M:SLIB.LIB(IOERROR) : error L2025: __doserrno : symbol defined more
\r
12862 pos: 16C6F Record type: 53C6
\r
12864 LINK : error L2029: Unresolved externals:
\r
12868 __RealCvtVector in file(s):
\r
12869 M:SLIB.LIB(REALCVT)
\r
12870 _abort in file(s):
\r
12871 M:SLIB.LIB(CVTFAK)
\r
12873 There were 3 errors detected
\r
12876 The error L2025 `symbol defined more than once' can in this case be
\r
12877 ignored. However, the unresolved externals `RealCvtVector' and
\r
12878 `abort' cannot be ignored. These two functions are referenced by the
\r
12879 function printf() which has been included in the C function. The
\r
12880 answer is to use as few of the compiler's run time library functions
\r
12881 as possible, use ROM calls instead with INT86() and INTDOSX() etc.
\r
12884 Adding High Resolution Graphics To Clipper With C
\r
12886 The most annoying omission from Clipper, in my opinion, is the lack of
\r
12887 high resolution graphics facilities. The following functions, written in
\r
12888 Turbo C, provide high resolution graphics to Clipper.
\r
12890 First we require a means to change the video display mode to a high
\r
12891 resolution graphics mode, and back to text mode. The IBM PC BIOS provides
\r
12892 the means for this and can be called from C as follows;
\r
12897 /* Servile Software Library For Clipper */
\r
12899 #include <nandef.h>
\r
12900 #include <extend.h>
\r
12903 CLIPPER s_smode()
\r
12905 /* Set Video Mode */
\r
12906 /* From Clipper use s_smode(mode) */
\r
12908 union REGS inreg,outreg;
\r
12910 inreg.h.al = _parni(1);
\r
12911 inreg.h.ah = 0x00;
\r
12912 int86 (0x10, &inreg, &outreg);
\r
12915 /* 1 40x25 colour text
\r
12917 3 80x25 colour text
\r
12918 4 320x200 4 colour graphics
\r
12919 5 320x200 4 colour graphics colour burst off
\r
12920 6 640x200 2 colour graphics
\r
12926 Having set the computer into graphics mode, how about setting pixels to a
\r
12927 specified colour?
\r
12930 /* Servile Software Library For Clipper */
\r
12932 #include <nandef.h>
\r
12933 #include <extend.h>
\r
12939 union REGS inreg,outreg;
\r
12941 /* Sets a pixel at the specified coordinates to the specified
\r
12944 inreg.h.bh = 0x00;
\r
12945 inreg.x.cx = _parni(1);
\r
12946 inreg.x.dx = _parni(2);
\r
12947 inreg.h.al = _parni(3);
\r
12948 inreg.h.ah = 0x0C;
\r
12949 int86(0x10, &inreg, &outreg);
\r
12952 Line drawing and circles are handled by these two functions;
\r
12955 /* Servile Software Library For Clipper */
\r
12957 #include <nandef.h>
\r
12958 #include <extend.h>
\r
12964 union REGS inreg,outreg;
\r
12966 /* Draws a straight line from (a,b) to (c,d) in colour col */
\r
13034 inreg.h.al = (unsigned char)col;
\r
13035 inreg.h.bh = 0x00;
\r
13036 inreg.h.ah = 0x0C;
\r
13037 for (i = 0; i <= m; i++)
\r
13039 inreg.x.cx = (unsigned int)(a);
\r
13040 inreg.x.dx = (unsigned int)(b);
\r
13041 int86(0x10, &inreg, &outreg);
\r
13059 This circle drawing function uses in-line assembler to speed up the
\r
13060 drawing process. It can easily be replaced with inreg and outreg
\r
13061 parameters as in the other functions, or the other functions can be
\r
13062 changed to in-line assembler. Both methods are shown to illustrate
\r
13063 different ways of achieving the same result.
\r
13066 /* Servile Software Library For Clipper */
\r
13068 #include <nandef.h>
\r
13069 #include <extend.h>
\r
13073 void plot(int x, int y, unsigned char colour)
\r
13075 asm mov al , colour;
\r
13079 asm mov ah , 0Ch;
\r
13085 /* Returns current video mode and number of columns in ncols
\r
13088 asm mov ah , 0Fh;
\r
13094 CLIPPER s_circle()
\r
13101 int startx,endx,x1,starty,endy,y1;
\r
13104 x_centre = _parni(1);
\r
13105 y_centre = _parni(2);
\r
13106 radius = _parni(3);
\r
13107 colour = _parni(4);
\r
13112 if (getmode() == 6)
\r
13118 delta = 3 - 2 * radius;
\r
13120 for(x = 0; x < y; )
\r
13122 starty = y * asp_ratio / 10;
\r
13123 endy = (y + 1) * asp_ratio / 10;
\r
13124 startx = x * asp_ratio / 10;
\r
13125 endx = (x + 1) * asp_ratio / 10;
\r
13127 for(x1 = startx; x1 < endx; ++x1)
\r
13129 plot(x1+x_centre,y+y_centre,colour);
\r
13130 plot(x1+x_centre,y_centre - y,colour);
\r
13131 plot(x_centre - x1,y_centre - y,colour);
\r
13132 plot(x_centre - x1,y + y_centre,colour);
\r
13135 for(y1 = starty; y1 < endy; ++y1)
\r
13137 plot(y1+x_centre,x+y_centre,colour);
\r
13138 plot(y1+x_centre,y_centre - x,colour);
\r
13139 plot(x_centre - y1,y_centre - x,colour);
\r
13140 plot(x_centre - y1,x + y_centre,colour);
\r
13144 delta += 4 * x + 6;
\r
13147 delta += 4*(x-y)+10;
\r
13158 starty = y * asp_ratio / 10;
\r
13159 endy = (y + 1) * asp_ratio / 10;
\r
13160 startx = x * asp_ratio / 10;
\r
13161 endx = (x + 1) * asp_ratio / 10;
\r
13162 for(x1 = startx; x1 < endx; ++x1)
\r
13164 plot(x1+x_centre,y+y_centre,colour);
\r
13165 plot(x1+x_centre,y_centre - y,colour);
\r
13166 plot(x_centre - x1,y_centre - y,colour);
\r
13167 plot(x_centre - x1,y + y_centre,colour);
\r
13170 for(y1 = starty; y1 < endy; ++y1)
\r
13172 plot(y1+x_centre,x+y_centre,colour);
\r
13173 plot(y1+x_centre,y_centre - x,colour);
\r
13174 plot(x_centre - y1,y_centre - x,colour);
\r
13175 plot(x_centre - y1,x + y_centre,colour);
\r
13181 The Clipper facilities for displaying text on the screen, @....SAY and ?
\r
13182 do not work when the monitor is in graphics mode. You then need the
\r
13183 following function to allow text to be displayed in a graphics mode;
\r
13186 /* Servile Software Library For Clipper */
\r
13188 #include <nandef.h>
\r
13189 #include <extend.h>
\r
13192 int sgetmode(int *ncols)
\r
13194 /* Returns current video mode and number of columns in ncols
\r
13197 union REGS inreg,outreg;
\r
13199 inreg.h.ah = 0x0F;
\r
13200 int86(0x10, &inreg, &outreg);
\r
13201 *ncols = outreg.h.ah;
\r
13202 return(outreg.h.al);
\r
13205 void at(int row, int col)
\r
13208 asm mov dh , row;
\r
13209 asm mov dl , col;
\r
13210 asm mov ah , 02h;
\r
13221 unsigned char page;
\r
13222 unsigned char text;
\r
13228 output = _parc(1);
\r
13231 attribute = _parni(4);
\r
13233 asm mov ah , 0Fh;
\r
13235 asm mov page, bh;
\r
13241 while (output[p])
\r
13243 text = output[p++];
\r
13244 asm mov bh , page;
\r
13245 asm mov bl , attribute;
\r
13246 asm mov cx , 01h;
\r
13247 asm mov ah , 09h;
\r
13248 asm mov al , text;
\r
13264 When drawing graphs, it is often required to fill in areas of the graph
\r
13265 in different patterns. This is a graphics function to fill boundered
\r
13266 shapes with a specified hatching pattern providing a means to achieve
\r
13267 more usable graphs;
\r
13271 /* Servile Software Library For Clipper */
\r
13273 #include <nandef.h>
\r
13274 #include <extend.h>
\r
13277 int pixset(int x, int y)
\r
13279 /* Returns the colour of the specified pixel */
\r
13290 /* Fill a boundered shape using a hatch pattern */
\r
13303 int hatch[10][8] = { 255,255,255,255,255,255,255,255,
\r
13304 128,64,32,16,8,4,2,1,
\r
13305 1,2,4,8,16,32,64,128,
\r
13307 238,238,238,238,238,238,238,238,
\r
13308 170,85,170,85,170,85,170,85,
\r
13309 192,96,48,24,12,6,3,1,
\r
13310 62,62,62,0,227,227,227,0,
\r
13311 129,66,36,24,24,36,66,129,
\r
13312 146,36,146,36,146,36,146,36};
\r
13314 /* Patterns for fill, each integer describes a row of dots */
\r
13322 pattern = _parni(4);
\r
13324 mode = getmode();
\r
13336 case 5: maxx = 320;
\r
13341 case 6: maxx = 640;
\r
13344 case 7: maxx = 720;
\r
13347 case 8: maxx = 160;
\r
13351 case 16: maxx = 640;
\r
13355 case 18: maxx = 640;
\r
13362 ya = y; /* Save Origin */
\r
13375 if (hatch[pattern][byn] != 0)
\r
13376 { /* If blank ignore */
\r
13379 if ((bn & hatch[pattern][byn]) == bn)
\r
13381 asm mov al , col;
\r
13385 asm mov ah , 0Ch;
\r
13393 while(!pixset(x,y) && (x > -1));
\r
13400 if ((bn & hatch[pattern][byn]) == bn)
\r
13402 asm mov al , col;
\r
13406 asm mov ah , 0Ch;
\r
13414 while((!pixset(x,y)) && (x <= maxx));
\r
13427 while(!pixset(x,y) && ( y > -1));
\r
13429 /* Now travel downwards */
\r
13437 /* Travel left */
\r
13438 if (hatch[pattern][byn] !=0)
\r
13442 if ((bn & hatch[pattern][byn]) == bn)
\r
13444 asm mov al , col;
\r
13448 asm mov ah , 0Ch;
\r
13456 while(!pixset(x,y) && (x > -1));
\r
13458 /* Back to x origin */
\r
13462 /* Travel right */
\r
13465 if ((bn & hatch[pattern][byn]) == bn)
\r
13467 asm mov al , col;
\r
13471 asm mov ah , 0Ch;
\r
13483 while((!pixset(x,y)) && (x <= maxx));
\r
13492 while((!pixset(x,y)) && (y <= maxy));
\r
13498 SPELL - AN EXAMPLE PROGRAM
\r
13500 It has been said that example programs provide a good way of learning a
\r
13501 new computer language. On that basis the following simple program is
\r
13502 offered as an example of making use of the dynamic memory allocation
\r
13505 This is a spell checker for ASCII (text) documents, written in, and
\r
13506 making use of Borland's Turbo C text graphics facilities for displaying
\r
13510 /* Spell checker for ascii documents */
\r
13511 /* Compile with -mc (compact memory model) and unsigned characters
\r
13515 #include <stdio.h>
\r
13516 #include <conio.h>
\r
13517 #include <fcntl.h>
\r
13520 #include <string.h>
\r
13521 #include <alloc.h>
\r
13522 #include <ctype.h>
\r
13523 #include <stdlib.h>
\r
13524 #include <bios.h>
\r
13526 #include <stat.h>
\r
13530 #define MAXIMUM 15000
\r
13531 #define WORDLEN 20
\r
13532 #define LEFTMARGIN 1
\r
13534 union REGS inreg,outreg;
\r
13537 char *dic[MAXIMUM]; /* Array of text lines */
\r
13543 char *ignore[100];
\r
13558 void AT(int, int);
\r
13559 void BANNER(void);
\r
13560 int COMPARE(void);
\r
13561 void CORRECT(void);
\r
13562 void FATAL(char *);
\r
13563 void FILERR(char *);
\r
13564 void GETDIC(void);
\r
13565 void IGNORE(void);
\r
13566 void INSERT(void);
\r
13567 int MATCHSTR(char *, char *);
\r
13568 void SPELL(void);
\r
13569 void UPDATE(void);
\r
13571 void CURSOR(char status)
\r
13573 /* Toggle cursor display on and off */
\r
13575 union REGS inreg,outreg;
\r
13578 inreg.h.ch = (unsigned char)status;
\r
13580 int86(0x10,&inreg,&outreg);
\r
13583 void DISPLAY(char *text)
\r
13585 /* Display 'text' expanding tabs and newline characters */
\r
13591 case '\n': cputs("\r\n");
\r
13593 case '\t': cputs(" ");
\r
13595 default: putch(*text);
\r
13606 /* Read dictionary into memory */
\r
13613 window(1,22,80,24);
\r
13616 cprintf("Reading Dictionary....");
\r
13621 dicname = searchpath("spell.dic");
\r
13622 handle = open(dicname,O_RDWR);
\r
13624 FILERR("spell.dic");
\r
13626 fp = fdopen(handle,"r");
\r
13628 FILERR("spell.dic");
\r
13632 dic[lastelem] = calloc(WORDLEN,1);
\r
13633 if (dic[lastelem])
\r
13635 p = fgets(dic[lastelem],79,fp);
\r
13636 /* Remove carriage return from end of text line */
\r
13637 poscr = (int)strlen(dic[lastelem]) - 1;
\r
13638 if (dic[lastelem][poscr] == '\n')
\r
13639 dic[lastelem][poscr] = 0;
\r
13642 FATAL("Unable To Allocate Memory");
\r
13644 while((p != NULL) && (lastelem++ < MAXIMUM));
\r
13661 window(1,22,80,24);
\r
13664 cprintf("Updating Dictionary....");
\r
13666 fp = fopen(dicname,"w+");
\r
13668 FILERR("spell.dic");
\r
13670 for(n = 0; n <= lastelem; n++)
\r
13671 fprintf(fp,"%s\n",dic[n]);
\r
13679 /* Add a word to the ignore table */
\r
13681 if (lastign < 100)
\r
13683 ignore[lastign] = calloc(strlen(word) + 1,1);
\r
13684 if (ignore[lastign])
\r
13685 strcpy(ignore[lastign++],comp);
\r
13689 cprintf("No available memory for new words!\r\nPress
\r
13697 cprintf("No available memory for new words!\r\nPress A
\r
13706 void FATAL(char *text)
\r
13708 /* Fatal error drop out */
\r
13710 textcolor(LIGHTGRAY);
\r
13711 textbackground(BLACK);
\r
13712 window(1,1,80,25);
\r
13714 printf("SERVILE SOFTWARE\n\nSPELL V1.7\nFATAL ERROR:
\r
13720 void FILERR(char *fname)
\r
13724 strcpy(text,"Unable To Access: ");
\r
13725 strcat(text,fname);
\r
13733 /* Check Ignore table */
\r
13734 for(p = ignore; p <= &ignore[lastign]; p++)
\r
13735 if (strcmp(comp,*p) == 0)
\r
13738 /* Binary search of dictionary file */
\r
13741 mp = (tp + bp) / 2;
\r
13746 while((result = strcmp(dic[mp],comp)) != 0)
\r
13761 mp = (bp + tp) / 2;
\r
13774 dic[n] = calloc(WORDLEN,1);
\r
13776 if (dic[n] == NULL)
\r
13779 cprintf("No available memory for new words!\r\nPress A
\r
13787 while(n > (insert + 1))
\r
13789 strcpy(dic[n],dic[n-1]);
\r
13793 strcpy(dic[insert + 1],comp);
\r
13818 window(1,1,80,20);
\r
13819 textcolor(BLACK);
\r
13820 textbackground(WHITE);
\r
13822 /* Open temporary file to take spell checked copy */
\r
13823 target = fopen("spell.$$$","w+");
\r
13825 source = fopen(fname,"r");
\r
13827 if (source == NULL)
\r
13844 /* Display read text */
\r
13851 for(m = 0; m < 15; m++)
\r
13853 x = fgets(temp,200,source);
\r
13856 strcat(text,temp);
\r
13859 if (wherey() > 18)
\r
13863 /* return cursor to start position */
\r
13868 memset(word,32,30);
\r
13873 if ((isalpha(c)) || (c == '-') && (curpos != 0))
\r
13874 word[curpos++] = c;
\r
13876 while(((isalpha(c)) || (c == '-') && (curpos != 0))
\r
13877 && (curpos < 30));
\r
13878 word[curpos] = 0;
\r
13879 strcpy(comp,word);
\r
13884 found = COMPARE();
\r
13886 textbackground(RED);
\r
13887 textcolor(WHITE);
\r
13897 textbackground(WHITE);
\r
13898 textcolor(BLACK);
\r
13906 case '\n': cputs("\r\n");
\r
13908 case '\t': cputs(" ");
\r
13910 default: putch(c);
\r
13918 window(1,22,80,24);
\r
13920 cputs("Unknown word ");
\r
13922 cprintf("%s ",word);
\r
13923 textcolor(BLACK);
\r
13924 cputs("[A]dd [I]gnore [C]orrect [S]kip");
\r
13927 key = toupper(getch());
\r
13931 while(strchr("AICSQ",key) == NULL);
\r
13935 case 'A':INSERT();
\r
13938 case 'C':CORRECT();
\r
13941 case 'I':IGNORE();
\r
13952 strcpy(textsav,--text);
\r
13953 /* Delete old word */
\r
13954 text -= strlen(comp);
\r
13956 /* Insert new word */
\r
13957 strcat(text,word);
\r
13958 /* Append remainder of text */
\r
13959 strcat(text,textsav);
\r
13960 text += strlen(word);
\r
13962 /* Length of text may have changed ! */
\r
13963 if (strlen(word) < strlen(comp))
\r
13964 col -= (strlen(comp) - strlen(word));
\r
13965 window(1,1,80,20);
\r
13973 cputs("Checking Spelling....");
\r
13974 window(1,1,80,20);
\r
13975 gotoxy(scol,srow);
\r
13978 window(1,1,80,20);
\r
13982 while((*text) && (key != 'Q'));
\r
13983 fprintf(target,"%s",p);
\r
13985 while((x != NULL) && (key != 'Q'));
\r
13987 window(1,22,80,24);
\r
13990 cprintf("Writing Updated File....");
\r
13997 p = fgets(temp,200,source);
\r
13999 fprintf(target,"%s",temp);
\r
14006 /* Now transfer spell.$$$ to fname */
\r
14008 rename("SPELL.$$$",fname);
\r
14013 /* Locate a good match and return word */
\r
14020 window(1,22,80,24);
\r
14023 cprintf("Searching For Alternatives....");
\r
14025 /* Remove any pending key strokes from keyboard buffer */
\r
14029 for(n = 0; n <= lastelem; n++)
\r
14031 if (MATCHSTR(dic[n],comp))
\r
14033 strcpy(text,dic[n]);
\r
14034 if (strlen(word) <= strlen(text))
\r
14036 for (m = 0; m < strlen(word); m++)
\r
14038 if (isupper(word[m]))
\r
14039 text[m] = toupper(text[m]);
\r
14041 text[m] = tolower(text[m]);
\r
14047 for(m = strlen(word); m < strlen(text); m++)
\r
14048 if (isupper(word[strlen(word)]))
\r
14049 text[m] = toupper(text[m]);
\r
14051 text[m] = tolower(text[m]);
\r
14055 for (m = 0; m < strlen(text); m++)
\r
14057 if (isupper(word[m]))
\r
14058 text[m] = toupper(text[m]);
\r
14060 text[m] = tolower(text[m]);
\r
14064 cprintf("Replace ");
\r
14066 cprintf("%s ",word);
\r
14067 textcolor(BLACK);
\r
14068 cprintf("With ");
\r
14070 cprintf("%s",text);
\r
14071 textcolor(BLACK);
\r
14072 cprintf(" Yes No Continue");
\r
14075 key = toupper(getch());
\r
14077 while(strchr("YNC",key) == NULL);
\r
14080 strcpy(word,text);
\r
14085 cprintf("Searching For Alternatives....");
\r
14087 /* Remove any pending key strokes from keyboard
\r
14102 cprintf("NO ALTERNATIVES FOUND! (Press a key)");
\r
14108 int MATCHSTR(char *src, char *tgt)
\r
14110 /* Compare two words and return non zero if they are similar */
\r
14118 strtgt = strlen(strupr(tgt));
\r
14119 strsrc = strlen(strupr(src));
\r
14121 longest = max(strtgt,strsrc);
\r
14125 if(strtgt > strsrc)
\r
14127 for(; *src ; match += (*src++ == *tgt++))
\r
14132 for(; *tgt ; match += (*src++ == *tgt++))
\r
14136 result = (match * 100 / longest);
\r
14138 /* result holds percentage similarity */
\r
14145 void AT(int row, int col)
\r
14147 /* Position the text cursor */
\r
14149 inreg.h.dh = row;
\r
14150 inreg.h.dl = col;
\r
14151 inreg.h.ah = 0x02;
\r
14152 int86 (0x10, &inreg, &outreg);
\r
14155 void WRTCHA (unsigned char ch, unsigned char attrib, int num)
\r
14157 /* Display a character num times in colour attrib */
\r
14158 /* via the BIOS */
\r
14162 inreg.h.bl = attrib;
\r
14163 inreg.x.cx = num;
\r
14164 inreg.h.ah = 0x09;
\r
14165 int86 (0x10, &inreg, &outreg);
\r
14168 void SHADE_BLOCK(int left,int top,int right,int bottom)
\r
14172 AT(bottom,right);
\r
14173 WRTCHA(223,56,1);
\r
14176 for (c = top+1; c < bottom; c++)
\r
14181 AT(bottom,left+1);
\r
14182 WRTCHA('
\9a',7,right-left);
\r
14188 void BOX(int l, int t, int r, int b)
\r
14190 /* Draws a single line box around a described area */
\r
14201 sprintf(tolc,"%c",218);
\r
14202 sprintf(bolc,"%c",192);
\r
14203 sprintf(hoor,"%c",196);
\r
14204 sprintf(torc,"%c",191);
\r
14205 sprintf(borc,"%c",217);
\r
14207 strcpy(top,tolc);
\r
14208 strcpy(bottom,bolc);
\r
14209 for(n = l + 1; n < r; n++)
\r
14211 strcat(top,hoor);
\r
14212 strcat(bottom,hoor);
\r
14214 strcat(top,torc);
\r
14215 strcat(bottom,borc);
\r
14217 window(1,1,80,25);
\r
14220 for (n = t + 1; n < b; n++)
\r
14236 window (2,2,78,4);
\r
14237 textcolor(BLACK);
\r
14238 textbackground(GREEN);
\r
14240 SHADE_BLOCK(1,1,78,4);
\r
14243 cprintf("Servile Software SPELL CHECKER V1.7
\r
14248 void main(int argc, char *argv[])
\r
14251 char tmp_name[160];
\r
14252 char tmp_fname[160];
\r
14256 puts("\nERROR: Usage is SPELL document");
\r
14260 strcpy(fname,argv[1]);
\r
14266 window(1,22,80,24);
\r
14269 cprintf("Making Backup File....");
\r
14271 strcpy(tmp_fname,argv[1]);
\r
14273 /* Remove extension from tmp_fname */
\r
14274 p = strchr(tmp_fname,'.');
\r
14278 /* Create backup file name using DOS */
\r
14279 sprintf(tmp_name,"copy %s %s.!s! > NUL",argv[1],tmp_fname);
\r
14281 system(tmp_name);
\r
14283 window(1,1,80,25);
\r
14288 textcolor(WHITE);
\r
14289 textbackground(BLACK);
\r
14292 cprintf("Checking Spelling....");
\r
14297 window(1,1,80,25);
\r
14298 textcolor(LIGHTGRAY);
\r
14299 textbackground(BLACK);
\r
14305 APPENDIX A - USING LINK
\r
14311 LINK [options] obj[,[exe][,[map][,[lib]]]][;]
\r
14313 `obj' is a list of object files to be linked. Each obj file name must be
\r
14314 separated by a + or a space. If you do not specify an extension, LINK
\r
14315 will assume .OBJ. `exe' allows you to specify a name for the executable
\r
14316 file. If this file name is ommited, LINK will use the first obj file name
\r
14317 and suffix it with .EXE. `map' is an optional map file name. If you
\r
14318 specify the name `NUL', no map file is produced. `lib' is a list of
\r
14319 library files to link. LINK searches each library file and only links in
\r
14320 modules which are referenced.
\r
14325 LINK filea+fileb,myfile,NUL;
\r
14327 Links .obj files `filea.obj' and `fileb.obj' into .exe file `myfile.exe'
\r
14328 with no map file produced. The ; at the end of the line tells LINK that
\r
14329 there are no more parameters.
\r
14335 Overlay .obj modules are specified by encasing the .obj name in
\r
14336 parenthesis in the link line.
\r
14340 LINK filea + (fileb) + (filec),myfile,NUL;
\r
14342 Will link filea.obj fileb.obj and filec.obj with modules fileb.obj and
\r
14343 filec.obj as overlay code.
\r
14346 Overlay modules must use FAR call/return instructions.
\r
14351 All LINK options commence with a forward slash `/'. Options which accept
\r
14352 a number can accept a decimal number or a hex number prefixed 0X. eg:
\r
14353 0X10 is interpreted as 10h, decimal 16.
\r
14357 Pause during Linking (/PAU)
\r
14359 Tells LINK to wait before writing the .exe file to disk. LINK
\r
14361 message and waits for you to press enter.
\r
14363 Display Linker Process Information (/I)
\r
14365 Tells LINK to display information about the link process.
\r
14367 Pack Executable File (/E)
\r
14369 Tells LINK to remove sequences of repeated bytes and to optimise the
\r
14371 relocation table before creating the executable file. Symbolic debug
\r
14372 information is stripped out of the file.
\r
14374 List Public Symbols (/M)
\r
14376 Tells LINK to create a list of all public symbols defined in the
\r
14380 Include Line Numbers In Map File (/LI)
\r
14382 Tells LINK to include line numbers and associated addresses of the
\r
14384 program in the MAP file.
\r
14386 Preserve Case Sensitivity (/NOI)
\r
14388 By default LINK treats uppercase and lowercase letters as the same.
\r
14390 option tells LINK that they are different.
\r
14392 Ignore Default Libraries (/NOD)
\r
14394 Tells LINK not to search any library specified in the object files
\r
14396 external references.
\r
14398 Controlling Stack Size (/ST:n)
\r
14400 Specifies the size of the stack segment where 'n' is the number of
\r
14403 Setting Maximum Allocation Space (/CP:n)
\r
14405 Tells LINK to write the parameter 'n' into the exe file header. When
\r
14407 file is executed by DOS, 'n' 16 byte paragraphs of memory are
\r
14409 is less than the minimum required, it will be set to the minimum.
\r
14411 is ESSENTIAL to free memory from the program. C programs free memory
\r
14412 automatically on start-up, assembly language programs which want to
\r
14414 dynamic memory allocation must be linked with this option set to a
\r
14417 Setting Maximum Number Of Segments (/SE:n)
\r
14419 Tells LINK how many segments a program is allowed to have. The
\r
14421 but 'n' can be any number between 1 and 3072.
\r
14424 Setting Overlay Interrupt (/O:n)
\r
14426 Tells LINK which interrupt number will be used for passing control
\r
14428 overlays. The default is 63. Valid values for 'n' are 0 through 255.
\r
14430 Ordering Segments (/DO)
\r
14432 Tells LINK to use DOS segment ordering. This option is also enabled
\r
14434 MASM directive .DOSSEG.
\r
14436 Controlling Data Loading (/DS)
\r
14438 By default LINK loads all data starting at the low end of the data
\r
14440 run time the DS register is set to the lowest possible address to
\r
14442 entire data segment to be used. This option tells LINK to load all
\r
14444 starting at the high end of the data segment.
\r
14446 Control Exe File Loading (/HI)
\r
14448 Tells LINK to place the exe file as high as possible in memory.
\r
14450 Prepare for Debugging (/CO)
\r
14452 Tells LINK to include symbolic debug information for use by
\r
14455 Optimising Far Calls (/F)
\r
14457 Tells LINK to translate FAR calls to NEAR calls where possible. This
\r
14461 Disabling Far Call Optimisation (/NOF)
\r
14463 Tells LINK not to translate FAR calls. This option is specified by
\r
14466 Packing Contiguous Segments (/PAC:n)
\r
14468 Tells LINK to group together neighbouring code segments, providing
\r
14470 oportunities for FAR call translation. 'n' specifies the maximum
\r
14472 segment. By default 'n' is 65530. This option is only relevant to
\r
14474 created using FAR calls.
\r
14476 Disabling Segment Packing (/NOP)
\r
14478 Disables segment packing. This option is specified by default.
\r
14482 Using Response Files
\r
14484 Linker options and file names may be specified in a response file. Each
\r
14485 file list starting on a new line instead of being separated by a comma.
\r
14489 filea.obj fileb.obj
\r
14492 liba.lib libb.lib
\r
14494 A response file is specified to LINK by prefixing the response file name
\r