]> 4ch.mooo.com Git - 16.git/blob - doc/CGUIDE_3.TXT
code miraculously works on real hardware
[16.git] / doc / CGUIDE_3.TXT
1 \r
2                                     \r
3                    THE IBM PC PROGRAMMER'S GUIDE TO C\r
4                                     \r
5                                     \r
6                                     \r
7                                3rd Edition\r
8                                     \r
9                                     \r
10                                     \r
11                              Matthew Probert\r
12 \r
13 \r
14                                     \r
15                             COPYRIGHT NOTICE\r
16 \r
17 \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
25 \r
26  BIOGRAPHICAL NOTES\r
27 \r
28 \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
33 \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
39 ever since.\r
40 \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
45 \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
54 \r
55 \r
56 Matthew Probert can be contacted via\r
57      Servile Software\r
58      5 Longcroft Close\r
59      Basingstoke\r
60      Hampshire\r
61      RG21 8XG\r
62      England\r
63 \r
64      Telephone 01256 478576\r
65 \r
66                                     \r
67                                  PREFACE\r
68 \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
76 \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
79 document.\r
80 \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
84 \r
85 \r
86 "To err is human, to make a complete balls up requires a computer!"\r
87 \r
88 \r
89 \r
90 Warning!\r
91 \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
95 \r
96 \r
97                                     \r
98                               INTRODUCTION\r
99 \r
100 \r
101 The major distinguishing features of the C programming language are;\r
102 \r
103  ú    block-structured flow-control constructs (typical of most high-level\r
104       languages);\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
107       languages);\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
111 \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
116 provided DOS.\r
117 \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
120 \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
123 of Microsoft C.\r
124 \r
125 \r
126 \r
127 Differences Between the Various Versions of C\r
128 \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
132 statement such as\r
133 \r
134  x=-y;\r
135 was compiled - it could have meant\r
136 \r
137  x = x - y;\r
138 or\r
139 \r
140  x = (-y);\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
143 \r
144 The major variations, however, are between K&R C and ANSI C.  These can\r
145 be summarized as follows:\r
146 \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
159       English languages;\r
160  ú    introduction of the `signed' keyword (to complement the `unsigned'\r
161       keyword when used in integer declarations) and the unary plus (`+')\r
162       operator.\r
163 \r
164 \r
165 \r
166 C is a medium level language\r
167 \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
174 \r
175 \r
176 Key words\r
177 \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
183 \r
184 \r
185 The 32 C key words are;\r
186 \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
194 do                if                 static             while\r
195 \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
199 \r
200 \r
201 near      far        huge\r
202 \r
203 \r
204 \r
205 Structure\r
206 \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
210 such as;\r
211 \r
212 \r
213            do-while,  for, while, if\r
214 \r
215 and the use of jumps, while provided for, are rarely used.\r
216 \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
222 \r
223 \r
224 if (x < 10)\r
225 {\r
226      a = 1;\r
227      b = 0;\r
228 }\r
229 \r
230 while this, is a complete function code block containing a sub code block\r
231 as a do-while loop;\r
232 \r
233 int GET_X()\r
234 {\r
235      int x;\r
236 \r
237      do\r
238      {\r
239           printf("\nEnter a number between 0 and 10 ");\r
240           scanf("%d",&x);\r
241      }\r
242      while(x < 0 || x > 10);\r
243      return(x);\r
244 }\r
245 \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
251 \r
252 Having a freeflow structure the following commands are recognised as the\r
253 same by the C compiler;\r
254 \r
255 \r
256      x = 0;\r
257      x      =0;\r
258      x=0;\r
259 \r
260 \r
261 The general form of a C program is as follows;\r
262 \r
263      compiler preprocessor statements\r
264      global data declarations\r
265      \r
266      \r
267      \r
268      \r
269      return-type main(parameter list)\r
270      {\r
271           statements\r
272      }\r
273      \r
274      return-type f1(parameter list)\r
275      {\r
276           statements\r
277      }\r
278      \r
279      return-type f2(parameter list)\r
280      {\r
281           statements\r
282      }\r
283      .\r
284      .\r
285      .\r
286      return-type fn(parameter list)\r
287      {\r
288           statements\r
289      }\r
290      \r
291 \r
292 \r
293 Comments\r
294 \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
297 comment;\r
298 \r
299 \r
300 /* This is a legitimate C comment line */\r
301 \r
302 \r
303 Libraries\r
304 \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
315 \r
316 \r
317                                     \r
318                                DATA TYPES\r
319 \r
320 \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
323 \r
324 "char", "int", "float" and "void" respectively.\r
325 \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
330 \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
334 \r
335 \r
336 Type                     Size                     Range\r
337                                                   \r
338 char                     8                        -127 to 127\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
343                                                   2147483647\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
348 \r
349 \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
355 \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
366 \r
367 \r
368 Declaring a variable\r
369 \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
372 \r
373 \r
374      type name;\r
375 \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
378 \r
379 \r
380      int x;\r
381 \r
382 Character strings may be declared, which are in reality arrays of\r
383 characters. They are declared as follows;\r
384 \r
385 \r
386      char name[number_of_elements];\r
387 \r
388 So, to declare a string thirty characters long, and called `name' you\r
389 would use the declaration;\r
390 \r
391 \r
392      char name[30];\r
393 \r
394 \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
397 array of integers;\r
398 \r
399 \r
400      int x[10][10];\r
401 \r
402 The elements of this array are then accessed as;\r
403 \r
404      x[0][0]\r
405      x[0][1]\r
406      x[n][n]\r
407 \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
415 \r
416 \r
417 For example;\r
418 \r
419      int error;\r
420      static int a;\r
421      \r
422      main()\r
423      {\r
424           int x;\r
425           int y;\r
426      \r
427      }\r
428      \r
429      funca()\r
430      {\r
431           /* Test variable 'a' for equality with 0 */\r
432           if (a == 0)\r
433           {\r
434                int b;\r
435                for(b = 0; b < 20; b++)\r
436                     printf("\nHello World");\r
437           }\r
438      \r
439      }\r
440      \r
441 \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
448 statement.\r
449 \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
452 \r
453 \r
454      extern int error;\r
455      \r
456      funcb()\r
457      {\r
458      }\r
459      \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
465 \r
466 \r
467      main()\r
468      {\r
469      \r
470           x = 5000;\r
471      \r
472      }\r
473      \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
477 \r
478 \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
483 expression;\r
484 \r
485 \r
486      main()\r
487      {\r
488           float x;\r
489           int y;\r
490      \r
491           x = 100 / 25;\r
492      \r
493           y = (int)x;\r
494      }\r
495 \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
498 variable y.\r
499 \r
500 \r
501 \r
502 Formal parameters\r
503 \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
507 \r
508 \r
509      int MULT(int x, int y)\r
510      {\r
511           /* Return parameter x multiplied by parameter y */\r
512           return(x * y);\r
513      }\r
514      \r
515      main()\r
516      {\r
517           int a;\r
518           int b;\r
519           int c;\r
520      \r
521           a = 5;\r
522           b = 7;\r
523           c = MULT(a,b);\r
524      \r
525           printf("%d multiplied by %d equals %d\n",a,b,c);\r
526      }\r
527      \r
528 \r
529 Access modifiers\r
530 \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
537 \r
538 \r
539 \r
540 Storage class types\r
541 C provides four storage types; `extern', `static', `auto' and `register'.\r
542 \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
545 \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
549 \r
550 Register variables are stored within CPU registers where ever possible,\r
551 providing the fastest possible access to their values.\r
552 \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
556                                     \r
557                                     \r
558                                     \r
559                                 OPERATORS\r
560 \r
561 Operators are tokens that cause a computation to occur when applied to\r
562 variables. C provides the following operators;\r
563 \r
564 \r
565 &                  Address\r
566 *                  Indirection\r
567 +                  Unary plus\r
568 -                  Unary minus\r
569 ~                  Bitwise compliment\r
570 !                  Logical negation\r
571 ++                 As a prefix;\r
572                    preincrement\r
573                    As a suffix;\r
574                    postincrement\r
575 --                 As a prefix;\r
576                    predecrement\r
577                    As a suffix;\r
578                    postdecrement\r
579 +                  Addition\r
580 -                  Subtraction\r
581 *                  Multiply\r
582 /                  Divide\r
583 %                  Remainder\r
584 <<                 Shift left\r
585 >>                 Shift right\r
586 &                  Bitwise AND\r
587 |                  Bitwise OR\r
588 ^                  Bitwise XOR\r
589 &&                 Logical AND\r
590 ||                 Logical OR\r
591 =                  Assignment\r
592 *=                 Assign product\r
593 /=                 Assign quotient\r
594 %=                 Assign remainder\r
595                    (modulus)\r
596 +=                 Assign sum\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
603 <                  Less than\r
604 >                  Greater than\r
605 <=                 Less than or equal\r
606                    to\r
607 >=                 Greater than or\r
608                    equal to\r
609 ==                 Equal to\r
610 !=                 Not equal to\r
611 .                  Direct component\r
612                    selector\r
613 ->                 Indirect component\r
614                    selector\r
615 a ? x:y            "If a is true then\r
616                    x else y"\r
617 []                 Define arrays\r
618 ()                 Parenthesis\r
619                    isolate conditions\r
620                    and expressions\r
621 ...                Ellipsis are used\r
622                    in formal\r
623                    parameter lists of\r
624                    function\r
625                    prototypes to show\r
626                    a variable number\r
627                    of\r
628                    parameters or\r
629                    parameters of\r
630                    varying types.\r
631 \r
632 To illustrate some more commonly used operators consider the following\r
633 short program;\r
634 \r
635 \r
636      main()\r
637      {\r
638           int a;\r
639           int b;\r
640           int c;\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
643      variable 'b' */\r
644           c = b * 2;       /* Assign the value of 'b' multiplied by two\r
645      to variable 'c' */\r
646      \r
647           if (a == c)     /* Test if 'a' holds the same value as 'c' */\r
648      \r
649                puts("Variable 'a' is an even number");\r
650           else\r
651                puts("Variable 'a' is an odd number");\r
652      }\r
653      \r
654      \r
655 Normally when incrementing the value of a variable you would write\r
656 something like;\r
657 \r
658           x = x + 1\r
659 \r
660 C provides the incremental operator '++' as well so that you can write;\r
661 \r
662           x++\r
663 \r
664 Similarly you can decrement the value of a variable using '--' as;\r
665 \r
666           x--\r
667 \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
670 \r
671 \r
672 NORMAL                               C\r
673                                      \r
674 x = x + 1                            x++\r
675 x = x - 1                            x--\r
676 x = x * 2                            x *= 2\r
677 x = x / y                            x /= y\r
678 x = x % 5                            x %= 5\r
679 \r
680 and so on.\r
681                                     \r
682                                     \r
683                                     \r
684                                 FUNCTIONS\r
685 \r
686 Functions are the source code procedures that comprise a C program. They\r
687 follow the general form;\r
688 \r
689      return_type function_name(parameter_list)\r
690      {\r
691           statements\r
692      }\r
693 \r
694 \r
695 The return_type specifies the data type that will be returned by the\r
696 function; char, int, double, void &c.\r
697 \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
702 \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
708 \r
709 \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
714 \r
715 \r
716      #include <stdio.h>\r
717      \r
718      int funca(int x, int y)\r
719      {\r
720           /* This function receives two parameters by value, x and y */\r
721      \r
722           x = x * 2;\r
723           y = y * 2;\r
724      \r
725           printf("\nValue of x in funca() %d value of y in funca()\r
726      %d",x,y);\r
727      \r
728           return(x);\r
729      }\r
730      \r
731      \r
732      int funcb(int *x, int *y)\r
733      {\r
734           /* This function receives two parameters by reference, x and y\r
735      */\r
736      \r
737           *x = *x * 2;\r
738           *y = *y * 2;\r
739      \r
740           printf("\nValue of x in funcb() %d value of y in funcb()\r
741      %d",*x,*y);\r
742      \r
743           return(*x);\r
744      }\r
745      \r
746      main()\r
747      {\r
748           int x;\r
749           int y;\r
750           int z;\r
751      \r
752           x = 5;\r
753           y = 7;\r
754      \r
755           z = funca(x,y);\r
756           z = funcb(&x,&y);\r
757      \r
758           printf("\nValue of x %d value of y %d value of z %d",x,y,z);\r
759      }\r
760      \r
761 \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
767 \r
768 \r
769 \r
770 Passing an array to a function\r
771 \r
772 The following program passes an array to a function, funca(), which\r
773 initialises the array elements;\r
774 \r
775      #include <stdio.h>\r
776      \r
777      void funca(int x[])\r
778      {\r
779           int n;\r
780      \r
781           for(n = 0; n < 100; n++)\r
782           x[n] = n;\r
783      }\r
784      \r
785      main()\r
786      {\r
787           int array[100];\r
788           int counter;\r
789      \r
790           funca(array);\r
791      \r
792           for(counter = 0; counter < 100; counter++)\r
793                printf("\nValue of element %d is\r
794      %d",counter,array[counter]);\r
795      }\r
796 \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
803 \r
804 \r
805      funca(const int x[])\r
806      {\r
807      }\r
808 \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
813 \r
814 \r
815      #include <stdio.h>\r
816      \r
817      int funca(const int x[])\r
818      {\r
819           int *ptr;\r
820           int n;\r
821      \r
822           /* This line gives a 'suspicious pointer conversion warning' */\r
823           /* because x is a const pointer, and ptr is not */\r
824           ptr = x;\r
825      \r
826           for(n = 0; n < 100; n++)\r
827           {\r
828                *ptr = n;\r
829                ptr++;\r
830           }\r
831      }\r
832      \r
833      main()\r
834      {\r
835           int array[100];\r
836           int counter;\r
837      \r
838           funca(array);\r
839      \r
840           for(counter = 0; counter < 100; counter++)\r
841                printf("\nValue of element %d is\r
842      %d",counter,array[counter]);\r
843      }\r
844      \r
845 \r
846 \r
847 Passing parameters to main()\r
848 \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
851 follows;\r
852 \r
853 \r
854      #include <stdio.h>\r
855      \r
856      main(int argc, char *argv[])\r
857      {\r
858           int n;\r
859      \r
860           for(n = 0; n < argc; n++)\r
861           printf("\nParameter %d equals %s",n,argv[n]);\r
862      }\r
863      \r
864 \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
869 scenario:\r
870 \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
881 \r
882 \r
883      #include <string.h>\r
884      \r
885      char system_file_name[160];\r
886      \r
887      void main(int argc,char *argv[])\r
888      {\r
889           char *data_file = "DATA.DAT";\r
890           char *p;\r
891      \r
892           strcpy(system_file_name,argv[0]);\r
893           p = strstr(system_file_name,".EXE");\r
894           if (p == NULL)\r
895           {\r
896                /* The executable is a .COM file */\r
897              p = strstr(system_file_name,".COM");\r
898           }\r
899      \r
900           /* Now back track to the last '\' character in the file name */\r
901           while(*(p - 1) != '\\')\r
902                p--;\r
903      \r
904           strcpy(p,data_file);\r
905      }\r
906 \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
911 \r
912 \r
913 \r
914 Returning from a function\r
915 \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
919 \r
920 \r
921 \r
922 Function prototypes\r
923 \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
928 \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
932                                     \r
933                            THE C PREPROCESSOR\r
934 \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
938 \r
939 \r
940     #if\r
941     #ifdef\r
942     #ifndef\r
943     #else\r
944     #elif\r
945     #include\r
946     #define\r
947     #undef\r
948     #line\r
949     #error\r
950     #pragma\r
951 \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
954 \r
955 \r
956 \r
957 #define\r
958 \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
962 \r
963 \r
964 #define FALSE 0\r
965 #define TRUE !FALSE\r
966 \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
971 \r
972 \r
973      printf("TRUE");\r
974 \r
975 would NOT be replaced, but\r
976 \r
977      printf("%d",FALSE);\r
978 \r
979 would be.\r
980 \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
984 \r
985 \r
986 This example declares a macro `larger()' that accepts two parameters and\r
987 returns the larger of the two;\r
988 \r
989      #include <stdio.h>\r
990      \r
991      #define larger(a,b) (a > b) ? (a) : (b)\r
992      \r
993      int main()\r
994      {\r
995           printf("\n%d is largest",larger(5,7));\r
996      \r
997      }\r
998 \r
999 \r
1000 #error\r
1001 \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
1004 \r
1005 \r
1006 #error REACHED MODULE B\r
1007 \r
1008 will cause the compiler to stop compilation and display;\r
1009 \r
1010      REACHED MODULE B\r
1011 \r
1012 \r
1013 \r
1014 #include\r
1015 \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
1019 \r
1020 \r
1021      #include "module2.c"\r
1022      #include <stdio.h>\r
1023 \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
1028 \r
1029 \r
1030 \r
1031 #if, #else, #elif, #endif\r
1032 \r
1033 The #if set of commands provide conditional compilation around the\r
1034 general form;\r
1035 \r
1036      #if constant_expression\r
1037           statements\r
1038      #else\r
1039           statements\r
1040      #endif\r
1041 \r
1042 #elif stands for '#else if' and follows the form;\r
1043 \r
1044      #if expression\r
1045           statements\r
1046      #elif expression\r
1047           statements\r
1048      #endif\r
1049 \r
1050 \r
1051 \r
1052 #ifdef, #ifndef\r
1053 \r
1054 These two commands stand for '#if defined' and '#if not defined'\r
1055 respectively and follow the general form;\r
1056 \r
1057      #ifdef macro_name\r
1058           statements\r
1059      #else\r
1060           statements\r
1061      #endif\r
1062 \r
1063      #ifndef macro_name\r
1064           statements\r
1065      #else\r
1066           statements\r
1067      #endif\r
1068 \r
1069 where 'macro_name' is an identifier declared by a #define statement.\r
1070 \r
1071 \r
1072 \r
1073 #undef\r
1074 \r
1075 Undefines a macro previously defined by #define.\r
1076 \r
1077 \r
1078 #line\r
1079 \r
1080 Changes the compiler declared global variables __LINE__ and __FILE__. The\r
1081 general form of #line is;\r
1082 \r
1083      #line number "filename"\r
1084 \r
1085 where number is inserted into the variable '__LINE__' and 'filename' is\r
1086 assigned to '__FILE__'.\r
1087 \r
1088 \r
1089 #pragma\r
1090 \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
1094                                     \r
1095                        PROGRAM CONTROL STATEMENTS\r
1096 \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
1100 zero.\r
1101 \r
1102 \r
1103 \r
1104 Selection statements\r
1105 \r
1106 The general purpose selection statement is "if" that follows the general\r
1107 form;\r
1108 \r
1109           if (expression)\r
1110                statement\r
1111           else\r
1112                statement\r
1113 \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
1119 \r
1120 \r
1121 An alternative to the if....else combination is the ?: command that takes\r
1122 the form;\r
1123 \r
1124 \r
1125            expression ? true_expression : false_expression\r
1126 \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
1129 get;\r
1130 \r
1131 \r
1132      #include <stdio.h>\r
1133      \r
1134      main()\r
1135      {\r
1136           int x;\r
1137      \r
1138           x = 6;\r
1139      \r
1140           printf("\nx is an %s number", x % 2 == 0 ? "even" : "odd");\r
1141      }\r
1142      \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
1146 switch is;\r
1147 \r
1148 \r
1149      switch (expression)\r
1150      {\r
1151           case value1 :  statements\r
1152                     break;\r
1153           case value2 :  statements\r
1154                     break;\r
1155           .\r
1156           .\r
1157           .\r
1158           .\r
1159           case valuen :  statements\r
1160                     break;\r
1161           default :      statements\r
1162      }\r
1163 \r
1164 The break statement is optional, but if omitted, program execution will\r
1165 continue down the list.\r
1166 \r
1167      #include <stdio.h>\r
1168      \r
1169      main()\r
1170      {\r
1171           int x;\r
1172      \r
1173           x = 6;\r
1174      \r
1175           switch(x)\r
1176           {\r
1177                case 0 : printf("\nx equals zero");\r
1178                      break;\r
1179                case 1 : printf("\nx equals one");\r
1180                      break;\r
1181                case 2 : printf("\nx equals two");\r
1182                      break;\r
1183                case 3 : printf("\nx equals three");\r
1184                      break;\r
1185                default : printf("\nx is larger than three");\r
1186           }\r
1187      }\r
1188 \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
1191 code!\r
1192 \r
1193 \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
1197 \r
1198 \r
1199      for(initialization;condition;increment)\r
1200 \r
1201 and is useful for counters such as in this example that displays the\r
1202 entire ascii character set;\r
1203 \r
1204      #include <stdio.h>\r
1205      \r
1206      main()\r
1207      {\r
1208           int x;\r
1209      \r
1210           for(x = 32; x < 128; x++)\r
1211                printf("%d\t%c\t",x,x);\r
1212      }\r
1213      \r
1214 An infinite for loop is also quite valid;\r
1215 \r
1216      for(;;)\r
1217      {\r
1218           statements\r
1219      }\r
1220 \r
1221 Also, C allows empty statements. The following for loop removes leading\r
1222 spaces from a string;\r
1223 \r
1224      for(; *str == ' '; str++)\r
1225           ;\r
1226 \r
1227 Notice the lack of an initializer, and the empty statement following the\r
1228 loop.\r
1229 \r
1230 The while loop is somewhat simpler than the for loop and follows the\r
1231 general form;\r
1232 \r
1233      while (condition)\r
1234           statements\r
1235 \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
1241 \r
1242 \r
1243      do\r
1244      {\r
1245           statements\r
1246      }\r
1247      while(condition);\r
1248 \r
1249 \r
1250 Jump statements\r
1251 \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
1255 \r
1256 \r
1257      int MULT(int x, int y)\r
1258      {\r
1259           return(x * y);\r
1260      }\r
1261 \r
1262 or;\r
1263 \r
1264      void FUNCA()\r
1265      {\r
1266           printf("\nHello World");\r
1267           return;\r
1268      }\r
1269 \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
1272 shown here;\r
1273 \r
1274 \r
1275      #include <stdio.h>\r
1276      \r
1277      main()\r
1278      {\r
1279           int x;\r
1280      \r
1281           for(x = 0; x < 256; x++)\r
1282           {\r
1283                if (x == 100)\r
1284                     break;\r
1285      \r
1286                printf("%d\t",x);\r
1287           }\r
1288      }\r
1289 \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
1292 statement.\r
1293 \r
1294 \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
1297 program;\r
1298 \r
1299 \r
1300      exit(return_value);\r
1301 \r
1302 \r
1303 More About ?:\r
1304 \r
1305 \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
1309 \r
1310 \r
1311      <boolean expression> ? <value for true> : <value for false>\r
1312 \r
1313 It can be used almost anywhere, for example it was used in the binary\r
1314 search demonstration program;\r
1315 \r
1316      printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");\r
1317 \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
1321 \r
1322      x = (a  == 0) ? (b) : (c);\r
1323 \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
1326 \r
1327 This example returns the name of the executing program, without any path\r
1328 description;\r
1329 \r
1330      #include <stdio.h>\r
1331      #include <stddef.h>\r
1332      #include <string.h>\r
1333      \r
1334      char *progname(char *pathname)\r
1335      {\r
1336           /* Return name of running program */\r
1337           unsigned l;\r
1338           char *p;\r
1339           char *q;\r
1340           static char bnbuf[256];\r
1341      \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
1347                      bnbuf[l] = '\0',\r
1348                      strlwr (bnbuf)\r
1349                   : NULL;\r
1350      }\r
1351      \r
1352      void main(int argc, char *argv[])\r
1353      {\r
1354           printf("\n%s",progname(argv[0]));\r
1355      }\r
1356      \r
1357      \r
1358 \r
1359 Continue\r
1360 \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
1365 \r
1366 \r
1367      #include <stdio.h>\r
1368      \r
1369      void main()\r
1370      {\r
1371           FILE *fp;\r
1372           char *p;\r
1373           char buff[100];\r
1374      \r
1375           fp = fopen("data.txt","r");\r
1376           if (fp == NULL)\r
1377           {\r
1378                fprintf(stderr,"Unable to open file data.txt");\r
1379                exit(0);\r
1380           }\r
1381      \r
1382           do\r
1383           {\r
1384                p = fgets(buff,100,fp);\r
1385                if (p == NULL)\r
1386                     /* Force exit from loop */\r
1387                     continue;\r
1388                puts(p);\r
1389           }\r
1390           while(p);\r
1391      }\r
1392      \r
1393 With a for() loop however, continue passes control back to the third\r
1394 parameter!\r
1395                                     \r
1396                             INPUT AND OUTPUT\r
1397 \r
1398 \r
1399 \r
1400 Input\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
1403 from a data port.\r
1404 \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
1411 \r
1412 For example, the following program will read a single integer from the\r
1413 stream stdin;\r
1414 \r
1415 \r
1416      main()\r
1417      {\r
1418           int x;\r
1419      \r
1420           scanf("%d",&x);\r
1421      }\r
1422 \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
1426 \r
1427 The format string is a character string that may contain three types of\r
1428 data:\r
1429 \r
1430 whitespace characters (space, tab and newline), non-whitespace characters\r
1431 (all ascii characters EXCEPT %) and format specifiers.\r
1432 \r
1433 Format specifiers have the general form;\r
1434 \r
1435 \r
1436      %[*][width][h|l|L]type_character\r
1437 \r
1438 After the % sign the format specifier is comprised of:\r
1439 \r
1440  an optional assignment suppression character, *, which suppresses\r
1441  assignment of the next input field.\r
1442  \r
1443  an optional width specifier, width, which declares the maximum number\r
1444  of characters to be read.\r
1445  \r
1446  an optional argument type modifier, h or l or L, where:\r
1447            h is a short integer\r
1448            l is a long\r
1449            L is a long double\r
1450 \r
1451      the data type character that is one of;\r
1452 \r
1453 \r
1454 d      Decimal integer\r
1455 D      Decimal long integer\r
1456 o      Octal integer\r
1457 O      Octal long integer\r
1458 i      Decimal, octal or hexadecimal integer\r
1459 I      Decimal, octal or hexadecimal long\r
1460        integer\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
1465 e      Floating point\r
1466 f      Floating point\r
1467 g      Floating point\r
1468 s      Character string\r
1469 c      Character\r
1470 %      % is stored\r
1471 \r
1472 An example using scanf();\r
1473 \r
1474 \r
1475      #include <stdio.h>\r
1476      \r
1477      main()\r
1478      {\r
1479           char name[30];\r
1480           int age;\r
1481      \r
1482           printf("\nEnter your name and age ");\r
1483           scanf("%30s%d",name,&age);\r
1484           printf("\n%s %d",name,age);\r
1485      }\r
1486 \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
1490 \r
1491 \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
1494 \r
1495 \r
1496      JOHN SMITH\r
1497 \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
1502 \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
1509 \r
1510 \r
1511      #include <stdio.h>\r
1512      #include <stdlib.h>\r
1513      #include <string.h>\r
1514      \r
1515      main()\r
1516      {\r
1517           char data[80];\r
1518           char *p;\r
1519           char name[30];\r
1520           int age;\r
1521      \r
1522           printf("\nEnter your name and age ");\r
1523           /* Read in a string of data */\r
1524           gets(data);\r
1525      \r
1526      \r
1527           /* P is a pointer to the last character in the input string */\r
1528           p = &data[strlen(data) - 1];\r
1529      \r
1530           /* Remove any trailing spaces by replacing them with null bytes\r
1531      */\r
1532           while(*p == ' '){\r
1533                *p = 0;\r
1534                p--;\r
1535           }\r
1536      \r
1537           /* Locate last space in the string */\r
1538           p = strrchr(data,' ');\r
1539      \r
1540           /* Read age from string and convert to an integer */\r
1541           age = atoi(p);\r
1542      \r
1543           /* Terminate data string at start of age field */\r
1544           *p = 0;\r
1545      \r
1546           /* Copy data string to name variable */\r
1547           strcpy(name,data);\r
1548      \r
1549           /* Display results */\r
1550           printf("\nName is %s age is %d",name,age);\r
1551      }\r
1552 \r
1553 \r
1554 Output\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
1561 \r
1562 \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
1565 \r
1566 The width specifier is also slightly different for printf(). In its most\r
1567 useful form is the precision specifier;\r
1568 \r
1569 \r
1570      width.precision\r
1571 \r
1572 So, to print a floating point number to three decimal places you would\r
1573 use;\r
1574 \r
1575      printf("%.3f",x);\r
1576 \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
1579 width;\r
1580 \r
1581      printf("%-20.20s",data);\r
1582 \r
1583 Special character constants may appear in the printf() parameter list.\r
1584 These are;\r
1585 \r
1586 \n         Newline\r
1587 \r         Carriage return\r
1588 \t         Tab\r
1589 \b         Sound the computer's bell\r
1590 \f         Formfeed\r
1591 \v         Vertical tab\r
1592 \\         Backslash character\r
1593 \'         Single quote\r
1594 \"         Double quote\r
1595 \?         Question mark\r
1596 \O         Octal string\r
1597 \x         Hexadecimal string\r
1598 \r
1599 Thus, to display "Hello World", surrounded in quotation marks and\r
1600 followed by a newline you would use;\r
1601 \r
1602      printf("\"Hello World\"\n");\r
1603 \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
1608 \r
1609 \r
1610      /* A simple decimal to hexadecimal and octal conversion program */\r
1611      \r
1612      #include <stdio.h>\r
1613      \r
1614      main()\r
1615      {\r
1616           int x;\r
1617      \r
1618           do\r
1619           {\r
1620                printf("\nEnter a number, or 0 to end ");\r
1621                scanf("%d",&x);\r
1622                printf("%04d  %04X  %04o",x,x,x);\r
1623           }\r
1624           while(x != 0);\r
1625      \r
1626      }\r
1627      \r
1628 There are associated functions to printf() that you should be aware of.\r
1629 fprintf() has the prototype;\r
1630 \r
1631      fprintf(FILE *fp,char *format[,argument,...]);\r
1632 \r
1633 This variation on printf() simply sends the formatted output to the\r
1634 specified file stream.\r
1635 \r
1636 sprintf() has the function prototype;\r
1637 \r
1638      sprintf(char *s,char *format[,argument,...]);\r
1639 \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
1644 \r
1645 An example of using sprintf() to copy multiple data to a string;\r
1646 \r
1647      #include<stdio.h>\r
1648      \r
1649      int main()\r
1650      {\r
1651           char buffer[50];\r
1652      \r
1653           sprintf(buffer,"7 * 5 == %d\n",7 * 5);\r
1654      \r
1655           puts(buffer);\r
1656      }\r
1657      \r
1658 \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
1663 \r
1664      printf("Hello World\n");\r
1665 \r
1666 You can use;\r
1667 \r
1668      puts("Hello World");\r
1669 \r
1670 \r
1671 \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
1678 \r
1679   1. They do not make use of the predefined streams, and hence may not be\r
1680      redirected\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
1685      modes).\r
1686                                     \r
1687                                     \r
1688                                     \r
1689                                 POINTERS\r
1690 \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
1694 \r
1695 \r
1696      char *p;\r
1697 \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
1702 \r
1703 The following program illustrates a simple, though fairly useless\r
1704 application of a pointer;\r
1705 \r
1706 \r
1707      #include <stdio.h>\r
1708      \r
1709      main()\r
1710      {\r
1711           int a;\r
1712           int *x;\r
1713      \r
1714           /* x is a pointer to an integer data type */\r
1715      \r
1716           a = 100;\r
1717           x = &a;\r
1718      \r
1719           printf("\nVariable 'a' holds the value %d at memory address\r
1720      %p",a,x);\r
1721      }\r
1722 \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
1727 \r
1728      #include <stdio.h>\r
1729      \r
1730      main()\r
1731      {\r
1732           int n;\r
1733           int a[25];\r
1734           int *x;\r
1735      \r
1736           /* x is a pointer to an integer data type */\r
1737      \r
1738           /* Assign x to point to array element zero */\r
1739           x = a;\r
1740      \r
1741           /* Assign values to the array */\r
1742           for(n = 0; n < 25; n++)\r
1743                a[n] = n;\r
1744      \r
1745      \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
1749      }\r
1750      \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
1754 \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
1761 \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
1766 \r
1767 \r
1768      any pointer type = malloc(number_of_bytes);\r
1769 \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
1773 \r
1774      #include <stdio.h>\r
1775      #include <stdlib.h>\r
1776      \r
1777      main()\r
1778      {\r
1779           int *x;\r
1780           int n;\r
1781      \r
1782           /* x is a pointer to an integer data type */\r
1783      \r
1784           /* Create a 1000 element table, sizeof() returns the compiler\r
1785      */\r
1786           /* specific number of bytes used to store an integer */\r
1787      \r
1788           x = malloc(1000 * sizeof(int));\r
1789      \r
1790      \r
1791           /* Check to see if the memory allocation succeeded */\r
1792           if (x == NULL)\r
1793           {\r
1794                printf("\nUnable to allocate a 1000 element integer\r
1795      table");\r
1796                exit(0);\r
1797           }\r
1798      \r
1799           /* Assign values to each table element */\r
1800           for(n = 0; n < 1000; n++)\r
1801           {\r
1802                *x = n;\r
1803                x++;\r
1804           }\r
1805      \r
1806           /* Return x to the start of the table */\r
1807           x -= 1000;\r
1808      \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
1812                x++;\r
1813           }\r
1814           /* Deallocate the block of memory now it's no longer required\r
1815      */\r
1816           free(x);\r
1817      }\r
1818      \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
1822 \r
1823      #include <stdio.h>\r
1824      #include <string.h>\r
1825      \r
1826      main()\r
1827      {\r
1828           char *p;\r
1829           char text[100];\r
1830           int len;\r
1831      \r
1832           /* Initialise variable 'text' with some writing */\r
1833           strcpy(text,"This is a string of data");\r
1834      \r
1835           /* Set variable p to the start of variable text */\r
1836           p = text;\r
1837      \r
1838           /* Initialise variable len to zero */\r
1839           len = 0;\r
1840      \r
1841           /* Count the characters in variable text */\r
1842           while(*p)\r
1843           {\r
1844                len++;\r
1845                p++;\r
1846           }\r
1847      \r
1848           /* Display the result */\r
1849           printf("\nThe string of data has %d characters in it",len);\r
1850      }\r
1851      \r
1852 \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
1857 \r
1858 This leads the C language on the IBM PC to have three new keywords; near,\r
1859 far and huge.\r
1860 \r
1861 near pointers are 16 bits wide and access only data within the current\r
1862 segment.\r
1863 \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
1870 \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
1874 \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
1878 \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
1881 \r
1882 \r
1883      void far *MK_FP(unsigned segment, unsigned offset);\r
1884 \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
1887 \r
1888           screen = MK_FP(0xB800,0);\r
1889 \r
1890 Two coressponding macros provided are;\r
1891 \r
1892 FP_SEG() and FP_OFF()\r
1893 \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
1897 \r
1898 \r
1899      #include <dos.h>\r
1900      \r
1901      int makedir(char *path)\r
1902      {\r
1903           /* Make sub directory, returning non zero on success */\r
1904      \r
1905           union REGS inreg,outreg;\r
1906           struct SREGS segreg;\r
1907      \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
1912      \r
1913           return(1 - outreg.x.cflag);\r
1914      }\r
1915      \r
1916 \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
1920 \r
1921 \r
1922      void segread(struct SREGS *segp);\r
1923 \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
1926 \r
1927      #include <dos.h>\r
1928      \r
1929      main()\r
1930      {\r
1931           struct SREGS sregs;\r
1932      \r
1933           segread(&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
1938      }\r
1939                                     \r
1940                                STRUCTURES\r
1941 \r
1942 \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
1946 \r
1947 The general form for a structure definition is;\r
1948 \r
1949 \r
1950      typedef struct\r
1951      {\r
1952           variable_type variable_name;\r
1953           variable_type variable_name;\r
1954      }\r
1955      structure_name;\r
1956 \r
1957 \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
1963 \r
1964 \r
1965      typedef struct\r
1966      {\r
1967           char name[30];\r
1968           char address[30];\r
1969           char town[30];\r
1970           char county[30];\r
1971           char post[12];\r
1972           char telephone[15];\r
1973      }\r
1974      data;\r
1975 \r
1976 The structure 'data' may then be used in the program as a variable data\r
1977 type for declaring variables;\r
1978 \r
1979      data record;\r
1980 \r
1981 Thus declares a variable called 'record' to be of type 'data'.\r
1982 \r
1983 The individual fields of the structure variable are accessed by the\r
1984 general form;\r
1985 \r
1986      structure_variable.field_name;\r
1987 \r
1988 Thus, the name field of the structure variable record is accessed with;\r
1989 \r
1990      record.name\r
1991 \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
1994 \r
1995 \r
1996      typedef struct\r
1997      {\r
1998           char name[30];\r
1999           int age;\r
2000           char *notes;\r
2001      }\r
2002      dp;\r
2003 \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
2006 \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
2009 \r
2010      func(&my_struct)\r
2011 \r
2012 \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
2015 \r
2016 \r
2017 /* A VERY simple address book written in ANSI C */\r
2018 \r
2019 #include <stdio.h>\r
2020 #include <stdlib.h>\r
2021 #include <io.h>\r
2022 #include <string.h>\r
2023 #include <fcntl.h>\r
2024 #include <sys\stat.h>\r
2025 \r
2026 /* num_lines is the number of screen display lines */\r
2027 #define num_lines   25\r
2028 \r
2029 \r
2030 typedef struct\r
2031 {\r
2032      char name[30];\r
2033      char address[30];\r
2034      char town[30];\r
2035      char county[30];\r
2036      char post[12];\r
2037      char telephone[15];\r
2038 }\r
2039 data;\r
2040 \r
2041 data record;\r
2042 int handle;\r
2043 \r
2044 \r
2045 /* Function prototypes */\r
2046 \r
2047 void ADD_REC(void);\r
2048 void CLS(void);\r
2049 void DISPDATA(void);\r
2050 void FATAL(char *);\r
2051 void GETDATA(void);\r
2052 void MENU(void);\r
2053 void OPENDATA(void);\r
2054 int SEARCH(void);\r
2055 \r
2056 void CLS()\r
2057 {\r
2058      int n;\r
2059 \r
2060      for(n = 0; n < num_lines; n++)\r
2061           puts("");\r
2062 }\r
2063 \r
2064 void FATAL(char *error)\r
2065 {\r
2066      printf("\nFATAL ERROR: %s",error);\r
2067      exit(0);\r
2068 }\r
2069 \r
2070 void OPENDATA()\r
2071 {\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
2074 \r
2075      handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);\r
2076 \r
2077      if (handle == -1)\r
2078      {\r
2079           handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);\r
2080           if (handle == -1)\r
2081                FATAL("Unable to create data file");\r
2082      }\r
2083 }\r
2084 \r
2085 void GETDATA()\r
2086 {\r
2087      /* Get address data from operator */\r
2088 \r
2089      CLS();\r
2090 \r
2091      printf("Name ");\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
2103 }\r
2104 \r
2105 void DISPDATA()\r
2106 {\r
2107      /* Display address data */\r
2108      char text[5];\r
2109 \r
2110      CLS();\r
2111 \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
2118 \r
2119      puts("Press RETURN to continue");\r
2120      gets(text);\r
2121 }\r
2122 \r
2123 void ADD_REC()\r
2124 {\r
2125      /* Insert or append a new record to the data file */\r
2126      int result;\r
2127 \r
2128      result = write(handle,&record,sizeof(data));\r
2129 \r
2130      if (result == -1)\r
2131           FATAL("Unable to write to data file");\r
2132 }\r
2133 \r
2134 int SEARCH()\r
2135 {\r
2136      char text[100];\r
2137      int result;\r
2138 \r
2139      printf("Enter data to search for ");\r
2140      gets(text);\r
2141      if (*text == 0)\r
2142           return(-1);\r
2143 \r
2144      /* Locate start of file */\r
2145      lseek(handle,0,SEEK_SET);\r
2146 \r
2147      do\r
2148      {\r
2149           /* Read record into memory */\r
2150           result = read(handle,&record,sizeof(data));\r
2151           if (result > 0)\r
2152           {\r
2153                /* Scan record for matching data */\r
2154                if (strstr(record.name,text) != NULL)\r
2155                     return(1);\r
2156                if (strstr(record.address,text) != NULL)\r
2157                     return(1);\r
2158                if (strstr(record.town,text) != NULL)\r
2159                     return(1);\r
2160                if (strstr(record.county,text) != NULL)\r
2161                     return(1);\r
2162                if (strstr(record.post,text) != NULL)\r
2163                     return(1);\r
2164                if (strstr(record.telephone,text) != NULL)\r
2165                     return(1);\r
2166           }\r
2167      }\r
2168      while(result > 0);\r
2169      return(0);\r
2170 }\r
2171 \r
2172 void MENU()\r
2173 {\r
2174      int option;\r
2175      char text[10];\r
2176 \r
2177      do\r
2178      {\r
2179           CLS();\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
2185           gets(text);\r
2186           option = atoi(text);\r
2187 \r
2188           switch(option)\r
2189           {\r
2190                case 1 : GETDATA();\r
2191                      /* Go to end of file to append new record */\r
2192                      lseek(handle,0,SEEK_END);\r
2193                      ADD_REC();\r
2194                      break;\r
2195 \r
2196                case 2 : if (SEARCH())\r
2197                           DISPDATA();\r
2198                      else\r
2199                      {\r
2200                           puts("NOT FOUND!");\r
2201                           puts("Press RETURN to continue");\r
2202                           gets(text);\r
2203                      }\r
2204                      break;\r
2205 \r
2206                case 3 : break;\r
2207           }\r
2208      }\r
2209      while(option != 3);\r
2210 }\r
2211 \r
2212 void main()\r
2213 {\r
2214      CLS();\r
2215      OPENDATA();\r
2216      MENU();\r
2217 }\r
2218 \r
2219 Bit Fields\r
2220 \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
2224 \r
2225 The general form for declaring a bit field is;\r
2226 \r
2227 \r
2228      type name : number_of_bits;\r
2229 \r
2230 \r
2231 For example, to declare a set of status flags, each occupying one bit;\r
2232 \r
2233 typedef struct\r
2234 {\r
2235      unsigned carry  : 1;\r
2236      unsigned zero   : 1;\r
2237      unsigned over   : 1;\r
2238      unsigned parity : 1;\r
2239 }\r
2240 df;\r
2241 \r
2242 df flags;\r
2243 \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
2246 field.\r
2247 \r
2248 \r
2249 \r
2250 Uunions\r
2251 \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
2256 \r
2257 The general form of a union definition is;\r
2258 \r
2259 \r
2260      union name\r
2261      {\r
2262           type variable_name;\r
2263           type variable_name;\r
2264           .\r
2265           .\r
2266           .\r
2267           type variable_name;\r
2268      };\r
2269 \r
2270 Thus, to declare a union structure for two integer variables;\r
2271 \r
2272      union data\r
2273      {\r
2274           int vara;\r
2275           int varb;\r
2276      };\r
2277 \r
2278 and to declare a variable of type 'data';\r
2279 \r
2280      data my_var;\r
2281 \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
2284 \r
2285      my_var.vara = 5;\r
2286 \r
2287 Assigns a value of 5 to the variable 'vara' of union 'my_var'.\r
2288 \r
2289 \r
2290 Enumerations\r
2291 \r
2292 An enumeration assigns ascending integer values to a list of symbols. An\r
2293 enumeration declaration takes the general form;\r
2294 \r
2295 \r
2296      enum name { enumeration list } variable_list;\r
2297 \r
2298 Thus to define a symbol list of colours, you can say;\r
2299 \r
2300      enum COLOURS\r
2301      {\r
2302           BLACK,\r
2303           BLUE,\r
2304           GREEN,\r
2305           CYAN,\r
2306           RED,\r
2307           MAGENTA,\r
2308           BROWN,\r
2309           LIGHTGRAY,\r
2310           DARKGRAY,\r
2311           LIGHTBLUE,\r
2312           LIGHTGREEN,\r
2313           LIGHTCYAN,\r
2314           LIGHTRED,\r
2315           LIGHTMAGENTA,\r
2316           YELLOW,\r
2317           WHITE\r
2318      };\r
2319 \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
2322 \r
2323 The following program illustrates the use of an enumeration list to\r
2324 symbolise integers;\r
2325 \r
2326      #include <stdio.h>\r
2327      \r
2328      enum COLOURS\r
2329      {\r
2330           BLACK,\r
2331           BLUE,\r
2332           GREEN,\r
2333           CYAN,\r
2334           RED,\r
2335           MAGENTA,\r
2336           BROWN,\r
2337           LIGHTGRAY,\r
2338           DARKGRAY,\r
2339           LIGHTBLUE,\r
2340           LIGHTGREEN,\r
2341           LIGHTCYAN,\r
2342           LIGHTRED,\r
2343           LIGHTMAGENTA,\r
2344           YELLOW,\r
2345           WHITE\r
2346      };\r
2347      \r
2348      \r
2349      void main()\r
2350      {\r
2351           int x;\r
2352      \r
2353           x = RED;\r
2354      \r
2355           printf("\nVariable 'x' holds %d",x);\r
2356      \r
2357      }\r
2358                                     \r
2359                                 FILE I/O\r
2360 \r
2361 \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
2364 \r
2365 \r
2366 \r
2367 Buffered streams\r
2368 \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
2371 a file pointer;\r
2372 \r
2373      #include <stdio.h>\r
2374 \r
2375      FILE *ptr;\r
2376 \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
2380 \r
2381 \r
2382 Mode   Description\r
2383        \r
2384 r      Open for reading\r
2385 w      Create for writing, destroying any\r
2386        existing file\r
2387 a      Open for append, creating a new file\r
2388        if it doesn't\r
2389         exist\r
2390 r+      Open an existing file for reading\r
2391        and writing\r
2392 w+      Create for reading and writing,\r
2393        destroying any\r
2394         existing file\r
2395 a+      Open for append, creating a new file\r
2396        if it doesn't exist.\r
2397 \r
2398 \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
2406 \r
2407 \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
2410 \r
2411 Thus, the following program will create a new file called "data.txt" and\r
2412 open it for reading and writing;\r
2413 \r
2414      #include <stdio.h>\r
2415      \r
2416      void main()\r
2417      {\r
2418           FILE *fp;\r
2419      \r
2420           fp = fopen("data.txt","w+");\r
2421      \r
2422      }\r
2423 \r
2424 To close a stream C provides the function fclose(), which accepts the\r
2425 stream's file pointer as a parameter;\r
2426 \r
2427      fclose(fp);\r
2428 \r
2429 If an error occurs in closing the file stream, fclose() returns non zero.\r
2430 \r
2431 There are four basic functions for receiving and sending data to and from\r
2432 streams; fgetc(), fputc(), fgets() and fputs().\r
2433 \r
2434 fgetc() simply reads a single character from the specified input stream;\r
2435 \r
2436      char fgetc(FILE *fp);\r
2437 \r
2438 Its opposite number is fputc(), which simply writes a single character to\r
2439 the specified input stream;\r
2440 \r
2441      char fputc(char c, FILE *fp);\r
2442 \r
2443 fgets() reads a string from the input stream;\r
2444 \r
2445      char *fgets(char s, int numbytes, FILE *fp);\r
2446 \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
2450 \r
2451 \r
2452 fputs() writes a null terminated string to a stream;\r
2453 \r
2454      int fputs(char *s, FILE *fp);\r
2455 \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
2459 \r
2460 \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
2463 \r
2464 \r
2465      #include <stdio.h>\r
2466      \r
2467      int main()\r
2468      {\r
2469           FILE *in;\r
2470           FILE *out;\r
2471      \r
2472           in = fopen("data.dat","r");\r
2473      \r
2474           if (in == NULL)\r
2475           {\r
2476                puts("\nUnable to open file data.dat for reading");\r
2477                return(0);\r
2478           }\r
2479      \r
2480           out = fopen("data.old","w+");\r
2481      \r
2482           if (out == NULL)\r
2483           {\r
2484                puts("\nUnable to create file data.old");\r
2485                return(0);\r
2486           }\r
2487      \r
2488           /* Loop reading and writing one byte at a time until end-of-\r
2489      file */\r
2490           while(!feof(in))\r
2491                fputc(fgetc(in),out);\r
2492      \r
2493           /* Close the file streams */\r
2494           fclose(in);\r
2495           fclose(out);\r
2496      \r
2497           return(0);\r
2498      }\r
2499 \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
2502 \r
2503      #include <stdio.h>\r
2504      \r
2505      int main()\r
2506      {\r
2507           FILE *fp;\r
2508           char text[100];\r
2509      \r
2510           fp = fopen("data.txt","w+");\r
2511      \r
2512           do\r
2513           {\r
2514                gets(text);\r
2515                fputs(text,fp);\r
2516           }\r
2517           while(*text);\r
2518      \r
2519           fclose(fp);\r
2520      }\r
2521 \r
2522 \r
2523 Random access using streams\r
2524 \r
2525 Random file access for streams is provided for by the fseek() function\r
2526 that has the following prototype;\r
2527 \r
2528      int fseek(FILE *fp, long numbytes, int fromwhere);\r
2529 \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
2536 \r
2537 \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
2540 \r
2541 \r
2542      long int ftell(FILE *fp);\r
2543 \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
2546 \r
2547 \r
2548 \r
2549 Handles\r
2550 \r
2551 File handles are opened with the open() function that has the prototype;\r
2552 \r
2553      int open(char *filename,int access[,unsigned mode]);\r
2554 \r
2555 If open() is successful, the number of the file handle is returned.\r
2556 Otherwise open() returns -1.\r
2557 \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
2561 \r
2562 \r
2563      O_APPEND       If set, the file pointer will be set to the end of\r
2564 the\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
2568 bytes.\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
2572 \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
2575 may be;\r
2576 \r
2577      S_IWRITE       Permission to write\r
2578      S_IREAD        Permission to read\r
2579 \r
2580 \r
2581 Once a file handle has been assigned with open(), the file may be\r
2582 accessed with read() and write().\r
2583 \r
2584 Read() has the function prototype;\r
2585 \r
2586      int read(int handle, void *buf, unsigned num_bytes);\r
2587 \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
2591 \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
2594 'buf'.\r
2595 \r
2596 Files opened with open() are closed using close() that has the function\r
2597 prototype;\r
2598 \r
2599      int close(int handle);\r
2600 \r
2601 close() returns zero on success, and -1 if an error occurs trying to\r
2602 close the handle.\r
2603 \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
2607 \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
2610 \r
2611      #include <io.h>\r
2612      #include <fcntl.h>\r
2613      #include <sys\stat.h>\r
2614      \r
2615      int main()\r
2616      {\r
2617           int handle;\r
2618           char text[100];\r
2619      \r
2620           handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);\r
2621      \r
2622           do\r
2623           {\r
2624                gets(text);\r
2625                write(handle,&text,strlen(text));\r
2626           }\r
2627           while(*text);\r
2628      \r
2629           close(handle);\r
2630      }\r
2631 \r
2632 \r
2633 \r
2634 Advanced File I/O\r
2635 \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
2638 \r
2639 \r
2640 fopen() has the prototype;\r
2641 \r
2642      FILE *fopen(const char *name,const char *mode);\r
2643 \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
2647 \r
2648 \r
2649 Mode      Description\r
2650  r        Open for reading only\r
2651  w        Create for writing, overwriting any existing file with the same\r
2652           name.\r
2653  a        Open for append (writing at end of file) or create the file if\r
2654 it\r
2655           does not exist.\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
2659 \r
2660 \r
2661 fclose() is used to close a file stream previously opened by a call to\r
2662 fopen(). It has the prototype;\r
2663 \r
2664      int fclose(FILE *fp);\r
2665 \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
2668 returns EOF.\r
2669 \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
2677 \r
2678      int fflush(FILE *fp);\r
2679 \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
2682 returns EOF.\r
2683 \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
2686 \r
2687 \r
2688      long int ftell(FILE *fp);\r
2689 \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
2692 \r
2693 Similarly, you can move the file pointer to a new position with fseek().\r
2694 fseek() has the prototype;\r
2695 \r
2696      int fseek(FILE *fp, long offset, int from_what_place);\r
2697 \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
2700 \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
2704 \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
2707 \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
2711 \r
2712      void rewind(FILE *fp);\r
2713 \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
2717 \r
2718 Errors occurring with file functions can be checked with the function\r
2719 ferror() that has the prototype;\r
2720 \r
2721      int ferror(FILE *fp);\r
2722 \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
2727 \r
2728      void clearerr(FILE *fp);\r
2729 \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
2732 \r
2733      int feof(FILE *fp);\r
2734 \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
2738 \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
2741 \r
2742 \r
2743      int fgetc(FILE *fp);\r
2744 \r
2745 fgetc() returns either the character read converted to an integer, or EOF\r
2746 if an error occurred.\r
2747 \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
2751 \r
2752 \r
2753      char *fgets(char s, int n, FILE *fp);\r
2754 \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
2760 \r
2761 \r
2762 Strings may be written to a stream using fputs() that has the prototype;\r
2763 \r
2764      int fputs(const char *s,FILE *fp);\r
2765 \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
2769 \r
2770 To write a single character to a stream use fputc() that has the\r
2771 prototype;\r
2772 \r
2773      int fputc(int c,FILE *fp);\r
2774 \r
2775 If successful, fputc() returns the character written, otherwise it\r
2776 returns EOF.\r
2777 \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
2780 \r
2781      size_t fread(void *ptr,size_t size, size_t n, FILE *fp);\r
2782 \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
2786 \r
2787 The sister function to fread() is fwrite() that has the prototype;\r
2788 \r
2789      size_t fwrite(const void *ptr,size_t size, size_t n,FILE *fp);\r
2790 \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
2793 \r
2794 Formatted input from a stream is achieved with fscanf() that has the\r
2795 prototype;\r
2796 \r
2797      int fscanf(FILE *fp, const char *format[,address ...]);\r
2798 \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
2802 \r
2803      #include <stdio.h>\r
2804      \r
2805      void main()\r
2806      {\r
2807           FILE *fp;\r
2808           int a;\r
2809           int b;\r
2810           int c;\r
2811           int d;\r
2812           int e;\r
2813           char text[100];\r
2814      \r
2815           fp = fopen("data.txt","w+");\r
2816      \r
2817           if(!fp)\r
2818           {\r
2819                perror("Unable to create file");\r
2820                exit(0);\r
2821           }\r
2822      \r
2823           fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");\r
2824      \r
2825           fflush(fp);\r
2826      \r
2827           if (ferror(fp))\r
2828           {\r
2829                fputs("Error flushing stream",stderr);\r
2830                exit(1);\r
2831           }\r
2832      \r
2833           rewind(fp);\r
2834           if (ferror(fp))\r
2835           {\r
2836                fputs("Error rewind stream",stderr);\r
2837                exit(1);\r
2838           }\r
2839      \r
2840           fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);\r
2841           if (ferror(fp))\r
2842           {\r
2843                fputs("Error reading from stream",stderr);\r
2844                exit(1);\r
2845           }\r
2846      \r
2847           printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);\r
2848      }\r
2849      \r
2850 As you can see from the example, fprintf() can be used to write formatted\r
2851 data to a stream.\r
2852 \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
2857 \r
2858      int fgetpos(FILE *fp, fpos_t *pos);\r
2859 \r
2860 fsetpos() repositions the file pointer and has the prototype;\r
2861 \r
2862      int fsetpos(FILE *fp, const fpos_t *fpos);\r
2863 \r
2864 fpos_t is defined in stdio.h.\r
2865 \r
2866 These functions are more convenient than doing an ftell() followed by an\r
2867 fseek().\r
2868 \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
2871 \r
2872      FILE *freopen(const char *name,const char *mode,FILE *fp);\r
2873 \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
2877 \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
2880 \r
2881      freopen("LPT1","w",stdout);\r
2882 \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
2885 \r
2886 \r
2887 Predefined I/O Streams\r
2888 \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
2895 such as with;\r
2896 \r
2897       fputs("Error message",stderr);\r
2898 \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
2902 \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
2906 \r
2907           dump filename.ext > target.ext\r
2908 \r
2909      #include <stdio.h>\r
2910      #include <fcntl.h>\r
2911      #include <io.h>\r
2912      #include <string.h>\r
2913      \r
2914      main(int argc, char *argv[])\r
2915      {\r
2916           unsigned counter;\r
2917           unsigned char v1[20];\r
2918           int f1;\r
2919           int x;\r
2920           int n;\r
2921      \r
2922           if (argc != 2)\r
2923           {\r
2924                fputs("\nERROR: Syntax is dump f1\n",stderr);\r
2925                return(1);\r
2926           }\r
2927      \r
2928           f1 = open(argv[1],O_RDONLY);\r
2929      \r
2930           if (f1 == -1)\r
2931           {\r
2932                fprintf(stderr,"\nERROR: Unable to open %s\n",argv[1]);\r
2933                return(1);\r
2934           }\r
2935      \r
2936           fprintf(stdout,"\nDUMP OF FILE %s\n\n",strupr(argv[1]));\r
2937      \r
2938           counter = 0;\r
2939      \r
2940           while(1)\r
2941           {\r
2942                /* Set buffer to zero bytes */\r
2943                memset(v1,0,20);\r
2944      \r
2945                /* Read buffer from file */\r
2946                x = _read(f1,&v1,16);\r
2947      \r
2948                /* x will be 0 on EOF or -1 on error */\r
2949                if (x < 1)\r
2950                     break;\r
2951      \r
2952                /* Print file offset to stdout */\r
2953                fprintf(stdout,"%06d(%05x) ",counter,counter);\r
2954      \r
2955                counter += 16;\r
2956      \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
2960      \r
2961                /* Print ascii values of buffer to stdout */\r
2962                for(n = 0; n < 16; n++)\r
2963                {\r
2964                     if ((v1[n] > 31) && (v1[n] < 128))\r
2965                          fprintf(stdout,"%c",v1[n]);\r
2966                     else\r
2967                          fputs(".",stdout);\r
2968                }\r
2969      \r
2970                /* Finish the line with a new line */\r
2971                fputs("\n",stdout);\r
2972           }\r
2973      \r
2974           /* successful termination */\r
2975           return(0);\r
2976      }\r
2977                                     \r
2978                                  STRINGS\r
2979 \r
2980 The C language has one of the most powerful string handling capabilities\r
2981 of any general purpose computer language.\r
2982 \r
2983 A string is a single dimension array of characters terminated by a zero\r
2984 byte.\r
2985 \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
2988 \r
2989      int main()\r
2990      {\r
2991           char *p = "System 5";\r
2992           char name[] = "Test Program" ;\r
2993      }\r
2994 \r
2995 or at run time by the function strcpy() that has the function prototype;\r
2996 \r
2997      char *strcpy(char *destination, char *source);\r
2998 \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
3001 \r
3002 \r
3003      #include<stdio.h>\r
3004      \r
3005      int main()\r
3006      {\r
3007           char name[50];\r
3008      \r
3009           strcpy(name,"Servile Software");\r
3010      \r
3011           printf("\nName equals %s",name);\r
3012      }\r
3013 \r
3014 C also allows direct access to each individual byte of the string, so the\r
3015 following is quite permissible;\r
3016 \r
3017      #include<stdio.h>\r
3018      \r
3019      int main()\r
3020      {\r
3021           char name[50];\r
3022      \r
3023           strcpy(name,"Servile Software");\r
3024      \r
3025           printf("\nName equals %s",name);\r
3026      \r
3027           /* Replace first byte with lower case 's' */\r
3028           name[0] = 's';\r
3029      \r
3030           printf("\nName equals %s",name);\r
3031      }\r
3032      \r
3033 The ANSI standard on the C programming language defines the following\r
3034 functions for use with strings;\r
3035 \r
3036 char *strcat(char *dest, char *source)            Appends string source\r
3037 to the end of string destination.\r
3038 \r
3039 char *strchr(char *s, int c)            Returns a pointer to the first\r
3040 occurence of character 'c' within s.\r
3041 \r
3042 int strcmp(char *s1, char *s2)               Compares strings s1 and s2\r
3043 returning        < 0 if s1 is less than s2\r
3044                                                        == 0 if s1 and s2\r
3045 are the same\r
3046                                                        > 0 if s1 is\r
3047 greater than s2\r
3048 \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
3052 than s2\r
3053                                              == 0 if s1 and s2 are the\r
3054 same\r
3055                                              > 0 if s1 is greater than s2\r
3056 \r
3057 char *strcpy(char *dest, char *src)          Copies string src into\r
3058 string dest.\r
3059 \r
3060 unsigned strcspn(char *s1, char *s2)         Returns the length of string\r
3061 s1 that consists entirely of characters not in\r
3062 string s2.\r
3063 \r
3064 unsigned strlen(char *s)                Returns the length of string s.\r
3065 \r
3066 char *strncat(char *dest, char *src, unsigned len)     Copies at most\r
3067 'len' characters from string src into string dest.\r
3068 \r
3069 int strncmp(char *s1, char *s2, unsigned len)     Compares at most 'len'\r
3070 characters from\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
3074 the same\r
3075                                                   > 0 if s1 is greater\r
3076 than s2\r
3077 \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
3081 \r
3082 char *strpbrk(char *s1, char *s2)            Returns a pointer to the\r
3083 first character in string s1 that occurs in\r
3084                               string s2.\r
3085 \r
3086 char *strrchr(char *s, int c)           Returns a pointer to the last\r
3087 occurence of 'c' within string s.\r
3088 \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
3092 \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
3096 string s1.\r
3097 \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
3101 tokens are found.\r
3102 \r
3103 The ANSI standard also defines various functions for converting strings\r
3104 into numbers and numbers into strings.\r
3105 \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
3109 return an\r
3110 \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
3113 \r
3114 \r
3115      #include<stdio.h>\r
3116      \r
3117      void strupr(char *source)\r
3118      {\r
3119           char *p;\r
3120      \r
3121           p = source;\r
3122           while(*p)\r
3123           {\r
3124                *p = toupper(*p);\r
3125                p++;\r
3126           }\r
3127      }\r
3128      \r
3129      void strlwr(char *source)\r
3130      {\r
3131           char *p;\r
3132      \r
3133           p = source;\r
3134           while(*p)\r
3135           {\r
3136                *p = tolower(*p);\r
3137                p++;\r
3138           }\r
3139      }\r
3140 \r
3141 \r
3142      int main()\r
3143      {\r
3144           char name[50];\r
3145      \r
3146           strcpy(name,"Servile Software");\r
3147      \r
3148           printf("\nName equals %s",name);\r
3149      \r
3150           strupr(name);\r
3151      \r
3152           printf("\nName equals %s",name);\r
3153      \r
3154           strlwr(name);\r
3155      \r
3156           printf("\nName equals %s",name);\r
3157      }\r
3158 \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
3163 \r
3164 An example program to reverse all the characters in a string.\r
3165 \r
3166      #include <stdio.h>\r
3167      #include <string.h>\r
3168      \r
3169      char *strrev(char *s)\r
3170      {\r
3171           /* Reverses the order of all characters in a string except the\r
3172      null */\r
3173           /* terminating byte */\r
3174      \r
3175           char *start;\r
3176           char *end;\r
3177           char tmp;\r
3178      \r
3179           /* Set pointer 'end' to last character in string */\r
3180           end = s + strlen(s) - 1;\r
3181      \r
3182           /* Preserve pointer to start of string */\r
3183           start = s;\r
3184      \r
3185           /* Swop characters */\r
3186           while(end >= s)\r
3187           {\r
3188                tmp = *end;\r
3189                *end = *s;\r
3190                *s = tmp;\r
3191                end--;\r
3192                s++;\r
3193           }\r
3194           return(start);\r
3195      }\r
3196      \r
3197      \r
3198      main()\r
3199      {\r
3200           char text[100];\r
3201           char *p;\r
3202      \r
3203           strcpy(text,"This is a string of data");\r
3204      \r
3205           p = strrev(text);\r
3206      \r
3207           printf("\n%s",p);\r
3208      }\r
3209      \r
3210 \r
3211 Strtok()\r
3212 \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
3217 \r
3218      #include <stdio.h>\r
3219      #include <string.h>\r
3220      \r
3221      main()\r
3222      {\r
3223           char data[50];\r
3224           char *p;\r
3225      \r
3226           strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");\r
3227      \r
3228           p = strtok(data,",");\r
3229           while(p)\r
3230           {\r
3231                puts(p);\r
3232                p = strtok(NULL,",");\r
3233           };\r
3234      }\r
3235      \r
3236 Or this program can be written with a for() loop thus;\r
3237 \r
3238      #include <stdio.h>\r
3239      #include <string.h>\r
3240      \r
3241      main()\r
3242      {\r
3243           char data[50];\r
3244           char *p;\r
3245      \r
3246           strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");\r
3247      \r
3248           for(strtok(data,","); p; p = strtok(NULL,","))\r
3249           {\r
3250                puts(p);\r
3251           };\r
3252      }\r
3253      \r
3254 They both compile to the same code but follow different programming\r
3255 styles.\r
3256 \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
3263 \r
3264 \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
3267 program;\r
3268 \r
3269      #include <stdio.h>\r
3270      #include <stdlib.h>\r
3271      #include <string.h>\r
3272      \r
3273      void main(int argc, char *argv[])\r
3274      {\r
3275           FILE *fp;\r
3276           char buffer[256];\r
3277           char *p;\r
3278           long count;\r
3279      \r
3280           if (argc != 2)\r
3281           {\r
3282                fputs("\nERROR: Usage is wordcnt <file>\n",stderr);\r
3283                exit(0);\r
3284           }\r
3285      \r
3286           /* Open file for reading */\r
3287           fp = fopen(argv[1],"r");\r
3288      \r
3289           /* Check the open was okay */\r
3290           if (!fp)\r
3291           {\r
3292                fputs("\nERROR: Cannot open source file\n",stderr);\r
3293                exit(0);\r
3294           }\r
3295      \r
3296           /* Initialise word count */\r
3297           count = 0;\r
3298      \r
3299           do\r
3300           {\r
3301                /* Read a line of data from the file */\r
3302                fgets(buffer,255,fp);\r
3303      \r
3304                /* check for an error in the read or EOF */\r
3305                if (ferror(fp) || feof(fp))\r
3306                     continue;\r
3307      \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
3312                while(p)\r
3313                {\r
3314                     count++;\r
3315                     p = strtok(NULL,"\t\n,;:.!?()- ");\r
3316                }\r
3317           }\r
3318           while(!ferror(fp) && !feof(fp));\r
3319      \r
3320           /* Finished reading. Was it due to an error? */\r
3321           if (ferror(fp))\r
3322           {\r
3323                fputs("\nERROR: Reading source file\n",stderr);\r
3324                fclose(fp);\r
3325                exit(0);\r
3326           }\r
3327      \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
3330           fclose(fp);\r
3331      }\r
3332      \r
3333 \r
3334 \r
3335 \r
3336 Converting Numbers To And From Strings\r
3337 \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
3346 enough.\r
3347 \r
3348 Example function for copying a signed integer into a string;\r
3349 \r
3350      void ITOS(long x, char *ptr)\r
3351      {\r
3352           /* Convert a signed decimal integer to a string */\r
3353      \r
3354           long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,\r
3355      1000, 100, 10, 1 };\r
3356           int n;\r
3357      \r
3358           /* Check sign */\r
3359           if (x < 0)\r
3360           {\r
3361                *ptr++ = '-';\r
3362                /* Convert x to absolute */\r
3363                x = 0 - x;\r
3364           }\r
3365      \r
3366           for(n = 0; n < 9; n++)\r
3367           {\r
3368                if (x > pt[n])\r
3369                {\r
3370                     *ptr++ = '0' + x / pt[n];\r
3371                     x %= pt[n];\r
3372                }\r
3373           }\r
3374           return;\r
3375      }\r
3376      \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
3379 \r
3380      double atof(const char *s);\r
3381 \r
3382 strtod has the prototype;\r
3383 \r
3384      double strtod(const char *s,char **endptr);\r
3385 \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
3392 atof().\r
3393 \r
3394 To convert a string to an integer use atoi() that has the prototype;\r
3395 \r
3396 \r
3397      int atoi(const char *s);\r
3398 \r
3399 atoi() does not check for an overflow, and the results are undefined!\r
3400 \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
3403 \r
3404                                     \r
3405                               TEXT HANDLING\r
3406 \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
3411 \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
3417 functions.\r
3418 \r
3419 stricmp() for example is the case insensitive form of strcmp(), and\r
3420 strnicmp() is the case insensitive form of strncmp().\r
3421 \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
3425 \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
3432 \r
3433 \r
3434      char *stristr(char *s1, char *s2)\r
3435      {\r
3436           char c1[1000];\r
3437           char c2[1000];\r
3438           char *p;\r
3439      \r
3440           strcpy(c1,s1);\r
3441           strcpy(c2,s2);\r
3442      \r
3443           strupr(c1);\r
3444           strupr(c2);\r
3445      \r
3446           p = strstr(c1,c2);\r
3447           if (p)\r
3448                return s1 + (p - c1);\r
3449           return NULL;\r
3450      }\r
3451      \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
3455 previously.\r
3456 \r
3457      int word_in(char *s1,char *s2)\r
3458      {\r
3459           /* return non-zero if s2 occurs as a word in s1 */\r
3460           char *p;\r
3461           char *q;\r
3462           int ok;\r
3463      \r
3464           ok = 0;\r
3465           q = s1;\r
3466      \r
3467           do\r
3468           {\r
3469                /* Locate character occurence s2 in s1 */\r
3470                p = stristr(q,s2);\r
3471                if (p)\r
3472                {\r
3473                     /* Found */\r
3474                     ok = 1;\r
3475      \r
3476                     if (p > s1)\r
3477                     {\r
3478                          /* Check previous character */\r
3479                          if (*(p - 1) >= 'A' && *(p - 1) <= 'z')\r
3480                               ok = 0;\r
3481                     }\r
3482      \r
3483                     /* Move p to end of character set */\r
3484                     p += strlen(s2);\r
3485                     if (*p)\r
3486                     {\r
3487                          /* Check character following */\r
3488                          if (*p >= 'A' && *p <= 'z')\r
3489                               ok = 0;\r
3490                     }\r
3491                }\r
3492                q = p;\r
3493           }\r
3494           while(p && !ok);\r
3495           return ok;\r
3496      }\r
3497 \r
3498 \r
3499 Some more useful functions for dealing with text are truncstr() that\r
3500 truncates a string;\r
3501 \r
3502      void truncstr(char *p,int num)\r
3503      {\r
3504           /* Truncate string by losing last num characters */\r
3505           if (num < strlen(p))\r
3506                p[strlen(p) - num] = 0;\r
3507      }\r
3508      \r
3509 trim() that removes trailing spaces from the end of a string;\r
3510 \r
3511      void trim(char *text)\r
3512      {\r
3513           /* remove trailing spaces */\r
3514           char *p;\r
3515      \r
3516           p = &text[strlen(text) - 1];\r
3517           while(*p == 32 && p >= text)\r
3518                *p-- = 0;\r
3519      }\r
3520      \r
3521 strlench() that changes the length of a string by adding or deleting\r
3522 characters;\r
3523 \r
3524      void strlench(char *p,int num)\r
3525      {\r
3526           /* Change length of string by adding or deleting characters */\r
3527      \r
3528           if (num > 0)\r
3529                memmove(p + num,p,strlen(p) + 1);\r
3530           else\r
3531           {\r
3532                num = 0 - num;\r
3533                memmove(p,p + num,strlen(p) + 1);\r
3534           }\r
3535      }\r
3536      \r
3537 strins() that inserts a string into another string;\r
3538 \r
3539      void strins(char *p, char *q)\r
3540      {\r
3541           /* Insert string q into p */\r
3542           strlench(p,strlen(q));\r
3543           strncpy(p,q,strlen(q));\r
3544      }\r
3545      \r
3546 strchg() that replaces all occurences of one sub-string with another\r
3547 within a target string;\r
3548 \r
3549      void strchg(char *data, char *s1, char *s2)\r
3550      {\r
3551           /* Replace all occurences of s1 with s2 */\r
3552           char *p;\r
3553           char changed;\r
3554      \r
3555           do\r
3556           {\r
3557                changed = 0;\r
3558                p = strstr(data,s1);\r
3559                if (p)\r
3560                {\r
3561                     /* Delete original string */\r
3562                     strlench(p,0 - strlen(s1));\r
3563      \r
3564                     /* Insert replacement string */\r
3565                     strins(p,s2);\r
3566                     changed = 1;\r
3567                }\r
3568           }\r
3569           while(changed);\r
3570      }\r
3571                                     \r
3572                                   TIME\r
3573 \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
3578 \r
3579      #include <stdio.h>\r
3580      #include <time.h>\r
3581      \r
3582      int main()\r
3583      {\r
3584           /* Structure to hold time, as defined in time.h  */\r
3585           time_t t;\r
3586      \r
3587           /* Get system date and time from computer */\r
3588           t = time(NULL);\r
3589           printf("Today's date and time: %s\n",ctime(&t));\r
3590      }\r
3591      \r
3592 The string returned by ctime() is comprised of seven fields;\r
3593 \r
3594      Day of the week,\r
3595      Month of the year,\r
3596      Date of the day of the month,\r
3597      hour,\r
3598      minutes,\r
3599      seconds,\r
3600      century of the year\r
3601 \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
3607 \r
3608 \r
3609      #include <stdio.h>\r
3610      #include <time.h>\r
3611      \r
3612      struct time\r
3613      {\r
3614           int ti_min;         /* Minutes */\r
3615           int ti_hour;        /* Hours */\r
3616           int ti_sec;         /* Seconds */\r
3617      };\r
3618      \r
3619      void gettime(struct time *now)\r
3620      {\r
3621           time_t t;\r
3622           char temp[26];\r
3623           char *ts;\r
3624      \r
3625           /* Get system date and time from computer */\r
3626           t = time(NULL);\r
3627      \r
3628           /* Translate dat and time into a string */\r
3629           strcpy(temp,ctime(&t));\r
3630      \r
3631           /* Copy out just time part of string */\r
3632           temp[19] = 0;\r
3633           ts = &temp[11];\r
3634      \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
3637      >ti_sec);\r
3638      }\r
3639      \r
3640      int main()\r
3641      {\r
3642           struct time now;\r
3643      \r
3644           gettime(&now);\r
3645      \r
3646           printf("\nThe time is\r
3647      %02d:%02d:%02d",now.ti_hour,now.ti_min,now.ti_sec);\r
3648      \r
3649      }\r
3650 \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
3653 \r
3654      #include <stdio.h>\r
3655      #include <time.h>\r
3656      \r
3657      int main()\r
3658      {\r
3659           time_t t;\r
3660           struct tm *tb;\r
3661      \r
3662           /* Get time into t */\r
3663           t = time(NULL);\r
3664      \r
3665           /* Convert time value t into structure pointed to by tb */\r
3666           tb = localtime(&t);\r
3667      \r
3668           printf("\nTime is %02d:%02d:%02d",tb->tm_hour,tb->tm_min,tb-\r
3669      >tm_sec);\r
3670      }\r
3671      \r
3672 The structure 'tm' is defined in time.h as;\r
3673 \r
3674      struct tm\r
3675      {\r
3676           int tm_sec;\r
3677           int tm_min;\r
3678           int tm_hour;\r
3679           int tm_mday;\r
3680           int tm_mon;\r
3681           int tm_year;\r
3682           int tm_wday;\r
3683           int tm_yday;\r
3684           int tm_isdst;\r
3685      };\r
3686 \r
3687 \r
3688 Timers\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
3693 \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
3696 \r
3697      time_t time(time_t *timer);\r
3698 \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
3702 \r
3703      #include <time.h>\r
3704      \r
3705      void main()\r
3706      {\r
3707           time_t now;\r
3708      \r
3709           now = time(NULL);\r
3710      }\r
3711      \r
3712 asctime() converts a time block to a twenty six character string of the\r
3713 format;\r
3714 \r
3715                 Wed Oct 14 10:23:45 1992\n\0\r
3716 \r
3717 asctime() has the prototype;\r
3718 \r
3719                char *asctime(const struct tm *tblock);\r
3720 \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
3723 \r
3724      #include <stdio.h>\r
3725      #include <time.h>\r
3726      \r
3727      void main()\r
3728      {\r
3729           time_t now;\r
3730           char date[30];\r
3731      \r
3732           now = time(NULL);\r
3733           strcpy(date,ctime(&now));\r
3734      }\r
3735      \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
3740 \r
3741 An example delay program;\r
3742 \r
3743      #include <stdio.h>\r
3744      #include <time.h>\r
3745      \r
3746      \r
3747      void DELAY(int period)\r
3748      {\r
3749           time_t start;\r
3750      \r
3751           start = time(NULL);\r
3752           while(time(NULL) < start + period)\r
3753                ;\r
3754      }\r
3755      \r
3756      void main()\r
3757      {\r
3758           printf("\nStarting delay now....(please wait 5 seconds)");\r
3759      \r
3760           DELAY(5);\r
3761      \r
3762           puts("\nOkay, I've finished!");\r
3763      }\r
3764 \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
3768 \r
3769 \r
3770 The time block is a predefined structure (declared in time.h) as follows;\r
3771 \r
3772      struct tm\r
3773      {\r
3774           int tm_sec;\r
3775           int tm_min;\r
3776           int tm_hour;\r
3777           int tm_mday;\r
3778           int tm_mon;\r
3779           int tm_year;\r
3780           int tm_wday;\r
3781           int tm_yday;\r
3782           int tm_isdst;\r
3783      };\r
3784 \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
3790 essence.\r
3791 \r
3792 mktime() converts a time block to a calendar format. It follows the\r
3793 prototype;\r
3794 \r
3795                 time_t mktime(struct tm *t);\r
3796 \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
3800 \r
3801      #include <stdio.h>\r
3802      #include <time.h>\r
3803      #include <string.h>\r
3804      \r
3805      void main()\r
3806      {\r
3807           struct tm tsruct;\r
3808           int okay;\r
3809           char data[100];\r
3810           char *p;\r
3811           char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",\r
3812      "Thursday", "Friday", "Saturday" ,\r
3813                     "prior to 1970, thus not known" };\r
3814           do\r
3815           {\r
3816                okay = 0;\r
3817                printf("\nEnter a date as dd/mm/yy ");\r
3818                p = fgets(data,8,stdin);\r
3819                p = strtok(data,"/");\r
3820      \r
3821                if (p != NULL)\r
3822                     tsruct.tm_mday = atoi(p);\r
3823                else\r
3824                     continue;\r
3825      \r
3826                p = strtok(NULL,"/");\r
3827                if (p != NULL)\r
3828                     tsruct.tm_mon = atoi(p);\r
3829                else\r
3830                     continue;\r
3831      \r
3832                p = strtok(NULL,"/");\r
3833      \r
3834                if (p != NULL)\r
3835                     tsruct.tm_year = atoi(p);\r
3836                else\r
3837                     continue;\r
3838                okay = 1;\r
3839           }\r
3840           while(!okay);\r
3841      \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
3846      \r
3847           /* Now get day of the week */\r
3848           if (mktime(&tsruct) == -1)\r
3849           tsruct.tm_wday = 7;\r
3850      \r
3851           printf("That was %s\n",wday[tsruct.tm_wday]);\r
3852      }\r
3853      \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
3856 days time thus;\r
3857 \r
3858 \r
3859      #include <stdio.h>\r
3860      #include <time.h>\r
3861      #include <string.h>\r
3862      \r
3863      void main()\r
3864      {\r
3865           struct tm *tsruct;\r
3866           time_t today;\r
3867      \r
3868           today = time(NULL);\r
3869           tsruct = localtime(&today);\r
3870      \r
3871           tsruct->tm_mday += 10;\r
3872           mktime(tsruct);\r
3873      \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
3876      \r
3877      }\r
3878      \r
3879 \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
3882 \r
3883      char *WDAY(int day, int month, int year)\r
3884      {\r
3885           /* Returns a pointer to a string representing the day of the\r
3886      week */\r
3887      \r
3888           static char *cday[] = { "Saturday","Sunday","Monday","Tuesday",\r
3889      "Wednesday","Thursday","Friday" };\r
3890           double yy;\r
3891           double yt;\r
3892           double j;\r
3893           int y1;\r
3894           int y4;\r
3895           int x;\r
3896      \r
3897           yy = year / 100;\r
3898           y1 = (int)(yy);\r
3899           yt = year / 400;\r
3900           y4 = (int)(yt);\r
3901           x = 0;\r
3902      \r
3903           if (month < 3)\r
3904           {\r
3905                year--;\r
3906                x = 12;\r
3907           }\r
3908      \r
3909           j = day + (int)(365.25*year);\r
3910      \r
3911           j += (int)(30.6001 * (month + 1 + x)) - y1 + y4;\r
3912      \r
3913           if (yy == y1 && yt != y4 && month < 3)\r
3914                j++;\r
3915      \r
3916           j = 1 + j - 7 * (int)(j/7);\r
3917      \r
3918           if (j > 6)\r
3919                j -= 7;\r
3920      \r
3921           return(cday[j]);\r
3922      }\r
3923      \r
3924      \r
3925 With time() and difftime() we can create a timer for testing the\r
3926 execution times of functions thus;\r
3927 \r
3928      #include <stdio.h>\r
3929      #include <time.h>\r
3930      \r
3931      main()\r
3932      {\r
3933           time_t now;\r
3934           time_t then;\r
3935           double elapsed;\r
3936      \r
3937           int n;\r
3938      \r
3939           now = time(NULL);\r
3940      \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
3944                func();\r
3945      \r
3946           then = time(NULL);\r
3947      \r
3948           elapsed = difftime(then,now);\r
3949           printf("\nElapsed seconds==%lf\n",elapsed);\r
3950      }\r
3951      \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
3954 \r
3955      #include <stdio.h>\r
3956      #include <time.h>\r
3957      \r
3958      main()\r
3959      {\r
3960           time_t now;\r
3961           char *date;\r
3962           int n;\r
3963      \r
3964           /* Get system time */\r
3965           now = time(NULL);\r
3966      \r
3967           /* Convert system time to a string */\r
3968           date = ctime(&now);\r
3969      \r
3970           /*Display system time */\r
3971           printf("\nIt is %s",date);\r
3972      }\r
3973      \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
3977                                     \r
3978                               HEADER FILES\r
3979 \r
3980 Function prototypes for library functions supplied with the C compiler,\r
3981 and standard macros are declared in header files.\r
3982 \r
3983 The ANSI standard on the C programming language lists the following\r
3984 header files;\r
3985 \r
3986 Header file    Description\r
3987                \r
3988 assert.h       Defines the assert debugging macro\r
3989 ctype.h        Character classification and\r
3990                conversion macros\r
3991 errno.h        Constant mnemonics for error codes\r
3992 float.h        Defines implementation specific\r
3993                macros for dealing with floating\r
3994                point mathematics\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
4000                setjmp/longjmp\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
4004                lists\r
4005 stddef.h       Common data types and macros\r
4006 stdio.h        Types and macros required for\r
4007                standard I/O\r
4008 stdlib.h       Prototypes of commonly used functions\r
4009                and miscellany\r
4010 string.h       String manipulation function\r
4011                prototypes\r
4012 time.h         Structures for time conversion\r
4013                routines\r
4014                                     \r
4015                                 DEBUGGING\r
4016 \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
4020 comprised of:\r
4021 \r
4022 \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
4026 pointer!\r
4027 \r
4028      #include <stdio.h>\r
4029      #include <assert.h>\r
4030      \r
4031      main()\r
4032      {\r
4033           /* Demonstration of assert */\r
4034      \r
4035           int *ptr;\r
4036           int x;\r
4037      \r
4038           x = 0;\r
4039      \r
4040           /* Whoops! error in this line! */\r
4041           ptr = x;\r
4042      \r
4043           assert(ptr != NULL);\r
4044      }\r
4045      \r
4046 When run, this program terminates with the message:\r
4047 \r
4048      Assertion failed: ptr != 0, file TEST.C, line 16\r
4049      Abnormal program termination\r
4050 \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
4053 \r
4054      #define NDEBUG\r
4055 \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
4059 side effects.\r
4060                                     \r
4061                                FLOAT ERRORS\r
4062 \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
4068 \r
4069 \r
4070      #include <stdio.h>\r
4071      \r
4072      void main()\r
4073      {\r
4074           float number;\r
4075      \r
4076           for(number = 1; number > 0.4; number -= 0.01)\r
4077                printf("\n%f",number);\r
4078      }\r
4079      \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
4082 \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
4089 \r
4090 \r
4091                                     \r
4092                              ERROR HANDLING\r
4093 \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
4100 \r
4101 \r
4102      void perror(const char *s);\r
4103 \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
4108 \r
4109 \r
4110      #include <stdio.h>\r
4111      \r
4112      void main()\r
4113      {\r
4114           FILE *fp;\r
4115           char fname[] = "none.xyz";\r
4116      \r
4117           fp = fopen(fname,"r");\r
4118      \r
4119           if(!fp)\r
4120                perror(fname);\r
4121           return;\r
4122      }\r
4123      \r
4124 If the fopen() operation fails, a message similar to;\r
4125 \r
4126      none.xyz: No such file or directory\r
4127 \r
4128 is displayed.\r
4129 \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
4132 \r
4133 \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
4136 \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
4139 \r
4140      "Abnormal program termination"\r
4141 \r
4142 and returns an exit code of 3 to the parent process or operating system.\r
4143 \r
4144 \r
4145 \r
4146 Critical Error Handling With The IBM PC AND DOS\r
4147 \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
4151 familiar;\r
4152 \r
4153      Not ready error writing drive A\r
4154      Abort Retry Ignore?\r
4155 \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
4158 \r
4159 The following example program shows how to redirect the DOS critical\r
4160 error interrupt to your own function;\r
4161 \r
4162 \r
4163      /* DOS critical error handler test */\r
4164      \r
4165      #include <stdio.h>\r
4166      #include <dos.h>\r
4167      \r
4168      void interrupt new_int();\r
4169      void interrupt (*old_int)();\r
4170      \r
4171      char status;\r
4172      \r
4173      main()\r
4174      {\r
4175           FILE *fp;\r
4176      \r
4177           old_int = getvect(0x24);\r
4178      \r
4179           /* Set critical error handler to my function */\r
4180           setvect(0x24,new_int);\r
4181      \r
4182           /* Generate an error by not having a disc in drive A */\r
4183           fp = fopen("a:\\data.txt","w+");\r
4184      \r
4185           /* Display error status returned */\r
4186           printf("\nStatus ==  %d",status);\r
4187      \r
4188      }\r
4189      \r
4190      void interrupt new_int()\r
4191      {\r
4192           /* set global error code */\r
4193           status = _DI;\r
4194      \r
4195           /* ignore error and return */\r
4196           _AL = 0;\r
4197      }\r
4198      \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
4201 \r
4202 Code                     Meaning\r
4203                          \r
4204 00                       Write-protect error\r
4205 01                       Unknown unit\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
4210                          length\r
4211 06                       Seek error\r
4212 07                       Unknown media type\r
4213 08                       Sector not found\r
4214 09                       Printer out of paper\r
4215 0A                       Write error\r
4216 0B                       Read error\r
4217 0C                       General failure\r
4218 \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
4221 AL to one of;\r
4222 \r
4223 \r
4224 Code                     Action\r
4225                          \r
4226 00                       Ignore error\r
4227 01                       Retry\r
4228 02                       Terminate program\r
4229 03                       Fail (Available with\r
4230                          DOS 3.3 and above)\r
4231 \r
4232 \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
4236 \r
4237 \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
4245 \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
4249 \r
4250      int DISCOK(int drive)\r
4251      {\r
4252           /* Checks for whether a disc can be read */\r
4253           /* Returns false (zero) on error */\r
4254           /* Thus if(!DISCOK(drive)) */\r
4255           /*          error();  */\r
4256      \r
4257           unsigned char buffer[25];\r
4258      \r
4259           /* Assume okay */\r
4260           status = 0;\r
4261      \r
4262           /* If already logged to disc, return okay */\r
4263           if ('A' + drive == diry[0])\r
4264                return(1);\r
4265      \r
4266           /* Attempt to read disc */\r
4267           memset(buffer,0,20);\r
4268           sprintf(buffer,"%c:$$$.$$$",'A'+drive);\r
4269      \r
4270           _open(buffer,O_RDONLY);\r
4271      \r
4272           /* Check critical error handler status */\r
4273           if (status == 0)\r
4274                return(1);\r
4275      \r
4276           /* Disc cannot be read */\r
4277           return(0);\r
4278      }\r
4279                                     \r
4280                                   CAST\r
4281 \r
4282 \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
4285 \r
4286      #include <stdio.h>\r
4287      \r
4288      void main()\r
4289      {\r
4290           int x;\r
4291           int y;\r
4292      \r
4293           x = 10;\r
4294           y = 3;\r
4295      \r
4296           printf("\n%lf",x / y);\r
4297      }\r
4298 \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
4303 \r
4304 \r
4305      #include <stdio.h>\r
4306      \r
4307      void main()\r
4308      {\r
4309           int x;\r
4310           int y;\r
4311      \r
4312           x = 10;\r
4313           y = 3;\r
4314      \r
4315           printf("\n%lf",(double)(x / y));\r
4316      }\r
4317 \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
4323 \r
4324      #include <stdio.h>\r
4325      \r
4326      void main()\r
4327      {\r
4328           int x;\r
4329           int y;\r
4330      \r
4331           x = 10;\r
4332           y = 3;\r
4333      \r
4334           printf("\n%lf",(double)(x) / (double)(y));\r
4335      }\r
4336 \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
4339 \r
4340 \r
4341 \r
4342                                     \r
4343                       THE IMPORTANCE OF PROTOTYPING\r
4344 \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
4348 \r
4349 \r
4350      char *strtok(char *s1, const char *s2);\r
4351 \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
4356 change.\r
4357 \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
4369 \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
4373 \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
4376 \r
4377 \r
4378      #include <stdio.h>\r
4379      \r
4380      int FUNCA(int x, int y)\r
4381      {\r
4382           return(MULT(x,y));\r
4383      }\r
4384      \r
4385      double MULT(double x, double y)\r
4386      {\r
4387           return(x * y);\r
4388      }\r
4389      \r
4390      \r
4391      main()\r
4392      {\r
4393           printf("\n%d",FUNCA(5,5));\r
4394      }\r
4395      \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
4402 \r
4403 \r
4404      "Type mismatch in redclaration of function 'MULT'"\r
4405 \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
4409 \r
4410                                     \r
4411                           POINTERS TO FUNCTIONS\r
4412 \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
4417 \r
4418      #include <stdio.h>\r
4419      #include <math.h>\r
4420      \r
4421      double (*fp[7])(double x);\r
4422      \r
4423      void main()\r
4424      {\r
4425           double x;\r
4426           int p;\r
4427      \r
4428           fp[0] = sin;\r
4429           fp[1] = cos;\r
4430           fp[2] = acos;\r
4431           fp[3] = asin;\r
4432           fp[4] = tan;\r
4433           fp[5] = atan;\r
4434           fp[6] = ceil;\r
4435      \r
4436           p = 4;\r
4437      \r
4438           x = fp[p](1.5);\r
4439           printf("\nResult %lf",x);\r
4440      }\r
4441 \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
4445 \r
4446      #include <stdio.h>\r
4447      #include <math.h>\r
4448      \r
4449      void main()\r
4450      {\r
4451           double x;\r
4452           int p;\r
4453      \r
4454           p = 4;\r
4455      \r
4456           switch(p)\r
4457           {\r
4458                case 0 :  x = sin(1.5);\r
4459                      break;\r
4460                case 1 :  x = cos(1.5);\r
4461                      break;\r
4462                case 2 :  x = acos(1.5);\r
4463                      break;\r
4464                case 3 :  x = asin(1.5);\r
4465                      break;\r
4466                case 4 :  x = tan(1.5);\r
4467                      break;\r
4468                case 5 :  x = atan(1.5);\r
4469                      break;\r
4470                case 6 :  x = ceil(1.5);\r
4471                      break;\r
4472           }\r
4473           puts("\nResult %lf",x);\r
4474      }\r
4475 \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
4478 \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
4484 \r
4485                                     \r
4486                             DANGEROUS PITFALLS\r
4487 \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
4499 \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
4502 \r
4503 \r
4504      void main()\r
4505      {\r
4506           int x;\r
4507           int y;\r
4508      \r
4509           scanf("%d%d",&x,&y);\r
4510      }\r
4511      \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
4515 \r
4516 \r
4517      void main()\r
4518      {\r
4519           int x;\r
4520           int y;\r
4521      \r
4522           scanf("%d%d",x,y);\r
4523      }\r
4524      \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
4527 strtoul().\r
4528 \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
4531 \r
4532      #include <stdio.h>\r
4533      #include <string.h>\r
4534      \r
4535      void main()\r
4536      {\r
4537           char input[80];\r
4538           char *p;\r
4539      \r
4540           puts("\nEnter a string ");\r
4541           fgets(input,79,stdin);\r
4542      \r
4543           /* now parse string for input fields */\r
4544           puts("The fields entered are:");\r
4545           p = strtok(input,", ");\r
4546           while(p)\r
4547           {\r
4548                puts(p);\r
4549                p = strtok(NULL,", ");\r
4550           }\r
4551      }\r
4552      \r
4553                                     \r
4554                                  SIZEOF\r
4555 \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
4559 \r
4560 \r
4561      #include <stdio.h>\r
4562      #include <mem.h>\r
4563      \r
4564      char string1[80]; char *text = "This is a string of data" ;\r
4565      \r
4566      void main()\r
4567      {\r
4568           /* Initialise string1 correctly */\r
4569           memset(string1,0,sizeof(string1));\r
4570      \r
4571           /* Copy some text into string1 ? */\r
4572           memcpy(string1,text,sizeof(text));\r
4573      \r
4574           /* Display string1 */\r
4575           printf("\nString 1 = %s\n",string1);\r
4576      }\r
4577      \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
4585 noticed.\r
4586 \r
4587                                     \r
4588                                INTERRUPTS\r
4589 \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
4597 \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
4601 _dos_setvect().\r
4602 \r
4603 getvect() has the function prototype;\r
4604 \r
4605      void interrupt(*getvect(int interrupt_no))();\r
4606 \r
4607 setvect() has the prototype;\r
4608 \r
4609      void setvect(int interrupt_no, void interrupt(*func)());\r
4610 \r
4611 To read and save the address of an existing interrupt a program uses\r
4612 getvect() thus;\r
4613 \r
4614      /* Declare variable to record old interrupt */\r
4615      void interrupt(*old)(void);\r
4616      \r
4617      main()\r
4618      {\r
4619           /* get old interrupt vector */\r
4620           old = getvect(0x1C);\r
4621           .\r
4622           .\r
4623           .\r
4624      }\r
4625      \r
4626 Where 0x1C is the interrupt vector to be retrieved.\r
4627 \r
4628 To then set the interrupt vector to a new address, our own function, we\r
4629 use setvect() thus;\r
4630 \r
4631      void interrupt new(void)\r
4632      {\r
4633           .\r
4634           .\r
4635           /* New interrupt function */\r
4636           .\r
4637           .\r
4638           .\r
4639      }\r
4640      \r
4641      main()\r
4642      {\r
4643           .\r
4644           .\r
4645           .\r
4646           setvect(0x1C,new);\r
4647           .\r
4648           .\r
4649           .\r
4650           .\r
4651      }\r
4652      \r
4653 There are two important points to note about interrupts;\r
4654 \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
4660 \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
4665 \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
4672 terminates.\r
4673 \r
4674      \r
4675      /* Compile in LARGE memory model */\r
4676      \r
4677      #include <stdio.h>\r
4678      #include <dos.h>\r
4679      #include <time.h>\r
4680      #include <conio.h>\r
4681      #include <stdlib.h>\r
4682      \r
4683      enum { FALSE, TRUE };\r
4684      \r
4685      #define COLOUR (BLUE << 4) | YELLOW\r
4686      \r
4687      #define BIOS_TIMER  0x1C\r
4688      \r
4689      static unsigned installed = FALSE;\r
4690      static void interrupt (*old_tick) (void);\r
4691      \r
4692      static void interrupt tick (void)\r
4693      {\r
4694           int i;\r
4695           struct tm *now;\r
4696           time_t this_time;\r
4697           char time_buf[9];\r
4698           static time_t last_time = 0L;\r
4699           static char video_buf[20] =\r
4700           {\r
4701                ' ', COLOUR, '0', COLOUR, '0', COLOUR, ':', COLOUR, '0',\r
4702      COLOUR,\r
4703                '0', COLOUR, ':', COLOUR, '0', COLOUR, '0', COLOUR, ' ',\r
4704      COLOUR\r
4705           };\r
4706      \r
4707           enable ();\r
4708      \r
4709           if (time (&this_time) != last_time)\r
4710           {\r
4711                last_time = this_time;\r
4712      \r
4713                now = localtime(&this_time);\r
4714      \r
4715                sprintf(time_buf, "%02d:%02d.%02d",now->tm_hour,now-\r
4716      >tm_min,now->tm_sec);\r
4717      \r
4718                for (i = 0; i < 8; i++)\r
4719                {\r
4720                     video_buf[(i + 1) << 1] = time_buf[i];\r
4721                }\r
4722      \r
4723                puttext (71, 1, 80, 1, video_buf);\r
4724           }\r
4725      \r
4726           old_tick ();\r
4727      }\r
4728      \r
4729      void stop_clock (void)\r
4730      {\r
4731           if (installed)\r
4732           {\r
4733                setvect (BIOS_TIMER, old_tick);\r
4734                installed = FALSE;\r
4735           }\r
4736      }\r
4737      \r
4738      void start_clock (void)\r
4739      {\r
4740           static unsigned first_time = TRUE;\r
4741      \r
4742           if (!installed)\r
4743           {\r
4744                if (first_time)\r
4745                {\r
4746                     atexit (stop_clock);\r
4747                     first_time = FALSE;\r
4748                }\r
4749      \r
4750                old_tick = getvect (BIOS_TIMER);\r
4751                setvect (BIOS_TIMER, tick);\r
4752                installed = TRUE;\r
4753           }\r
4754      }\r
4755                                     \r
4756                                  SIGNAL\r
4757 \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
4760 \r
4761 Signal() takes two parameters in the form;\r
4762 \r
4763      void (*signal (int sig, void (*func) (int))) (int);\r
4764 \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
4767 \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
4772 \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
4778 \r
4779 \r
4780      signal(SIGINT,SIG_IGN);\r
4781 \r
4782 This example catches floating point errors on a PC, and zero divisions!\r
4783 \r
4784      #include <stdio.h>\r
4785      #include <signal.h>\r
4786      \r
4787      void (*old_sig)();\r
4788      \r
4789      void catch(int sig)\r
4790      {\r
4791           printf("Catch was called with: %d\n",sig);\r
4792      }\r
4793      \r
4794      \r
4795      void main()\r
4796      {\r
4797           int a;\r
4798           int b;\r
4799      \r
4800           old_sig = signal(SIGFPE,catch);\r
4801      \r
4802           a = 0;\r
4803           b = 10 / a;\r
4804      \r
4805           /* Restore original handler before exiting! */\r
4806           signal(SIGFPE,old_sig);\r
4807      }\r
4808      \r
4809                                     \r
4810                           SORTING AND SEARCHING\r
4811 \r
4812 The ANSI C standard defines qsort(), a function for sorting a table of\r
4813 data. The function follows the format;\r
4814 \r
4815      qsort(void *base,size_t elements,size_t width,int (*cmp)(void *,\r
4816 void *));\r
4817 \r
4818 The following short program illustrates the use of qsort() with a\r
4819 character array.\r
4820 \r
4821      #include <string.h>\r
4822      \r
4823      main()\r
4824      {\r
4825           int n;\r
4826           char data[10][20];\r
4827      \r
4828           /* Initialise some arbirary data */\r
4829      \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
4840      \r
4841           /* Sort the data table */\r
4842           qsort(data[0],10,20,strcmp);\r
4843      \r
4844           /* Print the data table */\r
4845           for(n = 0; n < 10; n++)\r
4846                puts(data[n]);\r
4847      }\r
4848      \r
4849 \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
4853 \r
4854      #include <stdio.h>\r
4855      #include <stdlib.h>\r
4856      #include <string.h>\r
4857      \r
4858      #define LINELEN     80\r
4859      #define MAXLINES    2000\r
4860      \r
4861      char *lines[MAXLINES];\r
4862      int lastone;\r
4863      \r
4864      void SHELL(void);\r
4865      \r
4866      void SHELL()\r
4867      {\r
4868           /* SHELL Sort Courtesy of K & R */\r
4869      \r
4870           int gap;\r
4871           int i;\r
4872           int j;\r
4873           char temp[LINELEN];\r
4874      \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
4878      gap]) >\r
4879                   0; j -= gap)\r
4880                {\r
4881                     strcpy(temp,lines[j]);\r
4882                     strcpy(lines[j] , lines[j + gap]);\r
4883                     strcpy(lines[j + gap] , temp);\r
4884      \r
4885                }\r
4886      }\r
4887      \r
4888      void main(int argc, char *argv[])\r
4889      {\r
4890           FILE *fp;\r
4891           char buff[100];\r
4892           int n;\r
4893 \r
4894           /* Check command line parameter has been given */\r
4895           if (argc != 2)\r
4896           {\r
4897                printf("\nError: Usage is SERVSORT file");\r
4898                exit(0);\r
4899           }\r
4900      \r
4901           /* Attempt to open file for updating */\r
4902           fp = fopen(argv[1],"r+");\r
4903           if (fp == NULL)\r
4904           {\r
4905                printf("\nError: Unable to open %s",argv[1]);\r
4906                exit(0);\r
4907           }\r
4908      \r
4909           /* Initialise element counter to zero */\r
4910           lastone = 0;\r
4911      \r
4912           /* Read file to be sorted */\r
4913           while((fgets(buff,100,fp)) != NULL)\r
4914           {\r
4915                /* Allocate memory block*/\r
4916                lines[lastone] = malloc(LINELEN);\r
4917                if (lines[lastone] == NULL)\r
4918                {\r
4919                     printf("\nError: Unable to allocate memory");\r
4920                     fclose(fp);\r
4921                     exit(0);\r
4922                }\r
4923                strcpy(lines[lastone],buff);\r
4924                lastone++;\r
4925      \r
4926                if (lastone > MAXLINES)\r
4927                {\r
4928                     printf("\nError: Too many lines in source file");\r
4929                     exit(0);\r
4930                }\r
4931           }\r
4932           /* Call sort function */\r
4933           SHELL();\r
4934      \r
4935           /* Close file */\r
4936           fclose(fp);\r
4937      \r
4938           /* Reopen file in create mode */\r
4939           fp = fopen(argv[1],"w+");\r
4940      \r
4941           /* Copy sorted data from memory to disk */\r
4942           for(n = 0; n < lastone; n++)\r
4943                fputs(lines[n],fp);\r
4944      \r
4945           /* Close file finally */\r
4946           fclose(fp);\r
4947      \r
4948           /* Return to calling program */\r
4949           return(1);\r
4950      }\r
4951      \r
4952 \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
4955 \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
4960 \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
4964 \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
4970 \r
4971      #include <alloc.h>\r
4972      #include <string.h>\r
4973      \r
4974      /* Function prototype for comparison function */\r
4975      int (cmp)(char **,char **);\r
4976      \r
4977      int cmp(char **s1, char **s2)\r
4978      {\r
4979           /* comparison function using pointers to pointers */\r
4980           return(strcmp(*s1,*s2));\r
4981      }\r
4982      \r
4983      main()\r
4984      {\r
4985           int n;\r
4986           char *data[10];\r
4987      \r
4988           for(n = 0; n < 10; n++)\r
4989                data[n] = malloc(20);\r
4990      \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
5001      \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
5005      \r
5006           for(n = 0; n < 10; n++)\r
5007                puts(data[n]);\r
5008      }\r
5009      \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
5013 them as required.\r
5014 \r
5015 The following program implements the quick sort algorithm, which is\r
5016 usually already used by qsort();\r
5017 \r
5018 \r
5019      #include <string.h>\r
5020      \r
5021      #define MAXELE  2000\r
5022      \r
5023      char data[10][20];\r
5024      int lastone;\r
5025      \r
5026      void QKSORT()\r
5027      {\r
5028           /* Implementation of QUICKSORT algorithm */\r
5029      \r
5030           int i;\r
5031           int j;\r
5032           int l;\r
5033           int p;\r
5034           int r;\r
5035           int s;\r
5036           char temp[100];\r
5037           static int sl[MAXELE][2];\r
5038      \r
5039           /* sl[] is an index to the sub-table */\r
5040      \r
5041           l = 0;\r
5042           r = lastone;\r
5043           p = 0;\r
5044      \r
5045           do\r
5046           {\r
5047                while(l < r)\r
5048                {\r
5049                     i = l;\r
5050                     j = r;\r
5051                     s = -1;\r
5052      \r
5053                     while(i < j)\r
5054                     {\r
5055                          if (strcmp(data[i],data[j]) > 0)\r
5056                          {\r
5057                               strcpy(temp,data[i]);\r
5058                               strcpy(data[i],data[j]);\r
5059                               strcpy(data[j],temp);\r
5060                               s = 0 - s;\r
5061                          }\r
5062      \r
5063                          if (s == 1)\r
5064                               i++;\r
5065                          else\r
5066                               j--;\r
5067                     }\r
5068      \r
5069                     if (i + 1 < r)\r
5070                     {\r
5071                          p++;\r
5072                          sl[p][0] = i + 1;\r
5073                          sl[p][1] = r;\r
5074                     }\r
5075                     r = i - 1;\r
5076                }\r
5077                if (p != 0)\r
5078                {\r
5079                     l = sl[p][0];\r
5080                     r = sl[p][1];\r
5081                     p--;\r
5082                }\r
5083           }\r
5084           while(p > 0);\r
5085      }\r
5086      \r
5087      main()\r
5088      {\r
5089           int n;\r
5090      \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
5102      \r
5103           /* Set last element indicator */\r
5104           lastone = 9;\r
5105      \r
5106           /* Call quick sort function */\r
5107           QKSORT();\r
5108      \r
5109           /* Display sorted list */\r
5110           for(n = 0; n < 10; n++)\r
5111                puts(data[n]);\r
5112      \r
5113      }\r
5114      \r
5115 A table sorted into ascending order can be searched with bsearch(), this\r
5116 takes the format;\r
5117 \r
5118      bsearch(key,base,num_elements,width,int (*cmp)(void *, void *));\r
5119 \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
5122 \r
5123 Or you can write your own binary search function thus;\r
5124 \r
5125      int BSRCH(char *key, void *data, int numele, int width)\r
5126      {\r
5127           /* A binary search function returning one if found */\r
5128           /* Zero if not found */\r
5129      \r
5130           int bp;\r
5131           int tp;\r
5132           int mp;\r
5133           int result;\r
5134           char *p;\r
5135      \r
5136           bp = 0;\r
5137           tp = numele;\r
5138           mp = (tp + bp) / 2;\r
5139      \r
5140           /* Locate element mp in table by assigning pointer to start */\r
5141           /* and incrementing it by width * mp */\r
5142           p = data;\r
5143           p += width * mp;\r
5144      \r
5145           while((result = strcmp(p,key)) != 0)\r
5146           {\r
5147                if (mp >= tp)\r
5148                     /* Not found! */\r
5149                     return(0);\r
5150                if (result < 0)\r
5151                     bp = mp + 1;\r
5152                else\r
5153                     tp = mp - 1;\r
5154      \r
5155                mp = (bp + tp) / 2;\r
5156                p = data;\r
5157                p += width * mp;\r
5158           }\r
5159           return(1);\r
5160      }\r
5161      \r
5162      void main()\r
5163      {\r
5164           int result;\r
5165           char data[10][20];\r
5166      \r
5167           /* Initialise some arbirary data */\r
5168      \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
5179      \r
5180           /* Sort the data table */\r
5181           qsort(data[0],10,20,strcmp);\r
5182      \r
5183           result = BSRCH("CYAN",data[0],10,20);\r
5184      \r
5185           printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");\r
5186      }\r
5187      \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
5191 \r
5192      #include <stdio.h>\r
5193      #include <stdlib.h>\r
5194      #include <string.h>\r
5195      \r
5196      char data[1000][4];\r
5197      char save[1000][4];\r
5198      \r
5199      int lastone;\r
5200      \r
5201      void INITDATA(void);\r
5202      void QKSORT(void);\r
5203      void SHELL(void);\r
5204      void BUBBLE(void);\r
5205      void FBUBBLE(void);\r
5206      void INSERTION(void);\r
5207      void MKDATA(void);\r
5208      \r
5209      void QKSORT()\r
5210      {\r
5211           /* Implementation of QUICKSORT algorithm */\r
5212      \r
5213           int i;\r
5214           int j;\r
5215           int l;\r
5216           int p;\r
5217           int r;\r
5218           int s;\r
5219           char temp[20];\r
5220           static int sl[1000][2];\r
5221      \r
5222           l = 0;\r
5223           r = lastone;\r
5224           p = 0;\r
5225      \r
5226           do\r
5227           {\r
5228                while(l < r)\r
5229                {\r
5230                     i = l;\r
5231                     j = r;\r
5232                     s = -1;\r
5233      \r
5234                     while(i < j)\r
5235                     {\r
5236                          if (strcmp(data[i],data[j]) > 0)\r
5237                          {\r
5238                               strcpy(temp,data[i]);\r
5239                               strcpy(data[i],data[j]);\r
5240                               strcpy(data[j],temp);\r
5241                               s = 0 - s;\r
5242                          }\r
5243      \r
5244                          if (s == 1)\r
5245                               i++;\r
5246                          else\r
5247                               j--;\r
5248                     }\r
5249      \r
5250                     if (i + 1 < r)\r
5251                     {\r
5252                          p++;\r
5253                          sl[p][0] = i + 1;\r
5254                          sl[p][1] = r;\r
5255                     }\r
5256                     r = i - 1;\r
5257                }\r
5258                if (p != 0)\r
5259                {\r
5260                     l = sl[p][0];\r
5261                     r = sl[p][1];\r
5262                     p--;\r
5263                }\r
5264           }\r
5265           while(p > 0);\r
5266      }\r
5267      \r
5268      void SHELL()\r
5269      {\r
5270           /* SHELL Sort Courtesy of K & R */\r
5271      \r
5272           int gap;\r
5273           int i;\r
5274           int j;\r
5275           char temp[20];\r
5276      \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
5280      > 0;\r
5281                       j -= gap)\r
5282                {\r
5283                     strcpy(temp,data[j]);\r
5284                     strcpy(data[j] , data[j + gap]);\r
5285                     strcpy(data[j + gap] , temp);\r
5286                }\r
5287      }\r
5288      \r
5289      void BUBBLE()\r
5290      {\r
5291           int a;\r
5292           int b;\r
5293           char temp[20];\r
5294      \r
5295           for(a = lastone; a >= 0; a--)\r
5296           {\r
5297                for(b = 0; b < a; b++)\r
5298                {\r
5299                     if(strcmp(data[b],data[b + 1]) > 0)\r
5300                     {\r
5301                          strcpy(temp,data[b]);\r
5302                          strcpy(data[b] , data[b + 1]);\r
5303                          strcpy(data[b + 1] , temp);\r
5304                     }\r
5305                }\r
5306           }\r
5307      }\r
5308      \r
5309      void FBUBBLE()\r
5310      {\r
5311           /* bubble sort with swap flag*/\r
5312      \r
5313           int a;\r
5314           int b;\r
5315           int s;\r
5316           char temp[20];\r
5317      \r
5318           s = 1;\r
5319      \r
5320           for(a = lastone; a >= 0 && s == 1; a--)\r
5321           {\r
5322                s = 0;\r
5323                for(b = 0; b < a; b++)\r
5324                {\r
5325                     if(strcmp(data[b],data[b + 1]) > 0)\r
5326                     {\r
5327                          strcpy(temp,data[b]);\r
5328                          strcpy(data[b] , data[b + 1]);\r
5329                          strcpy(data[b + 1] , temp);\r
5330                          s = 1;\r
5331                     }\r
5332                }\r
5333           }\r
5334      }\r
5335      \r
5336      void INSERTION()\r
5337      {\r
5338           int a;\r
5339           int b;\r
5340           char temp[20];\r
5341      \r
5342           for(a = 0; a < lastone; a++)\r
5343           {\r
5344                b = a;\r
5345                strcpy(temp,data[a + 1]);\r
5346                while(b >= 0)\r
5347                {\r
5348                     if (strcmp(temp,data[b]) < 0)\r
5349                     {\r
5350                          strcpy(data[b+1],data[b]);\r
5351                          b--;\r
5352                     }\r
5353                     else\r
5354                          break;\r
5355                }\r
5356                strcpy(data[b+1],temp);\r
5357           }\r
5358      }\r
5359      \r
5360      void MKDATA()\r
5361      {\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
5365      \r
5366           int n;\r
5367           for(n = 0; n < 1000; n++)\r
5368                sprintf(save[n],"%d",random(1000));\r
5369      }\r
5370      \r
5371      void INITDATA()\r
5372      {\r
5373           int n;\r
5374      \r
5375           for(n = 0 ; n < 1000; n++)\r
5376                strcpy(data[n],save[n]);\r
5377      }\r
5378      \r
5379      void main()\r
5380      {\r
5381           MKDATA();\r
5382      \r
5383           /* Initialise arbitrary data */\r
5384           INITDATA();\r
5385      \r
5386           /* Set last element indicator */\r
5387           lastone = 999;\r
5388      \r
5389           /* Call quick sort function */\r
5390           QKSORT();\r
5391      \r
5392      \r
5393           /* Initialise arbitrary data */\r
5394           INITDATA();\r
5395      \r
5396           /* Set last element indicator */\r
5397           lastone = 1000;\r
5398      \r
5399           /* Call shell sort function */\r
5400           SHELL();\r
5401      \r
5402           /* Initialise arbitrary data */\r
5403           INITDATA();\r
5404      \r
5405           /* Set last element indicator */\r
5406           lastone = 999;\r
5407      \r
5408           /* Call bubble sort function */\r
5409           BUBBLE();\r
5410      \r
5411           /* Initialise arbitrary data */\r
5412           INITDATA();\r
5413      \r
5414           /* Set last element indicator */\r
5415           lastone = 999;\r
5416      \r
5417           /* Call bubble sort with swap flag function */\r
5418           FBUBBLE();\r
5419      \r
5420           /* Initialise arbitrary data */\r
5421           INITDATA();\r
5422      \r
5423           /* Set last element indicator */\r
5424           lastone = 999;\r
5425      \r
5426           /* Call insertion sort function */\r
5427           INSERTION();\r
5428      }\r
5429 \r
5430 Here are the profiler results of the above test program run on 1000 and\r
5431 5000 random items;\r
5432 \r
5433 STRING SORT - 1000 RANDOM ITEMS\r
5434 \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
5442 \r
5443 STRING SORT - 5000 RANDOM ITEMS\r
5444 \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
5452 \r
5453 Here is the same test program amended for sorting tables of integers;\r
5454 \r
5455      /* Integer sort test program */\r
5456      \r
5457      #include <stdio.h>\r
5458      #include <stdlib.h>\r
5459      \r
5460      void INITDATA(void);\r
5461      void QKSORT(void);\r
5462      void SHELL(void);\r
5463      void BUBBLE(void);\r
5464      void FBUBBLE(void);\r
5465      void INSERTION(void);\r
5466      void MKDATA(void);\r
5467      \r
5468      int data[1000];\r
5469      int save[1000];\r
5470      \r
5471      int lastone;\r
5472      \r
5473      void QKSORT()\r
5474      {\r
5475           /* Implementation of QUICKSORT algorithm */\r
5476      \r
5477           int i;\r
5478           int j;\r
5479           int l;\r
5480           int p;\r
5481           int r;\r
5482           int s;\r
5483           int temp;\r
5484           static int sl[1000][2];\r
5485      \r
5486           l = 0;\r
5487           r = lastone;\r
5488           p = 0;\r
5489      \r
5490           do\r
5491           {\r
5492                while(l < r)\r
5493                {\r
5494                     i = l;\r
5495                     j = r;\r
5496                     s = -1;\r
5497      \r
5498                     while(i < j)\r
5499                     {\r
5500                          if (data[i] > data[j])\r
5501                          {\r
5502                               temp = data[i];\r
5503                               data[i] = data[j];\r
5504                               data[j] = temp;\r
5505                               s = 0 - s;\r
5506                          }\r
5507      \r
5508                          if (s == 1)\r
5509                               i++;\r
5510                          else\r
5511                               j--;\r
5512                     }\r
5513      \r
5514                     if (i + 1 < r)\r
5515                     {\r
5516                          p++;\r
5517                          sl[p][0] = i + 1;\r
5518                          sl[p][1] = r;\r
5519                     }\r
5520                     r = i - 1;\r
5521                }\r
5522                if (p != 0)\r
5523                {\r
5524                     l = sl[p][0];\r
5525                     r = sl[p][1];\r
5526                     p--;\r
5527                }\r
5528           }\r
5529           while(p > 0);\r
5530      }\r
5531      \r
5532      void SHELL()\r
5533      {\r
5534           /* SHELL Sort Courtesy of K & R */\r
5535      \r
5536           int gap;\r
5537           int i;\r
5538           int j;\r
5539           int temp;\r
5540      \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
5544                       j -= gap)\r
5545                {\r
5546                     temp = data[j];\r
5547                     data[j] = data[j + gap];\r
5548                     data[j + gap] = temp;\r
5549                }\r
5550      }\r
5551      \r
5552      void BUBBLE()\r
5553      {\r
5554           int a;\r
5555           int b;\r
5556           int temp;\r
5557      \r
5558           for(a = lastone; a >= 0; a--)\r
5559           {\r
5560                for(b = 0; b < a; b++)\r
5561                {\r
5562                     if(data[b] > data[b + 1])\r
5563                     {\r
5564                          temp = data[b];\r
5565                          data[b] = data[b + 1];\r
5566                          data[b + 1] = temp;\r
5567                     }\r
5568                }\r
5569           }\r
5570      }\r
5571      \r
5572      void FBUBBLE()\r
5573      {\r
5574           /* bubble sort with swap flag */\r
5575      \r
5576           int a;\r
5577           int b;\r
5578           int s;\r
5579           int temp;\r
5580      \r
5581           s = 1;\r
5582      \r
5583           for(a = lastone; a >= 0 && s == 1; a--)\r
5584           {\r
5585                s = 0;\r
5586                for(b = 0; b < lastone - a; b++)\r
5587                {\r
5588                     if(data[b] > data[b + 1])\r
5589                     {\r
5590                          temp = data[b];\r
5591                          data[b] = data[b + 1];\r
5592                          data[b + 1] = temp;\r
5593                          s = 1;\r
5594                     }\r
5595                }\r
5596           }\r
5597      }\r
5598      \r
5599      void INSERTION()\r
5600      {\r
5601           int a;\r
5602           int b;\r
5603           int temp;\r
5604      \r
5605           for(a = 0; a < lastone; a++)\r
5606           {\r
5607                b = a;\r
5608                temp = data[a + 1];\r
5609                while(b >= 0)\r
5610                {\r
5611                     if (temp < data[b])\r
5612                     {\r
5613                          data[b+1] = data[b];\r
5614                          b--;\r
5615                     }\r
5616                     else\r
5617                          break;\r
5618                }\r
5619                data[b+1] = temp;\r
5620           }\r
5621      }\r
5622      \r
5623      void MKDATA()\r
5624      {\r
5625           int n;\r
5626      \r
5627           for(n = 0; n < 1000; n++)\r
5628                save[n] = random(1000);\r
5629      }\r
5630      \r
5631      void INITDATA()\r
5632      {\r
5633           int n;\r
5634      \r
5635           for(n = 0; n < 1000; n++)\r
5636           data[n] = save[n];\r
5637      }\r
5638      \r
5639      void main()\r
5640      {\r
5641           int n;\r
5642      \r
5643           /* Create 1000 random elements */\r
5644           MKDATA();\r
5645      \r
5646           /* Initialise arbitrary data */\r
5647           INITDATA();\r
5648      \r
5649           /* Set last element indicator */\r
5650           lastone = 999;\r
5651      \r
5652           /* Call quick sort function */\r
5653           QKSORT();\r
5654      \r
5655           /* Initialise arbitrary data */\r
5656           INITDATA();\r
5657      \r
5658           /* Set last element indicator */\r
5659           lastone = 1000;\r
5660      \r
5661           /* Call shell sort function */\r
5662           SHELL();\r
5663      \r
5664           /* Initialise arbitrary data */\r
5665           INITDATA();\r
5666      \r
5667           /* Set last element indicator */\r
5668           lastone = 999;\r
5669      \r
5670           /* Call bubble sort function */\r
5671           BUBBLE();\r
5672      \r
5673           /* Initialise arbitrary data */\r
5674           INITDATA();\r
5675      \r
5676           /* Set last element indicator */\r
5677           lastone = 999;\r
5678      \r
5679           /* Call bubble sort with swap flag function */\r
5680           FBUBBLE();\r
5681      \r
5682           /* Initialise arbitrary data */\r
5683           INITDATA();\r
5684      \r
5685           /* Set last element indicator */\r
5686           lastone = 999;\r
5687      \r
5688           /* Call insertion sort function */\r
5689           INSERTION();\r
5690      }\r
5691 \r
5692 And here are the profiler results for this program;\r
5693 \r
5694 INTEGER SORTS - 1000 RANDOM NUMBERS (0 - 999)\r
5695 \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
5703 \r
5704 INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 999)\r
5705 \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
5713 \r
5714 INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 99)\r
5715 \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
5721 \r
5722 INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 9)\r
5723 \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
5731 \r
5732 INTEGER SORTS - 5000 DESCENDING ORDERED NUMBERS\r
5733 \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
5740 \r
5741 INTEGER SORTS - 5000 ORDERED NUMBERS\r
5742 \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
5749 \r
5750 INTEGER SORTS - 10000 RANDOM NUMBERS (0 - 999)\r
5751 \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
5757 \r
5758 Theory has it that the performance of a sorting algorithm is dependant\r
5759 upon;\r
5760 \r
5761      a) the number of items to be sorted and\r
5762      b) how unsorted the list is to start with.\r
5763 \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
5767 \r
5768      1) With an already sorted list FBUBBLE() executes fastest\r
5769      \r
5770      2) With a random list of small variations between the values SHELL()\r
5771          executes fastest\r
5772      \r
5773      3) With a random list of large variations between the values\r
5774      QKSORT()\r
5775           executes the fastest\r
5776 \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
5781 \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
5788 \r
5789                                     \r
5790                         DYNAMIC MEMORY ALLOCATION\r
5791 \r
5792 \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
5798 \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
5808 \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
5816 \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
5820 \r
5821      #include <stdio.h>\r
5822      #include <stdlib.h>\r
5823      \r
5824      char *buffer;\r
5825      \r
5826      void main(int argc, char *argv[])\r
5827      {\r
5828           FILE *fp;\r
5829           long flen;\r
5830      \r
5831           /* Check number of parameters */\r
5832           if (argc != 3)\r
5833           {\r
5834                fputs("Usage is sgrep <text> <file spec>",stderr);\r
5835                exit(0);\r
5836           }\r
5837      \r
5838           /* Open stream fp to file */\r
5839           fp = fopen(argv[2],"r");\r
5840           if (!fp)\r
5841           {\r
5842                perror("Unable to open source file");\r
5843                exit(0);\r
5844           }\r
5845      \r
5846           /* Locate file end */\r
5847           if(fseek(fp,0L,SEEK_END))\r
5848           {\r
5849                fputs("Unable to determine file length",stderr);\r
5850                fclose(fp);\r
5851                exit(0);\r
5852           }\r
5853      \r
5854           /* Determine file length */\r
5855           flen = ftell(fp);\r
5856      \r
5857           /* Check for error */\r
5858           if (flen == -1L)\r
5859           {\r
5860                fputs("Unable to determine file length",stderr);\r
5861                fclose(fp);\r
5862                exit(0);\r
5863           }\r
5864      \r
5865           /* Set file pointer to start of file */\r
5866           rewind(fp);\r
5867      \r
5868           /* Allocate memory buffer */\r
5869           buffer = malloc(flen);\r
5870      \r
5871           if (!buffer)\r
5872           {\r
5873                fputs("Unable to allocate memory",stderr);\r
5874                fclose(fp);\r
5875                exit(0);\r
5876           }\r
5877      \r
5878           /* Read file into buffer */\r
5879           fread(buffer,flen,1,fp);\r
5880      \r
5881           /* Check for read error */\r
5882           if(ferror(fp))\r
5883           {\r
5884                fputs("Unable to read file",stderr);\r
5885      \r
5886                /* Deallocate memory block */\r
5887                free(buffer);\r
5888      \r
5889                fclose(fp);\r
5890                exit(0);\r
5891           }\r
5892      \r
5893           printf("%s %s in %s",argv[1],(strstr(buffer,argv[1])) ? "was\r
5894      found" : "was not found",argv[2]);\r
5895      \r
5896           /* Deallocate memory block before exiting */\r
5897           free(buffer);\r
5898           fclose(fp);\r
5899      }\r
5900                                     \r
5901                          VARIABLE ARGUMENT LISTS\r
5902 \r
5903 \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
5908 \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
5912 macros.\r
5913 \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
5918 \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
5923 \r
5924 \r
5925      <int> = va_arg(<va_list>,int);\r
5926 \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
5930 \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
5936 illustrated;\r
5937 \r
5938      #include <stdarg.h>\r
5939      \r
5940      char *ITOS(long x, char *ptr)\r
5941      {\r
5942           /* Convert a signed decimal integer to a string */\r
5943      \r
5944           long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,\r
5945      1000, 100, 10, 1 };\r
5946           int n;\r
5947      \r
5948           /* Check sign */\r
5949           if (x < 0)\r
5950           {\r
5951                *ptr++ = '-';\r
5952                /* Convert x to absolute */\r
5953                x = 0 - x;\r
5954           }\r
5955      \r
5956           for(n = 0; n < 9; n++)\r
5957           {\r
5958                if (x > pt[n])\r
5959                {\r
5960                     *ptr++ = 48 + x / pt[n];\r
5961                     x %= pt[n];\r
5962                }\r
5963           }\r
5964           return(ptr);\r
5965      }\r
5966      \r
5967      void varfunc(char *format, ...)\r
5968      {\r
5969           va_list arg_ptr;\r
5970           char output[1000];\r
5971           char *ptr;\r
5972           int bytes;\r
5973           int x;\r
5974           char *y;\r
5975           char z;\r
5976      \r
5977           /* Initialise pointer to argument list */\r
5978           va_start(arg_ptr, format);\r
5979      \r
5980           /* loop format string */\r
5981           ptr = output;\r
5982           bytes = 0;\r
5983           while(*format)\r
5984           {\r
5985                /* locate next argument */\r
5986                while(*format != '%')\r
5987                {\r
5988                     *ptr++ = *format++;\r
5989                     bytes++;\r
5990                }\r
5991                /* A % has been located */\r
5992                format++;\r
5993                switch(*format)\r
5994                {\r
5995                     case '%' :  *ptr++ = '%';\r
5996                              break;\r
5997      \r
5998                     case 'd' : /* integer expression follows */\r
5999                             x = va_arg(arg_ptr,int);\r
6000                             ptr = ITOS(x,ptr);\r
6001                             *ptr = 0;\r
6002                             format++;\r
6003                             bytes += strlen(output) - bytes;\r
6004                             break;\r
6005      \r
6006                     case 's' : /* String expression follows */\r
6007                             y = va_arg(arg_ptr,char *);\r
6008                             strcat(output,y);\r
6009                             x = strlen(y);\r
6010                             format++;\r
6011                             ptr += x;\r
6012                             bytes += x;\r
6013                             break;\r
6014      \r
6015                     case 'c' : /* Char expression follows */\r
6016                             z = va_arg(arg_ptr,char);\r
6017                             *ptr++ = z;\r
6018                             format++;\r
6019                             bytes++;\r
6020                             break;\r
6021                }\r
6022      \r
6023           }\r
6024      \r
6025           /* Clean stack just in case! */\r
6026           va_end(arg_ptr);\r
6027      \r
6028           /* Null terminate output string */\r
6029           *ptr = 0;\r
6030      \r
6031           /* Display what we got */\r
6032           printf("\nOUTPUT==%s",output);\r
6033      }\r
6034      \r
6035      void main()\r
6036      {\r
6037           varfunc("%d %s %c",5,"hello world",49);\r
6038      }\r
6039      \r
6040 \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
6045 \r
6046      #include <stdio.h>\r
6047      #include <stdarg.h>\r
6048      \r
6049      void varfunc(char *format, ...)\r
6050      {\r
6051           va_list arg_ptr;\r
6052           char output[1000];\r
6053      \r
6054           va_start(arg_ptr, format);\r
6055      \r
6056           vsprintf(output,format,arg_ptr);\r
6057      \r
6058           va_end(arg_ptr);\r
6059      \r
6060           /* Display what we got */\r
6061           printf("\nOUTPUT==%s",output);\r
6062      }\r
6063      \r
6064      void main()\r
6065      {\r
6066           varfunc("%d %s %c",5,"hello world",49);\r
6067      }\r
6068                                     \r
6069                          TRIGONOMETRY FUNCTIONS\r
6070 \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
6073 \r
6074 \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
6086 \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
6094 \r
6095                                     \r
6096                                  ATEXIT\r
6097 \r
6098 \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
6105 \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
6111 \r
6112      #include <stdio.h>\r
6113      \r
6114      void leave()\r
6115      {\r
6116           puts("\nBye Bye!");\r
6117      }\r
6118      \r
6119      void main()\r
6120      {\r
6121           FILE *fp;\r
6122           int a;\r
6123           int b;\r
6124           int c;\r
6125           int d;\r
6126           int e;\r
6127           char text[100];\r
6128      \r
6129           atexit(leave);\r
6130      \r
6131           fp = fopen("data.txt","w");\r
6132      \r
6133           if(!fp)\r
6134           {\r
6135                perror("Unable to create file");\r
6136                exit(0);\r
6137           }\r
6138      \r
6139           fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");\r
6140      \r
6141           fflush(fp);\r
6142      \r
6143           if (ferror(fp))\r
6144           {\r
6145                fputs("Error flushing stream",stderr);\r
6146                exit(1);\r
6147           }\r
6148      \r
6149           rewind(fp);\r
6150           if (ferror(fp))\r
6151           {\r
6152                fputs("Error rewind stream",stderr);\r
6153                exit(1);\r
6154           }\r
6155      \r
6156           fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);\r
6157           if (ferror(fp))\r
6158           {\r
6159                /* Unless you noticed the deliberate bug earlier */\r
6160                /* The program terminates here */\r
6161                fputs("Error reading from stream",stderr);\r
6162                exit(1);\r
6163           }\r
6164      \r
6165           printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);\r
6166      }\r
6167                                     \r
6168                             INCREASING SPEED\r
6169 \r
6170 \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
6178 \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
6183 \r
6184 \r
6185      'x * 3' becomes 'x + x + x'\r
6186 \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
6191 \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
6198 \r
6199 \r
6200      01001101  equal to   77\r
6201 \r
6202 shifted right one place it becomes;\r
6203 \r
6204      00100110  equal to   38\r
6205 \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
6209 say;\r
6210 \r
6211      percentage = x / y * 100\r
6212 \r
6213 This requires floating point variables. However, it can also be written\r
6214 as;\r
6215 \r
6216      z = x * 100;\r
6217      percentage = z / y\r
6218 \r
6219 Which works fine with integers, so long as you don't mind the percentage\r
6220 being truncated. eg;\r
6221 \r
6222      5 / 7 * 100 is equal to 71.43 with floating point\r
6223 \r
6224 but with integers;\r
6225 \r
6226      5 * 100 / 7 is equal to 71\r
6227 \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
6230 \r
6231 Here is a test program using this idea;\r
6232 \r
6233      float funca(double x, double y)\r
6234      {\r
6235           return(x / y * 100);\r
6236      }\r
6237      \r
6238      int funcb(int x,int y)\r
6239      {\r
6240           return(x * 100 / y);\r
6241      }\r
6242      \r
6243      void main()\r
6244      {\r
6245           int n;\r
6246           double x;\r
6247           int y;\r
6248      \r
6249           for(n = 0; n < 5000; n++)\r
6250           {\r
6251                x = funca(5,7);\r
6252                y = funcb(5,7);\r
6253           }\r
6254      }\r
6255      \r
6256 And here is the results of the test program fed through a profiler;\r
6257 \r
6258 funca            1.9169 sec  96%\r
6259 |**********************************************\r
6260 funcb            0.0753 sec   3%  |*\r
6261 \r
6262 You can clearly see that the floating point function is 25 times slower\r
6263 than the integer equivalent!\r
6264 \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
6268 manual.\r
6269 \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
6272 \r
6273      x = data[i];\r
6274 \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
6278 illustrates;\r
6279 \r
6280      void BUBBLE()\r
6281      {\r
6282           /* Bubble sort using array indexing */\r
6283      \r
6284           int a;\r
6285           int b;\r
6286           int temp;\r
6287      \r
6288           for(a = lastone; a >= 0; a--)\r
6289           {\r
6290                for(b = 0; b < a; b++)\r
6291                {\r
6292                     if(data[b] > data[b + 1])\r
6293                     {\r
6294                          temp = data[b];\r
6295                          data[b] = data[b + 1];\r
6296                          data[b + 1] = temp;\r
6297                     }\r
6298                }\r
6299           }\r
6300      }\r
6301      \r
6302      void PTRBUBBLE()\r
6303      {\r
6304           /* Bubble sort using pointers */\r
6305      \r
6306           int temp;\r
6307           int *ptr;\r
6308           int *ptr2;\r
6309      \r
6310           for(ptr = &data[lastone]; ptr >= data; ptr--)\r
6311           {\r
6312                for(ptr2 = data; ptr2 < ptr; ptr2++)\r
6313                {\r
6314                     if(*ptr2 > *(ptr2 + 1))\r
6315                     {\r
6316                          temp = *ptr2;\r
6317                          *ptr2 = *(ptr2 + 1);\r
6318                          *(ptr2 + 1) = temp;\r
6319                     }\r
6320                }\r
6321           }\r
6322      }\r
6323      \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
6326 \r
6327 BUBBLE       3.1307 sec  59% |******************************************\r
6328 PTRBUBBLE    2.1686 sec  40% |***************************\r
6329 \r
6330 \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
6333 \r
6334      /* Index array initialisation */\r
6335      int n;\r
6336      \r
6337      for(n = 0; n < 1000; n++)\r
6338           data[n] = random(1000);\r
6339      \r
6340      \r
6341      /* Pointer array initialisation */\r
6342      int *n;\r
6343      \r
6344      for(n = data; n < &data[1000]; n++)\r
6345           *n = random(1000);\r
6346      \r
6347 \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
6353 \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
6357 \r
6358 static char *colours[] = { "Black", "Blue", "Green", "Yellow", "Red",\r
6359 "White" };\r
6360 \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
6364 approach instead;\r
6365 \r
6366 First the table is changed into a single string, with some delimiter\r
6367 between the elements.\r
6368 \r
6369      static char *colours = "Black/Blue/Green/Yellow/Red/White";\r
6370 \r
6371 Then to confirm that a value is held in the table you can use strstr();\r
6372 \r
6373      result = strstr(colours,"Cyan");\r
6374 \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
6384 \r
6385                                     \r
6386                                PC GRAPHICS\r
6387 \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
6391 \r
6392 \r
6393 \r
6394 Introduction To PC Graphics\r
6395 \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
6406 \r
6407      CGA, EGA and VGA\r
6408 \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
6412 graphics work.\r
6413 \r
6414 \r
6415 Display Modes\r
6416 \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
6422 \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
6430 display mode.\r
6431 \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
6434 \r
6435 \r
6436 MODE   TYPE               RESOLUTION         COLOURS\r
6437                                              \r
6438  0       Text             40 x 25            4 (CGA), 16 (EGA,\r
6439                                              VGA) Shades of\r
6440                                              grey\r
6441  1       Text             40 x 25            4 (CGA), 16 (EGA,\r
6442                                              VGA)\r
6443  2       Text             80 x 25            4 (CGA), 16 (EGA,\r
6444                                              VGA) Shades of\r
6445                                              grey\r
6446  3       Text             80 x 25            4 (CGA), 16 (EGA,\r
6447                                              VGA)\r
6448  4       Graphics         320 x 200          4\r
6449  5       Graphics         320 x 200          4 (grey on CGA\r
6450                                              and EGA)\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
6460 \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
6466 \r
6467 \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
6472 \r
6473 \r
6474 \r
6475 Accessing The Display\r
6476 \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
6483 \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
6491 \r
6492           mov   ah , 00\r
6493           mov   al , 04\r
6494           int   10h\r
6495 \r
6496 \r
6497 A C function for selecting video display modes can be written;\r
6498 \r
6499      #include <dos.h>\r
6500      \r
6501      void setmode(unsigned char mode)\r
6502      {\r
6503           /* Sets the video display mode */\r
6504      \r
6505           union REGS inregs outreg;\r
6506      \r
6507           inreg.h.ah = 0;\r
6508           inreg.h.al = mode;\r
6509           int86(0x10,&inreg,&outregs);\r
6510      }\r
6511      \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
6518 \r
6519 \r
6520           mov   ah, 12\r
6521           mov   al, colour\r
6522           mov   bh, 0\r
6523           mov   cx, x_coord\r
6524           mov   dx, y_coord\r
6525           int   10h\r
6526 \r
6527 The corresponding C function is;\r
6528 \r
6529      #include <dos.h>\r
6530      \r
6531      void plot(int x_coord, int y_coord, unsigned char colour)\r
6532      {\r
6533           /* Sets the colour of a pixel */\r
6534      \r
6535           union REGS regs;\r
6536      \r
6537           regs.h.ah = 12;\r
6538           regs.h.al = colour;\r
6539           regs.h.bh = 0;\r
6540           regs.x.cx = x_coord;\r
6541           regs.x.dx = y_coord;\r
6542           int86(0x10,&regs,&regs);\r
6543      }\r
6544 \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
6550 register;\r
6551 \r
6552 \r
6553      #include <dos.h>\r
6554      \r
6555      unsigned char get_pixel(int x_coord, int y_coord)\r
6556      {\r
6557           /* Reads the colour of a pixel */\r
6558      \r
6559           union REGS inreg, outreg;\r
6560      \r
6561           inreg.h.ah = 13;\r
6562           inreg.h.bh = 0;\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
6567      }\r
6568 \r
6569 \r
6570 Colour And The CGA\r
6571 \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
6576 \r
6577      background, cyan, magenta and white.\r
6578 \r
6579 the second palette contains the colours;\r
6580 \r
6581        background, green, red and yellow.\r
6582 \r
6583 Colour 0 is always the same as the background colour.\r
6584 \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
6588 \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
6592 \r
6593 \r
6594           mov   ah, 11\r
6595           mov   bh, palette\r
6596           int   10h\r
6597 \r
6598 The C function for selecting the CGA palette is;\r
6599 \r
6600      void palette(unsigned char palette)\r
6601      {\r
6602           union REGS inreg, outreg;\r
6603      \r
6604           inreg.h.ah = 11;\r
6605           inreg.h.bh = palette;\r
6606           int86(0x10,&inreg,&outreg);\r
6607      }\r
6608      \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
6613 \r
6614 \r
6615      mov   ah, 11\r
6616      mov   bh, 0\r
6617      mov   bl, colour\r
6618      int   10h\r
6619 \r
6620 In C this function can be written;\r
6621 \r
6622      void background(unsigned char colour)\r
6623      {\r
6624           union REGS inreg, outreg;\r
6625      \r
6626           inreg.h.ah = 11;\r
6627           inreg.h.bh = 0;\r
6628           inreg.h.bl = colour;\r
6629           int86(0x10,&inreg,&outreg);\r
6630      }\r
6631      \r
6632 The background colours available are;\r
6633 \r
6634      0       Black\r
6635      1       Blue\r
6636      2       Green\r
6637      3       Cyan\r
6638      4       Red\r
6639      5       Magenta\r
6640      6       Yellow\r
6641      7       White\r
6642 \r
6643 \r
6644 Colour And The EGA\r
6645 \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
6652 \r
6653 The colours are;\r
6654 \r
6655      0    Black\r
6656      1    Blue\r
6657      2    Green\r
6658      3    Cyan\r
6659      4    Red\r
6660      5    Magenta\r
6661      6    Brown\r
6662      7    Light grey\r
6663      8    Dark grey\r
6664      9    Light blue\r
6665      10        Light green\r
6666      11   Light cyan\r
6667      12        Light red\r
6668      13        Light magenta\r
6669      14        Yellow\r
6670      15        White\r
6671 \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
6676 thus;\r
6677 \r
6678 \r
6679      mov   ah,16\r
6680      mov   al,0\r
6681      mov   bl,palette\r
6682      mov   bh,colour\r
6683      int   10h\r
6684 \r
6685 In C this function may be written;\r
6686 \r
6687      void ega_palette(unsigned char colour, unsigned char palette)\r
6688      {\r
6689           union REGS inreg,outreg;\r
6690      \r
6691           inreg.h.ah = 16;\r
6692           inreg.h.al = 0;\r
6693           inreg.h.bl = palette;\r
6694           inreg.h.bh = colour;\r
6695      \r
6696           int86(0x10,&inreg,&outreg);\r
6697      }\r
6698 \r
6699 \r
6700 \r
6701 Colour And The VGA\r
6702 \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
6713 \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
6720 \r
6721 \r
6722           mov   ah,16\r
6723           mov   al,16\r
6724           mov   ch,green\r
6725           mov   cl,blue\r
6726           mov   dh,red\r
6727           mov   bx,dac\r
6728           int   10h\r
6729 \r
6730 \r
6731 The C function to set a DAC register looks lik this;\r
6732 \r
6733      void set_dac(int dac, unsigned char green, unsigned char blue,\r
6734      unsigned char red)\r
6735      {\r
6736           union REGS regs;\r
6737      \r
6738           regs.h.ah = 16;\r
6739           regs.h.al = 16;\r
6740           regs.x.bx = dac;\r
6741           regs.h.ch = green;\r
6742           regs.h.cl = blue;\r
6743           regs.h.dh = red;\r
6744           int86(0x10,&regs,&regs);\r
6745      }\r
6746      \r
6747 \r
6748 \r
6749 Displaying Text\r
6750 \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
6762 \r
6763 \r
6764           mov   ah,14\r
6765           mov   al,character\r
6766           mov   bh,0\r
6767           mov   bl,foreground\r
6768           int   10h\r
6769 \r
6770 \r
6771 A C function for accessing the write text in teletype mode may be written\r
6772 like this;\r
6773 \r
6774      #include <dos.h>\r
6775      \r
6776      void teletype(unsigned char character, unsigned char foreground)\r
6777      {\r
6778           union REGS inreg, outreg;\r
6779      \r
6780           inreg.h.ah = 14;\r
6781           inreg.h.al = character;\r
6782           inreg.h.bh = 0;\r
6783           inreg.h.bl = foreground;\r
6784           int86(0x10,&inrg,&outreg);\r
6785      }\r
6786      \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
6793 \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
6802 \r
6803 \r
6804           mov   ah,9\r
6805           mov   al,character\r
6806           mov   bh,0\r
6807           mov   bl,foreground + 16 * background\r
6808           mov   cx,number\r
6809           int   10h\r
6810 \r
6811 And in C;\r
6812 \r
6813      #include <dos.h>\r
6814      \r
6815      void char_attrib(unsigned char character, unsigned char foreground,\r
6816      unsigned char background, int number)\r
6817      {\r
6818           union REGS inreg,outreg;\r
6819      \r
6820           inreg.h.ah = 9;\r
6821           inreg.h.al = character;\r
6822           inreg.h.bh = 0;\r
6823           inreg.h.bl = (background << 4) + foreground;\r
6824           inreg.x.cx = number;\r
6825           int86(0x10,&inreg,&outreg);\r
6826      }\r
6827 \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
6830 \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
6837 \r
6838 \r
6839           mov   ah,10\r
6840           mov   al,character\r
6841           mov   bh,0\r
6842           mov   bl,foreground   ; For graphics modes ONLY\r
6843           mov   cx,number\r
6844           int   10h\r
6845 \r
6846 And in C;\r
6847 \r
6848      #include <dos.h>\r
6849      \r
6850      void char_attrib(unsigned char character, unsigned char foreground,\r
6851      int number)\r
6852      {\r
6853           union REGS inreg,outreg;\r
6854      \r
6855           inreg.h.ah = 10;\r
6856           inreg.h.al = character;\r
6857           inreg.h.bh = 0;\r
6858           inreg.h.bl = foreground; /* For graphics modes ONLY */\r
6859           inreg.x.cx = number;\r
6860           int86(0x10,&inreg,&outreg);\r
6861      }\r
6862      \r
6863 \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
6867 register DL;\r
6868 \r
6869           mov   ah,2\r
6870           mov   bh,0\r
6871           mov   dh,row\r
6872           mov   dl,column\r
6873           int   10h\r
6874 \r
6875 The corresponding function in C looks like this;\r
6876 \r
6877      #include <dos.h>\r
6878      \r
6879      void at(unsigned char row, unsigned char column)\r
6880      {\r
6881           union REGS regs;\r
6882      \r
6883           regs.h.ah = 2;\r
6884           regs.h.bh = 0;\r
6885           regs.h.dh = row;\r
6886           regs.h.dl = column;\r
6887           int86(0x10,&regs,&regs);\r
6888      }\r
6889 \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
6894 \r
6895 \r
6896      #include <dos.h>\r
6897      #include <stdarg.h>\r
6898      \r
6899      void at(unsigned char row, unsigned char column)\r
6900      {\r
6901           union REGS regs;\r
6902      \r
6903           regs.h.ah = 2;\r
6904           regs.h.bh = 0;\r
6905           regs.h.dh = row;\r
6906           regs.h.dl = column;\r
6907           int86(0x10,&regs,&regs);\r
6908      }\r
6909      \r
6910      void xprintf(unsigned char foreground, unsigned char background,\r
6911      char *format,...)\r
6912      {\r
6913           union REGS inreg,outreg;\r
6914      \r
6915           va_list arg_ptr;\r
6916           static char output[1000];\r
6917           unsigned char col;\r
6918           unsigned char row;\r
6919           unsigned char n;\r
6920           unsigned char p;\r
6921           unsigned char text;\r
6922           unsigned char attr;\r
6923      \r
6924           /* Convert foreground and background colours into a single\r
6925      attribute */\r
6926           attr = (background << 4) + foreground;\r
6927      \r
6928           /* Copy data into a single string */\r
6929           va_start(arg_ptr, format);\r
6930           vsprintf(output, format, arg_ptr);\r
6931      \r
6932           /* Determine number of display columns */\r
6933           inreg.h.ah = 15;\r
6934           int86(0x10,&inreg,&outreg);\r
6935           n = outreg.h.ah;\r
6936      \r
6937           /* Determine current cursor position */\r
6938           inreg.h.ah = 3;\r
6939           inreg.h.bh = 0;\r
6940           int86(0x10,&inreg,&outreg);\r
6941           row = outreg.h.dh;\r
6942           col = outreg.h.dl;\r
6943      \r
6944           /* Now display data */\r
6945           p = 0;\r
6946           while (output[p])\r
6947           {\r
6948                /* Display this character */\r
6949                inreg.h.bh = 0;\r
6950                inreg.h.bl = attr;\r
6951                inreg.x.cx = 01;\r
6952                inreg.h.ah = 9;\r
6953                inreg.h.al = output[p++];\r
6954                int86(0x10,&inreg,&outreg);\r
6955      \r
6956                /* Update cursor position */\r
6957                /* moving down a row if required */\r
6958                col++;\r
6959                if (col < (n - 1))\r
6960                     at(row, col);\r
6961                else\r
6962                {\r
6963                     col = 0;\r
6964                     at(++row, col);\r
6965                }\r
6966           }\r
6967      }\r
6968      \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
6972 \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
6977 \r
6978 \r
6979                                     \r
6980                      ADVANCED GRAPHICS ON THE IBM PC\r
6981 \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
6985 \r
6986 \r
6987 \r
6988 Display Pages\r
6989 \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
6995 display pages.\r
6996 \r
6997 \r
6998 MODE               PAGES\r
6999  0                 8\r
7000  1                 8\r
7001  2                 4 (CGA) 8 (EGA, VGA)\r
7002  3                 4 (CGA) 8 (EGA, VGA)\r
7003  4                 1\r
7004  5                 1\r
7005  6                 1\r
7006  7                 8 (EGA, VGA)\r
7007 13                 8 (EGA, VGA)\r
7008 14                 4 (EGA, VGA)\r
7009 15                 2 (EGA, VGA)\r
7010 16                 2 (EGA, VGA)\r
7011 17                 1 (VGA)\r
7012 18                 1 (VGA)\r
7013 19                 1 (VGA)\r
7014 \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
7017 displayed.\r
7018 \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
7022 \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
7026 \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
7030 \r
7031           mov   ah,5\r
7032           mov   al,page\r
7033           int   10h\r
7034 \r
7035 Or, from C this function becomes;\r
7036 \r
7037      #include <dos.h>\r
7038      \r
7039      void set_page(unsigned char page)\r
7040      {\r
7041           union REGS inreg, outreg;\r
7042      \r
7043           inreg.h.ah = 5;\r
7044           inreg.h.al = page;\r
7045           int86(0x10,&inreg,&outreg);\r
7046      }\r
7047      \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
7051 \r
7052           mov   ah, 12\r
7053           mov   al, colour\r
7054           mov   bh, page\r
7055           mov   cx, x_coord\r
7056           mov   dx, y_coord\r
7057           int   10h\r
7058 \r
7059 And the corresponding C function becomes;\r
7060 \r
7061      #include <dos.h>\r
7062      \r
7063      void plot(int x_coord, int y_coord, unsigned char colour, unsigned\r
7064      char page)\r
7065      {\r
7066           /* Sets the colour of a pixel */\r
7067      \r
7068           union REGS inreg, outreg;\r
7069      \r
7070           inreg.h.ah = 12;\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
7076      }\r
7077 \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
7081 \r
7082           mov   ah,15\r
7083           int   10h\r
7084                               ; BH now holds active page number\r
7085 \r
7086                                     \r
7087                          Advanced Text Routines\r
7088 \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
7096 \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
7100 \r
7101 \r
7102           mov   ah,1\r
7103           mov   ch,top\r
7104           mov   cl,bottom\r
7105           int   10h\r
7106 \r
7107 A C function to set the cursor shape may be be written thus;\r
7108 \r
7109      #include <dos.h>\r
7110      \r
7111      void setcursor(unsigned char top, unsigned char bottom)\r
7112      {\r
7113           union REGS inreg, outreg;\r
7114      \r
7115           inreg.h.ch = start;\r
7116           inreg.h.cl = end;\r
7117           inreg.h.ah = 1;\r
7118           int86(0x10, &inreg, &outreg);\r
7119      }\r
7120      \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
7123 \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
7126 \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
7131 \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
7135 \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
7144 \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
7149 \r
7150 \r
7151 Scrolling\r
7152 \r
7153 A C function to scroll the entire screen down one line can be written\r
7154 thus;\r
7155 \r
7156      #include <dos.h>\r
7157      \r
7158      void scroll_down(unsigned char attr)\r
7159      {\r
7160           union REGS inreg, outreg;\r
7161      \r
7162           inreg.h.al = 1;\r
7163           inreg.h.cl = 0;\r
7164           inreg.h.ch = 0;\r
7165           inreg.h.dl = 79;\r
7166           inreg.h.dh = 24;    /* Assuming a 25 line display */\r
7167           inreg.h.bh = attr;\r
7168           inreg.h.ah = 7;\r
7169           int86(0x10, &inreg, &outreg);\r
7170      }\r
7171 \r
7172 \r
7173 Clear Screen\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
7176 inreg.h.al to 0;\r
7177 \r
7178      #include <dos.h>\r
7179      \r
7180      void cls(unsigned char attr)\r
7181      {\r
7182           union REGS inreg, outreg;\r
7183      \r
7184           inreg.h.al = 0;\r
7185           inreg.h.cl = 0;\r
7186           inreg.h.ch = 0;\r
7187           inreg.h.dl = 79;\r
7188           inreg.h.dh = 24;    /* Assuming a 25 line display */\r
7189           inreg.h.bh = attr;\r
7190           inreg.h.ah = 7;\r
7191           int86(0x10, &inreg, &outreg);\r
7192      }\r
7193 \r
7194 \r
7195 \r
7196 Windowing\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
7200 \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
7206 \r
7207 The following C functions allow an area of the display to be preserved,\r
7208 and later restored;\r
7209 \r
7210      #include <dos.h>\r
7211      \r
7212      void at(unsigned char row, unsigned char column, unsigned char page)\r
7213      {\r
7214           /* Position the cursor */\r
7215      \r
7216           union REGS inreg,outreg;\r
7217      \r
7218           inreg.h.ah = 2;\r
7219           inreg.h.bh = page;\r
7220           inreg.h.dh = row;\r
7221           inreg.h.dl = column;\r
7222           int86(0x10,&inreg,&outreg);\r
7223      }\r
7224      \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
7227      {\r
7228           /* Read a text window into a variable */\r
7229      \r
7230           union REGS inreg,outreg;\r
7231      \r
7232           unsigned char old_left;\r
7233           unsigned char old_row;\r
7234           unsigned char old_col;\r
7235      \r
7236           /* save current cursor position */\r
7237           inreg.h.ah = 3;\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
7242      \r
7243           while(top <= bottom)\r
7244           {\r
7245                old_left = left;\r
7246                while(left <= right)\r
7247                {\r
7248                     at(top,left,page);\r
7249                     inreg.h.bh = page;\r
7250                     inreg.h.ah = 8;\r
7251                     int86(0x10,&inreg,&outreg);\r
7252                     *buffer++ = outreg.h.al;\r
7253                     *buffer++ = outreg.h.ah;\r
7254                     left++;\r
7255                }\r
7256      \r
7257                left = old_left;\r
7258                top++;\r
7259           }\r
7260      \r
7261           /* Restore cursor to original location */\r
7262           at(old_row,old_col,page);\r
7263      }\r
7264      \r
7265      void put_win(unsigned char left, unsigned char top, unsigned char\r
7266      right,  unsigned char bottom, unsigned char            page, char\r
7267      *buffer)\r
7268      {\r
7269           /* Display a text window from a variable */\r
7270      \r
7271           union REGS inreg,outreg;\r
7272      \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
7278      \r
7279           /* save current cursor position */\r
7280           inreg.h.ah = 3;\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
7285      \r
7286           while(top <= bottom)\r
7287           {\r
7288                old_left = left;\r
7289                while(left <= right)\r
7290                {\r
7291                     at(top,left,page);\r
7292                     chr = *buffer++;\r
7293                     attr = *buffer++;\r
7294                     inreg.h.bh = page;\r
7295                     inreg.h.ah = 9;\r
7296                     inreg.h.al = chr;\r
7297                     inreg.h.bl = attr;\r
7298                     inreg.x.cx = 1;\r
7299                     int86(0x10,&inreg,&outreg);\r
7300                     left++;\r
7301                }\r
7302                left = old_left;\r
7303                top++;\r
7304           }\r
7305      \r
7306           /* Restore cursor to original location */\r
7307           at(old_row,old_col,page);\r
7308      }\r
7309                                     \r
7310                    DIRECT VIDEO ACCESS WITH THE IBM PC\r
7311 \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
7317 \r
7318 The following C function will set a pixel in CGA display modes 4 and 5\r
7319 directly;\r
7320 \r
7321 \r
7322      void dplot4(int y, int x, int colour)\r
7323      {\r
7324           /* Direct plotting in modes 4 & 5 ONLY! */\r
7325      \r
7326           union mask\r
7327           {\r
7328                char c[2];\r
7329                int i;\r
7330           }bit_mask;\r
7331      \r
7332           int index;\r
7333           int bit_position;\r
7334      \r
7335           unsigned char t;\r
7336           char xor;\r
7337      \r
7338           char far *ptr = (char far *) 0xB8000000;\r
7339      \r
7340           bit_mask.i = 0xFF3F;\r
7341      \r
7342           if ( y < 0 || y > 319 || x < 0 || x > 199)\r
7343                return;\r
7344      \r
7345           xor = colour & 128;\r
7346      \r
7347           colour = colour & 127;\r
7348      \r
7349           bit_position = y % 4;\r
7350      \r
7351           colour <<= 2 * (3 - bit_position);\r
7352      \r
7353           bit_mask.i >>= 2 * bit_position;\r
7354      \r
7355           index = x * 40 + (y / 4);\r
7356      \r
7357           if (x % 2)\r
7358                index += 8152;\r
7359      \r
7360      \r
7361      \r
7362      \r
7363           if (!xor)\r
7364           {\r
7365                t = *(ptr + index) & bit_mask.c[0];\r
7366                *(ptr + index) = t | colour;\r
7367           }\r
7368           else\r
7369           {\r
7370                t = *(ptr + index) | (char)0;\r
7371                *(ptr + index) = t ^ colour;\r
7372           }\r
7373      }\r
7374      \r
7375 \r
7376 Direct plotting in VGA mode 19 is very much simpler;\r
7377 \r
7378      void dplot19(int x, int y, unsigned char colour)\r
7379      {\r
7380           /* Direct plot in mode 19 ONLY */\r
7381           char far *video;\r
7382      \r
7383           video = MK_FP(0xA000,0);\r
7384           video[x + y * 320] = colour;\r
7385 }\r
7386                                     \r
7387               ADVANCED GRAPHICS TECHNIQUES WITH THE IBM PC\r
7388 \r
7389 \r
7390 \r
7391 \r
7392 Increasing Colours\r
7393 \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
7397 shades.\r
7398 \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
7403 \r
7404 The following C program illustrates this idea;\r
7405 \r
7406 \r
7407      #include <dos.h>\r
7408      \r
7409      union REGS inreg, outreg;\r
7410      \r
7411      void setvideo(unsigned char mode)\r
7412      {\r
7413           /* Sets the video display mode */\r
7414      \r
7415           inreg.h.al = mode;\r
7416           inreg.h.ah = 0;\r
7417           int86(0x10, &inreg, &outreg);\r
7418      }\r
7419      \r
7420      void plot(int x, int y, unsigned char colour)\r
7421      {\r
7422           /* Sets a pixel at the specified coordinates */\r
7423      \r
7424           inreg.h.al = colour;\r
7425           inreg.h.bh = 0;\r
7426           inreg.x.cx = x;\r
7427           inreg.x.dx = y;\r
7428           inreg.h.ah = 0x0C;\r
7429           int86(0x10, &inreg, &outreg);\r
7430      }\r
7431      \r
7432      void line(int a, int b, int c, int d, unsigned char colour)\r
7433      {\r
7434           /* Draws a straight line from (a,b) to (c,d) */\r
7435      \r
7436           int u;\r
7437           int v;\r
7438           int d1x;\r
7439           int d1y;\r
7440           int d2x;\r
7441           int d2y;\r
7442           int m;\r
7443           int n;\r
7444           int s;\r
7445           int i;\r
7446      \r
7447           u = c - a;\r
7448           v = d - b;\r
7449           if (u == 0)\r
7450           {\r
7451                d1x = 0;\r
7452                m = 0;\r
7453           }\r
7454           else\r
7455           {\r
7456                m = abs(u);\r
7457                if (u < 0)\r
7458                     d1x = -1;\r
7459                else\r
7460                if (u > 0)\r
7461                     d1x = 1;\r
7462           }\r
7463           if ( v == 0)\r
7464           {\r
7465                d1y = 0;\r
7466                n = 0;\r
7467           }\r
7468           else\r
7469           {\r
7470                n = abs(v);\r
7471                if (v < 0)\r
7472                     d1y = -1;\r
7473                else\r
7474                if (v > 0)\r
7475                     d1y = 1;\r
7476           }\r
7477           if (m > n)\r
7478           {\r
7479                d2x = d1x;\r
7480                d2y = 0;\r
7481           }\r
7482           else\r
7483           {\r
7484                d2x = 0;\r
7485                d2y = d1y;\r
7486                m = n;\r
7487                n = abs(u);\r
7488           }\r
7489           s = m / 2;\r
7490      \r
7491           for (i = 0; i <= m; i++)\r
7492           {\r
7493                plot(a,b,colour);\r
7494                s += n;\r
7495                if (s >= m)\r
7496                {\r
7497                     s -= m;\r
7498                     a += d1x;\r
7499                     b += d1y;\r
7500                }\r
7501                else\r
7502                {\r
7503                     a += d2x;\r
7504                     b += d2y;\r
7505                }\r
7506           }\r
7507      }\r
7508      \r
7509      void dot_line(int a, int b, int c, int d, int colour)\r
7510      {\r
7511           /* Draws a dotted straight line from (a,b) to (c,d) */\r
7512      \r
7513           int u;\r
7514           int v;\r
7515           int d1x;\r
7516           int d1y;\r
7517           int d2x;\r
7518           int d2y;\r
7519           int m;\r
7520           int n;\r
7521           int s;\r
7522           int i;\r
7523      \r
7524           u = c - a;\r
7525           v = d - b;\r
7526           if (u == 0)\r
7527           {\r
7528                d1x = 0;\r
7529                m = 0;\r
7530           }\r
7531           else\r
7532           {\r
7533                m = abs(u);\r
7534                if (u < 0)\r
7535                     d1x = -2;\r
7536                else\r
7537                if (u > 0)\r
7538                     d1x = 2;\r
7539           }\r
7540           if (v == 0)\r
7541           {\r
7542                d1y = 0;\r
7543                n = 0;\r
7544           }\r
7545           else\r
7546           {\r
7547                n = abs(v);\r
7548                if (v < 0)\r
7549                     d1y = -2;\r
7550                else\r
7551                if (v > 0)\r
7552                     d1y = 2;\r
7553           }\r
7554           if (m > n)\r
7555           {\r
7556                d2x = d1x;\r
7557                d2y = 0;\r
7558           }\r
7559           else\r
7560           {\r
7561                d2x = 0;\r
7562                d2y = d1y;\r
7563                m = n;\r
7564                n = abs(u);\r
7565           }\r
7566           s = m / 2;\r
7567      \r
7568           for (i = 0; i <= m; i+=2)\r
7569           {\r
7570                plot(a,b,colour);\r
7571                s += n;\r
7572                if (s >= m)\r
7573                {\r
7574                     s -= m;\r
7575                     a += d1x;\r
7576                     b += d1y;\r
7577                }\r
7578                else\r
7579                {\r
7580                     a += d2x;\r
7581                     b += d2y;\r
7582                }\r
7583           }\r
7584      }\r
7585      \r
7586      \r
7587      void main(void)\r
7588      {\r
7589           int n;\r
7590      \r
7591           /* Display different colour bands */\r
7592      \r
7593           setvideo(16);\r
7594      \r
7595           for(n = 1; n < 16; n++)\r
7596           {\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
7600      \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
7604      \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
7608      \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
7612      \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
7616      \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
7620           }\r
7621      }\r
7622 \r
7623 This technique can be put to good use for drawing three dimensional\r
7624 boxes;\r
7625 \r
7626      void box3d(int xa,int ya, int xb, int yb, int col)\r
7627      {\r
7628           /* Draws a box for use in 3d histogram graphs etc */\r
7629      \r
7630           int xc;\r
7631           int xd;\r
7632      \r
7633           int n;\r
7634      \r
7635           xd = (xb - xa) / 2;\r
7636           xc = xa + xd;\r
7637      \r
7638           /* First draw the solid face */\r
7639           for(n = xa; n < xb; n++)\r
7640                line(n,ya,n,yb,col);\r
7641      \r
7642           /* Now "shaded" top and side */\r
7643           for(n = 0; n < xd; n++)\r
7644           {\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
7648           }\r
7649      }\r
7650      \r
7651 \r
7652 \r
7653 Displaying Text At Pixel Coordinates\r
7654 \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
7661 \r
7662      void gr_print(char *output, int x, int y, unsigned char colour)\r
7663      {\r
7664           unsigned char far *ptr;\r
7665           unsigned char chr;\r
7666           unsigned char bmask;\r
7667           int i;\r
7668           int k;\r
7669           int oldy;\r
7670           int p;\r
7671           int height;\r
7672      \r
7673           /* The height of the characters in the font being accessed */\r
7674           height = 8;\r
7675      \r
7676           /* Set pointer to start of font definition in the ROM */\r
7677           ptr = getfontptr(3);\r
7678      \r
7679           oldy = y;\r
7680           p = 0;\r
7681      \r
7682           /* Loop output string */\r
7683           while(output[p])\r
7684           {\r
7685                /* Get first character to be displayed */\r
7686                chr = output[p];\r
7687      \r
7688                /* Loop pixel lines in character definition */\r
7689                for(i = 0; i < height; i++)\r
7690                {\r
7691                     /* Get pixel line definition from the ROM */\r
7692                     bmask = *(ptr + (chr * height) + i);\r
7693      \r
7694                     /* Loop pixel columns */\r
7695                     for (k = 0; k < 8; ++k, bmask <<= 1)\r
7696                     {\r
7697                          /* Test for a set bit */\r
7698                          if(bmask & 128)\r
7699                               /* Plot a pixel if appropriate */\r
7700                               plot(x,y,colour);\r
7701                          else\r
7702                               plot(x,y,0);\r
7703                          x++;\r
7704                     }\r
7705                     /* Down to next row and left to start of character */\r
7706                     y++;\r
7707                     x -= 8;\r
7708                }\r
7709                /* Next character to be displayed */\r
7710                p++;\r
7711      \r
7712                /* Back to top row of the display position */\r
7713                y = oldy;\r
7714      \r
7715                /* Right to next character display position */\r
7716                x += 8;\r
7717           }\r
7718      }\r
7719 \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
7723 \r
7724 \r
7725      ; GET FONT POINTER\r
7726      ; Small memory model\r
7727      ; compile with tasm /mx\r
7728      \r
7729      _TEXT     segment   byte public 'CODE'\r
7730                      assume   cs:_TEXT,ds:NOTHING\r
7731      \r
7732      _getfontptr    proc near\r
7733                push bp\r
7734                mov   bp,sp\r
7735                mov   ax,1130h\r
7736                mov   bh, [bp+4]         ; Number for font to be retrieved\r
7737                int   10h\r
7738                mov   dx,es\r
7739                mov   ax,bp\r
7740                pop   bp\r
7741                ret\r
7742      _getfontptr    endp\r
7743      \r
7744      _TEXT     ends\r
7745      \r
7746           public    _getfontptr\r
7747           end\r
7748      \r
7749 The font number supplied to "getfontptr()" can be one of;\r
7750 \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
7754 \r
7755 A Graphics Function Library For Turbo C\r
7756 \r
7757      /* Graphics library for 'Turbo C'  (V2.01) */\r
7758      \r
7759      /* (C)1992 Copyright Servile Software */\r
7760      \r
7761      #include <stdio.h>\r
7762      #include <dos.h>\r
7763      #include <stdlib.h>\r
7764      #include <stdarg.h>\r
7765      #include <string.h>\r
7766      #include <math.h>\r
7767      \r
7768      /* Global variables */\r
7769      static unsigned char attribute;\r
7770      int _dmode;\r
7771      int _lastx;\r
7772      int _lasty;\r
7773      \r
7774      /* Maximum coordinates for graphics screen */\r
7775      static int maxx;\r
7776      static int maxy;\r
7777      \r
7778      /* Sprite structure */\r
7779      struct SP\r
7780      {\r
7781           int x;\r
7782           int y;\r
7783           char data[256];\r
7784           char save[256];\r
7785      };\r
7786      typedef struct SP SPRITE;\r
7787      \r
7788      \r
7789      /* Sine and cosine tables for trig' operations */\r
7790      \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
7972           };\r
7973 \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
8155      \r
8156      int dtoi(double x)\r
8157      {\r
8158           /* Rounds a floating point number to an integer */\r
8159      \r
8160           int y;\r
8161      \r
8162           y = x;\r
8163           if (y < (x + 0.5))\r
8164                y++;\r
8165           return(y);\r
8166      }\r
8167      \r
8168      int getmode(int *ncols)\r
8169      {\r
8170           /* Returns current video mode and number of columns in ncols */\r
8171      \r
8172           union REGS inreg,outreg;\r
8173      \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
8178      }\r
8179      \r
8180      void setvideo(unsigned char mode)\r
8181      {\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
8184      \r
8185           asm mov ax , mode;\r
8186           asm int 10h;\r
8187      \r
8188           /* Set global variables */\r
8189           switch(mode)\r
8190           {\r
8191                case 0:\r
8192                case 1:\r
8193                case 2:\r
8194                case 3: break;\r
8195                case 4:\r
8196                case 9:\r
8197                case 13:\r
8198                case 19:\r
8199                case 5: maxx = 320;\r
8200                     maxy = 200;\r
8201                     break;\r
8202                case 14:\r
8203                case 10:\r
8204                case 6: maxx = 640;\r
8205                     maxy = 200;\r
8206                     break;\r
8207                case 7: maxx = 720;\r
8208                     maxy = 400;\r
8209                     break;\r
8210                case 8: maxx = 160;\r
8211                     maxy = 200;\r
8212                     break;\r
8213                case 15:\r
8214                case 16: maxx = 640;\r
8215                      maxy = 350;\r
8216                      break;\r
8217                case 17:\r
8218                case 18: maxx = 640;\r
8219                      maxy = 480;\r
8220                      break;\r
8221      \r
8222           }\r
8223           _dmode = mode;\r
8224      }\r
8225      \r
8226      void getcursor(int *row, int *col)\r
8227      {\r
8228           /* Returns the cursor position and size for the currently\r
8229      active\r
8230              video display page */\r
8231      \r
8232           union REGS inreg,outreg;\r
8233      \r
8234           inreg.h.bh = 0;\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
8239      }\r
8240      \r
8241      void setcursor(char status)\r
8242      {\r
8243           /* Set cursor size, if status is 0x20 the cursor is not\r
8244      displayed */\r
8245      \r
8246           asm mov ah,1\r
8247           asm mov ch,status\r
8248           asm mov cl,7\r
8249           asm int 10h\r
8250      }\r
8251      \r
8252      void at(int row, int col)\r
8253      {\r
8254           /* Position text cursor on current screen */\r
8255      \r
8256           asm mov bh , 0;\r
8257           asm mov dh , row;\r
8258           asm mov dl , col;\r
8259           asm mov ah , 02h;\r
8260           asm int 10h;\r
8261      }\r
8262      \r
8263      void clsc(unsigned char attrib)\r
8264      {\r
8265           /* Clear display and fill with new attribute */\r
8266      \r
8267           asm mov ax , 073dh;\r
8268           asm mov bh , attrib;\r
8269           asm mov cx , 0;\r
8270           asm mov dx , 3c4fh;\r
8271           asm int 10h;\r
8272      \r
8273           /* Now move text cursor to origin (0,0) */\r
8274           asm mov bh , 0;\r
8275           asm mov dx , 0;\r
8276           asm mov ah , 02h;\r
8277           asm int 10h;\r
8278      }\r
8279      \r
8280      void clrwindow(int x1, int y1,int x2, int y2, unsigned char attrib)\r
8281      {\r
8282           /* Clear a text window */\r
8283      \r
8284           union REGS inreg,outreg;\r
8285      \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
8294      }\r
8295      \r
8296      void scrollsc(void)\r
8297      {\r
8298           /* Scroll the text screen */\r
8299      \r
8300           asm mov al,01h;\r
8301           asm mov cx,0\r
8302           asm mov dx,3c4fh;\r
8303           asm mov bh,attribute;\r
8304           asm mov ah, 06h;\r
8305           asm int 10h;\r
8306      }\r
8307      \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
8311      {\r
8312           /* Scroll a text window */\r
8313      \r
8314           asm mov al , numlines;\r
8315           asm mov cl , left;\r
8316           asm mov ch , top;\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
8321           asm int 10h;\r
8322      }\r
8323      \r
8324      unsigned char attr(int foregrnd, int backgrnd)\r
8325      {\r
8326           /* Convert a colour pair into a single attribute */\r
8327      \r
8328           return((char)((backgrnd << 4) + foregrnd));\r
8329      }\r
8330      \r
8331      void shadewin(unsigned char left, unsigned char top, unsigned char\r
8332      right,   unsigned char bottom)\r
8333      {\r
8334           /* Shade a text window */\r
8335      \r
8336           int row;\r
8337           int col;\r
8338           int oldrow;\r
8339           int oldcol;\r
8340      \r
8341           /* Preserve existing coords */\r
8342           getcursor(&oldrow,&oldcol);\r
8343      \r
8344           col = right + 1;\r
8345           for (row = top + 1; row <= bottom + 1; row++)\r
8346           {\r
8347                /* Move to row,col */\r
8348                asm mov bh , 0;\r
8349                asm mov dh , row;\r
8350                asm mov dl , col;\r
8351                asm mov ah , 02h;\r
8352                asm int 10h;\r
8353                /* Get character */\r
8354                asm mov ah , 0Fh;\r
8355                asm int 10h;\r
8356                asm mov ah , 08h;\r
8357                asm int 10h;\r
8358      \r
8359                /* Write in attribute 7 */\r
8360                asm mov ah , 09h;\r
8361                asm mov bl, 07h;\r
8362                asm mov cx, 01h;\r
8363                asm int 10h;\r
8364           }\r
8365           bottom++;\r
8366           for (col = left + 1; col <= right + 1; col++)\r
8367           {\r
8368                /* Move to row,col */\r
8369                asm mov bh , 0;\r
8370                asm mov dh , bottom;\r
8371                asm mov dl , col;\r
8372                asm mov ah , 02h;\r
8373                asm int 10h;\r
8374      \r
8375                /* Get character */\r
8376                asm mov ah , 0Fh;\r
8377                asm int 10h;\r
8378                asm mov ah , 08h;\r
8379                asm int 10h;\r
8380      \r
8381                /* Write in attribute 7 */\r
8382                asm mov ah , 09h;\r
8383                asm mov bl, 07h;\r
8384                asm mov cx, 01h;\r
8385                asm int 10h;\r
8386           }\r
8387      \r
8388           /* Return to original position */\r
8389           /* Move to row,col */\r
8390           asm mov bh , 0;\r
8391           asm mov dh , oldrow;\r
8392           asm mov dl , oldcol;\r
8393           asm mov ah , 02h;\r
8394           asm int 10h;\r
8395      }\r
8396      \r
8397      void bprintf(char *format, ...)\r
8398      {\r
8399           /* print text to graphics screen correctly */\r
8400      \r
8401           va_list arg_ptr;\r
8402           char output[1000];\r
8403           int c, r, n, p = 0;\r
8404           unsigned char text;\r
8405           unsigned char page;\r
8406      \r
8407           va_start(arg_ptr, format);\r
8408           vsprintf(output, format, arg_ptr);\r
8409      \r
8410           if (strcmp(output,"\r\n") == 0)\r
8411                fputs(output,stdout);\r
8412      \r
8413           else\r
8414           {\r
8415                asm mov ah , 0Fh;\r
8416                asm int 10h;\r
8417                asm mov page, bh;\r
8418      \r
8419                getmode(&n);\r
8420                getcursor(&r,&c);\r
8421      \r
8422                while (output[p])\r
8423                {\r
8424                     text = output[p++];\r
8425                     asm mov bh , page;\r
8426                     asm mov bl , attribute;\r
8427                     asm mov cx , 01h;\r
8428                     asm mov ah , 09h;\r
8429                     asm mov al , text;\r
8430                     asm int 10h;\r
8431      \r
8432                     c++;\r
8433                     if (c < (n-1))\r
8434                          at( r, c);\r
8435                     else\r
8436                     {\r
8437                          c = 0;\r
8438                          at(++r,0);\r
8439                     }\r
8440                }\r
8441           }\r
8442      }\r
8443      \r
8444      void wrtstr(char *output)\r
8445      {\r
8446           /* TTY text output. The original screen attributes remain\r
8447      unaffected */\r
8448      \r
8449           int p = 0;\r
8450           unsigned char page;\r
8451           unsigned char text;\r
8452      \r
8453           asm mov ah , 0Fh;\r
8454           asm int 10h;\r
8455           asm mov page, bh;\r
8456      \r
8457           while (output[p])\r
8458           {\r
8459                text = output[p++];\r
8460                asm mov bh , page;\r
8461                asm mov ah , 0Eh;\r
8462                asm mov al , text;\r
8463                asm int 10h;\r
8464           }\r
8465      }\r
8466      \r
8467      void getwin(int left, int top, int right, int bottom, char *buffer)\r
8468      {\r
8469           /* Read a text window into a variable */\r
8470      \r
8471           int oldleft;\r
8472           unsigned char page;\r
8473      \r
8474           asm mov ah , 0Fh;\r
8475           asm int 10h;\r
8476           asm mov page, bh;\r
8477      \r
8478           while(top <= bottom)\r
8479           {\r
8480                oldleft = left;\r
8481                while(left <= right)\r
8482                {\r
8483                     at(top,left);\r
8484                     asm mov bh , page;\r
8485                     asm mov ah , 08h;\r
8486                     asm int 10h;\r
8487                     *buffer++ = _AL;\r
8488                     *buffer++ = _AH;\r
8489                     left++;\r
8490                }\r
8491                left = oldleft;\r
8492                top++;\r
8493           }\r
8494      }\r
8495      \r
8496      void putwin(int left, int top, int right, int bottom,char *buffer)\r
8497      {\r
8498           /* Display a text window from a variable */\r
8499      \r
8500           int oldleft;\r
8501           unsigned char chr;\r
8502           unsigned char attr;\r
8503           unsigned char page;\r
8504      \r
8505           asm mov ah , 0Fh;\r
8506           asm int 10h;\r
8507           asm mov page, bh;\r
8508      \r
8509           while(top <= bottom)\r
8510           {\r
8511                oldleft = left;\r
8512                while(left <= right)\r
8513                {\r
8514                     at(top,left);\r
8515                     chr = *buffer++;\r
8516                     attr = *buffer++;\r
8517                     asm mov bh , page;\r
8518                     asm mov ah , 09h;\r
8519                     asm mov al, chr;\r
8520                     asm mov bl, attr;\r
8521                     asm mov cx,1;\r
8522                     asm int 10h;\r
8523                     left++;\r
8524                }\r
8525                left = oldleft;\r
8526                top++;\r
8527           }\r
8528      }\r
8529      \r
8530      void setpalette(unsigned char palno)\r
8531      {\r
8532           /* Sets the video palette */\r
8533      \r
8534           asm mov bh,01h;\r
8535           asm mov bl,palno;\r
8536           asm mov ah,0Bh;\r
8537           asm int 10h;\r
8538      }\r
8539      \r
8540      void setborder(unsigned char x)\r
8541      {\r
8542           /* Set border colour */\r
8543      \r
8544           asm mov bh, x;\r
8545           asm mov ax ,1001h;\r
8546           asm int 10h;\r
8547      }\r
8548      \r
8549      void setlines(unsigned char x)\r
8550      {\r
8551           /* Set text display number of lines */\r
8552      \r
8553           asm mov ah,11h;\r
8554           asm mov al,x;\r
8555           asm mov bl,0;\r
8556           asm int 10h;\r
8557      }\r
8558      \r
8559      void graphbackground(unsigned char colour)\r
8560      {\r
8561           /* Selects the background colour for a graphics mode */\r
8562      \r
8563           asm mov bh,0;\r
8564           asm mov bl,colour;\r
8565           asm mov ah, 0Bh;\r
8566           asm int 10h;\r
8567      }\r
8568      \r
8569      void plot(int x, int y, unsigned char colour)\r
8570      {\r
8571           /* Sets a pixel at the specified coordinates to the specified\r
8572      colour.\r
8573              The variables _lastx and _lasty are updated. */\r
8574      \r
8575           unsigned char far *video;\r
8576      \r
8577           _lastx = x;\r
8578           _lasty = y;\r
8579      \r
8580           if (_dmode == 19)\r
8581           {\r
8582                video = MK_FP(0xA000,0);\r
8583                video[x+y*320] = colour;\r
8584           }\r
8585           else\r
8586           {\r
8587                asm mov al , colour;\r
8588                asm mov bh , 00;\r
8589                asm mov cx , x;\r
8590                asm mov dx , y;\r
8591                asm mov ah , 0Ch;\r
8592                asm int 10h;\r
8593           }\r
8594      }\r
8595      \r
8596      int pixset(int x, int y)\r
8597      {\r
8598           /* Returns the colour of the specified pixel */\r
8599      \r
8600           asm mov cx ,x;\r
8601           asm mov dx ,y;\r
8602           asm mov ah ,0Dh;\r
8603           asm int 10h;\r
8604           return(_AL);\r
8605      }\r
8606      \r
8607      void move(int x, int y)\r
8608      {\r
8609           /* Sets the value of the variables _lastx and _lasty */\r
8610      \r
8611           _lastx = x;\r
8612           _lasty = y;\r
8613      }\r
8614      \r
8615      void line(int a, int b, int c, int d, int col)\r
8616      {\r
8617           /* Draws a straight line from (a,b) to (c,d) in colour col */\r
8618      \r
8619           int u;\r
8620           int v;\r
8621           int d1x;\r
8622           int d1y;\r
8623           int d2x;\r
8624           int d2y;\r
8625           int m;\r
8626           int n;\r
8627           int s;\r
8628           int i;\r
8629      \r
8630           if (a < 0)\r
8631                a = 0;\r
8632           else\r
8633           if (a > maxx)\r
8634                a = maxx;\r
8635      \r
8636           if (c < 0)\r
8637                c = 0;\r
8638           else\r
8639           if (c > maxx)\r
8640                c = maxx;\r
8641      \r
8642           if (b < 0)\r
8643                b = 0;\r
8644           else\r
8645           if (b > maxy)\r
8646                b = maxy;\r
8647      \r
8648           if (d < 0)\r
8649                d = 0;\r
8650           else\r
8651           if (d > maxy)\r
8652                d = maxy;\r
8653      \r
8654           u = c - a;\r
8655           v = d - b;\r
8656      \r
8657           if (u == 0)\r
8658           {\r
8659                d1x = 0;\r
8660                m = 0;\r
8661           }\r
8662           else\r
8663           {\r
8664                m = abs(u);\r
8665                if (u < 0)\r
8666                     d1x = -1;\r
8667                else\r
8668                if (u > 0)\r
8669                     d1x = 1;\r
8670           }\r
8671           if ( v == 0)\r
8672           {\r
8673                d1y = 0;\r
8674                n = 0;\r
8675           }\r
8676           else\r
8677           {\r
8678                n = abs(v);\r
8679                if (v < 0)\r
8680                     d1y = -1;\r
8681                else\r
8682                if (v > 0)\r
8683                     d1y = 1;\r
8684           }\r
8685           if (m > n)\r
8686           {\r
8687                d2x = d1x;\r
8688                d2y = 0;\r
8689           }\r
8690           else\r
8691           {\r
8692                d2x = 0;\r
8693                d2y = d1y;\r
8694                m = n;\r
8695                n = abs(u);\r
8696           }\r
8697           s = m / 2;\r
8698      \r
8699           for (i = 0; i <= m; i++)\r
8700           {\r
8701                asm mov al , col;\r
8702                asm mov bh , 0;\r
8703                asm mov ah ,0Ch;\r
8704                asm mov cx ,a;\r
8705                asm mov dx ,b;\r
8706                asm int 10h;\r
8707      \r
8708                s += n;\r
8709                if (s >= m)\r
8710                {\r
8711                     s -= m;\r
8712                     a += d1x;\r
8713                     b += d1y;\r
8714                }\r
8715                else\r
8716                {\r
8717                     a += d2x;\r
8718                     b += d2y;\r
8719                }\r
8720           }\r
8721           _lastx = a;\r
8722           _lasty = b;\r
8723      }\r
8724      \r
8725      void ellipse(int x, int y, int xrad, int yrad,double incline,int\r
8726      col)\r
8727      {\r
8728           /* Draws an ellipse */\r
8729      \r
8730           int f;\r
8731           float a;\r
8732           float b;\r
8733           float c;\r
8734           float d;\r
8735           int cols;\r
8736           double div;\r
8737      \r
8738           incline = 1 / sintable[(int)incline];\r
8739      \r
8740           if (getmode(&cols) == 6)\r
8741                div = 2.2;\r
8742           else\r
8743                div = 1.3;\r
8744      \r
8745                /* Compensate for pixel shape */\r
8746      \r
8747           a = x + xrad;\r
8748           b = y + sintable[0] * yrad + xrad/incline / div;\r
8749      \r
8750           for(f = 5; f < 360; f += 5)\r
8751           {\r
8752                c = x + costable[f] * xrad;\r
8753                d = y + sintable[f] * yrad + (costable[f] *\r
8754      xrad)/incline/div;\r
8755      \r
8756                line(a,b,c,d,col);\r
8757      \r
8758                a = c;\r
8759                b = d;\r
8760           }\r
8761           /* Ensure join */\r
8762           line(a,b,x + xrad,y + sintable[0] * yrad + xrad/incline /\r
8763      div,col);\r
8764      }\r
8765      \r
8766      void polygon(int x, int y, int rad, int col, int sides, int start)\r
8767      {\r
8768           /* Draws a regular polygon */\r
8769      \r
8770           double f;\r
8771           double div;\r
8772           double a;\r
8773           double b;\r
8774           double c;\r
8775           double d;\r
8776           double aa;\r
8777           double bb;\r
8778           int cols;\r
8779           double step;\r
8780      \r
8781           step = 360 / sides;\r
8782      \r
8783           if (getmode(&cols) == 6)\r
8784                div = 2.2;\r
8785           else\r
8786                div = 1.3;\r
8787           aa = a = x + costable[start] * rad;\r
8788           bb = b = y + sintable[start] * rad / div;\r
8789      \r
8790           for(f = start + step; f < start + 360; f += step)\r
8791           {\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
8795                a = c;\r
8796                b = d;\r
8797           }\r
8798           line(a,b,aa,bb,col);\r
8799      }\r
8800      \r
8801      void arc(int x, int y, int rad, int start, int end,int col)\r
8802      {\r
8803           /* Draw an arc */\r
8804      \r
8805           int f;\r
8806           float a;\r
8807           float b;\r
8808           float c;\r
8809           float d;\r
8810           int cols;\r
8811           float div;\r
8812      \r
8813           if (getmode(&cols) == 6)\r
8814                div = 2.2;\r
8815           else\r
8816                div = 1.3;\r
8817           a = x + costable[start] * rad;\r
8818           b = y + sintable[start] * rad / div;\r
8819      \r
8820           for(f = start; f <= end; f ++)\r
8821           {\r
8822                c = x + costable[f] * rad;\r
8823                d = y + sintable[f] * rad / div;\r
8824                line(a,b,c,d,col);\r
8825                a = c;\r
8826                b = d;\r
8827           }\r
8828      }\r
8829      \r
8830      void segm(int x, int y, int rad, int start, int end,int col)\r
8831      {\r
8832           /* Draw a segment of a circle */\r
8833      \r
8834           int f;\r
8835           float a;\r
8836           float b;\r
8837           float c;\r
8838           float d;\r
8839           int cols;\r
8840           double div;\r
8841      \r
8842           if (getmode(&cols) == 6)\r
8843                div = 2.2;\r
8844           else\r
8845                div = 1.3;\r
8846           a = x + costable[start] * rad;\r
8847           b = y + sintable[start] * rad / div;\r
8848      \r
8849           line(x,y,a,b,col);\r
8850      \r
8851           for(f = start; f <= end ; f ++)\r
8852           {\r
8853                c = x + costable[f] * rad;\r
8854                d = y + sintable[f] * rad / div;\r
8855                line(a,b,c,d,col);\r
8856                a = c;\r
8857                b = d;\r
8858           }\r
8859           line(x,y,a,b,col);\r
8860      }\r
8861      \r
8862      void box(int xa,int ya, int xb, int yb, int col)\r
8863      {\r
8864           /* Draws a box for use in 2d histogram graphs etc */\r
8865      \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
8870      }\r
8871      \r
8872      void tri(int xa,int ya, int xb, int yb, int xc, int yc,int col)\r
8873      {\r
8874           /* Draw a triangle */\r
8875      \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
8879      }\r
8880      \r
8881      void fill(int x, int y, int col,int pattern)\r
8882      {\r
8883           /* Fill a boundered shape using a hatch pattern */\r
8884      \r
8885           int xa;\r
8886           int ya;\r
8887           int bn;\r
8888           int byn;\r
8889      \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
8893                          1,2,4,8,8,4,2,1,\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
8900                     };\r
8901      \r
8902           /* Patterns for fill, each integer describes a row of dots */\r
8903      \r
8904           xa = x;\r
8905           ya = y;  /* Save Origin */\r
8906      \r
8907           if(pixset(x,y))\r
8908                return;\r
8909      \r
8910           bn = 1;\r
8911           byn = 0;\r
8912      \r
8913           do\r
8914           {\r
8915                if (hatch[pattern][byn] != 0)\r
8916                {\r
8917                     /* If blank ignore */\r
8918                     do\r
8919                     {\r
8920                          if ((bn & hatch[pattern][byn]) == bn)\r
8921                          {\r
8922                               asm mov al , col;\r
8923                               asm mov bh , 00;\r
8924                               asm mov cx , x;\r
8925                               asm mov dx , y;\r
8926                               asm mov ah , 0Ch;\r
8927                               asm int 10h;\r
8928                          }\r
8929                          x--;\r
8930                          bn <<= 1;\r
8931                          if (bn > 128)\r
8932                               bn = 1;\r
8933                     }\r
8934                     while(!pixset(x,y) && (x > -1));\r
8935      \r
8936                     x = xa + 1;\r
8937                     bn = 128;\r
8938      \r
8939                     do\r
8940                     {\r
8941                          if ((bn & hatch[pattern][byn]) == bn)\r
8942                          {\r
8943                               asm mov al , col;\r
8944                               asm mov bh , 00;\r
8945                               asm mov cx , x;\r
8946                               asm mov dx , y;\r
8947                               asm mov ah , 0Ch;\r
8948                               asm int 10h;\r
8949                          }\r
8950                          x++;\r
8951                          bn >>=1;\r
8952                          if (bn <1)\r
8953                               bn = 128;\r
8954                     }\r
8955                     while((!pixset(x,y)) && (x <= maxx));\r
8956                }\r
8957                x = xa;\r
8958                y--;\r
8959                bn = 1;\r
8960                byn++;\r
8961                if (byn > 7)\r
8962                     byn = 0;\r
8963      \r
8964           }\r
8965           while(!pixset(x,y) && ( y > -1));\r
8966      \r
8967           /* Now travel downwards */\r
8968      \r
8969           y = ya + 1;\r
8970      \r
8971           byn = 7;\r
8972           bn = 1;\r
8973           do\r
8974           {\r
8975                /* Travel left */\r
8976                if (hatch[pattern][byn] !=0)\r
8977                {\r
8978                     do\r
8979                     {\r
8980                          if ( (bn & hatch[pattern][byn]) == bn)\r
8981                          {\r
8982                               asm mov al , col;\r
8983                               asm mov bh , 00;\r
8984                               asm mov cx , x;\r
8985                               asm mov dx , y;\r
8986                               asm mov ah , 0Ch;\r
8987                               asm int 10h;\r
8988                          }\r
8989      \r
8990                          x--;\r
8991                          bn <<= 1;\r
8992                          if (bn > 128)\r
8993                               bn = 1;\r
8994                     }\r
8995                     while(!pixset(x,y) && (x > -1));\r
8996      \r
8997                     /* Back to x origin */\r
8998                     x = xa + 1 ;\r
8999                     bn = 128;\r
9000      \r
9001                     /* Travel right */\r
9002                     do\r
9003                     {\r
9004                          if ((bn & hatch[pattern][byn]) == bn)\r
9005                          {\r
9006                               asm mov al , col;\r
9007                               asm mov bh , 00;\r
9008                               asm mov cx , x;\r
9009                               asm mov dx , y;\r
9010                               asm mov ah , 0Ch;\r
9011                               asm int 10h;\r
9012                          }\r
9013                          x++;\r
9014                          bn >>=1;\r
9015                          if (bn <1)\r
9016                               bn = 128;\r
9017                     }\r
9018                     while((!pixset(x,y)) && (x <= maxx));\r
9019                }\r
9020                x = xa;\r
9021                bn = 1;\r
9022                y++;\r
9023                byn--;\r
9024                if (byn < 0)\r
9025                     byn = 7;\r
9026           }\r
9027           while((!pixset(x,y)) && (y <= maxy));\r
9028      }\r
9029      \r
9030      void invert(int xa,int ya, int xb, int yb, int col)\r
9031      {\r
9032           /* Invert a pixel window */\r
9033      \r
9034           union REGS inreg,outreg;\r
9035      \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
9041      inreg.x.dx++)\r
9042                     int86(0x10, &inreg, &outreg);\r
9043      }\r
9044      \r
9045      void circle(int x_centre , int y_centre, int radius, int colour)\r
9046      {\r
9047           int x,y,delta;\r
9048           int startx,endx,x1,starty,endy,y1;\r
9049           int asp_ratio;\r
9050      \r
9051           if (_dmode == 6)\r
9052                asp_ratio = 22;\r
9053           else\r
9054                asp_ratio = 13;\r
9055      \r
9056           y = radius;\r
9057           delta = 3 - 2 * radius;\r
9058           for(x = 0; x < y; )\r
9059           {\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
9065                {\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
9070                }\r
9071      \r
9072                for(y1 = starty; y1 < endy; ++y1)\r
9073                {\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
9078                }\r
9079      \r
9080                if (delta < 0)\r
9081                     delta += 4 * x + 6;\r
9082                else\r
9083                {\r
9084                     delta += 4*(x-y)+10;\r
9085                     y--;\r
9086                }\r
9087                x++;\r
9088           }\r
9089      \r
9090           if(y)\r
9091           {\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
9097                {\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
9102                }\r
9103      \r
9104                for(y1 = starty; y1 < endy; ++y1)\r
9105                {\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
9110                }\r
9111           }\r
9112      }\r
9113      \r
9114      void draw(int x, int y, int colour)\r
9115      {\r
9116           /* Draws a line from _lastx,_lasty to x,y */\r
9117      \r
9118           line(_lastx,_lasty,x,y,colour);\r
9119      }\r
9120      \r
9121      void psprite(SPRITE *sprite,int x,int y)\r
9122      {\r
9123           int origx;\r
9124           int origy;\r
9125           int z;\r
9126           int count;\r
9127           int col;\r
9128           int pos;\r
9129           unsigned char far *video;\r
9130      \r
9131           if (_dmode == 19)\r
9132           {\r
9133                /* Super fast direct video write in mode 19 for sprites */\r
9134      \r
9135                video = MK_FP(0xA000,0);\r
9136      \r
9137                origx = x;\r
9138                origy = y;\r
9139      \r
9140                if (sprite->x != -1)\r
9141                {\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
9146                     x = sprite->x;\r
9147                     y = sprite->y;\r
9148                     col = 0;\r
9149                     pos = x + y * 320;\r
9150                     for(count = 0; count < 256; count++)\r
9151                     {\r
9152                          video[pos] = sprite->save[count];\r
9153                          col++;\r
9154                          if (col == 16)\r
9155                          {\r
9156                               pos += 305;\r
9157                               col = 0;\r
9158                          }\r
9159                          else\r
9160                               pos++;\r
9161                     }\r
9162                }\r
9163      \r
9164                x = origx;\r
9165                y = origy;\r
9166                col = 0;\r
9167      \r
9168                pos = x + y * 320;\r
9169      \r
9170                for(count = 0; count < 256; count++)\r
9171                {\r
9172                     sprite->save[count] = video[pos];\r
9173                     z = sprite->data[count];\r
9174                     if (z != 255)\r
9175                          video[pos] = z;\r
9176      \r
9177                     col++;\r
9178                     if (col == 16)\r
9179                     {\r
9180                          pos += 305;\r
9181                          col = 0;\r
9182                     }\r
9183                     else\r
9184                          pos++;\r
9185                }\r
9186                sprite->x = origx;\r
9187                sprite->y = origy;\r
9188      \r
9189                return;\r
9190           }\r
9191      \r
9192           origx = x;\r
9193           origy = y;\r
9194      \r
9195           if (sprite->x != -1)\r
9196           {\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
9200      itself */\r
9201                x = sprite->x;\r
9202                y = sprite->y;\r
9203                col = 0;\r
9204                for(count = 0; count < 256; count++)\r
9205                {\r
9206                     if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))\r
9207                     {\r
9208                          z = sprite->save[count];\r
9209                          asm mov al , z;\r
9210                          asm mov bh , 00;\r
9211                          asm mov cx , x;\r
9212                          asm mov dx , y;\r
9213                          asm mov ah , 0Ch;\r
9214                          asm int 10h;\r
9215                     }\r
9216                     col++;\r
9217                     if (col == 16)\r
9218                     {\r
9219                          y++;\r
9220                          x = sprite->x;\r
9221                          col = 0;\r
9222                     }\r
9223                     else\r
9224                          x++;\r
9225                }\r
9226           }\r
9227      \r
9228           x = origx;\r
9229           y = origy;\r
9230           col = 0;\r
9231      \r
9232           for(count = 0; count < 256; count++)\r
9233           {\r
9234                if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))\r
9235                {\r
9236                     asm mov cx , x;\r
9237                     asm mov dx , y;\r
9238                     asm mov ah , 0Dh;\r
9239                     asm int 10h;\r
9240                     asm mov z ,al;\r
9241                     sprite->save[count] = z;\r
9242                     z = sprite->data[count];\r
9243      \r
9244                     if (z != 255)\r
9245                     {\r
9246                          asm mov al , z;\r
9247                          asm mov bh , 0;\r
9248                          asm mov cx , x;\r
9249                          asm mov dx , y;\r
9250                          asm mov ah , 0Ch;\r
9251                          asm int 10h;\r
9252                     }\r
9253                }\r
9254                col++;\r
9255                if (col == 16)\r
9256                {\r
9257                     y++;\r
9258                     x = origx;\r
9259                     col = 0;\r
9260                }\r
9261                else\r
9262                     x++;\r
9263           }\r
9264           sprite->x = origx;\r
9265           sprite->y = origy;\r
9266           return;\r
9267      }\r
9268      \r
9269      \r
9270 \r
9271 Displaying A PCX File\r
9272 \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
9275 the screen.\r
9276 \r
9277      \r
9278      /* Read a PCX file and display image */\r
9279      \r
9280      #include <dos.h>\r
9281      #include <io.h>\r
9282      #include <fcntl.h>\r
9283      \r
9284      typedef struct\r
9285      {\r
9286           unsigned char man;\r
9287           unsigned char version;\r
9288           unsigned char encoding;\r
9289           unsigned char bpp;\r
9290           int xmin;\r
9291           int ymin;\r
9292           int xmax;\r
9293           int ymax;\r
9294           int hdpi;\r
9295           int vdpi;\r
9296           int colormap[24];\r
9297           char reserved;\r
9298           unsigned char planes;\r
9299           int bpl;\r
9300           int palette;\r
9301           int hss;\r
9302           int vsize;\r
9303           char pad[54];\r
9304      }\r
9305      PCX_HEADER;\r
9306      \r
9307      PCX_HEADER header;\r
9308      \r
9309      int x;\r
9310      int y;\r
9311      \r
9312      union REGS inreg,outreg;\r
9313      \r
9314      void setvideo(unsigned char mode)\r
9315      {\r
9316           /* Sets the video display mode     and clears the screen */\r
9317      \r
9318           inreg.h.al = mode;\r
9319           inreg.h.ah = 0x00;\r
9320           int86(0x10, &inreg, &outreg);\r
9321      }\r
9322      \r
9323      void plot(int x, int y, unsigned char colour)\r
9324      {\r
9325      \r
9326              if (x < header.xmax && y < header.ymax)\r
9327              {\r
9328      \r
9329              /* Direct video plot in modes 16 & 18 only! */\r
9330              asm mov   ax,y;\r
9331              asm mov   dx,80;\r
9332              asm mul   dx;\r
9333              asm mov   bx,x;\r
9334              asm mov   cl,bl;\r
9335      \r
9336              asm shr   bx,1;\r
9337              asm shr   bx,1;\r
9338              asm shr   bx,1;\r
9339              asm add   bx,ax;\r
9340      \r
9341              asm and   cl,7;\r
9342              asm xor   cl,7;\r
9343              asm mov   ah,1;\r
9344              asm shl   ah,cl;\r
9345      \r
9346              asm mov   dx,3ceh;\r
9347              asm mov   al,8;\r
9348              asm out   dx,ax;\r
9349      \r
9350              asm mov   ax,(02h shl 8) + 5;\r
9351              asm out   dx,ax;\r
9352      \r
9353              asm mov   ax,0A000h;\r
9354              asm mov   es,ax;\r
9355      \r
9356              asm mov   al,es:[bx];\r
9357              asm mov   al,byte ptr colour;\r
9358              asm mov   es:[bx],al;\r
9359      \r
9360              asm mov   ax,(0FFh shl 8 ) + 8;\r
9361              asm out   dx,ax;\r
9362      \r
9363              asm mov   ax,(00h shl 8) + 5;\r
9364              asm out   dx,ax;\r
9365           }\r
9366      }\r
9367      \r
9368      void DISPLAY(unsigned char data)\r
9369      {\r
9370           int n;\r
9371           int bit;\r
9372      \r
9373           bit = 32;\r
9374      \r
9375           for (n = 0; n < 6; n++)\r
9376           {\r
9377                if (data & bit)\r
9378                     plot(x,y,1);\r
9379                else\r
9380                     plot(x,y,0);\r
9381                bit >>= 1;\r
9382                x++;\r
9383           }\r
9384      }\r
9385      \r
9386      main(int argc, char *argv[])\r
9387      {\r
9388           int fp;\r
9389           int total_bytes;\r
9390           int n;\r
9391           unsigned char data;\r
9392           int count;\r
9393           int scan;\r
9394      \r
9395           if (argc != 2)\r
9396           {\r
9397                puts("USAGE IS getpcx <filename>");\r
9398                exit(0);\r
9399           }\r
9400      \r
9401           setvideo(16);\r
9402      \r
9403           x = 0;\r
9404           y = 0;\r
9405      \r
9406           fp = open(argv[1],O_RDONLY|O_BINARY);\r
9407      \r
9408           _read(fp,&header,128);\r
9409      \r
9410           total_bytes = header.planes * header.bpl;\r
9411      \r
9412           for(scan = 0; scan <= header.ymax; scan++)\r
9413           {\r
9414                x = 0;\r
9415      \r
9416                /* First scan line */\r
9417      \r
9418                for(n = 0; n < total_bytes; n++)\r
9419                {\r
9420                     /* Read byte */\r
9421                     _read(fp,&data,1);\r
9422      \r
9423                     count = data & 192;\r
9424      \r
9425                     if (count == 192)\r
9426                     {\r
9427                          count = data & 63;\r
9428                          n += count - 1;\r
9429                          _read(fp,&data,1);\r
9430                          while(count)\r
9431                          {\r
9432                               DISPLAY(data);\r
9433                               count--;\r
9434                          }\r
9435                     }\r
9436                     else\r
9437                          DISPLAY(data);\r
9438      \r
9439                }\r
9440                x = 0;\r
9441                y++;\r
9442           }\r
9443      }\r
9444           \r
9445 \r
9446 Drawing Circles\r
9447 \r
9448 \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
9455 \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
9458 \r
9459 \r
9460      Xp = Xo + Sine(Angle) * Radius\r
9461      Yp = Yo + Cosine(Angle) * Radius\r
9462 \r
9463 Where Xp,Yp is the point to be plotted, and Xo,Yo is the centre of the\r
9464 circle.\r
9465 \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
9472 \r
9473 The problem of the distance between the angular points may be tackled in\r
9474 two ways;\r
9475 \r
9476   1)The number of points to be plotted can be increased, to say every 30\r
9477      minutes.\r
9478   2)A straight line may be drawn between each angular point.\r
9479 \r
9480 The simplest circle drawing pseudo-code may then look like this;\r
9481 \r
9482 \r
9483      FOR angle = 1 TO 360\r
9484           PLOT Xo + SINE(angle) * radius, Yo + COSINE(angle) * radius\r
9485      NEXT angle\r
9486 \r
9487 This code has two major disadvantages;\r
9488 \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
9491 \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
9498 360 times;\r
9499 \r
9500 \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
9508      NEXT angle\r
9509 \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
9515 \r
9516 \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
9524      NEXT angle\r
9525 \r
9526 \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
9535 \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
9539 \r
9540        0.0175\r
9541 \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
9545 becomes;\r
9546 \r
9547        175\r
9548 \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
9552 \r
9553           Xa = SINE[angle] * radius / factor\r
9554           Ya = COSINE[angle] * radius / factor\r
9555 \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
9567 \r
9568 The offset formulae then become;\r
9569 \r
9570           Xa = SINE[angle] * radius / factor\r
9571           Ya = COSINE[angle] * radius / (factor * aspect ratio)\r
9572 \r
9573 The following short C program illustrates a practical circle drawing code\r
9574 segment, in a demonstrable  form;\r
9575 \r
9576 \r
9577      /* Circles.c   A demonstration circle drawing program for the IBM PC\r
9578      */\r
9579      \r
9580      \r
9581      #include <stdlib.h>\r
9582      \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
9605                9998,10000\r
9606      };\r
9607      \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
9629                872,698,523,349,\r
9630                175,0\r
9631      };\r
9632      \r
9633      void setvideo(unsigned char mode)\r
9634      {\r
9635           /* Sets the video display mode for an IBM PC */\r
9636      \r
9637           asm mov al , mode;\r
9638           asm mov ah , 00;\r
9639           asm int 10h;\r
9640      }\r
9641      \r
9642      void plot(int x, int y, unsigned char colour)\r
9643      {\r
9644           /* Code for IBM PC BIOS ROM */\r
9645           /* Sets a pixel at the specified coordinates to a specified\r
9646      colour */\r
9647      \r
9648           /* Return if out of range */\r
9649           if (x < 0 || y < 0 || x > 320 || y > 200)\r
9650                return;\r
9651      \r
9652           asm mov al , colour;\r
9653           asm mov bh , 0;\r
9654           asm mov cx , x;\r
9655           asm mov dx , y;\r
9656           asm mov ah, 0Ch;\r
9657           asm int 10h;\r
9658      }\r
9659      \r
9660      void Line(int a, int b, int c, int d, int col)\r
9661      {\r
9662           /* Draws a straight line from point a,b to point c,d in colour\r
9663      col */\r
9664      \r
9665           int u;\r
9666           int v;\r
9667           int d1x;\r
9668           int d1y;\r
9669           int d2x;\r
9670           int d2y;\r
9671           int m;\r
9672           int n;\r
9673           double s; /* The only real number variable, but it's essential\r
9674      */\r
9675           int i;\r
9676      \r
9677           u = c - a;\r
9678           v = d - b;\r
9679           if (u == 0)\r
9680           {\r
9681                d1x = 0;\r
9682                m = 0;\r
9683           }\r
9684           else\r
9685           {\r
9686                m = abs(u);\r
9687                if (u < 0)\r
9688                     d1x = -1;\r
9689                else\r
9690                if (u > 0)\r
9691                     d1x = 1;\r
9692           }\r
9693      \r
9694           if ( v == 0)\r
9695           {\r
9696                d1y = 0;\r
9697                n = 0;\r
9698           }\r
9699           else\r
9700           {\r
9701                n = abs(v);\r
9702                if (v < 0)\r
9703                     d1y = -1;\r
9704                else\r
9705                if (v > 0)\r
9706                     d1y = 1;\r
9707           }\r
9708           if (m > n)\r
9709           {\r
9710                d2x = d1x;\r
9711                d2y = 0;\r
9712           }\r
9713           else\r
9714           {\r
9715                d2x = 0;\r
9716                d2y = d1y;\r
9717                m = n;\r
9718                n = abs(u);\r
9719           }\r
9720           s = m / 2;\r
9721      \r
9722           for (i = 0; i <= m; i++)\r
9723           {\r
9724                plot(a,b,col);\r
9725                s += n;\r
9726                if (s >= m)\r
9727                {\r
9728                     s -= m;\r
9729                     a += d1x;\r
9730                     b += d1y;\r
9731                }\r
9732                else\r
9733                {\r
9734                     a += d2x;\r
9735                     b += d2y;\r
9736                }\r
9737           }\r
9738      }\r
9739      \r
9740      \r
9741      void Circle(int x, int y, int rad, int col)\r
9742      {\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
9746      \r
9747           int f;\r
9748           long xa;\r
9749           long ya;\r
9750           int a1;\r
9751           int b1;\r
9752           int a2;\r
9753           int b2;\r
9754           int a3;\r
9755           int b3;\r
9756           int a4;\r
9757           int b4;\r
9758      \r
9759      \r
9760           /* Calculate first point in each segment */\r
9761      \r
9762           a1 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
9763           b1 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
9764      \r
9765           a2 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
9766           b2 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
9767      \r
9768           a3 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
9769           b3 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
9770      \r
9771           a4 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
9772           b4 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
9773      \r
9774           /* Start loop at second point */\r
9775           for(f = 1; f <= 90; f++)\r
9776           {\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
9780      \r
9781                /* Draw a line from the previous point to the new point in\r
9782                   each segment */\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
9787      \r
9788                /* Update the previous point in each segment */\r
9789                a1 = x + xa;\r
9790                b1 = y + ya;\r
9791                a2 = x - xa;\r
9792                b2 = y + ya;\r
9793                a3 = x - xa;\r
9794                b3 = y - ya;\r
9795                a4 = x + xa;\r
9796                b4 = y - ya;\r
9797           }\r
9798      }\r
9799      \r
9800      main()\r
9801      {\r
9802           int n;\r
9803      \r
9804           /* Select VGA 256 colour 320 x 200 video mode */\r
9805           setvideo(19);\r
9806      \r
9807           /* Draw some circles */\r
9808           for(n = 0; n < 100; n++)\r
9809                Circle(160,100,n,n + 20);\r
9810      }\r
9811      \r
9812      \r
9813 \r
9814 Vesa Mode\r
9815 \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
9819 \r
9820 Mode               Display\r
9821                    \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
9835 \r
9836 These modes are in addition to the standard BIOS video modes described\r
9837 earlier.\r
9838 \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
9842 command line.\r
9843 \r
9844      #include <dos.h>\r
9845      #include <ctype.h>\r
9846      \r
9847      void setvideo(int mode)\r
9848      {\r
9849           /* Sets the video display to a VESA or normal mode and clears\r
9850      the screen */\r
9851      \r
9852           union REGS inreg,outreg;\r
9853      \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
9858      }\r
9859      \r
9860      main(int argc, char *argv[])\r
9861      {\r
9862           setvideo(atoi(argv[1]));\r
9863      }\r
9864 \r
9865 \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
9868 \r
9869      \r
9870      void plot(int x, int y, unsigned char colour)\r
9871      {\r
9872           asm mov al , colour;\r
9873           asm mov bh , 00;\r
9874           asm mov cx , x;\r
9875           asm mov dx , y;\r
9876           asm mov ah , 0Ch;\r
9877           asm int 10h;\r
9878      }\r
9879 \r
9880 \r
9881 Or, in a 800 x 600 resolution mode you can use this fast direct video\r
9882 access plot function;\r
9883 \r
9884      void plot( int x, int y, unsigned char pcolor)\r
9885      {\r
9886           /*\r
9887                Fast 800 x 600 mode (0x58 or 0x102) plotting\r
9888           */\r
9889      \r
9890           asm mov   ax,y;\r
9891           asm mov   dx,800/8;\r
9892           asm mul   dx;\r
9893           asm mov   bx,x;\r
9894           asm mov   cl,bl;\r
9895      \r
9896           asm shr   bx,1;\r
9897           asm shr   bx,1;\r
9898           asm shr   bx,1;\r
9899           asm add   bx,ax;\r
9900      \r
9901           asm and   cl,7;\r
9902           asm xor   cl,7;\r
9903           asm mov   ah,1;\r
9904           asm shl   ah,cl;\r
9905      \r
9906           asm mov   dx,03CEh;\r
9907           asm mov   al,8;\r
9908           asm out   dx,ax;\r
9909      \r
9910           asm mov   ax,(02h shl 8) + 5;\r
9911           asm out   dx,ax;\r
9912      \r
9913           asm mov   ax,0A000h;\r
9914           asm mov   es,ax;\r
9915      \r
9916           asm mov   al,es:[bx];\r
9917           asm mov   al,byte ptr pcolor;\r
9918           asm mov   es:[bx],al;\r
9919      \r
9920           asm mov   ax,(0FFh shl 8 ) + 8;\r
9921           asm out   dx,ax;\r
9922      \r
9923           asm mov   ax,(00h shl 8) + 5;\r
9924           asm out   dx,ax;\r
9925      }\r
9926 \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
9931                                     \r
9932                DIRECTORY SEARCHING WITH THE IBM PC AND DOS\r
9933 \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
9943 \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
9950 \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
9957 \r
9958 The following Turbo C example imitates the DOS directory command, in a\r
9959 basic form;\r
9960 \r
9961      #include <stdio.h>\r
9962      #include <dir.h>\r
9963      #include <dos.h>\r
9964      \r
9965      void main(void)\r
9966      {\r
9967           /* Display directory listing of current directory */\r
9968      \r
9969           int done;\r
9970           int day;\r
9971           int month;\r
9972           int year;\r
9973           int hour;\r
9974           int min;\r
9975           char amflag;\r
9976           struct ffblk ffblk;\r
9977           struct fcb fcb;\r
9978      \r
9979           /* First display sub directory entries */\r
9980           done = findfirst("*.",&ffblk,16);\r
9981      \r
9982           while (!done)\r
9983           {\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
9989      \r
9990                amflag = 'a';\r
9991      \r
9992                if (hour > 12)\r
9993                {\r
9994                     hour -= 12;\r
9995                     amflag = 'p';\r
9996                }\r
9997      \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
10001           }\r
10002      \r
10003           /* Now all files except directories */\r
10004           done = findfirst("*.*",&ffblk,231);\r
10005      \r
10006           while (!done)\r
10007           {\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
10013      \r
10014                amflag = 'a';\r
10015      \r
10016                if (hour > 12)\r
10017                {\r
10018                     hour -= 12;\r
10019                     amflag = 'p';\r
10020                }\r
10021      \r
10022                parsfnm(ffblk.ff_name,&fcb,1);\r
10023      \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
10028           }\r
10029      }\r
10030      \r
10031 \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
10037 \r
10038 \r
10039 The DOS DTA is comprised as follows;\r
10040 \r
10041 Offset                   Length                   Contents\r
10042                                                   \r
10043 00                       15                       Reserved\r
10044 15                       Byte                     Attribute of matched\r
10045                                                   file\r
10046 16                       Word                     File time\r
10047 18                       Word                     File date\r
10048 1A                       04                       File size\r
10049 1E                       0D                       File name and extension\r
10050                                                   as ascii string\r
10051 \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
10054 \r
10055 Bits      Contents\r
10056 \r
10057  0 -  4             Seconds divided by 2\r
10058  5 - 10        Minutes\r
10059 11 - 15   Hours\r
10060 \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
10063 \r
10064 Bits      Contents\r
10065 \r
10066  0 -  4             Day\r
10067  5 -  8             Month\r
10068  9 - 15        Years since 1980\r
10069 \r
10070 To extract these details from the DTA requires a little manipulation, as\r
10071 illustrated in the above example.\r
10072 \r
10073 The DTA attribute flag is comprised of the following bits being set or\r
10074 not;\r
10075 \r
10076 Bit       Attribute\r
10077 \r
10078 0         Read only\r
10079 1         Hidden\r
10080 2         System\r
10081 3         Volume label\r
10082 4         Directory\r
10083 5         Archive\r
10084                                     \r
10085                         ACCESSING EXPANDED MEMORY\r
10086 \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
10094 \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
10100 \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
10104 active.\r
10105 \r
10106 The following source code illustrates some basic functions for testing\r
10107 for and accessing expanded memory.\r
10108 \r
10109      /*\r
10110      Various functions for using Expanded memory\r
10111      */\r
10112      \r
10113      #include <dos.h>\r
10114      #define   EMM  0x67\r
10115      \r
10116      char far *emmbase;\r
10117      emmtest()\r
10118      {\r
10119           /*\r
10120           Tests for the presence of expnaded memory by attempting to\r
10121           open the file EMMXXXX0.\r
10122           */\r
10123      \r
10124           union REGS regs;\r
10125           struct SREGS sregs;\r
10126           int error;\r
10127           long handle;\r
10128      \r
10129           /* Attempt to open the file device EMMXXXX0 */\r
10130           regs.x.ax = 0x3d00;\r
10131           regs.x.dx = (int)"EMMXXXX0";\r
10132           sregs.ds = _DS;\r
10133           intdosx(&regs,&regs,&sregs);\r
10134           handle = regs.x.ax;\r
10135           error = regs.x.cflag;\r
10136      \r
10137           if (!error)\r
10138           {\r
10139                regs.h.ah = 0x3e;\r
10140                regs.x.bx = handle;\r
10141                intdos(&regs,&regs);\r
10142           }\r
10143           return error;\r
10144      }\r
10145      \r
10146      emmok()\r
10147      {\r
10148           /*\r
10149           Checks whether the expanded memory manager responds correctly\r
10150           */\r
10151      \r
10152           union REGS regs;\r
10153      \r
10154           regs.h.ah = 0x40;\r
10155           int86(EMM,&regs,&regs);\r
10156      \r
10157           if (regs.h.ah)\r
10158                return 0;\r
10159      \r
10160           regs.h.ah = 0x41;\r
10161           int86(EMM,&regs,&regs);\r
10162      \r
10163           if (regs.h.ah)\r
10164                return 0;\r
10165      \r
10166           emmbase = MK_FP(regs.x.bx,0);\r
10167           return 1;\r
10168      }\r
10169      \r
10170      long emmavail()\r
10171      {\r
10172         /*\r
10173         Returns the number of available (free) 16K pages of expanded\r
10174      memory\r
10175         or -1 if an error occurs.\r
10176         */\r
10177      \r
10178              union REGS regs;\r
10179      \r
10180           regs.h.ah = 0x42;\r
10181           int86(EMM,&regs,&regs);\r
10182           if (!regs.h.ah)\r
10183                return regs.x.bx;\r
10184           return -1;\r
10185      }\r
10186      \r
10187      long emmalloc(int n)\r
10188      {\r
10189           /*\r
10190           Requests 'n' pages of expanded memory and returns the file\r
10191      handle\r
10192           assigned to the pages or -1 if there is an error\r
10193           */\r
10194      \r
10195           union REGS regs;\r
10196      \r
10197           regs.h.ah = 0x43;\r
10198           regs.x.bx = n;\r
10199           int86(EMM,&regs,&regs);\r
10200           if (regs.h.ah)\r
10201                return -1;\r
10202           return regs.x.dx;\r
10203      }\r
10204      \r
10205      emmmap(long handle, int phys, int page)\r
10206      {\r
10207           /*\r
10208           Maps a physical page from expanded memory into the page frame\r
10209      in the\r
10210           conventional memory 16K window so that data can be transfered\r
10211      between\r
10212           the expanded memory and conventional memory.\r
10213           */\r
10214      \r
10215           union REGS regs;\r
10216      \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,&regs,&regs);\r
10222           return (regs.h.ah == 0);\r
10223      }\r
10224      \r
10225      void emmmove(int page, char *str, int n)\r
10226      {\r
10227           /*\r
10228           Move 'n' bytes from conventional memory to the specified\r
10229      expanded memory\r
10230           page\r
10231           */\r
10232      \r
10233           char far *ptr;\r
10234      \r
10235           ptr = emmbase + page * 16384;\r
10236           while(n-- > 0)\r
10237                *ptr++ = *str++;\r
10238      }\r
10239      \r
10240      void emmget(int page, char *str, int n)\r
10241      {\r
10242           /*\r
10243           Move 'n' bytes from the specified expanded memory page into\r
10244      conventional\r
10245           memory\r
10246           */\r
10247      \r
10248           char far *ptr;\r
10249      \r
10250           ptr = emmbase + page * 16384;\r
10251           while(n-- > 0)\r
10252                *str++ = *ptr++;\r
10253      }\r
10254      \r
10255      emmclose(long handle)\r
10256      {\r
10257           /*\r
10258           Release control of the expanded memory pages allocated to\r
10259      'handle'\r
10260           */\r
10261      \r
10262           union REGS regs;\r
10263      \r
10264           regs.h.ah = 0x45;\r
10265           regs.x.dx = handle;\r
10266           int86(EMM,&regs,&regs);\r
10267           return (regs.h.ah == 0);\r
10268      }\r
10269      \r
10270      /*\r
10271      Test function for the EMM routines\r
10272      */\r
10273      \r
10274      void main()\r
10275      {\r
10276           long emmhandle;\r
10277           long avail;\r
10278           char teststr[80];\r
10279           int i;\r
10280      \r
10281           if(!emmtest())\r
10282           {\r
10283                printf("Expanded memory is not present\n");\r
10284                exit(0);\r
10285           }\r
10286      \r
10287           if(!emmok())\r
10288           {\r
10289                printf("Expanded memory manager is not present\n");\r
10290                exit(0);\r
10291           }\r
10292      \r
10293           avail = emmavail();\r
10294           if (avail == -1)\r
10295           {\r
10296                printf("Expanded memory manager error\n");\r
10297                exit(0);\r
10298           }\r
10299           printf("There are %ld pages available\n",avail);\r
10300      \r
10301           /* Request 10 pages of expanded memory */\r
10302           if((emmhandle = emmalloc(10)) < 0)\r
10303           {\r
10304                printf("Insufficient pages available\n");\r
10305                exit(0);\r
10306           }\r
10307      \r
10308           for (i = 0; i < 10; i++)\r
10309           {\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
10313           }\r
10314      \r
10315           for (i = 0; i < 10; i++)\r
10316           {\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
10320           }\r
10321      \r
10322           emmclose(emmhandle);\r
10323      }\r
10324                                     \r
10325                         ACCESSING EXTENDED MEMORY\r
10326 \r
10327 \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
10335 \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
10341 buffer.\r
10342 \r
10343 \r
10344      \r
10345      /*\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
10349      */\r
10350      \r
10351      #ifdef __SMALL__\r
10352      #define BLOCKSIZE (16L * 1024L)\r
10353      #endif\r
10354      \r
10355      \r
10356      #ifdef __MEDIUM__\r
10357      #define BLOCKSIZE (16L * 1024L)\r
10358      #endif\r
10359      \r
10360      \r
10361      #ifdef __COMPACT__\r
10362      #define BLOCKSIZE (64L * 1024L)\r
10363      #endif\r
10364      \r
10365      #ifdef __LARGE__\r
10366      #define BLOCKSIZE (64L * 1024L)\r
10367      #endif\r
10368      \r
10369      \r
10370      char XMS_init()\r
10371      {\r
10372           /*\r
10373                returns 0 if XMS present,\r
10374                     1 if XMS absent\r
10375                     2 if unable to allocate conventional memory transfer\r
10376      buffer\r
10377           */\r
10378           unsigned char status;\r
10379           _AX=0x4300;\r
10380           geninterrupt(0x2F);\r
10381           status = _AL;\r
10382           if(status==0x80)\r
10383           {\r
10384                GetXMSEntry();\r
10385                XMSBuf = (char far *) farmalloc(BLOCKSIZE);\r
10386                if (XMSBuf == NULL)\r
10387                     return 2;\r
10388                return 0;\r
10389           }\r
10390           return 1;\r
10391      }\r
10392      \r
10393      void GetXMSEntry(void)\r
10394      {\r
10395           /*\r
10396                GetXMSEntry sets XMSFunc to the XMS Manager entry point\r
10397                so we can call it later\r
10398           */\r
10399      \r
10400           _AX=0x4310;\r
10401           geninterrupt(0x2F);\r
10402           XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);\r
10403      }\r
10404 \r
10405 \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
10408 \r
10409      void XMSSize(int *kbAvail, int *largestAvail)\r
10410      {\r
10411           /*\r
10412                XMSSize returns the total kilobytes available, and the\r
10413      size\r
10414                in kilobytes of the largest available block\r
10415           */\r
10416      \r
10417           _AH=8;\r
10418           (*XMSFunc)();\r
10419           *largestAvail=_DX;\r
10420           *kbAvail=_AX;\r
10421      }\r
10422 \r
10423 \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
10426 \r
10427      char AllocXMS(unsigned long numberBytes)\r
10428      {\r
10429           /*\r
10430                Allocate a block of XMS memory numberBytes long\r
10431                Returns 1 on success\r
10432                     0 on failure\r
10433           */\r
10434      \r
10435           _DX = (int)(numberBytes / 1024);\r
10436           _AH = 9;\r
10437           (*XMSFunc)();\r
10438           if (_AX==0)\r
10439           {\r
10440                return 0;\r
10441           }\r
10442           XMSHandle=_DX;\r
10443           return 1;\r
10444      }\r
10445      \r
10446 \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
10451 \r
10452      void XMS_free(void)\r
10453      {\r
10454           /*\r
10455                Free used XMS\r
10456           */\r
10457           _DX=XMSHandle;\r
10458           _AH=0x0A;\r
10459           (*XMSFunc)();\r
10460      }\r
10461 \r
10462 \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
10465 \r
10466      /*\r
10467           XMSParms is a structure for copying information to and from\r
10468           real-mode memory to XMS memory\r
10469      */\r
10470      \r
10471      struct parmstruct\r
10472      {\r
10473           /*\r
10474                blocklength is the size in bytes of block to copy\r
10475           */\r
10476           unsigned long blockLength;\r
10477      \r
10478           /*\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
10483           */\r
10484      \r
10485           unsigned int sourceHandle;\r
10486           far void *sourcePtr;\r
10487      \r
10488           /*\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
10493           */\r
10494      \r
10495           unsigned int destHandle;\r
10496           far void *destPtr;\r
10497      }\r
10498      XMSParms;\r
10499      \r
10500      \r
10501      char XMS_write(unsigned long loc, char far *val, unsigned length)\r
10502      {\r
10503           /*\r
10504                Round length up to next even value\r
10505           */\r
10506           length += length % 2;\r
10507      \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
10514           _AH=0x0B;\r
10515           (*XMSFunc)();\r
10516           if (_AX==0)\r
10517           {\r
10518                return 0;\r
10519           }\r
10520           return 1;\r
10521      }\r
10522      \r
10523      \r
10524      void *XMS_read(unsigned long loc,unsigned length)\r
10525      {\r
10526           /*\r
10527                Returns pointer to data\r
10528                or NULL on error\r
10529           */\r
10530      \r
10531           /*\r
10532                Round length up to next even value\r
10533           */\r
10534           length += length % 2;\r
10535      \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
10542           _AH=0x0B;\r
10543           (*XMSFunc)();\r
10544           if (_AX==0)\r
10545           {\r
10546                return NULL;\r
10547           }\r
10548           return XMSBuf;\r
10549      }\r
10550      \r
10551      \r
10552 And now putting it all together is a demonstration program.\r
10553 \r
10554      /* A sequential table of variable length records in XMS */\r
10555      \r
10556      #include <dos.h>\r
10557      #include <stdio.h>\r
10558      #include <stdlib.h>\r
10559      #include <alloc.h>\r
10560      #include <string.h>\r
10561      \r
10562      #define TRUE 1\r
10563      #define FALSE 0\r
10564      \r
10565      /*\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
10569      */\r
10570      \r
10571      #ifdef __SMALL__\r
10572      #define BLOCKSIZE (16L * 1024L)\r
10573      #endif\r
10574      \r
10575      \r
10576      #ifdef __MEDIUM__\r
10577      #define BLOCKSIZE (16L * 1024L)\r
10578      #endif\r
10579      \r
10580      \r
10581      #ifdef __COMPACT__\r
10582      #define BLOCKSIZE (64L * 1024L)\r
10583      #endif\r
10584      \r
10585      #ifdef __LARGE__\r
10586      #define BLOCKSIZE (64L * 1024L)\r
10587      #endif\r
10588      \r
10589      \r
10590      /*\r
10591           XMSParms is a structure for copying information to and from\r
10592           real-mode memory to XMS memory\r
10593      */\r
10594      \r
10595      struct parmstruct\r
10596      {\r
10597           /*\r
10598                blocklength is the size in bytes of block to copy\r
10599           */\r
10600           unsigned long blockLength;\r
10601      \r
10602           /*\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
10607           */\r
10608      \r
10609           unsigned int sourceHandle;\r
10610           far void *sourcePtr;\r
10611      \r
10612           /*\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
10617           */\r
10618      \r
10619           unsigned int destHandle;\r
10620           far void *destPtr;\r
10621      }\r
10622      XMSParms;\r
10623      \r
10624      void far (*XMSFunc) (void);   /* Used to call XMS manager\r
10625      (himem.sys) */\r
10626      char GetBuf(void);\r
10627      void GetXMSEntry(void);\r
10628      \r
10629      char *XMSBuf;  /* Conventional memory buffer for transfers */\r
10630      \r
10631      unsigned int XMSHandle;  /* handle to allocated XMS block */\r
10632      \r
10633      \r
10634      char XMS_init()\r
10635      {\r
10636           /*\r
10637                returns 0 if XMS present,\r
10638                     1 if XMS absent\r
10639                     2 if unable to allocate transfer buffer\r
10640           */\r
10641           unsigned char status;\r
10642           _AX=0x4300;\r
10643           geninterrupt(0x2F);\r
10644           status = _AL;\r
10645           if(status==0x80)\r
10646           {\r
10647                GetXMSEntry();\r
10648                XMSBuf = (char far *) farmalloc(BLOCKSIZE);\r
10649                if (XMSBuf == NULL)\r
10650                     return 2;\r
10651                return 0;\r
10652           }\r
10653           return 1;\r
10654      }\r
10655      \r
10656      void GetXMSEntry(void)\r
10657      {\r
10658           /*\r
10659                GetXMSEntry sets XMSFunc to the XMS Manager entry point\r
10660                so we can call it later\r
10661           */\r
10662      \r
10663           _AX=0x4310;\r
10664           geninterrupt(0x2F);\r
10665           XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);\r
10666      }\r
10667      \r
10668      \r
10669      void XMSSize(int *kbAvail, int *largestAvail)\r
10670      {\r
10671           /*\r
10672                XMSSize returns the total kilobytes available, and the\r
10673      size\r
10674                in kilobytes of the largest available block\r
10675           */\r
10676      \r
10677           _AH=8;\r
10678           (*XMSFunc)();\r
10679           *largestAvail=_DX;\r
10680           *kbAvail=_AX;\r
10681      }\r
10682      \r
10683      char AllocXMS(unsigned long numberBytes)\r
10684      {\r
10685           /*\r
10686                Allocate a block of XMS memory numberBytes long\r
10687           */\r
10688      \r
10689           _DX = (int)(numberBytes / 1024);\r
10690           _AH = 9;\r
10691           (*XMSFunc)();\r
10692           if (_AX==0)\r
10693           {\r
10694                return FALSE;\r
10695           }\r
10696           XMSHandle=_DX;\r
10697           return TRUE;\r
10698      }\r
10699      \r
10700      void XMS_free(void)\r
10701      {\r
10702           /*\r
10703                Free used XMS\r
10704           */\r
10705           _DX=XMSHandle;\r
10706           _AH=0x0A;\r
10707           (*XMSFunc)();\r
10708      }\r
10709      \r
10710      char XMS_write(unsigned long loc, char far *val, unsigned length)\r
10711      {\r
10712           /*\r
10713                Round length up to next even value\r
10714           */\r
10715           length += length % 2;\r
10716      \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
10723           _AH=0x0B;\r
10724           (*XMSFunc)();\r
10725           if (_AX==0)\r
10726           {\r
10727                return FALSE;\r
10728           }\r
10729           return TRUE;\r
10730      }\r
10731      \r
10732      \r
10733      void *XMS_read(unsigned long loc,unsigned length)\r
10734      {\r
10735           /*\r
10736                Returns pointer to data\r
10737                or NULL on error\r
10738           */\r
10739      \r
10740           /*\r
10741                Round length up to next even value\r
10742           */\r
10743           length += length % 2;\r
10744      \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
10751           _AH=0x0B;\r
10752           (*XMSFunc)();\r
10753           if (_AX==0)\r
10754           {\r
10755                return NULL;\r
10756           }\r
10757           return XMSBuf;\r
10758      }\r
10759      \r
10760      \r
10761      /*\r
10762           Demonstration code\r
10763           Read various length strings into a single XMS block (EMB)\r
10764           and write them out again\r
10765      */\r
10766      \r
10767      int main()\r
10768      {\r
10769           int kbAvail,largestAvail;\r
10770           char buffer[80];\r
10771           char *p;\r
10772           long pos;\r
10773           long end;\r
10774      \r
10775           if (XMS_init() == 0)\r
10776                printf("XMS Available ...\n");\r
10777           else\r
10778           {\r
10779                printf("XMS Not Available\n");\r
10780                return(1);\r
10781           }\r
10782      \r
10783           XMSSize(&kbAvail,&largestAvail);\r
10784           printf("Kilobytes Available: %d; Largest block:\r
10785      %dK\n",kbAvail,largestAvail);\r
10786      \r
10787           if (!AllocXMS(2000 * 1024L))\r
10788                return(1);\r
10789      \r
10790      \r
10791           pos = 0;\r
10792      \r
10793           do\r
10794           {\r
10795                p = fgets(buffer,1000,stdin);\r
10796                if (p != NULL)\r
10797                {\r
10798                     XMS_write(pos,buffer,strlen(buffer) + 1);\r
10799                     pos += strlen(buffer) + 1;\r
10800                }\r
10801           }\r
10802           while(p != NULL);\r
10803      \r
10804           end = pos;\r
10805      \r
10806           pos = 0;\r
10807      \r
10808           do\r
10809           {\r
10810                memcpy(buffer,XMS_read(pos,100),70);\r
10811                printf("%s",buffer);\r
10812                pos += strlen(buffer) + 1;\r
10813           }\r
10814           while(pos < end);\r
10815      \r
10816           /*\r
10817                It is VERY important to free any XMS before exiting!\r
10818           */\r
10819           XMS_free();\r
10820           return 0;\r
10821      }\r
10822 \r
10823                                     \r
10824                                     \r
10825                                     \r
10826                              TSR PROGRAMMING\r
10827 \r
10828 \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
10833 sucessfuly.\r
10834 \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
10838 themselves.\r
10839 \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
10846 \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
10849 \r
10850 There are a few basic rules which help to clarify the problems\r
10851 encountered in programming TSRs:\r
10852 \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
10864      process.\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
10869 \r
10870 \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
10876 again.\r
10877 \r
10878 \r
10879      /*\r
10880         A practical TSR program (a pop-up address book database)\r
10881         Compile in small memory model with stack checking OFF\r
10882      */\r
10883      \r
10884      #include <dos.h>\r
10885      #include <stdio.h>\r
10886      #include <string.h>\r
10887      #include <dir.h>\r
10888      \r
10889      static union REGS rg;\r
10890      \r
10891      /*\r
10892         Size of the program to remain resident\r
10893         experimentation is required to make this as small as possible\r
10894      */\r
10895      unsigned sizeprogram = 28000/16;\r
10896      \r
10897      /* Activate with Alt . */\r
10898      unsigned scancode = 52;  /* . */\r
10899      unsigned keymask = 8;         /* ALT */\r
10900      \r
10901      char signature[]= "POPADDR";\r
10902      char fpath[40];\r
10903      \r
10904      /*\r
10905           Function prototypes\r
10906      */\r
10907      \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
10913      void wait(void);\r
10914      void resident_psp(void);\r
10915      void exec(void);\r
10916      \r
10917      /*\r
10918         Entry point from DOS\r
10919      */\r
10920      \r
10921      main(int argc, char *argv[])\r
10922      {\r
10923           void interrupt ifunc();\r
10924           int ivec;\r
10925      \r
10926           /*\r
10927              For simplicity, assume the data file is in the root\r
10928      directory\r
10929              of drive C:\r
10930           */\r
10931           strcpy(fpath,"C:\\ADDRESS.DAT");\r
10932      \r
10933           if ((ivec = resident(signature,ifunc)) != 0)\r
10934           {\r
10935                /* TSR is resident */\r
10936                if (argc > 1)\r
10937                {\r
10938                     rg.x.ax = 0;\r
10939                     if (strcmp(argv[1],"quit") == 0)\r
10940                          rg.x.ax = 1;\r
10941                     else if (strcmp(argv[1],"restart") == 0)\r
10942                          rg.x.ax = 2;\r
10943                     else if (strcmp(argv[1],"wait") == 0)\r
10944                          rg.x.ax = 3;\r
10945                     if (rg.x.ax)\r
10946                     {\r
10947                          int86(ivec,&rg,&rg);\r
10948                          return;\r
10949                     }\r
10950                }\r
10951                printf("\nPopup Address Book is already resident");\r
10952           }\r
10953           else\r
10954           {\r
10955                /* Initial load of TSR program */\r
10956                printf("Popup Address Book Resident.\nPress Alt . To\r
10957      Activate....\n");\r
10958                resinit();\r
10959           }\r
10960      }\r
10961      \r
10962      void interrupt ifunc(bp,di,si,ds,es,dx,cx,bx,ax)\r
10963      {\r
10964           if(ax == 1)\r
10965                terminate();\r
10966           else if(ax == 2)\r
10967                restart();\r
10968           else if(ax == 3)\r
10969                wait();\r
10970      }\r
10971      \r
10972      popup()\r
10973      {\r
10974           int x,y;\r
10975      \r
10976           curr_cursor(&x,&y);\r
10977      \r
10978           /* Call the TSR C program here */\r
10979           exec();\r
10980           cursor(x,y);\r
10981      }\r
10982      \r
10983      /*\r
10984           Second source module\r
10985      */\r
10986      \r
10987      #include <dos.h>\r
10988      #include <stdio.h>\r
10989      \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
11007      static int pp;\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
11013      \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
11019      \r
11020      extern unsigned sizeprogram;\r
11021      extern unsigned scancode;\r
11022      extern unsigned keymask;\r
11023      \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
11030      \r
11031      void dores(void);\r
11032      void pidaddr(void);\r
11033      \r
11034      void resinit()\r
11035      {\r
11036           segread(&seg);\r
11037           myss = seg.ss;\r
11038      \r
11039           rg.h.ah = 0x34;\r
11040           intdos(&rg,&rg);\r
11041           dosseg = _ES;\r
11042           dosbusy = rg.x.bx;\r
11043      \r
11044           mydta = getdta();\r
11045           pidaddr();\r
11046           oldtimer = getvect(0x1c);\r
11047           old28 = getvect(0x28);\r
11048           oldkb = getvect(9);\r
11049           olddisk = getvect(0x13);\r
11050      \r
11051           setvect(0x1c,newtimer);\r
11052           setvect(9,newkb);\r
11053           setvect(0x28,new28);\r
11054           setvect(0x13,newdisk);\r
11055      \r
11056           stack = (sizeprogram - (seg.ds - seg.cs)) * 16 - 300;\r
11057           rg.x.ax = 0x3100;\r
11058           rg.x.dx = sizeprogram;\r
11059           intdos(&rg,&rg);\r
11060      }\r
11061      \r
11062      void interrupt newdisk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)\r
11063      {\r
11064           diskflag++;\r
11065           (*olddisk)();\r
11066           ax = _AX;\r
11067           newcrit();\r
11068           flgs = cflag;\r
11069           --diskflag;\r
11070      }\r
11071      \r
11072      void interrupt newcrit(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)\r
11073      {\r
11074           ax = 0;\r
11075           cflag = flgs;\r
11076      }\r
11077      \r
11078      void interrupt newkb()\r
11079      {\r
11080           if (inportb(0x60) == scancode)\r
11081           {\r
11082                kbval = peekb(0,0x417);\r
11083                if (!resoff && ((kbval & keymask) ^ keymask) == 0)\r
11084                {\r
11085                     kbval = inportb(0x61);\r
11086                     outportb(0x61,kbval | 0x80);\r
11087                     outportb(0x61,kbval);\r
11088                     disable();\r
11089                     outportb(0x20,0x20);\r
11090                     enable();\r
11091                     if (!running)\r
11092                          popflg = 1;\r
11093                     return;\r
11094                }\r
11095           }\r
11096           (*oldkb)();\r
11097      }\r
11098      \r
11099      void interrupt newtimer()\r
11100      {\r
11101           (*oldtimer)();\r
11102           if (popflg && peekb(dosseg,dosbusy) == 0)\r
11103                if(diskflag == 0)\r
11104                {\r
11105                     outportb(0x20,0x20);\r
11106                     popflg = 0;\r
11107                     dores();\r
11108                }\r
11109      }\r
11110      \r
11111      void interrupt new28()\r
11112      {\r
11113           (*old28)();\r
11114           if (popflg && peekb(dosseg,dosbusy) != 0)\r
11115           {\r
11116                popflg = 0;\r
11117                dores();\r
11118           }\r
11119      }\r
11120      \r
11121      resident_psp()\r
11122      {\r
11123           intpsp = peek(dosseg,*pids);\r
11124           for(pp = 0; pp < pidctr; pp++)\r
11125                poke(dosseg,pids[pp],mypsp);\r
11126      }\r
11127      \r
11128      interrupted_psp()\r
11129      {\r
11130           for(pp = 0; pp < pidctr; pp++)\r
11131                poke(dosseg,pids[pp],intpsp);\r
11132      }\r
11133      \r
11134      void dores()\r
11135      {\r
11136           running = 1;\r
11137           disable();\r
11138           intsp = _SP;\r
11139           intss = _SS;\r
11140           _SP = stack;\r
11141           _SS = myss;\r
11142           enable();\r
11143           oldcrit = getvect(0x24);\r
11144           setvect(0x24,newcrit);\r
11145           rg.x.ax = 0x3300;\r
11146           intdos(&rg,&rg);\r
11147           ctrl_break = rg.h.dl;\r
11148           rg.x.ax = 0x3301;\r
11149           rg.h.dl = 0;\r
11150           intdos(&rg,&rg);\r
11151           intdta = getdta();\r
11152           setdta(mydta);\r
11153           resident_psp();\r
11154           popup();\r
11155           interrupted_psp();\r
11156           setdta(intdta);\r
11157           setvect(0x24,oldcrit);\r
11158           rg.x.ax = 0x3301;\r
11159           rg.h.dl = ctrl_break;\r
11160           intdos(&rg,&rg);\r
11161           disable();\r
11162           _SP = intsp;\r
11163           _SS = intss;\r
11164           enable();\r
11165           running = 0;\r
11166      }\r
11167      \r
11168      static int avec = 0;\r
11169      \r
11170      unsigned resident(char *signature,void interrupt(*ifunc)())\r
11171      {\r
11172           char *sg;\r
11173           unsigned df;\r
11174           int vec;\r
11175      \r
11176           segread(&seg);\r
11177           df = seg.ds-seg.cs;\r
11178           for(vec = 0x60; vec < 0x68; vec++)\r
11179           {\r
11180                if (getvect(vec) == NULL)\r
11181                {\r
11182                     if (!avec)\r
11183                          avec = vec;\r
11184                     continue;\r
11185                }\r
11186                for(sg = signature; *sg; sg++)\r
11187                if (*sg != peekb(peek(0,2+vec*4)+df,(unsigned)sg))\r
11188                     break;\r
11189                if (!*sg)\r
11190                     return vec;\r
11191           }\r
11192           if (avec)\r
11193                setvect(avec,ifunc);\r
11194           return 0;\r
11195      }\r
11196      \r
11197      static void pidaddr()\r
11198      {\r
11199           unsigned adr = 0;\r
11200      \r
11201           rg.h.ah = 0x51;\r
11202           intdos(&rg,&rg);\r
11203           mypsp = rg.x.bx;\r
11204           rg.h.ah = 0x52;\r
11205           intdos(&rg,&rg);\r
11206           enddos = _ES;\r
11207           enddos = peek(enddos,rg.x.bx-2);\r
11208           while(pidctr < 2 && (unsigned)((dosseg<<4) + adr) < (enddos\r
11209      <<4))\r
11210           {\r
11211                if (peek(dosseg,adr) == mypsp)\r
11212                {\r
11213                     rg.h.ah = 0x50;\r
11214                     rg.x.bx = mypsp + 1;\r
11215                     intdos(&rg,&rg);\r
11216                     if (peek(dosseg,adr) == mypsp + 1)\r
11217                          pids[pidctr++] = adr;\r
11218                     rg.h.ah = 0x50;\r
11219                     rg.x.bx = mypsp;\r
11220                     intdos(&rg,&rg);\r
11221                }\r
11222                adr++;\r
11223           }\r
11224      }\r
11225      \r
11226      static resterm()\r
11227      {\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
11233           rg.h.ah = 0x52;\r
11234           intdos(&rg,&rg);\r
11235           mcbseg = _ES;\r
11236           mcbseg = peek(mcbseg,rg.x.bx-2);\r
11237           segread(&seg);\r
11238           while(peekb(mcbseg,0) == 0x4d)\r
11239           {\r
11240                if(peek(mcbseg,1) == mypsp)\r
11241                {\r
11242                     rg.h.ah = 0x49;\r
11243                     seg.es = mcbseg+1;\r
11244                     intdosx(&rg,&rg,&seg);\r
11245                }\r
11246                mcbseg += peek(mcbseg,3) + 1;\r
11247           }\r
11248      }\r
11249      \r
11250      terminate()\r
11251      {\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
11256                          {\r
11257                               resterm();\r
11258                               return;\r
11259                          }\r
11260           resoff = 1;\r
11261      }\r
11262      \r
11263      restart()\r
11264      {\r
11265           resoff = 0;\r
11266      }\r
11267      \r
11268      wait()\r
11269      {\r
11270           resoff = 1;\r
11271      }\r
11272      \r
11273      void cursor(int y, int x)\r
11274      {\r
11275           rg.x.ax = 0x0200;\r
11276           rg.x.bx = 0;\r
11277           rg.x.dx = ((y << 8) & 0xff00) + x;\r
11278           int86(16,&rg,&rg);\r
11279      }\r
11280      \r
11281      void curr_cursor(int *y, int *x)\r
11282      {\r
11283           rg.x.ax = 0x0300;\r
11284           rg.x.bx = 0;\r
11285           int86(16,&rg,&rg);\r
11286           *x = rg.h.dl;\r
11287           *y = rg.h.dh;\r
11288      }\r
11289      \r
11290      /*\r
11291         Third module, the simple pop-up address book\r
11292         with mouse support\r
11293      */\r
11294      \r
11295      #include <stdio.h>\r
11296      #include <stdlib.h>\r
11297      #include <io.h>\r
11298      #include <string.h>\r
11299      #include <fcntl.h>\r
11300      #include <sys\stat.h>\r
11301      #include <dos.h>\r
11302      #include <conio.h>\r
11303      #include <graphics.h>\r
11304      #include <bios.h>\r
11305      \r
11306      /* left cannot be less than 3 */\r
11307      #define left   4\r
11308      \r
11309      /* Data structure for records */\r
11310      typedef struct\r
11311      {\r
11312           char name[31];\r
11313           char company[31];\r
11314           char address[31];\r
11315           char area[31];\r
11316           char town[31];\r
11317           char county[31];\r
11318           char post[13];\r
11319           char telephone[16];\r
11320           char fax[16];\r
11321      }\r
11322      data;\r
11323      \r
11324      extern char fpath[];\r
11325      \r
11326      static char scr[4000];\r
11327      \r
11328      static char sbuff[2000];\r
11329      char stext[30];\r
11330      data rec;\r
11331      int handle;\r
11332      int recsize;\r
11333      union REGS inreg,outreg;\r
11334      \r
11335      /*\r
11336           Function prototypes\r
11337      */\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
11348      void MENU(void);\r
11349      \r
11350      int GET_MOUSE(int *buttons)\r
11351      {\r
11352           inreg.x.ax = 0;\r
11353           int86(0x33,&inreg,&outreg);\r
11354           *buttons = outreg.x.bx;\r
11355           return outreg.x.ax;\r
11356      }\r
11357      \r
11358      void MOUSE_CURSOR(int status)\r
11359      {\r
11360           /* Status = 0 cursor off */\r
11361           /*          1 cursor on */\r
11362      \r
11363           inreg.x.ax = 2 - status;\r
11364           int86(0x33,&inreg,&outreg);\r
11365      }\r
11366      \r
11367      int MOUSE_LOCATION(int *x, int *y)\r
11368      {\r
11369           inreg.x.ax = 3;\r
11370           int86(0x33,&inreg,&outreg);\r
11371      \r
11372           *x = outreg.x.cx / 8;\r
11373           *y = outreg.x.dx / 8;\r
11374      \r
11375           return outreg.x.bx;\r
11376      }\r
11377      \r
11378      int GETOPT()\r
11379      {\r
11380           int result;\r
11381           int x;\r
11382           int y;\r
11383      \r
11384           do\r
11385           {\r
11386                do\r
11387                {\r
11388                     result = MOUSE_LOCATION(&x,&y);\r
11389                     if (result & 1)\r
11390                     {\r
11391                          if (x >= 52 && x <= 53 && y >= 7 && y <= 15)\r
11392                               return y - 7;\r
11393                          if (x >= 4 && x <= 40 && y >= 7 && y <= 14)\r
11394                               return y + 10;\r
11395      \r
11396                          if (x >= 4 && x <= 40 && y == 15)\r
11397                               return y + 10;\r
11398                     }\r
11399                }\r
11400                while(!bioskey(1));\r
11401      \r
11402                result = bioskey(0);\r
11403                x = result & 0xff;\r
11404                if (x == 0)\r
11405                {\r
11406                     result = result >> 8;\r
11407                     result -= 60;\r
11408                }\r
11409           }\r
11410           while(result < 0 || result > 8);\r
11411           return result;\r
11412      }\r
11413      \r
11414      void setvideo(unsigned char mode)\r
11415      {\r
11416           /* Sets the video display mode     and clears the screen */\r
11417      \r
11418           inreg.h.al = mode;\r
11419           inreg.h.ah = 0x00;\r
11420           int86(0x10, &inreg, &outreg);\r
11421      }\r
11422      \r
11423      \r
11424      int activepage(void)\r
11425      {\r
11426           /* Returns the currently selected video display page */\r
11427      \r
11428           union REGS inreg,outreg;\r
11429      \r
11430           inreg.h.ah = 0x0F;\r
11431           int86(0x10, &inreg, &outreg);\r
11432           return(outreg.h.bh);\r
11433      }\r
11434      \r
11435      void print(char *str)\r
11436      {\r
11437           /*\r
11438              Prints characters only directly to the current display page\r
11439              starting at the current cursor position. The cursor is not\r
11440              advanced.\r
11441              This function assumes a colour display card. For use with a\r
11442              monochrome display card change 0xB800 to read 0xB000\r
11443           */\r
11444      \r
11445           int page;\r
11446           int offset;\r
11447           unsigned row;\r
11448           unsigned col;\r
11449           char far *ptr;\r
11450      \r
11451           page = activepage();\r
11452           curr_cursor(&row,&col);\r
11453      \r
11454           offset = page * 4000 + row * 160 + col * 2;\r
11455      \r
11456           ptr = MK_FP(0xB800,offset);\r
11457      \r
11458           while(*str)\r
11459           {\r
11460                *ptr++= *str++;\r
11461                ptr++;\r
11462           }\r
11463      }\r
11464      \r
11465      \r
11466      void TRUESHADE(int lef, int top, int right, int bottom)\r
11467      {\r
11468           int n;\r
11469      \r
11470           /* True Shading of a screen block */\r
11471      \r
11472           gettext(lef,top,right,bottom,sbuff);\r
11473           for(n = 1; n < 2000; n+= 2)\r
11474                sbuff[n] = 7;\r
11475           puttext(lef,top,right,bottom,sbuff);\r
11476      }\r
11477      \r
11478      void DBOX(int l, int t, int r, int b)\r
11479      {\r
11480           /* Draws a double line box around the described area */\r
11481      \r
11482           int n;\r
11483      \r
11484           cursor(t,l);\r
11485           print("\90");\r
11486           for(n = 1; n < r - l; n++)\r
11487           {\r
11488                cursor(t,l + n);\r
11489                print("I");\r
11490           }\r
11491           cursor(t,r);\r
11492           print("¯");\r
11493      \r
11494           for (n = t + 1; n < b; n++)\r
11495           {\r
11496                cursor(n,l);\r
11497                print("§");\r
11498                cursor(n,r);\r
11499                print("§");\r
11500           }\r
11501           cursor(b,l);\r
11502           print("E");\r
11503           for(n = 1; n < r - l; n++)\r
11504           {\r
11505                cursor(b,l+n);\r
11506                print("I");\r
11507           }\r
11508           cursor(b,r);\r
11509           print("¬");\r
11510      }\r
11511      \r
11512      int INPUT(char *text,unsigned length)\r
11513      {\r
11514           /* Receive a string from the operator */\r
11515      \r
11516           unsigned key_pos;\r
11517           int key;\r
11518           unsigned start_row;\r
11519           unsigned start_col;\r
11520           unsigned end;\r
11521           char temp[80];\r
11522           char *p;\r
11523      \r
11524           curr_cursor(&start_row,&start_col);\r
11525      \r
11526           key_pos = 0;\r
11527           end = strlen(text);\r
11528           for(;;)\r
11529           {\r
11530                key = bioskey(0);\r
11531                if ((key & 0xFF) == 0)\r
11532                {\r
11533                     key = key >> 8;\r
11534                     if (key == 79)\r
11535                     {\r
11536                          while(key_pos < end)\r
11537                               key_pos++;\r
11538                          cursor(start_row,start_col + key_pos);\r
11539                     }\r
11540                     else\r
11541                     if (key == 71)\r
11542                     {\r
11543                          key_pos = 0;\r
11544                          cursor(start_row,start_col);\r
11545                     }\r
11546                     else\r
11547                     if ((key == 75) && (key_pos > 0))\r
11548                     {\r
11549                          key_pos--;\r
11550                          cursor(start_row,start_col + key_pos);\r
11551                     }\r
11552                     else\r
11553                     if ((key == 77) && (key_pos < end))\r
11554                     {\r
11555                          key_pos++;\r
11556                          cursor(start_row,start_col + key_pos);\r
11557                     }\r
11558                     else\r
11559                     if (key == 83)\r
11560                     {\r
11561                          p = text + key_pos;\r
11562                          while(*(p+1))\r
11563                          {\r
11564                               *p = *(p+1);\r
11565                               p++;\r
11566                          }\r
11567                          *p = 32;\r
11568                          if (end > 0)\r
11569                               end--;\r
11570                          cursor(start_row,start_col);\r
11571                          cprintf(text);\r
11572                          cprintf(" ");\r
11573                          if ((key_pos > 0) && (key_pos == end))\r
11574                               key_pos--;\r
11575                          cursor(start_row,start_col + key_pos);\r
11576                     }\r
11577                }\r
11578                else\r
11579                {\r
11580                     key = key & 0xFF;\r
11581                     if (key == 13 || key == 27)\r
11582                          break;\r
11583                     else\r
11584                     if ((key == 8) && (key_pos > 0))\r
11585                     {\r
11586                          end--;\r
11587                          key_pos--;\r
11588                          text[key_pos--] = '\0';\r
11589                          strcpy(temp,text);\r
11590                          p = text + key_pos + 2;\r
11591                          strcat(temp,p);\r
11592                          strcpy(text,temp);\r
11593                          cursor(start_row,start_col);\r
11594                          cprintf("%-*.*s",length,length,text);\r
11595                          key_pos++;\r
11596                          cursor(start_row,start_col + key_pos);\r
11597                     }\r
11598                     else\r
11599                     if ((key > 31) && (key_pos < length)  &&\r
11600                        (start_col + key_pos < 80))\r
11601                     {\r
11602                          if (key_pos <= end)\r
11603                          {\r
11604                               p = text + key_pos;\r
11605                               memmove(p+1,p,end - key_pos);\r
11606                               if (end < length)\r
11607                                    end++;\r
11608                               text[end] = '\0';\r
11609                          }\r
11610                          text[key_pos++] = (char)key;\r
11611                          if (key_pos > end)\r
11612                          {\r
11613                               end++;\r
11614                               text[end] = '\0';\r
11615                          }\r
11616                          cursor(start_row,start_col);\r
11617                          cprintf("%-*.*s",length,length,text);\r
11618                          cursor(start_row,start_col + key_pos);\r
11619                     }\r
11620                }\r
11621           }\r
11622           text[end] = '\0';\r
11623           return key;\r
11624      }\r
11625      \r
11626      void FATAL(char *error)\r
11627      {\r
11628           /* A fatal error has occured */\r
11629      \r
11630           printf("\nFATAL ERROR: %s",error);\r
11631           exit(0);\r
11632      }\r
11633      \r
11634      void OPENDATA()\r
11635      {\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
11638      \r
11639           handle = open(fpath,O_RDWR,S_IWRITE);\r
11640      \r
11641           if (handle == -1)\r
11642           {\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
11646           }\r
11647           /* Read in first rec */\r
11648           read(handle,&rec,recsize);\r
11649      }\r
11650      \r
11651      void CLOSEDATA()\r
11652      {\r
11653           close(handle);\r
11654      }\r
11655      \r
11656      void GETDATA(int start)\r
11657      {\r
11658           /* Get address data from operator */\r
11659      \r
11660           textcolor(BLACK);\r
11661           textbackground(GREEN);\r
11662           gotoxy(left,8);\r
11663           print("Name ");\r
11664           gotoxy(left,9);\r
11665           print("Company ");\r
11666           gotoxy(left,10);\r
11667           print("Address ");\r
11668           gotoxy(left,11);\r
11669           print("Area ");\r
11670           gotoxy(left,12);\r
11671           print("Town ");\r
11672           gotoxy(left,13);\r
11673           print("County ");\r
11674           gotoxy(left,14);\r
11675           print("Post Code ");\r
11676           gotoxy(left,15);\r
11677           print("Telephone ");\r
11678           gotoxy(left,16);\r
11679           print("Fax ");\r
11680      \r
11681           switch(start)\r
11682           {\r
11683                case 0: gotoxy(left + 10,8);\r
11684                     if(INPUT(rec.name,30) == 27)\r
11685                          break;\r
11686                case 1: gotoxy(left + 10,9);\r
11687                     if(INPUT(rec.company,30) == 27)\r
11688                          break;\r
11689                case 2: gotoxy(left + 10,10);\r
11690                     if(INPUT(rec.address,30) == 27)\r
11691                          break;\r
11692                case 3: gotoxy(left + 10,11);\r
11693                     if(INPUT(rec.area,30) == 27)\r
11694                          break;\r
11695                case 4: gotoxy(left + 10,12);\r
11696                     if(INPUT(rec.town,30) == 27)\r
11697                          break;\r
11698                case 5: gotoxy(left + 10,13);\r
11699                     if(INPUT(rec.county,30) == 27)\r
11700                          break;\r
11701                case 6: gotoxy(left + 10,14);\r
11702                     if(INPUT(rec.post,12) == 27)\r
11703                          break;\r
11704                case 7: gotoxy(left + 10,15);\r
11705                     if(INPUT(rec.telephone,15) == 27)\r
11706                          break;\r
11707                case 8: gotoxy(left + 10,16);\r
11708                     INPUT(rec.fax,15);\r
11709                     break;\r
11710           }\r
11711           textcolor(WHITE);\r
11712           textbackground(RED);\r
11713           gotoxy(left + 23,21);\r
11714           print("                                                 ");\r
11715      }\r
11716      \r
11717      void DISPDATA()\r
11718      {\r
11719           /* Display address data */\r
11720           textcolor(BLACK);\r
11721           textbackground(GREEN);\r
11722           cursor(7,3);\r
11723           cprintf("Name    %-30.30s",rec.name);\r
11724           cursor(8,3);\r
11725           cprintf("Company   %-30.30s",rec.company);\r
11726           cursor(9,3);\r
11727           cprintf("Address   %-30.30s",rec.address);\r
11728           cursor(10,3);\r
11729           cprintf("Area    %-30.30s",rec.area);\r
11730           cursor(11,3);\r
11731           cprintf("Town    %-30.30s",rec.town);\r
11732           cursor(12,3);\r
11733           cprintf("County     %-30.30s",rec.county);\r
11734           cursor(13,3);\r
11735           cprintf("Post Code %-30.30s",rec.post);\r
11736           cursor(14,3);\r
11737           cprintf("Telephone %-30.30s",rec.telephone);\r
11738           cursor(15,3);\r
11739           cprintf("Fax      %-30.30s",rec.fax);\r
11740      }\r
11741      \r
11742      int LOCATE(char *text)\r
11743      {\r
11744           int result;\r
11745      \r
11746           do\r
11747           {\r
11748                /* Read rec into memory */\r
11749                result = read(handle,&rec,recsize);\r
11750                if (result > 0)\r
11751                {\r
11752                     /* Scan rec for matching data */\r
11753                     if (strstr(strupr(rec.name),text) != NULL)\r
11754                          return(1);\r
11755                     if (strstr(strupr(rec.company),text) != NULL)\r
11756                          return(1);\r
11757                     if (strstr(strupr(rec.address),text) != NULL)\r
11758                          return(1);\r
11759                     if (strstr(strupr(rec.area),text) != NULL)\r
11760                          return(1);\r
11761                     if (strstr(strupr(rec.town),text) != NULL)\r
11762                          return(1);\r
11763                     if (strstr(strupr(rec.county),text) != NULL)\r
11764                          return(1);\r
11765                     if (strstr(strupr(rec.post),text) != NULL)\r
11766                          return(1);\r
11767                     if (strstr(strupr(rec.telephone),text) != NULL)\r
11768                          return(1);\r
11769                     if (strstr(strupr(rec.fax),text) != NULL)\r
11770                          return(1);\r
11771                }\r
11772           }\r
11773           while(result > 0);\r
11774           return(0);\r
11775      }\r
11776      \r
11777      void SEARCH()\r
11778      {\r
11779           int result;\r
11780      \r
11781           gotoxy(left,21);\r
11782           textcolor(WHITE);\r
11783           textbackground(RED);\r
11784           cprintf("Enter data to search for ");\r
11785           strcpy(stext,"");\r
11786           INPUT(stext,30);\r
11787           if (*stext == 0)\r
11788           {\r
11789                gotoxy(left,21);\r
11790                cprintf("%70c",32);\r
11791                return;\r
11792           }\r
11793           gotoxy(left,21);\r
11794           textcolor(WHITE);\r
11795           textbackground(RED);\r
11796           cprintf("Searching for %s Please Wait....",stext);\r
11797           strupr(stext);\r
11798           /* Locate start of file */\r
11799           lseek(handle,0,SEEK_SET);\r
11800           result = LOCATE(stext);\r
11801           if (result == 0)\r
11802           {\r
11803                gotoxy(left,21);\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
11809                bioskey(0);\r
11810                gotoxy(left,21);\r
11811                cprintf("%70c",32);\r
11812                gotoxy(left,22);\r
11813                cprintf("%70c",32);\r
11814           }\r
11815           else\r
11816           {\r
11817                lseek(handle,0 - recsize,SEEK_CUR);\r
11818                read(handle,&rec,recsize);\r
11819                DISPDATA();\r
11820           }\r
11821           textcolor(WHITE);\r
11822           textbackground(RED);\r
11823           gotoxy(left,21);\r
11824           cprintf("%70c",32);\r
11825           textcolor(BLACK);\r
11826           textbackground(GREEN);\r
11827      }\r
11828      \r
11829      void CONTINUE()\r
11830      {\r
11831           int result;\r
11832           long curpos;\r
11833      \r
11834           curpos = tell(handle) - recsize;\r
11835      \r
11836           result = LOCATE(stext);\r
11837           textcolor(WHITE);\r
11838           textbackground(RED);\r
11839           if (result == 0)\r
11840           {\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
11845                bioskey(0);\r
11846                gotoxy(left,21);\r
11847                cprintf("%70c",32);\r
11848                gotoxy(left,22);\r
11849                cprintf("%70c",32);\r
11850                lseek(handle,curpos,SEEK_SET);\r
11851                read(handle,&rec,recsize);\r
11852                DISPDATA();\r
11853           }\r
11854           else\r
11855           {\r
11856                lseek(handle,0 - recsize,SEEK_CUR);\r
11857                read(handle,&rec,recsize);\r
11858                DISPDATA();\r
11859           }\r
11860           textcolor(WHITE);\r
11861           textbackground(RED);\r
11862           gotoxy(left,21);\r
11863           cprintf("%70c",32);\r
11864           gotoxy(left,22);\r
11865           cprintf("                                           ");\r
11866           textcolor(BLACK);\r
11867           textbackground(GREEN);\r
11868      }\r
11869      \r
11870      void PRINT_MULTI()\r
11871      {\r
11872           data buffer;\r
11873           char destination[60];\r
11874           char text[5];\r
11875           int result;\r
11876           int ok;\r
11877           int ok2;\r
11878           int blanks;\r
11879           int total_lines;\r
11880           char *p;\r
11881           FILE *fp;\r
11882      \r
11883           textcolor(WHITE);\r
11884           textbackground(RED);\r
11885           gotoxy(left + 23,21);\r
11886           cprintf("Enter selection criteria");\r
11887      \r
11888           /* Clear existing rec details */\r
11889           memset(&rec,0,recsize);\r
11890      \r
11891           DISPDATA();\r
11892           GETDATA(0);\r
11893      \r
11894           textcolor(WHITE);\r
11895           textbackground(RED);\r
11896           gotoxy(left,21);\r
11897           cprintf("Enter report destination PRN");\r
11898           strcpy(destination,"PRN");\r
11899           gotoxy(left,22);\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
11905           INPUT(text,2);\r
11906           gotoxy(left,21);\r
11907           cprintf("%72c",32);\r
11908           gotoxy(left,22);\r
11909           cprintf("%72c",32);\r
11910      \r
11911           total_lines = atoi(text) - 6;\r
11912           if (total_lines < 0)\r
11913                total_lines = 0;\r
11914      \r
11915           fp = fopen(destination,"w+");\r
11916           if (fp == NULL)\r
11917           {\r
11918                gotoxy(left,21);\r
11919                cprintf("Unable to print to %s",destination);\r
11920                gotoxy(left,22);\r
11921                cprintf("Press RETURN to Continue");\r
11922                bioskey(0);\r
11923                gotoxy(left,21);\r
11924                cprintf("%78c",32);\r
11925                gotoxy(left,22);\r
11926                cprintf("                          ");\r
11927           }\r
11928      \r
11929           /* Locate start of file */\r
11930           lseek(handle,0,SEEK_SET);\r
11931      \r
11932           do\r
11933           {\r
11934                /* Read rec into memory */\r
11935                result = read(handle,&buffer,recsize);\r
11936                if (result > 0)\r
11937                {\r
11938                     ok = 1;\r
11939                     /* Scan rec for matching data */\r
11940                     if (*rec.name)\r
11941                          if (stricmp(buffer.name,rec.name))\r
11942                               ok = 0;\r
11943                     if (*rec.company)\r
11944                          if (stricmp(buffer.company,rec.company))\r
11945                               ok = 0;\r
11946                     if (*rec.address)\r
11947                          if (stricmp(buffer.address,rec.address))\r
11948                               ok = 0;\r
11949                     if (*rec.area)\r
11950                          if (stricmp(buffer.area,rec.area))\r
11951                               ok = 0;\r
11952                     if (*rec.town)\r
11953                          if (stricmp(buffer.town,rec.town))\r
11954                               ok = 0;\r
11955                     if (*rec.county)\r
11956                          if (stricmp(buffer.county,rec.county))\r
11957                               ok = 0;\r
11958                     if (*rec.post)\r
11959                          if (stricmp(buffer.post,rec.post))\r
11960                          ok = 0;\r
11961                     if (*rec.telephone)\r
11962                          if (stricmp(buffer.telephone,rec.telephone))\r
11963                               ok = 0;\r
11964                     if (*rec.fax)\r
11965                          if (stricmp(buffer.fax,rec.fax))\r
11966                               ok = 0;\r
11967                     if (ok)\r
11968                     {\r
11969                          blanks = total_lines;\r
11970                          p = buffer.name;\r
11971                          ok2 = 0;\r
11972                          while(*p)\r
11973                          {\r
11974                               if (*p != 32)\r
11975                               {\r
11976                                    ok2 = 1;\r
11977                                    break;\r
11978                               }\r
11979                               p++;\r
11980                          }\r
11981                          if (!ok2)\r
11982                               blanks++;\r
11983                          else\r
11984                               fprintf(fp,"%s\n",buffer.name);\r
11985                          p = buffer.company;\r
11986                          ok2 = 0;\r
11987                          while(*p)\r
11988                          {\r
11989                               if (*p != 32)\r
11990                               {\r
11991                                    ok2 = 1;\r
11992                                    break;\r
11993                               }\r
11994                               p++;\r
11995                          }\r
11996                          if (!ok2)\r
11997                               blanks++;\r
11998                          else\r
11999                               fprintf(fp,"%s\n",buffer.company);\r
12000                          p = buffer.address;\r
12001                          ok2 = 0;\r
12002      \r
12003                          while(*p)\r
12004                          {\r
12005                               if (*p != 32)\r
12006                               {\r
12007                                    ok2 = 1;\r
12008                                    break;\r
12009                               }\r
12010                               p++;\r
12011                          }\r
12012                          if (!ok2)\r
12013                               blanks++;\r
12014                          else\r
12015                               fprintf(fp,"%s\n",buffer.address);\r
12016                          p = buffer.area;\r
12017                          ok2 = 0;\r
12018                          while(*p)\r
12019                          {\r
12020                               if (*p != 32)\r
12021                               {\r
12022                                    ok2 = 1;\r
12023                                    break;\r
12024                               }\r
12025                               p++;\r
12026                          }\r
12027                          if (!ok2)\r
12028                               blanks++;\r
12029                          else\r
12030                               fprintf(fp,"%s\n",buffer.area);\r
12031                          p = buffer.town;\r
12032                          ok2 = 0;\r
12033                          while(*p)\r
12034                          {\r
12035                               if (*p != 32)\r
12036                               {\r
12037                                    ok2 = 1;\r
12038                                    break;\r
12039                               }\r
12040                               p++;\r
12041                          }\r
12042                          if (!ok2)\r
12043                               blanks++;\r
12044                          else\r
12045                               fprintf(fp,"%s\n",buffer.town);\r
12046                          p = buffer.county;\r
12047                          ok2 = 0;\r
12048      \r
12049                          while(*p)\r
12050                          {\r
12051                               if (*p != 32)\r
12052                               {\r
12053                                    ok2 = 1;\r
12054                                    break;\r
12055                               }\r
12056                               p++;\r
12057                          }\r
12058                          if (!ok2)\r
12059                               blanks++;\r
12060                          else\r
12061                               fprintf(fp,"%s\n",buffer.county);\r
12062                          p = buffer.post;\r
12063                          ok2 = 0;\r
12064                          while(*p)\r
12065                          {\r
12066                               if (*p != 32)\r
12067                               {\r
12068                                    ok2 = 1;\r
12069                                    break;\r
12070                               }\r
12071                               p++;\r
12072                          }\r
12073                          if (!ok2)\r
12074                               blanks++;\r
12075                          else\r
12076                               fprintf(fp,"%s\n",buffer.post);\r
12077                          while(blanks)\r
12078                          {\r
12079                               fprintf(fp,"\n");\r
12080                               blanks--;\r
12081                          }\r
12082                     }\r
12083                }\r
12084           }\r
12085           while(result > 0);\r
12086           fclose(fp);\r
12087           lseek(handle,0,SEEK_SET);\r
12088           read(handle,&rec,recsize);\r
12089           DISPDATA();\r
12090      }\r
12091      \r
12092      void EXPORT_MULTI()\r
12093      {\r
12094           data buffer;\r
12095           char destination[60];\r
12096           int result;\r
12097           int ok;\r
12098           FILE *fp;\r
12099      \r
12100           textcolor(WHITE);\r
12101           textbackground(RED);\r
12102           gotoxy(left + 23,21);\r
12103           cprintf("Enter selection criteria");\r
12104      \r
12105           /* Clear existing rec details */\r
12106           memset(&rec,0,recsize);\r
12107      \r
12108           DISPDATA();\r
12109           GETDATA(0);\r
12110      \r
12111           textcolor(WHITE);\r
12112           textbackground(RED);\r
12113           gotoxy(left,21);\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
12118           gotoxy(left,21);\r
12119           cprintf("%70c",32);\r
12120      \r
12121           fp = fopen(destination,"w+");\r
12122           if (fp == NULL)\r
12123           {\r
12124                gotoxy(left,21);\r
12125                cprintf("Unable to print to %s",destination);\r
12126                gotoxy(left,22);\r
12127                cprintf("Press RETURN to Continue");\r
12128                bioskey(0);\r
12129                gotoxy(left,21);\r
12130                cprintf("%78c",32);\r
12131                gotoxy(left,22);\r
12132                cprintf("                          ");\r
12133           }\r
12134           /* Locate start of file */\r
12135           lseek(handle,0,SEEK_SET);\r
12136      \r
12137           do\r
12138           {\r
12139                /* Read rec into memory */\r
12140                result = read(handle,&buffer,recsize);\r
12141                if (result > 0)\r
12142                {\r
12143                     ok = 1;\r
12144                     /* Scan rec for matching data */\r
12145                     if (*rec.name)\r
12146                          if (stricmp(buffer.name,rec.name))\r
12147                               ok = 0;\r
12148                     if (*rec.company)\r
12149                          if (stricmp(buffer.company,rec.company))\r
12150                               ok = 0;\r
12151                     if (*rec.address)\r
12152                          if (stricmp(buffer.address,rec.address))\r
12153                               ok = 0;\r
12154                     if (*rec.area)\r
12155                          if (stricmp(buffer.area,rec.area))\r
12156                               ok = 0;\r
12157                     if (*rec.town)\r
12158                          if (stricmp(buffer.town,rec.town))\r
12159                               ok = 0;\r
12160                     if (*rec.county)\r
12161                          if (stricmp(buffer.county,rec.county))\r
12162                               ok = 0;\r
12163                     if (*rec.post)\r
12164                          if (stricmp(buffer.post,rec.post))\r
12165                          ok = 0;\r
12166                     if (*rec.telephone)\r
12167                          if (stricmp(buffer.telephone,rec.telephone))\r
12168                               ok = 0;\r
12169                     if (*rec.fax)\r
12170                          if (stricmp(buffer.fax,rec.fax))\r
12171                               ok = 0;\r
12172                     if (ok)\r
12173                     {\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
12183      \r
12184                     }\r
12185                }\r
12186           }\r
12187      \r
12188           while(result > 0);\r
12189           fclose(fp);\r
12190           lseek(handle,0,SEEK_SET);\r
12191           read(handle,&rec,recsize);\r
12192           DISPDATA();\r
12193      }\r
12194      \r
12195      void MENU()\r
12196      {\r
12197           int option;\r
12198           long result;\r
12199           long end;\r
12200           int new;\r
12201      \r
12202           do\r
12203           {\r
12204                cursor(21,26);\r
12205                print("Select option (F2 - F10)");\r
12206                cursor(7,52);\r
12207                print("F2 Next record");\r
12208                cursor(8,52);\r
12209                print("F3 Previous record");\r
12210                cursor(9,52);\r
12211                print("F4 Amend record");\r
12212                cursor(10,52);\r
12213                print("F5 Add new record");\r
12214                cursor(11,52);\r
12215                print("F6 Search");\r
12216                cursor(12,52);\r
12217                print("F7 Continue search");\r
12218                cursor(13,52);\r
12219                print("F8 Print address labels");\r
12220                cursor(14,52);\r
12221                print("F9 Export records");\r
12222                cursor(15,52);\r
12223                print("F10 Exit");\r
12224                MOUSE_CURSOR(1);\r
12225                option = GETOPT();\r
12226                MOUSE_CURSOR(0);\r
12227      \r
12228                switch(option)\r
12229                {\r
12230                     case 0 : /* Next rec */\r
12231                           result = read(handle,&rec,recsize);\r
12232                           if (!result)\r
12233                           {\r
12234                               lseek(handle,0,SEEK_SET);\r
12235                                result = read(handle,&rec,recsize);\r
12236                           }\r
12237                           DISPDATA();\r
12238                           break;\r
12239      \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
12245                          DISPDATA();\r
12246                          break;\r
12247 \r
12248                     case 3 : /* Add rec */\r
12249                           lseek(handle,0,SEEK_END);\r
12250                           memset(&rec,0,recsize);\r
12251                           DISPDATA();\r
12252      \r
12253                     case 2 : /* Amend current rec */\r
12254                           new = 1;\r
12255                           if (*rec.name)\r
12256                               new = 0;\r
12257                           else\r
12258                           if (*rec.company)\r
12259                               new = 0;\r
12260                           else\r
12261                           if (*rec.address)\r
12262                               new = 0;\r
12263                           else\r
12264                           if (*rec.area)\r
12265                               new = 0;\r
12266                           else\r
12267                           if (*rec.town)\r
12268                               new = 0;\r
12269                           else\r
12270                           if (*rec.county)\r
12271                               new = 0;\r
12272                           else\r
12273                           if (*rec.post)\r
12274                               new = 0;\r
12275                           else\r
12276                           if (*rec.telephone)\r
12277                               new = 0;\r
12278                           else\r
12279                           if (*rec.fax)\r
12280                               new = 0;\r
12281                           result = tell(handle);\r
12282                           lseek(handle,0,SEEK_END);\r
12283                           end = tell(handle);\r
12284      \r
12285                           /* Back to original position */\r
12286                           lseek(handle,result,SEEK_SET);\r
12287      \r
12288                           /* If not at end of file, && !new rewind one\r
12289 rec */\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
12296                           GETDATA(0);\r
12297                           if (*rec.name || *rec.company)\r
12298                               result =  write(handle,&rec,recsize);\r
12299                           break;\r
12300 \r
12301                     case 4 : /* Search */\r
12302                           gotoxy(left + 22,21);\r
12303                           print("                           ");\r
12304                           SEARCH();\r
12305                           break;\r
12306      \r
12307                     case 5 : /* Continue */\r
12308                           gotoxy(left + 22,21);\r
12309                           print("                           ");\r
12310                           CONTINUE();\r
12311                           break;\r
12312      \r
12313                     case 6 : /* Print */\r
12314                           gotoxy(left + 22,21);\r
12315                           print("                           ");\r
12316                           PRINT_MULTI();\r
12317                           break;\r
12318 \r
12319                     case 7 : /* Export */\r
12320                           gotoxy(left + 22,21);\r
12321                           print("                           ");\r
12322                           EXPORT_MULTI();\r
12323                           break;\r
12324      \r
12325                     case 8 : /* Exit */\r
12326                           break;\r
12327      \r
12328                     default: /* Amend current rec */\r
12329                           new = 1;\r
12330                           if (*rec.name)\r
12331                               new = 0;\r
12332                           else\r
12333                           if (*rec.company)\r
12334                               new = 0;\r
12335                           else\r
12336                           if (*rec.address)\r
12337                               new = 0;\r
12338                           else\r
12339                           if (*rec.area)\r
12340                               new = 0;\r
12341                           else\r
12342                           if (*rec.town)\r
12343                               new = 0;\r
12344                           else\r
12345                           if (*rec.county)\r
12346                               new = 0;\r
12347                           else\r
12348                           if (*rec.post)\r
12349                               new = 0;\r
12350                           else\r
12351                           if (*rec.telephone)\r
12352                               new = 0;\r
12353                           else\r
12354                           if (*rec.fax)\r
12355                               new = 0;\r
12356                           result = tell(handle);\r
12357                           lseek(handle,0,SEEK_END);\r
12358                           end = tell(handle);\r
12359 \r
12360                           /* Back to original position */\r
12361                           lseek(handle,result,SEEK_SET);\r
12362 \r
12363                           /* If not at end of file, && !new rewind one\r
12364 rec */\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
12374                           option = -1;\r
12375                           break;\r
12376 \r
12377                }\r
12378           }\r
12379      \r
12380           while(option != 8);\r
12381      }\r
12382      \r
12383      void exec()\r
12384      {\r
12385           gettext(1,1,80,25,scr);\r
12386           setvideo(3);\r
12387           textbackground(WHITE);\r
12388           textcolor(BLACK);\r
12389           clrscr();\r
12390           recsize = sizeof(data);\r
12391      \r
12392           OPENDATA();\r
12393      \r
12394           TRUESHADE(left,3,79,5);\r
12395           window(left - 2,2 ,78, 4);\r
12396           textcolor(YELLOW);\r
12397           textbackground(MAGENTA);\r
12398           clrscr();\r
12399           DBOX(left - 3, 1, 77, 3);\r
12400           gotoxy(3,2);\r
12401           print("Servile Software             PC ADDRESS BOOK 5.2\r
12402      (c) 1994");\r
12403      \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
12408           clrscr();\r
12409           DBOX(left - 3, 6, left + 41, 16);\r
12410      \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
12415           clrscr();\r
12416           DBOX(left + 45,6,77,16);\r
12417      \r
12418           TRUESHADE(left ,21,79,24);\r
12419           window(left - 2, 20 , 78, 23);\r
12420           textbackground(RED);\r
12421           textcolor(WHITE);\r
12422           clrscr();\r
12423           DBOX(left - 3,19,77,22);\r
12424      \r
12425           window(1,1,80,25);\r
12426           textcolor(BLACK);\r
12427           textbackground(GREEN);\r
12428           DISPDATA();\r
12429      \r
12430           MENU();\r
12431      \r
12432           CLOSEDATA();\r
12433           puttext(1,1,80,25,scr);\r
12434           return;\r
12435      }\r
12436      \r
12437                                     \r
12438                        INTERFACING C WITH CLIPPER\r
12439 \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
12445 \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
12449 \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
12454 \r
12455 \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
12461 libraries.\r
12462 \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
12466 \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
12469 line.\r
12470 \r
12471 All C functions must be compiled using the large memory model the\r
12472 following line is used with Microsoft C\r
12473 \r
12474 \r
12475      cl /c /AL /Zl /Oalt /FPa /Gs <program.c>\r
12476 \r
12477 and this compile line may be used with Turbo C\r
12478 \r
12479      tcc -c -ml <program>\r
12480 \r
12481 simply substitute <program> for the program name to be compiled.\r
12482 \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
12486 \r
12487 \r
12488      LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend\r
12489 \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
12493 \r
12494 \r
12495      LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend cl\r
12496 \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
12501 \r
12502 \r
12503       LIB mylib +prog1 +prog2, NUL, NUL\r
12504 \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
12508 \r
12509 \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
12512 example;\r
12513 \r
12514 \r
12515       COPY C:\TURBOC\LIB\cl.lib mylib.lib\r
12516       LIB mylib +prog1 +prog2, NUL, NUL\r
12517 \r
12518 Then when you link your Clipper application you will use a link line\r
12519 similar to;\r
12520 \r
12521      LINK /SE:500 /NOE myprog,,,Clipper Extend Mylib\r
12522 \r
12523 Often  when  linking C functions with  Clipper applications link errors\r
12524 will occur such as those shown below;\r
12525 \r
12526      \r
12527      Microsoft (R) Overlay Linker  Version 3.65\r
12528      Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.\r
12529      \r
12530      \r
12531      LINK : error L2029: Unresolved externals:\r
12532      \r
12533      \r
12534      FIWRQQ in file(s):\r
12535       M:SLIB.LIB(TEST)\r
12536      FIDRQQ in file(s):\r
12537       M:SLIB.LIB(TEST)\r
12538      \r
12539      There were 2 errors detected\r
12540      \r
12541 \r
12542 Example Link Errors\r
12543 \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
12551 \r
12552 \r
12553      LIB emu,emu.lst\r
12554 \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
12559 \r
12560 \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
12568      \r
12569      \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
12574      \r
12575      EMU086         Offset: 00000470H  Code and data size: 2630H\r
12576        e086_Entry        e086_Shortcut\r
12577      \r
12578      EMU087         Offset: 00003200H  Code and data size: 417H\r
12579        e087_Entry        e087_Shortcut\r
12580      \r
12581 \r
12582 \r
12583 Receiving Parameters\r
12584 \r
12585 Clipper provides six different functions for receiving parameters in a C\r
12586 function. These functions are;\r
12587 \r
12588 \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
12595 \r
12596 \r
12597 \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
12602 \r
12603 \r
12604      #include <nandef.h>            /* Clipper header files */\r
12605      #include <extend.h>\r
12606      #include <dos.h>                   /* Header file to define REGS */\r
12607      \r
12608      CLIPPER s_curset()\r
12609      {\r
12610           /* Demonstration function to set cursor shape */\r
12611      \r
12612           union REGS inreg,outreg;\r
12613      \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
12619      }\r
12620 \r
12621 Clipper  provides four more functions for dealing with received\r
12622 parameters;\r
12623 \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
12629 parameter.\r
12630 \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
12635 \r
12636 \r
12637 \r
12638      #include <nandef.h>\r
12639      #include <extend.h>\r
12640      \r
12641      CLIPPER s_alen()\r
12642      {\r
12643           int total;\r
12644           int n;\r
12645           int defined;\r
12646           int type;\r
12647      \r
12648           /* Return the number of defined elements in an array */\r
12649           /* From Clipper use defined = s_alen(arr) */\r
12650      \r
12651           total = _parinfa(1,0); /* Get declared number of elements in\r
12652      array */\r
12653      \r
12654           defined = 0;\r
12655      \r
12656           for (n = 1; n <= total; n++){\r
12657                type = _parinfa(1,n);   /* Get array parameter type */\r
12658                if (type)\r
12659                     defined++;\r
12660           }\r
12661           _retni(defined);              /* Return an integer to Clipper\r
12662      */\r
12663      }\r
12664      \r
12665 \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
12671 \r
12672 \r
12673      #include <nandef.h>\r
12674      #include <extend.h>\r
12675      \r
12676      CLIPPER s_aave()\r
12677      {\r
12678           int total;\r
12679           int defined;\r
12680           int n;\r
12681           int type;\r
12682           double sum;\r
12683      \r
12684           /* Return the mean average value of numbers in array */\r
12685           /* From Clipper use mean = s_aave(arr)\r
12686      \r
12687      \r
12688           total = _parinfa(1,0);               /* Get declared number of\r
12689      elements */\r
12690      \r
12691           defined = 0;\r
12692      \r
12693           for (n = 1; n <= total; n++){    /* Determine number of defined\r
12694      */\r
12695                type = _parinfa(1,n);            /* elements */\r
12696                if (type == 2)\r
12697                     defined++;\r
12698           }\r
12699      \r
12700           sum = 0;\r
12701      \r
12702           for (n = 1; n <= total; n++){\r
12703                type = _parinfa(1,n);\r
12704                if (type == 2)                  /* Only sum numeric values\r
12705      */\r
12706                     sum += _parnd(1,n);\r
12707           }\r
12708           _retnd(sum / defined);               /* Return a double to\r
12709      Clipper */\r
12710      }\r
12711      \r
12712      \r
12713 \r
12714 Returning Values\r
12715 \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
12718 follows;\r
12719 \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
12727 \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
12734 \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
12738 \r
12739 \r
12740      #include <nandef.h>\r
12741      #include <extend.h>\r
12742      #include <dos.h>\r
12743      \r
12744      CLIPPER s_random()\r
12745      {\r
12746           /* Returns a random number between 0 and param1 - 1 */\r
12747           /* From Clipper use x = s_random(param1) */\r
12748      \r
12749           int param1;\r
12750           int x;\r
12751      \r
12752           param1 = _parni(1);\r
12753      \r
12754           x = rand() % param1;\r
12755           _retni(x);\r
12756      }\r
12757      \r
12758 \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
12763 \r
12764 \r
12765      #include <nandef.h>\r
12766      #include <extend.h>\r
12767      \r
12768      CLIPPER s_upper()\r
12769      {\r
12770           /* Returns an upper case copy of string */\r
12771           /* From Clipper use ? s_upper("this is a string")\r
12772      \r
12773           char *p;\r
12774           char *q;\r
12775           char *string;\r
12776           char target[5000];\r
12777           int n;\r
12778      \r
12779           string = _parc(1);\r
12780      \r
12781           p = string;\r
12782           q = target;\r
12783      \r
12784           while(*string){\r
12785                *q++ = toupper(*string);\r
12786                string++;\r
12787           }\r
12788           *q = '\0';\r
12789           string = p;\r
12790           _retc(target);\r
12791      }\r
12792      \r
12793      \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
12797 \r
12798      #include <nandef.h>\r
12799      #include <extend.h>\r
12800      #include <dos.h>\r
12801      \r
12802      CLIPPER s_chdir()\r
12803      {\r
12804           /* Attempts to change the current DOS directory */\r
12805           /* From Clipper use result = s_chdir(path) */\r
12806      \r
12807           union REGS inreg,outreg;\r
12808           struct SREGS segreg;\r
12809      \r
12810           char *path;\r
12811           int x;\r
12812      \r
12813           path = _parc(1);              /* Retrieve string from Clipper\r
12814      */\r
12815      \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
12820      \r
12821           x = outreg.x.ax;\r
12822      \r
12823           if (x == 3)\r
12824                _retl(0);    /* Return logical .F. back to Clipper */\r
12825           else\r
12826                _retl(1);    /* Return logical .T. back to Clipper */\r
12827      }\r
12828      \r
12829 \r
12830 \r
12831 Avoiding Unresolved Externals\r
12832 \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
12835 \r
12836 The  following  example  C function called s_print()   will  not  link\r
12837 with Clipper.\r
12838 \r
12839      #include <nandef.h>\r
12840      #include <extend.h>\r
12841      #include <stdio.h>\r
12842      \r
12843      CLIPPER s_print()\r
12844      {\r
12845           char *x;\r
12846      \r
12847           x = _parc(1);\r
12848      \r
12849           printf("\nI received %s from Clipper.\n",x);\r
12850      \r
12851           _ret();\r
12852      }\r
12853      \r
12854 \r
12855 The linker gives you the following reply;\r
12856 \r
12857      Microsoft (R) Overlay Linker  Version 3.65\r
12858      Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.\r
12859      \r
12860      M:SLIB.LIB(IOERROR) : error L2025: __doserrno : symbol defined more\r
12861      than once\r
12862       pos: 16C6F Record type: 53C6\r
12863      \r
12864      LINK : error L2029: Unresolved externals:\r
12865      \r
12866      \r
12867 \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
12872      \r
12873      There were 3 errors detected\r
12874      \r
12875 \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
12882 \r
12883 \r
12884 Adding High Resolution Graphics To Clipper With C\r
12885 \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
12889 \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
12893 \r
12894 \r
12895 \r
12896 \r
12897      /*         Servile Software Library For Clipper              */\r
12898      \r
12899      #include <nandef.h>\r
12900      #include <extend.h>\r
12901      #include <dos.h>\r
12902      \r
12903      CLIPPER s_smode()\r
12904      {\r
12905           /* Set Video Mode */\r
12906           /* From Clipper use s_smode(mode) */\r
12907      \r
12908           union REGS inreg,outreg;\r
12909      \r
12910           inreg.h.al = _parni(1);\r
12911           inreg.h.ah = 0x00;\r
12912           int86 (0x10, &inreg, &outreg);\r
12913      \r
12914      \r
12915      /*  1 40x25 colour text\r
12916           2 40x25 bw 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
12921           etc\r
12922      */\r
12923           _ret();\r
12924      }\r
12925 \r
12926 Having set the computer into graphics mode, how about setting pixels to a\r
12927 specified colour?\r
12928 \r
12929      \r
12930      /*         Servile Software Library For Clipper              */\r
12931      \r
12932      #include <nandef.h>\r
12933      #include <extend.h>\r
12934      #include <dos.h>\r
12935      \r
12936      CLIPPER s_plot()\r
12937      {\r
12938      \r
12939           union REGS inreg,outreg;\r
12940      \r
12941           /* Sets a pixel at the specified coordinates to the specified\r
12942      colour. */\r
12943      \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
12950      }\r
12951      \r
12952 Line drawing and circles are handled by these two functions;\r
12953 \r
12954      \r
12955      /*         Servile Software Library For Clipper              */\r
12956      \r
12957      #include <nandef.h>\r
12958      #include <extend.h>\r
12959      #include <dos.h>\r
12960      \r
12961      CLIPPER s_line()\r
12962      {\r
12963      \r
12964           union REGS inreg,outreg;\r
12965      \r
12966           /* Draws a straight line from (a,b) to (c,d) in colour col */\r
12967      \r
12968           int a;\r
12969           int b;\r
12970           int c;\r
12971           int d;\r
12972           int col;\r
12973           int u;\r
12974           int v;\r
12975           int d1x;\r
12976           int d1y;\r
12977           int d2x;\r
12978           int d2y;\r
12979           int m;\r
12980           int n;\r
12981           int s;\r
12982           int i;\r
12983      \r
12984           a = _parni(1);\r
12985           b = _parni(2);\r
12986           c = _parni(3);\r
12987           d = _parni(4);\r
12988           col = _parni(5);\r
12989      \r
12990           u = c - a;\r
12991           v = d - b;\r
12992           if (u == 0)\r
12993           {\r
12994                d1x = 0;\r
12995                m = 0;\r
12996           }\r
12997           else\r
12998           {\r
12999                m = abs(u);\r
13000                if (u < 0)\r
13001                     d1x = -1;\r
13002                else\r
13003                     if (u > 0)\r
13004                          d1x = 1;\r
13005           }\r
13006           if ( v == 0)\r
13007           {\r
13008                d1y = 0;\r
13009                n = 0;\r
13010           }\r
13011           else\r
13012           {\r
13013                n = abs(v);\r
13014                if (v < 0)\r
13015                     d1y = -1;\r
13016                else\r
13017                     if (v > 0)\r
13018                          d1y = 1;\r
13019           }\r
13020           if (m > n)\r
13021           {\r
13022                d2x = d1x;\r
13023                d2y = 0;\r
13024           }\r
13025           else\r
13026           {\r
13027                d2x = 0;\r
13028                d2y = d1y;\r
13029                m = n;\r
13030                n = abs(u);\r
13031           }\r
13032           s = (m / 2);\r
13033      \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
13038           {\r
13039                inreg.x.cx = (unsigned int)(a);\r
13040                inreg.x.dx = (unsigned int)(b);\r
13041                int86(0x10, &inreg, &outreg);\r
13042                s += n;\r
13043                if (s >= m)\r
13044                {\r
13045                     s -= m;\r
13046                     a += d1x;\r
13047                     b += d1y;\r
13048                }\r
13049                else\r
13050                {\r
13051                     a += d2x;\r
13052                     b += d2y;\r
13053                }\r
13054           }\r
13055      }\r
13056      \r
13057      \r
13058 \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
13064 \r
13065 \r
13066      /*         Servile Software Library For Clipper              */\r
13067      \r
13068      #include <nandef.h>\r
13069      #include <extend.h>\r
13070      #include <dos.h>\r
13071      \r
13072      \r
13073      void plot(int x, int y, unsigned char colour)\r
13074      {\r
13075           asm mov al , colour;\r
13076           asm mov bh , 00;\r
13077           asm mov cx , x;\r
13078           asm mov dx , y;\r
13079           asm mov ah , 0Ch;\r
13080           asm int 10h;\r
13081      }\r
13082      \r
13083      int getmode()\r
13084      {\r
13085           /* Returns current video mode  and number of columns in ncols\r
13086      */\r
13087      \r
13088           asm mov ah , 0Fh;\r
13089           asm int 10h;\r
13090           return(_AL);\r
13091      }\r
13092      \r
13093      \r
13094      CLIPPER s_circle()\r
13095      {\r
13096           int x_centre;\r
13097           int y_centre;\r
13098           int radius;\r
13099           int colour;\r
13100           int x,y,delta;\r
13101           int startx,endx,x1,starty,endy,y1;\r
13102           int asp_ratio;\r
13103      \r
13104           x_centre = _parni(1);\r
13105           y_centre = _parni(2);\r
13106           radius = _parni(3);\r
13107           colour = _parni(4);\r
13108      \r
13109      \r
13110      \r
13111      \r
13112           if (getmode() == 6)\r
13113                asp_ratio = 22;\r
13114           else\r
13115                asp_ratio = 13;\r
13116      \r
13117           y = radius;\r
13118           delta = 3 - 2 * radius;\r
13119      \r
13120           for(x = 0; x < y; )\r
13121           {\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
13126      \r
13127                for(x1 = startx; x1 < endx; ++x1)\r
13128                {\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
13133                }\r
13134      \r
13135                for(y1 = starty; y1 < endy; ++y1)\r
13136                {\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
13141                }\r
13142      \r
13143                if (delta < 0)\r
13144                     delta += 4 * x + 6;\r
13145                else\r
13146                {\r
13147                     delta += 4*(x-y)+10;\r
13148                     y--;\r
13149                }\r
13150                x++;\r
13151           }\r
13152      \r
13153      \r
13154      \r
13155      \r
13156           if(y)\r
13157           {\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
13163                {\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
13168                }\r
13169      \r
13170                for(y1 = starty; y1 < endy; ++y1)\r
13171                {\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
13176                }\r
13177           }\r
13178      }\r
13179      \r
13180 \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
13184 \r
13185      \r
13186      /*         Servile Software Library For Clipper              */\r
13187      \r
13188      #include <nandef.h>\r
13189      #include <extend.h>\r
13190      #include <dos.h>\r
13191      \r
13192      int sgetmode(int *ncols)\r
13193      {\r
13194           /* Returns current video mode  and number of columns in ncols\r
13195      */\r
13196      \r
13197           union REGS inreg,outreg;\r
13198      \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
13203      }\r
13204      \r
13205      void at(int row, int col)\r
13206      {\r
13207           asm mov bh , 0;\r
13208           asm mov dh , row;\r
13209           asm mov dl , col;\r
13210           asm mov ah , 02h;\r
13211           asm int 10h;\r
13212      }\r
13213      \r
13214      \r
13215      \r
13216      \r
13217      CLIPPER s_say()\r
13218      {\r
13219           char *output;\r
13220           int p = 0;\r
13221           unsigned char page;\r
13222           unsigned char text;\r
13223           int n;\r
13224           int r;\r
13225           int c;\r
13226           int attribute;\r
13227      \r
13228           output = _parc(1);\r
13229           r = _parni(2);\r
13230           c = _parni(3);\r
13231           attribute = _parni(4);\r
13232      \r
13233           asm mov ah , 0Fh;\r
13234           asm int 10h;\r
13235           asm mov page, bh;\r
13236      \r
13237           sgetmode(&n);\r
13238      \r
13239           at(r,c);\r
13240      \r
13241           while (output[p])\r
13242           {\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
13249                asm int 10h;\r
13250                c++;\r
13251                if (c < (n-1))\r
13252                     at( r, c);\r
13253                else\r
13254                {\r
13255                     c = 0;\r
13256                     at(++r,0);\r
13257                }\r
13258           }\r
13259      }\r
13260      \r
13261 \r
13262 \r
13263 \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
13268 \r
13269 \r
13270      \r
13271      /*         Servile Software Library For Clipper              */\r
13272      \r
13273      #include <nandef.h>\r
13274      #include <extend.h>\r
13275      #include <dos.h>\r
13276      \r
13277      int pixset(int x, int y)\r
13278      {\r
13279           /* Returns the colour of the specified pixel */\r
13280      \r
13281           asm mov cx ,x;\r
13282           asm mov dx ,y;\r
13283           asm mov ah ,0Dh;\r
13284           asm int 10h;\r
13285           return(_AL);\r
13286      }\r
13287      \r
13288      CLIPPER s_fill()\r
13289      {\r
13290           /* Fill a boundered shape using a hatch pattern */\r
13291      \r
13292           int mode;\r
13293           int xa;\r
13294           int ya;\r
13295           int bn;\r
13296           int byn;\r
13297           int x;\r
13298           int y;\r
13299           int col;\r
13300           int pattern;\r
13301           int maxx;\r
13302           int maxy;\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
13306                                    1,2,4,8,8,4,2,1,\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
13313      \r
13314           /* Patterns for fill, each integer describes a row of dots */\r
13315      \r
13316      \r
13317      \r
13318      \r
13319           x = _parni(1);\r
13320           y = _parni(2);\r
13321           col = _parni(3);\r
13322           pattern = _parni(4);\r
13323      \r
13324           mode = getmode();\r
13325      \r
13326           switch(mode)\r
13327           {\r
13328                case 0:\r
13329                case 1:\r
13330                case 2:\r
13331                case 3: break;\r
13332                case 4:\r
13333                case 9:\r
13334                case 13:\r
13335                case 19:\r
13336                case 5: maxx = 320;\r
13337                          maxy = 200;\r
13338                          break;\r
13339                case 14:\r
13340                case 10:\r
13341                case 6: maxx = 640;\r
13342                          maxy = 200;\r
13343                          break;\r
13344                case 7: maxx = 720;\r
13345                          maxy = 400;\r
13346                          break;\r
13347                case 8: maxx = 160;\r
13348                          maxy = 200;\r
13349                          break;\r
13350                case 15:\r
13351                case 16: maxx = 640;\r
13352                           maxy = 350;\r
13353                           break;\r
13354                case 17:\r
13355                case 18: maxx = 640;\r
13356                           maxy = 480;\r
13357                           break;\r
13358      \r
13359           }\r
13360      \r
13361           xa = x;\r
13362           ya = y;  /* Save Origin */\r
13363      \r
13364           if(pixset(x,y))\r
13365                return;\r
13366      \r
13367           bn = 1;\r
13368           byn = 0;\r
13369      \r
13370      \r
13371      \r
13372      \r
13373           do\r
13374           {\r
13375                if (hatch[pattern][byn] != 0)\r
13376                {  /* If blank ignore */\r
13377                     do\r
13378                     {\r
13379                          if ((bn & hatch[pattern][byn]) == bn)\r
13380                          {\r
13381                               asm mov al , col;\r
13382                               asm mov bh , 00;\r
13383                               asm mov cx , x;\r
13384                               asm mov dx , y;\r
13385                               asm mov ah , 0Ch;\r
13386                               asm int 10h;\r
13387                          }\r
13388                          x--;\r
13389                          bn <<= 1;\r
13390                          if (bn > 128)\r
13391                               bn = 1;\r
13392                     }\r
13393                     while(!pixset(x,y) && (x > -1));\r
13394      \r
13395                     x = xa + 1;\r
13396                     bn = 128;\r
13397      \r
13398                     do\r
13399                     {\r
13400                          if ((bn & hatch[pattern][byn]) == bn)\r
13401                          {\r
13402                               asm mov al , col;\r
13403                               asm mov bh , 00;\r
13404                               asm mov cx , x;\r
13405                               asm mov dx , y;\r
13406                               asm mov ah , 0Ch;\r
13407                               asm int 10h;\r
13408                          }\r
13409                          x++;\r
13410                          bn >>=1;\r
13411                          if (bn <1)\r
13412                               bn = 128;\r
13413                     }\r
13414                     while((!pixset(x,y)) && (x <= maxx));\r
13415                }\r
13416                x = xa;\r
13417                y--;\r
13418                bn = 1;\r
13419                byn++;\r
13420                if (byn > 7)\r
13421                     byn = 0;\r
13422      \r
13423      \r
13424      \r
13425      \r
13426           }\r
13427           while(!pixset(x,y) && ( y > -1));\r
13428      \r
13429           /* Now travel downwards */\r
13430      \r
13431           y = ya + 1;\r
13432      \r
13433           byn = 7;\r
13434           bn = 1;\r
13435           do\r
13436           {\r
13437                /* Travel left */\r
13438                if (hatch[pattern][byn] !=0)\r
13439                {\r
13440                     do\r
13441                     {\r
13442                          if ((bn & hatch[pattern][byn]) == bn)\r
13443                          {\r
13444                               asm mov al , col;\r
13445                               asm mov bh , 00;\r
13446                               asm mov cx , x;\r
13447                               asm mov dx , y;\r
13448                               asm mov ah , 0Ch;\r
13449                               asm int 10h;\r
13450                          }\r
13451                          x--;\r
13452                          bn <<= 1;\r
13453                          if (bn > 128)\r
13454                               bn = 1;\r
13455                     }\r
13456                     while(!pixset(x,y) && (x > -1));\r
13457      \r
13458                     /* Back to x origin */\r
13459                     x = xa + 1 ;\r
13460                     bn = 128;\r
13461      \r
13462                     /* Travel right */\r
13463                     do\r
13464                     {\r
13465                          if ((bn & hatch[pattern][byn]) == bn)\r
13466                          {\r
13467                               asm mov al , col;\r
13468                               asm mov bh , 00;\r
13469                               asm mov cx , x;\r
13470                               asm mov dx , y;\r
13471                               asm mov ah , 0Ch;\r
13472                               asm int 10h;\r
13473                          }\r
13474                          x++;\r
13475                          bn >>=1;\r
13476      \r
13477      \r
13478      \r
13479      \r
13480                          if (bn <1)\r
13481                               bn = 128;\r
13482                     }\r
13483                     while((!pixset(x,y)) && (x <= maxx));\r
13484                }\r
13485                x = xa;\r
13486                bn = 1;\r
13487                y++;\r
13488                byn--;\r
13489                if (byn < 0)\r
13490                     byn = 7;\r
13491           }\r
13492           while((!pixset(x,y)) && (y <= maxy));\r
13493      }\r
13494      \r
13495      \r
13496      \r
13497                                     \r
13498                        SPELL - AN EXAMPLE PROGRAM\r
13499 \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
13503 provided by DOS.\r
13504 \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
13507 windows of text.\r
13508 \r
13509 \r
13510      /* Spell checker for ascii documents */\r
13511      /* Compile with -mc (compact memory model) and unsigned characters\r
13512      */\r
13513      \r
13514      \r
13515      #include <stdio.h>\r
13516      #include <conio.h>\r
13517      #include <fcntl.h>\r
13518      #include <io.h>\r
13519      #include <dos.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
13525      #include <dir.h>\r
13526      #include <stat.h>\r
13527      \r
13528      #define  ON           0x06\r
13529      #define  OFF         0x20\r
13530      #define  MAXIMUM     15000\r
13531      #define  WORDLEN     20\r
13532      #define  LEFTMARGIN  1\r
13533      \r
13534      union REGS inreg,outreg;\r
13535      \r
13536      char *dicname;\r
13537      char *dic[MAXIMUM];      /* Array of text lines */\r
13538      char word[31];\r
13539      char comp[31];\r
13540      char fname[160];\r
13541      int lastelem;\r
13542      char changed;\r
13543      char *ignore[100];\r
13544      int lastign;\r
13545      int insert;\r
13546      int n;\r
13547      int bp;\r
13548      int mp;\r
13549      int tp;\r
13550      int result;\r
13551      \r
13552      \r
13553      \r
13554      \r
13555      char *text;\r
13556      char *textsav;\r
13557      \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
13570      \r
13571      void CURSOR(char status)\r
13572      {\r
13573           /* Toggle cursor display on and off */\r
13574      \r
13575           union REGS inreg,outreg;\r
13576      \r
13577           inreg.h.ah = 1;\r
13578           inreg.h.ch = (unsigned char)status;\r
13579           inreg.h.cl = 7;\r
13580           int86(0x10,&inreg,&outreg);\r
13581      }\r
13582      \r
13583      void DISPLAY(char *text)\r
13584      {\r
13585           /* Display 'text' expanding tabs and newline characters */\r
13586      \r
13587           while(*text)\r
13588           {\r
13589                switch(*text)\r
13590                {\r
13591                     case '\n':  cputs("\r\n");\r
13592                                    break;\r
13593                     case '\t':  cputs("      ");\r
13594                                    break;\r
13595                     default:  putch(*text);\r
13596                }\r
13597                text++;\r
13598           }\r
13599      }\r
13600      \r
13601      \r
13602      \r
13603      \r
13604      void GETDIC()\r
13605      {\r
13606           /* Read dictionary into memory */\r
13607      \r
13608           FILE *fp;\r
13609           char *p;\r
13610           int poscr;\r
13611           int handle;\r
13612      \r
13613           window(1,22,80,24);\r
13614           clrscr();\r
13615           gotoxy(28,2);\r
13616           cprintf("Reading Dictionary....");\r
13617      \r
13618           changed = 0;\r
13619           lastelem = 0;\r
13620      \r
13621           dicname = searchpath("spell.dic");\r
13622           handle = open(dicname,O_RDWR);\r
13623           if (handle < 0)\r
13624                FILERR("spell.dic");\r
13625      \r
13626           fp = fdopen(handle,"r");\r
13627           if (fp == NULL)\r
13628                FILERR("spell.dic");\r
13629      \r
13630           do\r
13631           {\r
13632                dic[lastelem] = calloc(WORDLEN,1);\r
13633                if (dic[lastelem])\r
13634                {\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
13640                }\r
13641                else\r
13642                     FATAL("Unable To Allocate Memory");\r
13643           }\r
13644           while((p != NULL) && (lastelem++ < MAXIMUM));\r
13645      \r
13646           lastelem--;\r
13647      \r
13648           fclose(fp);\r
13649      }\r
13650      \r
13651      \r
13652      \r
13653      \r
13654      void UPDATE()\r
13655      {\r
13656           FILE *fp;\r
13657           int n;\r
13658      \r
13659           if (changed)\r
13660           {\r
13661                window(1,22,80,24);\r
13662                clrscr();\r
13663                gotoxy(27,2);\r
13664                cprintf("Updating Dictionary....");\r
13665      \r
13666                fp = fopen(dicname,"w+");\r
13667                if (fp == NULL)\r
13668                     FILERR("spell.dic");\r
13669      \r
13670                for(n = 0; n <= lastelem; n++)\r
13671                     fprintf(fp,"%s\n",dic[n]);\r
13672      \r
13673                fclose(fp);\r
13674           }\r
13675      }\r
13676      \r
13677      void IGNORE()\r
13678      {\r
13679           /* Add a word to the ignore table */\r
13680      \r
13681           if (lastign < 100)\r
13682           {\r
13683                ignore[lastign] = calloc(strlen(word) + 1,1);\r
13684                if (ignore[lastign])\r
13685                     strcpy(ignore[lastign++],comp);\r
13686                else\r
13687                {\r
13688                     clrscr();\r
13689                     cprintf("No available memory for new words!\r\nPress\r
13690      A key....");\r
13691                     bioskey(0);\r
13692                }\r
13693            }\r
13694            else\r
13695            {\r
13696                clrscr();\r
13697                cprintf("No available memory for new words!\r\nPress A\r
13698      key....");\r
13699                bioskey(0);\r
13700            }\r
13701      }\r
13702      \r
13703      \r
13704      \r
13705      \r
13706      void FATAL(char *text)\r
13707      {\r
13708           /* Fatal error drop out */\r
13709      \r
13710           textcolor(LIGHTGRAY);\r
13711           textbackground(BLACK);\r
13712           window(1,1,80,25);\r
13713           clrscr();\r
13714           printf("SERVILE SOFTWARE\n\nSPELL V1.7\nFATAL ERROR:\r
13715      %s\n\n",text);\r
13716           CURSOR(ON);\r
13717           exit(0);\r
13718      }\r
13719      \r
13720      void FILERR(char *fname)\r
13721      {\r
13722           char text[60];\r
13723      \r
13724           strcpy(text,"Unable To Access: ");\r
13725           strcat(text,fname);\r
13726           FATAL(text);\r
13727      }\r
13728      \r
13729      int COMPARE()\r
13730      {\r
13731           char **p;\r
13732      \r
13733           /* Check Ignore table */\r
13734           for(p = ignore; p <= &ignore[lastign]; p++)\r
13735                if (strcmp(comp,*p) == 0)\r
13736                     return(1);\r
13737      \r
13738           /* Binary search of dictionary file */\r
13739           bp = 0;\r
13740           tp = lastelem;\r
13741           mp = (tp + bp) / 2;\r
13742      \r
13743      \r
13744      \r
13745      \r
13746           while((result = strcmp(dic[mp],comp)) != 0)\r
13747           {\r
13748                if (mp >= tp)\r
13749                {\r
13750                     /* Not found! */\r
13751                     insert = mp;\r
13752                     if (result > 0)\r
13753                          insert--;\r
13754                     return(0);\r
13755                }\r
13756                if (result < 0)\r
13757                     bp = mp + 1;\r
13758                else\r
13759                     tp = mp - 1;\r
13760      \r
13761                mp = (bp + tp) / 2;\r
13762           }\r
13763           return(1);\r
13764      }\r
13765      \r
13766      void INSERT()\r
13767      {\r
13768           int n;\r
13769      \r
13770           changed = 1;\r
13771           lastelem++;\r
13772           n = lastelem;\r
13773      \r
13774           dic[n] = calloc(WORDLEN,1);\r
13775      \r
13776           if (dic[n] == NULL)\r
13777           {\r
13778                clrscr();\r
13779                cprintf("No available memory for new words!\r\nPress A\r
13780      key....");\r
13781                bioskey(0);\r
13782                free(dic[n]);\r
13783                lastelem--;\r
13784                return;\r
13785           }\r
13786      \r
13787           while(n > (insert + 1))\r
13788           {\r
13789                strcpy(dic[n],dic[n-1]);\r
13790                n--;\r
13791           };\r
13792      \r
13793           strcpy(dic[insert + 1],comp);\r
13794      }\r
13795      \r
13796      \r
13797      \r
13798      \r
13799      void SPELL()\r
13800      {\r
13801           FILE *target;\r
13802           FILE *source;\r
13803           char *p;\r
13804           char *x;\r
13805           char temp[256];\r
13806           char dat1[1250];\r
13807           char dat2[1250];\r
13808           int c;\r
13809           int m;\r
13810           int found;\r
13811           int curpos;\r
13812           int key;\r
13813           int row;\r
13814           int col;\r
13815           int srow;\r
13816           int scol;\r
13817      \r
13818           window(1,1,80,20);\r
13819           textcolor(BLACK);\r
13820           textbackground(WHITE);\r
13821      \r
13822           /* Open temporary file to take spell checked copy */\r
13823           target = fopen("spell.$$$","w+");\r
13824      \r
13825           source = fopen(fname,"r");\r
13826      \r
13827           if (source == NULL)\r
13828                FILERR(fname);\r
13829      \r
13830           lastign = 0;\r
13831      \r
13832           do\r
13833           {\r
13834                clrscr();\r
13835      \r
13836                text = dat1;\r
13837      \r
13838                p = text;\r
13839      \r
13840                textsav = dat2;\r
13841      \r
13842                strcpy(text,"");\r
13843      \r
13844                /* Display read text */\r
13845                row = wherey();\r
13846                col = wherex();\r
13847      \r
13848      \r
13849      \r
13850      \r
13851                for(m = 0; m < 15; m++)\r
13852                {\r
13853                     x = fgets(temp,200,source);\r
13854                     if (x)\r
13855                     {\r
13856                          strcat(text,temp);\r
13857                          DISPLAY(temp);\r
13858                     }\r
13859                     if (wherey() > 18)\r
13860                          break;\r
13861                }\r
13862      \r
13863                /* return cursor to start position */\r
13864                gotoxy(col,row);\r
13865      \r
13866                do\r
13867                {\r
13868                     memset(word,32,30);\r
13869                     curpos = 0;\r
13870                     do\r
13871                     {\r
13872                          c = *text++;\r
13873                          if ((isalpha(c)) || (c == '-') && (curpos != 0))\r
13874                               word[curpos++] = c;\r
13875                     }\r
13876                     while(((isalpha(c)) || (c == '-') && (curpos != 0))\r
13877                             && (curpos < 30));\r
13878                     word[curpos] = 0;\r
13879                     strcpy(comp,word);\r
13880                     strupr(comp);\r
13881      \r
13882                     if (*comp != 0)\r
13883                     {\r
13884                          found = COMPARE();\r
13885                          if (!found){\r
13886                               textbackground(RED);\r
13887                               textcolor(WHITE);\r
13888                          }\r
13889                     }\r
13890                     else\r
13891                          found = 1;\r
13892      \r
13893                     srow = wherey();\r
13894                     scol = wherex();\r
13895      \r
13896                     cputs(word);\r
13897                     textbackground(WHITE);\r
13898                     textcolor(BLACK);\r
13899      \r
13900      \r
13901      \r
13902      \r
13903      \r
13904                     switch(c)\r
13905                     {\r
13906                          case '\n': cputs("\r\n");\r
13907                                       break;\r
13908                          case '\t': cputs("       ");\r
13909                                       break;\r
13910                          default: putch(c);\r
13911                     }\r
13912      \r
13913                     row = wherey();\r
13914                     col = wherex();\r
13915      \r
13916                     if (!found)\r
13917                     {\r
13918                          window(1,22,80,24);\r
13919                          clrscr();\r
13920                          cputs("Unknown word ");\r
13921                          textcolor(BLUE);\r
13922                          cprintf("%s ",word);\r
13923                          textcolor(BLACK);\r
13924                          cputs("[A]dd  [I]gnore  [C]orrect  [S]kip");\r
13925                          do\r
13926                          {\r
13927                               key = toupper(getch());\r
13928                               if (key == 27)\r
13929                                    key = 'Q';\r
13930                          }\r
13931                          while(strchr("AICSQ",key) == NULL);\r
13932      \r
13933                          switch(key)\r
13934                          {\r
13935                               case 'A':INSERT();\r
13936                                          break;\r
13937      \r
13938                               case 'C':CORRECT();\r
13939                                          break;\r
13940      \r
13941                               case 'I':IGNORE();\r
13942                                          break;\r
13943                          }\r
13944      \r
13945      \r
13946      \r
13947      \r
13948                          if (key == 'C')\r
13949                          {\r
13950                               clrscr();\r
13951                               gotoxy(1,1);\r
13952                               strcpy(textsav,--text);\r
13953                               /* Delete old word */\r
13954                               text -= strlen(comp);\r
13955                               *text = 0;\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
13961                               text++;\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
13966                               clrscr();\r
13967                               DISPLAY(p);\r
13968                          }\r
13969                          else\r
13970                          {\r
13971                               clrscr();\r
13972                               gotoxy(29,2);\r
13973                               cputs("Checking Spelling....");\r
13974                               window(1,1,80,20);\r
13975                               gotoxy(scol,srow);\r
13976                               cputs(word);\r
13977                          }\r
13978                          window(1,1,80,20);\r
13979                          gotoxy(col,row);\r
13980                     }\r
13981                }\r
13982                while((*text) && (key != 'Q'));\r
13983                fprintf(target,"%s",p);\r
13984           }\r
13985           while((x != NULL) && (key != 'Q'));\r
13986      \r
13987           window(1,22,80,24);\r
13988           clrscr();\r
13989           gotoxy(27,2);\r
13990           cprintf("Writing Updated File....");\r
13991      \r
13992      \r
13993      \r
13994      \r
13995           do\r
13996           {\r
13997                p = fgets(temp,200,source);\r
13998                if (p)\r
13999                     fprintf(target,"%s",temp);\r
14000           }\r
14001           while(p);\r
14002      \r
14003           fclose(target);\r
14004           fclose(source);\r
14005      \r
14006           /* Now transfer spell.$$$ to fname */\r
14007           unlink(fname);\r
14008           rename("SPELL.$$$",fname);\r
14009      }\r
14010      \r
14011      void CORRECT()\r
14012      {\r
14013           /* Locate a good match and return word */\r
14014      \r
14015           char text[51];\r
14016           int m;\r
14017           int n;\r
14018           int key;\r
14019      \r
14020           window(1,22,80,24);\r
14021           clrscr();\r
14022           gotoxy(25,2);\r
14023           cprintf("Searching For Alternatives....");\r
14024      \r
14025           /* Remove any pending key strokes from keyboard buffer */\r
14026           while(kbhit())\r
14027                getch();\r
14028      \r
14029           for(n = 0; n <= lastelem; n++)\r
14030           {\r
14031                if (MATCHSTR(dic[n],comp))\r
14032                {\r
14033                     strcpy(text,dic[n]);\r
14034                     if (strlen(word) <= strlen(text))\r
14035                     {\r
14036                          for (m = 0; m < strlen(word); m++)\r
14037                          {\r
14038                               if (isupper(word[m]))\r
14039                                    text[m] = toupper(text[m]);\r
14040                               else\r
14041                                    text[m] = tolower(text[m]);\r
14042                          }\r
14043      \r
14044      \r
14045      \r
14046      \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
14050                               else\r
14051                                    text[m] = tolower(text[m]);\r
14052                     }\r
14053                     else\r
14054                     {\r
14055                          for (m = 0; m < strlen(text); m++)\r
14056                          {\r
14057                               if (isupper(word[m]))\r
14058                                    text[m] = toupper(text[m]);\r
14059                               else\r
14060                                    text[m] = tolower(text[m]);\r
14061                          }\r
14062                     }\r
14063                     clrscr();\r
14064                     cprintf("Replace ");\r
14065                     textcolor(BLUE);\r
14066                     cprintf("%s ",word);\r
14067                     textcolor(BLACK);\r
14068                     cprintf("With ");\r
14069                     textcolor(BLUE);\r
14070                     cprintf("%s",text);\r
14071                     textcolor(BLACK);\r
14072                     cprintf(" Yes No Continue");\r
14073                     do\r
14074                     {\r
14075                          key = toupper(getch());\r
14076                     }\r
14077                     while(strchr("YNC",key) == NULL);\r
14078                     if (key == 'Y')\r
14079                     {\r
14080                          strcpy(word,text);\r
14081                          return;\r
14082                     }\r
14083                     clrscr();\r
14084                     gotoxy(25,2);\r
14085                     cprintf("Searching For Alternatives....");\r
14086      \r
14087                     /* Remove any pending key strokes from keyboard\r
14088      buffer */\r
14089                     while(kbhit())\r
14090                          getch();\r
14091      \r
14092                     if (key == 'C')\r
14093                          return;\r
14094                }\r
14095           }\r
14096           clrscr();\r
14097           gotoxy(23,2);\r
14098      \r
14099      \r
14100      \r
14101      \r
14102           cprintf("NO ALTERNATIVES FOUND! (Press a key)");\r
14103           bioskey(0);\r
14104           return;\r
14105      }\r
14106      \r
14107      \r
14108      int MATCHSTR(char *src, char *tgt)\r
14109      {\r
14110           /* Compare two words and return non zero if they are similar */\r
14111      \r
14112           int match;\r
14113           int result;\r
14114           int strsrc;\r
14115           int strtgt;\r
14116           int longest;\r
14117      \r
14118           strtgt = strlen(strupr(tgt));\r
14119           strsrc = strlen(strupr(src));\r
14120      \r
14121           longest = max(strtgt,strsrc);\r
14122      \r
14123           match = 0;\r
14124      \r
14125           if(strtgt > strsrc)\r
14126           {\r
14127                for(; *src ; match += (*src++ == *tgt++))\r
14128                     ;\r
14129           }\r
14130           else\r
14131           {\r
14132                for(; *tgt ; match += (*src++ == *tgt++))\r
14133                     ;\r
14134           }\r
14135      \r
14136           result = (match * 100 / longest);\r
14137      \r
14138           /* result holds percentage similarity */\r
14139      \r
14140           if (result > 50)\r
14141                return(1);\r
14142           return(0);\r
14143      }\r
14144      \r
14145      void AT(int row, int col)\r
14146      {\r
14147           /* Position the text cursor */\r
14148           inreg.h.bh = 0;\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
14153      }\r
14154      \r
14155      void WRTCHA (unsigned char ch, unsigned char attrib,  int num)\r
14156      {\r
14157           /* Display a character num times in colour attrib */\r
14158           /* via the BIOS */\r
14159      \r
14160           inreg.h.al = ch;\r
14161           inreg.h.bh = 0;\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
14166      }\r
14167      \r
14168      void SHADE_BLOCK(int left,int top,int right,int bottom)\r
14169      {\r
14170           int c;\r
14171      \r
14172           AT(bottom,right);\r
14173           WRTCHA(223,56,1);\r
14174           AT(top,right);\r
14175           WRTCHA('á',7,1);\r
14176           for (c = top+1; c < bottom; c++)\r
14177           {\r
14178                AT(c,right);\r
14179                WRTCHA(' ',7,1);\r
14180           }\r
14181           AT(bottom,left+1);\r
14182           WRTCHA('\9a',7,right-left);\r
14183      }\r
14184      \r
14185      \r
14186      \r
14187      \r
14188      void BOX(int l, int t, int r, int b)\r
14189      {\r
14190           /* Draws a single line box around a described area */\r
14191      \r
14192           int n;\r
14193           char top[81];\r
14194           char bottom[81];\r
14195           char tolc[5];\r
14196           char torc[5];\r
14197           char bolc[5];\r
14198           char borc[5];\r
14199           char hoor[5];\r
14200      \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
14206      \r
14207                strcpy(top,tolc);\r
14208           strcpy(bottom,bolc);\r
14209           for(n = l + 1; n < r; n++)\r
14210           {\r
14211                strcat(top,hoor);\r
14212                strcat(bottom,hoor);\r
14213           }\r
14214           strcat(top,torc);\r
14215           strcat(bottom,borc);\r
14216      \r
14217           window(1,1,80,25);\r
14218                gotoxy(l,t);\r
14219           cputs(top);\r
14220           for (n = t + 1; n < b; n++)\r
14221           {\r
14222                gotoxy(l,n);\r
14223                putch(179);\r
14224                gotoxy(r,n);\r
14225                putch(179);\r
14226           }\r
14227           gotoxy(l,b);\r
14228           cputs(bottom);\r
14229      }\r
14230      \r
14231      \r
14232      \r
14233      \r
14234      void BANNER()\r
14235      {\r
14236           window (2,2,78,4);\r
14237           textcolor(BLACK);\r
14238           textbackground(GREEN);\r
14239           clrscr();\r
14240           SHADE_BLOCK(1,1,78,4);\r
14241           BOX(2,2,78,4);\r
14242           gotoxy(4,3);\r
14243           cprintf("Servile Software                SPELL CHECKER V1.7\r
14244                      (c)1992");\r
14245      }\r
14246      \r
14247      \r
14248      void main(int argc, char *argv[])\r
14249      {\r
14250           char *p;\r
14251           char tmp_name[160];\r
14252           char tmp_fname[160];\r
14253      \r
14254           if (argc != 2)\r
14255           {\r
14256                puts("\nERROR: Usage is SPELL document");\r
14257                exit(1);\r
14258           }\r
14259           else\r
14260                strcpy(fname,argv[1]);\r
14261      \r
14262           CURSOR(OFF);\r
14263      \r
14264           GETDIC();\r
14265      \r
14266           window(1,22,80,24);\r
14267           clrscr();\r
14268           gotoxy(28,2);\r
14269           cprintf("Making Backup File....");\r
14270      \r
14271           strcpy(tmp_fname,argv[1]);\r
14272      \r
14273           /* Remove extension from tmp_fname */\r
14274           p = strchr(tmp_fname,'.');\r
14275           if(p)\r
14276                *p = 0;\r
14277      \r
14278           /* Create backup file name using DOS */\r
14279           sprintf(tmp_name,"copy %s %s.!s! > NUL",argv[1],tmp_fname);\r
14280      \r
14281           system(tmp_name);\r
14282      \r
14283           window(1,1,80,25);\r
14284      \r
14285      \r
14286      \r
14287      \r
14288           textcolor(WHITE);\r
14289           textbackground(BLACK);\r
14290           clrscr();\r
14291           gotoxy(29,2);\r
14292           cprintf("Checking Spelling....");\r
14293      \r
14294           SPELL();\r
14295      \r
14296           UPDATE();\r
14297           window(1,1,80,25);\r
14298           textcolor(LIGHTGRAY);\r
14299           textbackground(BLACK);\r
14300           clrscr();\r
14301           CURSOR(ON);\r
14302      }\r
14303      \r
14304                                     \r
14305                          APPENDIX A - USING LINK\r
14306 \r
14307 \r
14308 \r
14309 General Syntax:\r
14310 \r
14311      LINK [options] obj[,[exe][,[map][,[lib]]]][;]\r
14312 \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
14321 \r
14322 \r
14323 eg:\r
14324 \r
14325      LINK filea+fileb,myfile,NUL;\r
14326 \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
14330 \r
14331 \r
14332 \r
14333 Using Overlays\r
14334 \r
14335 Overlay .obj modules are specified by encasing the .obj name in\r
14336 parenthesis in the link line.\r
14337 \r
14338 eg:\r
14339 \r
14340      LINK filea + (fileb) + (filec),myfile,NUL;\r
14341 \r
14342 Will link filea.obj fileb.obj and filec.obj with modules fileb.obj and\r
14343 filec.obj as overlay code.\r
14344 \r
14345 \r
14346 Overlay modules must use FAR call/return instructions.\r
14347 \r
14348 \r
14349 Linker Options\r
14350 \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
14354 \r
14355 \r
14356 \r
14357 Pause during Linking (/PAU)\r
14358 \r
14359      Tells LINK to wait before writing the .exe file to disk. LINK\r
14360 displays a\r
14361      message and waits for you to press enter.\r
14362 \r
14363 Display Linker Process Information (/I)\r
14364 \r
14365      Tells LINK to display information about the link process.\r
14366 \r
14367 Pack Executable File (/E)\r
14368 \r
14369      Tells LINK to remove sequences of repeated bytes and to optimise the\r
14370 load-time\r
14371      relocation table before creating the executable file. Symbolic debug\r
14372      information is stripped out of the file.\r
14373 \r
14374 List Public Symbols (/M)\r
14375 \r
14376      Tells LINK to create a list of all public symbols defined in the\r
14377      object files\r
14378      in the MAP file.\r
14379 \r
14380 Include Line Numbers In Map File (/LI)\r
14381 \r
14382      Tells LINK to include line numbers and associated addresses of the\r
14383 source\r
14384      program in the MAP file.\r
14385 \r
14386 Preserve Case Sensitivity (/NOI)\r
14387 \r
14388      By default LINK treats uppercase and lowercase letters as the same.\r
14389 This\r
14390      option tells LINK that they are different.\r
14391 \r
14392 Ignore Default Libraries (/NOD)\r
14393 \r
14394      Tells LINK not to search any library specified in the object files\r
14395 to resolve\r
14396      external references.\r
14397 \r
14398 Controlling Stack Size (/ST:n)\r
14399 \r
14400      Specifies the size of the stack segment where 'n' is the number of\r
14401 bytes.\r
14402 \r
14403 Setting Maximum Allocation Space (/CP:n)\r
14404 \r
14405      Tells LINK to write the parameter 'n' into the exe file header. When\r
14406 the exe\r
14407      file is executed by DOS, 'n' 16 byte paragraphs of memory are\r
14408 reserved. If 'n'\r
14409      is less than the minimum required, it will be set to the minimum.\r
14410 This option\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
14413 use\r
14414      dynamic memory allocation must be linked with this option set to a\r
14415 minimum.\r
14416 \r
14417 Setting Maximum Number Of Segments (/SE:n)\r
14418 \r
14419      Tells LINK how many segments a program is allowed to have. The\r
14420 default is 128\r
14421      but 'n' can be any number between 1 and 3072.\r
14422 \r
14423 \r
14424 Setting Overlay Interrupt (/O:n)\r
14425 \r
14426      Tells LINK which interrupt number will be used for passing control\r
14427 to\r
14428      overlays. The default is 63. Valid values for 'n' are 0 through 255.\r
14429 \r
14430 Ordering Segments (/DO)\r
14431 \r
14432      Tells LINK to use DOS segment ordering. This option is also enabled\r
14433 by the\r
14434      MASM directive .DOSSEG.\r
14435 \r
14436 Controlling Data Loading (/DS)\r
14437 \r
14438      By default LINK loads all data starting at the low end of the data\r
14439 segment. At\r
14440      run time the DS register is set to the lowest possible address to\r
14441 allow the\r
14442      entire data segment to be used. This option tells LINK to load all\r
14443 data\r
14444      starting at the high end of the data segment.\r
14445 \r
14446 Control Exe File Loading (/HI)\r
14447 \r
14448      Tells LINK to place the exe file as high as possible in memory.\r
14449 \r
14450 Prepare for Debugging (/CO)\r
14451 \r
14452      Tells LINK to include symbolic debug information for use by\r
14453 codeview.\r
14454 \r
14455 Optimising Far Calls (/F)\r
14456 \r
14457      Tells LINK to translate FAR calls to NEAR calls where possible. This\r
14458 results\r
14459      in faster code.\r
14460 \r
14461 Disabling Far Call Optimisation (/NOF)\r
14462 \r
14463      Tells LINK not to translate FAR calls. This option is specified by\r
14464 default.\r
14465 \r
14466 Packing Contiguous Segments (/PAC:n)\r
14467 \r
14468      Tells LINK to group together neighbouring code segments, providing\r
14469 more\r
14470      oportunities for FAR call translation. 'n' specifies the maximum\r
14471 size of a\r
14472      segment. By default 'n' is 65530. This option is only relevant to\r
14473 obj files\r
14474      created using FAR calls.\r
14475 \r
14476 Disabling Segment Packing (/NOP)\r
14477 \r
14478      Disables segment packing. This option is specified by default.\r
14479 \r
14480 \r
14481 \r
14482 Using Response Files\r
14483 \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
14486 \r
14487 eg:\r
14488 \r
14489      filea.obj fileb.obj\r
14490      myfile.exe\r
14491      NUL\r
14492      liba.lib libb.lib\r
14493      \r
14494 A response file is specified to LINK by prefixing the response file name\r
14495 with '@'.\r
14496 eg:\r
14497 \r
14498      LINK @response\r
14499 \r