+++ /dev/null
-\r
- \r
- THE IBM PC PROGRAMMER'S GUIDE TO C\r
- \r
- \r
- \r
- 3rd Edition\r
- \r
- \r
- \r
- Matthew Probert\r
-\r
-\r
- \r
- COPYRIGHT NOTICE\r
-\r
-\r
-This publication remains the property of Matthew Probert. License is\r
-hereby given for this work to be freely distibuted in whole under the\r
-proviso that credit is given to the author. Sections of this work may be\r
-used and distributed without payment under the proviso that credit is\r
-given to both this work and the author. Source code occuring in this work\r
-may be used within commercial and non-commercial applications without\r
-charge and without reference to the author.\r
-\r
- BIOGRAPHICAL NOTES\r
-\r
-\r
-Matthew Probert is a software consultant working for his own firm,\r
-Servile Software. He has been involved with micro-computer software\r
-design and programming since the age of eighteen and has been involved\r
-with the C programming language for the past ten years.\r
-\r
-His educational background lies in the non-too distinguished honour of\r
-having a nervous break down during his last year of secondary school\r
-which resulted in a lack of formal qualifications. However, Matthew has\r
-made up for it by achieving a complete recovery and has been studying\r
-Psychology with particular attention to behaviourism and conversation\r
-ever since.\r
-\r
-His chequered career spans back twelve years during which time he has\r
-trained people in the design and use of database management applications,\r
-been called upon to design and implement structured methodologies and has\r
-been a "good old fashioned" analyst programmer.\r
-\r
-Matthew Probert is currently researching Artificial Intelligence with\r
-particular reference to the application of natural language processing,\r
-whereby a computer software package may decode written human language and\r
-respond to it in an intelligent manner. He is also monitoring the\r
-progress of facilitated communication amongst autistic and children with\r
-severe learning and challenging behaviour and hopes one day to be able to\r
-develope a computer based mechanism for true and reliable communication\r
-between autistic people and the rest of society.\r
-\r
-\r
-Matthew Probert can be contacted via\r
- Servile Software\r
- 5 Longcroft Close\r
- Basingstoke\r
- Hampshire\r
- RG21 8XG\r
- England\r
-\r
- Telephone 01256 478576\r
-\r
- \r
- PREFACE\r
-\r
-In 1992, an English software house, Servile Software published a paper\r
-entitled "HOW TO C", which sought to introduce computer programmers to\r
-the C programming language. That paper was written by Matthew Probert. A\r
-follow up effort was "HOW TO CMORE", a document that was also published\r
-by Servile Software. Now those two documents have been amalgamated and\r
-thoroughly revamped to create this book. I have included loads of new\r
-source code that can be lifted directly out of the text.\r
-\r
-All the program listings have been typed in to the Turbo C compiler,\r
-compiled and executed successfully before being imported into this\r
-document.\r
-\r
-I hope you enjoy my work, and more I hope that you learn to program in C.\r
-It really is a great language, there can be no other language that gives\r
-the computer the opportunity to live up to the old saying;\r
-\r
-\r
-"To err is human, to make a complete balls up requires a computer!"\r
-\r
-\r
-\r
-Warning!\r
-\r
-This document is the result of over ten years experience as a software\r
-engineer. This document contains professional source code that is not\r
-intended for beginers.\r
-\r
-\r
- \r
- INTRODUCTION\r
-\r
-\r
-The major distinguishing features of the C programming language are;\r
-\r
- ú block-structured flow-control constructs (typical of most high-level\r
- languages);\r
- ú freedom to manipulate basic machine objects (eg: bytes) and to refer\r
- to them using any particular object view desired (typical of assembly-\r
- languages);\r
- ú both high-level operations (eg: floating-point arithmetic) and low-\r
- level operations (which map closely onto machine-language instructions,\r
- thereby offering the means to code in an optimal, yet portable, manner).\r
-\r
-This book sets out to describe the C programming language, as commonly\r
-found with compilers for the IBM PC, to enable a computer programmer with\r
-no previous knowledge of the C programming language to program in C using\r
-the IBM PC including the ROM facilities provided by the PC and facilities\r
-provided DOS.\r
-\r
-It is assumed that the reader has access to a C compiler, and to the\r
-documentation that accompanies it regarding library functions.\r
-\r
-The example programs were written with Borland's Turbo C, most of the non-\r
-standard facilities provided by Turbo C should be found in later releases\r
-of Microsoft C.\r
-\r
-\r
-\r
-Differences Between the Various Versions of C\r
-\r
-The original C (prior to the definitive book by K&R) defined the\r
-combination assignment operators (eg: +=, *=, etc.) backwards (ie: they\r
-were written =+, =*, etc.). This caused terrible confusion when a\r
-statement such as\r
-\r
- x=-y;\r
-was compiled - it could have meant\r
-\r
- x = x - y;\r
-or\r
-\r
- x = (-y);\r
-Ritchie soon spotted this ambiguity and changed the language to have\r
-these operators written in the now-familiar manner (+=, *=, etc.).\r
-\r
-The major variations, however, are between K&R C and ANSI C. These can\r
-be summarized as follows:\r
-\r
- ú introduction of function prototypes in declarations; change of\r
- function definition preamble to match the style of prototypes;\r
- ú introduction of the ellipsis ("...") to show variable-length\r
- function argument lists;\r
- ú introduction of the keyword `void' (for functions not returning a\r
- value) and the type `void *' for generic pointer variables;\r
- ú addition of string-merging, token-pasting and stringizing functions\r
- in the preprocessor;\r
- ú addition of trigraph translation in the preprocessor;\r
- ú addition of the `#pragma' directive and formalization of the\r
- `declared()' pseudofunction in the preprocessor;\r
- ú introduction of multi-byte strings and characters to support non-\r
- English languages;\r
- ú introduction of the `signed' keyword (to complement the `unsigned'\r
- keyword when used in integer declarations) and the unary plus (`+')\r
- operator.\r
-\r
-\r
-\r
-C is a medium level language\r
-\r
-The powerful facilities offered by C to allow manipulation of direct\r
-memory addresses and data, even down to the bit level, along with C's\r
-structured approach to programming cause C to be classified as a "medium\r
-level" programming language. It possesses fewer ready made facilities\r
-than a high level language, such as BASIC, but a higher level of\r
-structure than low level Assembler.\r
-\r
-\r
-Key words\r
-\r
-The original C language as described in; "The C programming language", by\r
-Kernighan and Ritchie, provided 27 key words. To those 27 the ANSI\r
-standards committee on C have added five more. This confusingly results\r
-in two standards for the C language. However, the ANSI standard is\r
-quickly taking over from the old K & R standard.\r
-\r
-\r
-The 32 C key words are;\r
-\r
-auto double int struct\r
-break else long switch\r
-case enum register typedef\r
-char extern return union\r
-const float short unsigned\r
-continue for signed void\r
-default goto sizeof volatile\r
-do if static while\r
-\r
-Some C compilers offer additional key words specific to the hardware\r
-environment that they operate on. You should be aware of your own C\r
-compilers additional key words. Most notably on the PC these are;\r
-\r
-\r
-near far huge\r
-\r
-\r
-\r
-Structure\r
-\r
-C programs are written in a structured manner. A collection of code\r
-blocks are created that call each other to comprise the complete program.\r
-As a structured language C provides various looping and testing commands\r
-such as;\r
-\r
-\r
- do-while, for, while, if\r
-\r
-and the use of jumps, while provided for, are rarely used.\r
-\r
-A C code block is contained within a pair of curly braces "{ }", and may\r
-be a complete procedure, in C terminology called a "function", or a\r
-subset of code within a function. For example the following is a code\r
-block. The statements within the curly braces are only executed upon\r
-satisfaction of the condition that "x < 10";\r
-\r
-\r
-if (x < 10)\r
-{\r
- a = 1;\r
- b = 0;\r
-}\r
-\r
-while this, is a complete function code block containing a sub code block\r
-as a do-while loop;\r
-\r
-int GET_X()\r
-{\r
- int x;\r
-\r
- do\r
- {\r
- printf("\nEnter a number between 0 and 10 ");\r
- scanf("%d",&x);\r
- }\r
- while(x < 0 || x > 10);\r
- return(x);\r
-}\r
-\r
-Notice how every statement line is terminated in a semicolon, unless that\r
-statement marks the start of a code block, in which case it is followed\r
-by a curly brace. C is a case sensitive but free flow language, spaces\r
-between commands are ignored, and therefore the semicolon delimiter is\r
-required to mark the end of the command line.\r
-\r
-Having a freeflow structure the following commands are recognised as the\r
-same by the C compiler;\r
-\r
-\r
- x = 0;\r
- x =0;\r
- x=0;\r
-\r
-\r
-The general form of a C program is as follows;\r
-\r
- compiler preprocessor statements\r
- global data declarations\r
- \r
- \r
- \r
- \r
- return-type main(parameter list)\r
- {\r
- statements\r
- }\r
- \r
- return-type f1(parameter list)\r
- {\r
- statements\r
- }\r
- \r
- return-type f2(parameter list)\r
- {\r
- statements\r
- }\r
- .\r
- .\r
- .\r
- return-type fn(parameter list)\r
- {\r
- statements\r
- }\r
- \r
-\r
-\r
-Comments\r
-\r
-C allows comments to be included in the program. A comment line is\r
-defined by being enclosed within "/*" and "*/". Thus the following is a\r
-comment;\r
-\r
-\r
-/* This is a legitimate C comment line */\r
-\r
-\r
-Libraries\r
-\r
-C programs are compiled and combined with library functions provided with\r
-the C compiler. These libraries are of generally standard functions, the\r
-functionality of which are defined in the ANSI standard of the C\r
-language, but are provided by the individual C compiler manufacturers to\r
-be machine dependant. Thus, the standard library function "printf()"\r
-provides the same facilities on a DEC VAX as on an IBM PC, although the\r
-actual machine language code in the library is quite different for each.\r
-The C programmer however, does not need to know about the internals of\r
-the libraries, only that each library function will behave in the same\r
-way on any computer.\r
-\r
-\r
- \r
- DATA TYPES\r
-\r
-\r
-There are four basic types of data in the C language; character, integer,\r
-floating point, and valueless that are referred to by the C key words;\r
-\r
-"char", "int", "float" and "void" respectively.\r
-\r
-To the basic data types may be added the type modifiers; signed,\r
-unsigned, long and short to produce further data types. By default data\r
-types are assumed signed, and the signed modifier is rarely used, unless\r
-to overide a compiler switch defaulting a data type to unsigned.\r
-\r
-The size of each data type varies from one hardware platform to another,\r
-but the least range of values that can be held is described in the ANSI\r
-standard as follows;\r
-\r
-\r
-Type Size Range\r
- \r
-char 8 -127 to 127\r
-unsigned char 8 0 to 255\r
-int 16 -32767 to 32767\r
-unsigned int 16 0 to 65535\r
-long int 32 -2147483647 to\r
- 2147483647\r
-unsigned long int 32 0 to 4294967295\r
-float 32 Six digit precision\r
-double 64 Ten digit precision\r
-long double 80 Ten digit precision\r
-\r
-\r
-In practice, this means that the data type `char' is particularly\r
-suitable for storing flag type variables, such as status codes, which\r
-have a limited range of values. The `int' data type can be used, but if\r
-the range of values does not exceed 127 (or 255 for an unsigned char),\r
-then each declared variable would be wasting storage space.\r
-\r
-Which real number data type to use: `float', `double' or `long double' is\r
-another tricky question. When numeric accuracy is required, for example\r
-in an accounting application, the instinct would be to use the `long\r
-double', but this requires at least 10 bytes of storage space for each\r
-variable. And real numbers are not as precise as integers anyway, so\r
-perhaps one should use integer data types instead and work around the\r
-problem. The data type `float' is worse than useless since its six digit\r
-precision is too inaccurate to be relied upon. Generally, then, you\r
-should use integer data types where ever possible, but if real numbers\r
-are required use at least a `double'.\r
-\r
-\r
-Declaring a variable\r
-\r
-All variables in a C program must be declared before they can be used.\r
-The general form of a variable definition is;\r
-\r
-\r
- type name;\r
-\r
-So, for example to declare a variable "x", of data type "int" so that it\r
-may store a value in the range -32767 to 32767, you use the statement;\r
-\r
-\r
- int x;\r
-\r
-Character strings may be declared, which are in reality arrays of\r
-characters. They are declared as follows;\r
-\r
-\r
- char name[number_of_elements];\r
-\r
-So, to declare a string thirty characters long, and called `name' you\r
-would use the declaration;\r
-\r
-\r
- char name[30];\r
-\r
-\r
-Arrays of other data types also may be declared in one, two or more\r
-dimensions in the same way. For example to declare a two dimensional\r
-array of integers;\r
-\r
-\r
- int x[10][10];\r
-\r
-The elements of this array are then accessed as;\r
-\r
- x[0][0]\r
- x[0][1]\r
- x[n][n]\r
-\r
-There are three levels of access to variable; local, module and global. A\r
-variable declared within a code block is only known to the statements\r
-within that code block. A variable declared outside any function code\r
-blocks but prefixed with the storage modifier "static" is known only to\r
-the statements within that source module. A variable declared outside any\r
-functions and not prefixed with the static storage type modifier may be\r
-accessed by any statement within any source module of the program.\r
-\r
-\r
-For example;\r
-\r
- int error;\r
- static int a;\r
- \r
- main()\r
- {\r
- int x;\r
- int y;\r
- \r
- }\r
- \r
- funca()\r
- {\r
- /* Test variable 'a' for equality with 0 */\r
- if (a == 0)\r
- {\r
- int b;\r
- for(b = 0; b < 20; b++)\r
- printf("\nHello World");\r
- }\r
- \r
- }\r
- \r
-\r
-In this example the variable `error' is accessible by all source code\r
-modules compiled together to form the finished program. The variable `a'\r
-is accessible by statements in both functions `main()' and `funca()', but\r
-is invisible to any other source module. Variables `x' and `y' are only\r
-accessible by statements within function `main()'. The variable `b' is\r
-only accessible by statements within the code block following the `if'\r
-statement.\r
-\r
-If a second source module wished to access the variable `error' it would\r
-need to declare `error' as an `extern' global variable thus;\r
-\r
-\r
- extern int error;\r
- \r
- funcb()\r
- {\r
- }\r
- \r
-C will quite happily allow you, the programmer, to assign different data\r
-types to each other. For example, you may declare a variable to be of\r
-type `char' in which case a single byte of data will be allocated to\r
-store the variable. To this variable you can attempt to allocate larger\r
-values, for example;\r
-\r
-\r
- main()\r
- {\r
- \r
- x = 5000;\r
- \r
- }\r
- \r
-In this example the variable `x' can only store a value between -127 and\r
-128, so the figure 5000 will NOT be assigned to the variable `x'. Rather\r
-the value 136 will be assigned!\r
-\r
-\r
-Often you may wish to assign different data types to each other, and to\r
-prevent the compiler from warning you of a possible error you can use a\r
-cast to tell the compiler that you know what you're doing. A cast\r
-statement is a data type in parenthesis preceding a variable or\r
-expression;\r
-\r
-\r
- main()\r
- {\r
- float x;\r
- int y;\r
- \r
- x = 100 / 25;\r
- \r
- y = (int)x;\r
- }\r
-\r
-In this example the (int) cast tells the compiler to convert the value of\r
-the floating point variable x to an integer before assigning it to the\r
-variable y.\r
-\r
-\r
-\r
-Formal parameters\r
-\r
-A C function may receive parameters from a calling function. These\r
-parameters are declared as variables within the parentheses of the\r
-function name, thus;\r
-\r
-\r
- int MULT(int x, int y)\r
- {\r
- /* Return parameter x multiplied by parameter y */\r
- return(x * y);\r
- }\r
- \r
- main()\r
- {\r
- int a;\r
- int b;\r
- int c;\r
- \r
- a = 5;\r
- b = 7;\r
- c = MULT(a,b);\r
- \r
- printf("%d multiplied by %d equals %d\n",a,b,c);\r
- }\r
- \r
-\r
-Access modifiers\r
-\r
-There are two access modifiers; `const' and `volatile'. A variable\r
-declared to be `const' may not be changed by the program, whereas a\r
-variable declared as type as type `volatile' may be changed by the\r
-program. In addition, declaring a variable to be volatile prevents the C\r
-compiler from allocating the variable to a register, and reduces the\r
-optimization carried out on the variable.\r
-\r
-\r
-\r
-Storage class types\r
-C provides four storage types; `extern', `static', `auto' and `register'.\r
-\r
-The extern storage type is used to allow a source module within a C\r
-program to access a variable declared in another source module.\r
-\r
-Static variables are only accessible within the code block that declared\r
-them, and additionally if the variable is local, rather than global, they\r
-retain their old value between subsequent calls to the code block.\r
-\r
-Register variables are stored within CPU registers where ever possible,\r
-providing the fastest possible access to their values.\r
-\r
-The auto type variable is only used with local variables, and declares\r
-the variable to retain its value locally only. Since this is the default\r
-for local variables the auto storage type is very rarely used.\r
- \r
- \r
- \r
- OPERATORS\r
-\r
-Operators are tokens that cause a computation to occur when applied to\r
-variables. C provides the following operators;\r
-\r
-\r
-& Address\r
-* Indirection\r
-+ Unary plus\r
-- Unary minus\r
-~ Bitwise compliment\r
-! Logical negation\r
-++ As a prefix;\r
- preincrement\r
- As a suffix;\r
- postincrement\r
--- As a prefix;\r
- predecrement\r
- As a suffix;\r
- postdecrement\r
-+ Addition\r
-- Subtraction\r
-* Multiply\r
-/ Divide\r
-% Remainder\r
-<< Shift left\r
->> Shift right\r
-& Bitwise AND\r
-| Bitwise OR\r
-^ Bitwise XOR\r
-&& Logical AND\r
-|| Logical OR\r
-= Assignment\r
-*= Assign product\r
-/= Assign quotient\r
-%= Assign remainder\r
- (modulus)\r
-+= Assign sum\r
--= Assign difference\r
-<<= Assign left shift\r
->>= Assign right shift\r
-&= Assign bitwise AND\r
-|= Assign bitwise OR\r
-^= Assign bitwise XOR\r
-< Less than\r
-> Greater than\r
-<= Less than or equal\r
- to\r
->= Greater than or\r
- equal to\r
-== Equal to\r
-!= Not equal to\r
-. Direct component\r
- selector\r
--> Indirect component\r
- selector\r
-a ? x:y "If a is true then\r
- x else y"\r
-[] Define arrays\r
-() Parenthesis\r
- isolate conditions\r
- and expressions\r
-... Ellipsis are used\r
- in formal\r
- parameter lists of\r
- function\r
- prototypes to show\r
- a variable number\r
- of\r
- parameters or\r
- parameters of\r
- varying types.\r
-\r
-To illustrate some more commonly used operators consider the following\r
-short program;\r
-\r
-\r
- main()\r
- {\r
- int a;\r
- int b;\r
- int c;\r
- a = 5; /* Assign a value of 5 to variable 'a' */\r
- b = a / 2; /* Assign the value of 'a' divided by two to\r
- variable 'b' */\r
- c = b * 2; /* Assign the value of 'b' multiplied by two\r
- to variable 'c' */\r
- \r
- if (a == c) /* Test if 'a' holds the same value as 'c' */\r
- \r
- puts("Variable 'a' is an even number");\r
- else\r
- puts("Variable 'a' is an odd number");\r
- }\r
- \r
- \r
-Normally when incrementing the value of a variable you would write\r
-something like;\r
-\r
- x = x + 1\r
-\r
-C provides the incremental operator '++' as well so that you can write;\r
-\r
- x++\r
-\r
-Similarly you can decrement the value of a variable using '--' as;\r
-\r
- x--\r
-\r
-All the other mathematical operators may be used the same, so in a C\r
-program you can write in shorthand;\r
-\r
-\r
-NORMAL C\r
- \r
-x = x + 1 x++\r
-x = x - 1 x--\r
-x = x * 2 x *= 2\r
-x = x / y x /= y\r
-x = x % 5 x %= 5\r
-\r
-and so on.\r
- \r
- \r
- \r
- FUNCTIONS\r
-\r
-Functions are the source code procedures that comprise a C program. They\r
-follow the general form;\r
-\r
- return_type function_name(parameter_list)\r
- {\r
- statements\r
- }\r
-\r
-\r
-The return_type specifies the data type that will be returned by the\r
-function; char, int, double, void &c.\r
-\r
-The code within a C function is invisible to any other C function, and\r
-jumps may not be made from one function into the middle of another,\r
-although functions may call other functions. Also, functions cannot be\r
-defined within functions, only within source modules.\r
-\r
-Parameters may be passed to a function either by value, or by reference.\r
-If a parameter is passed by value, then only a copy of the current value\r
-of the parameter is passed to the function. A parameter passed by\r
-reference however, is a pointer to the actual parameter that may then be\r
-changed by the function.\r
-\r
-\r
-The following example passes two parameters by value to a function,\r
-funca(), which attempts to change the value of the variables passed to\r
-it. And then passes the same two parameters by reference to funcb() which\r
-also attempts to modify their values.\r
-\r
-\r
- #include <stdio.h>\r
- \r
- int funca(int x, int y)\r
- {\r
- /* This function receives two parameters by value, x and y */\r
- \r
- x = x * 2;\r
- y = y * 2;\r
- \r
- printf("\nValue of x in funca() %d value of y in funca()\r
- %d",x,y);\r
- \r
- return(x);\r
- }\r
- \r
- \r
- int funcb(int *x, int *y)\r
- {\r
- /* This function receives two parameters by reference, x and y\r
- */\r
- \r
- *x = *x * 2;\r
- *y = *y * 2;\r
- \r
- printf("\nValue of x in funcb() %d value of y in funcb()\r
- %d",*x,*y);\r
- \r
- return(*x);\r
- }\r
- \r
- main()\r
- {\r
- int x;\r
- int y;\r
- int z;\r
- \r
- x = 5;\r
- y = 7;\r
- \r
- z = funca(x,y);\r
- z = funcb(&x,&y);\r
- \r
- printf("\nValue of x %d value of y %d value of z %d",x,y,z);\r
- }\r
- \r
-\r
-Actually funcb() does not change the values of the parameters it\r
-receives. Rather it changes the contents of the memory addresses pointed\r
-to by the received parameters. While funca() receives the values of\r
-variables `x' and `y' from function main(), funcb() receives the memory\r
-addresses of the variables `x' and `y' from function main().\r
-\r
-\r
-\r
-Passing an array to a function\r
-\r
-The following program passes an array to a function, funca(), which\r
-initialises the array elements;\r
-\r
- #include <stdio.h>\r
- \r
- void funca(int x[])\r
- {\r
- int n;\r
- \r
- for(n = 0; n < 100; n++)\r
- x[n] = n;\r
- }\r
- \r
- main()\r
- {\r
- int array[100];\r
- int counter;\r
- \r
- funca(array);\r
- \r
- for(counter = 0; counter < 100; counter++)\r
- printf("\nValue of element %d is\r
- %d",counter,array[counter]);\r
- }\r
-\r
-The parameter of funca() `int x[]' is declared to be an array of any\r
-length. This works because the compiler passes the address of the start\r
-of the array parameter to the function, rather than the value of the\r
-individual elements. This does of course mean that the function can\r
-change the value of the array elements. To prevent a function from\r
-changing the values you can specify the parameter as type `const';\r
-\r
-\r
- funca(const int x[])\r
- {\r
- }\r
-\r
-This will then generate a compiler error at the line that attempts to\r
-write a value to the array. However, specifying a parameter to be const\r
-does not protect the parameter from indirect assignment as the following\r
-program illustrates;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- int funca(const int x[])\r
- {\r
- int *ptr;\r
- int n;\r
- \r
- /* This line gives a 'suspicious pointer conversion warning' */\r
- /* because x is a const pointer, and ptr is not */\r
- ptr = x;\r
- \r
- for(n = 0; n < 100; n++)\r
- {\r
- *ptr = n;\r
- ptr++;\r
- }\r
- }\r
- \r
- main()\r
- {\r
- int array[100];\r
- int counter;\r
- \r
- funca(array);\r
- \r
- for(counter = 0; counter < 100; counter++)\r
- printf("\nValue of element %d is\r
- %d",counter,array[counter]);\r
- }\r
- \r
-\r
-\r
-Passing parameters to main()\r
-\r
-C allows parameters to be passed from the operating system to the program\r
-when it starts executing through two parameters; argc and argv[], as\r
-follows;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- main(int argc, char *argv[])\r
- {\r
- int n;\r
- \r
- for(n = 0; n < argc; n++)\r
- printf("\nParameter %d equals %s",n,argv[n]);\r
- }\r
- \r
-\r
-Parameter argc holds the number of parameters passed to the program, and\r
-the array argv[] holds the addresses of each parameter passed. argv[0] is\r
-always the program name. This feature may be put to good use in\r
-applications that need to access system files. Consider the following\r
-scenario:\r
-\r
-A simple database application stores its data in a single file called\r
-"data.dat". The application needs to be created so that it may be stored\r
-in any directory on either a floppy diskette or a hard disk, and executed\r
-both from within the host directory and through a DOS search path. To\r
-work correctly the application must always know where to find the data\r
-file; "data.dat". This is solved by assuming that the data file will be\r
-in the same directory as the executable module, a not unreasonable\r
-restriction to place upon the operator. The following code fragment then\r
-illustrates how an application may apply this algorithm into practice to\r
-be always able to locate a desired system file:\r
-\r
-\r
- #include <string.h>\r
- \r
- char system_file_name[160];\r
- \r
- void main(int argc,char *argv[])\r
- {\r
- char *data_file = "DATA.DAT";\r
- char *p;\r
- \r
- strcpy(system_file_name,argv[0]);\r
- p = strstr(system_file_name,".EXE");\r
- if (p == NULL)\r
- {\r
- /* The executable is a .COM file */\r
- p = strstr(system_file_name,".COM");\r
- }\r
- \r
- /* Now back track to the last '\' character in the file name */\r
- while(*(p - 1) != '\\')\r
- p--;\r
- \r
- strcpy(p,data_file);\r
- }\r
-\r
-In practice this code creates a string in system_file_name that is\r
-comprised of path\data.dat, so if for example the executable file is\r
-called "test.exe" and resides in the directory \borlandc, then\r
-system_file_name will be assigned with: \borlandc\data.dat\r
-\r
-\r
-\r
-Returning from a function\r
-\r
-The command `return' is used to return immediately from a function. If\r
-the function was declared with a return data type, then return should be\r
-used with a parameter of the same data type.\r
-\r
-\r
-\r
-Function prototypes\r
-\r
-Prototypes for functions allow the C compiler to check that the type of\r
-data being passed to and from functions is correct. This is very\r
-important to prevent data overflowing its allocated storage space into\r
-other variables areas.\r
-\r
-A function prototype is placed at the beginning of the program, after any\r
-preprocessor commands, such as #include <stdio.h>, and before the\r
-declaration of any functions.\r
- \r
- THE C PREPROCESSOR\r
-\r
-C allows for commands to the compiler to be included in the source code.\r
-These commands are then called preprocessor commands and are defined by\r
-the ANSI standard to be;\r
-\r
-\r
- #if\r
- #ifdef\r
- #ifndef\r
- #else\r
- #elif\r
- #include\r
- #define\r
- #undef\r
- #line\r
- #error\r
- #pragma\r
-\r
-All preprocessor commands start with a hash symbol, "#", and must be on a\r
-line on their own (although comments may follow).\r
-\r
-\r
-\r
-#define\r
-\r
-The #define command specifies an identifier and a string that the\r
-compiler will substitute every time it comes accross the identifier\r
-within that source code module. For example;\r
-\r
-\r
-#define FALSE 0\r
-#define TRUE !FALSE\r
-\r
-The compiler will replace any subsequent occurence of `FALSE' with `0'\r
-and any subsequent occurence of `TRUE' with `!0'. The substitution does\r
-NOT take place if the compiler finds that the identifier is enclosed by\r
-quotation marks, so\r
-\r
-\r
- printf("TRUE");\r
-\r
-would NOT be replaced, but\r
-\r
- printf("%d",FALSE);\r
-\r
-would be.\r
-\r
-The #define command also can be used to define macros that may include\r
-parameters. The parameters are best enclosed in parenthesis to ensure\r
-that correct substitution occurs.\r
-\r
-\r
-This example declares a macro `larger()' that accepts two parameters and\r
-returns the larger of the two;\r
-\r
- #include <stdio.h>\r
- \r
- #define larger(a,b) (a > b) ? (a) : (b)\r
- \r
- int main()\r
- {\r
- printf("\n%d is largest",larger(5,7));\r
- \r
- }\r
-\r
-\r
-#error\r
-\r
-The #error command causes the compiler to stop compilation and to display\r
-the text following the #error command. For example;\r
-\r
-\r
-#error REACHED MODULE B\r
-\r
-will cause the compiler to stop compilation and display;\r
-\r
- REACHED MODULE B\r
-\r
-\r
-\r
-#include\r
-\r
-The #include command tells the compiler to read the contents of another\r
-source file. The name of the source file must be enclosed either by\r
-quotes or by angular brackets thus;\r
-\r
-\r
- #include "module2.c"\r
- #include <stdio.h>\r
-\r
-Generally, if the file name is enclosed in angular brackets, then the\r
-compiler will search for the file in a directory defined in the\r
-compiler's setup. Whereas if the file name is enclosed in quotes then the\r
-compiler will look for the file in the current directory.\r
-\r
-\r
-\r
-#if, #else, #elif, #endif\r
-\r
-The #if set of commands provide conditional compilation around the\r
-general form;\r
-\r
- #if constant_expression\r
- statements\r
- #else\r
- statements\r
- #endif\r
-\r
-#elif stands for '#else if' and follows the form;\r
-\r
- #if expression\r
- statements\r
- #elif expression\r
- statements\r
- #endif\r
-\r
-\r
-\r
-#ifdef, #ifndef\r
-\r
-These two commands stand for '#if defined' and '#if not defined'\r
-respectively and follow the general form;\r
-\r
- #ifdef macro_name\r
- statements\r
- #else\r
- statements\r
- #endif\r
-\r
- #ifndef macro_name\r
- statements\r
- #else\r
- statements\r
- #endif\r
-\r
-where 'macro_name' is an identifier declared by a #define statement.\r
-\r
-\r
-\r
-#undef\r
-\r
-Undefines a macro previously defined by #define.\r
-\r
-\r
-#line\r
-\r
-Changes the compiler declared global variables __LINE__ and __FILE__. The\r
-general form of #line is;\r
-\r
- #line number "filename"\r
-\r
-where number is inserted into the variable '__LINE__' and 'filename' is\r
-assigned to '__FILE__'.\r
-\r
-\r
-#pragma\r
-\r
-This command is used to give compiler specific commands to the compiler.\r
-The compiler's manual should give you full details of any valid options\r
-to go with the particular implementation of #pragma that it supports.\r
- \r
- PROGRAM CONTROL STATEMENTS\r
-\r
-As with any computer language, C includes statements that test the\r
-outcome of an expression. The outcome of the test is either TRUE or\r
-FALSE. The C language defines a value of TRUE as non-zero, and FALSE as\r
-zero.\r
-\r
-\r
-\r
-Selection statements\r
-\r
-The general purpose selection statement is "if" that follows the general\r
-form;\r
-\r
- if (expression)\r
- statement\r
- else\r
- statement\r
-\r
-Where "statement" may be a single statement, or a code block enclosed in\r
-curly braces. The "else" is optional. If the result of the expression\r
-equates to TRUE, then the statement(s) following the if() will be\r
-evaluated. Otherwise the statement(s) following the else, if there is\r
-one, will be evaluated.\r
-\r
-\r
-An alternative to the if....else combination is the ?: command that takes\r
-the form;\r
-\r
-\r
- expression ? true_expression : false_expression\r
-\r
-Where if the expression evaluates to TRUE, then the true_expression will\r
-be evaluated, otherwise the false_expression will be evaluated. Thus we\r
-get;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int x;\r
- \r
- x = 6;\r
- \r
- printf("\nx is an %s number", x % 2 == 0 ? "even" : "odd");\r
- }\r
- \r
-C also provides a multiple branch selection statement, switch, which\r
-successively tests a value of an expression against a list of values and\r
-branches program execution to the first match found. The general form of\r
-switch is;\r
-\r
-\r
- switch (expression)\r
- {\r
- case value1 : statements\r
- break;\r
- case value2 : statements\r
- break;\r
- .\r
- .\r
- .\r
- .\r
- case valuen : statements\r
- break;\r
- default : statements\r
- }\r
-\r
-The break statement is optional, but if omitted, program execution will\r
-continue down the list.\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int x;\r
- \r
- x = 6;\r
- \r
- switch(x)\r
- {\r
- case 0 : printf("\nx equals zero");\r
- break;\r
- case 1 : printf("\nx equals one");\r
- break;\r
- case 2 : printf("\nx equals two");\r
- break;\r
- case 3 : printf("\nx equals three");\r
- break;\r
- default : printf("\nx is larger than three");\r
- }\r
- }\r
-\r
-Switch statements may be nested within one another. This is a\r
-particularly useful feature for confusing people who read your source\r
-code!\r
-\r
-\r
-Iteration statements\r
-C provides three looping or iteration statements; for, while, and do-\r
-while. The for loop has the general form;\r
-\r
-\r
- for(initialization;condition;increment)\r
-\r
-and is useful for counters such as in this example that displays the\r
-entire ascii character set;\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int x;\r
- \r
- for(x = 32; x < 128; x++)\r
- printf("%d\t%c\t",x,x);\r
- }\r
- \r
-An infinite for loop is also quite valid;\r
-\r
- for(;;)\r
- {\r
- statements\r
- }\r
-\r
-Also, C allows empty statements. The following for loop removes leading\r
-spaces from a string;\r
-\r
- for(; *str == ' '; str++)\r
- ;\r
-\r
-Notice the lack of an initializer, and the empty statement following the\r
-loop.\r
-\r
-The while loop is somewhat simpler than the for loop and follows the\r
-general form;\r
-\r
- while (condition)\r
- statements\r
-\r
-The statement following the condition, or statements enclosed in curly\r
-braces will be executed until the condition is FALSE. If the condition is\r
-false before the loop commences, the loop statements will not be\r
-executed. The do-while loop on the other hand is always executed at least\r
-once. It takes the general form;\r
-\r
-\r
- do\r
- {\r
- statements\r
- }\r
- while(condition);\r
-\r
-\r
-Jump statements\r
-\r
-The "return" statement is used to return from a function to the calling\r
-function. Depending upon the declared return data type of the function it\r
-may or may not return a value;\r
-\r
-\r
- int MULT(int x, int y)\r
- {\r
- return(x * y);\r
- }\r
-\r
-or;\r
-\r
- void FUNCA()\r
- {\r
- printf("\nHello World");\r
- return;\r
- }\r
-\r
-The "break" statement is used to break out of a loop or from a switch\r
-statement. In a loop it may be used to terminate the loop prematurely, as\r
-shown here;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int x;\r
- \r
- for(x = 0; x < 256; x++)\r
- {\r
- if (x == 100)\r
- break;\r
- \r
- printf("%d\t",x);\r
- }\r
- }\r
-\r
-In contrast to "break" is "continue", which forces the next iteration of\r
-the loop to occur, effectively forcing program control back to the loop\r
-statement.\r
-\r
-\r
-C provides a function for terminating the program prematurely, "exit()".\r
-Exit() may be used with a return value to pass back to the calling\r
-program;\r
-\r
-\r
- exit(return_value);\r
-\r
-\r
-More About ?:\r
-\r
-\r
-A powerful, but often misunderstood feature of the C programming language\r
-is ?:. This is an operator that acts upon a boolean expression, and\r
-returns one of two values dependant upon the result of the expression;\r
-\r
-\r
- <boolean expression> ? <value for true> : <value for false>\r
-\r
-It can be used almost anywhere, for example it was used in the binary\r
-search demonstration program;\r
-\r
- printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");\r
-\r
-Here it passes either "Not found" or "Located okay" to the printf()\r
-function dependant upon the outcome of the boolean expression `result ==\r
-0'. Alternatively it can be used for assigning values to a variable;\r
-\r
- x = (a == 0) ? (b) : (c);\r
-\r
-Which will assign the value of b to variable x if a is equal to zero,\r
-otherwise it will assign the value of c to variable x.\r
-\r
-This example returns the name of the executing program, without any path\r
-description;\r
-\r
- #include <stdio.h>\r
- #include <stddef.h>\r
- #include <string.h>\r
- \r
- char *progname(char *pathname)\r
- {\r
- /* Return name of running program */\r
- unsigned l;\r
- char *p;\r
- char *q;\r
- static char bnbuf[256];\r
- \r
- return pathname? p = strrchr (pathname, '\\'),\r
- q = strrchr (pathname, '.'),\r
- l = (q == NULL? strchr (pathname, '\0'): q)\r
- - (p == NULL? p = pathname: ++p),\r
- strncpy (bnbuf, p, l),\r
- bnbuf[l] = '\0',\r
- strlwr (bnbuf)\r
- : NULL;\r
- }\r
- \r
- void main(int argc, char *argv[])\r
- {\r
- printf("\n%s",progname(argv[0]));\r
- }\r
- \r
- \r
-\r
-Continue\r
-\r
-The continue keyword forces control to jump to the test statement of the\r
-innermost loop (while, do...while()). This can be useful for terminating\r
-a loop gracefuly as this program that reads strings from a file until\r
-there are no more illustrates;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- FILE *fp;\r
- char *p;\r
- char buff[100];\r
- \r
- fp = fopen("data.txt","r");\r
- if (fp == NULL)\r
- {\r
- fprintf(stderr,"Unable to open file data.txt");\r
- exit(0);\r
- }\r
- \r
- do\r
- {\r
- p = fgets(buff,100,fp);\r
- if (p == NULL)\r
- /* Force exit from loop */\r
- continue;\r
- puts(p);\r
- }\r
- while(p);\r
- }\r
- \r
-With a for() loop however, continue passes control back to the third\r
-parameter!\r
- \r
- INPUT AND OUTPUT\r
-\r
-\r
-\r
-Input\r
-Input to a C program may occur from the console, the standard input\r
-device (unless otherwise redirected this is the console), from a file or\r
-from a data port.\r
-\r
-The general input command for reading data from the standard input stream\r
-`stdin' is scanf(). Scanf() scans a series of input fields, one character\r
-at a time. Each field is then formatted according to the appropriate\r
-format specifier passed to the scanf() function as a parameter. This\r
-field is then stored at the ADDRESS passed to scanf() following the\r
-format specifiers list.\r
-\r
-For example, the following program will read a single integer from the\r
-stream stdin;\r
-\r
-\r
- main()\r
- {\r
- int x;\r
- \r
- scanf("%d",&x);\r
- }\r
-\r
-Notice the address operator & prefixing the variable name `x' in the\r
-scanf() parameter list. This is because scanf() stores values at\r
-ADDRESSES rather than assigning values to variables directly.\r
-\r
-The format string is a character string that may contain three types of\r
-data:\r
-\r
-whitespace characters (space, tab and newline), non-whitespace characters\r
-(all ascii characters EXCEPT %) and format specifiers.\r
-\r
-Format specifiers have the general form;\r
-\r
-\r
- %[*][width][h|l|L]type_character\r
-\r
-After the % sign the format specifier is comprised of:\r
-\r
- an optional assignment suppression character, *, which suppresses\r
- assignment of the next input field.\r
- \r
- an optional width specifier, width, which declares the maximum number\r
- of characters to be read.\r
- \r
- an optional argument type modifier, h or l or L, where:\r
- h is a short integer\r
- l is a long\r
- L is a long double\r
-\r
- the data type character that is one of;\r
-\r
-\r
-d Decimal integer\r
-D Decimal long integer\r
-o Octal integer\r
-O Octal long integer\r
-i Decimal, octal or hexadecimal integer\r
-I Decimal, octal or hexadecimal long\r
- integer\r
-u Decimal unsigned integer\r
-U Decimal unsigned long integer\r
-x Hexadecimal integer\r
-X Hexadecimal long integer\r
-e Floating point\r
-f Floating point\r
-g Floating point\r
-s Character string\r
-c Character\r
-% % is stored\r
-\r
-An example using scanf();\r
-\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- char name[30];\r
- int age;\r
- \r
- printf("\nEnter your name and age ");\r
- scanf("%30s%d",name,&age);\r
- printf("\n%s %d",name,age);\r
- }\r
-\r
-Notice the include line, "#include <stdio.h>", this is to tell the\r
-compiler to also read the file stdio.h that contains the function\r
-prototypes for scanf() and printf().\r
-\r
-\r
-If you type in and run this sample program you will see that only one\r
-name can be entered, that is you can't enter;\r
-\r
-\r
- JOHN SMITH\r
-\r
-because scanf() detects the whitespace between "JOHN" and "SMITH" and\r
-moves on to the next input field, which is age, and attempts to assign\r
-the value "SMITH" to the age field! The limitations of scanf() as an\r
-input function are obvious.\r
-\r
-An alternative input function is gets() that reads a string of characters\r
-from the stream stdin until a newline character is detected. The newline\r
-character is replaced by a null (0 byte) in the target string. This\r
-function has the advantage of allowing whitespace to be read in. The\r
-following program is a modification to the earlier one, using gets()\r
-instead of scanf().\r
-\r
-\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- #include <string.h>\r
- \r
- main()\r
- {\r
- char data[80];\r
- char *p;\r
- char name[30];\r
- int age;\r
- \r
- printf("\nEnter your name and age ");\r
- /* Read in a string of data */\r
- gets(data);\r
- \r
- \r
- /* P is a pointer to the last character in the input string */\r
- p = &data[strlen(data) - 1];\r
- \r
- /* Remove any trailing spaces by replacing them with null bytes\r
- */\r
- while(*p == ' '){\r
- *p = 0;\r
- p--;\r
- }\r
- \r
- /* Locate last space in the string */\r
- p = strrchr(data,' ');\r
- \r
- /* Read age from string and convert to an integer */\r
- age = atoi(p);\r
- \r
- /* Terminate data string at start of age field */\r
- *p = 0;\r
- \r
- /* Copy data string to name variable */\r
- strcpy(name,data);\r
- \r
- /* Display results */\r
- printf("\nName is %s age is %d",name,age);\r
- }\r
-\r
-\r
-Output\r
-The most common output function is printf() that is very similar to\r
-scanf() except that it writes formatted data out to the standard output\r
-stream stdout. Printf() takes a list of output data fields and applies\r
-format specifiers to each and outputs the result. The format specifiers\r
-are the same as for scanf() except that flags may be added to the format\r
-specifiers. These flags include;\r
-\r
-\r
- - Which left justifies the output padding to the right with spaces.\r
- + Which causes numbers to be prefixed by their sign\r
-\r
-The width specifier is also slightly different for printf(). In its most\r
-useful form is the precision specifier;\r
-\r
-\r
- width.precision\r
-\r
-So, to print a floating point number to three decimal places you would\r
-use;\r
-\r
- printf("%.3f",x);\r
-\r
-And to pad a string with spaces to the right or truncate the string to\r
-twenty characters if it is longer, to form a fixed twenty character\r
-width;\r
-\r
- printf("%-20.20s",data);\r
-\r
-Special character constants may appear in the printf() parameter list.\r
-These are;\r
-\r
-\n Newline\r
-\r Carriage return\r
-\t Tab\r
-\b Sound the computer's bell\r
-\f Formfeed\r
-\v Vertical tab\r
-\\ Backslash character\r
-\' Single quote\r
-\" Double quote\r
-\? Question mark\r
-\O Octal string\r
-\x Hexadecimal string\r
-\r
-Thus, to display "Hello World", surrounded in quotation marks and\r
-followed by a newline you would use;\r
-\r
- printf("\"Hello World\"\n");\r
-\r
-The following program shows how a decimal integer may be displayed as a\r
-decimal, hexadecimal or octal integer. The 04 following the % in the\r
-printf() format tells the compiler to pad the displayed figure to a width\r
-of at least four digits padded with leading zeroes if required.\r
-\r
-\r
- /* A simple decimal to hexadecimal and octal conversion program */\r
- \r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int x;\r
- \r
- do\r
- {\r
- printf("\nEnter a number, or 0 to end ");\r
- scanf("%d",&x);\r
- printf("%04d %04X %04o",x,x,x);\r
- }\r
- while(x != 0);\r
- \r
- }\r
- \r
-There are associated functions to printf() that you should be aware of.\r
-fprintf() has the prototype;\r
-\r
- fprintf(FILE *fp,char *format[,argument,...]);\r
-\r
-This variation on printf() simply sends the formatted output to the\r
-specified file stream.\r
-\r
-sprintf() has the function prototype;\r
-\r
- sprintf(char *s,char *format[,argument,...]);\r
-\r
-and writes the formatted output to a string. You should take care using\r
-sprintf() that the target string has been declared long enough to hold\r
-the result of the sprintf() output. Otherwise other data will be\r
-overwritten in memory.\r
-\r
-An example of using sprintf() to copy multiple data to a string;\r
-\r
- #include<stdio.h>\r
- \r
- int main()\r
- {\r
- char buffer[50];\r
- \r
- sprintf(buffer,"7 * 5 == %d\n",7 * 5);\r
- \r
- puts(buffer);\r
- }\r
- \r
-\r
-An alternative to printf() for outputting a simple string to the stream\r
-stdout is puts(). This function sends a string to the stream stdout\r
-followed by a newline character. It is faster than printf(), but far less\r
-flexible. Instead of;\r
-\r
- printf("Hello World\n");\r
-\r
-You can use;\r
-\r
- puts("Hello World");\r
-\r
-\r
-\r
-Direct Console I/O\r
-Data may be sent to, and read directly from the console (keyboard and\r
-screen) using the direct console I/O functions. These functions are\r
-prefixed `c'. The direct console I/O equivalent of printf() is then\r
-cprintf(), and the equivalent of puts() is cputs(). Direct console I/O\r
-functions differ from standard I?o functions:\r
-\r
- 1. They do not make use of the predefined streams, and hence may not be\r
- redirected\r
- 2. They are not portable across operating systems (for example you cant\r
- use direct console I/O functions in a Windows programme).\r
- 3. They are faster than their standard I/O equivalents\r
- 4. They may not work with all video modes (especially VESA display\r
- modes).\r
- \r
- \r
- \r
- POINTERS\r
-\r
-A pointer is a variable that holds the memory address of an item of data.\r
-Therefore it points to another item. A pointer is declared like an\r
-ordinary variable, but its name is prefixed by `*', thus;\r
-\r
-\r
- char *p;\r
-\r
-This declares the variable `p' to be a pointer to a character variable.\r
-Pointers are very powerful, and similarly dangerous! If only because a\r
-pointer can be inadvertently set to point to the code segment of a\r
-program and then some value assigned to the address of the pointer!\r
-\r
-The following program illustrates a simple, though fairly useless\r
-application of a pointer;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int a;\r
- int *x;\r
- \r
- /* x is a pointer to an integer data type */\r
- \r
- a = 100;\r
- x = &a;\r
- \r
- printf("\nVariable 'a' holds the value %d at memory address\r
- %p",a,x);\r
- }\r
-\r
-Here is a more useful example of a pointer illustrating how because the\r
-compiler knows the type of data pointed to by the pointer, when the\r
-pointer is incremented it is incremented the correct number of bytes for\r
-the data type. In this case two bytes;\r
-\r
- #include <stdio.h>\r
- \r
- main()\r
- {\r
- int n;\r
- int a[25];\r
- int *x;\r
- \r
- /* x is a pointer to an integer data type */\r
- \r
- /* Assign x to point to array element zero */\r
- x = a;\r
- \r
- /* Assign values to the array */\r
- for(n = 0; n < 25; n++)\r
- a[n] = n;\r
- \r
- \r
- /* Now print out all array element values */\r
- for(n = 0; n < 25; n++ , x++)\r
- printf("\nElement %d holds %d",n,*x);\r
- }\r
- \r
-To read or assign a value to the address held by a pointer you use the\r
-indirection operator `*'. Thus in the above example, to print the value\r
-at the memory address pointed to by variable x I have used `*x'.\r
-\r
-Pointers may be incremented and decremented and have other mathematics\r
-applied to them also. For example in the above program to move variable x\r
-along the array one element at a time we put the statement `x++' in the\r
-for loop. We could move x along two elements by saying `x += 2'. Notice\r
-that this doesn't mean "add 2 bytes to the value of x", but rather it\r
-means "add 2 of the pointer's data type size units to the value of x".\r
-\r
-Pointers are used extensively in dynamic memory allocation. When a\r
-program is running it is often necessary to temporarily allocate a block\r
-of data, say a table, in memory. C provides the function malloc() for\r
-this purpose that follows the general form;\r
-\r
-\r
- any pointer type = malloc(number_of_bytes);\r
-\r
-malloc() actually returns a void pointer type, which means it can be any\r
-type; integer, character, floating point or whatever. This example\r
-allocates a table in memory for 1000 integers;\r
-\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- \r
- main()\r
- {\r
- int *x;\r
- int n;\r
- \r
- /* x is a pointer to an integer data type */\r
- \r
- /* Create a 1000 element table, sizeof() returns the compiler\r
- */\r
- /* specific number of bytes used to store an integer */\r
- \r
- x = malloc(1000 * sizeof(int));\r
- \r
- \r
- /* Check to see if the memory allocation succeeded */\r
- if (x == NULL)\r
- {\r
- printf("\nUnable to allocate a 1000 element integer\r
- table");\r
- exit(0);\r
- }\r
- \r
- /* Assign values to each table element */\r
- for(n = 0; n < 1000; n++)\r
- {\r
- *x = n;\r
- x++;\r
- }\r
- \r
- /* Return x to the start of the table */\r
- x -= 1000;\r
- \r
- /* Display the values in the table */\r
- for(n = 0; n < 1000; n++){\r
- printf("\nElement %d holds a value of %d",n,*x);\r
- x++;\r
- }\r
- /* Deallocate the block of memory now it's no longer required\r
- */\r
- free(x);\r
- }\r
- \r
-Pointers are also extensively used with character arrays, called strings.\r
-Since all C program strings are terminated by a zero byte we can count\r
-the letters in a string using a pointer;\r
-\r
- #include <stdio.h>\r
- #include <string.h>\r
- \r
- main()\r
- {\r
- char *p;\r
- char text[100];\r
- int len;\r
- \r
- /* Initialise variable 'text' with some writing */\r
- strcpy(text,"This is a string of data");\r
- \r
- /* Set variable p to the start of variable text */\r
- p = text;\r
- \r
- /* Initialise variable len to zero */\r
- len = 0;\r
- \r
- /* Count the characters in variable text */\r
- while(*p)\r
- {\r
- len++;\r
- p++;\r
- }\r
- \r
- /* Display the result */\r
- printf("\nThe string of data has %d characters in it",len);\r
- }\r
- \r
-\r
-The 8088/8086 group of CPUs, as used in the IBM PC, divide memory into\r
-64K segments. To address all 1Mb of memory a 20 bit number is used\r
-comprised of an `offset' to and a 64K `segment'. The IBM PC uses special\r
-registers called "segment registers" to record the segments of addresses.\r
-\r
-This leads the C language on the IBM PC to have three new keywords; near,\r
-far and huge.\r
-\r
-near pointers are 16 bits wide and access only data within the current\r
-segment.\r
-\r
-far pointers are comprised of an offset and a segment address allowing\r
-them to access data any where in memory, but arithmetic with far pointers\r
-is fraught with danger! When a value is added or subtracted from a far\r
-pointer it is only actualy the segment part of the pointer that is\r
-affected, thus leading to the situation where a far pointer being\r
-incremented wraps around on its own offset 64K segment.\r
-\r
-huge pointers are a variation on the far pointer that can be successfuly\r
-incremented and decremented through the entire 1Mb range since the\r
-compiler generates code to amend the offset as applicable.\r
-\r
-It will come as no surprise that code using near pointers is executed\r
-faster than code using far pointers, which in turn is faster than code\r
-using huge pointers.\r
-\r
-to give a literal address to a far pointer IBM PC C compilers provide a\r
-macro MK-FP() that has the prototype;\r
-\r
-\r
- void far *MK_FP(unsigned segment, unsigned offset);\r
-\r
-For example, to create a far pointer to the start of video memory on a\r
-colour IBM PC system you could use;\r
-\r
- screen = MK_FP(0xB800,0);\r
-\r
-Two coressponding macros provided are;\r
-\r
-FP_SEG() and FP_OFF()\r
-\r
-Which return the segment and offset respectively of a far pointer. The\r
-following example uses FP_SEG() and FP_OFF() to send segment and offset\r
-addresses to a DOS call to create a new directory path;\r
-\r
-\r
- #include <dos.h>\r
- \r
- int makedir(char *path)\r
- {\r
- /* Make sub directory, returning non zero on success */\r
- \r
- union REGS inreg,outreg;\r
- struct SREGS segreg;\r
- \r
- inreg.h.ah = 0x39;\r
- segreg.ds = FP_SEG(path);\r
- inreg.x.dx = FP_OFF(path);\r
- intdosx(&inreg,&outreg,&segreg);\r
- \r
- return(1 - outreg.x.cflag);\r
- }\r
- \r
-\r
-Finally, the CPU's segment registers can be read with the function\r
-`segread()'. This is a function unique to C compilers for the the 8086\r
-family of processors. segread() has the function prototype:\r
-\r
-\r
- void segread(struct SREGS *segp);\r
-\r
-It returns the current values of the segment registers in the SREGS type\r
-structure pointed to by the pointer `segp'. For example:\r
-\r
- #include <dos.h>\r
- \r
- main()\r
- {\r
- struct SREGS sregs;\r
- \r
- segread(&sregs);\r
- printf("\nCode segment is currently at %04X, Data segment is at\r
- %04X",sregs.cs,sregs.ds);\r
- printf("\nExtra segment is at %04X, Stack segment is at\r
- %04X",sregs.es,sregs.ss);\r
- }\r
- \r
- STRUCTURES\r
-\r
-\r
-C provides the means to group together variables under one name providing\r
-a convenient means of keeping related information together and providing\r
-a structured approach to data.\r
-\r
-The general form for a structure definition is;\r
-\r
-\r
- typedef struct\r
- {\r
- variable_type variable_name;\r
- variable_type variable_name;\r
- }\r
- structure_name;\r
-\r
-\r
-When accessing data files, with a fixed record structure, the use of a\r
-structure variable becomes essential. The following example shows a\r
-record structure for a very simple name and address file. It declares a\r
-data structure called `data', and comprised of six fields; `name',\r
-`address', `town', `county' , `post' and `telephone'.\r
-\r
-\r
- typedef struct\r
- {\r
- char name[30];\r
- char address[30];\r
- char town[30];\r
- char county[30];\r
- char post[12];\r
- char telephone[15];\r
- }\r
- data;\r
-\r
-The structure 'data' may then be used in the program as a variable data\r
-type for declaring variables;\r
-\r
- data record;\r
-\r
-Thus declares a variable called 'record' to be of type 'data'.\r
-\r
-The individual fields of the structure variable are accessed by the\r
-general form;\r
-\r
- structure_variable.field_name;\r
-\r
-Thus, the name field of the structure variable record is accessed with;\r
-\r
- record.name\r
-\r
-There is no limit to the number of fields that may comprise a structure,\r
-nor do the fields have to be of the same types, for example;\r
-\r
-\r
- typedef struct\r
- {\r
- char name[30];\r
- int age;\r
- char *notes;\r
- }\r
- dp;\r
-\r
-Declares a structure 'dp' that is comprised of a character array field,\r
-an integer field and a character pointer field.\r
-\r
-A structure variable may be passed as a parameter by passing the address\r
-of the variable as the parameter with the & operator thus;\r
-\r
- func(&my_struct)\r
-\r
-\r
-The following is an example program that makes use of a structure to\r
-provide the basic access to the data in a simple name and address file;\r
-\r
-\r
-/* A VERY simple address book written in ANSI C */\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <io.h>\r
-#include <string.h>\r
-#include <fcntl.h>\r
-#include <sys\stat.h>\r
-\r
-/* num_lines is the number of screen display lines */\r
-#define num_lines 25\r
-\r
-\r
-typedef struct\r
-{\r
- char name[30];\r
- char address[30];\r
- char town[30];\r
- char county[30];\r
- char post[12];\r
- char telephone[15];\r
-}\r
-data;\r
-\r
-data record;\r
-int handle;\r
-\r
-\r
-/* Function prototypes */\r
-\r
-void ADD_REC(void);\r
-void CLS(void);\r
-void DISPDATA(void);\r
-void FATAL(char *);\r
-void GETDATA(void);\r
-void MENU(void);\r
-void OPENDATA(void);\r
-int SEARCH(void);\r
-\r
-void CLS()\r
-{\r
- int n;\r
-\r
- for(n = 0; n < num_lines; n++)\r
- puts("");\r
-}\r
-\r
-void FATAL(char *error)\r
-{\r
- printf("\nFATAL ERROR: %s",error);\r
- exit(0);\r
-}\r
-\r
-void OPENDATA()\r
-{\r
- /* Check for existence of data file and if not create it */\r
- /* otherwise open it for reading/writing at end of file */\r
-\r
- handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);\r
-\r
- if (handle == -1)\r
- {\r
- handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);\r
- if (handle == -1)\r
- FATAL("Unable to create data file");\r
- }\r
-}\r
-\r
-void GETDATA()\r
-{\r
- /* Get address data from operator */\r
-\r
- CLS();\r
-\r
- printf("Name ");\r
- gets(record.name);\r
- printf("\nAddress ");\r
- gets(record.address);\r
- printf("\nTown ");\r
- gets(record.town);\r
- printf("\nCounty ");\r
- gets(record.county);\r
- printf("\nPost Code ");\r
- gets(record.post);\r
- printf("\nTelephone ");\r
- gets(record.telephone);\r
-}\r
-\r
-void DISPDATA()\r
-{\r
- /* Display address data */\r
- char text[5];\r
-\r
- CLS();\r
-\r
- printf("Name %s",record.name);\r
- printf("\nAddress %s",record.address);\r
- printf("\nTown %s",record.town);\r
- printf("\nCounty %s",record.county);\r
- printf("\nPost Code %s",record.post);\r
- printf("\nTelephone %s\n\n",record.telephone);\r
-\r
- puts("Press RETURN to continue");\r
- gets(text);\r
-}\r
-\r
-void ADD_REC()\r
-{\r
- /* Insert or append a new record to the data file */\r
- int result;\r
-\r
- result = write(handle,&record,sizeof(data));\r
-\r
- if (result == -1)\r
- FATAL("Unable to write to data file");\r
-}\r
-\r
-int SEARCH()\r
-{\r
- char text[100];\r
- int result;\r
-\r
- printf("Enter data to search for ");\r
- gets(text);\r
- if (*text == 0)\r
- return(-1);\r
-\r
- /* Locate start of file */\r
- lseek(handle,0,SEEK_SET);\r
-\r
- do\r
- {\r
- /* Read record into memory */\r
- result = read(handle,&record,sizeof(data));\r
- if (result > 0)\r
- {\r
- /* Scan record for matching data */\r
- if (strstr(record.name,text) != NULL)\r
- return(1);\r
- if (strstr(record.address,text) != NULL)\r
- return(1);\r
- if (strstr(record.town,text) != NULL)\r
- return(1);\r
- if (strstr(record.county,text) != NULL)\r
- return(1);\r
- if (strstr(record.post,text) != NULL)\r
- return(1);\r
- if (strstr(record.telephone,text) != NULL)\r
- return(1);\r
- }\r
- }\r
- while(result > 0);\r
- return(0);\r
-}\r
-\r
-void MENU()\r
-{\r
- int option;\r
- char text[10];\r
-\r
- do\r
- {\r
- CLS();\r
- puts("\n\t\t\tSelect Option");\r
- puts("\n\n\t\t\t1 Add new record");\r
- puts("\n\n\t\t\t2 Search for data");\r
- puts("\n\n\t\t\t3 Exit");\r
- puts("\n\n\n\n\n");\r
- gets(text);\r
- option = atoi(text);\r
-\r
- switch(option)\r
- {\r
- case 1 : GETDATA();\r
- /* Go to end of file to append new record */\r
- lseek(handle,0,SEEK_END);\r
- ADD_REC();\r
- break;\r
-\r
- case 2 : if (SEARCH())\r
- DISPDATA();\r
- else\r
- {\r
- puts("NOT FOUND!");\r
- puts("Press RETURN to continue");\r
- gets(text);\r
- }\r
- break;\r
-\r
- case 3 : break;\r
- }\r
- }\r
- while(option != 3);\r
-}\r
-\r
-void main()\r
-{\r
- CLS();\r
- OPENDATA();\r
- MENU();\r
-}\r
-\r
-Bit Fields\r
-\r
-C allows the inclusion of variables with a size of less than eight bits\r
-to be included in structures. These variables are known as bit fields,\r
-and may be any declared size from one bit upwards.\r
-\r
-The general form for declaring a bit field is;\r
-\r
-\r
- type name : number_of_bits;\r
-\r
-\r
-For example, to declare a set of status flags, each occupying one bit;\r
-\r
-typedef struct\r
-{\r
- unsigned carry : 1;\r
- unsigned zero : 1;\r
- unsigned over : 1;\r
- unsigned parity : 1;\r
-}\r
-df;\r
-\r
-df flags;\r
-\r
-The variable `flags' then occupies only four bits in memory, and yet is\r
-comprised of four variables that may be accessed like any other structure\r
-field.\r
-\r
-\r
-\r
-Uunions\r
-\r
-Another facility provided by C for efficient use of available memory is\r
-the union structure. A union structure is a collection of variables that\r
-all share the same memory storage address. As such only one of the\r
-variables is ever accessible at a time.\r
-\r
-The general form of a union definition is;\r
-\r
-\r
- union name\r
- {\r
- type variable_name;\r
- type variable_name;\r
- .\r
- .\r
- .\r
- type variable_name;\r
- };\r
-\r
-Thus, to declare a union structure for two integer variables;\r
-\r
- union data\r
- {\r
- int vara;\r
- int varb;\r
- };\r
-\r
-and to declare a variable of type 'data';\r
-\r
- data my_var;\r
-\r
-The variable 'my_var' is then comprised of the two variables 'vara' and\r
-'varb' that are accessed like with any form of structure, eg;\r
-\r
- my_var.vara = 5;\r
-\r
-Assigns a value of 5 to the variable 'vara' of union 'my_var'.\r
-\r
-\r
-Enumerations\r
-\r
-An enumeration assigns ascending integer values to a list of symbols. An\r
-enumeration declaration takes the general form;\r
-\r
-\r
- enum name { enumeration list } variable_list;\r
-\r
-Thus to define a symbol list of colours, you can say;\r
-\r
- enum COLOURS\r
- {\r
- BLACK,\r
- BLUE,\r
- GREEN,\r
- CYAN,\r
- RED,\r
- MAGENTA,\r
- BROWN,\r
- LIGHTGRAY,\r
- DARKGRAY,\r
- LIGHTBLUE,\r
- LIGHTGREEN,\r
- LIGHTCYAN,\r
- LIGHTRED,\r
- LIGHTMAGENTA,\r
- YELLOW,\r
- WHITE\r
- };\r
-\r
-Then, the number zero may be referred to by the symbol BLACK, the number\r
-one by the symbol BLUE, the number two by the symbol GREEN and so on.\r
-\r
-The following program illustrates the use of an enumeration list to\r
-symbolise integers;\r
-\r
- #include <stdio.h>\r
- \r
- enum COLOURS\r
- {\r
- BLACK,\r
- BLUE,\r
- GREEN,\r
- CYAN,\r
- RED,\r
- MAGENTA,\r
- BROWN,\r
- LIGHTGRAY,\r
- DARKGRAY,\r
- LIGHTBLUE,\r
- LIGHTGREEN,\r
- LIGHTCYAN,\r
- LIGHTRED,\r
- LIGHTMAGENTA,\r
- YELLOW,\r
- WHITE\r
- };\r
- \r
- \r
- void main()\r
- {\r
- int x;\r
- \r
- x = RED;\r
- \r
- printf("\nVariable 'x' holds %d",x);\r
- \r
- }\r
- \r
- FILE I/O\r
-\r
-\r
-C provides buffered file streams for file access. Some C platforms, such\r
-as Unix and DOS provide unbuffered file handles as well.\r
-\r
-\r
-\r
-Buffered streams\r
-\r
-Buffered streams are accessed through a variable of type 'file pointer'.\r
-The data type FILE is defined in the header file stdio.h. Thus to declare\r
-a file pointer;\r
-\r
- #include <stdio.h>\r
-\r
- FILE *ptr;\r
-\r
-To open a stream C provides the function fopen(), which accepts two\r
-parameters, the name of the file to be opened, and the access mode for\r
-the file to be opened with. The access mode may be any one of;\r
-\r
-\r
-Mode Description\r
- \r
-r Open for reading\r
-w Create for writing, destroying any\r
- existing file\r
-a Open for append, creating a new file\r
- if it doesn't\r
- exist\r
-r+ Open an existing file for reading\r
- and writing\r
-w+ Create for reading and writing,\r
- destroying any\r
- existing file\r
-a+ Open for append, creating a new file\r
- if it doesn't exist.\r
-\r
-\r
-Optionaly either `b' or `t' may be appended for binary or text mode. If\r
-neither is appended, then the file stream will be opened in the mode\r
-described by the global variable _fmode. Data read or written from file\r
-streams opened in text mode undergoes conversion, that is the characters\r
-CR and LF are converted to CR LF pairs on writing, and the CR LF pair is\r
-converted to a single LF on reading. File streams opened in binary mode\r
-do not undergo conversion.\r
-\r
-\r
-If fopen() fails to open the file, it returns a value of NULL (defined in\r
-stdio.h) to the file pointer.\r
-\r
-Thus, the following program will create a new file called "data.txt" and\r
-open it for reading and writing;\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- FILE *fp;\r
- \r
- fp = fopen("data.txt","w+");\r
- \r
- }\r
-\r
-To close a stream C provides the function fclose(), which accepts the\r
-stream's file pointer as a parameter;\r
-\r
- fclose(fp);\r
-\r
-If an error occurs in closing the file stream, fclose() returns non zero.\r
-\r
-There are four basic functions for receiving and sending data to and from\r
-streams; fgetc(), fputc(), fgets() and fputs().\r
-\r
-fgetc() simply reads a single character from the specified input stream;\r
-\r
- char fgetc(FILE *fp);\r
-\r
-Its opposite number is fputc(), which simply writes a single character to\r
-the specified input stream;\r
-\r
- char fputc(char c, FILE *fp);\r
-\r
-fgets() reads a string from the input stream;\r
-\r
- char *fgets(char s, int numbytes, FILE *fp);\r
-\r
-It stops reading when either numbytes - 1 bytes have been read, or a\r
-newline character is read in. A null terminating byte is appended to the\r
-read string, s. If an error occurs, fgets() returns NULL.\r
-\r
-\r
-fputs() writes a null terminated string to a stream;\r
-\r
- int fputs(char *s, FILE *fp);\r
-\r
-Excepting fgets(), which returns a NULL pointer if an error occurs, all\r
-the other functions described above return EOF (defined in stdio.h) if an\r
-error occurs during the operation.\r
-\r
-\r
-The following program creates a copy of the file "data.dat" as "data.old"\r
-and illustrates the use of fopen(), fgetc(), fputc() and fclose();\r
-\r
-\r
- #include <stdio.h>\r
- \r
- int main()\r
- {\r
- FILE *in;\r
- FILE *out;\r
- \r
- in = fopen("data.dat","r");\r
- \r
- if (in == NULL)\r
- {\r
- puts("\nUnable to open file data.dat for reading");\r
- return(0);\r
- }\r
- \r
- out = fopen("data.old","w+");\r
- \r
- if (out == NULL)\r
- {\r
- puts("\nUnable to create file data.old");\r
- return(0);\r
- }\r
- \r
- /* Loop reading and writing one byte at a time until end-of-\r
- file */\r
- while(!feof(in))\r
- fputc(fgetc(in),out);\r
- \r
- /* Close the file streams */\r
- fclose(in);\r
- fclose(out);\r
- \r
- return(0);\r
- }\r
-\r
-Example program using fputs() to copy text from stream stdin (usually\r
-typed in at the keyboard) to a new file called "data.txt".\r
-\r
- #include <stdio.h>\r
- \r
- int main()\r
- {\r
- FILE *fp;\r
- char text[100];\r
- \r
- fp = fopen("data.txt","w+");\r
- \r
- do\r
- {\r
- gets(text);\r
- fputs(text,fp);\r
- }\r
- while(*text);\r
- \r
- fclose(fp);\r
- }\r
-\r
-\r
-Random access using streams\r
-\r
-Random file access for streams is provided for by the fseek() function\r
-that has the following prototype;\r
-\r
- int fseek(FILE *fp, long numbytes, int fromwhere);\r
-\r
-fseek() repositions a file pointer associated with a stream previously\r
-opened by a call to fopen(). The file pointer is positioned `numbytes'\r
-from the location `fromwhere', which may be the file beginning, the\r
-current file pointer position, or the end of the file, symbolised by the\r
-constants SEEK_SET, SEEK_CUR and SEEK_END respectively. If a call to\r
-fseek() succeeds, a value of zero is returned.\r
-\r
-\r
-Associated with fseek() is ftell(), which reports the current file\r
-pointer position of a stream, and has the following function prototype;\r
-\r
-\r
- long int ftell(FILE *fp);\r
-\r
-ftell() returns either the position of the file pointer, measured in\r
-bytes from the start of the file, or -1 upon an error occurring.\r
-\r
-\r
-\r
-Handles\r
-\r
-File handles are opened with the open() function that has the prototype;\r
-\r
- int open(char *filename,int access[,unsigned mode]);\r
-\r
-If open() is successful, the number of the file handle is returned.\r
-Otherwise open() returns -1.\r
-\r
-The access integer is comprised from bitwise oring together of the\r
-symbolic constants declared in fcntl.h. These vary from compiler to\r
-compiler but may be;\r
-\r
-\r
- O_APPEND If set, the file pointer will be set to the end of\r
-the\r
- file prior to each write.\r
- O_CREAT If the file does not exist it is created.\r
- O_TRUNC Truncates the existing file to a length of zero\r
-bytes.\r
- O_EXCL Used with O_CREAT\r
- O_BINARY Opens the file in binary mode\r
- O_TEXT Opens file in text mode\r
-\r
-The optional mode parameter is comprised by bitwise oring of the symbolic\r
-constants defined in stat.h. These vary from C compiler to C compiler but\r
-may be;\r
-\r
- S_IWRITE Permission to write\r
- S_IREAD Permission to read\r
-\r
-\r
-Once a file handle has been assigned with open(), the file may be\r
-accessed with read() and write().\r
-\r
-Read() has the function prototype;\r
-\r
- int read(int handle, void *buf, unsigned num_bytes);\r
-\r
-It attempts to read 'num_bytes' and returns the number of bytes actually\r
-read from the file handle 'handle', and stores these bytes in the memory\r
-block pointed to by 'buf'.\r
-\r
-Write() is very similar to read() and has the same function prototype and\r
-return values, but writes 'num_bytes' from the memory block pointed to by\r
-'buf'.\r
-\r
-Files opened with open() are closed using close() that has the function\r
-prototype;\r
-\r
- int close(int handle);\r
-\r
-close() returns zero on success, and -1 if an error occurs trying to\r
-close the handle.\r
-\r
-Random access is provided by lseek(), which is very similar to fseek(),\r
-except that it accepts an integer file handle as the first parameter\r
-rather than a stream FILE pointer.\r
-\r
-This example uses file handles to read data from stdin (usually the\r
-keyboard) and copy the text to a new file called "data.txt".\r
-\r
- #include <io.h>\r
- #include <fcntl.h>\r
- #include <sys\stat.h>\r
- \r
- int main()\r
- {\r
- int handle;\r
- char text[100];\r
- \r
- handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);\r
- \r
- do\r
- {\r
- gets(text);\r
- write(handle,&text,strlen(text));\r
- }\r
- while(*text);\r
- \r
- close(handle);\r
- }\r
-\r
-\r
-\r
-Advanced File I/O\r
-\r
-The ANSI standard on C defines file IO as by way of file streams, and\r
-defines various functions for file access;\r
-\r
-\r
-fopen() has the prototype;\r
-\r
- FILE *fopen(const char *name,const char *mode);\r
-\r
-fopen() attempts to open a stream to a file name in a specified mode. If\r
-successful a FILE type pointer is returned to the file stream, if the\r
-call fails NULL is returned. The mode string can be one of the following;\r
-\r
-\r
-Mode Description\r
- r Open for reading only\r
- w Create for writing, overwriting any existing file with the same\r
- name.\r
- a Open for append (writing at end of file) or create the file if\r
-it\r
- does not exist.\r
- r+ Open an existing file for reading and writing.\r
- w+ Create a new file for reading and writing.\r
- a+ Open for append with read and write access.\r
-\r
-\r
-fclose() is used to close a file stream previously opened by a call to\r
-fopen(). It has the prototype;\r
-\r
- int fclose(FILE *fp);\r
-\r
-When a call to fclose() is successful, all buffers to the stream are\r
-flushed and a value of zero is returned. If the call fails fclose()\r
-returns EOF.\r
-\r
-Many host computers, and the IBM PC is no exception, use buffered file\r
-access, that is when writing to a file stream the data is stored in\r
-memory and only written to the stream when it exceeds a predefined number\r
-of bytes. A power failure occurring before the data has been written to\r
-the stream will result in the data never being written, so the function\r
-fflush() can be called to force all pending data to be written. fflush()\r
-has the prototype;\r
-\r
- int fflush(FILE *fp);\r
-\r
-When a call to fflush() is successful, the buffers connected with the\r
-stream are flushed and a value of zero is returned. On failure fflush()\r
-returns EOF.\r
-\r
-The location of the file pointer connected with a stream can be\r
-determined with the function ftell(). ftell() has the prototype;\r
-\r
-\r
- long int ftell(FILE *fp);\r
-\r
-and returns the offset of the file pointer in bytes from the start of the\r
-file, or -1L if the call fails.\r
-\r
-Similarly, you can move the file pointer to a new position with fseek().\r
-fseek() has the prototype;\r
-\r
- int fseek(FILE *fp, long offset, int from_what_place);\r
-\r
-fseek() attempts to move the file pointer, 'fp' 'offset' bytes from the\r
-position 'from_what_place'. 'from_what_place' is predefined as one of;\r
-\r
- SEEK_SET The file's beginning\r
- SEEK_CUR The file pointer's current position\r
- SEEK_END End of file\r
-\r
-The offset may be a positive value to move the file pointer on through\r
-the file, or negative to move backwards.\r
-\r
-To move a file pointer quickly back to the start of a file, and clear any\r
-references to errors that have occurred C provides the function rewind()\r
-that has the prototype;\r
-\r
- void rewind(FILE *fp);\r
-\r
-rewind(fp) is similar to fseek(fp,0L,SEEK_SET) in that they both set the\r
-file pointer to the start of the file, but whereas fseek() clears the EOF\r
-error marker, rewind() clears all error indicators.\r
-\r
-Errors occurring with file functions can be checked with the function\r
-ferror() that has the prototype;\r
-\r
- int ferror(FILE *fp);\r
-\r
-ferror() returns a nonzero value if an error has occurred on the\r
-specified stream. After checking ferror() and reporting any errors you\r
-should clear the error indicators, and this can be done by a call to\r
-clearerr() that has the prototype;\r
-\r
- void clearerr(FILE *fp);\r
-\r
-The condition of reaching end of file (EOF) can be tested for with the\r
-predefined macro feof() that has the prototype;\r
-\r
- int feof(FILE *fp);\r
-\r
-feof() returns a nonzero value if an end of file error indicator was\r
-detected on the specified file stream, and zero if the end of file has\r
-not yet been reached.\r
-\r
-Reading data from a file stream can be achieved using several functions;\r
-A single character can be read with fgetc() that has the prototype;\r
-\r
-\r
- int fgetc(FILE *fp);\r
-\r
-fgetc() returns either the character read converted to an integer, or EOF\r
-if an error occurred.\r
-\r
-Reading a string of data is achieved with fgets(). fgets() attempts to\r
-read a string terminated by a newline character and of no more than a\r
-specified number of bytes from the file stream. It has the prototype;\r
-\r
-\r
- char *fgets(char s, int n, FILE *fp);\r
-\r
-A successful call to fgets() results in a string being stored in `s' that\r
-is either terminated by a newline character, or that is `n' - 1\r
-characters long, which ever came first. The newline character is retained\r
-by fgets(), and a null bytes is appended to the string. If the call fails\r
-a NULL pointer is returned.\r
-\r
-\r
-Strings may be written to a stream using fputs() that has the prototype;\r
-\r
- int fputs(const char *s,FILE *fp);\r
-\r
-fputs() writes all the characters in the string `s' to the stream `fp'\r
-except the null terminating byte. On success, fputs() returns the last\r
-character written, on failure it returns EOF.\r
-\r
-To write a single character to a stream use fputc() that has the\r
-prototype;\r
-\r
- int fputc(int c,FILE *fp);\r
-\r
-If successful, fputc() returns the character written, otherwise it\r
-returns EOF.\r
-\r
-To read a large block of data, or a record from a stream you can use\r
-fread() that has the prototype;\r
-\r
- size_t fread(void *ptr,size_t size, size_t n, FILE *fp);\r
-\r
-fread() attempts to read 'n' items, each of length 'size' from the file\r
-stream 'fp' into the block of memory pointed to by 'ptr'. To check the\r
-success or failure status of fread() use ferror().\r
-\r
-The sister function to fread() is fwrite() that has the prototype;\r
-\r
- size_t fwrite(const void *ptr,size_t size, size_t n,FILE *fp);\r
-\r
-that writes 'n' items each of length 'size' from the memory area pointed\r
-to by 'ptr' to the specified stream 'fp'.\r
-\r
-Formatted input from a stream is achieved with fscanf() that has the\r
-prototype;\r
-\r
- int fscanf(FILE *fp, const char *format[,address ...]);\r
-\r
-fscanf() returns the number of fields successfully stored, and EOF on end\r
-of file. This short example shows how fscanf() is quite useful for\r
-reading numbers from a stream, but hopeless when it comes to strings!\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- FILE *fp;\r
- int a;\r
- int b;\r
- int c;\r
- int d;\r
- int e;\r
- char text[100];\r
- \r
- fp = fopen("data.txt","w+");\r
- \r
- if(!fp)\r
- {\r
- perror("Unable to create file");\r
- exit(0);\r
- }\r
- \r
- fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");\r
- \r
- fflush(fp);\r
- \r
- if (ferror(fp))\r
- {\r
- fputs("Error flushing stream",stderr);\r
- exit(1);\r
- }\r
- \r
- rewind(fp);\r
- if (ferror(fp))\r
- {\r
- fputs("Error rewind stream",stderr);\r
- exit(1);\r
- }\r
- \r
- fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);\r
- if (ferror(fp))\r
- {\r
- fputs("Error reading from stream",stderr);\r
- exit(1);\r
- }\r
- \r
- printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);\r
- }\r
- \r
-As you can see from the example, fprintf() can be used to write formatted\r
-data to a stream.\r
-\r
-If you wish to store the position of a file pointer on a stream, and then\r
-later restore it to the same position you can use the functions fgetpos()\r
-and fsetpos(). fgetpos() reads the current location of the file pointer\r
-and has the prototype;\r
-\r
- int fgetpos(FILE *fp, fpos_t *pos);\r
-\r
-fsetpos() repositions the file pointer and has the prototype;\r
-\r
- int fsetpos(FILE *fp, const fpos_t *fpos);\r
-\r
-fpos_t is defined in stdio.h.\r
-\r
-These functions are more convenient than doing an ftell() followed by an\r
-fseek().\r
-\r
-An open stream can have a new file associated with it in place of the\r
-existing file by using the function freopen() that has the prototype;\r
-\r
- FILE *freopen(const char *name,const char *mode,FILE *fp);\r
-\r
-freopen() closes the existing stream and then attempts to reopens it with\r
-the specified file name. This is useful for redirecting the predefined\r
-streams stdin, stdout and stderr to a file or device.\r
-\r
-For example; if you wish to redirect all output intended to stdout\r
-(usually the host computer's display device) to a printer you might use;\r
-\r
- freopen("LPT1","w",stdout);\r
-\r
-where LPT1 is the name of the printer device (on a PC host, LPT1 is the\r
-name of the parallel port).\r
-\r
-\r
-Predefined I/O Streams\r
-\r
-There are three predefined I/O streams; stdin, stdout, and stderr. The\r
-streams stdin and stdout default to the keyboard and display\r
-respectively, but can be redirected on some hardware platforms, such as\r
-the PC and under UNIX. The stream stderr defaults to the display and is\r
-not usually redirected by the operator. Thus it can be used for the\r
-display of error messages even when program output has been redirected,\r
-such as with;\r
-\r
- fputs("Error message",stderr);\r
-\r
-The functions printf() and puts(), output data to the stream stdout, and\r
-can therefore be redirected by the operator of the program. scanf() and\r
-gets() accept input from the stream stdin.\r
-\r
-As an example of file i/o with the PC consider the following short\r
-program that does a hex dump of a specified file to the predefined stream\r
-stdout, which may be redirected to a file using;\r
-\r
- dump filename.ext > target.ext\r
-\r
- #include <stdio.h>\r
- #include <fcntl.h>\r
- #include <io.h>\r
- #include <string.h>\r
- \r
- main(int argc, char *argv[])\r
- {\r
- unsigned counter;\r
- unsigned char v1[20];\r
- int f1;\r
- int x;\r
- int n;\r
- \r
- if (argc != 2)\r
- {\r
- fputs("\nERROR: Syntax is dump f1\n",stderr);\r
- return(1);\r
- }\r
- \r
- f1 = open(argv[1],O_RDONLY);\r
- \r
- if (f1 == -1)\r
- {\r
- fprintf(stderr,"\nERROR: Unable to open %s\n",argv[1]);\r
- return(1);\r
- }\r
- \r
- fprintf(stdout,"\nDUMP OF FILE %s\n\n",strupr(argv[1]));\r
- \r
- counter = 0;\r
- \r
- while(1)\r
- {\r
- /* Set buffer to zero bytes */\r
- memset(v1,0,20);\r
- \r
- /* Read buffer from file */\r
- x = _read(f1,&v1,16);\r
- \r
- /* x will be 0 on EOF or -1 on error */\r
- if (x < 1)\r
- break;\r
- \r
- /* Print file offset to stdout */\r
- fprintf(stdout,"%06d(%05x) ",counter,counter);\r
- \r
- counter += 16;\r
- \r
- /* print hex values of buffer to stdout */\r
- for(n = 0; n < 16; n++)\r
- fprintf(stdout,"%02x ",v1[n]);\r
- \r
- /* Print ascii values of buffer to stdout */\r
- for(n = 0; n < 16; n++)\r
- {\r
- if ((v1[n] > 31) && (v1[n] < 128))\r
- fprintf(stdout,"%c",v1[n]);\r
- else\r
- fputs(".",stdout);\r
- }\r
- \r
- /* Finish the line with a new line */\r
- fputs("\n",stdout);\r
- }\r
- \r
- /* successful termination */\r
- return(0);\r
- }\r
- \r
- STRINGS\r
-\r
-The C language has one of the most powerful string handling capabilities\r
-of any general purpose computer language.\r
-\r
-A string is a single dimension array of characters terminated by a zero\r
-byte.\r
-\r
-Strings may be initialised in two ways. Either in the source code where\r
-they may be assigned a constant value, as in;\r
-\r
- int main()\r
- {\r
- char *p = "System 5";\r
- char name[] = "Test Program" ;\r
- }\r
-\r
-or at run time by the function strcpy() that has the function prototype;\r
-\r
- char *strcpy(char *destination, char *source);\r
-\r
-strcpy() copies the string pointed to by source into the location pointed\r
-to by destination as in the following example;\r
-\r
-\r
- #include<stdio.h>\r
- \r
- int main()\r
- {\r
- char name[50];\r
- \r
- strcpy(name,"Servile Software");\r
- \r
- printf("\nName equals %s",name);\r
- }\r
-\r
-C also allows direct access to each individual byte of the string, so the\r
-following is quite permissible;\r
-\r
- #include<stdio.h>\r
- \r
- int main()\r
- {\r
- char name[50];\r
- \r
- strcpy(name,"Servile Software");\r
- \r
- printf("\nName equals %s",name);\r
- \r
- /* Replace first byte with lower case 's' */\r
- name[0] = 's';\r
- \r
- printf("\nName equals %s",name);\r
- }\r
- \r
-The ANSI standard on the C programming language defines the following\r
-functions for use with strings;\r
-\r
-char *strcat(char *dest, char *source) Appends string source\r
-to the end of string destination.\r
-\r
-char *strchr(char *s, int c) Returns a pointer to the first\r
-occurence of character 'c' within s.\r
-\r
-int strcmp(char *s1, char *s2) Compares strings s1 and s2\r
-returning < 0 if s1 is less than s2\r
- == 0 if s1 and s2\r
-are the same\r
- > 0 if s1 is\r
-greater than s2\r
-\r
-int strcoll(char *s1, char *s2) Compares strings s1 and s2\r
- according to the collating sequence set by\r
- setlocale() returning < 0 if s1 is less\r
-than s2\r
- == 0 if s1 and s2 are the\r
-same\r
- > 0 if s1 is greater than s2\r
-\r
-char *strcpy(char *dest, char *src) Copies string src into\r
-string dest.\r
-\r
-unsigned strcspn(char *s1, char *s2) Returns the length of string\r
-s1 that consists entirely of characters not in\r
-string s2.\r
-\r
-unsigned strlen(char *s) Returns the length of string s.\r
-\r
-char *strncat(char *dest, char *src, unsigned len) Copies at most\r
-'len' characters from string src into string dest.\r
-\r
-int strncmp(char *s1, char *s2, unsigned len) Compares at most 'len'\r
-characters from\r
- string s1 with string s2 returning < 0\r
-if s1 is less than s2\r
- == 0 if s1 and s2 are\r
-the same\r
- > 0 if s1 is greater\r
-than s2\r
-\r
-char *strncpy(char *dest, char *src, unsigned len) Copies 'len'\r
-characters from string src into string dest, truncating or\r
- padding with zero bytes as required.\r
-\r
-char *strpbrk(char *s1, char *s2) Returns a pointer to the\r
-first character in string s1 that occurs in\r
- string s2.\r
-\r
-char *strrchr(char *s, int c) Returns a pointer to the last\r
-occurence of 'c' within string s.\r
-\r
-unsigned strspn(char *s1, char *s2) Returns the length of the\r
-initial segment of string s1 that consists\r
- entirely of characters in string s2.\r
-\r
-char *strstr(char *s1, char *s2) Returns a pointer to the\r
-first occurence of string s2 within string\r
- s1, or NULL if string s2 is not found in\r
-string s1.\r
-\r
-char *strtok(char *s1, char *s2) Returns a pointer to the\r
-token found in string s1 that is defined by\r
- delimiters in string s2. Returns NULLif no\r
-tokens are found.\r
-\r
-The ANSI standard also defines various functions for converting strings\r
-into numbers and numbers into strings.\r
-\r
-Some C compilers include functions to convert strings to upper and lower\r
-case, but these functions are not defined in the ANSI standard. However,\r
-the ANSI standard does define the functions; toupper() and tolower() that\r
-return an\r
-\r
-integer parameter converted to upper and lowercase respectively. By using\r
-these functions we can create our own ANSI compatible versions;\r
-\r
-\r
- #include<stdio.h>\r
- \r
- void strupr(char *source)\r
- {\r
- char *p;\r
- \r
- p = source;\r
- while(*p)\r
- {\r
- *p = toupper(*p);\r
- p++;\r
- }\r
- }\r
- \r
- void strlwr(char *source)\r
- {\r
- char *p;\r
- \r
- p = source;\r
- while(*p)\r
- {\r
- *p = tolower(*p);\r
- p++;\r
- }\r
- }\r
-\r
-\r
- int main()\r
- {\r
- char name[50];\r
- \r
- strcpy(name,"Servile Software");\r
- \r
- printf("\nName equals %s",name);\r
- \r
- strupr(name);\r
- \r
- printf("\nName equals %s",name);\r
- \r
- strlwr(name);\r
- \r
- printf("\nName equals %s",name);\r
- }\r
-\r
-C does not impose a maximum length that a string may be, unlike other\r
-computer languages. However, some CPUs impose restrictions on the maximum\r
-size a block of memory can be. For example, the 8088 family of CPUs, as\r
-used by the IBM PC, impose a limit of 64K bytes on a segment of memory.\r
-\r
-An example program to reverse all the characters in a string.\r
-\r
- #include <stdio.h>\r
- #include <string.h>\r
- \r
- char *strrev(char *s)\r
- {\r
- /* Reverses the order of all characters in a string except the\r
- null */\r
- /* terminating byte */\r
- \r
- char *start;\r
- char *end;\r
- char tmp;\r
- \r
- /* Set pointer 'end' to last character in string */\r
- end = s + strlen(s) - 1;\r
- \r
- /* Preserve pointer to start of string */\r
- start = s;\r
- \r
- /* Swop characters */\r
- while(end >= s)\r
- {\r
- tmp = *end;\r
- *end = *s;\r
- *s = tmp;\r
- end--;\r
- s++;\r
- }\r
- return(start);\r
- }\r
- \r
- \r
- main()\r
- {\r
- char text[100];\r
- char *p;\r
- \r
- strcpy(text,"This is a string of data");\r
- \r
- p = strrev(text);\r
- \r
- printf("\n%s",p);\r
- }\r
- \r
-\r
-Strtok()\r
-\r
-The function strtok() is a very powerful standard C feature for\r
-extracting substrings from within a single string. It is used where the\r
-substrings are separated by known delimiters, such as commas in the\r
-following example;\r
-\r
- #include <stdio.h>\r
- #include <string.h>\r
- \r
- main()\r
- {\r
- char data[50];\r
- char *p;\r
- \r
- strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");\r
- \r
- p = strtok(data,",");\r
- while(p)\r
- {\r
- puts(p);\r
- p = strtok(NULL,",");\r
- };\r
- }\r
- \r
-Or this program can be written with a for() loop thus;\r
-\r
- #include <stdio.h>\r
- #include <string.h>\r
- \r
- main()\r
- {\r
- char data[50];\r
- char *p;\r
- \r
- strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");\r
- \r
- for(strtok(data,","); p; p = strtok(NULL,","))\r
- {\r
- puts(p);\r
- };\r
- }\r
- \r
-They both compile to the same code but follow different programming\r
-styles.\r
-\r
-Initially, you call strtok() with the name of the string variable to be\r
-parsed, and a second string that contains the known delimiters. Strtok()\r
-then returns a pointer to the start of the first substring and replaces\r
-the first token with a zero delimiter. Subsequent calls to strtok() can\r
-be made in a loop passing NULL as the string to be parsed, and strtok()\r
-will return the subsequent substrings.\r
-\r
-\r
-Since strtok() can accept many delimiter characters in the second\r
-parameter string we can use it as the basis of a simple word counting\r
-program;\r
-\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- #include <string.h>\r
- \r
- void main(int argc, char *argv[])\r
- {\r
- FILE *fp;\r
- char buffer[256];\r
- char *p;\r
- long count;\r
- \r
- if (argc != 2)\r
- {\r
- fputs("\nERROR: Usage is wordcnt <file>\n",stderr);\r
- exit(0);\r
- }\r
- \r
- /* Open file for reading */\r
- fp = fopen(argv[1],"r");\r
- \r
- /* Check the open was okay */\r
- if (!fp)\r
- {\r
- fputs("\nERROR: Cannot open source file\n",stderr);\r
- exit(0);\r
- }\r
- \r
- /* Initialise word count */\r
- count = 0;\r
- \r
- do\r
- {\r
- /* Read a line of data from the file */\r
- fgets(buffer,255,fp);\r
- \r
- /* check for an error in the read or EOF */\r
- if (ferror(fp) || feof(fp))\r
- continue;\r
- \r
- /* count words in received line */\r
- /* Words are defined as separated by the characters */\r
- /* \t(tab) \n(newline) , ; : . ! ? ( ) - and [space] */\r
- p = strtok(buffer,"\t\n,;:.!?()- ");\r
- while(p)\r
- {\r
- count++;\r
- p = strtok(NULL,"\t\n,;:.!?()- ");\r
- }\r
- }\r
- while(!ferror(fp) && !feof(fp));\r
- \r
- /* Finished reading. Was it due to an error? */\r
- if (ferror(fp))\r
- {\r
- fputs("\nERROR: Reading source file\n",stderr);\r
- fclose(fp);\r
- exit(0);\r
- }\r
- \r
- /* Reading finished due to EOF, quite valid so print count */\r
- printf("\nFile %s contains %ld words\n",argv[1],count);\r
- fclose(fp);\r
- }\r
- \r
-\r
-\r
-\r
-Converting Numbers To And From Strings\r
-\r
-All C compilers provide a facility for converting numbers to strings.\r
-This being sprintf(). However, as happens sprintf() is a multi-purpose\r
-function that is therefore large and slow. The following function ITOS()\r
-accepts two parameters, the first being a signed integer and the second\r
-being a pointer to a character string. It then copies the integer into\r
-the memory pointed to by the character pointer. As with sprintf() ITOS()\r
-does not check that the target string is long enough to accept the result\r
-of the conversion. You should then ensure that the target string is long\r
-enough.\r
-\r
-Example function for copying a signed integer into a string;\r
-\r
- void ITOS(long x, char *ptr)\r
- {\r
- /* Convert a signed decimal integer to a string */\r
- \r
- long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,\r
- 1000, 100, 10, 1 };\r
- int n;\r
- \r
- /* Check sign */\r
- if (x < 0)\r
- {\r
- *ptr++ = '-';\r
- /* Convert x to absolute */\r
- x = 0 - x;\r
- }\r
- \r
- for(n = 0; n < 9; n++)\r
- {\r
- if (x > pt[n])\r
- {\r
- *ptr++ = '0' + x / pt[n];\r
- x %= pt[n];\r
- }\r
- }\r
- return;\r
- }\r
- \r
-To convert a string to a floating point number, C provides two functions;\r
-atof() and strtod(). atof() has the prototype;\r
-\r
- double atof(const char *s);\r
-\r
-strtod has the prototype;\r
-\r
- double strtod(const char *s,char **endptr);\r
-\r
-Both functions scan the string and convert it as far as they can, until\r
-they come across a character they don't understand. The difference\r
-between the two functions is that if strtod() is passed a character\r
-pointer for parameter `endptr', it sets that pointer to the first\r
-character in the string that terminated the conversion. Because of its\r
-better error reporting, by way of endptr, strtod() is often preferred to\r
-atof().\r
-\r
-To convert a string to an integer use atoi() that has the prototype;\r
-\r
-\r
- int atoi(const char *s);\r
-\r
-atoi() does not check for an overflow, and the results are undefined!\r
-\r
-atol() is a similar function but returns a long. Alternatively, you can\r
-use strtol() and stroul() instead that have better error checking.\r
-\r
- \r
- TEXT HANDLING\r
-\r
-Human languages write information down as `text'. This is comprised of\r
-words, figures and punctuation. The words being made up of upper case and\r
-lower case letters. Processing text with a computer is a commonly\r
-required task, and yet quite a difficult one.\r
-\r
-The ANSI C definitions include string processing functions that are by\r
-their nature sensitive to case. That is the letter `A' is seen as\r
-distinct from the letter `a'. This is the first problem that must be\r
-overcome by the programmer. Fortunately both Borland's Turbo C compilers\r
-and Microsoft's C compilers include case insensitive forms of the string\r
-functions.\r
-\r
-stricmp() for example is the case insensitive form of strcmp(), and\r
-strnicmp() is the case insensitive form of strncmp().\r
-\r
-If you are concerned about writing portable code, then you must restrict\r
-yourself to the ANSI C functions, and write your own case insensitive\r
-functions using the tools provided.\r
-\r
-Here is a simple implementation of a case insensitive version of\r
-strstr(). The function simply makes a copy of the parameter strings,\r
-converts those copies both to upper case and then does a standard\r
-strstr() on the copies. The offset of the target string within the\r
-source string will be the same for the copy as the original, and so it\r
-can be returned relative to the parameter string.\r
-\r
-\r
- char *stristr(char *s1, char *s2)\r
- {\r
- char c1[1000];\r
- char c2[1000];\r
- char *p;\r
- \r
- strcpy(c1,s1);\r
- strcpy(c2,s2);\r
- \r
- strupr(c1);\r
- strupr(c2);\r
- \r
- p = strstr(c1,c2);\r
- if (p)\r
- return s1 + (p - c1);\r
- return NULL;\r
- }\r
- \r
-This function scans a string, s1 looking for the word held in s2. The\r
-word must be a complete word, not simply a character pattern, for the\r
-function to return true. It makes use of the stristr() function described\r
-previously.\r
-\r
- int word_in(char *s1,char *s2)\r
- {\r
- /* return non-zero if s2 occurs as a word in s1 */\r
- char *p;\r
- char *q;\r
- int ok;\r
- \r
- ok = 0;\r
- q = s1;\r
- \r
- do\r
- {\r
- /* Locate character occurence s2 in s1 */\r
- p = stristr(q,s2);\r
- if (p)\r
- {\r
- /* Found */\r
- ok = 1;\r
- \r
- if (p > s1)\r
- {\r
- /* Check previous character */\r
- if (*(p - 1) >= 'A' && *(p - 1) <= 'z')\r
- ok = 0;\r
- }\r
- \r
- /* Move p to end of character set */\r
- p += strlen(s2);\r
- if (*p)\r
- {\r
- /* Check character following */\r
- if (*p >= 'A' && *p <= 'z')\r
- ok = 0;\r
- }\r
- }\r
- q = p;\r
- }\r
- while(p && !ok);\r
- return ok;\r
- }\r
-\r
-\r
-Some more useful functions for dealing with text are truncstr() that\r
-truncates a string;\r
-\r
- void truncstr(char *p,int num)\r
- {\r
- /* Truncate string by losing last num characters */\r
- if (num < strlen(p))\r
- p[strlen(p) - num] = 0;\r
- }\r
- \r
-trim() that removes trailing spaces from the end of a string;\r
-\r
- void trim(char *text)\r
- {\r
- /* remove trailing spaces */\r
- char *p;\r
- \r
- p = &text[strlen(text) - 1];\r
- while(*p == 32 && p >= text)\r
- *p-- = 0;\r
- }\r
- \r
-strlench() that changes the length of a string by adding or deleting\r
-characters;\r
-\r
- void strlench(char *p,int num)\r
- {\r
- /* Change length of string by adding or deleting characters */\r
- \r
- if (num > 0)\r
- memmove(p + num,p,strlen(p) + 1);\r
- else\r
- {\r
- num = 0 - num;\r
- memmove(p,p + num,strlen(p) + 1);\r
- }\r
- }\r
- \r
-strins() that inserts a string into another string;\r
-\r
- void strins(char *p, char *q)\r
- {\r
- /* Insert string q into p */\r
- strlench(p,strlen(q));\r
- strncpy(p,q,strlen(q));\r
- }\r
- \r
-strchg() that replaces all occurences of one sub-string with another\r
-within a target string;\r
-\r
- void strchg(char *data, char *s1, char *s2)\r
- {\r
- /* Replace all occurences of s1 with s2 */\r
- char *p;\r
- char changed;\r
- \r
- do\r
- {\r
- changed = 0;\r
- p = strstr(data,s1);\r
- if (p)\r
- {\r
- /* Delete original string */\r
- strlench(p,0 - strlen(s1));\r
- \r
- /* Insert replacement string */\r
- strins(p,s2);\r
- changed = 1;\r
- }\r
- }\r
- while(changed);\r
- }\r
- \r
- TIME\r
-\r
-C provides a function, time(), which reads the computer's system clock to\r
-return the system time as a number of seconds since midnight on January\r
-the first, 1970. However, this value can be converted to a useful string\r
-by the function ctime() as illustrated in the following example;\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- int main()\r
- {\r
- /* Structure to hold time, as defined in time.h */\r
- time_t t;\r
- \r
- /* Get system date and time from computer */\r
- t = time(NULL);\r
- printf("Today's date and time: %s\n",ctime(&t));\r
- }\r
- \r
-The string returned by ctime() is comprised of seven fields;\r
-\r
- Day of the week,\r
- Month of the year,\r
- Date of the day of the month,\r
- hour,\r
- minutes,\r
- seconds,\r
- century of the year\r
-\r
-terminated by a newline character and null terminating byte. Since the\r
-fields always occupy the same width, slicing operations can be carried\r
-out on the string with ease. The following program defines a structure\r
-`time' and a function gettime() that extracts the hours, minutes and\r
-seconds of the current time and places them in the structure;\r
-\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- struct time\r
- {\r
- int ti_min; /* Minutes */\r
- int ti_hour; /* Hours */\r
- int ti_sec; /* Seconds */\r
- };\r
- \r
- void gettime(struct time *now)\r
- {\r
- time_t t;\r
- char temp[26];\r
- char *ts;\r
- \r
- /* Get system date and time from computer */\r
- t = time(NULL);\r
- \r
- /* Translate dat and time into a string */\r
- strcpy(temp,ctime(&t));\r
- \r
- /* Copy out just time part of string */\r
- temp[19] = 0;\r
- ts = &temp[11];\r
- \r
- /* Scan time string and copy into time structure */\r
- sscanf(ts,"%2d:%2d:%2d",&now->ti_hour,&now->ti_min,&now-\r
- >ti_sec);\r
- }\r
- \r
- int main()\r
- {\r
- struct time now;\r
- \r
- gettime(&now);\r
- \r
- printf("\nThe time is\r
- %02d:%02d:%02d",now.ti_hour,now.ti_min,now.ti_sec);\r
- \r
- }\r
-\r
-The ANSI standard on C does actually provide a function ready made to\r
-convert the value returned by time() into a structure;\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- int main()\r
- {\r
- time_t t;\r
- struct tm *tb;\r
- \r
- /* Get time into t */\r
- t = time(NULL);\r
- \r
- /* Convert time value t into structure pointed to by tb */\r
- tb = localtime(&t);\r
- \r
- printf("\nTime is %02d:%02d:%02d",tb->tm_hour,tb->tm_min,tb-\r
- >tm_sec);\r
- }\r
- \r
-The structure 'tm' is defined in time.h as;\r
-\r
- struct tm\r
- {\r
- int tm_sec;\r
- int tm_min;\r
- int tm_hour;\r
- int tm_mday;\r
- int tm_mon;\r
- int tm_year;\r
- int tm_wday;\r
- int tm_yday;\r
- int tm_isdst;\r
- };\r
-\r
-\r
-Timers\r
-Often a program must determine the date and time from the host computer's\r
-non-volatile RAM. There are several time functions provided by the ANSI\r
-standard on C that allow a program to retrieve, from the host computer,\r
-the current date and time;\r
-\r
-time() returns the number of seconds that have elapsed since midnight on\r
-January the 1st 1970. It has the prototype;\r
-\r
- time_t time(time_t *timer);\r
-\r
-time() fills in the time_t variable sent as a parameter and returns the\r
-same value. You can call time() with a NULL parameter and just collect\r
-the return value thus;\r
-\r
- #include <time.h>\r
- \r
- void main()\r
- {\r
- time_t now;\r
- \r
- now = time(NULL);\r
- }\r
- \r
-asctime() converts a time block to a twenty six character string of the\r
-format;\r
-\r
- Wed Oct 14 10:23:45 1992\n\0\r
-\r
-asctime() has the prototype;\r
-\r
- char *asctime(const struct tm *tblock);\r
-\r
-ctime() converts a time value (as returned by time()) into a twenty six\r
-chracter string of the same format as asctime(). For example;\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- void main()\r
- {\r
- time_t now;\r
- char date[30];\r
- \r
- now = time(NULL);\r
- strcpy(date,ctime(&now));\r
- }\r
- \r
-difftime() returns the difference, in seconds, between two values (as\r
-returned by time()). This can be useful for testing the elapsed time\r
-between two events, the time a function takes to execute, and for\r
-creating consistent delays that are irrelevant of the host computer.\r
-\r
-An example delay program;\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- \r
- void DELAY(int period)\r
- {\r
- time_t start;\r
- \r
- start = time(NULL);\r
- while(time(NULL) < start + period)\r
- ;\r
- }\r
- \r
- void main()\r
- {\r
- printf("\nStarting delay now....(please wait 5 seconds)");\r
- \r
- DELAY(5);\r
- \r
- puts("\nOkay, I've finished!");\r
- }\r
-\r
-gmtime() converts a local time value (as returned by time()) to the GMT\r
-time and stores it in a time block. This function depends upon the global\r
-variable timezone being set.\r
-\r
-\r
-The time block is a predefined structure (declared in time.h) as follows;\r
-\r
- struct tm\r
- {\r
- int tm_sec;\r
- int tm_min;\r
- int tm_hour;\r
- int tm_mday;\r
- int tm_mon;\r
- int tm_year;\r
- int tm_wday;\r
- int tm_yday;\r
- int tm_isdst;\r
- };\r
-\r
-tm_mday records the day of the month, ranging from 1 to 31; tm_wday is\r
-the day of the week with Sunday being represented by 0; the year is\r
-recorded less 1900; tm_isdst is a flag to show whether daylight saving\r
-time is in effect. The actual names of the structure and its elements may\r
-vary from compiler to compiler, but the structure should be the same in\r
-essence.\r
-\r
-mktime() converts a time block to a calendar format. It follows the\r
-prototype;\r
-\r
- time_t mktime(struct tm *t);\r
-\r
-The following example allows entry of a date, and uses mktime() to\r
-calculate the day of the week appropriate to that date. Only dates from\r
-the first of January 1970 are recognisable by the time functions.\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- #include <string.h>\r
- \r
- void main()\r
- {\r
- struct tm tsruct;\r
- int okay;\r
- char data[100];\r
- char *p;\r
- char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",\r
- "Thursday", "Friday", "Saturday" ,\r
- "prior to 1970, thus not known" };\r
- do\r
- {\r
- okay = 0;\r
- printf("\nEnter a date as dd/mm/yy ");\r
- p = fgets(data,8,stdin);\r
- p = strtok(data,"/");\r
- \r
- if (p != NULL)\r
- tsruct.tm_mday = atoi(p);\r
- else\r
- continue;\r
- \r
- p = strtok(NULL,"/");\r
- if (p != NULL)\r
- tsruct.tm_mon = atoi(p);\r
- else\r
- continue;\r
- \r
- p = strtok(NULL,"/");\r
- \r
- if (p != NULL)\r
- tsruct.tm_year = atoi(p);\r
- else\r
- continue;\r
- okay = 1;\r
- }\r
- while(!okay);\r
- \r
- tsruct.tm_hour = 0;\r
- tsruct.tm_min = 0;\r
- tsruct.tm_sec = 1;\r
- tsruct.tm_isdst = -1;\r
- \r
- /* Now get day of the week */\r
- if (mktime(&tsruct) == -1)\r
- tsruct.tm_wday = 7;\r
- \r
- printf("That was %s\n",wday[tsruct.tm_wday]);\r
- }\r
- \r
-mktime() also makes the neccessary adjustments for values out of range,\r
-this can be utilised for discovering what the date will be in n number of\r
-days time thus;\r
-\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- #include <string.h>\r
- \r
- void main()\r
- {\r
- struct tm *tsruct;\r
- time_t today;\r
- \r
- today = time(NULL);\r
- tsruct = localtime(&today);\r
- \r
- tsruct->tm_mday += 10;\r
- mktime(tsruct);\r
- \r
- printf("In ten days it will be %02d/%02d/%2d\n", tsruct-\r
- >tm_mday,tsruct->tm_mon + 1,tsruct->tm_year);\r
- \r
- }\r
- \r
-\r
-This program uses Julian Dates to decide any day of the week since the\r
-1st of October 1582 when the Gregorian calendar was introduced.\r
-\r
- char *WDAY(int day, int month, int year)\r
- {\r
- /* Returns a pointer to a string representing the day of the\r
- week */\r
- \r
- static char *cday[] = { "Saturday","Sunday","Monday","Tuesday",\r
- "Wednesday","Thursday","Friday" };\r
- double yy;\r
- double yt;\r
- double j;\r
- int y1;\r
- int y4;\r
- int x;\r
- \r
- yy = year / 100;\r
- y1 = (int)(yy);\r
- yt = year / 400;\r
- y4 = (int)(yt);\r
- x = 0;\r
- \r
- if (month < 3)\r
- {\r
- year--;\r
- x = 12;\r
- }\r
- \r
- j = day + (int)(365.25*year);\r
- \r
- j += (int)(30.6001 * (month + 1 + x)) - y1 + y4;\r
- \r
- if (yy == y1 && yt != y4 && month < 3)\r
- j++;\r
- \r
- j = 1 + j - 7 * (int)(j/7);\r
- \r
- if (j > 6)\r
- j -= 7;\r
- \r
- return(cday[j]);\r
- }\r
- \r
- \r
-With time() and difftime() we can create a timer for testing the\r
-execution times of functions thus;\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- main()\r
- {\r
- time_t now;\r
- time_t then;\r
- double elapsed;\r
- \r
- int n;\r
- \r
- now = time(NULL);\r
- \r
- /* This loop is adjustable for multiple passes */\r
- for(n = 0; n < 5000; n++)\r
- /* Call the function to test */\r
- func();\r
- \r
- then = time(NULL);\r
- \r
- elapsed = difftime(then,now);\r
- printf("\nElapsed seconds==%lf\n",elapsed);\r
- }\r
- \r
-By way of time() and ctime() the current system date and time can be\r
-retrieved from the host computer thus;\r
-\r
- #include <stdio.h>\r
- #include <time.h>\r
- \r
- main()\r
- {\r
- time_t now;\r
- char *date;\r
- int n;\r
- \r
- /* Get system time */\r
- now = time(NULL);\r
- \r
- /* Convert system time to a string */\r
- date = ctime(&now);\r
- \r
- /*Display system time */\r
- printf("\nIt is %s",date);\r
- }\r
- \r
-time_t is a type defined in time.h as the type of variable returned by\r
-time(). This type may vary from compiler to compiler, and therefore is\r
-represented by the type "time_t".\r
- \r
- HEADER FILES\r
-\r
-Function prototypes for library functions supplied with the C compiler,\r
-and standard macros are declared in header files.\r
-\r
-The ANSI standard on the C programming language lists the following\r
-header files;\r
-\r
-Header file Description\r
- \r
-assert.h Defines the assert debugging macro\r
-ctype.h Character classification and\r
- conversion macros\r
-errno.h Constant mnemonics for error codes\r
-float.h Defines implementation specific\r
- macros for dealing with floating\r
- point mathematics\r
-limits.h Defines implementation specific\r
- limits on type values\r
-locale.h Country specific parameters\r
-math.h Prototypes for mathematics functions\r
-setjmp.h Defines typedef and functions for\r
- setjmp/longjmp\r
-signal.h Constants and declarations for use by\r
- signal() and raise()\r
-stdarg.h Macros for dealing with argument\r
- lists\r
-stddef.h Common data types and macros\r
-stdio.h Types and macros required for\r
- standard I/O\r
-stdlib.h Prototypes of commonly used functions\r
- and miscellany\r
-string.h String manipulation function\r
- prototypes\r
-time.h Structures for time conversion\r
- routines\r
- \r
- DEBUGGING\r
-\r
-The ANSI standard on C includes a macro function for debugging called\r
-assert(). This expands to an if() statement, which if it returns true\r
-terminates the program and outputs to the standard error stream a message\r
-comprised of:\r
-\r
-\r
- Assertion failed: <test>, file <module>, line <line number>\r
- Abnormal program termination\r
-For example, the following program accidentally assigns a zero value to a\r
-pointer!\r
-\r
- #include <stdio.h>\r
- #include <assert.h>\r
- \r
- main()\r
- {\r
- /* Demonstration of assert */\r
- \r
- int *ptr;\r
- int x;\r
- \r
- x = 0;\r
- \r
- /* Whoops! error in this line! */\r
- ptr = x;\r
- \r
- assert(ptr != NULL);\r
- }\r
- \r
-When run, this program terminates with the message:\r
-\r
- Assertion failed: ptr != 0, file TEST.C, line 16\r
- Abnormal program termination\r
-\r
-When a program is running okay, the assert() functions can be removed\r
-from the compiled program by simply adding the line;\r
-\r
- #define NDEBUG\r
-\r
-before the #include <assert.h> line. Effectively the assert functions are\r
-commented out in the preprocessed source before compilation, this means\r
-that the assert expressions are not evaluated, and thus cannot cause any\r
-side effects.\r
- \r
- FLOAT ERRORS\r
-\r
-Floating point numbers are decimal fractions, decimal fractions do not\r
-accurately equate to normal fractions as not every number will divide\r
-precisely by ten. This creates the potential for rounding errors in\r
-calculations that use floating point numbers. The following program\r
-illustrates one such example of rounding error problems;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- float number;\r
- \r
- for(number = 1; number > 0.4; number -= 0.01)\r
- printf("\n%f",number);\r
- }\r
- \r
-At about 0.47 (depending upon the host computer and compiler) the program\r
-starts to store an inaccurate value for 'number'.\r
-\r
-This problem can be minimised by using longer floating point numbers,\r
-doubles or long doubles that have larger storage space allocated to them.\r
-For really accurate work though, you should use integers and only convert\r
-to a floating point number for display. You also should notice that most\r
-C compilers default floating point numbers to `doubles' and when using\r
-`float' types have to convert the double down to a float!\r
-\r
-\r
- \r
- ERROR HANDLING\r
-\r
-When a system error occurs within a program, for example when an attempt\r
-to open a file fails, it is helpful to the program's user to display a\r
-message reporting the failure. Equally it is useful to the program's\r
-developer to know why the error occurred, or at least as much about it as\r
-possible. To this end the ANSI standard on C describes a function,\r
-perror(), which has the prototype;\r
-\r
-\r
- void perror(const char *s);\r
-\r
-and is used to display an error message. The program's own prefixed error\r
-message is passed to perror() as the string parameter. This error message\r
-is displayed by perror() followed by the host's system error separated by\r
-a colon. The following example illustrates a use for perror();\r
-\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- FILE *fp;\r
- char fname[] = "none.xyz";\r
- \r
- fp = fopen(fname,"r");\r
- \r
- if(!fp)\r
- perror(fname);\r
- return;\r
- }\r
- \r
-If the fopen() operation fails, a message similar to;\r
-\r
- none.xyz: No such file or directory\r
-\r
-is displayed.\r
-\r
-You should note, perror() sends its output to the predefined stream\r
-`stderr', which is usually the host computer's display unit.\r
-\r
-\r
-perror() finds its message from the host computer via the global variable\r
-'errno' that is set by most, but not all system functions.\r
-\r
-Unpleasant errors might justify the use of abort(). abort() is a function\r
-that terminates the running program with a message;\r
-\r
- "Abnormal program termination"\r
-\r
-and returns an exit code of 3 to the parent process or operating system.\r
-\r
-\r
-\r
-Critical Error Handling With The IBM PC AND DOS\r
-\r
-The IBM PC DOS operating system provides a user amendable critical error\r
-handling function. This function is usually discovered by attempting to\r
-write to a disk drive that does not have a disk in it, in which case the\r
-familiar;\r
-\r
- Not ready error writing drive A\r
- Abort Retry Ignore?\r
-\r
-Message is displayed on the screen. Fine when it occurs from within a DOS\r
-program, not so fine from within your own program!\r
-\r
-The following example program shows how to redirect the DOS critical\r
-error interrupt to your own function;\r
-\r
-\r
- /* DOS critical error handler test */\r
- \r
- #include <stdio.h>\r
- #include <dos.h>\r
- \r
- void interrupt new_int();\r
- void interrupt (*old_int)();\r
- \r
- char status;\r
- \r
- main()\r
- {\r
- FILE *fp;\r
- \r
- old_int = getvect(0x24);\r
- \r
- /* Set critical error handler to my function */\r
- setvect(0x24,new_int);\r
- \r
- /* Generate an error by not having a disc in drive A */\r
- fp = fopen("a:\\data.txt","w+");\r
- \r
- /* Display error status returned */\r
- printf("\nStatus == %d",status);\r
- \r
- }\r
- \r
- void interrupt new_int()\r
- {\r
- /* set global error code */\r
- status = _DI;\r
- \r
- /* ignore error and return */\r
- _AL = 0;\r
- }\r
- \r
-When the DOS critical error interrupt is called, a status message is\r
-passed in the low byte of the DI register. This message is one of;\r
-\r
-Code Meaning\r
- \r
-00 Write-protect error\r
-01 Unknown unit\r
-02 Drive not ready\r
-03 Unknown command\r
-04 Data error, bad CRC\r
-05 Bad request structure\r
- length\r
-06 Seek error\r
-07 Unknown media type\r
-08 Sector not found\r
-09 Printer out of paper\r
-0A Write error\r
-0B Read error\r
-0C General failure\r
-\r
-Your critical error interrupt handler can transfer this status message\r
-into a global variable, and then set the result message held in register\r
-AL to one of;\r
-\r
-\r
-Code Action\r
- \r
-00 Ignore error\r
-01 Retry\r
-02 Terminate program\r
-03 Fail (Available with\r
- DOS 3.3 and above)\r
-\r
-\r
-If you choose to set AL to 02, terminate program, you should ensure ALL\r
-files are closed first since DOS will terminate the program abruptly,\r
-leaving files open and memory allocated, not a pretty state to be in!\r
-\r
-\r
-The example program shown returns an ignore status from the critical\r
-error interrupt, and leaves the checking of any errors to the program\r
-itself. So, in this example after the call to fopen() we could check the\r
-return value in fp, and if it reveals an error (NULL in this case) we\r
-could then check the global variable status and act accordingly, perhaps\r
-displaying a polite message to the user to put a disk in the floppy drive\r
-and ensure that the door is closed.\r
-\r
-The following is a practical function for checking whether a specified\r
-disc drive can be accessed. It should be used with the earlier critical\r
-error handler and global variable `status'.\r
-\r
- int DISCOK(int drive)\r
- {\r
- /* Checks for whether a disc can be read */\r
- /* Returns false (zero) on error */\r
- /* Thus if(!DISCOK(drive)) */\r
- /* error(); */\r
- \r
- unsigned char buffer[25];\r
- \r
- /* Assume okay */\r
- status = 0;\r
- \r
- /* If already logged to disc, return okay */\r
- if ('A' + drive == diry[0])\r
- return(1);\r
- \r
- /* Attempt to read disc */\r
- memset(buffer,0,20);\r
- sprintf(buffer,"%c:$$$.$$$",'A'+drive);\r
- \r
- _open(buffer,O_RDONLY);\r
- \r
- /* Check critical error handler status */\r
- if (status == 0)\r
- return(1);\r
- \r
- /* Disc cannot be read */\r
- return(0);\r
- }\r
- \r
- CAST\r
-\r
-\r
-Casting tells the compiler what a data type is, and can be used to change\r
-a data type. For example, consider the following;\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- int x;\r
- int y;\r
- \r
- x = 10;\r
- y = 3;\r
- \r
- printf("\n%lf",x / y);\r
- }\r
-\r
-The printf() function has been told to expect a double. However, the\r
-compiler sees the variables `x' and `y' as integers, and an error occurs!\r
-To make this example work we must tell the compiler that the result of\r
-the expression x / y is a double, this is done with a cast thus;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- int x;\r
- int y;\r
- \r
- x = 10;\r
- y = 3;\r
- \r
- printf("\n%lf",(double)(x / y));\r
- }\r
-\r
-Notice the data type `double' is enclosed by parenthesis, and so is the\r
-expression to convert. But now, the compiler knows that the result of the\r
-expression is a double, but it still knows that the variables `x' and `y'\r
-are integers and so an integer division will be carried out. We have to\r
-cast the constants thus;\r
-\r
- #include <stdio.h>\r
- \r
- void main()\r
- {\r
- int x;\r
- int y;\r
- \r
- x = 10;\r
- y = 3;\r
- \r
- printf("\n%lf",(double)(x) / (double)(y));\r
- }\r
-\r
-Because both of the constants are doubles, the compiler knows that the\r
-outcome of the expression will also be a double.\r
-\r
-\r
-\r
- \r
- THE IMPORTANCE OF PROTOTYPING\r
-\r
-Prototyping a function involves letting the compiler know in advance what\r
-type of values a function will receive and return. For example, lets look\r
-at strtok(). This has the prototype;\r
-\r
-\r
- char *strtok(char *s1, const char *s2);\r
-\r
-This prototype tells the compiler that strtok() will return a character\r
-pointer, the first received parameter will be a pointer to a character\r
-string, and that string can be changed by strtok(), and the last\r
-parameter will be a pointer to a character string that strtok() cannot\r
-change.\r
-\r
-The compiler knows how much space to allocate for the return parameter,\r
-sizeof(char *), but without a prototype for the function the compiler\r
-will assume that the return value of strtok() is an integer, and will\r
-allocate space for a return type of int, that is sizeof(int). If an\r
-integer and a character pointer occupy the same number of bytes on the\r
-host computer no major problems will occur, but if a character pointer\r
-occupies more space than an integer, then the compiler wont have\r
-allocated enough space for the return value and the return from a call to\r
-strtok() will overwrite some other bit of memory. If, as so often happens\r
-the return value is returned via the stack, the results of confusing the\r
-compiler can be disastrous!\r
-\r
-Thankfully most C compilers will warn the programmer if a call to a\r
-function has been made without a prototype, so that you can add the\r
-required function prototypes.\r
-\r
-Consider the following example that will not compile on most modern C\r
-compilers due to the nasty error in it;\r
-\r
-\r
- #include <stdio.h>\r
- \r
- int FUNCA(int x, int y)\r
- {\r
- return(MULT(x,y));\r
- }\r
- \r
- double MULT(double x, double y)\r
- {\r
- return(x * y);\r
- }\r
- \r
- \r
- main()\r
- {\r
- printf("\n%d",FUNCA(5,5));\r
- }\r
- \r
-When the compiler first encounters the function MULT() it is as a call\r
-from within FUNCA(). In the absence of any prototype for MULT() the\r
-compiler assumes that MULT() returns an integer. When the compiler finds\r
-the definition for function MULT() it sees that a return of type double\r
-has been declared. The compiler then reports an error in the compilation\r
-saying something like;\r
-\r
-\r
- "Type mismatch in redclaration of function 'MULT'"\r
-\r
-What the compiler is really trying to say is, prototype your functions\r
-before using them! If this example did compile, and was then run it\r
-probably would crash the computer's stack and cause a system hang.\r
-\r
- \r
- POINTERS TO FUNCTIONS\r
-\r
-C allows a pointer to point to the address of a function, and this\r
-pointer to be called rather than specifying the function. This is used by\r
-interrupt changing functions and may be used for indexing functions\r
-rather than using switch statements. For example;\r
-\r
- #include <stdio.h>\r
- #include <math.h>\r
- \r
- double (*fp[7])(double x);\r
- \r
- void main()\r
- {\r
- double x;\r
- int p;\r
- \r
- fp[0] = sin;\r
- fp[1] = cos;\r
- fp[2] = acos;\r
- fp[3] = asin;\r
- fp[4] = tan;\r
- fp[5] = atan;\r
- fp[6] = ceil;\r
- \r
- p = 4;\r
- \r
- x = fp[p](1.5);\r
- printf("\nResult %lf",x);\r
- }\r
-\r
-This example program defines an array of pointers to functions, (*fp[])()\r
-that are then called dependant upon the value in the indexing variable p.\r
-This program could also be written;\r
-\r
- #include <stdio.h>\r
- #include <math.h>\r
- \r
- void main()\r
- {\r
- double x;\r
- int p;\r
- \r
- p = 4;\r
- \r
- switch(p)\r
- {\r
- case 0 : x = sin(1.5);\r
- break;\r
- case 1 : x = cos(1.5);\r
- break;\r
- case 2 : x = acos(1.5);\r
- break;\r
- case 3 : x = asin(1.5);\r
- break;\r
- case 4 : x = tan(1.5);\r
- break;\r
- case 5 : x = atan(1.5);\r
- break;\r
- case 6 : x = ceil(1.5);\r
- break;\r
- }\r
- puts("\nResult %lf",x);\r
- }\r
-\r
-The first example, using pointers to the functions, compiles into much\r
-smaller code and executes faster than the second example.\r
-\r
-The table of pointers to functions is a useful facility when writing\r
-language interpreters, the program compares an entered instruction\r
-against a table of key words that results in an index variable being set\r
-and then the program simply needs to call the function pointer indexed by\r
-the variable, rather than wading through a lengthy switch() statement.\r
-\r
- \r
- DANGEROUS PITFALLS\r
-\r
-One of the most dangerous pitfalls can occur with the use of gets(). This\r
-function accepts input from the stream stdin until it receives a newline\r
-character, which it does not pass to the program. All the data it\r
-receives is stored in memory starting at the address of the specified\r
-string, and quite happily overflowing into other variables! This danger\r
-can be avoided by using fgets() that allows a maximum number of\r
-characters to be specified, so you can avoid overflow problems. Notice\r
-though that fgets() does retain the newline character scanf() is another\r
-function best avoided. It accepts input from stdin and stores the\r
-received data at the addresses provided to it. If those addresses are not\r
-really addresses where the data ends up is anybodys guess!\r
-\r
-This example is okay, since scanf() has been told to store the data at\r
-the addresses occupied by the two variables `x' and `y'.\r
-\r
-\r
- void main()\r
- {\r
- int x;\r
- int y;\r
- \r
- scanf("%d%d",&x,&y);\r
- }\r
- \r
-But in this example scanf() has been told to store the data at the\r
-addresses suggested by the current values of `x' and `y'! An easy and\r
-common mistake to make, and yet one that can have very peculiar effects.\r
-\r
-\r
- void main()\r
- {\r
- int x;\r
- int y;\r
- \r
- scanf("%d%d",x,y);\r
- }\r
- \r
-The answer is, don't use scanf(), use fgets() and parse your string\r
-manually using the standard library functions strtod(), strtol() and\r
-strtoul().\r
-\r
-Here is the basis of a simple input string parser that returns the\r
-individual input fields from an entered string;\r
-\r
- #include <stdio.h>\r
- #include <string.h>\r
- \r
- void main()\r
- {\r
- char input[80];\r
- char *p;\r
- \r
- puts("\nEnter a string ");\r
- fgets(input,79,stdin);\r
- \r
- /* now parse string for input fields */\r
- puts("The fields entered are:");\r
- p = strtok(input,", ");\r
- while(p)\r
- {\r
- puts(p);\r
- p = strtok(NULL,", ");\r
- }\r
- }\r
- \r
- \r
- SIZEOF\r
-\r
-A preprocessor instruction, `sizeof', returns the size of an item, be it\r
-a structure, pointer, string or whatever. However! take care when using\r
-`sizeof'. Consider the following program;\r
-\r
-\r
- #include <stdio.h>\r
- #include <mem.h>\r
- \r
- char string1[80]; char *text = "This is a string of data" ;\r
- \r
- void main()\r
- {\r
- /* Initialise string1 correctly */\r
- memset(string1,0,sizeof(string1));\r
- \r
- /* Copy some text into string1 ? */\r
- memcpy(string1,text,sizeof(text));\r
- \r
- /* Display string1 */\r
- printf("\nString 1 = %s\n",string1);\r
- }\r
- \r
-What it is meant to do is initialise all 80 elements of string1 to\r
-zeroes, which it does alright, and then copy the constant string `text'\r
-into the variable `string1'. However, variable text is a pointer, and so\r
-the sizeof(text) instruction returns the size of the character pointer\r
-(perhaps two bytes) rather than the length of the string pointed to by\r
-the pointer! If the length of the string pointed to by `text' happened\r
-to be the same as the size of a character pointer then no error would be\r
-noticed.\r
-\r
- \r
- INTERRUPTS\r
-\r
-The IBM PC BIOS and DOS contain functions that may be called by a program\r
-by way of the function's interrupt number. The address of the function\r
-assigned to each interrupt is recorded in a table in RAM called the\r
-interrupt vector table. By changing the address of an interrupt vector a\r
-program can effectively disable the original interrupt function and\r
-divert any calls to it to its own function. This was done by the critical\r
-error handler described in the section on error handling.\r
-\r
-Borland's Turbo C provides two library functions for reading and changing\r
-an interrupt vector. These are: setvect() and getvect(). The\r
-corresponding Microsoft C library functions are: _dos_getvect() and\r
-_dos_setvect().\r
-\r
-getvect() has the function prototype;\r
-\r
- void interrupt(*getvect(int interrupt_no))();\r
-\r
-setvect() has the prototype;\r
-\r
- void setvect(int interrupt_no, void interrupt(*func)());\r
-\r
-To read and save the address of an existing interrupt a program uses\r
-getvect() thus;\r
-\r
- /* Declare variable to record old interrupt */\r
- void interrupt(*old)(void);\r
- \r
- main()\r
- {\r
- /* get old interrupt vector */\r
- old = getvect(0x1C);\r
- .\r
- .\r
- .\r
- }\r
- \r
-Where 0x1C is the interrupt vector to be retrieved.\r
-\r
-To then set the interrupt vector to a new address, our own function, we\r
-use setvect() thus;\r
-\r
- void interrupt new(void)\r
- {\r
- .\r
- .\r
- /* New interrupt function */\r
- .\r
- .\r
- .\r
- }\r
- \r
- main()\r
- {\r
- .\r
- .\r
- .\r
- setvect(0x1C,new);\r
- .\r
- .\r
- .\r
- .\r
- }\r
- \r
-There are two important points to note about interrupts;\r
-\r
-First, if the interrupt is called by external events then before changing\r
-the vector you MUST disable the interrupt callers using disable() and\r
-then re-enable the interrupts after the vector has been changed using\r
-enable(). If a call is made to the interrupt while the vector is being\r
-changed ANYTHING could happen!\r
-\r
-Secondly, before your program terminates and returns to DOS you must\r
-reset any changed interrupt vectors! The exception to this is the\r
-critical error handler interrupt vector that is restored automatically by\r
-DOS, so your program needn't bother restoring it.\r
-\r
-This example program hooks the PC clock timer interrupt to provide a\r
-background clock process while the rest of the program continues to run.\r
-If included with your own program that requires a constantly displayed\r
-clock on screen, you need only amend the display coordinates in the call\r
-to puttext(). Sincle the closk display code is called by a hardware\r
-issued interrupt, your program can start the clock and forget it until it\r
-terminates.\r
-\r
- \r
- /* Compile in LARGE memory model */\r
- \r
- #include <stdio.h>\r
- #include <dos.h>\r
- #include <time.h>\r
- #include <conio.h>\r
- #include <stdlib.h>\r
- \r
- enum { FALSE, TRUE };\r
- \r
- #define COLOUR (BLUE << 4) | YELLOW\r
- \r
- #define BIOS_TIMER 0x1C\r
- \r
- static unsigned installed = FALSE;\r
- static void interrupt (*old_tick) (void);\r
- \r
- static void interrupt tick (void)\r
- {\r
- int i;\r
- struct tm *now;\r
- time_t this_time;\r
- char time_buf[9];\r
- static time_t last_time = 0L;\r
- static char video_buf[20] =\r
- {\r
- ' ', COLOUR, '0', COLOUR, '0', COLOUR, ':', COLOUR, '0',\r
- COLOUR,\r
- '0', COLOUR, ':', COLOUR, '0', COLOUR, '0', COLOUR, ' ',\r
- COLOUR\r
- };\r
- \r
- enable ();\r
- \r
- if (time (&this_time) != last_time)\r
- {\r
- last_time = this_time;\r
- \r
- now = localtime(&this_time);\r
- \r
- sprintf(time_buf, "%02d:%02d.%02d",now->tm_hour,now-\r
- >tm_min,now->tm_sec);\r
- \r
- for (i = 0; i < 8; i++)\r
- {\r
- video_buf[(i + 1) << 1] = time_buf[i];\r
- }\r
- \r
- puttext (71, 1, 80, 1, video_buf);\r
- }\r
- \r
- old_tick ();\r
- }\r
- \r
- void stop_clock (void)\r
- {\r
- if (installed)\r
- {\r
- setvect (BIOS_TIMER, old_tick);\r
- installed = FALSE;\r
- }\r
- }\r
- \r
- void start_clock (void)\r
- {\r
- static unsigned first_time = TRUE;\r
- \r
- if (!installed)\r
- {\r
- if (first_time)\r
- {\r
- atexit (stop_clock);\r
- first_time = FALSE;\r
- }\r
- \r
- old_tick = getvect (BIOS_TIMER);\r
- setvect (BIOS_TIMER, tick);\r
- installed = TRUE;\r
- }\r
- }\r
- \r
- SIGNAL\r
-\r
-Interrupts raised by the host computer can be trapped and diverted in\r
-several ways. A simple method is to use signal().\r
-\r
-Signal() takes two parameters in the form;\r
-\r
- void (*signal (int sig, void (*func) (int))) (int);\r
-\r
-The first parameter, `sig' is the signal to be caught. These are often\r
-predefined by the header file `signal.h'.\r
-\r
-The second parameter is a pointer to a function to be called when the\r
-signal is raised. This can either be a user function, or a macro defined\r
-in the header file `signal.h' to do some arbitrary task, such as ignore\r
-the signal for example.\r
-\r
-On a PC platform, it is often useful to disable the `ctrl-break' key\r
-combination that is used to terminate a running program by the user. The\r
-following PC signal() call replaces the predefined signal `SIGINT', which\r
-equates to the ctrl-break interrupt request, with the predefined macro\r
-`SIG-IGN', ignore the request;\r
-\r
-\r
- signal(SIGINT,SIG_IGN);\r
-\r
-This example catches floating point errors on a PC, and zero divisions!\r
-\r
- #include <stdio.h>\r
- #include <signal.h>\r
- \r
- void (*old_sig)();\r
- \r
- void catch(int sig)\r
- {\r
- printf("Catch was called with: %d\n",sig);\r
- }\r
- \r
- \r
- void main()\r
- {\r
- int a;\r
- int b;\r
- \r
- old_sig = signal(SIGFPE,catch);\r
- \r
- a = 0;\r
- b = 10 / a;\r
- \r
- /* Restore original handler before exiting! */\r
- signal(SIGFPE,old_sig);\r
- }\r
- \r
- \r
- SORTING AND SEARCHING\r
-\r
-The ANSI C standard defines qsort(), a function for sorting a table of\r
-data. The function follows the format;\r
-\r
- qsort(void *base,size_t elements,size_t width,int (*cmp)(void *,\r
-void *));\r
-\r
-The following short program illustrates the use of qsort() with a\r
-character array.\r
-\r
- #include <string.h>\r
- \r
- main()\r
- {\r
- int n;\r
- char data[10][20];\r
- \r
- /* Initialise some arbirary data */\r
- \r
- strcpy(data[0],"RED");\r
- strcpy(data[1],"BLUE");\r
- strcpy(data[2],"GREEN");\r
- strcpy(data[3],"YELLOW");\r
- strcpy(data[4],"INDIGO");\r
- strcpy(data[5],"BROWN");\r
- strcpy(data[6],"BLACK");\r
- strcpy(data[7],"ORANGE");\r
- strcpy(data[8],"PINK");\r
- strcpy(data[9],"CYAN");\r
- \r
- /* Sort the data table */\r
- qsort(data[0],10,20,strcmp);\r
- \r
- /* Print the data table */\r
- for(n = 0; n < 10; n++)\r
- puts(data[n]);\r
- }\r
- \r
-\r
-Here is a program that implements the shell sort algorithm (this one is\r
-based on the routine in K & R), which sorts arrays of pointers based upon\r
-the data pointed to by the pointers;\r
-\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- #include <string.h>\r
- \r
- #define LINELEN 80\r
- #define MAXLINES 2000\r
- \r
- char *lines[MAXLINES];\r
- int lastone;\r
- \r
- void SHELL(void);\r
- \r
- void SHELL()\r
- {\r
- /* SHELL Sort Courtesy of K & R */\r
- \r
- int gap;\r
- int i;\r
- int j;\r
- char temp[LINELEN];\r
- \r
- for(gap = lastone / 2; gap > 0; gap /= 2)\r
- for(i = gap; i < lastone; i++)\r
- for(j = i - gap; j >= 0 && strcmp(lines[j] , lines[j +\r
- gap]) >\r
- 0; j -= gap)\r
- {\r
- strcpy(temp,lines[j]);\r
- strcpy(lines[j] , lines[j + gap]);\r
- strcpy(lines[j + gap] , temp);\r
- \r
- }\r
- }\r
- \r
- void main(int argc, char *argv[])\r
- {\r
- FILE *fp;\r
- char buff[100];\r
- int n;\r
-\r
- /* Check command line parameter has been given */\r
- if (argc != 2)\r
- {\r
- printf("\nError: Usage is SERVSORT file");\r
- exit(0);\r
- }\r
- \r
- /* Attempt to open file for updating */\r
- fp = fopen(argv[1],"r+");\r
- if (fp == NULL)\r
- {\r
- printf("\nError: Unable to open %s",argv[1]);\r
- exit(0);\r
- }\r
- \r
- /* Initialise element counter to zero */\r
- lastone = 0;\r
- \r
- /* Read file to be sorted */\r
- while((fgets(buff,100,fp)) != NULL)\r
- {\r
- /* Allocate memory block*/\r
- lines[lastone] = malloc(LINELEN);\r
- if (lines[lastone] == NULL)\r
- {\r
- printf("\nError: Unable to allocate memory");\r
- fclose(fp);\r
- exit(0);\r
- }\r
- strcpy(lines[lastone],buff);\r
- lastone++;\r
- \r
- if (lastone > MAXLINES)\r
- {\r
- printf("\nError: Too many lines in source file");\r
- exit(0);\r
- }\r
- }\r
- /* Call sort function */\r
- SHELL();\r
- \r
- /* Close file */\r
- fclose(fp);\r
- \r
- /* Reopen file in create mode */\r
- fp = fopen(argv[1],"w+");\r
- \r
- /* Copy sorted data from memory to disk */\r
- for(n = 0; n < lastone; n++)\r
- fputs(lines[n],fp);\r
- \r
- /* Close file finally */\r
- fclose(fp);\r
- \r
- /* Return to calling program */\r
- return(1);\r
- }\r
- \r
-\r
-If we want to use qsort() with a table of pointers we have to be a bit\r
-more clever than usual.\r
-\r
-This example uses the colours again, but this time they are stored in\r
-main memory and indexed by a table of pointers. Because we have a table\r
-of pointers to sort there are two differences between this program's\r
-qsort() and the previous one;\r
-\r
-First we can't use strcmp() as the qsort() comparison function, secondly\r
-the width of the table being sorted is sizeof(char *), that is the size\r
-of a character pointer.\r
-\r
-Notice the comparison function cmp() that receives two parameters, both\r
-are pointers to a pointer. qsort() sends to this function the values held\r
-in data[], which are in turn pointers to the data. So we need to use this\r
-indirection to locate the data, otherwise we would be comparing the\r
-addresses at which the data is held rather than the data itself!\r
-\r
- #include <alloc.h>\r
- #include <string.h>\r
- \r
- /* Function prototype for comparison function */\r
- int (cmp)(char **,char **);\r
- \r
- int cmp(char **s1, char **s2)\r
- {\r
- /* comparison function using pointers to pointers */\r
- return(strcmp(*s1,*s2));\r
- }\r
- \r
- main()\r
- {\r
- int n;\r
- char *data[10];\r
- \r
- for(n = 0; n < 10; n++)\r
- data[n] = malloc(20);\r
- \r
- strcpy(data[0],"RED");\r
- strcpy(data[1],"BLUE");\r
- strcpy(data[2],"GREEN");\r
- strcpy(data[3],"YELLOW");\r
- strcpy(data[4],"INDIGO");\r
- strcpy(data[5],"BROWN");\r
- strcpy(data[6],"BLACK");\r
- strcpy(data[7],"ORANGE");\r
- strcpy(data[8],"PINK");\r
- strcpy(data[9],"CYAN");\r
- \r
- /* The data table is comprised of pointers */\r
- /* so the call to qsort() must reflect this */\r
- qsort(data,10,sizeof(char *),cmp);\r
- \r
- for(n = 0; n < 10; n++)\r
- puts(data[n]);\r
- }\r
- \r
-The quick sort is a fast sorting algorithm that works by subdividing the\r
-data table into two sub-tables and then subdividing the sub-tables. As it\r
-subdivides the table, so it compares the elements in the table and swaps\r
-them as required.\r
-\r
-The following program implements the quick sort algorithm, which is\r
-usually already used by qsort();\r
-\r
-\r
- #include <string.h>\r
- \r
- #define MAXELE 2000\r
- \r
- char data[10][20];\r
- int lastone;\r
- \r
- void QKSORT()\r
- {\r
- /* Implementation of QUICKSORT algorithm */\r
- \r
- int i;\r
- int j;\r
- int l;\r
- int p;\r
- int r;\r
- int s;\r
- char temp[100];\r
- static int sl[MAXELE][2];\r
- \r
- /* sl[] is an index to the sub-table */\r
- \r
- l = 0;\r
- r = lastone;\r
- p = 0;\r
- \r
- do\r
- {\r
- while(l < r)\r
- {\r
- i = l;\r
- j = r;\r
- s = -1;\r
- \r
- while(i < j)\r
- {\r
- if (strcmp(data[i],data[j]) > 0)\r
- {\r
- strcpy(temp,data[i]);\r
- strcpy(data[i],data[j]);\r
- strcpy(data[j],temp);\r
- s = 0 - s;\r
- }\r
- \r
- if (s == 1)\r
- i++;\r
- else\r
- j--;\r
- }\r
- \r
- if (i + 1 < r)\r
- {\r
- p++;\r
- sl[p][0] = i + 1;\r
- sl[p][1] = r;\r
- }\r
- r = i - 1;\r
- }\r
- if (p != 0)\r
- {\r
- l = sl[p][0];\r
- r = sl[p][1];\r
- p--;\r
- }\r
- }\r
- while(p > 0);\r
- }\r
- \r
- main()\r
- {\r
- int n;\r
- \r
- /* Initialise arbitrary data */\r
- strcpy(data[0],"RED");\r
- strcpy(data[1],"BLUE");\r
- strcpy(data[2],"GREEN");\r
- strcpy(data[3],"YELLOW");\r
- strcpy(data[4],"INDIGO");\r
- strcpy(data[5],"BROWN");\r
- strcpy(data[6],"BLACK");\r
- strcpy(data[7],"ORANGE");\r
- strcpy(data[8],"PINK");\r
- strcpy(data[9],"CYAN");\r
- \r
- /* Set last element indicator */\r
- lastone = 9;\r
- \r
- /* Call quick sort function */\r
- QKSORT();\r
- \r
- /* Display sorted list */\r
- for(n = 0; n < 10; n++)\r
- puts(data[n]);\r
- \r
- }\r
- \r
-A table sorted into ascending order can be searched with bsearch(), this\r
-takes the format;\r
-\r
- bsearch(key,base,num_elements,width,int (*cmp)(void *, void *));\r
-\r
-bsearch() returns a pointer to the first element in the table that\r
-matches the key, or zero if no match is found.\r
-\r
-Or you can write your own binary search function thus;\r
-\r
- int BSRCH(char *key, void *data, int numele, int width)\r
- {\r
- /* A binary search function returning one if found */\r
- /* Zero if not found */\r
- \r
- int bp;\r
- int tp;\r
- int mp;\r
- int result;\r
- char *p;\r
- \r
- bp = 0;\r
- tp = numele;\r
- mp = (tp + bp) / 2;\r
- \r
- /* Locate element mp in table by assigning pointer to start */\r
- /* and incrementing it by width * mp */\r
- p = data;\r
- p += width * mp;\r
- \r
- while((result = strcmp(p,key)) != 0)\r
- {\r
- if (mp >= tp)\r
- /* Not found! */\r
- return(0);\r
- if (result < 0)\r
- bp = mp + 1;\r
- else\r
- tp = mp - 1;\r
- \r
- mp = (bp + tp) / 2;\r
- p = data;\r
- p += width * mp;\r
- }\r
- return(1);\r
- }\r
- \r
- void main()\r
- {\r
- int result;\r
- char data[10][20];\r
- \r
- /* Initialise some arbirary data */\r
- \r
- strcpy(data[0],"RED");\r
- strcpy(data[1],"BLUE");\r
- strcpy(data[2],"GREEN");\r
- strcpy(data[3],"YELLOW");\r
- strcpy(data[4],"INDIGO");\r
- strcpy(data[5],"BROWN");\r
- strcpy(data[6],"BLACK");\r
- strcpy(data[7],"ORANGE");\r
- strcpy(data[8],"PINK");\r
- strcpy(data[9],"CYAN");\r
- \r
- /* Sort the data table */\r
- qsort(data[0],10,20,strcmp);\r
- \r
- result = BSRCH("CYAN",data[0],10,20);\r
- \r
- printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");\r
- }\r
- \r
-There are other sorting algorithms as well. This program incorporates the\r
-QUICK SORT, BUBBLE SORT, FAST BUBBLE SORT, INSERTION SORT and SHELL SORT\r
-for comparing how each performs on a random 1000 item string list;\r
-\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- #include <string.h>\r
- \r
- char data[1000][4];\r
- char save[1000][4];\r
- \r
- int lastone;\r
- \r
- void INITDATA(void);\r
- void QKSORT(void);\r
- void SHELL(void);\r
- void BUBBLE(void);\r
- void FBUBBLE(void);\r
- void INSERTION(void);\r
- void MKDATA(void);\r
- \r
- void QKSORT()\r
- {\r
- /* Implementation of QUICKSORT algorithm */\r
- \r
- int i;\r
- int j;\r
- int l;\r
- int p;\r
- int r;\r
- int s;\r
- char temp[20];\r
- static int sl[1000][2];\r
- \r
- l = 0;\r
- r = lastone;\r
- p = 0;\r
- \r
- do\r
- {\r
- while(l < r)\r
- {\r
- i = l;\r
- j = r;\r
- s = -1;\r
- \r
- while(i < j)\r
- {\r
- if (strcmp(data[i],data[j]) > 0)\r
- {\r
- strcpy(temp,data[i]);\r
- strcpy(data[i],data[j]);\r
- strcpy(data[j],temp);\r
- s = 0 - s;\r
- }\r
- \r
- if (s == 1)\r
- i++;\r
- else\r
- j--;\r
- }\r
- \r
- if (i + 1 < r)\r
- {\r
- p++;\r
- sl[p][0] = i + 1;\r
- sl[p][1] = r;\r
- }\r
- r = i - 1;\r
- }\r
- if (p != 0)\r
- {\r
- l = sl[p][0];\r
- r = sl[p][1];\r
- p--;\r
- }\r
- }\r
- while(p > 0);\r
- }\r
- \r
- void SHELL()\r
- {\r
- /* SHELL Sort Courtesy of K & R */\r
- \r
- int gap;\r
- int i;\r
- int j;\r
- char temp[20];\r
- \r
- for(gap = lastone / 2; gap > 0; gap /= 2)\r
- for(i = gap; i < lastone; i++)\r
- for(j = i - gap; j >= 0 && strcmp(data[j] , data[j + gap])\r
- > 0;\r
- j -= gap)\r
- {\r
- strcpy(temp,data[j]);\r
- strcpy(data[j] , data[j + gap]);\r
- strcpy(data[j + gap] , temp);\r
- }\r
- }\r
- \r
- void BUBBLE()\r
- {\r
- int a;\r
- int b;\r
- char temp[20];\r
- \r
- for(a = lastone; a >= 0; a--)\r
- {\r
- for(b = 0; b < a; b++)\r
- {\r
- if(strcmp(data[b],data[b + 1]) > 0)\r
- {\r
- strcpy(temp,data[b]);\r
- strcpy(data[b] , data[b + 1]);\r
- strcpy(data[b + 1] , temp);\r
- }\r
- }\r
- }\r
- }\r
- \r
- void FBUBBLE()\r
- {\r
- /* bubble sort with swap flag*/\r
- \r
- int a;\r
- int b;\r
- int s;\r
- char temp[20];\r
- \r
- s = 1;\r
- \r
- for(a = lastone; a >= 0 && s == 1; a--)\r
- {\r
- s = 0;\r
- for(b = 0; b < a; b++)\r
- {\r
- if(strcmp(data[b],data[b + 1]) > 0)\r
- {\r
- strcpy(temp,data[b]);\r
- strcpy(data[b] , data[b + 1]);\r
- strcpy(data[b + 1] , temp);\r
- s = 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- void INSERTION()\r
- {\r
- int a;\r
- int b;\r
- char temp[20];\r
- \r
- for(a = 0; a < lastone; a++)\r
- {\r
- b = a;\r
- strcpy(temp,data[a + 1]);\r
- while(b >= 0)\r
- {\r
- if (strcmp(temp,data[b]) < 0)\r
- {\r
- strcpy(data[b+1],data[b]);\r
- b--;\r
- }\r
- else\r
- break;\r
- }\r
- strcpy(data[b+1],temp);\r
- }\r
- }\r
- \r
- void MKDATA()\r
- {\r
- /* Initialise arbitrary data */\r
- /* Uses random(), which is not ANSI C */\r
- /* Returns a random number between 0 and n - 1 */\r
- \r
- int n;\r
- for(n = 0; n < 1000; n++)\r
- sprintf(save[n],"%d",random(1000));\r
- }\r
- \r
- void INITDATA()\r
- {\r
- int n;\r
- \r
- for(n = 0 ; n < 1000; n++)\r
- strcpy(data[n],save[n]);\r
- }\r
- \r
- void main()\r
- {\r
- MKDATA();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call quick sort function */\r
- QKSORT();\r
- \r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 1000;\r
- \r
- /* Call shell sort function */\r
- SHELL();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call bubble sort function */\r
- BUBBLE();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call bubble sort with swap flag function */\r
- FBUBBLE();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call insertion sort function */\r
- INSERTION();\r
- }\r
-\r
-Here are the profiler results of the above test program run on 1000 and\r
-5000 random items;\r
-\r
-STRING SORT - 1000 RANDOM ITEMS\r
-\r
-FBUBBLE 26.436 sec 41%\r
-|********************************************\r
-BUBBLE 26.315 sec 41%\r
-|*******************************************\r
-INSERTION 10.210 sec 15% |***************\r
-SHELL 0.8050 sec 1% |*\r
-QKSORT 0.3252 sec <1% |\r
-\r
-STRING SORT - 5000 RANDOM ITEMS\r
-\r
-FBUBBLE 563.70 sec 41%\r
-|********************************************\r
-BUBBLE 558.01 sec 41%\r
-|********************************************\r
-INSERTION 220.61 sec 16% |***************\r
-SHELL 5.2531 sec <1% |\r
-QKSORT 0.8379 sec <1% |\r
-\r
-Here is the same test program amended for sorting tables of integers;\r
-\r
- /* Integer sort test program */\r
- \r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- \r
- void INITDATA(void);\r
- void QKSORT(void);\r
- void SHELL(void);\r
- void BUBBLE(void);\r
- void FBUBBLE(void);\r
- void INSERTION(void);\r
- void MKDATA(void);\r
- \r
- int data[1000];\r
- int save[1000];\r
- \r
- int lastone;\r
- \r
- void QKSORT()\r
- {\r
- /* Implementation of QUICKSORT algorithm */\r
- \r
- int i;\r
- int j;\r
- int l;\r
- int p;\r
- int r;\r
- int s;\r
- int temp;\r
- static int sl[1000][2];\r
- \r
- l = 0;\r
- r = lastone;\r
- p = 0;\r
- \r
- do\r
- {\r
- while(l < r)\r
- {\r
- i = l;\r
- j = r;\r
- s = -1;\r
- \r
- while(i < j)\r
- {\r
- if (data[i] > data[j])\r
- {\r
- temp = data[i];\r
- data[i] = data[j];\r
- data[j] = temp;\r
- s = 0 - s;\r
- }\r
- \r
- if (s == 1)\r
- i++;\r
- else\r
- j--;\r
- }\r
- \r
- if (i + 1 < r)\r
- {\r
- p++;\r
- sl[p][0] = i + 1;\r
- sl[p][1] = r;\r
- }\r
- r = i - 1;\r
- }\r
- if (p != 0)\r
- {\r
- l = sl[p][0];\r
- r = sl[p][1];\r
- p--;\r
- }\r
- }\r
- while(p > 0);\r
- }\r
- \r
- void SHELL()\r
- {\r
- /* SHELL Sort Courtesy of K & R */\r
- \r
- int gap;\r
- int i;\r
- int j;\r
- int temp;\r
- \r
- for(gap = lastone / 2; gap > 0; gap /= 2)\r
- for(i = gap; i < lastone; i++)\r
- for(j = i - gap; j >= 0 && data[j] > data[j + gap];\r
- j -= gap)\r
- {\r
- temp = data[j];\r
- data[j] = data[j + gap];\r
- data[j + gap] = temp;\r
- }\r
- }\r
- \r
- void BUBBLE()\r
- {\r
- int a;\r
- int b;\r
- int temp;\r
- \r
- for(a = lastone; a >= 0; a--)\r
- {\r
- for(b = 0; b < a; b++)\r
- {\r
- if(data[b] > data[b + 1])\r
- {\r
- temp = data[b];\r
- data[b] = data[b + 1];\r
- data[b + 1] = temp;\r
- }\r
- }\r
- }\r
- }\r
- \r
- void FBUBBLE()\r
- {\r
- /* bubble sort with swap flag */\r
- \r
- int a;\r
- int b;\r
- int s;\r
- int temp;\r
- \r
- s = 1;\r
- \r
- for(a = lastone; a >= 0 && s == 1; a--)\r
- {\r
- s = 0;\r
- for(b = 0; b < lastone - a; b++)\r
- {\r
- if(data[b] > data[b + 1])\r
- {\r
- temp = data[b];\r
- data[b] = data[b + 1];\r
- data[b + 1] = temp;\r
- s = 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- void INSERTION()\r
- {\r
- int a;\r
- int b;\r
- int temp;\r
- \r
- for(a = 0; a < lastone; a++)\r
- {\r
- b = a;\r
- temp = data[a + 1];\r
- while(b >= 0)\r
- {\r
- if (temp < data[b])\r
- {\r
- data[b+1] = data[b];\r
- b--;\r
- }\r
- else\r
- break;\r
- }\r
- data[b+1] = temp;\r
- }\r
- }\r
- \r
- void MKDATA()\r
- {\r
- int n;\r
- \r
- for(n = 0; n < 1000; n++)\r
- save[n] = random(1000);\r
- }\r
- \r
- void INITDATA()\r
- {\r
- int n;\r
- \r
- for(n = 0; n < 1000; n++)\r
- data[n] = save[n];\r
- }\r
- \r
- void main()\r
- {\r
- int n;\r
- \r
- /* Create 1000 random elements */\r
- MKDATA();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call quick sort function */\r
- QKSORT();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 1000;\r
- \r
- /* Call shell sort function */\r
- SHELL();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call bubble sort function */\r
- BUBBLE();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call bubble sort with swap flag function */\r
- FBUBBLE();\r
- \r
- /* Initialise arbitrary data */\r
- INITDATA();\r
- \r
- /* Set last element indicator */\r
- lastone = 999;\r
- \r
- /* Call insertion sort function */\r
- INSERTION();\r
- }\r
-\r
-And here are the profiler results for this program;\r
-\r
-INTEGER SORTS - 1000 RANDOM NUMBERS (0 - 999)\r
-\r
-FBUBBLE 3.7197 sec 41%\r
-|********************************************\r
-BUBBLE 3.5981 sec 39%\r
-|******************************************\r
-INSERTION 1.4258 sec 15% |***************\r
-SHELL 0.1207 sec 1% |*\r
-QKSORT 0.0081 sec <1% |\r
-\r
-INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 999)\r
-\r
-FBUBBLE 92.749 sec 42%\r
-|********************************************\r
-BUBBLE 89.731 sec 41%\r
-|********************************************\r
-INSERTION 35.201 sec 16% |***************\r
-SHELL 0.9838 sec <1% |\r
-QKSORT 0.0420 sec <1% |\r
-\r
-INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 99)\r
-\r
-FBUBBLE 92.594 sec 42% |*****************************************\r
-BUBBLE 89.595 sec 40% |****************************************\r
-INSERTION 35.026 sec 16% |***************\r
-SHELL 0.7563 sec <1% |\r
-QKSORT 0.6018 sec <1% |\r
-\r
-INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 9)\r
-\r
-FBUBBLE 89.003 sec 41%\r
-|*******************************************\r
-BUBBLE 86.921 sec 40%\r
-|*******************************************\r
-INSERTION 31.544 sec 14% |***************\r
-QKSORT 6.0358 sec 2% |**\r
-SHELL 0.5424 sec <1% |\r
-\r
-INTEGER SORTS - 5000 DESCENDING ORDERED NUMBERS\r
-\r
-FBUBBLE 122.99 sec 39%\r
-|******************************************\r
-BUBBLE 117.22 sec 37% |****************************************\r
-INSERTION 70.595 sec 22% |**********************\r
-SHELL 0.6438 sec <1% |\r
-QKSORT 0.0741 sec <1% |\r
-\r
-INTEGER SORTS - 5000 ORDERED NUMBERS\r
-\r
-BUBBLE 62.908 sec 99%\r
-|******************************************\r
-SHELL 0.3971 sec <1% |\r
-INSERTION 0.0510 sec <1% |\r
-QKSORT 0.0382 sec <1% |\r
-FBUBBLE 0.0251 sec <1% |\r
-\r
-INTEGER SORTS - 10000 RANDOM NUMBERS (0 - 999)\r
-\r
-FBUBBLE 371.18 sec 42% |****************************************\r
-BUBBLE 359.06 sec 41% |***************************************\r
-INSERTION 140.88 sec 16% |**************\r
-SHELL 2.0423 sec <1% |\r
-QKSORT 0.6183 sec <1% |\r
-\r
-Theory has it that the performance of a sorting algorithm is dependant\r
-upon;\r
-\r
- a) the number of items to be sorted and\r
- b) how unsorted the list is to start with.\r
-\r
-With this in mind it is worth testing the various sorting routines\r
-described here to determine which one will best suit your particular\r
-application. If you examine the above profiler results you will see that:\r
-\r
- 1) With an already sorted list FBUBBLE() executes fastest\r
- \r
- 2) With a random list of small variations between the values SHELL()\r
- executes fastest\r
- \r
- 3) With a random list of large variations between the values\r
- QKSORT()\r
- executes the fastest\r
-\r
-What the profiler does not highlight is that when the comparison aspect\r
-of a sort function takes a disproportionately long time to execute in\r
-relation to the rest of the sort function, then the bubble sort with a\r
-swap flag will execute faster than the bubble sort with out a swap flag.\r
-\r
-When considering a sort routine take into consideration memory\r
-constraints and the type of data to be sorted as well as the relative\r
-performances of the sort functions. Generally, the faster a sort\r
-operates, the more memory it requires. Compare the simple bubble sort\r
-with the quick sort, and you will see that the quick sort requires far\r
-more memory than the bubble sort.\r
-\r
- \r
- DYNAMIC MEMORY ALLOCATION\r
-\r
-\r
-If a program needs a table of data, but the size of the table is\r
-variable, perhaps for a list of all file names in the current directory,\r
-it is inefficient to waste memory by declaring a data table of the\r
-maximum possible size. Rather it is better to dynamically allocate the\r
-table as required.\r
-\r
-Turbo C allocates RAM as being available for dynamic allocation into an\r
-area called the "heap". The size of the heap varies with memory model.\r
-The tiny memory model defaults to occupy 64K of RAM. The small memory\r
-model allocates upto 64K for the program/code and heap with a far heap\r
-being available within the remainder of conventional memory. The other\r
-memory models make all conventional memory available to the heap. This is\r
-significant when programming in the tiny memory model when you want to\r
-reduce the memory overhead of your program to a minimum. The way to do\r
-this is to reduce the heap to a minimum size. The smallest is 1 byte.\r
-\r
-C provides a function malloc() which allocates a block of free memory of\r
-a specified size and returns a pointer to the start of the block; it also\r
-provides free() which deallocates a block of memory previously allocated\r
-by malloc(). Notice, however, that the IBM PC doesnot properly free\r
-blocks of memory, and contiuous use of malloc() and free() will\r
-fragmentise memory, eventually causing no memory to be available untill\r
-the program terminates.\r
-\r
-This program searches a specified file for a specified string (with case\r
-sensitivity). It uses malloc() to allocate just enough memory for the\r
-file to be read into memory.\r
-\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- \r
- char *buffer;\r
- \r
- void main(int argc, char *argv[])\r
- {\r
- FILE *fp;\r
- long flen;\r
- \r
- /* Check number of parameters */\r
- if (argc != 3)\r
- {\r
- fputs("Usage is sgrep <text> <file spec>",stderr);\r
- exit(0);\r
- }\r
- \r
- /* Open stream fp to file */\r
- fp = fopen(argv[2],"r");\r
- if (!fp)\r
- {\r
- perror("Unable to open source file");\r
- exit(0);\r
- }\r
- \r
- /* Locate file end */\r
- if(fseek(fp,0L,SEEK_END))\r
- {\r
- fputs("Unable to determine file length",stderr);\r
- fclose(fp);\r
- exit(0);\r
- }\r
- \r
- /* Determine file length */\r
- flen = ftell(fp);\r
- \r
- /* Check for error */\r
- if (flen == -1L)\r
- {\r
- fputs("Unable to determine file length",stderr);\r
- fclose(fp);\r
- exit(0);\r
- }\r
- \r
- /* Set file pointer to start of file */\r
- rewind(fp);\r
- \r
- /* Allocate memory buffer */\r
- buffer = malloc(flen);\r
- \r
- if (!buffer)\r
- {\r
- fputs("Unable to allocate memory",stderr);\r
- fclose(fp);\r
- exit(0);\r
- }\r
- \r
- /* Read file into buffer */\r
- fread(buffer,flen,1,fp);\r
- \r
- /* Check for read error */\r
- if(ferror(fp))\r
- {\r
- fputs("Unable to read file",stderr);\r
- \r
- /* Deallocate memory block */\r
- free(buffer);\r
- \r
- fclose(fp);\r
- exit(0);\r
- }\r
- \r
- printf("%s %s in %s",argv[1],(strstr(buffer,argv[1])) ? "was\r
- found" : "was not found",argv[2]);\r
- \r
- /* Deallocate memory block before exiting */\r
- free(buffer);\r
- fclose(fp);\r
- }\r
- \r
- VARIABLE ARGUMENT LISTS\r
-\r
-\r
-Some functions, such as printf(), accept a variable number and type of\r
-arguments. C provides a mechanism to write your own functions which can\r
-accept a variable argument list. This mechanism is the va_ family defined\r
-in the header file `stdarg.h'.\r
-\r
-There are three macros which allow implementation of variable argument\r
-lists; va_start(), va_arg() and va_end() and a variable type va_list\r
-which defines an array which holds the information required by the\r
-macros.\r
-\r
-va_start() takes two parameters, the first is the va_list variable and\r
-the second is the last fixed parameter sent to the function. va_start()\r
-must be called before attempting to access the variable argument list as\r
-it sets up pointers required by the other macros.\r
-\r
-va_arg() returns the next variable from the argument list. It is called\r
-with two parameters, the first is the va_list variable and the second is\r
-the type of the argument to be extracted. So, if the next variable in the\r
-argument list is an integer, it can be extracted with;\r
-\r
-\r
- <int> = va_arg(<va_list>,int);\r
-\r
-va_end() is called after extracting all required variables from the\r
-argument list, and simply tidies up the internal stack if appropriate.\r
-va_end() accepts a single parameter, the va_list variable.\r
-\r
-The following simple example program illustrates the basis for a printf()\r
-type implementation where the types of the arguments is not known, but\r
-can be determined from the fixed parameter. This example only caters for\r
-integer, string and character types, but could easily by extended to\r
-cater for other variable types as well by following the method\r
-illustrated;\r
-\r
- #include <stdarg.h>\r
- \r
- char *ITOS(long x, char *ptr)\r
- {\r
- /* Convert a signed decimal integer to a string */\r
- \r
- long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,\r
- 1000, 100, 10, 1 };\r
- int n;\r
- \r
- /* Check sign */\r
- if (x < 0)\r
- {\r
- *ptr++ = '-';\r
- /* Convert x to absolute */\r
- x = 0 - x;\r
- }\r
- \r
- for(n = 0; n < 9; n++)\r
- {\r
- if (x > pt[n])\r
- {\r
- *ptr++ = 48 + x / pt[n];\r
- x %= pt[n];\r
- }\r
- }\r
- return(ptr);\r
- }\r
- \r
- void varfunc(char *format, ...)\r
- {\r
- va_list arg_ptr;\r
- char output[1000];\r
- char *ptr;\r
- int bytes;\r
- int x;\r
- char *y;\r
- char z;\r
- \r
- /* Initialise pointer to argument list */\r
- va_start(arg_ptr, format);\r
- \r
- /* loop format string */\r
- ptr = output;\r
- bytes = 0;\r
- while(*format)\r
- {\r
- /* locate next argument */\r
- while(*format != '%')\r
- {\r
- *ptr++ = *format++;\r
- bytes++;\r
- }\r
- /* A % has been located */\r
- format++;\r
- switch(*format)\r
- {\r
- case '%' : *ptr++ = '%';\r
- break;\r
- \r
- case 'd' : /* integer expression follows */\r
- x = va_arg(arg_ptr,int);\r
- ptr = ITOS(x,ptr);\r
- *ptr = 0;\r
- format++;\r
- bytes += strlen(output) - bytes;\r
- break;\r
- \r
- case 's' : /* String expression follows */\r
- y = va_arg(arg_ptr,char *);\r
- strcat(output,y);\r
- x = strlen(y);\r
- format++;\r
- ptr += x;\r
- bytes += x;\r
- break;\r
- \r
- case 'c' : /* Char expression follows */\r
- z = va_arg(arg_ptr,char);\r
- *ptr++ = z;\r
- format++;\r
- bytes++;\r
- break;\r
- }\r
- \r
- }\r
- \r
- /* Clean stack just in case! */\r
- va_end(arg_ptr);\r
- \r
- /* Null terminate output string */\r
- *ptr = 0;\r
- \r
- /* Display what we got */\r
- printf("\nOUTPUT==%s",output);\r
- }\r
- \r
- void main()\r
- {\r
- varfunc("%d %s %c",5,"hello world",49);\r
- }\r
- \r
-\r
-A simpler variation is to use vsprintf() rather than implementing our own\r
-variable argument list access. However, it is beneficial to understand\r
-how variable argument lists behave. The following is a simplification of\r
-the same program, but leaving the dirty work to the compiler;\r
-\r
- #include <stdio.h>\r
- #include <stdarg.h>\r
- \r
- void varfunc(char *format, ...)\r
- {\r
- va_list arg_ptr;\r
- char output[1000];\r
- \r
- va_start(arg_ptr, format);\r
- \r
- vsprintf(output,format,arg_ptr);\r
- \r
- va_end(arg_ptr);\r
- \r
- /* Display what we got */\r
- printf("\nOUTPUT==%s",output);\r
- }\r
- \r
- void main()\r
- {\r
- varfunc("%d %s %c",5,"hello world",49);\r
- }\r
- \r
- TRIGONOMETRY FUNCTIONS\r
-\r
-The ANSI standard on C defines a number of trigonometry functions, all of\r
-which accept an angle parameter in radians;\r
-\r
-\r
-FUNCTION PROTOTYPE DESCRIPTION\r
-acos double acos(double x) arc cosine of x\r
-asin double asin(double x) arc sine of x\r
-atan double atan(double x) arc tangent of x\r
-atan2 double atan2(double x, double y) arc tangent of y/x\r
-cos double cos(double x) cosine of x\r
-cosh double cosh(double x) hyperbolic cosine of x\r
-sin double sin(double x) sine of x\r
-sinh double sinh(double x) hyperbolic sine of x\r
-tan double tan(double x) tangent of x\r
-tanh double tanh(double x) hyperbolic tangent of x\r
-\r
-There are 2PI radians in a circle, therefore 1 radian is equal to 360/2PI\r
-degrees or approximately 57 degrees in 1 radian. The calculation of any\r
-of the above functions requires large floating point numbers to be used\r
-which is a very slow process. If you are going to use calls to a trig'\r
-function, it is a good idea to use a lookup table of values rather than\r
-keep on calling the function. This approach is used in the discussion on\r
-circle drawing later in this book.\r
-\r
- \r
- ATEXIT\r
-\r
-\r
-When ever a program terminates, it should close any open files (this is\r
-done for you by the C compiler's startup/termination code which it\r
-surrounds your program with), and restore the host computer to some\r
-semblance of order. Within a large program where exit may occur from a\r
-number of locations it is a pain to have to keep on writing calls to the\r
-cleanup routine. Fortunately we don't have to!\r
-\r
-The ANSI standard on C describes a function, atexit(), which registers\r
-the specified function, supplied as a parameter to atexit(), as a\r
-function which is called immediately before terminating the program. This\r
-function is called automatically, so the following program calls\r
-`leave()' whether an error occurs or not;\r
-\r
- #include <stdio.h>\r
- \r
- void leave()\r
- {\r
- puts("\nBye Bye!");\r
- }\r
- \r
- void main()\r
- {\r
- FILE *fp;\r
- int a;\r
- int b;\r
- int c;\r
- int d;\r
- int e;\r
- char text[100];\r
- \r
- atexit(leave);\r
- \r
- fp = fopen("data.txt","w");\r
- \r
- if(!fp)\r
- {\r
- perror("Unable to create file");\r
- exit(0);\r
- }\r
- \r
- fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");\r
- \r
- fflush(fp);\r
- \r
- if (ferror(fp))\r
- {\r
- fputs("Error flushing stream",stderr);\r
- exit(1);\r
- }\r
- \r
- rewind(fp);\r
- if (ferror(fp))\r
- {\r
- fputs("Error rewind stream",stderr);\r
- exit(1);\r
- }\r
- \r
- fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);\r
- if (ferror(fp))\r
- {\r
- /* Unless you noticed the deliberate bug earlier */\r
- /* The program terminates here */\r
- fputs("Error reading from stream",stderr);\r
- exit(1);\r
- }\r
- \r
- printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);\r
- }\r
- \r
- INCREASING SPEED\r
-\r
-\r
-In order to reduce the time your program spends executing it is essential\r
-to know your host computer. Most computers are very slow at displaying\r
-information on the screen. And the IBM PC is no exception to this. C\r
-offers various functions for displaying data, printf() being one of the\r
-most commonly used and also the slowest. Whenever possible try to use\r
-puts(varname) in place of printf("%s\n",varname). Remembering that puts()\r
-appends a newline to the string sent to the screen.\r
-\r
-When multiplying a variable by a constant which is a factor of 2 many C\r
-compilers will recognise that a left shift is all that is required in the\r
-assembler code to carry out the multiplication rapidly. When multiplying\r
-by other values it is often faster to do a multiple addition instead, so;\r
-\r
-\r
- 'x * 3' becomes 'x + x + x'\r
-\r
-Don't try this with variable multipliers in a loop because it becomes\r
-very slow! But, where the multiplier is a constant it can be faster.\r
-(Sometimes!) Another way to speed up multiplication and division is with\r
-the shift commands, << and >>.\r
-\r
-The instruction x /= 2 can equally well be written x >>= 1, shift the\r
-bits of x right one place. Many compilers actually convert integer\r
-divisions by 2 into a shift right instruction. You can use the shifts for\r
-multiplying and dividing by 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 &c.\r
-If you have difficulty understanding the shift commands consider the\r
-binary form of a number;\r
-\r
-\r
- 01001101 equal to 77\r
-\r
-shifted right one place it becomes;\r
-\r
- 00100110 equal to 38\r
-\r
-Try to use integers rather than floating point numbers where ever\r
-possible. Sometimes you can use integers where you didn't think you\r
-could! For example, to convert a fraction to a decimal one would normally\r
-say;\r
-\r
- percentage = x / y * 100\r
-\r
-This requires floating point variables. However, it can also be written\r
-as;\r
-\r
- z = x * 100;\r
- percentage = z / y\r
-\r
-Which works fine with integers, so long as you don't mind the percentage\r
-being truncated. eg;\r
-\r
- 5 / 7 * 100 is equal to 71.43 with floating point\r
-\r
-but with integers;\r
-\r
- 5 * 100 / 7 is equal to 71\r
-\r
-(Assuming left to right expression evaluation. You may need to force the\r
-multiplication to be done first as with `z = x * 100').\r
-\r
-Here is a test program using this idea;\r
-\r
- float funca(double x, double y)\r
- {\r
- return(x / y * 100);\r
- }\r
- \r
- int funcb(int x,int y)\r
- {\r
- return(x * 100 / y);\r
- }\r
- \r
- void main()\r
- {\r
- int n;\r
- double x;\r
- int y;\r
- \r
- for(n = 0; n < 5000; n++)\r
- {\r
- x = funca(5,7);\r
- y = funcb(5,7);\r
- }\r
- }\r
- \r
-And here is the results of the test program fed through a profiler;\r
-\r
-funca 1.9169 sec 96%\r
-|**********************************************\r
-funcb 0.0753 sec 3% |*\r
-\r
-You can clearly see that the floating point function is 25 times slower\r
-than the integer equivalent!\r
-\r
-NB: Although it is normal practice for expressions to be evaluated left\r
-to right, the ANSI standard on C does not specify an order of preference\r
-for expression evaluation, and as such you should check your compiler\r
-manual.\r
-\r
-Another way of increasing speed is to use pointers rather than array\r
-indexing. When you access an array through an index, for example with;\r
-\r
- x = data[i];\r
-\r
-the compiler has to calculate the offset of data[i] from the beginning of\r
-the array. A slow process. Using pointers can often improve things as the\r
-following two bubble sorts, one with array indexing and one with pointers\r
-illustrates;\r
-\r
- void BUBBLE()\r
- {\r
- /* Bubble sort using array indexing */\r
- \r
- int a;\r
- int b;\r
- int temp;\r
- \r
- for(a = lastone; a >= 0; a--)\r
- {\r
- for(b = 0; b < a; b++)\r
- {\r
- if(data[b] > data[b + 1])\r
- {\r
- temp = data[b];\r
- data[b] = data[b + 1];\r
- data[b + 1] = temp;\r
- }\r
- }\r
- }\r
- }\r
- \r
- void PTRBUBBLE()\r
- {\r
- /* Bubble sort using pointers */\r
- \r
- int temp;\r
- int *ptr;\r
- int *ptr2;\r
- \r
- for(ptr = &data[lastone]; ptr >= data; ptr--)\r
- {\r
- for(ptr2 = data; ptr2 < ptr; ptr2++)\r
- {\r
- if(*ptr2 > *(ptr2 + 1))\r
- {\r
- temp = *ptr2;\r
- *ptr2 = *(ptr2 + 1);\r
- *(ptr2 + 1) = temp;\r
- }\r
- }\r
- }\r
- }\r
- \r
-Here are the profiler results for the two versions of the same bubble\r
-sort operating on the same 1000 item, randomly sorted list;\r
-\r
-BUBBLE 3.1307 sec 59% |******************************************\r
-PTRBUBBLE 2.1686 sec 40% |***************************\r
-\r
-\r
-Here is another example of how to initialise an array using first the\r
-common indexing approach, and secondly the pointer approach;\r
-\r
- /* Index array initialisation */\r
- int n;\r
- \r
- for(n = 0; n < 1000; n++)\r
- data[n] = random(1000);\r
- \r
- \r
- /* Pointer array initialisation */\r
- int *n;\r
- \r
- for(n = data; n < &data[1000]; n++)\r
- *n = random(1000);\r
- \r
-\r
-Needless to say, the pointer approach is faster than the index. The\r
-pointer approach is only really of benefit when an array is going to be\r
-traversed, as in the above examples. In the case of say a binary search\r
-where a different and non-adjacent element is going to be tested each\r
-pass then the pointer approach is no better than using array indexing.\r
-\r
-The exception to this rule of using pointers rather than indexed access,\r
-comes with pointer to pointers. Say your program has declared a table of\r
-static data, such as:\r
-\r
-static char *colours[] = { "Black", "Blue", "Green", "Yellow", "Red",\r
-"White" };\r
-\r
-It is faster to access the table with colours[n] than it is with a\r
-pointer, since each element in the table colours[], is a pointer. If you\r
-need to scan a string table for a value you can use this very fast\r
-approach instead;\r
-\r
-First the table is changed into a single string, with some delimiter\r
-between the elements.\r
-\r
- static char *colours = "Black/Blue/Green/Yellow/Red/White";\r
-\r
-Then to confirm that a value is held in the table you can use strstr();\r
-\r
- result = strstr(colours,"Cyan");\r
-\r
-Using in-line assembler code can provide the greatest speed increase.\r
-Care must be taken however not to interfere with the compiled C code. It\r
-is usually safe to write a complete function with in-line assembler code,\r
-but mixing in-line assembler with C code can be hazardous. As a rule of\r
-thumb, get your program working without assembler code, and then if you\r
-want to use in-line assembler, convert small portions of the code at a\r
-time, testing the program at each stage. Video I/O is a very slow process\r
-with C, and usually benefits from in-line assembler, and we have used\r
-this principle quite widely in the example programs which follow later.\r
-\r
- \r
- PC GRAPHICS\r
-\r
-When programming graphics you should bear in mind that they are a machine\r
-dependant subject. Code to produce graphics on an IBM PC will not port to\r
-an Amiga, or VAX or any other type of computer.\r
-\r
-\r
-\r
-Introduction To PC Graphics\r
-\r
-The IBM PC and compatible range of computers display information on a\r
-visual display unit (VDU). To enable the computer to send information to\r
-the VDU a component is included within the computer called a "display\r
-card". There are various display cards and VDUs which have been produced\r
-for the IBM PC range of computers; monochrome display adapter (MDA),\r
-colour graphics adapter (CGA), Hercules graphics card (HGC), Enhanced\r
-graphics adapter (EGA), video graphics array (VGA), super video graphics\r
-array (SVGA), memory controller gate array (MCGA), 8514/A and the Txas\r
-Instruments Graphics Architecture (TIGA). For simplicity, this section\r
-will concern itself only with the three more common types of display;\r
-\r
- CGA, EGA and VGA\r
-\r
-Information about the VGA display is also relevant to the SVGA display\r
-which in simple terms can do the same and more. This section will not\r
-concern itself with monochrome displays since they are of limited use in\r
-graphics work.\r
-\r
-\r
-Display Modes\r
-\r
-When an IBM PC computer is first switched on is set to display 80 columns\r
-by 25 rows of writing. This measurement, 80 x 25 is called the\r
-"resolution". A display mode which is intended for displaying writing is\r
-called a "text" mode. Where as a display mode which is intended for\r
-displaying pictures is called a "graphics" mode.\r
-\r
-If you look closely at the display you will see that each displayed\r
-character is comprised of dots. In reality the entire display is\r
-comprised of dots, called "pixels", which may be set to different\r
-colours. In text display modes these pixels are not very relevant,\r
-however, in graphics display modes each pixel may be selected and set by\r
-a program. The size of the pixels varies with the display resolution. In\r
-80 x 25 text mode the pixels are half the width they are in 40 x 25 text\r
-display mode.\r
-\r
-Depending upon the display card installed in the computer, there are a\r
-number of display modes which may be used;\r
-\r
-\r
-MODE TYPE RESOLUTION COLOURS\r
- \r
- 0 Text 40 x 25 4 (CGA), 16 (EGA,\r
- VGA) Shades of\r
- grey\r
- 1 Text 40 x 25 4 (CGA), 16 (EGA,\r
- VGA)\r
- 2 Text 80 x 25 4 (CGA), 16 (EGA,\r
- VGA) Shades of\r
- grey\r
- 3 Text 80 x 25 4 (CGA), 16 (EGA,\r
- VGA)\r
- 4 Graphics 320 x 200 4\r
- 5 Graphics 320 x 200 4 (grey on CGA\r
- and EGA)\r
- 6 Graphics 640 x 200 2\r
- 7 Text 80 x 25 Mono (EGA, VGA)\r
-13 Graphics 320 x 200 16 (EGA, VGA)\r
-14 Graphics 640 x 200 16 (EGA, VGA)\r
-15 Graphics 640 x 350 Mono (EGA, VGA)\r
-16 Graphics 640 x 350 16 (EGA, VGA)\r
-17 Graphics 640 x 480 2 (VGA)\r
-18 Graphics 640 x 480 16 (VGA)\r
-19 Graphics 320 x 200 256 (VGA)\r
-\r
-The term resolution in graphics modes refers to the number of pixels\r
-across and down the VDU. The larger the number of pixels, the smaller\r
-each is and the sharper any displayed image appears. As you can see from\r
-the table, the VGA display can produce a higher resolution than the other\r
-display cards, resulting in sharper images being produced.\r
-\r
-\r
-The CGA display card can produce a maximum resolution of 320 x 200\r
-pixels, where as the VGA display card can produce a resolution of 640 x\r
-480 pixels. This is why writing on a VGA VDU looks so much sharper than\r
-the writing displayed on a CGA VDU.\r
-\r
-\r
-\r
-Accessing The Display\r
-\r
-Inside the IBM PC computer is a silicon chip called the BIOS ROM, this\r
-chip contains functions which may be called by an external computer\r
-program to access the display card, which in turn passes the information\r
-on to the VDU. The BIOS display functions are all accessed by generating\r
-interrupt 10 calls, with the number of the appropriate function stored in\r
-the assembly language AH register.\r
-\r
-A programmer interested in creating graphics displays must first be able\r
-to switch the display card to an appropriate graphics mode. This is\r
-achieved by calling the BIOS display function 0, with th number of the\r
-desired display mode from the table stored in the assembly language AL\r
-register thus the following assembly language code segment will switch\r
-the display card to CGA graphics mode 4, assuming that is that the\r
-display card is capable of this mode;\r
-\r
- mov ah , 00\r
- mov al , 04\r
- int 10h\r
-\r
-\r
-A C function for selecting video display modes can be written;\r
-\r
- #include <dos.h>\r
- \r
- void setmode(unsigned char mode)\r
- {\r
- /* Sets the video display mode */\r
- \r
- union REGS inregs outreg;\r
- \r
- inreg.h.ah = 0;\r
- inreg.h.al = mode;\r
- int86(0x10,&inreg,&outregs);\r
- }\r
- \r
-Any graphics are created by setting different pixels to different\r
-colours, this is termed "plotting", and is achieved by calling BIOS\r
-display function 12 with the pixel's horizontal coordinate in the\r
-assembly language CX register and it's vertical coordinate in the\r
-assembly language DX register and the required colour in the assembly\r
-language AL register thus;\r
-\r
-\r
- mov ah, 12\r
- mov al, colour\r
- mov bh, 0\r
- mov cx, x_coord\r
- mov dx, y_coord\r
- int 10h\r
-\r
-The corresponding C function is;\r
-\r
- #include <dos.h>\r
- \r
- void plot(int x_coord, int y_coord, unsigned char colour)\r
- {\r
- /* Sets the colour of a pixel */\r
- \r
- union REGS regs;\r
- \r
- regs.h.ah = 12;\r
- regs.h.al = colour;\r
- regs.h.bh = 0;\r
- regs.x.cx = x_coord;\r
- regs.x.dx = y_coord;\r
- int86(0x10,®s,®s);\r
- }\r
-\r
-The inverse function of plot is to read the existing colour setting of a\r
-pixel. This is done by calling BIOS ROM display function 13, again with\r
-the pixel's horizontal coordinate in the assembly language CX register\r
-and it's vertical coordinate in the assembly language DX register. This\r
-function then returns the pixel's colour in the assembly language AL\r
-register;\r
-\r
-\r
- #include <dos.h>\r
- \r
- unsigned char get_pixel(int x_coord, int y_coord)\r
- {\r
- /* Reads the colour of a pixel */\r
- \r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ah = 13;\r
- inreg.h.bh = 0;\r
- inreg.x.cx = x_coord;\r
- inreg.x.dx = y_coord;\r
- int86(0x10,&inreg,&outreg);\r
- return(outreg.h.al);\r
- }\r
-\r
-\r
-Colour And The CGA\r
-\r
-The CGA display card can display a maximum of 4 colours simultaneously at\r
-any time. However, the display card can generate a total of 8 colours.\r
-There are two sets of colours, called "palettes". The first palette\r
-contains the colours;\r
-\r
- background, cyan, magenta and white.\r
-\r
-the second palette contains the colours;\r
-\r
- background, green, red and yellow.\r
-\r
-Colour 0 is always the same as the background colour.\r
-\r
-The pixels displayed on the VDU take their colours from the currently\r
-active palette, and are continuously being refreshed. So, if the active\r
-palette changes, so to do the colours of the displayed pixels on the VDU.\r
-\r
-Selection of the active CGA palette is achieved by calling the BIOS\r
-display function 11 with the number of the desired palette (either 0 or\r
-1) in the assembly language BH register;\r
-\r
-\r
- mov ah, 11\r
- mov bh, palette\r
- int 10h\r
-\r
-The C function for selecting the CGA palette is;\r
-\r
- void palette(unsigned char palette)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ah = 11;\r
- inreg.h.bh = palette;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
- \r
-The background colour may be selected independantly from any of the eight\r
-available colours by calling the same BIOS display function with a value\r
-of 0 stored in the assembly language BH register and the desired\r
-background colour in the assembly language BL register;\r
-\r
-\r
- mov ah, 11\r
- mov bh, 0\r
- mov bl, colour\r
- int 10h\r
-\r
-In C this function can be written;\r
-\r
- void background(unsigned char colour)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ah = 11;\r
- inreg.h.bh = 0;\r
- inreg.h.bl = colour;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
- \r
-The background colours available are;\r
-\r
- 0 Black\r
- 1 Blue\r
- 2 Green\r
- 3 Cyan\r
- 4 Red\r
- 5 Magenta\r
- 6 Yellow\r
- 7 White\r
-\r
-\r
-Colour And The EGA\r
-\r
-The EGA display card can display a maximum of 16 colours simultaneously\r
-at any time. The colour of all pixels is continuously refreshed by the\r
-display card by reading the colour from the EGA palette. Unlike the CGA\r
-display card, the EGA display card allows you to redefine any or all of\r
-the colours in the palette. Unfortunately only the first 8 colours may be\r
-loaded into other palette colours.\r
-\r
-The colours are;\r
-\r
- 0 Black\r
- 1 Blue\r
- 2 Green\r
- 3 Cyan\r
- 4 Red\r
- 5 Magenta\r
- 6 Brown\r
- 7 Light grey\r
- 8 Dark grey\r
- 9 Light blue\r
- 10 Light green\r
- 11 Light cyan\r
- 12 Light red\r
- 13 Light magenta\r
- 14 Yellow\r
- 15 White\r
-\r
-Changing a palette colour is achieved by calling the BIOS display\r
-function 16 with a value of 0 in the assembly language AL register, the\r
-colour value (0 to 7) in the assembly language BH register and the number\r
-of the palette colour (0 to 15) in the assembly language BL register\r
-thus;\r
-\r
-\r
- mov ah,16\r
- mov al,0\r
- mov bl,palette\r
- mov bh,colour\r
- int 10h\r
-\r
-In C this function may be written;\r
-\r
- void ega_palette(unsigned char colour, unsigned char palette)\r
- {\r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 16;\r
- inreg.h.al = 0;\r
- inreg.h.bl = palette;\r
- inreg.h.bh = colour;\r
- \r
- int86(0x10,&inreg,&outreg);\r
- }\r
-\r
-\r
-\r
-Colour And The VGA\r
-\r
-The VGA display card can display a maximum of 256 colours on the VDU at\r
-any one time, these colours are defined by information held in 256\r
-special registers called "DAC" registers. As with the CGA and EGA\r
-displays, the colour of displayed pixels is continuously being refreshed,\r
-and as such any change to a DAC register is immediately visible. Each DAC\r
-register has three component data parts which record the amount of green,\r
-blue and red colours which make up the displayed colour. Each of these\r
-seperate data components can hold a value between 0 and 63 giving the VGA\r
-display card the ability to display 262,144 colours! Although only a\r
-small subset of them can be displayed at any one time.\r
-\r
-Setting the value of a DAC register is achieved by calling the BIOS\r
-display function 16 with a value of 16 stored in the assembly language AL\r
-register, the green value stored in the assembly language CH register,\r
-the blue value stored in the assembly language CL register and the red\r
-value stored in the assembly language DH register and the number of the\r
-DAC register to be set stored in the assembly language BX register;\r
-\r
-\r
- mov ah,16\r
- mov al,16\r
- mov ch,green\r
- mov cl,blue\r
- mov dh,red\r
- mov bx,dac\r
- int 10h\r
-\r
-\r
-The C function to set a DAC register looks lik this;\r
-\r
- void set_dac(int dac, unsigned char green, unsigned char blue,\r
- unsigned char red)\r
- {\r
- union REGS regs;\r
- \r
- regs.h.ah = 16;\r
- regs.h.al = 16;\r
- regs.x.bx = dac;\r
- regs.h.ch = green;\r
- regs.h.cl = blue;\r
- regs.h.dh = red;\r
- int86(0x10,®s,®s);\r
- }\r
- \r
-\r
-\r
-Displaying Text\r
-\r
-The BIOS ROM provides three functions for displaying a single character.\r
-The first function to consider is the one used extensively by DOS for\r
-displaying messages, this is function 14 called "write text in teletype\r
-mode". This function interprets some control characters; bell (ascii 7),\r
-backspace (ascii 8), carriage return (ascii 10) and line feed (ascii 13)\r
-but all other ascii codes are displayed, and the current cursor position\r
-updated accordingly, moving down a row when a character is displayed in\r
-the far right column. To call this function the assembly language\r
-register AL holds the ascii code of the character to be displayed and\r
-assembly language register BL holds the foreground colour for the\r
-character to be displayed in if a graphics mode is active;\r
-\r
-\r
- mov ah,14\r
- mov al,character\r
- mov bh,0\r
- mov bl,foreground\r
- int 10h\r
-\r
-\r
-A C function for accessing the write text in teletype mode may be written\r
-like this;\r
-\r
- #include <dos.h>\r
- \r
- void teletype(unsigned char character, unsigned char foreground)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ah = 14;\r
- inreg.h.al = character;\r
- inreg.h.bh = 0;\r
- inreg.h.bl = foreground;\r
- int86(0x10,&inrg,&outreg);\r
- }\r
- \r
-The second BIOS ROM display function for displaying a character allows\r
-the foreground and background colours of the displayed character to be\r
-defined. It also allows multiple copies of the character to be displayed\r
-one after another automatically displaying subsequent characters at the\r
-next display position, although the current cursor position is not\r
-changed by this function.\r
-\r
-This function is called "write character and attribute", and is BIOS ROM\r
-display function number 9. It is called with the ascii code of the\r
-character to be displayed in the assembly language AL register, the\r
-display page in assembly language register BH, the foreground colour in\r
-the first four bits of the assembly language register BL and the\r
-background colour in the last four bits of the assembly language register\r
-BL, the number of times the character is to be displayed is stored in the\r
-assembly language CX register thus;\r
-\r
-\r
- mov ah,9\r
- mov al,character\r
- mov bh,0\r
- mov bl,foreground + 16 * background\r
- mov cx,number\r
- int 10h\r
-\r
-And in C;\r
-\r
- #include <dos.h>\r
- \r
- void char_attrib(unsigned char character, unsigned char foreground,\r
- unsigned char background, int number)\r
- {\r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 9;\r
- inreg.h.al = character;\r
- inreg.h.bh = 0;\r
- inreg.h.bl = (background << 4) + foreground;\r
- inreg.x.cx = number;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
-\r
-The last BIOS ROM display function for displaying a character retains the\r
-foreground and background colours of the display position.\r
-\r
-This function is called "write character", and is BIOS ROM display\r
-function number 10. It is identical to BIOS ROM display function 9 except\r
-that the colours of the displayed character are those which are prevalent\r
-at the display position, except in graphics modes when the foreground\r
-colour of the character is determined by the value in the assembly\r
-language BL register. Its use is as follows;\r
-\r
-\r
- mov ah,10\r
- mov al,character\r
- mov bh,0\r
- mov bl,foreground ; For graphics modes ONLY\r
- mov cx,number\r
- int 10h\r
-\r
-And in C;\r
-\r
- #include <dos.h>\r
- \r
- void char_attrib(unsigned char character, unsigned char foreground,\r
- int number)\r
- {\r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 10;\r
- inreg.h.al = character;\r
- inreg.h.bh = 0;\r
- inreg.h.bl = foreground; /* For graphics modes ONLY */\r
- inreg.x.cx = number;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
- \r
-\r
-Positioning of the text cursor is provided for by the ROM BIOS display\r
-function number 2. It is called with the row number in the assembly\r
-language register DH and the column number in the assembly language\r
-register DL;\r
-\r
- mov ah,2\r
- mov bh,0\r
- mov dh,row\r
- mov dl,column\r
- int 10h\r
-\r
-The corresponding function in C looks like this;\r
-\r
- #include <dos.h>\r
- \r
- void at(unsigned char row, unsigned char column)\r
- {\r
- union REGS regs;\r
- \r
- regs.h.ah = 2;\r
- regs.h.bh = 0;\r
- regs.h.dh = row;\r
- regs.h.dl = column;\r
- int86(0x10,®s,®s);\r
- }\r
-\r
-From these basic functions a more useful replacement for the C language's\r
-"printf()" function can be written which allows data to be displayed at\r
-the current cursor position, previously set by a call to "at()", with\r
-prescribed attributes;\r
-\r
-\r
- #include <dos.h>\r
- #include <stdarg.h>\r
- \r
- void at(unsigned char row, unsigned char column)\r
- {\r
- union REGS regs;\r
- \r
- regs.h.ah = 2;\r
- regs.h.bh = 0;\r
- regs.h.dh = row;\r
- regs.h.dl = column;\r
- int86(0x10,®s,®s);\r
- }\r
- \r
- void xprintf(unsigned char foreground, unsigned char background,\r
- char *format,...)\r
- {\r
- union REGS inreg,outreg;\r
- \r
- va_list arg_ptr;\r
- static char output[1000];\r
- unsigned char col;\r
- unsigned char row;\r
- unsigned char n;\r
- unsigned char p;\r
- unsigned char text;\r
- unsigned char attr;\r
- \r
- /* Convert foreground and background colours into a single\r
- attribute */\r
- attr = (background << 4) + foreground;\r
- \r
- /* Copy data into a single string */\r
- va_start(arg_ptr, format);\r
- vsprintf(output, format, arg_ptr);\r
- \r
- /* Determine number of display columns */\r
- inreg.h.ah = 15;\r
- int86(0x10,&inreg,&outreg);\r
- n = outreg.h.ah;\r
- \r
- /* Determine current cursor position */\r
- inreg.h.ah = 3;\r
- inreg.h.bh = 0;\r
- int86(0x10,&inreg,&outreg);\r
- row = outreg.h.dh;\r
- col = outreg.h.dl;\r
- \r
- /* Now display data */\r
- p = 0;\r
- while (output[p])\r
- {\r
- /* Display this character */\r
- inreg.h.bh = 0;\r
- inreg.h.bl = attr;\r
- inreg.x.cx = 01;\r
- inreg.h.ah = 9;\r
- inreg.h.al = output[p++];\r
- int86(0x10,&inreg,&outreg);\r
- \r
- /* Update cursor position */\r
- /* moving down a row if required */\r
- col++;\r
- if (col < (n - 1))\r
- at(row, col);\r
- else\r
- {\r
- col = 0;\r
- at(++row, col);\r
- }\r
- }\r
- }\r
- \r
-This function, "xprintf()" illustrates two more functions of the BIOS\r
-ROM. The first is the call to function 15 which returns the number of\r
-text display columns for the currently active display mode.\r
-\r
-The other function illustrated, but not yet discussed, is BIOS ROM\r
-function 3 which returns information about the cursor. The cursor's row\r
-is returned in the assembly language register DH, and it's column in the\r
-assembly language register DL.\r
-\r
-\r
- \r
- ADVANCED GRAPHICS ON THE IBM PC\r
-\r
-This section aims to reveal more about the graphics facilities offered by\r
-the IBM PC, in particular topics which are of a more complex nature than\r
-those addressed in the previous section.\r
-\r
-\r
-\r
-Display Pages\r
-\r
-The information for display by the display card is stored in an area of\r
-memory called the "video RAM". The size of the video RAM varies from one\r
-display card to another, and the amount of video RAM required for a\r
-display varies with the selected display mode. Display modes which do not\r
-require all of the video RAM use the remaining video RAM for additional\r
-display pages.\r
-\r
-\r
-MODE PAGES\r
- 0 8\r
- 1 8\r
- 2 4 (CGA) 8 (EGA, VGA)\r
- 3 4 (CGA) 8 (EGA, VGA)\r
- 4 1\r
- 5 1\r
- 6 1\r
- 7 8 (EGA, VGA)\r
-13 8 (EGA, VGA)\r
-14 4 (EGA, VGA)\r
-15 2 (EGA, VGA)\r
-16 2 (EGA, VGA)\r
-17 1 (VGA)\r
-18 1 (VGA)\r
-19 1 (VGA)\r
-\r
-Many of the BIOS ROM display functions allow selection of the display\r
-page to be written to, regardless of which page is currently being\r
-displayed.\r
-\r
-The display card continuously updates the VDU from the information in the\r
-active display page. Changing the active display page instantaneously\r
-changes the display.\r
-\r
-This provides the graphics programmer with the means to build a display\r
-on an undisplayed page, and to then change the active display page so\r
-that the viewer does not see the display being drawn.\r
-\r
-Selection of the active display page is achieved by calling BIOS ROM\r
-display function 5 with the number of the required display page stored in\r
-the assembly language register AL;\r
-\r
- mov ah,5\r
- mov al,page\r
- int 10h\r
-\r
-Or, from C this function becomes;\r
-\r
- #include <dos.h>\r
- \r
- void set_page(unsigned char page)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ah = 5;\r
- inreg.h.al = page;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
- \r
-The display page to which BIOS ROM display functions write is decided by\r
-the value stored in the assembly language BH register. The functions for\r
-setting a pixel's colour may be amended thus;\r
-\r
- mov ah, 12\r
- mov al, colour\r
- mov bh, page\r
- mov cx, x_coord\r
- mov dx, y_coord\r
- int 10h\r
-\r
-And the corresponding C function becomes;\r
-\r
- #include <dos.h>\r
- \r
- void plot(int x_coord, int y_coord, unsigned char colour, unsigned\r
- char page)\r
- {\r
- /* Sets the colour of a pixel */\r
- \r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ah = 12;\r
- inreg.h.al = colour;\r
- inreg.h.bh = page;\r
- inreg.x.cx = x_coord;\r
- inreg.x.dx = y_coord;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
-\r
-The currently active display page can be determined by calling BIOS ROM\r
-display function 15. This function returns the active display page in the\r
-assembly language register BH;\r
-\r
- mov ah,15\r
- int 10h\r
- ; BH now holds active page number\r
-\r
- \r
- Advanced Text Routines\r
-\r
-When the IBM PC display is in a text mode a blinking cursor is displayed\r
-at the current cursor position. This cursor is formed of a rectangle\r
-which is one complete character width, but it's top and bottom pixel\r
-lines are definable within the limits of the character height by calling\r
-BIOS ROM display function 1. A CGA display has a character height of 8\r
-pixel lines, an EGA display has a character height of 14 lines and a VGA\r
-display has a character height of 16 lines.\r
-\r
-BIOS ROM function 1 is called with the top pixel line number of the\r
-desired cursor shape in assembly language register CH and the bottom\r
-pixel line number in assembly language register CL;\r
-\r
-\r
- mov ah,1\r
- mov ch,top\r
- mov cl,bottom\r
- int 10h\r
-\r
-A C function to set the cursor shape may be be written thus;\r
-\r
- #include <dos.h>\r
- \r
- void setcursor(unsigned char top, unsigned char bottom)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.ch = start;\r
- inreg.h.cl = end;\r
- inreg.h.ah = 1;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
-If the top pixel line is defined as larger than the bottom line, then the\r
-cursor will appear as a pair of parallel rectangles.\r
-\r
-The cursor may be removed from view by calling BIOS ROM display function\r
-1 with a value of 32 in the assembly language CH register.\r
-\r
-The current shape of the cursor can be determined by calling BIOS ROM\r
-function 3, which returns the top pixel line number of the cursor shape\r
-in the assembly language CH register, and the bottom line number in the\r
-assembly language register CL.\r
-\r
-Two functions are provided by the BIOS ROM for scrolling of the currently\r
-active display page. These are function 6, which scrolls the display up\r
-and function 7 which scrolls the display down.\r
-\r
-Both functions accept the same parameters, these being the number of\r
-lines to scroll in the assembly language register AL, the colour\r
-attribute for the resulting blank line in the assembly language BH\r
-register, the top row of the area to be scrolled in the assembly language\r
-CH register, the left column of the area to be scrolled in the assembly\r
-language CL register, the bottom row to be scrolled in the assembly\r
-language DH register and the right most column to be scrolled in the\r
-assembly language DL register.\r
-\r
-If the number of lines being scrolled is greater than the number of lines\r
-in the specified area, then the result is to clear the specified area,\r
-filling it with spaces in the attribute specified in the assembly\r
-language BH register.\r
-\r
-\r
-Scrolling\r
-\r
-A C function to scroll the entire screen down one line can be written\r
-thus;\r
-\r
- #include <dos.h>\r
- \r
- void scroll_down(unsigned char attr)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.al = 1;\r
- inreg.h.cl = 0;\r
- inreg.h.ch = 0;\r
- inreg.h.dl = 79;\r
- inreg.h.dh = 24; /* Assuming a 25 line display */\r
- inreg.h.bh = attr;\r
- inreg.h.ah = 7;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
-\r
-\r
-Clear Screen\r
-A simple clear screen function can be written in C based upon the\r
-"scroll_down()" function simply by changing the value assigned to\r
-inreg.h.al to 0;\r
-\r
- #include <dos.h>\r
- \r
- void cls(unsigned char attr)\r
- {\r
- union REGS inreg, outreg;\r
- \r
- inreg.h.al = 0;\r
- inreg.h.cl = 0;\r
- inreg.h.ch = 0;\r
- inreg.h.dl = 79;\r
- inreg.h.dh = 24; /* Assuming a 25 line display */\r
- inreg.h.bh = attr;\r
- inreg.h.ah = 7;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
-\r
-\r
-\r
-Windowing\r
-Windowing functions need to preserve the display they overwrite, and\r
-restore it when the window is removed from display. The BIOS ROM provides\r
-a display function which enables this to be done.\r
-\r
-Function 8 requires the appropriate display page number to be stored in\r
-assembly language register BH, and then when called it returns the ascii\r
-code of the character at the current cursor position of that display page\r
-in the assembly language AL register, and the display attribute of the\r
-character in the assembly language AH register.\r
-\r
-The following C functions allow an area of the display to be preserved,\r
-and later restored;\r
-\r
- #include <dos.h>\r
- \r
- void at(unsigned char row, unsigned char column, unsigned char page)\r
- {\r
- /* Position the cursor */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 2;\r
- inreg.h.bh = page;\r
- inreg.h.dh = row;\r
- inreg.h.dl = column;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
- \r
- void get_win(unsigned char left, unsigned char top, unsigned char\r
- right,unsigned char bottom, unsigned char page, char *buffer)\r
- {\r
- /* Read a text window into a variable */\r
- \r
- union REGS inreg,outreg;\r
- \r
- unsigned char old_left;\r
- unsigned char old_row;\r
- unsigned char old_col;\r
- \r
- /* save current cursor position */\r
- inreg.h.ah = 3;\r
- inreg.h.bh = page;\r
- int86(0x10,&inreg,&outreg);\r
- old_row = outreg.h.dh;\r
- old_col = outreg.h.dl;\r
- \r
- while(top <= bottom)\r
- {\r
- old_left = left;\r
- while(left <= right)\r
- {\r
- at(top,left,page);\r
- inreg.h.bh = page;\r
- inreg.h.ah = 8;\r
- int86(0x10,&inreg,&outreg);\r
- *buffer++ = outreg.h.al;\r
- *buffer++ = outreg.h.ah;\r
- left++;\r
- }\r
- \r
- left = old_left;\r
- top++;\r
- }\r
- \r
- /* Restore cursor to original location */\r
- at(old_row,old_col,page);\r
- }\r
- \r
- void put_win(unsigned char left, unsigned char top, unsigned char\r
- right, unsigned char bottom, unsigned char page, char\r
- *buffer)\r
- {\r
- /* Display a text window from a variable */\r
- \r
- union REGS inreg,outreg;\r
- \r
- unsigned char old_left;\r
- unsigned char chr;\r
- unsigned char attr;\r
- unsigned char old_row;\r
- unsigned char old_col;\r
- \r
- /* save current cursor position */\r
- inreg.h.ah = 3;\r
- inreg.h.bh = page;\r
- int86(0x10,&inreg,&outreg);\r
- old_row = outreg.h.dh;\r
- old_col = outreg.h.dl;\r
- \r
- while(top <= bottom)\r
- {\r
- old_left = left;\r
- while(left <= right)\r
- {\r
- at(top,left,page);\r
- chr = *buffer++;\r
- attr = *buffer++;\r
- inreg.h.bh = page;\r
- inreg.h.ah = 9;\r
- inreg.h.al = chr;\r
- inreg.h.bl = attr;\r
- inreg.x.cx = 1;\r
- int86(0x10,&inreg,&outreg);\r
- left++;\r
- }\r
- left = old_left;\r
- top++;\r
- }\r
- \r
- /* Restore cursor to original location */\r
- at(old_row,old_col,page);\r
- }\r
- \r
- DIRECT VIDEO ACCESS WITH THE IBM PC\r
-\r
-Accessing video RAM directly is much faster than using the BIOS ROM\r
-display functions. There are problems however. Different video modes\r
-arrange their use of video RAM in different ways so a number of functions\r
-are required for plotting using direct video access, where as only one\r
-function is required if use is made of the BIOS ROM display function.\r
-\r
-The following C function will set a pixel in CGA display modes 4 and 5\r
-directly;\r
-\r
-\r
- void dplot4(int y, int x, int colour)\r
- {\r
- /* Direct plotting in modes 4 & 5 ONLY! */\r
- \r
- union mask\r
- {\r
- char c[2];\r
- int i;\r
- }bit_mask;\r
- \r
- int index;\r
- int bit_position;\r
- \r
- unsigned char t;\r
- char xor;\r
- \r
- char far *ptr = (char far *) 0xB8000000;\r
- \r
- bit_mask.i = 0xFF3F;\r
- \r
- if ( y < 0 || y > 319 || x < 0 || x > 199)\r
- return;\r
- \r
- xor = colour & 128;\r
- \r
- colour = colour & 127;\r
- \r
- bit_position = y % 4;\r
- \r
- colour <<= 2 * (3 - bit_position);\r
- \r
- bit_mask.i >>= 2 * bit_position;\r
- \r
- index = x * 40 + (y / 4);\r
- \r
- if (x % 2)\r
- index += 8152;\r
- \r
- \r
- \r
- \r
- if (!xor)\r
- {\r
- t = *(ptr + index) & bit_mask.c[0];\r
- *(ptr + index) = t | colour;\r
- }\r
- else\r
- {\r
- t = *(ptr + index) | (char)0;\r
- *(ptr + index) = t ^ colour;\r
- }\r
- }\r
- \r
-\r
-Direct plotting in VGA mode 19 is very much simpler;\r
-\r
- void dplot19(int x, int y, unsigned char colour)\r
- {\r
- /* Direct plot in mode 19 ONLY */\r
- char far *video;\r
- \r
- video = MK_FP(0xA000,0);\r
- video[x + y * 320] = colour;\r
-}\r
- \r
- ADVANCED GRAPHICS TECHNIQUES WITH THE IBM PC\r
-\r
-\r
-\r
-\r
-Increasing Colours\r
-\r
-The EGA display is limited displaying a maximum of 16 colours, however in\r
-high resolution graphics modes (such as mode 16) the small physical size\r
-of the pixels allows blending of adjacent colours to produce additional\r
-shades.\r
-\r
-If a line is drawn straight across a black background display in blue,\r
-and then a subsequent line is drawn beneath it also in blue but only\r
-plotting alternate pixels, the second line will appear in a darker shade\r
-of the same colour.\r
-\r
-The following C program illustrates this idea;\r
-\r
-\r
- #include <dos.h>\r
- \r
- union REGS inreg, outreg;\r
- \r
- void setvideo(unsigned char mode)\r
- {\r
- /* Sets the video display mode */\r
- \r
- inreg.h.al = mode;\r
- inreg.h.ah = 0;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- void plot(int x, int y, unsigned char colour)\r
- {\r
- /* Sets a pixel at the specified coordinates */\r
- \r
- inreg.h.al = colour;\r
- inreg.h.bh = 0;\r
- inreg.x.cx = x;\r
- inreg.x.dx = y;\r
- inreg.h.ah = 0x0C;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- void line(int a, int b, int c, int d, unsigned char colour)\r
- {\r
- /* Draws a straight line from (a,b) to (c,d) */\r
- \r
- int u;\r
- int v;\r
- int d1x;\r
- int d1y;\r
- int d2x;\r
- int d2y;\r
- int m;\r
- int n;\r
- int s;\r
- int i;\r
- \r
- u = c - a;\r
- v = d - b;\r
- if (u == 0)\r
- {\r
- d1x = 0;\r
- m = 0;\r
- }\r
- else\r
- {\r
- m = abs(u);\r
- if (u < 0)\r
- d1x = -1;\r
- else\r
- if (u > 0)\r
- d1x = 1;\r
- }\r
- if ( v == 0)\r
- {\r
- d1y = 0;\r
- n = 0;\r
- }\r
- else\r
- {\r
- n = abs(v);\r
- if (v < 0)\r
- d1y = -1;\r
- else\r
- if (v > 0)\r
- d1y = 1;\r
- }\r
- if (m > n)\r
- {\r
- d2x = d1x;\r
- d2y = 0;\r
- }\r
- else\r
- {\r
- d2x = 0;\r
- d2y = d1y;\r
- m = n;\r
- n = abs(u);\r
- }\r
- s = m / 2;\r
- \r
- for (i = 0; i <= m; i++)\r
- {\r
- plot(a,b,colour);\r
- s += n;\r
- if (s >= m)\r
- {\r
- s -= m;\r
- a += d1x;\r
- b += d1y;\r
- }\r
- else\r
- {\r
- a += d2x;\r
- b += d2y;\r
- }\r
- }\r
- }\r
- \r
- void dot_line(int a, int b, int c, int d, int colour)\r
- {\r
- /* Draws a dotted straight line from (a,b) to (c,d) */\r
- \r
- int u;\r
- int v;\r
- int d1x;\r
- int d1y;\r
- int d2x;\r
- int d2y;\r
- int m;\r
- int n;\r
- int s;\r
- int i;\r
- \r
- u = c - a;\r
- v = d - b;\r
- if (u == 0)\r
- {\r
- d1x = 0;\r
- m = 0;\r
- }\r
- else\r
- {\r
- m = abs(u);\r
- if (u < 0)\r
- d1x = -2;\r
- else\r
- if (u > 0)\r
- d1x = 2;\r
- }\r
- if (v == 0)\r
- {\r
- d1y = 0;\r
- n = 0;\r
- }\r
- else\r
- {\r
- n = abs(v);\r
- if (v < 0)\r
- d1y = -2;\r
- else\r
- if (v > 0)\r
- d1y = 2;\r
- }\r
- if (m > n)\r
- {\r
- d2x = d1x;\r
- d2y = 0;\r
- }\r
- else\r
- {\r
- d2x = 0;\r
- d2y = d1y;\r
- m = n;\r
- n = abs(u);\r
- }\r
- s = m / 2;\r
- \r
- for (i = 0; i <= m; i+=2)\r
- {\r
- plot(a,b,colour);\r
- s += n;\r
- if (s >= m)\r
- {\r
- s -= m;\r
- a += d1x;\r
- b += d1y;\r
- }\r
- else\r
- {\r
- a += d2x;\r
- b += d2y;\r
- }\r
- }\r
- }\r
- \r
- \r
- void main(void)\r
- {\r
- int n;\r
- \r
- /* Display different colour bands */\r
- \r
- setvideo(16);\r
- \r
- for(n = 1; n < 16; n++)\r
- {\r
- line(0,n * 20,639,n * 20,n);\r
- line(0,1 + n * 20,639,1 + n * 20,n);\r
- line(0,2 + n * 20,639,2 + n * 20,n);\r
- \r
- dot_line(0,4 + n * 20,639,4 + n * 20,n);\r
- dot_line(1,5 + n * 20,639,5 + n * 20,n);\r
- dot_line(0,6 + n * 20,639,6 + n * 20,n);\r
- \r
- dot_line(0,8 + n * 20,639,8 + n * 20,n);\r
- dot_line(1,9 + n * 20,639,9 + n * 20,n);\r
- dot_line(0,10 + n * 20,639,10 + n * 20,n);\r
- \r
- dot_line(1,8 + n * 20,639,8 + n * 20,7);\r
- dot_line(0,9 + n * 20,639,9 + n * 20,7);\r
- dot_line(1,10 + n * 20,639,10 + n * 20,7);\r
- \r
- dot_line(1,12 + n * 20,639,12 + n * 20,n);\r
- dot_line(0,13 + n * 20,639,13 + n * 20,n);\r
- dot_line(1,14 + n * 20,639,14 + n * 20,n);\r
- \r
- dot_line(0,12 + n * 20,639,12 + n * 20,14);\r
- dot_line(1,13 + n * 20,639,13 + n * 20,14);\r
- dot_line(0,14 + n * 20,639,14 + n * 20,14);\r
- }\r
- }\r
-\r
-This technique can be put to good use for drawing three dimensional\r
-boxes;\r
-\r
- void box3d(int xa,int ya, int xb, int yb, int col)\r
- {\r
- /* Draws a box for use in 3d histogram graphs etc */\r
- \r
- int xc;\r
- int xd;\r
- \r
- int n;\r
- \r
- xd = (xb - xa) / 2;\r
- xc = xa + xd;\r
- \r
- /* First draw the solid face */\r
- for(n = xa; n < xb; n++)\r
- line(n,ya,n,yb,col);\r
- \r
- /* Now "shaded" top and side */\r
- for(n = 0; n < xd; n++)\r
- {\r
- dotline(xa + n,yb - n ,xc + n,yb - n,col);\r
- dotline(xa + xd + n,yb - n ,xc + xd + n,yb - n,col);\r
- dotline(xb +n ,ya - n ,xb + n,yb - n,col);\r
- }\r
- }\r
- \r
-\r
-\r
-Displaying Text At Pixel Coordinates\r
-\r
-When using graphics display modes it is useful to be able to display text\r
-not at the fixed character boundaries, but at pixel coordinates. This can\r
-be achieved by implementing a print function which reads the character\r
-definition data from the BIOS ROM and uses it to plot pixels to create\r
-the shapes of the desired text. The following C function, "gr_print()"\r
-illustrates this idea using the ROM CGA (8x8) character set;\r
-\r
- void gr_print(char *output, int x, int y, unsigned char colour)\r
- {\r
- unsigned char far *ptr;\r
- unsigned char chr;\r
- unsigned char bmask;\r
- int i;\r
- int k;\r
- int oldy;\r
- int p;\r
- int height;\r
- \r
- /* The height of the characters in the font being accessed */\r
- height = 8;\r
- \r
- /* Set pointer to start of font definition in the ROM */\r
- ptr = getfontptr(3);\r
- \r
- oldy = y;\r
- p = 0;\r
- \r
- /* Loop output string */\r
- while(output[p])\r
- {\r
- /* Get first character to be displayed */\r
- chr = output[p];\r
- \r
- /* Loop pixel lines in character definition */\r
- for(i = 0; i < height; i++)\r
- {\r
- /* Get pixel line definition from the ROM */\r
- bmask = *(ptr + (chr * height) + i);\r
- \r
- /* Loop pixel columns */\r
- for (k = 0; k < 8; ++k, bmask <<= 1)\r
- {\r
- /* Test for a set bit */\r
- if(bmask & 128)\r
- /* Plot a pixel if appropriate */\r
- plot(x,y,colour);\r
- else\r
- plot(x,y,0);\r
- x++;\r
- }\r
- /* Down to next row and left to start of character */\r
- y++;\r
- x -= 8;\r
- }\r
- /* Next character to be displayed */\r
- p++;\r
- \r
- /* Back to top row of the display position */\r
- y = oldy;\r
- \r
- /* Right to next character display position */\r
- x += 8;\r
- }\r
- }\r
-\r
-The following assembly language support function is required to retrieve\r
-the address of the ROM font by calling the BIOS ROM display function\r
-which will return the address;\r
-\r
-\r
- ; GET FONT POINTER\r
- ; Small memory model\r
- ; compile with tasm /mx\r
- \r
- _TEXT segment byte public 'CODE'\r
- assume cs:_TEXT,ds:NOTHING\r
- \r
- _getfontptr proc near\r
- push bp\r
- mov bp,sp\r
- mov ax,1130h\r
- mov bh, [bp+4] ; Number for font to be retrieved\r
- int 10h\r
- mov dx,es\r
- mov ax,bp\r
- pop bp\r
- ret\r
- _getfontptr endp\r
- \r
- _TEXT ends\r
- \r
- public _getfontptr\r
- end\r
- \r
-The font number supplied to "getfontptr()" can be one of;\r
-\r
- 2 ROM EGA 8 x 14 font\r
- 3 ROM CGA 8 x 8 font\r
- 6 ROM VGA 8 x 16 font\r
-\r
-A Graphics Function Library For Turbo C\r
-\r
- /* Graphics library for 'Turbo C' (V2.01) */\r
- \r
- /* (C)1992 Copyright Servile Software */\r
- \r
- #include <stdio.h>\r
- #include <dos.h>\r
- #include <stdlib.h>\r
- #include <stdarg.h>\r
- #include <string.h>\r
- #include <math.h>\r
- \r
- /* Global variables */\r
- static unsigned char attribute;\r
- int _dmode;\r
- int _lastx;\r
- int _lasty;\r
- \r
- /* Maximum coordinates for graphics screen */\r
- static int maxx;\r
- static int maxy;\r
- \r
- /* Sprite structure */\r
- struct SP\r
- {\r
- int x;\r
- int y;\r
- char data[256];\r
- char save[256];\r
- };\r
- typedef struct SP SPRITE;\r
- \r
- \r
- /* Sine and cosine tables for trig' operations */\r
- \r
- static double sintable[360] =\r
- {0.000000000000000001,0.017452406437283512,\r
- 0.034899496702500969,0.052335956242943835,\r
- 0.069756473744125302,0.087155742747658166,\r
- 0.104528463267653457,0.121869343405147462,\r
- 0.139173100960065438,0.156434465040230869,\r
- 0.173648177666930331,0.190808995376544804,\r
- 0.207911690817759315,0.224951054343864976,\r
- 0.241921895599667702,0.258819045102520739,\r
- 0.275637355816999163,0.292371704722736769,\r
- 0.309016994374947451,0.325568154457156755,\r
- 0.342020143325668824,0.358367949545300379,\r
- 0.374606593415912181,0.390731128489273882,\r
- 0.406736643075800375,0.422618261740699608,\r
- 0.438371146789077626,0.453990499739547027,\r
- 0.469471562785891028,0.484809620246337225,\r
- 0.500000000000000222,0.515038074910054489,\r
- 0.529919264233205234,0.544639035015027417,\r
- 0.559192903470747127,0.573576436351046381,\r
- 0.587785252292473470,0.601815023152048600,\r
- 0.615661475325658625,0.629320391049837835,\r
- 0.642787609686539696,0.656059028990507720,\r
- 0.669130606358858682,0.681998360062498921,\r
- 0.694658370458997698,0.707106781186548017,\r
- 0.719339800338651636,0.731353701619170904,\r
- 0.743144825477394688,0.754709580222772458,\r
- 0.766044443118978569,0.777145961456971346,\r
- 0.788010753606722458,0.798635510047293384,\r
- 0.809016994374947895,0.819152044288992243,\r
- 0.829037572555042179,0.838670567945424494,\r
- 0.848048096156426512,0.857167300702112778,\r
- 0.866025403784439152,0.874619707139396296,\r
- 0.882947592858927432,0.891006524188368343,\r
- 0.898794046299167482,0.906307787036650381,\r
- 0.913545457642601311,0.920504853452440819,\r
- 0.927183854566787868,0.933580426497202187,\r
- 0.939692620785908761,0.945518575599317179,\r
- 0.951056516295153975,0.956304755963035880,\r
- 0.961261695938319227,0.965925826289068645,\r
- 0.970295726275996806,0.974370064785235579,\r
- 0.978147600733805911,0.981627183447664198,\r
- 0.984807753012208353,0.987688340595137992,\r
- 0.990268068741570473,0.992546151641322205,\r
- 0.994521895368273512,0.996194698091745656,\r
- 0.997564050259824309,0.998629534754573944,\r
- 0.999390827019095762,0.999847695156391270,\r
- 1.000000000000000000,0.999847695156391159,\r
- 0.999390827019095651,0.998629534754573833,\r
- 0.997564050259824087,0.996194698091745323,\r
- 0.994521895368273179,0.992546151641321761,\r
- 0.990268068741570029,0.987688340595137437,\r
- 0.984807753012207687,0.981627183447663532,\r
- 0.978147600733805245,0.974370064785234802,\r
- 0.970295726275996029,0.965925826289067757,\r
- 0.961261695938318228,0.956304755963034880,\r
- 0.951056516295152865,0.945518575599316069,\r
- 0.939692620785907651,0.933580426497200966,\r
- 0.927183854566786536,0.920504853452439486,\r
- 0.913545457642599978,0.906307787036649048,\r
- 0.898794046299166149,0.891006524188367122,\r
- 0.882947592858926211,0.874619707139395186,\r
- 0.866025403784438042,0.857167300702111778,\r
- 0.848048096156425624,0.838670567945423717,\r
- 0.829037572555041513,0.819152044288991688,\r
- 0.809016994374947451,0.798635510047293051,\r
- 0.788010753606722236,0.777145961456971346,\r
- 0.766044443118978569,0.754709580222772680,\r
- 0.743144825477395132,0.731353701619171459,\r
- 0.719339800338652302,0.707106781186548794,\r
- 0.694658370458998808,0.681998360062500142,\r
- 0.669130606358860014,0.656059028990509274,\r
- 0.642787609686541472,0.629320391049839833,\r
- 0.615661475325660845,0.601815023152051043,\r
- 0.587785252292476135,0.573576436351049268,\r
- 0.559192903470750236,0.544639035015030637,\r
- 0.529919264233208676,0.515038074910058152,\r
- 0.500000000000004219,0.484809620246341444,\r
- 0.469471562785895413,0.453990499739551634,\r
- 0.438371146789082455,0.422618261740704715,\r
- 0.406736643075805704,0.390731128489279489,\r
- 0.374606593415918010,0.358367949545306430,\r
- 0.342020143325675152,0.325568154457163306,\r
- 0.309016994374954279,0.292371704722743819,\r
- 0.275637355817006491,0.258819045102528289,\r
- 0.241921895599675502,0.224951054343872997,\r
- 0.207911690817767558,0.190808995376553270,\r
- 0.173648177666939019,0.156434465040239751,\r
- 0.139173100960074542,0.121869343405156802,\r
- 0.104528463267663005,0.087155742747667922,\r
- 0.069756473744135267,0.052335956242954007,\r
- 0.034899496702511350,0.017452406437294093,\r
- 0.000000000000010781,-0.017452406437272534,\r
- -0.034899496702489805,-0.052335956242932476,\r
- -0.069756473744113756,-0.087155742747646453,\r
- -0.104528463267641564,-0.121869343405135402,\r
- -0.139173100960053198,-0.156434465040218462,\r
- -0.173648177666917786,-0.190808995376532092,\r
- -0.207911690817746464,-0.224951054343851986,\r
- -0.241921895599654574,-0.258819045102507472,\r
- -0.275637355816985785,-0.292371704722723225,\r
- -0.309016994374933796,-0.325568154457142933,\r
- -0.342020143325654891,-0.358367949545286335,\r
- -0.374606593415898026,-0.390731128489259616,\r
- -0.406736643075785997,-0.422618261740685175,\r
- -0.438371146789063082,-0.453990499739532427,\r
- -0.469471562785876373,-0.484809620246322570,\r
- -0.499999999999985512,-0.515038074910039723,\r
- -0.529919264233190468,-0.544639035015012540,\r
- -0.559192903470732361,-0.573576436351031616,\r
- -0.587785252292458593,-0.601815023152033834,\r
- -0.615661475325643859,-0.629320391049823069,\r
- -0.642787609686525041,-0.656059028990493065,\r
- -0.669130606358844027,-0.681998360062484377,\r
- -0.694658370458983265,-0.707106781186533584,\r
- -0.719339800338637314,-0.731353701619156804,\r
- -0.743144825477380699,-0.754709580222758580,\r
- -0.766044443118964691,-0.777145961456957690,\r
- -0.788010753606709025,-0.798635510047280062,\r
- -0.809016994374934795,-0.819152044288979364,\r
- -0.829037572555029412,-0.838670567945412060,\r
- -0.848048096156414188,-0.857167300702100676,\r
- -0.866025403784427272,-0.874619707139384750,\r
- -0.882947592858916108,-0.891006524188357352,\r
- -0.898794046299156713,-0.906307787036639945,\r
- -0.913545457642591208,-0.920504853452430938,\r
- -0.927183854566778320,-0.933580426497192972,\r
- -0.939692620785899990,-0.945518575599308742,\r
- -0.951056516295145871,-0.956304755963028108,\r
- -0.961261695938311900,-0.965925826289061651,\r
- -0.970295726275990256,-0.974370064785229362,\r
- -0.978147600733800138,-0.981627183447658869,\r
- -0.984807753012203468,-0.987688340595133552,\r
- -0.990268068741566587,-0.992546151641318764,\r
- -0.994521895368270514,-0.996194698091743103,\r
- -0.997564050259822310,-0.998629534754572390,\r
- -0.999390827019094763,-0.999847695156390714,\r
- -1.000000000000000000,-0.999847695156391714,\r
- -0.999390827019096761,-0.998629534754575388,\r
- -0.997564050259826307,-0.996194698091748099,\r
- -0.994521895368276398,-0.992546151641325647,\r
- -0.990268068741574470,-0.987688340595142433,\r
- -0.984807753012213349,-0.981627183447669860,\r
- -0.978147600733812128,-0.974370064785242240,\r
- -0.970295726276004022,-0.965925826289076417,\r
- -0.961261695938327665,-0.956304755963044872,\r
- -0.951056516295163523,-0.945518575599327393,\r
- -0.939692620785919530,-0.933580426497213511,\r
- -0.927183854566799748,-0.920504853452453253,\r
- -0.913545457642614411,-0.906307787036664148,\r
- -0.898794046299181804,-0.891006524188383331,\r
- -0.882947592858942976,-0.874619707139412506,\r
- -0.866025403784455916,-0.857167300702130208,\r
- -0.848048096156444498,-0.838670567945443146,\r
- -0.829037572555061497,-0.819152044289012227,\r
- -0.809016994374968434,-0.798635510047314479,\r
- -0.788010753606744219,-0.777145961456993772,\r
- -0.766044443119001550,-0.754709580222796106,\r
- -0.743144825477418891,-0.731353701619195773,\r
- -0.719339800338677060,-0.707106781186574107,\r
- -0.694658370459024455,-0.681998360062526232,\r
- -0.669130606358886548,-0.656059028990536253,\r
- -0.642787609686568784,-0.629320391049867478,\r
- -0.615661475325688934,-0.601815023152079465,\r
- -0.587785252292504890,-0.573576436351078467,\r
- -0.559192903470779767,-0.544639035015060502,\r
- -0.529919264233238985,-0.515038074910088794,\r
- -0.500000000000035083,-0.484809620246372641,\r
- -0.469471562785926888,-0.453990499739583386,\r
- -0.438371146789114541,-0.422618261740737022,\r
- -0.406736643075838289,-0.390731128489312296,\r
- -0.374606593415951039,-0.358367949545339737,\r
- -0.342020143325708625,-0.325568154457197001,\r
- -0.309016994374988196,-0.292371704722777903,\r
- -0.275637355817040797,-0.258819045102562761,\r
- -0.241921895599710085,-0.224951054343907719,\r
- -0.207911690817802419,-0.190808995376588242,\r
- -0.173648177666974129,-0.156434465040274973,\r
- -0.139173100960109847,-0.121869343405192190,\r
- -0.104528463267698463,-0.087155742747703435,\r
- -0.069756473744170822,-0.052335956242989604,\r
- -0.034899496702546981,-0.017452406437329739\r
- };\r
-\r
-static double costable[360] = {\r
-1.000000000000000000,0.999847695156391270,\r
- 0.999390827019095762,0.998629534754573833,\r
- 0.997564050259824198,0.996194698091745545,\r
- 0.994521895368273290,0.992546151641321983,\r
- 0.990268068741570362,0.987688340595137770,\r
- 0.984807753012208020,0.981627183447663976,\r
- 0.978147600733805689,0.974370064785235246,\r
- 0.970295726275996473,0.965925826289068312,\r
- 0.961261695938318894,0.956304755963035436,\r
- 0.951056516295153531,0.945518575599316735,\r
- 0.939692620785908317,0.933580426497201743,\r
- 0.927183854566787313,0.920504853452440264,\r
- 0.913545457642600867,0.906307787036649826,\r
- 0.898794046299166927,0.891006524188367788,\r
- 0.882947592858926766,0.874619707139395630,\r
- 0.866025403784438486,0.857167300702112112,\r
- 0.848048096156425846,0.838670567945423828,\r
- 0.829037572555041513,0.819152044288991576,\r
- 0.809016994374947229,0.798635510047292607,\r
- 0.788010753606721681,0.777145961456970569,\r
- 0.766044443118977680,0.754709580222771681,\r
- 0.743144825477393911,0.731353701619170127,\r
- 0.719339800338650748,0.707106781186547129,\r
- 0.694658370458996810,0.681998360062498032,\r
- 0.669130606358857682,0.656059028990506721,\r
- 0.642787609686538697,0.629320391049836836,\r
- 0.615661475325657626,0.601815023152047601,\r
- 0.587785252292472471,0.573576436351045382,\r
- 0.559192903470746128,0.544639035015026307,\r
- 0.529919264233204124,0.515038074910053378,\r
- 0.499999999999999112,0.484809620246336115,\r
- 0.469471562785889862,0.453990499739545861,\r
- 0.438371146789076460,0.422618261740698442,\r
- 0.406736643075799154,0.390731128489272661,\r
- 0.374606593415910960,0.358367949545299158,\r
- 0.342020143325667547,0.325568154457155479,\r
- 0.309016994374946230,0.292371704722735493,\r
- 0.275637355816997887,0.258819045102519463,\r
- 0.241921895599666398,0.224951054343863643,\r
- 0.207911690817757955,0.190808995376543389,\r
- 0.173648177666928888,0.156434465040229398,\r
- 0.139173100960063939,0.121869343405145950,\r
- 0.104528463267651903,0.087155742747656584,\r
- 0.069756473744123679,0.052335956242942190,\r
- 0.034899496702499304,0.017452406437281822,\r
- -0.000000000000001715,-0.017452406437285253,\r
- -0.034899496702502732,-0.052335956242945618,\r
- -0.069756473744127107,-0.087155742747659998,\r
- -0.104528463267655317,-0.121869343405149350,\r
- -0.139173100960067325,-0.156434465040232784,\r
- -0.173648177666932274,-0.190808995376546747,\r
- -0.207911690817761285,-0.224951054343866974,\r
- -0.241921895599669701,-0.258819045102522793,\r
- -0.275637355817001217,-0.292371704722738768,\r
- -0.309016994374949450,-0.325568154457158698,\r
- -0.342020143325670822,-0.358367949545302322,\r
- -0.374606593415914124,-0.390731128489275825,\r
- -0.406736643075802318,-0.422618261740701329,\r
- -0.438371146789079125,-0.453990499739548303,\r
- -0.469471562785892083,-0.484809620246338169,\r
- -0.500000000000000999,-0.515038074910054933,\r
- -0.529919264233205567,-0.544639035015027528,\r
- -0.559192903470747127,-0.573576436351046159,\r
- -0.587785252292473026,-0.601815023152048045,\r
- -0.615661475325657848,-0.629320391049836947,\r
- -0.642787609686538697,-0.656059028990506499,\r
- -0.669130606358857238,-0.681998360062497477,\r
- -0.694658370458996033,-0.707106781186546240,\r
- -0.719339800338649749,-0.731353701619168906,\r
- -0.743144825477392579,-0.754709580222770238,\r
- -0.766044443118976237,-0.777145961456968903,\r
- -0.788010753606719905,-0.798635510047290720,\r
- -0.809016994374945231,-0.819152044288989578,\r
- -0.829037572555039404,-0.838670567945421719,\r
- -0.848048096156423625,-0.857167300702109891,\r
- -0.866025403784436265,-0.874619707139393410,\r
- -0.882947592858924435,-0.891006524188365345,\r
- -0.898794046299164484,-0.906307787036647494,\r
- -0.913545457642598424,-0.920504853452437932,\r
- -0.927183854566784982,-0.933580426497199412,\r
- -0.939692620785906096,-0.945518575599314515,\r
- -0.951056516295151311,-0.956304755963033326,\r
- -0.961261695938316785,-0.965925826289066314,\r
- -0.970295726275994586,-0.974370064785233358,\r
- -0.978147600733803912,-0.981627183447662310,\r
- -0.984807753012206577,-0.987688340595136327,\r
- -0.990268068741569030,-0.992546151641320873,\r
- -0.994521895368272291,-0.996194698091744657,\r
- -0.997564050259823532,-0.998629534754573389,\r
- -0.999390827019095318,-0.999847695156391048,\r
- -1.000000000000000000,-0.999847695156391381,\r
- -0.999390827019096095,-0.998629534754574499,\r
- -0.997564050259825086,-0.996194698091746544,\r
- -0.994521895368274622,-0.992546151641323537,\r
- -0.990268068741572027,-0.987688340595139658,\r
- -0.984807753012210241,-0.981627183447666418,\r
- -0.978147600733808353,-0.974370064785238244,\r
- -0.970295726275999804,-0.965925826289071865,\r
- -0.961261695938322669,-0.956304755963039654,\r
- -0.951056516295157972,-0.945518575599321509,\r
- -0.939692620785913424,-0.933580426497207072,\r
- -0.927183854566793086,-0.920504853452446370,\r
- -0.913545457642607195,-0.906307787036656598,\r
- -0.898794046299173921,-0.891006524188375226,\r
- -0.882947592858934649,-0.874619707139403846,\r
- -0.866025403784447034,-0.857167300702120993,\r
- -0.848048096156435061,-0.838670567945433487,\r
- -0.829037572555051505,-0.819152044289001902,\r
- -0.809016994374957998,-0.798635510047303709,\r
- -0.788010753606733227,-0.777145961456982559,\r
- -0.766044443118990004,-0.754709580222784449,\r
- -0.743144825477407012,-0.731353701619183672,\r
- -0.719339800338664737,-0.707106781186561450,\r
- -0.694658370459011576,-0.681998360062513242,\r
- -0.669130606358873337,-0.656059028990522708,\r
- -0.642787609686555128,-0.629320391049853711,\r
- -0.615661475325674945,-0.601815023152065254,\r
- -0.587785252292490457,-0.573576436351063812,\r
- -0.559192903470765002,-0.544639035015045625,\r
- -0.529919264233223886,-0.515038074910073473,\r
- -0.500000000000019651,-0.484809620246357043,\r
- -0.469471562785911123,-0.453990499739567510,\r
- -0.438371146789098498,-0.422618261740720869,\r
- -0.406736643075822024,-0.390731128489295920,\r
- -0.374606593415934497,-0.358367949545323083,\r
- -0.342020143325691917,-0.325568154457180181,\r
- -0.309016994374971266,-0.292371704722760861,\r
- -0.275637355817023644,-0.258819045102545497,\r
- -0.241921895599692793,-0.224951054343890372,\r
- -0.207911690817784989,-0.190808995376570756,\r
- -0.173648177666956560,-0.156434465040257376,\r
- -0.139173100960092194,-0.121869343405174496,\r
- -0.104528463267680741,-0.087155742747685686,\r
- -0.069756473744153044,-0.052335956242971805,\r
- -0.034899496702529162,-0.017452406437311916,\r
- -0.000000000000028605,0.017452406437254715,\r
- 0.034899496702471985,0.052335956242914670,\r
- 0.069756473744095979,0.087155742747628689,\r
- 0.104528463267623842,0.121869343405117708,\r
- 0.139173100960035545,0.156434465040200865,\r
- 0.173648177666900216,0.190808995376514606,\r
- 0.207911690817729033,0.224951054343834611,\r
- 0.241921895599637282,0.258819045102490264,\r
- 0.275637355816968632,0.292371704722706127,\r
- 0.309016994374916809,0.325568154457126058,\r
- 0.342020143325638126,0.358367949545269682,\r
- 0.374606593415881484,0.390731128489243240,\r
- 0.406736643075769733,0.422618261740669021,\r
- 0.438371146789047095,0.453990499739516551,\r
- 0.469471562785860608,0.484809620246306972,\r
- 0.499999999999970080,0.515038074910024402,\r
- 0.529919264233175369,0.544639035014997663,\r
- 0.559192903470717484,0.573576436351016961,\r
- 0.587785252292444271,0.601815023152019624,\r
- 0.615661475325629759,0.629320391049809191,\r
- 0.642787609686511385,0.656059028990479520,\r
- 0.669130606358830815,0.681998360062471387,\r
- 0.694658370458970387,0.707106781186521038,\r
- 0.719339800338624991,0.731353701619144592,\r
- 0.743144825477368709,0.754709580222746812,\r
- 0.766044443118953255,0.777145961456946477,\r
- 0.788010753606698033,0.798635510047269292,\r
- 0.809016994374924359,0.819152044288969150,\r
- 0.829037572555019531,0.838670567945402290,\r
- 0.848048096156404752,0.857167300702091572,\r
- 0.866025403784418391,0.874619707139376090,\r
- 0.882947592858907782,0.891006524188349247,\r
- 0.898794046299148941,0.906307787036632395,\r
- 0.913545457642583991,0.920504853452423943,\r
- 0.927183854566771659,0.933580426497186644,\r
- 0.939692620785893884,0.945518575599302968,\r
- 0.951056516295140320,0.956304755963022890,\r
- 0.961261695938306904,0.965925826289056988,\r
- 0.970295726275985926,0.974370064785225365,\r
- 0.978147600733796474,0.981627183447655538,\r
- 0.984807753012200360,0.987688340595130776,\r
- 0.990268068741564034,0.992546151641316543,\r
- 0.994521895368268627,0.996194698091741548,\r
- 0.997564050259821089,0.998629534754571502,\r
- 0.999390827019094097,0.999847695156390381\r
- \r
- int dtoi(double x)\r
- {\r
- /* Rounds a floating point number to an integer */\r
- \r
- int y;\r
- \r
- y = x;\r
- if (y < (x + 0.5))\r
- y++;\r
- return(y);\r
- }\r
- \r
- int getmode(int *ncols)\r
- {\r
- /* Returns current video mode and number of columns in ncols */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 0x0F;\r
- int86(0x10, &inreg, &outreg);\r
- *ncols = outreg.h.ah;\r
- return(outreg.h.al);\r
- }\r
- \r
- void setvideo(unsigned char mode)\r
- {\r
- /* Sets the video display mode and clears the screen */\r
- /* (modes above 127 on VGA monitors do not clear the screen) */\r
- \r
- asm mov ax , mode;\r
- asm int 10h;\r
- \r
- /* Set global variables */\r
- switch(mode)\r
- {\r
- case 0:\r
- case 1:\r
- case 2:\r
- case 3: break;\r
- case 4:\r
- case 9:\r
- case 13:\r
- case 19:\r
- case 5: maxx = 320;\r
- maxy = 200;\r
- break;\r
- case 14:\r
- case 10:\r
- case 6: maxx = 640;\r
- maxy = 200;\r
- break;\r
- case 7: maxx = 720;\r
- maxy = 400;\r
- break;\r
- case 8: maxx = 160;\r
- maxy = 200;\r
- break;\r
- case 15:\r
- case 16: maxx = 640;\r
- maxy = 350;\r
- break;\r
- case 17:\r
- case 18: maxx = 640;\r
- maxy = 480;\r
- break;\r
- \r
- }\r
- _dmode = mode;\r
- }\r
- \r
- void getcursor(int *row, int *col)\r
- {\r
- /* Returns the cursor position and size for the currently\r
- active\r
- video display page */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.bh = 0;\r
- inreg.h.ah = 0x03;\r
- int86(0x10, &inreg, &outreg);\r
- *row = outreg.h.dh;\r
- *col = outreg.h.dl;\r
- }\r
- \r
- void setcursor(char status)\r
- {\r
- /* Set cursor size, if status is 0x20 the cursor is not\r
- displayed */\r
- \r
- asm mov ah,1\r
- asm mov ch,status\r
- asm mov cl,7\r
- asm int 10h\r
- }\r
- \r
- void at(int row, int col)\r
- {\r
- /* Position text cursor on current screen */\r
- \r
- asm mov bh , 0;\r
- asm mov dh , row;\r
- asm mov dl , col;\r
- asm mov ah , 02h;\r
- asm int 10h;\r
- }\r
- \r
- void clsc(unsigned char attrib)\r
- {\r
- /* Clear display and fill with new attribute */\r
- \r
- asm mov ax , 073dh;\r
- asm mov bh , attrib;\r
- asm mov cx , 0;\r
- asm mov dx , 3c4fh;\r
- asm int 10h;\r
- \r
- /* Now move text cursor to origin (0,0) */\r
- asm mov bh , 0;\r
- asm mov dx , 0;\r
- asm mov ah , 02h;\r
- asm int 10h;\r
- }\r
- \r
- void clrwindow(int x1, int y1,int x2, int y2, unsigned char attrib)\r
- {\r
- /* Clear a text window */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.al = (unsigned char)(y2 - y1 + 1);\r
- inreg.h.bh = attrib;\r
- inreg.h.cl = (unsigned char)x1;\r
- inreg.h.ch = (unsigned char)y1;\r
- inreg.h.dl = (unsigned char)x2;\r
- inreg.h.dh = (unsigned char)y2;\r
- inreg.h.ah = 0x06;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- void scrollsc(void)\r
- {\r
- /* Scroll the text screen */\r
- \r
- asm mov al,01h;\r
- asm mov cx,0\r
- asm mov dx,3c4fh;\r
- asm mov bh,attribute;\r
- asm mov ah, 06h;\r
- asm int 10h;\r
- }\r
- \r
- void winscr(unsigned char left,unsigned char top, unsigned char\r
- right,unsigned char bottom,\r
- unsigned char direction, unsigned char numlines)\r
- {\r
- /* Scroll a text window */\r
- \r
- asm mov al , numlines;\r
- asm mov cl , left;\r
- asm mov ch , top;\r
- asm mov dl , right;\r
- asm mov dh , bottom;\r
- asm mov bh , attribute;\r
- asm mov ah , direction;\r
- asm int 10h;\r
- }\r
- \r
- unsigned char attr(int foregrnd, int backgrnd)\r
- {\r
- /* Convert a colour pair into a single attribute */\r
- \r
- return((char)((backgrnd << 4) + foregrnd));\r
- }\r
- \r
- void shadewin(unsigned char left, unsigned char top, unsigned char\r
- right, unsigned char bottom)\r
- {\r
- /* Shade a text window */\r
- \r
- int row;\r
- int col;\r
- int oldrow;\r
- int oldcol;\r
- \r
- /* Preserve existing coords */\r
- getcursor(&oldrow,&oldcol);\r
- \r
- col = right + 1;\r
- for (row = top + 1; row <= bottom + 1; row++)\r
- {\r
- /* Move to row,col */\r
- asm mov bh , 0;\r
- asm mov dh , row;\r
- asm mov dl , col;\r
- asm mov ah , 02h;\r
- asm int 10h;\r
- /* Get character */\r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov ah , 08h;\r
- asm int 10h;\r
- \r
- /* Write in attribute 7 */\r
- asm mov ah , 09h;\r
- asm mov bl, 07h;\r
- asm mov cx, 01h;\r
- asm int 10h;\r
- }\r
- bottom++;\r
- for (col = left + 1; col <= right + 1; col++)\r
- {\r
- /* Move to row,col */\r
- asm mov bh , 0;\r
- asm mov dh , bottom;\r
- asm mov dl , col;\r
- asm mov ah , 02h;\r
- asm int 10h;\r
- \r
- /* Get character */\r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov ah , 08h;\r
- asm int 10h;\r
- \r
- /* Write in attribute 7 */\r
- asm mov ah , 09h;\r
- asm mov bl, 07h;\r
- asm mov cx, 01h;\r
- asm int 10h;\r
- }\r
- \r
- /* Return to original position */\r
- /* Move to row,col */\r
- asm mov bh , 0;\r
- asm mov dh , oldrow;\r
- asm mov dl , oldcol;\r
- asm mov ah , 02h;\r
- asm int 10h;\r
- }\r
- \r
- void bprintf(char *format, ...)\r
- {\r
- /* print text to graphics screen correctly */\r
- \r
- va_list arg_ptr;\r
- char output[1000];\r
- int c, r, n, p = 0;\r
- unsigned char text;\r
- unsigned char page;\r
- \r
- va_start(arg_ptr, format);\r
- vsprintf(output, format, arg_ptr);\r
- \r
- if (strcmp(output,"\r\n") == 0)\r
- fputs(output,stdout);\r
- \r
- else\r
- {\r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov page, bh;\r
- \r
- getmode(&n);\r
- getcursor(&r,&c);\r
- \r
- while (output[p])\r
- {\r
- text = output[p++];\r
- asm mov bh , page;\r
- asm mov bl , attribute;\r
- asm mov cx , 01h;\r
- asm mov ah , 09h;\r
- asm mov al , text;\r
- asm int 10h;\r
- \r
- c++;\r
- if (c < (n-1))\r
- at( r, c);\r
- else\r
- {\r
- c = 0;\r
- at(++r,0);\r
- }\r
- }\r
- }\r
- }\r
- \r
- void wrtstr(char *output)\r
- {\r
- /* TTY text output. The original screen attributes remain\r
- unaffected */\r
- \r
- int p = 0;\r
- unsigned char page;\r
- unsigned char text;\r
- \r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov page, bh;\r
- \r
- while (output[p])\r
- {\r
- text = output[p++];\r
- asm mov bh , page;\r
- asm mov ah , 0Eh;\r
- asm mov al , text;\r
- asm int 10h;\r
- }\r
- }\r
- \r
- void getwin(int left, int top, int right, int bottom, char *buffer)\r
- {\r
- /* Read a text window into a variable */\r
- \r
- int oldleft;\r
- unsigned char page;\r
- \r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov page, bh;\r
- \r
- while(top <= bottom)\r
- {\r
- oldleft = left;\r
- while(left <= right)\r
- {\r
- at(top,left);\r
- asm mov bh , page;\r
- asm mov ah , 08h;\r
- asm int 10h;\r
- *buffer++ = _AL;\r
- *buffer++ = _AH;\r
- left++;\r
- }\r
- left = oldleft;\r
- top++;\r
- }\r
- }\r
- \r
- void putwin(int left, int top, int right, int bottom,char *buffer)\r
- {\r
- /* Display a text window from a variable */\r
- \r
- int oldleft;\r
- unsigned char chr;\r
- unsigned char attr;\r
- unsigned char page;\r
- \r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov page, bh;\r
- \r
- while(top <= bottom)\r
- {\r
- oldleft = left;\r
- while(left <= right)\r
- {\r
- at(top,left);\r
- chr = *buffer++;\r
- attr = *buffer++;\r
- asm mov bh , page;\r
- asm mov ah , 09h;\r
- asm mov al, chr;\r
- asm mov bl, attr;\r
- asm mov cx,1;\r
- asm int 10h;\r
- left++;\r
- }\r
- left = oldleft;\r
- top++;\r
- }\r
- }\r
- \r
- void setpalette(unsigned char palno)\r
- {\r
- /* Sets the video palette */\r
- \r
- asm mov bh,01h;\r
- asm mov bl,palno;\r
- asm mov ah,0Bh;\r
- asm int 10h;\r
- }\r
- \r
- void setborder(unsigned char x)\r
- {\r
- /* Set border colour */\r
- \r
- asm mov bh, x;\r
- asm mov ax ,1001h;\r
- asm int 10h;\r
- }\r
- \r
- void setlines(unsigned char x)\r
- {\r
- /* Set text display number of lines */\r
- \r
- asm mov ah,11h;\r
- asm mov al,x;\r
- asm mov bl,0;\r
- asm int 10h;\r
- }\r
- \r
- void graphbackground(unsigned char colour)\r
- {\r
- /* Selects the background colour for a graphics mode */\r
- \r
- asm mov bh,0;\r
- asm mov bl,colour;\r
- asm mov ah, 0Bh;\r
- asm int 10h;\r
- }\r
- \r
- void plot(int x, int y, unsigned char colour)\r
- {\r
- /* Sets a pixel at the specified coordinates to the specified\r
- colour.\r
- The variables _lastx and _lasty are updated. */\r
- \r
- unsigned char far *video;\r
- \r
- _lastx = x;\r
- _lasty = y;\r
- \r
- if (_dmode == 19)\r
- {\r
- video = MK_FP(0xA000,0);\r
- video[x+y*320] = colour;\r
- }\r
- else\r
- {\r
- asm mov al , colour;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- }\r
- \r
- int pixset(int x, int y)\r
- {\r
- /* Returns the colour of the specified pixel */\r
- \r
- asm mov cx ,x;\r
- asm mov dx ,y;\r
- asm mov ah ,0Dh;\r
- asm int 10h;\r
- return(_AL);\r
- }\r
- \r
- void move(int x, int y)\r
- {\r
- /* Sets the value of the variables _lastx and _lasty */\r
- \r
- _lastx = x;\r
- _lasty = y;\r
- }\r
- \r
- void line(int a, int b, int c, int d, int col)\r
- {\r
- /* Draws a straight line from (a,b) to (c,d) in colour col */\r
- \r
- int u;\r
- int v;\r
- int d1x;\r
- int d1y;\r
- int d2x;\r
- int d2y;\r
- int m;\r
- int n;\r
- int s;\r
- int i;\r
- \r
- if (a < 0)\r
- a = 0;\r
- else\r
- if (a > maxx)\r
- a = maxx;\r
- \r
- if (c < 0)\r
- c = 0;\r
- else\r
- if (c > maxx)\r
- c = maxx;\r
- \r
- if (b < 0)\r
- b = 0;\r
- else\r
- if (b > maxy)\r
- b = maxy;\r
- \r
- if (d < 0)\r
- d = 0;\r
- else\r
- if (d > maxy)\r
- d = maxy;\r
- \r
- u = c - a;\r
- v = d - b;\r
- \r
- if (u == 0)\r
- {\r
- d1x = 0;\r
- m = 0;\r
- }\r
- else\r
- {\r
- m = abs(u);\r
- if (u < 0)\r
- d1x = -1;\r
- else\r
- if (u > 0)\r
- d1x = 1;\r
- }\r
- if ( v == 0)\r
- {\r
- d1y = 0;\r
- n = 0;\r
- }\r
- else\r
- {\r
- n = abs(v);\r
- if (v < 0)\r
- d1y = -1;\r
- else\r
- if (v > 0)\r
- d1y = 1;\r
- }\r
- if (m > n)\r
- {\r
- d2x = d1x;\r
- d2y = 0;\r
- }\r
- else\r
- {\r
- d2x = 0;\r
- d2y = d1y;\r
- m = n;\r
- n = abs(u);\r
- }\r
- s = m / 2;\r
- \r
- for (i = 0; i <= m; i++)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 0;\r
- asm mov ah ,0Ch;\r
- asm mov cx ,a;\r
- asm mov dx ,b;\r
- asm int 10h;\r
- \r
- s += n;\r
- if (s >= m)\r
- {\r
- s -= m;\r
- a += d1x;\r
- b += d1y;\r
- }\r
- else\r
- {\r
- a += d2x;\r
- b += d2y;\r
- }\r
- }\r
- _lastx = a;\r
- _lasty = b;\r
- }\r
- \r
- void ellipse(int x, int y, int xrad, int yrad,double incline,int\r
- col)\r
- {\r
- /* Draws an ellipse */\r
- \r
- int f;\r
- float a;\r
- float b;\r
- float c;\r
- float d;\r
- int cols;\r
- double div;\r
- \r
- incline = 1 / sintable[(int)incline];\r
- \r
- if (getmode(&cols) == 6)\r
- div = 2.2;\r
- else\r
- div = 1.3;\r
- \r
- /* Compensate for pixel shape */\r
- \r
- a = x + xrad;\r
- b = y + sintable[0] * yrad + xrad/incline / div;\r
- \r
- for(f = 5; f < 360; f += 5)\r
- {\r
- c = x + costable[f] * xrad;\r
- d = y + sintable[f] * yrad + (costable[f] *\r
- xrad)/incline/div;\r
- \r
- line(a,b,c,d,col);\r
- \r
- a = c;\r
- b = d;\r
- }\r
- /* Ensure join */\r
- line(a,b,x + xrad,y + sintable[0] * yrad + xrad/incline /\r
- div,col);\r
- }\r
- \r
- void polygon(int x, int y, int rad, int col, int sides, int start)\r
- {\r
- /* Draws a regular polygon */\r
- \r
- double f;\r
- double div;\r
- double a;\r
- double b;\r
- double c;\r
- double d;\r
- double aa;\r
- double bb;\r
- int cols;\r
- double step;\r
- \r
- step = 360 / sides;\r
- \r
- if (getmode(&cols) == 6)\r
- div = 2.2;\r
- else\r
- div = 1.3;\r
- aa = a = x + costable[start] * rad;\r
- bb = b = y + sintable[start] * rad / div;\r
- \r
- for(f = start + step; f < start + 360; f += step)\r
- {\r
- c = x + costable[(int)f] * rad;\r
- d = y + sintable[(int)f] * rad / div;\r
- line(a,b,c,d,col);\r
- a = c;\r
- b = d;\r
- }\r
- line(a,b,aa,bb,col);\r
- }\r
- \r
- void arc(int x, int y, int rad, int start, int end,int col)\r
- {\r
- /* Draw an arc */\r
- \r
- int f;\r
- float a;\r
- float b;\r
- float c;\r
- float d;\r
- int cols;\r
- float div;\r
- \r
- if (getmode(&cols) == 6)\r
- div = 2.2;\r
- else\r
- div = 1.3;\r
- a = x + costable[start] * rad;\r
- b = y + sintable[start] * rad / div;\r
- \r
- for(f = start; f <= end; f ++)\r
- {\r
- c = x + costable[f] * rad;\r
- d = y + sintable[f] * rad / div;\r
- line(a,b,c,d,col);\r
- a = c;\r
- b = d;\r
- }\r
- }\r
- \r
- void segm(int x, int y, int rad, int start, int end,int col)\r
- {\r
- /* Draw a segment of a circle */\r
- \r
- int f;\r
- float a;\r
- float b;\r
- float c;\r
- float d;\r
- int cols;\r
- double div;\r
- \r
- if (getmode(&cols) == 6)\r
- div = 2.2;\r
- else\r
- div = 1.3;\r
- a = x + costable[start] * rad;\r
- b = y + sintable[start] * rad / div;\r
- \r
- line(x,y,a,b,col);\r
- \r
- for(f = start; f <= end ; f ++)\r
- {\r
- c = x + costable[f] * rad;\r
- d = y + sintable[f] * rad / div;\r
- line(a,b,c,d,col);\r
- a = c;\r
- b = d;\r
- }\r
- line(x,y,a,b,col);\r
- }\r
- \r
- void box(int xa,int ya, int xb, int yb, int col)\r
- {\r
- /* Draws a box for use in 2d histogram graphs etc */\r
- \r
- line(xa,ya,xa,yb,col);\r
- line(xa,yb,xb,yb,col);\r
- line(xb,yb,xb,ya,col);\r
- line(xb,ya,xa,ya,col);\r
- }\r
- \r
- void tri(int xa,int ya, int xb, int yb, int xc, int yc,int col)\r
- {\r
- /* Draw a triangle */\r
- \r
- line(xa,ya,xb,yb,col);\r
- line(xb,yb,xc,yc,col);\r
- line(xc,yc,xa,ya,col);\r
- }\r
- \r
- void fill(int x, int y, int col,int pattern)\r
- {\r
- /* Fill a boundered shape using a hatch pattern */\r
- \r
- int xa;\r
- int ya;\r
- int bn;\r
- int byn;\r
- \r
- int hatch[10][8] = { 255,255,255,255,255,255,255,255,\r
- 128,64,32,16,8,4,2,1,\r
- 1,2,4,8,16,32,64,128,\r
- 1,2,4,8,8,4,2,1,\r
- 238,238,238,238,238,238,238,238,\r
- 170,85,170,85,170,85,170,85,\r
- 192,96,48,24,12,6,3,1,\r
- 62,62,62,0,227,227,227,0,\r
- 129,66,36,24,24,36,66,129,\r
- 146,36,146,36,146,36,146,36\r
- };\r
- \r
- /* Patterns for fill, each integer describes a row of dots */\r
- \r
- xa = x;\r
- ya = y; /* Save Origin */\r
- \r
- if(pixset(x,y))\r
- return;\r
- \r
- bn = 1;\r
- byn = 0;\r
- \r
- do\r
- {\r
- if (hatch[pattern][byn] != 0)\r
- {\r
- /* If blank ignore */\r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x--;\r
- bn <<= 1;\r
- if (bn > 128)\r
- bn = 1;\r
- }\r
- while(!pixset(x,y) && (x > -1));\r
- \r
- x = xa + 1;\r
- bn = 128;\r
- \r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x++;\r
- bn >>=1;\r
- if (bn <1)\r
- bn = 128;\r
- }\r
- while((!pixset(x,y)) && (x <= maxx));\r
- }\r
- x = xa;\r
- y--;\r
- bn = 1;\r
- byn++;\r
- if (byn > 7)\r
- byn = 0;\r
- \r
- }\r
- while(!pixset(x,y) && ( y > -1));\r
- \r
- /* Now travel downwards */\r
- \r
- y = ya + 1;\r
- \r
- byn = 7;\r
- bn = 1;\r
- do\r
- {\r
- /* Travel left */\r
- if (hatch[pattern][byn] !=0)\r
- {\r
- do\r
- {\r
- if ( (bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- \r
- x--;\r
- bn <<= 1;\r
- if (bn > 128)\r
- bn = 1;\r
- }\r
- while(!pixset(x,y) && (x > -1));\r
- \r
- /* Back to x origin */\r
- x = xa + 1 ;\r
- bn = 128;\r
- \r
- /* Travel right */\r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x++;\r
- bn >>=1;\r
- if (bn <1)\r
- bn = 128;\r
- }\r
- while((!pixset(x,y)) && (x <= maxx));\r
- }\r
- x = xa;\r
- bn = 1;\r
- y++;\r
- byn--;\r
- if (byn < 0)\r
- byn = 7;\r
- }\r
- while((!pixset(x,y)) && (y <= maxy));\r
- }\r
- \r
- void invert(int xa,int ya, int xb, int yb, int col)\r
- {\r
- /* Invert a pixel window */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.al = (unsigned char)(128 | col);\r
- inreg.h.ah = 0x0C;\r
- for(inreg.x.cx = (unsigned int)xa; inreg.x.cx <= (unsigned\r
- int)xb; inreg.x.cx++)\r
- for(inreg.x.dx = (unsigned)ya; inreg.x.dx <= (unsigned)yb;\r
- inreg.x.dx++)\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- void circle(int x_centre , int y_centre, int radius, int colour)\r
- {\r
- int x,y,delta;\r
- int startx,endx,x1,starty,endy,y1;\r
- int asp_ratio;\r
- \r
- if (_dmode == 6)\r
- asp_ratio = 22;\r
- else\r
- asp_ratio = 13;\r
- \r
- y = radius;\r
- delta = 3 - 2 * radius;\r
- for(x = 0; x < y; )\r
- {\r
- starty = y * asp_ratio / 10;\r
- endy = (y + 1) * asp_ratio / 10;\r
- startx = x * asp_ratio / 10;\r
- endx = (x + 1) * asp_ratio / 10;\r
- for(x1 = startx; x1 < endx; ++x1)\r
- {\r
- plot(x1+x_centre,y+y_centre,colour);\r
- plot(x1+x_centre,y_centre - y,colour);\r
- plot(x_centre - x1,y_centre - y,colour);\r
- plot(x_centre - x1,y + y_centre,colour);\r
- }\r
- \r
- for(y1 = starty; y1 < endy; ++y1)\r
- {\r
- plot(y1+x_centre,x+y_centre,colour);\r
- plot(y1+x_centre,y_centre - x,colour);\r
- plot(x_centre - y1,y_centre - x,colour);\r
- plot(x_centre - y1,x + y_centre,colour);\r
- }\r
- \r
- if (delta < 0)\r
- delta += 4 * x + 6;\r
- else\r
- {\r
- delta += 4*(x-y)+10;\r
- y--;\r
- }\r
- x++;\r
- }\r
- \r
- if(y)\r
- {\r
- starty = y * asp_ratio / 10;\r
- endy = (y + 1) * asp_ratio / 10;\r
- startx = x * asp_ratio / 10;\r
- endx = (x + 1) * asp_ratio / 10;\r
- for(x1 = startx; x1 < endx; ++x1)\r
- {\r
- plot(x1+x_centre,y+y_centre,colour);\r
- plot(x1+x_centre,y_centre - y,colour);\r
- plot(x_centre - x1,y_centre - y,colour);\r
- plot(x_centre - x1,y + y_centre,colour);\r
- }\r
- \r
- for(y1 = starty; y1 < endy; ++y1)\r
- {\r
- plot(y1+x_centre,x+y_centre,colour);\r
- plot(y1+x_centre,y_centre - x,colour);\r
- plot(x_centre - y1,y_centre - x,colour);\r
- plot(x_centre - y1,x + y_centre,colour);\r
- }\r
- }\r
- }\r
- \r
- void draw(int x, int y, int colour)\r
- {\r
- /* Draws a line from _lastx,_lasty to x,y */\r
- \r
- line(_lastx,_lasty,x,y,colour);\r
- }\r
- \r
- void psprite(SPRITE *sprite,int x,int y)\r
- {\r
- int origx;\r
- int origy;\r
- int z;\r
- int count;\r
- int col;\r
- int pos;\r
- unsigned char far *video;\r
- \r
- if (_dmode == 19)\r
- {\r
- /* Super fast direct video write in mode 19 for sprites */\r
- \r
- video = MK_FP(0xA000,0);\r
- \r
- origx = x;\r
- origy = y;\r
- \r
- if (sprite->x != -1)\r
- {\r
- /* This sprite has been displayed before */\r
- /* replace background */\r
- /* This must be done first in case the sprite\r
- overlaps itself */\r
- x = sprite->x;\r
- y = sprite->y;\r
- col = 0;\r
- pos = x + y * 320;\r
- for(count = 0; count < 256; count++)\r
- {\r
- video[pos] = sprite->save[count];\r
- col++;\r
- if (col == 16)\r
- {\r
- pos += 305;\r
- col = 0;\r
- }\r
- else\r
- pos++;\r
- }\r
- }\r
- \r
- x = origx;\r
- y = origy;\r
- col = 0;\r
- \r
- pos = x + y * 320;\r
- \r
- for(count = 0; count < 256; count++)\r
- {\r
- sprite->save[count] = video[pos];\r
- z = sprite->data[count];\r
- if (z != 255)\r
- video[pos] = z;\r
- \r
- col++;\r
- if (col == 16)\r
- {\r
- pos += 305;\r
- col = 0;\r
- }\r
- else\r
- pos++;\r
- }\r
- sprite->x = origx;\r
- sprite->y = origy;\r
- \r
- return;\r
- }\r
- \r
- origx = x;\r
- origy = y;\r
- \r
- if (sprite->x != -1)\r
- {\r
- /* This sprite has been displayed before */\r
- /* replace background */\r
- /* This must be done first in case the sprite overlaps\r
- itself */\r
- x = sprite->x;\r
- y = sprite->y;\r
- col = 0;\r
- for(count = 0; count < 256; count++)\r
- {\r
- if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))\r
- {\r
- z = sprite->save[count];\r
- asm mov al , z;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- col++;\r
- if (col == 16)\r
- {\r
- y++;\r
- x = sprite->x;\r
- col = 0;\r
- }\r
- else\r
- x++;\r
- }\r
- }\r
- \r
- x = origx;\r
- y = origy;\r
- col = 0;\r
- \r
- for(count = 0; count < 256; count++)\r
- {\r
- if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))\r
- {\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Dh;\r
- asm int 10h;\r
- asm mov z ,al;\r
- sprite->save[count] = z;\r
- z = sprite->data[count];\r
- \r
- if (z != 255)\r
- {\r
- asm mov al , z;\r
- asm mov bh , 0;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- }\r
- col++;\r
- if (col == 16)\r
- {\r
- y++;\r
- x = origx;\r
- col = 0;\r
- }\r
- else\r
- x++;\r
- }\r
- sprite->x = origx;\r
- sprite->y = origy;\r
- return;\r
- }\r
- \r
- \r
-\r
-Displaying A PCX File\r
-\r
-The following program is offered as a practical example of graphics with\r
-the IBM PC. It reads a file of the `PCX' format and displays the image on\r
-the screen.\r
-\r
- \r
- /* Read a PCX file and display image */\r
- \r
- #include <dos.h>\r
- #include <io.h>\r
- #include <fcntl.h>\r
- \r
- typedef struct\r
- {\r
- unsigned char man;\r
- unsigned char version;\r
- unsigned char encoding;\r
- unsigned char bpp;\r
- int xmin;\r
- int ymin;\r
- int xmax;\r
- int ymax;\r
- int hdpi;\r
- int vdpi;\r
- int colormap[24];\r
- char reserved;\r
- unsigned char planes;\r
- int bpl;\r
- int palette;\r
- int hss;\r
- int vsize;\r
- char pad[54];\r
- }\r
- PCX_HEADER;\r
- \r
- PCX_HEADER header;\r
- \r
- int x;\r
- int y;\r
- \r
- union REGS inreg,outreg;\r
- \r
- void setvideo(unsigned char mode)\r
- {\r
- /* Sets the video display mode and clears the screen */\r
- \r
- inreg.h.al = mode;\r
- inreg.h.ah = 0x00;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- void plot(int x, int y, unsigned char colour)\r
- {\r
- \r
- if (x < header.xmax && y < header.ymax)\r
- {\r
- \r
- /* Direct video plot in modes 16 & 18 only! */\r
- asm mov ax,y;\r
- asm mov dx,80;\r
- asm mul dx;\r
- asm mov bx,x;\r
- asm mov cl,bl;\r
- \r
- asm shr bx,1;\r
- asm shr bx,1;\r
- asm shr bx,1;\r
- asm add bx,ax;\r
- \r
- asm and cl,7;\r
- asm xor cl,7;\r
- asm mov ah,1;\r
- asm shl ah,cl;\r
- \r
- asm mov dx,3ceh;\r
- asm mov al,8;\r
- asm out dx,ax;\r
- \r
- asm mov ax,(02h shl 8) + 5;\r
- asm out dx,ax;\r
- \r
- asm mov ax,0A000h;\r
- asm mov es,ax;\r
- \r
- asm mov al,es:[bx];\r
- asm mov al,byte ptr colour;\r
- asm mov es:[bx],al;\r
- \r
- asm mov ax,(0FFh shl 8 ) + 8;\r
- asm out dx,ax;\r
- \r
- asm mov ax,(00h shl 8) + 5;\r
- asm out dx,ax;\r
- }\r
- }\r
- \r
- void DISPLAY(unsigned char data)\r
- {\r
- int n;\r
- int bit;\r
- \r
- bit = 32;\r
- \r
- for (n = 0; n < 6; n++)\r
- {\r
- if (data & bit)\r
- plot(x,y,1);\r
- else\r
- plot(x,y,0);\r
- bit >>= 1;\r
- x++;\r
- }\r
- }\r
- \r
- main(int argc, char *argv[])\r
- {\r
- int fp;\r
- int total_bytes;\r
- int n;\r
- unsigned char data;\r
- int count;\r
- int scan;\r
- \r
- if (argc != 2)\r
- {\r
- puts("USAGE IS getpcx <filename>");\r
- exit(0);\r
- }\r
- \r
- setvideo(16);\r
- \r
- x = 0;\r
- y = 0;\r
- \r
- fp = open(argv[1],O_RDONLY|O_BINARY);\r
- \r
- _read(fp,&header,128);\r
- \r
- total_bytes = header.planes * header.bpl;\r
- \r
- for(scan = 0; scan <= header.ymax; scan++)\r
- {\r
- x = 0;\r
- \r
- /* First scan line */\r
- \r
- for(n = 0; n < total_bytes; n++)\r
- {\r
- /* Read byte */\r
- _read(fp,&data,1);\r
- \r
- count = data & 192;\r
- \r
- if (count == 192)\r
- {\r
- count = data & 63;\r
- n += count - 1;\r
- _read(fp,&data,1);\r
- while(count)\r
- {\r
- DISPLAY(data);\r
- count--;\r
- }\r
- }\r
- else\r
- DISPLAY(data);\r
- \r
- }\r
- x = 0;\r
- y++;\r
- }\r
- }\r
- \r
-\r
-Drawing Circles\r
-\r
-\r
-What has drawing circles got to do with advanced C programming? Well\r
-quite a lot, it is a task which is often desired by modern programmers,\r
-and it is a task which can be attacked from a number of angles. This\r
-example illustrates some of the ideas already discussed for replacing\r
-floating point numbers with integers, and using lookup tables rather than\r
-repeat calls to maths functions.\r
-\r
-A circle may be drawn by plotting each point on its circumference. The\r
-location of any point is given by;\r
-\r
-\r
- Xp = Xo + Sine(Angle) * Radius\r
- Yp = Yo + Cosine(Angle) * Radius\r
-\r
-Where Xp,Yp is the point to be plotted, and Xo,Yo is the centre of the\r
-circle.\r
-\r
-Thus, the simplest way to draw a circle is to calculate Xp and Yp for\r
-each angle between 1 and 360 degrees and to plot these points. There is\r
-however one fundamental error with this. As the radius of the circle\r
-increases, so also does the length of the arc between each angular point.\r
-Thus a circle of sufficient radius will be drawn with a dotted line\r
-rather than a solid line.\r
-\r
-The problem of the distance between the angular points may be tackled in\r
-two ways;\r
-\r
- 1)The number of points to be plotted can be increased, to say every 30\r
- minutes.\r
- 2)A straight line may be drawn between each angular point.\r
-\r
-The simplest circle drawing pseudo-code may then look like this;\r
-\r
-\r
- FOR angle = 1 TO 360\r
- PLOT Xo + SINE(angle) * radius, Yo + COSINE(angle) * radius\r
- NEXT angle\r
-\r
-This code has two major disadvantages;\r
-\r
- 1) It uses REAL numbers for the sine and cosine figures\r
- 2) It makes numerous calculations of sine and cosine values\r
-\r
-Both of these disadvantages result in a very slow piece of code. Since a\r
-circle is a regular figure with two axis of symmetry, one in both the X\r
-and Y axis, one only needs to calculate the relative offsets of points in\r
-one quadrant of the circle and then these offsets may be applied to the\r
-other three quadrants to produce a faster piece of code. Faster because\r
-the slow sine and cosine calculations are only done 90 times instead of\r
-360 times;\r
-\r
-\r
- FOR angle = 1 TO 90\r
- Xa = SINE(angle) * radius\r
- Ya = COSINE(angle) * radius\r
- PLOT Xo + Xa, Yo + Ya\r
- PLOT Xo + Xa, Yo - Ya\r
- PLOT Xo - Xa, Yo + Ya\r
- PLOT Xo - Xa, Yo - Ya\r
- NEXT angle\r
-\r
-A further enhancement may be made by making use of sine and cosine lookup\r
-tables instead of calculating them. This means calculating the sine and\r
-cosine values for each required angle and storing them in a table. Then,\r
-instead of calculating the values for each angle the circle drawing code\r
-need only retrieve the values from a table;\r
-\r
-\r
- FOR angle = 1 TO 90\r
- Xa = SINE[angle] * radius\r
- Ya = COSINE[angle] * radius\r
- PLOT Xo + Xa, Yo + Ya\r
- PLOT Xo + Xa, Yo - Ya\r
- PLOT Xo - Xa, Yo + Ya\r
- PLOT Xo - Xa, Yo - Ya\r
- NEXT angle\r
-\r
-\r
-Most computer languages work in RADIANS rather than DEGREES. There being\r
-approximately 57 degrees in one radian, 2 * PI radians in one circle.\r
-This implies that to calculate sine and cosine values of sufficient\r
-points to draw a reasonable circle using radians one must again use real\r
-numbers, that is numbers which have figures following a decimal point.\r
-Real number arithmetic, also known as floating point arithmetic, is\r
-horrendously slow to calculate. Integer arithmetic on the other hand is\r
-very quick to calculate, but less precise.\r
-\r
-To use integer arithmetic in circle drawing code requires ingenuity. If\r
-one agrees to use sine and cosine lookup tables for degrees, rather than\r
-radians. Then the sine value of an angle of 1 degree is;\r
-\r
- 0.0175\r
-\r
-Which, truncated to an integer becomes zero! To overcome this the sine\r
-and cosine values held in the table should be multiplied by some factor,\r
-say 10000. Then, the integer value of the sine of an angle of 1 degree\r
-becomes;\r
-\r
- 175\r
-\r
-If the sine and cosine values have been multiplied by a factor, then when\r
-the calculation of the point's offset is carried out one must remember to\r
-divide the result by the same factor. Thus the calculation becomes;\r
-\r
- Xa = SINE[angle] * radius / factor\r
- Ya = COSINE[angle] * radius / factor\r
-\r
-The final obstacle to drawing circles on a computer is the relationship\r
-between the width of the display screen and its height. This ratio\r
-between width and height is known as the "aspect ratio" and varies upon\r
-video display mode. The IBM VGA 256 colour mode for example can display\r
-320 pixels across and 200 up the screen. This equates to an aspect ratio\r
-of 1:1.3. If the circle drawing code ignores the aspect ratio, then the\r
-shape displayed will often be ovalar to a greater or lesser degree due to\r
-the rectangular shape of the display pixels. Thus in order to display a\r
-true circle, the formulae to calculate each point on the circumference\r
-must be amended to calculate a slight ellipse in compensation of the\r
-distorting factor of the display.\r
-\r
-The offset formulae then become;\r
-\r
- Xa = SINE[angle] * radius / factor\r
- Ya = COSINE[angle] * radius / (factor * aspect ratio)\r
-\r
-The following short C program illustrates a practical circle drawing code\r
-segment, in a demonstrable form;\r
-\r
-\r
- /* Circles.c A demonstration circle drawing program for the IBM PC\r
- */\r
- \r
- \r
- #include <stdlib.h>\r
- \r
- int sintable[91] = {0,175,349,523,698,\r
- 872,1045,1219,1392,\r
- 1564,1736,1908,2079,\r
- 2250,2419,2588,2756,\r
- 2924,3090,3256,3420,\r
- 3584,3746,3907,4067,\r
- 4226,4384,4540,4695,\r
- 4848,5000,5150,5299,\r
- 5446,5592,5736,5878,\r
- 6018,6157,6293,6428,\r
- 6561,6691,6820,6947,\r
- 7071,7193,7314,7431,\r
- 7547,7660,7771,7880,\r
- 7986,8090,8192,8290,\r
- 8387,8480,8572,8660,\r
- 8746,8829,8910,8988,\r
- 9063,9135,9205,9272,\r
- 9336,9397,9455,9511,\r
- 9563,9613,9659,9703,\r
- 9744,9781,9816,9848,\r
- 9877,9903,9925,9945,\r
- 9962,9976,9986,9994,\r
- 9998,10000\r
- };\r
- \r
- int costable[91] = { 10000,9998,9994,9986,9976,\r
- 9962,9945,9925,9903,\r
- 9877,9848,9816,9781,\r
- 9744,9703,9659,9613,\r
- 9563,9511,9455,9397,\r
- 9336,9272,9205,9135,\r
- 9063,8988,8910,8829,\r
- 8746,8660,8572,8480,\r
- 8387,8290,8192,8090,\r
- 7986,7880,7771,7660,\r
- 7547,7431,7314,7193,\r
- 7071,6947,6820,6691,\r
- 6561,6428,6293,6157,\r
- 6018,5878,5736,5592,\r
- 5446,5299,5150,5000,\r
- 4848,4695,4540,4384,\r
- 4226,4067,3907,3746,\r
- 3584,3420,3256,3090,\r
- 2924,2756,2588,2419,\r
- 2250,2079,1908,1736,\r
- 1564,1392,1219,1045,\r
- 872,698,523,349,\r
- 175,0\r
- };\r
- \r
- void setvideo(unsigned char mode)\r
- {\r
- /* Sets the video display mode for an IBM PC */\r
- \r
- asm mov al , mode;\r
- asm mov ah , 00;\r
- asm int 10h;\r
- }\r
- \r
- void plot(int x, int y, unsigned char colour)\r
- {\r
- /* Code for IBM PC BIOS ROM */\r
- /* Sets a pixel at the specified coordinates to a specified\r
- colour */\r
- \r
- /* Return if out of range */\r
- if (x < 0 || y < 0 || x > 320 || y > 200)\r
- return;\r
- \r
- asm mov al , colour;\r
- asm mov bh , 0;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah, 0Ch;\r
- asm int 10h;\r
- }\r
- \r
- void Line(int a, int b, int c, int d, int col)\r
- {\r
- /* Draws a straight line from point a,b to point c,d in colour\r
- col */\r
- \r
- int u;\r
- int v;\r
- int d1x;\r
- int d1y;\r
- int d2x;\r
- int d2y;\r
- int m;\r
- int n;\r
- double s; /* The only real number variable, but it's essential\r
- */\r
- int i;\r
- \r
- u = c - a;\r
- v = d - b;\r
- if (u == 0)\r
- {\r
- d1x = 0;\r
- m = 0;\r
- }\r
- else\r
- {\r
- m = abs(u);\r
- if (u < 0)\r
- d1x = -1;\r
- else\r
- if (u > 0)\r
- d1x = 1;\r
- }\r
- \r
- if ( v == 0)\r
- {\r
- d1y = 0;\r
- n = 0;\r
- }\r
- else\r
- {\r
- n = abs(v);\r
- if (v < 0)\r
- d1y = -1;\r
- else\r
- if (v > 0)\r
- d1y = 1;\r
- }\r
- if (m > n)\r
- {\r
- d2x = d1x;\r
- d2y = 0;\r
- }\r
- else\r
- {\r
- d2x = 0;\r
- d2y = d1y;\r
- m = n;\r
- n = abs(u);\r
- }\r
- s = m / 2;\r
- \r
- for (i = 0; i <= m; i++)\r
- {\r
- plot(a,b,col);\r
- s += n;\r
- if (s >= m)\r
- {\r
- s -= m;\r
- a += d1x;\r
- b += d1y;\r
- }\r
- else\r
- {\r
- a += d2x;\r
- b += d2y;\r
- }\r
- }\r
- }\r
- \r
- \r
- void Circle(int x, int y, int rad, int col)\r
- {\r
- /* Draws a circle about origin x,y */\r
- /* With a radius of rad */\r
- /* The col parameter defines the colour for plotting */\r
- \r
- int f;\r
- long xa;\r
- long ya;\r
- int a1;\r
- int b1;\r
- int a2;\r
- int b2;\r
- int a3;\r
- int b3;\r
- int a4;\r
- int b4;\r
- \r
- \r
- /* Calculate first point in each segment */\r
- \r
- a1 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
- b1 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
- \r
- a2 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
- b2 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
- \r
- a3 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
- b3 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
- \r
- a4 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;\r
- b4 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;\r
- \r
- /* Start loop at second point */\r
- for(f = 1; f <= 90; f++)\r
- {\r
- /* Calculate offset to new point */\r
- xa = ((long)(costable[f]) * (long)(rad) + 5000) / 10000;\r
- ya = ((long)(sintable[f]) * (long)(rad) + 5000) / 13000;\r
- \r
- /* Draw a line from the previous point to the new point in\r
- each segment */\r
- Line(a1,b1,x + xa, y + ya,col);\r
- Line(a2,b2,x - xa, y + ya,col);\r
- Line(a3,b3,x - xa, y - ya,col);\r
- Line(a4,b4,x + xa, y - ya,col);\r
- \r
- /* Update the previous point in each segment */\r
- a1 = x + xa;\r
- b1 = y + ya;\r
- a2 = x - xa;\r
- b2 = y + ya;\r
- a3 = x - xa;\r
- b3 = y - ya;\r
- a4 = x + xa;\r
- b4 = y - ya;\r
- }\r
- }\r
- \r
- main()\r
- {\r
- int n;\r
- \r
- /* Select VGA 256 colour 320 x 200 video mode */\r
- setvideo(19);\r
- \r
- /* Draw some circles */\r
- for(n = 0; n < 100; n++)\r
- Circle(160,100,n,n + 20);\r
- }\r
- \r
- \r
-\r
-Vesa Mode\r
-\r
-The VESA BIOS provides a number of new, and exciting video modes not\r
-supported by the standard BIOS. These modes vary from one video card to\r
-another, but most support the following modes:\r
-\r
-Mode Display\r
- \r
-0x54 Text 16 colours 132 x 43\r
-0x55 Text 16 colours 132 x 25\r
-0x58 Graphics 16 colours 800 x 600\r
-0x5C Graphics 256 colours 800 x 600\r
-0x5D Graphics 16 colours 1024 x 768\r
-0x5F Graphics 256 colours 640 x 480\r
-0x60 Graphics 256 colours 1024 x 768\r
-0x64 Graphics 64k colours 640 x 480\r
-0x65 Graphics 64k colours 800 x 600\r
-0x6A Graphics 16 colours 800 x 600\r
-0x6C Graphics 16 colours 1280 x 1024\r
-0x70 Graphics 16m colours 320 x 200\r
-0x71 Graphics 16m colours 640 x 480\r
-\r
-These modes are in addition to the standard BIOS video modes described\r
-earlier.\r
-\r
-Setting a VESA video mode requires a call to a different BIOS function\r
-than the standard BIOS, as illustrated in the following example which\r
-enables any VESA or standard display mode to be selected from the DOS\r
-command line.\r
-\r
- #include <dos.h>\r
- #include <ctype.h>\r
- \r
- void setvideo(int mode)\r
- {\r
- /* Sets the video display to a VESA or normal mode and clears\r
- the screen */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 0x4f;\r
- inreg.h.al = 0x02;\r
- inreg.x.bx = mode;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- main(int argc, char *argv[])\r
- {\r
- setvideo(atoi(argv[1]));\r
- }\r
-\r
-\r
-Plotting pixels in a VESA mode graphics display can be acgieved with the\r
-standard BIOS plot functiona call, as illustrated here;\r
-\r
- \r
- void plot(int x, int y, unsigned char colour)\r
- {\r
- asm mov al , colour;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
-\r
-\r
-Or, in a 800 x 600 resolution mode you can use this fast direct video\r
-access plot function;\r
-\r
- void plot( int x, int y, unsigned char pcolor)\r
- {\r
- /*\r
- Fast 800 x 600 mode (0x58 or 0x102) plotting\r
- */\r
- \r
- asm mov ax,y;\r
- asm mov dx,800/8;\r
- asm mul dx;\r
- asm mov bx,x;\r
- asm mov cl,bl;\r
- \r
- asm shr bx,1;\r
- asm shr bx,1;\r
- asm shr bx,1;\r
- asm add bx,ax;\r
- \r
- asm and cl,7;\r
- asm xor cl,7;\r
- asm mov ah,1;\r
- asm shl ah,cl;\r
- \r
- asm mov dx,03CEh;\r
- asm mov al,8;\r
- asm out dx,ax;\r
- \r
- asm mov ax,(02h shl 8) + 5;\r
- asm out dx,ax;\r
- \r
- asm mov ax,0A000h;\r
- asm mov es,ax;\r
- \r
- asm mov al,es:[bx];\r
- asm mov al,byte ptr pcolor;\r
- asm mov es:[bx],al;\r
- \r
- asm mov ax,(0FFh shl 8 ) + 8;\r
- asm out dx,ax;\r
- \r
- asm mov ax,(00h shl 8) + 5;\r
- asm out dx,ax;\r
- }\r
-\r
-There are lots more functions supported by the VESA BIOS, but this will\r
-get you going with the basic operations. Remember though that when using\r
-VESA display modes, that the direct console I/O functions in the C\r
-compiler library may not function correctly.\r
- \r
- DIRECTORY SEARCHING WITH THE IBM PC AND DOS\r
-\r
-Amongst the many functions provided by DOS for programmers, are a pair of\r
-functions; "Find first" and "Find next" which are used to search a DOS\r
-directory for a specified file name or names. The first function, "Find\r
-first" is accessed via DOS interrupt 21, function 4E. It takes an ascii\r
-string file specification, which can include wildcards, and the required\r
-attribute for files to match. Upon return the function fills the disk\r
-transfer area (DTA) with details of the located file and returns with the\r
-carry flag clear. If an error occurs, such as no matching files are\r
-located, the function returns with the carry flag set.\r
-\r
-Following a successful call to "Find first", a program can call "Find\r
-next", DOS interrupt 21, function 4F, to locate the next file matching\r
-the specifications provided by the initial call to "Find first". If this\r
-function succeeds, then the DTA is filled in with details of the next\r
-matching file, and the function returns with the carry flag clear.\r
-Otherwise a return is made with the carry flag set.\r
-\r
-Most C compilers for the IBM PC provide non standard library functions\r
-for accessing these two functions. Turbo C provides "findfirst()" and\r
-"findnext()". Making use of the supplied library functions shields the\r
-programmer from the messy task of worrying about the DTA. Microsoft C\r
-programmers should substitue findfirst() with _dos_findfirst() and\r
-findnext() with _dos_findnext().\r
-\r
-The following Turbo C example imitates the DOS directory command, in a\r
-basic form;\r
-\r
- #include <stdio.h>\r
- #include <dir.h>\r
- #include <dos.h>\r
- \r
- void main(void)\r
- {\r
- /* Display directory listing of current directory */\r
- \r
- int done;\r
- int day;\r
- int month;\r
- int year;\r
- int hour;\r
- int min;\r
- char amflag;\r
- struct ffblk ffblk;\r
- struct fcb fcb;\r
- \r
- /* First display sub directory entries */\r
- done = findfirst("*.",&ffblk,16);\r
- \r
- while (!done)\r
- {\r
- year = (ffblk.ff_fdate >> 9) + 80;\r
- month = (ffblk.ff_fdate >> 5) & 0x0f;\r
- day = ffblk.ff_fdate & 0x1f;\r
- hour = (ffblk.ff_ftime >> 11);\r
- min = (ffblk.ff_ftime >> 5) & 63;\r
- \r
- amflag = 'a';\r
- \r
- if (hour > 12)\r
- {\r
- hour -= 12;\r
- amflag = 'p';\r
- }\r
- \r
- printf("%-11.11s <DIR> %02d-%02d-%02d %2d:%02d%c\n",\r
- ffblk.ff_name,day,month,year,hour,min,amflag);\r
- done = findnext(&ffblk);\r
- }\r
- \r
- /* Now all files except directories */\r
- done = findfirst("*.*",&ffblk,231);\r
- \r
- while (!done)\r
- {\r
- year = (ffblk.ff_fdate >> 9) + 80;\r
- month = (ffblk.ff_fdate >> 5) & 0x0f;\r
- day = ffblk.ff_fdate & 0x1f;\r
- hour = (ffblk.ff_ftime >> 11);\r
- min = (ffblk.ff_ftime >> 5) & 63;\r
- \r
- amflag = 'a';\r
- \r
- if (hour > 12)\r
- {\r
- hour -= 12;\r
- amflag = 'p';\r
- }\r
- \r
- parsfnm(ffblk.ff_name,&fcb,1);\r
- \r
- printf("%-8.8s %-3.3s %8ld %02d-%02d-%02d %2d:%02d%c\n",\r
- fcb.fcb_name,fcb.fcb_ext,ffblk.ff_fsize,\r
- day,month,year,hour,min,amflag);\r
- done = findnext(&ffblk);\r
- }\r
- }\r
- \r
-\r
-The function "parsfnm()" is a Turbo C library command which makes use of\r
-the DOS function for parsing an ascii string containing a file name, into\r
-its component parts. These component parts are then put into a DOS file\r
-control block (fcb), from where they may be easily retrieved for\r
-displaying by printf().\r
-\r
-\r
-The DOS DTA is comprised as follows;\r
-\r
-Offset Length Contents\r
- \r
-00 15 Reserved\r
-15 Byte Attribute of matched\r
- file\r
-16 Word File time\r
-18 Word File date\r
-1A 04 File size\r
-1E 0D File name and extension\r
- as ascii string\r
-\r
-The file time word contains the time at which the file was last written\r
-to disc and is comprised as follows;\r
-\r
-Bits Contents\r
-\r
- 0 - 4 Seconds divided by 2\r
- 5 - 10 Minutes\r
-11 - 15 Hours\r
-\r
-The file date word holds the date on which the file was last written to\r
-disc and is comprised of;\r
-\r
-Bits Contents\r
-\r
- 0 - 4 Day\r
- 5 - 8 Month\r
- 9 - 15 Years since 1980\r
-\r
-To extract these details from the DTA requires a little manipulation, as\r
-illustrated in the above example.\r
-\r
-The DTA attribute flag is comprised of the following bits being set or\r
-not;\r
-\r
-Bit Attribute\r
-\r
-0 Read only\r
-1 Hidden\r
-2 System\r
-3 Volume label\r
-4 Directory\r
-5 Archive\r
- \r
- ACCESSING EXPANDED MEMORY\r
-\r
-Memory (RAM) in an IBM PC comes in three flavours; Conventional, Expanded\r
-and Extended. Conventional memory is the 640K of RAM which the operating\r
-system DOS can access. This memory is normally used. However, it is often\r
-insufficient for todays RAM hungry systems. Expanded memory is RAM which\r
-is addressed outside of the area of conventional RAM not by DOS but by a\r
-second program called a LIM EMS driver. Access to this device driver is\r
-made through interrupt 67h.\r
-\r
-The main problem with accessing expanded memory is that no matter how\r
-much expanded memory is fitted to the computer, it can only be accessed\r
-through 16K blocks refered to as pages. So for example. If you have 2mB\r
-of expanded RAM fitted to your PC then that is comprised of 128 pages\r
-(128 * 16K = 2mB).\r
-\r
-A program can determine whether a LIM EMS driver is installed by\r
-attempting to open the file `EMMXXXX0' which is guarranteed by the LIM\r
-standard to be present as an IOCTL device when the device driver is\r
-active.\r
-\r
-The following source code illustrates some basic functions for testing\r
-for and accessing expanded memory.\r
-\r
- /*\r
- Various functions for using Expanded memory\r
- */\r
- \r
- #include <dos.h>\r
- #define EMM 0x67\r
- \r
- char far *emmbase;\r
- emmtest()\r
- {\r
- /*\r
- Tests for the presence of expnaded memory by attempting to\r
- open the file EMMXXXX0.\r
- */\r
- \r
- union REGS regs;\r
- struct SREGS sregs;\r
- int error;\r
- long handle;\r
- \r
- /* Attempt to open the file device EMMXXXX0 */\r
- regs.x.ax = 0x3d00;\r
- regs.x.dx = (int)"EMMXXXX0";\r
- sregs.ds = _DS;\r
- intdosx(®s,®s,&sregs);\r
- handle = regs.x.ax;\r
- error = regs.x.cflag;\r
- \r
- if (!error)\r
- {\r
- regs.h.ah = 0x3e;\r
- regs.x.bx = handle;\r
- intdos(®s,®s);\r
- }\r
- return error;\r
- }\r
- \r
- emmok()\r
- {\r
- /*\r
- Checks whether the expanded memory manager responds correctly\r
- */\r
- \r
- union REGS regs;\r
- \r
- regs.h.ah = 0x40;\r
- int86(EMM,®s,®s);\r
- \r
- if (regs.h.ah)\r
- return 0;\r
- \r
- regs.h.ah = 0x41;\r
- int86(EMM,®s,®s);\r
- \r
- if (regs.h.ah)\r
- return 0;\r
- \r
- emmbase = MK_FP(regs.x.bx,0);\r
- return 1;\r
- }\r
- \r
- long emmavail()\r
- {\r
- /*\r
- Returns the number of available (free) 16K pages of expanded\r
- memory\r
- or -1 if an error occurs.\r
- */\r
- \r
- union REGS regs;\r
- \r
- regs.h.ah = 0x42;\r
- int86(EMM,®s,®s);\r
- if (!regs.h.ah)\r
- return regs.x.bx;\r
- return -1;\r
- }\r
- \r
- long emmalloc(int n)\r
- {\r
- /*\r
- Requests 'n' pages of expanded memory and returns the file\r
- handle\r
- assigned to the pages or -1 if there is an error\r
- */\r
- \r
- union REGS regs;\r
- \r
- regs.h.ah = 0x43;\r
- regs.x.bx = n;\r
- int86(EMM,®s,®s);\r
- if (regs.h.ah)\r
- return -1;\r
- return regs.x.dx;\r
- }\r
- \r
- emmmap(long handle, int phys, int page)\r
- {\r
- /*\r
- Maps a physical page from expanded memory into the page frame\r
- in the\r
- conventional memory 16K window so that data can be transfered\r
- between\r
- the expanded memory and conventional memory.\r
- */\r
- \r
- union REGS regs;\r
- \r
- regs.h.ah = 0x44;\r
- regs.h.al = page;\r
- regs.x.bx = phys;\r
- regs.x.dx = handle;\r
- int86(EMM,®s,®s);\r
- return (regs.h.ah == 0);\r
- }\r
- \r
- void emmmove(int page, char *str, int n)\r
- {\r
- /*\r
- Move 'n' bytes from conventional memory to the specified\r
- expanded memory\r
- page\r
- */\r
- \r
- char far *ptr;\r
- \r
- ptr = emmbase + page * 16384;\r
- while(n-- > 0)\r
- *ptr++ = *str++;\r
- }\r
- \r
- void emmget(int page, char *str, int n)\r
- {\r
- /*\r
- Move 'n' bytes from the specified expanded memory page into\r
- conventional\r
- memory\r
- */\r
- \r
- char far *ptr;\r
- \r
- ptr = emmbase + page * 16384;\r
- while(n-- > 0)\r
- *str++ = *ptr++;\r
- }\r
- \r
- emmclose(long handle)\r
- {\r
- /*\r
- Release control of the expanded memory pages allocated to\r
- 'handle'\r
- */\r
- \r
- union REGS regs;\r
- \r
- regs.h.ah = 0x45;\r
- regs.x.dx = handle;\r
- int86(EMM,®s,®s);\r
- return (regs.h.ah == 0);\r
- }\r
- \r
- /*\r
- Test function for the EMM routines\r
- */\r
- \r
- void main()\r
- {\r
- long emmhandle;\r
- long avail;\r
- char teststr[80];\r
- int i;\r
- \r
- if(!emmtest())\r
- {\r
- printf("Expanded memory is not present\n");\r
- exit(0);\r
- }\r
- \r
- if(!emmok())\r
- {\r
- printf("Expanded memory manager is not present\n");\r
- exit(0);\r
- }\r
- \r
- avail = emmavail();\r
- if (avail == -1)\r
- {\r
- printf("Expanded memory manager error\n");\r
- exit(0);\r
- }\r
- printf("There are %ld pages available\n",avail);\r
- \r
- /* Request 10 pages of expanded memory */\r
- if((emmhandle = emmalloc(10)) < 0)\r
- {\r
- printf("Insufficient pages available\n");\r
- exit(0);\r
- }\r
- \r
- for (i = 0; i < 10; i++)\r
- {\r
- sprintf(teststr,"%02d This is a test string\n",i);\r
- emmmap(emmhandle,i,0);\r
- emmmove(0,teststr,strlen(teststr) + 1);\r
- }\r
- \r
- for (i = 0; i < 10; i++)\r
- {\r
- emmmap(emmhandle,i,0);\r
- emmget(0,teststr,strlen(teststr) + 1);\r
- printf("READING BLOCK %d: %s\n",i,teststr);\r
- }\r
- \r
- emmclose(emmhandle);\r
- }\r
- \r
- ACCESSING EXTENDED MEMORY\r
-\r
-\r
-Extended memory has all but taken over from Expanded Memory now (1996).\r
-It is faster and more useable than expanded memory. As with Expanded\r
-memory, Extended memory cannot be directly accessed through the standard\r
-DOS mode, and so a transfer buffer in conventional or "real-mode" memory\r
-needs to be used. The process to write data to Extended memory then\r
-involves copying the data to the transfer buffer in conventional memory,\r
-and from there copying it to Extended memory.\r
-\r
-Before any use may be made of Extended memory, a program should test to\r
-see if Extended memory is available. The following function, XMS_init(),\r
-tests for the presence of Extended memory, and if available calls another\r
-function, GetXMSEntry() to initialise the program for using Extended\r
-Memory. The function also allocates a conventional memory transfer\r
-buffer.\r
-\r
-\r
- \r
- /*\r
- BLOCKSIZE will be the size of our real-memory buffer that\r
- we'll swap XMS through (must be a multiple of 1024, since\r
- XMS is allocated in 1K chunks.)\r
- */\r
- \r
- #ifdef __SMALL__\r
- #define BLOCKSIZE (16L * 1024L)\r
- #endif\r
- \r
- \r
- #ifdef __MEDIUM__\r
- #define BLOCKSIZE (16L * 1024L)\r
- #endif\r
- \r
- \r
- #ifdef __COMPACT__\r
- #define BLOCKSIZE (64L * 1024L)\r
- #endif\r
- \r
- #ifdef __LARGE__\r
- #define BLOCKSIZE (64L * 1024L)\r
- #endif\r
- \r
- \r
- char XMS_init()\r
- {\r
- /*\r
- returns 0 if XMS present,\r
- 1 if XMS absent\r
- 2 if unable to allocate conventional memory transfer\r
- buffer\r
- */\r
- unsigned char status;\r
- _AX=0x4300;\r
- geninterrupt(0x2F);\r
- status = _AL;\r
- if(status==0x80)\r
- {\r
- GetXMSEntry();\r
- XMSBuf = (char far *) farmalloc(BLOCKSIZE);\r
- if (XMSBuf == NULL)\r
- return 2;\r
- return 0;\r
- }\r
- return 1;\r
- }\r
- \r
- void GetXMSEntry(void)\r
- {\r
- /*\r
- GetXMSEntry sets XMSFunc to the XMS Manager entry point\r
- so we can call it later\r
- */\r
- \r
- _AX=0x4310;\r
- geninterrupt(0x2F);\r
- XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);\r
- }\r
-\r
-\r
-Once the presence of Extended memory has been confirmed, a program can\r
-find out how much Extended memory is available;\r
-\r
- void XMSSize(int *kbAvail, int *largestAvail)\r
- {\r
- /*\r
- XMSSize returns the total kilobytes available, and the\r
- size\r
- in kilobytes of the largest available block\r
- */\r
- \r
- _AH=8;\r
- (*XMSFunc)();\r
- *largestAvail=_DX;\r
- *kbAvail=_AX;\r
- }\r
-\r
-\r
-The following function may be called to allocate a block of Extended\r
-memory, like you would allocate a block of conventional memory.\r
-\r
- char AllocXMS(unsigned long numberBytes)\r
- {\r
- /*\r
- Allocate a block of XMS memory numberBytes long\r
- Returns 1 on success\r
- 0 on failure\r
- */\r
- \r
- _DX = (int)(numberBytes / 1024);\r
- _AH = 9;\r
- (*XMSFunc)();\r
- if (_AX==0)\r
- {\r
- return 0;\r
- }\r
- XMSHandle=_DX;\r
- return 1;\r
- }\r
- \r
-\r
-Allocated Extended memory is not freed by DOS. A program using Extended\r
-memory must release it before terminating. This function frees a block of\r
-extended memory previously allocated by AllocXMS. Note, XMSHandle is a\r
-global variable of type int.\r
-\r
- void XMS_free(void)\r
- {\r
- /*\r
- Free used XMS\r
- */\r
- _DX=XMSHandle;\r
- _AH=0x0A;\r
- (*XMSFunc)();\r
- }\r
-\r
-\r
-Two functions are now given. One for writing data to Extended memory, and\r
-one for reading data from Extended memory into conventional memory.\r
-\r
- /*\r
- XMSParms is a structure for copying information to and from\r
- real-mode memory to XMS memory\r
- */\r
- \r
- struct parmstruct\r
- {\r
- /*\r
- blocklength is the size in bytes of block to copy\r
- */\r
- unsigned long blockLength;\r
- \r
- /*\r
- sourceHandle is the XMS handle of source; 0 means that\r
- sourcePtr will be a 16:16 real-mode pointer, otherwise\r
- sourcePtr is a 32-bit offset from the beginning of the\r
- XMS area that sourceHandle points to\r
- */\r
- \r
- unsigned int sourceHandle;\r
- far void *sourcePtr;\r
- \r
- /*\r
- destHandle is the XMS handle of destination; 0 means that\r
- destPtr will be a 16:16 real-mode pointer, otherwise\r
- destPtr is a 32-bit offset from the beginning of the XMS\r
- area that destHandle points to\r
- */\r
- \r
- unsigned int destHandle;\r
- far void *destPtr;\r
- }\r
- XMSParms;\r
- \r
- \r
- char XMS_write(unsigned long loc, char far *val, unsigned length)\r
- {\r
- /*\r
- Round length up to next even value\r
- */\r
- length += length % 2;\r
- \r
- XMSParms.sourceHandle=0;\r
- XMSParms.sourcePtr=val;\r
- XMSParms.destHandle=XMSHandle;\r
- XMSParms.destPtr=(void far *) (loc);\r
- XMSParms.blockLength=length; /* Must be an even number! */\r
- _SI = FP_OFF(&XMSParms);\r
- _AH=0x0B;\r
- (*XMSFunc)();\r
- if (_AX==0)\r
- {\r
- return 0;\r
- }\r
- return 1;\r
- }\r
- \r
- \r
- void *XMS_read(unsigned long loc,unsigned length)\r
- {\r
- /*\r
- Returns pointer to data\r
- or NULL on error\r
- */\r
- \r
- /*\r
- Round length up to next even value\r
- */\r
- length += length % 2;\r
- \r
- XMSParms.sourceHandle=XMSHandle;\r
- XMSParms.sourcePtr=(void far *) (loc);\r
- XMSParms.destHandle=0;\r
- XMSParms.destPtr=XMSBuf;\r
- XMSParms.blockLength=length; /* Must be an even number */\r
- _SI=FP_OFF(&XMSParms);\r
- _AH=0x0B;\r
- (*XMSFunc)();\r
- if (_AX==0)\r
- {\r
- return NULL;\r
- }\r
- return XMSBuf;\r
- }\r
- \r
- \r
-And now putting it all together is a demonstration program.\r
-\r
- /* A sequential table of variable length records in XMS */\r
- \r
- #include <dos.h>\r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- #include <alloc.h>\r
- #include <string.h>\r
- \r
- #define TRUE 1\r
- #define FALSE 0\r
- \r
- /*\r
- BLOCKSIZE will be the size of our real-memory buffer that\r
- we'll swap XMS through (must be a multiple of 1024, since\r
- XMS is allocated in 1K chunks.)\r
- */\r
- \r
- #ifdef __SMALL__\r
- #define BLOCKSIZE (16L * 1024L)\r
- #endif\r
- \r
- \r
- #ifdef __MEDIUM__\r
- #define BLOCKSIZE (16L * 1024L)\r
- #endif\r
- \r
- \r
- #ifdef __COMPACT__\r
- #define BLOCKSIZE (64L * 1024L)\r
- #endif\r
- \r
- #ifdef __LARGE__\r
- #define BLOCKSIZE (64L * 1024L)\r
- #endif\r
- \r
- \r
- /*\r
- XMSParms is a structure for copying information to and from\r
- real-mode memory to XMS memory\r
- */\r
- \r
- struct parmstruct\r
- {\r
- /*\r
- blocklength is the size in bytes of block to copy\r
- */\r
- unsigned long blockLength;\r
- \r
- /*\r
- sourceHandle is the XMS handle of source; 0 means that\r
- sourcePtr will be a 16:16 real-mode pointer, otherwise\r
- sourcePtr is a 32-bit offset from the beginning of the\r
- XMS area that sourceHandle points to\r
- */\r
- \r
- unsigned int sourceHandle;\r
- far void *sourcePtr;\r
- \r
- /*\r
- destHandle is the XMS handle of destination; 0 means that\r
- destPtr will be a 16:16 real-mode pointer, otherwise\r
- destPtr is a 32-bit offset from the beginning of the XMS\r
- area that destHandle points to\r
- */\r
- \r
- unsigned int destHandle;\r
- far void *destPtr;\r
- }\r
- XMSParms;\r
- \r
- void far (*XMSFunc) (void); /* Used to call XMS manager\r
- (himem.sys) */\r
- char GetBuf(void);\r
- void GetXMSEntry(void);\r
- \r
- char *XMSBuf; /* Conventional memory buffer for transfers */\r
- \r
- unsigned int XMSHandle; /* handle to allocated XMS block */\r
- \r
- \r
- char XMS_init()\r
- {\r
- /*\r
- returns 0 if XMS present,\r
- 1 if XMS absent\r
- 2 if unable to allocate transfer buffer\r
- */\r
- unsigned char status;\r
- _AX=0x4300;\r
- geninterrupt(0x2F);\r
- status = _AL;\r
- if(status==0x80)\r
- {\r
- GetXMSEntry();\r
- XMSBuf = (char far *) farmalloc(BLOCKSIZE);\r
- if (XMSBuf == NULL)\r
- return 2;\r
- return 0;\r
- }\r
- return 1;\r
- }\r
- \r
- void GetXMSEntry(void)\r
- {\r
- /*\r
- GetXMSEntry sets XMSFunc to the XMS Manager entry point\r
- so we can call it later\r
- */\r
- \r
- _AX=0x4310;\r
- geninterrupt(0x2F);\r
- XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);\r
- }\r
- \r
- \r
- void XMSSize(int *kbAvail, int *largestAvail)\r
- {\r
- /*\r
- XMSSize returns the total kilobytes available, and the\r
- size\r
- in kilobytes of the largest available block\r
- */\r
- \r
- _AH=8;\r
- (*XMSFunc)();\r
- *largestAvail=_DX;\r
- *kbAvail=_AX;\r
- }\r
- \r
- char AllocXMS(unsigned long numberBytes)\r
- {\r
- /*\r
- Allocate a block of XMS memory numberBytes long\r
- */\r
- \r
- _DX = (int)(numberBytes / 1024);\r
- _AH = 9;\r
- (*XMSFunc)();\r
- if (_AX==0)\r
- {\r
- return FALSE;\r
- }\r
- XMSHandle=_DX;\r
- return TRUE;\r
- }\r
- \r
- void XMS_free(void)\r
- {\r
- /*\r
- Free used XMS\r
- */\r
- _DX=XMSHandle;\r
- _AH=0x0A;\r
- (*XMSFunc)();\r
- }\r
- \r
- char XMS_write(unsigned long loc, char far *val, unsigned length)\r
- {\r
- /*\r
- Round length up to next even value\r
- */\r
- length += length % 2;\r
- \r
- XMSParms.sourceHandle=0;\r
- XMSParms.sourcePtr=val;\r
- XMSParms.destHandle=XMSHandle;\r
- XMSParms.destPtr=(void far *) (loc);\r
- XMSParms.blockLength=length; /* Must be an even number! */\r
- _SI = FP_OFF(&XMSParms);\r
- _AH=0x0B;\r
- (*XMSFunc)();\r
- if (_AX==0)\r
- {\r
- return FALSE;\r
- }\r
- return TRUE;\r
- }\r
- \r
- \r
- void *XMS_read(unsigned long loc,unsigned length)\r
- {\r
- /*\r
- Returns pointer to data\r
- or NULL on error\r
- */\r
- \r
- /*\r
- Round length up to next even value\r
- */\r
- length += length % 2;\r
- \r
- XMSParms.sourceHandle=XMSHandle;\r
- XMSParms.sourcePtr=(void far *) (loc);\r
- XMSParms.destHandle=0;\r
- XMSParms.destPtr=XMSBuf;\r
- XMSParms.blockLength=length; /* Must be an even number */\r
- _SI=FP_OFF(&XMSParms);\r
- _AH=0x0B;\r
- (*XMSFunc)();\r
- if (_AX==0)\r
- {\r
- return NULL;\r
- }\r
- return XMSBuf;\r
- }\r
- \r
- \r
- /*\r
- Demonstration code\r
- Read various length strings into a single XMS block (EMB)\r
- and write them out again\r
- */\r
- \r
- int main()\r
- {\r
- int kbAvail,largestAvail;\r
- char buffer[80];\r
- char *p;\r
- long pos;\r
- long end;\r
- \r
- if (XMS_init() == 0)\r
- printf("XMS Available ...\n");\r
- else\r
- {\r
- printf("XMS Not Available\n");\r
- return(1);\r
- }\r
- \r
- XMSSize(&kbAvail,&largestAvail);\r
- printf("Kilobytes Available: %d; Largest block:\r
- %dK\n",kbAvail,largestAvail);\r
- \r
- if (!AllocXMS(2000 * 1024L))\r
- return(1);\r
- \r
- \r
- pos = 0;\r
- \r
- do\r
- {\r
- p = fgets(buffer,1000,stdin);\r
- if (p != NULL)\r
- {\r
- XMS_write(pos,buffer,strlen(buffer) + 1);\r
- pos += strlen(buffer) + 1;\r
- }\r
- }\r
- while(p != NULL);\r
- \r
- end = pos;\r
- \r
- pos = 0;\r
- \r
- do\r
- {\r
- memcpy(buffer,XMS_read(pos,100),70);\r
- printf("%s",buffer);\r
- pos += strlen(buffer) + 1;\r
- }\r
- while(pos < end);\r
- \r
- /*\r
- It is VERY important to free any XMS before exiting!\r
- */\r
- XMS_free();\r
- return 0;\r
- }\r
-\r
- \r
- \r
- \r
- TSR PROGRAMMING\r
-\r
-\r
-Programs which remain running and resident in memory while other programs\r
-are running are the most exciting line of programming for many PC\r
-developers. This type of program is known as a "Terminate and Stay\r
-Resident" or "TSR" program and they are very difficult to program\r
-sucessfuly.\r
-\r
-The difficulties in programming TSRs comes from the limitations of DOS\r
-which is not a multi-tasking operating system, and does not react well to\r
-re-enterant code. That is it's own functions (interrupts) calling\r
-themselves.\r
-\r
-In theory a TSR is quite simple. It is an ordinary program which\r
-terminates not through the usual DOS terminate function, but through the\r
-DOS "keep" function - interrupt 27h. This function reserves an area of\r
-memory, used by the program so that no other programs will overwrite it.\r
-This in itself is not a very difficult task, excepting that the program\r
-needs to tell DOS how much memory to leave it!\r
-\r
-The problems stem mainly from not being able to use DOS function calls\r
-within the TSR program once it has "gone resident".\r
-\r
-There are a few basic rules which help to clarify the problems\r
-encountered in programming TSRs:\r
-\r
- 1.Avoid DOS function calls\r
- 2.Monitor the DOS busy flag, when this flag is nonzero, DOS is\r
- executing an interrupt 21h function and MUST NOT be disturbed!\r
- 3.Monitor interrupt 28h. This reveals when DOS is busy waiting for\r
- console input. At this time you can disturb DOS regardless of the\r
- DOS busy flag setting.\r
- 4.Provide some way of checking whether the TSR is already loaded to\r
- prevent multiple copies occuring in memory.\r
- 5.Remember that other TSR programs may be chained to interrupts, and\r
- so you must chain any interrupt vectors that your program needs.\r
- 6.Your TSR program must use its own stack, and NOT that of the running\r
- process.\r
- 7.TSR programs must be compiled in a small memory model with stack\r
- checking turned off.\r
- 8.When control passes to your TSR program, it must tell DOS that the\r
- active process has changed.\r
-\r
-\r
-The following three source code modules describe a complete TSR program.\r
-This is a useful pop-up address book database which can be activated\r
-while any other program is running by pressing the key combination `Alt'\r
-and `.'. If the address book does not respond to the key press, it is\r
-probably because DOS cannot be disturbed, and you should try to pop-it-up\r
-again.\r
-\r
-\r
- /*\r
- A practical TSR program (a pop-up address book database)\r
- Compile in small memory model with stack checking OFF\r
- */\r
- \r
- #include <dos.h>\r
- #include <stdio.h>\r
- #include <string.h>\r
- #include <dir.h>\r
- \r
- static union REGS rg;\r
- \r
- /*\r
- Size of the program to remain resident\r
- experimentation is required to make this as small as possible\r
- */\r
- unsigned sizeprogram = 28000/16;\r
- \r
- /* Activate with Alt . */\r
- unsigned scancode = 52; /* . */\r
- unsigned keymask = 8; /* ALT */\r
- \r
- char signature[]= "POPADDR";\r
- char fpath[40];\r
- \r
- /*\r
- Function prototypes\r
- */\r
- \r
- void curr_cursor(int *x, int *y);\r
- int resident(char *, void interrupt(*)());\r
- void resinit(void);\r
- void terminate(void);\r
- void restart(void);\r
- void wait(void);\r
- void resident_psp(void);\r
- void exec(void);\r
- \r
- /*\r
- Entry point from DOS\r
- */\r
- \r
- main(int argc, char *argv[])\r
- {\r
- void interrupt ifunc();\r
- int ivec;\r
- \r
- /*\r
- For simplicity, assume the data file is in the root\r
- directory\r
- of drive C:\r
- */\r
- strcpy(fpath,"C:\\ADDRESS.DAT");\r
- \r
- if ((ivec = resident(signature,ifunc)) != 0)\r
- {\r
- /* TSR is resident */\r
- if (argc > 1)\r
- {\r
- rg.x.ax = 0;\r
- if (strcmp(argv[1],"quit") == 0)\r
- rg.x.ax = 1;\r
- else if (strcmp(argv[1],"restart") == 0)\r
- rg.x.ax = 2;\r
- else if (strcmp(argv[1],"wait") == 0)\r
- rg.x.ax = 3;\r
- if (rg.x.ax)\r
- {\r
- int86(ivec,&rg,&rg);\r
- return;\r
- }\r
- }\r
- printf("\nPopup Address Book is already resident");\r
- }\r
- else\r
- {\r
- /* Initial load of TSR program */\r
- printf("Popup Address Book Resident.\nPress Alt . To\r
- Activate....\n");\r
- resinit();\r
- }\r
- }\r
- \r
- void interrupt ifunc(bp,di,si,ds,es,dx,cx,bx,ax)\r
- {\r
- if(ax == 1)\r
- terminate();\r
- else if(ax == 2)\r
- restart();\r
- else if(ax == 3)\r
- wait();\r
- }\r
- \r
- popup()\r
- {\r
- int x,y;\r
- \r
- curr_cursor(&x,&y);\r
- \r
- /* Call the TSR C program here */\r
- exec();\r
- cursor(x,y);\r
- }\r
- \r
- /*\r
- Second source module\r
- */\r
- \r
- #include <dos.h>\r
- #include <stdio.h>\r
- \r
- static union REGS rg;\r
- static struct SREGS seg;\r
- static unsigned mcbseg;\r
- static unsigned dosseg;\r
- static unsigned dosbusy;\r
- static unsigned enddos;\r
- char far *intdta;\r
- static unsigned intsp;\r
- static unsigned intss;\r
- static char far *mydta;\r
- static unsigned myss;\r
- static unsigned stack;\r
- static unsigned ctrl_break;\r
- static unsigned mypsp;\r
- static unsigned intpsp;\r
- static unsigned pids[2];\r
- static int pidctr = 0;\r
- static int pp;\r
- static void interrupt (*oldtimer)();\r
- static void interrupt (*old28)();\r
- static void interrupt (*oldkb)();\r
- static void interrupt (*olddisk)();\r
- static void interrupt (*oldcrit)();\r
- \r
- void interrupt newtimer();\r
- void interrupt new28();\r
- void interrupt newkb();\r
- void interrupt newdisk();\r
- void interrupt newcrit();\r
- \r
- extern unsigned sizeprogram;\r
- extern unsigned scancode;\r
- extern unsigned keymask;\r
- \r
- static int resoff = 0;\r
- static int running = 0;\r
- static int popflg = 0;\r
- static int diskflag = 0;\r
- static int kbval;\r
- static int cflag;\r
- \r
- void dores(void);\r
- void pidaddr(void);\r
- \r
- void resinit()\r
- {\r
- segread(&seg);\r
- myss = seg.ss;\r
- \r
- rg.h.ah = 0x34;\r
- intdos(&rg,&rg);\r
- dosseg = _ES;\r
- dosbusy = rg.x.bx;\r
- \r
- mydta = getdta();\r
- pidaddr();\r
- oldtimer = getvect(0x1c);\r
- old28 = getvect(0x28);\r
- oldkb = getvect(9);\r
- olddisk = getvect(0x13);\r
- \r
- setvect(0x1c,newtimer);\r
- setvect(9,newkb);\r
- setvect(0x28,new28);\r
- setvect(0x13,newdisk);\r
- \r
- stack = (sizeprogram - (seg.ds - seg.cs)) * 16 - 300;\r
- rg.x.ax = 0x3100;\r
- rg.x.dx = sizeprogram;\r
- intdos(&rg,&rg);\r
- }\r
- \r
- void interrupt newdisk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)\r
- {\r
- diskflag++;\r
- (*olddisk)();\r
- ax = _AX;\r
- newcrit();\r
- flgs = cflag;\r
- --diskflag;\r
- }\r
- \r
- void interrupt newcrit(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)\r
- {\r
- ax = 0;\r
- cflag = flgs;\r
- }\r
- \r
- void interrupt newkb()\r
- {\r
- if (inportb(0x60) == scancode)\r
- {\r
- kbval = peekb(0,0x417);\r
- if (!resoff && ((kbval & keymask) ^ keymask) == 0)\r
- {\r
- kbval = inportb(0x61);\r
- outportb(0x61,kbval | 0x80);\r
- outportb(0x61,kbval);\r
- disable();\r
- outportb(0x20,0x20);\r
- enable();\r
- if (!running)\r
- popflg = 1;\r
- return;\r
- }\r
- }\r
- (*oldkb)();\r
- }\r
- \r
- void interrupt newtimer()\r
- {\r
- (*oldtimer)();\r
- if (popflg && peekb(dosseg,dosbusy) == 0)\r
- if(diskflag == 0)\r
- {\r
- outportb(0x20,0x20);\r
- popflg = 0;\r
- dores();\r
- }\r
- }\r
- \r
- void interrupt new28()\r
- {\r
- (*old28)();\r
- if (popflg && peekb(dosseg,dosbusy) != 0)\r
- {\r
- popflg = 0;\r
- dores();\r
- }\r
- }\r
- \r
- resident_psp()\r
- {\r
- intpsp = peek(dosseg,*pids);\r
- for(pp = 0; pp < pidctr; pp++)\r
- poke(dosseg,pids[pp],mypsp);\r
- }\r
- \r
- interrupted_psp()\r
- {\r
- for(pp = 0; pp < pidctr; pp++)\r
- poke(dosseg,pids[pp],intpsp);\r
- }\r
- \r
- void dores()\r
- {\r
- running = 1;\r
- disable();\r
- intsp = _SP;\r
- intss = _SS;\r
- _SP = stack;\r
- _SS = myss;\r
- enable();\r
- oldcrit = getvect(0x24);\r
- setvect(0x24,newcrit);\r
- rg.x.ax = 0x3300;\r
- intdos(&rg,&rg);\r
- ctrl_break = rg.h.dl;\r
- rg.x.ax = 0x3301;\r
- rg.h.dl = 0;\r
- intdos(&rg,&rg);\r
- intdta = getdta();\r
- setdta(mydta);\r
- resident_psp();\r
- popup();\r
- interrupted_psp();\r
- setdta(intdta);\r
- setvect(0x24,oldcrit);\r
- rg.x.ax = 0x3301;\r
- rg.h.dl = ctrl_break;\r
- intdos(&rg,&rg);\r
- disable();\r
- _SP = intsp;\r
- _SS = intss;\r
- enable();\r
- running = 0;\r
- }\r
- \r
- static int avec = 0;\r
- \r
- unsigned resident(char *signature,void interrupt(*ifunc)())\r
- {\r
- char *sg;\r
- unsigned df;\r
- int vec;\r
- \r
- segread(&seg);\r
- df = seg.ds-seg.cs;\r
- for(vec = 0x60; vec < 0x68; vec++)\r
- {\r
- if (getvect(vec) == NULL)\r
- {\r
- if (!avec)\r
- avec = vec;\r
- continue;\r
- }\r
- for(sg = signature; *sg; sg++)\r
- if (*sg != peekb(peek(0,2+vec*4)+df,(unsigned)sg))\r
- break;\r
- if (!*sg)\r
- return vec;\r
- }\r
- if (avec)\r
- setvect(avec,ifunc);\r
- return 0;\r
- }\r
- \r
- static void pidaddr()\r
- {\r
- unsigned adr = 0;\r
- \r
- rg.h.ah = 0x51;\r
- intdos(&rg,&rg);\r
- mypsp = rg.x.bx;\r
- rg.h.ah = 0x52;\r
- intdos(&rg,&rg);\r
- enddos = _ES;\r
- enddos = peek(enddos,rg.x.bx-2);\r
- while(pidctr < 2 && (unsigned)((dosseg<<4) + adr) < (enddos\r
- <<4))\r
- {\r
- if (peek(dosseg,adr) == mypsp)\r
- {\r
- rg.h.ah = 0x50;\r
- rg.x.bx = mypsp + 1;\r
- intdos(&rg,&rg);\r
- if (peek(dosseg,adr) == mypsp + 1)\r
- pids[pidctr++] = adr;\r
- rg.h.ah = 0x50;\r
- rg.x.bx = mypsp;\r
- intdos(&rg,&rg);\r
- }\r
- adr++;\r
- }\r
- }\r
- \r
- static resterm()\r
- {\r
- setvect(0x1c,oldtimer);\r
- setvect(9,oldkb);\r
- setvect(0x28,old28);\r
- setvect(0x13,olddisk);\r
- setvect(avec,(void interrupt (*)()) 0);\r
- rg.h.ah = 0x52;\r
- intdos(&rg,&rg);\r
- mcbseg = _ES;\r
- mcbseg = peek(mcbseg,rg.x.bx-2);\r
- segread(&seg);\r
- while(peekb(mcbseg,0) == 0x4d)\r
- {\r
- if(peek(mcbseg,1) == mypsp)\r
- {\r
- rg.h.ah = 0x49;\r
- seg.es = mcbseg+1;\r
- intdosx(&rg,&rg,&seg);\r
- }\r
- mcbseg += peek(mcbseg,3) + 1;\r
- }\r
- }\r
- \r
- terminate()\r
- {\r
- if (getvect(0x13) == (void interrupt (*)()) newdisk)\r
- if (getvect(9) == newkb)\r
- if(getvect(0x28) == new28)\r
- if(getvect(0x1c) == newtimer)\r
- {\r
- resterm();\r
- return;\r
- }\r
- resoff = 1;\r
- }\r
- \r
- restart()\r
- {\r
- resoff = 0;\r
- }\r
- \r
- wait()\r
- {\r
- resoff = 1;\r
- }\r
- \r
- void cursor(int y, int x)\r
- {\r
- rg.x.ax = 0x0200;\r
- rg.x.bx = 0;\r
- rg.x.dx = ((y << 8) & 0xff00) + x;\r
- int86(16,&rg,&rg);\r
- }\r
- \r
- void curr_cursor(int *y, int *x)\r
- {\r
- rg.x.ax = 0x0300;\r
- rg.x.bx = 0;\r
- int86(16,&rg,&rg);\r
- *x = rg.h.dl;\r
- *y = rg.h.dh;\r
- }\r
- \r
- /*\r
- Third module, the simple pop-up address book\r
- with mouse support\r
- */\r
- \r
- #include <stdio.h>\r
- #include <stdlib.h>\r
- #include <io.h>\r
- #include <string.h>\r
- #include <fcntl.h>\r
- #include <sys\stat.h>\r
- #include <dos.h>\r
- #include <conio.h>\r
- #include <graphics.h>\r
- #include <bios.h>\r
- \r
- /* left cannot be less than 3 */\r
- #define left 4\r
- \r
- /* Data structure for records */\r
- typedef struct\r
- {\r
- char name[31];\r
- char company[31];\r
- char address[31];\r
- char area[31];\r
- char town[31];\r
- char county[31];\r
- char post[13];\r
- char telephone[16];\r
- char fax[16];\r
- }\r
- data;\r
- \r
- extern char fpath[];\r
- \r
- static char scr[4000];\r
- \r
- static char sbuff[2000];\r
- char stext[30];\r
- data rec;\r
- int handle;\r
- int recsize;\r
- union REGS inreg,outreg;\r
- \r
- /*\r
- Function prototypes\r
- */\r
- void FATAL(char *);\r
- void OPENDATA(void);\r
- void CONTINUE(void);\r
- void EXPORT_MULTI(void);\r
- void GETDATA(int);\r
- int GETOPT(void);\r
- void DISPDATA(void);\r
- void ADD_REC(void);\r
- void PRINT_MULTI(void);\r
- void SEARCH(void);\r
- void MENU(void);\r
- \r
- int GET_MOUSE(int *buttons)\r
- {\r
- inreg.x.ax = 0;\r
- int86(0x33,&inreg,&outreg);\r
- *buttons = outreg.x.bx;\r
- return outreg.x.ax;\r
- }\r
- \r
- void MOUSE_CURSOR(int status)\r
- {\r
- /* Status = 0 cursor off */\r
- /* 1 cursor on */\r
- \r
- inreg.x.ax = 2 - status;\r
- int86(0x33,&inreg,&outreg);\r
- }\r
- \r
- int MOUSE_LOCATION(int *x, int *y)\r
- {\r
- inreg.x.ax = 3;\r
- int86(0x33,&inreg,&outreg);\r
- \r
- *x = outreg.x.cx / 8;\r
- *y = outreg.x.dx / 8;\r
- \r
- return outreg.x.bx;\r
- }\r
- \r
- int GETOPT()\r
- {\r
- int result;\r
- int x;\r
- int y;\r
- \r
- do\r
- {\r
- do\r
- {\r
- result = MOUSE_LOCATION(&x,&y);\r
- if (result & 1)\r
- {\r
- if (x >= 52 && x <= 53 && y >= 7 && y <= 15)\r
- return y - 7;\r
- if (x >= 4 && x <= 40 && y >= 7 && y <= 14)\r
- return y + 10;\r
- \r
- if (x >= 4 && x <= 40 && y == 15)\r
- return y + 10;\r
- }\r
- }\r
- while(!bioskey(1));\r
- \r
- result = bioskey(0);\r
- x = result & 0xff;\r
- if (x == 0)\r
- {\r
- result = result >> 8;\r
- result -= 60;\r
- }\r
- }\r
- while(result < 0 || result > 8);\r
- return result;\r
- }\r
- \r
- void setvideo(unsigned char mode)\r
- {\r
- /* Sets the video display mode and clears the screen */\r
- \r
- inreg.h.al = mode;\r
- inreg.h.ah = 0x00;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
- \r
- int activepage(void)\r
- {\r
- /* Returns the currently selected video display page */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 0x0F;\r
- int86(0x10, &inreg, &outreg);\r
- return(outreg.h.bh);\r
- }\r
- \r
- void print(char *str)\r
- {\r
- /*\r
- Prints characters only directly to the current display page\r
- starting at the current cursor position. The cursor is not\r
- advanced.\r
- This function assumes a colour display card. For use with a\r
- monochrome display card change 0xB800 to read 0xB000\r
- */\r
- \r
- int page;\r
- int offset;\r
- unsigned row;\r
- unsigned col;\r
- char far *ptr;\r
- \r
- page = activepage();\r
- curr_cursor(&row,&col);\r
- \r
- offset = page * 4000 + row * 160 + col * 2;\r
- \r
- ptr = MK_FP(0xB800,offset);\r
- \r
- while(*str)\r
- {\r
- *ptr++= *str++;\r
- ptr++;\r
- }\r
- }\r
- \r
- \r
- void TRUESHADE(int lef, int top, int right, int bottom)\r
- {\r
- int n;\r
- \r
- /* True Shading of a screen block */\r
- \r
- gettext(lef,top,right,bottom,sbuff);\r
- for(n = 1; n < 2000; n+= 2)\r
- sbuff[n] = 7;\r
- puttext(lef,top,right,bottom,sbuff);\r
- }\r
- \r
- void DBOX(int l, int t, int r, int b)\r
- {\r
- /* Draws a double line box around the described area */\r
- \r
- int n;\r
- \r
- cursor(t,l);\r
- print("\90");\r
- for(n = 1; n < r - l; n++)\r
- {\r
- cursor(t,l + n);\r
- print("I");\r
- }\r
- cursor(t,r);\r
- print("¯");\r
- \r
- for (n = t + 1; n < b; n++)\r
- {\r
- cursor(n,l);\r
- print("§");\r
- cursor(n,r);\r
- print("§");\r
- }\r
- cursor(b,l);\r
- print("E");\r
- for(n = 1; n < r - l; n++)\r
- {\r
- cursor(b,l+n);\r
- print("I");\r
- }\r
- cursor(b,r);\r
- print("¬");\r
- }\r
- \r
- int INPUT(char *text,unsigned length)\r
- {\r
- /* Receive a string from the operator */\r
- \r
- unsigned key_pos;\r
- int key;\r
- unsigned start_row;\r
- unsigned start_col;\r
- unsigned end;\r
- char temp[80];\r
- char *p;\r
- \r
- curr_cursor(&start_row,&start_col);\r
- \r
- key_pos = 0;\r
- end = strlen(text);\r
- for(;;)\r
- {\r
- key = bioskey(0);\r
- if ((key & 0xFF) == 0)\r
- {\r
- key = key >> 8;\r
- if (key == 79)\r
- {\r
- while(key_pos < end)\r
- key_pos++;\r
- cursor(start_row,start_col + key_pos);\r
- }\r
- else\r
- if (key == 71)\r
- {\r
- key_pos = 0;\r
- cursor(start_row,start_col);\r
- }\r
- else\r
- if ((key == 75) && (key_pos > 0))\r
- {\r
- key_pos--;\r
- cursor(start_row,start_col + key_pos);\r
- }\r
- else\r
- if ((key == 77) && (key_pos < end))\r
- {\r
- key_pos++;\r
- cursor(start_row,start_col + key_pos);\r
- }\r
- else\r
- if (key == 83)\r
- {\r
- p = text + key_pos;\r
- while(*(p+1))\r
- {\r
- *p = *(p+1);\r
- p++;\r
- }\r
- *p = 32;\r
- if (end > 0)\r
- end--;\r
- cursor(start_row,start_col);\r
- cprintf(text);\r
- cprintf(" ");\r
- if ((key_pos > 0) && (key_pos == end))\r
- key_pos--;\r
- cursor(start_row,start_col + key_pos);\r
- }\r
- }\r
- else\r
- {\r
- key = key & 0xFF;\r
- if (key == 13 || key == 27)\r
- break;\r
- else\r
- if ((key == 8) && (key_pos > 0))\r
- {\r
- end--;\r
- key_pos--;\r
- text[key_pos--] = '\0';\r
- strcpy(temp,text);\r
- p = text + key_pos + 2;\r
- strcat(temp,p);\r
- strcpy(text,temp);\r
- cursor(start_row,start_col);\r
- cprintf("%-*.*s",length,length,text);\r
- key_pos++;\r
- cursor(start_row,start_col + key_pos);\r
- }\r
- else\r
- if ((key > 31) && (key_pos < length) &&\r
- (start_col + key_pos < 80))\r
- {\r
- if (key_pos <= end)\r
- {\r
- p = text + key_pos;\r
- memmove(p+1,p,end - key_pos);\r
- if (end < length)\r
- end++;\r
- text[end] = '\0';\r
- }\r
- text[key_pos++] = (char)key;\r
- if (key_pos > end)\r
- {\r
- end++;\r
- text[end] = '\0';\r
- }\r
- cursor(start_row,start_col);\r
- cprintf("%-*.*s",length,length,text);\r
- cursor(start_row,start_col + key_pos);\r
- }\r
- }\r
- }\r
- text[end] = '\0';\r
- return key;\r
- }\r
- \r
- void FATAL(char *error)\r
- {\r
- /* A fatal error has occured */\r
- \r
- printf("\nFATAL ERROR: %s",error);\r
- exit(0);\r
- }\r
- \r
- void OPENDATA()\r
- {\r
- /* Check for existence of data file and if not create it */\r
- /* otherwise open it for reading/writing at end of file */\r
- \r
- handle = open(fpath,O_RDWR,S_IWRITE);\r
- \r
- if (handle == -1)\r
- {\r
- handle = open(fpath,O_RDWR|O_CREAT,S_IWRITE);\r
- if (handle == -1)\r
- FATAL("Unable to create data file");\r
- }\r
- /* Read in first rec */\r
- read(handle,&rec,recsize);\r
- }\r
- \r
- void CLOSEDATA()\r
- {\r
- close(handle);\r
- }\r
- \r
- void GETDATA(int start)\r
- {\r
- /* Get address data from operator */\r
- \r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- gotoxy(left,8);\r
- print("Name ");\r
- gotoxy(left,9);\r
- print("Company ");\r
- gotoxy(left,10);\r
- print("Address ");\r
- gotoxy(left,11);\r
- print("Area ");\r
- gotoxy(left,12);\r
- print("Town ");\r
- gotoxy(left,13);\r
- print("County ");\r
- gotoxy(left,14);\r
- print("Post Code ");\r
- gotoxy(left,15);\r
- print("Telephone ");\r
- gotoxy(left,16);\r
- print("Fax ");\r
- \r
- switch(start)\r
- {\r
- case 0: gotoxy(left + 10,8);\r
- if(INPUT(rec.name,30) == 27)\r
- break;\r
- case 1: gotoxy(left + 10,9);\r
- if(INPUT(rec.company,30) == 27)\r
- break;\r
- case 2: gotoxy(left + 10,10);\r
- if(INPUT(rec.address,30) == 27)\r
- break;\r
- case 3: gotoxy(left + 10,11);\r
- if(INPUT(rec.area,30) == 27)\r
- break;\r
- case 4: gotoxy(left + 10,12);\r
- if(INPUT(rec.town,30) == 27)\r
- break;\r
- case 5: gotoxy(left + 10,13);\r
- if(INPUT(rec.county,30) == 27)\r
- break;\r
- case 6: gotoxy(left + 10,14);\r
- if(INPUT(rec.post,12) == 27)\r
- break;\r
- case 7: gotoxy(left + 10,15);\r
- if(INPUT(rec.telephone,15) == 27)\r
- break;\r
- case 8: gotoxy(left + 10,16);\r
- INPUT(rec.fax,15);\r
- break;\r
- }\r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left + 23,21);\r
- print(" ");\r
- }\r
- \r
- void DISPDATA()\r
- {\r
- /* Display address data */\r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- cursor(7,3);\r
- cprintf("Name %-30.30s",rec.name);\r
- cursor(8,3);\r
- cprintf("Company %-30.30s",rec.company);\r
- cursor(9,3);\r
- cprintf("Address %-30.30s",rec.address);\r
- cursor(10,3);\r
- cprintf("Area %-30.30s",rec.area);\r
- cursor(11,3);\r
- cprintf("Town %-30.30s",rec.town);\r
- cursor(12,3);\r
- cprintf("County %-30.30s",rec.county);\r
- cursor(13,3);\r
- cprintf("Post Code %-30.30s",rec.post);\r
- cursor(14,3);\r
- cprintf("Telephone %-30.30s",rec.telephone);\r
- cursor(15,3);\r
- cprintf("Fax %-30.30s",rec.fax);\r
- }\r
- \r
- int LOCATE(char *text)\r
- {\r
- int result;\r
- \r
- do\r
- {\r
- /* Read rec into memory */\r
- result = read(handle,&rec,recsize);\r
- if (result > 0)\r
- {\r
- /* Scan rec for matching data */\r
- if (strstr(strupr(rec.name),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.company),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.address),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.area),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.town),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.county),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.post),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.telephone),text) != NULL)\r
- return(1);\r
- if (strstr(strupr(rec.fax),text) != NULL)\r
- return(1);\r
- }\r
- }\r
- while(result > 0);\r
- return(0);\r
- }\r
- \r
- void SEARCH()\r
- {\r
- int result;\r
- \r
- gotoxy(left,21);\r
- textcolor(WHITE);\r
- textbackground(RED);\r
- cprintf("Enter data to search for ");\r
- strcpy(stext,"");\r
- INPUT(stext,30);\r
- if (*stext == 0)\r
- {\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- return;\r
- }\r
- gotoxy(left,21);\r
- textcolor(WHITE);\r
- textbackground(RED);\r
- cprintf("Searching for %s Please Wait....",stext);\r
- strupr(stext);\r
- /* Locate start of file */\r
- lseek(handle,0,SEEK_SET);\r
- result = LOCATE(stext);\r
- if (result == 0)\r
- {\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- gotoxy(left + 27,21);\r
- cprintf("NO MATCHING RECORDS");\r
- gotoxy(left + 24,22);\r
- cprintf("Press RETURN to Continue");\r
- bioskey(0);\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- gotoxy(left,22);\r
- cprintf("%70c",32);\r
- }\r
- else\r
- {\r
- lseek(handle,0 - recsize,SEEK_CUR);\r
- read(handle,&rec,recsize);\r
- DISPDATA();\r
- }\r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- }\r
- \r
- void CONTINUE()\r
- {\r
- int result;\r
- long curpos;\r
- \r
- curpos = tell(handle) - recsize;\r
- \r
- result = LOCATE(stext);\r
- textcolor(WHITE);\r
- textbackground(RED);\r
- if (result == 0)\r
- {\r
- gotoxy(left + 24,21);\r
- cprintf("NO MORE MATCHING RECORDS");\r
- gotoxy(left + 24,22);\r
- cprintf("Press RETURN to Continue");\r
- bioskey(0);\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- gotoxy(left,22);\r
- cprintf("%70c",32);\r
- lseek(handle,curpos,SEEK_SET);\r
- read(handle,&rec,recsize);\r
- DISPDATA();\r
- }\r
- else\r
- {\r
- lseek(handle,0 - recsize,SEEK_CUR);\r
- read(handle,&rec,recsize);\r
- DISPDATA();\r
- }\r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- gotoxy(left,22);\r
- cprintf(" ");\r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- }\r
- \r
- void PRINT_MULTI()\r
- {\r
- data buffer;\r
- char destination[60];\r
- char text[5];\r
- int result;\r
- int ok;\r
- int ok2;\r
- int blanks;\r
- int total_lines;\r
- char *p;\r
- FILE *fp;\r
- \r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left + 23,21);\r
- cprintf("Enter selection criteria");\r
- \r
- /* Clear existing rec details */\r
- memset(&rec,0,recsize);\r
- \r
- DISPDATA();\r
- GETDATA(0);\r
- \r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left,21);\r
- cprintf("Enter report destination PRN");\r
- strcpy(destination,"PRN");\r
- gotoxy(left,22);\r
- cprintf("Enter Address length in lines 18");\r
- strcpy(text,"18");\r
- gotoxy(left + 25,21);\r
- INPUT(destination,40);\r
- gotoxy(left +30,22);\r
- INPUT(text,2);\r
- gotoxy(left,21);\r
- cprintf("%72c",32);\r
- gotoxy(left,22);\r
- cprintf("%72c",32);\r
- \r
- total_lines = atoi(text) - 6;\r
- if (total_lines < 0)\r
- total_lines = 0;\r
- \r
- fp = fopen(destination,"w+");\r
- if (fp == NULL)\r
- {\r
- gotoxy(left,21);\r
- cprintf("Unable to print to %s",destination);\r
- gotoxy(left,22);\r
- cprintf("Press RETURN to Continue");\r
- bioskey(0);\r
- gotoxy(left,21);\r
- cprintf("%78c",32);\r
- gotoxy(left,22);\r
- cprintf(" ");\r
- }\r
- \r
- /* Locate start of file */\r
- lseek(handle,0,SEEK_SET);\r
- \r
- do\r
- {\r
- /* Read rec into memory */\r
- result = read(handle,&buffer,recsize);\r
- if (result > 0)\r
- {\r
- ok = 1;\r
- /* Scan rec for matching data */\r
- if (*rec.name)\r
- if (stricmp(buffer.name,rec.name))\r
- ok = 0;\r
- if (*rec.company)\r
- if (stricmp(buffer.company,rec.company))\r
- ok = 0;\r
- if (*rec.address)\r
- if (stricmp(buffer.address,rec.address))\r
- ok = 0;\r
- if (*rec.area)\r
- if (stricmp(buffer.area,rec.area))\r
- ok = 0;\r
- if (*rec.town)\r
- if (stricmp(buffer.town,rec.town))\r
- ok = 0;\r
- if (*rec.county)\r
- if (stricmp(buffer.county,rec.county))\r
- ok = 0;\r
- if (*rec.post)\r
- if (stricmp(buffer.post,rec.post))\r
- ok = 0;\r
- if (*rec.telephone)\r
- if (stricmp(buffer.telephone,rec.telephone))\r
- ok = 0;\r
- if (*rec.fax)\r
- if (stricmp(buffer.fax,rec.fax))\r
- ok = 0;\r
- if (ok)\r
- {\r
- blanks = total_lines;\r
- p = buffer.name;\r
- ok2 = 0;\r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.name);\r
- p = buffer.company;\r
- ok2 = 0;\r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.company);\r
- p = buffer.address;\r
- ok2 = 0;\r
- \r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.address);\r
- p = buffer.area;\r
- ok2 = 0;\r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.area);\r
- p = buffer.town;\r
- ok2 = 0;\r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.town);\r
- p = buffer.county;\r
- ok2 = 0;\r
- \r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.county);\r
- p = buffer.post;\r
- ok2 = 0;\r
- while(*p)\r
- {\r
- if (*p != 32)\r
- {\r
- ok2 = 1;\r
- break;\r
- }\r
- p++;\r
- }\r
- if (!ok2)\r
- blanks++;\r
- else\r
- fprintf(fp,"%s\n",buffer.post);\r
- while(blanks)\r
- {\r
- fprintf(fp,"\n");\r
- blanks--;\r
- }\r
- }\r
- }\r
- }\r
- while(result > 0);\r
- fclose(fp);\r
- lseek(handle,0,SEEK_SET);\r
- read(handle,&rec,recsize);\r
- DISPDATA();\r
- }\r
- \r
- void EXPORT_MULTI()\r
- {\r
- data buffer;\r
- char destination[60];\r
- int result;\r
- int ok;\r
- FILE *fp;\r
- \r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left + 23,21);\r
- cprintf("Enter selection criteria");\r
- \r
- /* Clear existing rec details */\r
- memset(&rec,0,recsize);\r
- \r
- DISPDATA();\r
- GETDATA(0);\r
- \r
- textcolor(WHITE);\r
- textbackground(RED);\r
- gotoxy(left,21);\r
- cprintf("Enter export file address.txt");\r
- strcpy(destination,"address.txt");\r
- gotoxy(left + 18,21);\r
- INPUT(destination,59);\r
- gotoxy(left,21);\r
- cprintf("%70c",32);\r
- \r
- fp = fopen(destination,"w+");\r
- if (fp == NULL)\r
- {\r
- gotoxy(left,21);\r
- cprintf("Unable to print to %s",destination);\r
- gotoxy(left,22);\r
- cprintf("Press RETURN to Continue");\r
- bioskey(0);\r
- gotoxy(left,21);\r
- cprintf("%78c",32);\r
- gotoxy(left,22);\r
- cprintf(" ");\r
- }\r
- /* Locate start of file */\r
- lseek(handle,0,SEEK_SET);\r
- \r
- do\r
- {\r
- /* Read rec into memory */\r
- result = read(handle,&buffer,recsize);\r
- if (result > 0)\r
- {\r
- ok = 1;\r
- /* Scan rec for matching data */\r
- if (*rec.name)\r
- if (stricmp(buffer.name,rec.name))\r
- ok = 0;\r
- if (*rec.company)\r
- if (stricmp(buffer.company,rec.company))\r
- ok = 0;\r
- if (*rec.address)\r
- if (stricmp(buffer.address,rec.address))\r
- ok = 0;\r
- if (*rec.area)\r
- if (stricmp(buffer.area,rec.area))\r
- ok = 0;\r
- if (*rec.town)\r
- if (stricmp(buffer.town,rec.town))\r
- ok = 0;\r
- if (*rec.county)\r
- if (stricmp(buffer.county,rec.county))\r
- ok = 0;\r
- if (*rec.post)\r
- if (stricmp(buffer.post,rec.post))\r
- ok = 0;\r
- if (*rec.telephone)\r
- if (stricmp(buffer.telephone,rec.telephone))\r
- ok = 0;\r
- if (*rec.fax)\r
- if (stricmp(buffer.fax,rec.fax))\r
- ok = 0;\r
- if (ok)\r
- {\r
- fprintf(fp,"\"%s\",",buffer.name);\r
- fprintf(fp,"\"%s\",",buffer.company);\r
- fprintf(fp,"\"%s\",",buffer.address);\r
- fprintf(fp,"\"%s\",",buffer.area);\r
- fprintf(fp,"\"%s\",",buffer.town);\r
- fprintf(fp,"\"%s\",",buffer.county);\r
- fprintf(fp,"\"%s\",",buffer.post);\r
- fprintf(fp,"\"%s\",",buffer.telephone);\r
- fprintf(fp,"\"%s\"\n",buffer.fax);\r
- \r
- }\r
- }\r
- }\r
- \r
- while(result > 0);\r
- fclose(fp);\r
- lseek(handle,0,SEEK_SET);\r
- read(handle,&rec,recsize);\r
- DISPDATA();\r
- }\r
- \r
- void MENU()\r
- {\r
- int option;\r
- long result;\r
- long end;\r
- int new;\r
- \r
- do\r
- {\r
- cursor(21,26);\r
- print("Select option (F2 - F10)");\r
- cursor(7,52);\r
- print("F2 Next record");\r
- cursor(8,52);\r
- print("F3 Previous record");\r
- cursor(9,52);\r
- print("F4 Amend record");\r
- cursor(10,52);\r
- print("F5 Add new record");\r
- cursor(11,52);\r
- print("F6 Search");\r
- cursor(12,52);\r
- print("F7 Continue search");\r
- cursor(13,52);\r
- print("F8 Print address labels");\r
- cursor(14,52);\r
- print("F9 Export records");\r
- cursor(15,52);\r
- print("F10 Exit");\r
- MOUSE_CURSOR(1);\r
- option = GETOPT();\r
- MOUSE_CURSOR(0);\r
- \r
- switch(option)\r
- {\r
- case 0 : /* Next rec */\r
- result = read(handle,&rec,recsize);\r
- if (!result)\r
- {\r
- lseek(handle,0,SEEK_SET);\r
- result = read(handle,&rec,recsize);\r
- }\r
- DISPDATA();\r
- break;\r
- \r
- case 1 : /* Previous rec */\r
- result = lseek(handle,0 - recsize * 2,SEEK_CUR);\r
- if (result <= -1)\r
- lseek(handle,0 - recsize,SEEK_END);\r
- result = read(handle,&rec,recsize);\r
- DISPDATA();\r
- break;\r
-\r
- case 3 : /* Add rec */\r
- lseek(handle,0,SEEK_END);\r
- memset(&rec,0,recsize);\r
- DISPDATA();\r
- \r
- case 2 : /* Amend current rec */\r
- new = 1;\r
- if (*rec.name)\r
- new = 0;\r
- else\r
- if (*rec.company)\r
- new = 0;\r
- else\r
- if (*rec.address)\r
- new = 0;\r
- else\r
- if (*rec.area)\r
- new = 0;\r
- else\r
- if (*rec.town)\r
- new = 0;\r
- else\r
- if (*rec.county)\r
- new = 0;\r
- else\r
- if (*rec.post)\r
- new = 0;\r
- else\r
- if (*rec.telephone)\r
- new = 0;\r
- else\r
- if (*rec.fax)\r
- new = 0;\r
- result = tell(handle);\r
- lseek(handle,0,SEEK_END);\r
- end = tell(handle);\r
- \r
- /* Back to original position */\r
- lseek(handle,result,SEEK_SET);\r
- \r
- /* If not at end of file, && !new rewind one\r
-rec */\r
- if (result != end || ! new)\r
- result = lseek(handle,0 -\r
-recsize,SEEK_CUR);\r
- result = tell(handle);\r
- gotoxy(left + 22,21);\r
- print(" Enter address details ");\r
- GETDATA(0);\r
- if (*rec.name || *rec.company)\r
- result = write(handle,&rec,recsize);\r
- break;\r
-\r
- case 4 : /* Search */\r
- gotoxy(left + 22,21);\r
- print(" ");\r
- SEARCH();\r
- break;\r
- \r
- case 5 : /* Continue */\r
- gotoxy(left + 22,21);\r
- print(" ");\r
- CONTINUE();\r
- break;\r
- \r
- case 6 : /* Print */\r
- gotoxy(left + 22,21);\r
- print(" ");\r
- PRINT_MULTI();\r
- break;\r
-\r
- case 7 : /* Export */\r
- gotoxy(left + 22,21);\r
- print(" ");\r
- EXPORT_MULTI();\r
- break;\r
- \r
- case 8 : /* Exit */\r
- break;\r
- \r
- default: /* Amend current rec */\r
- new = 1;\r
- if (*rec.name)\r
- new = 0;\r
- else\r
- if (*rec.company)\r
- new = 0;\r
- else\r
- if (*rec.address)\r
- new = 0;\r
- else\r
- if (*rec.area)\r
- new = 0;\r
- else\r
- if (*rec.town)\r
- new = 0;\r
- else\r
- if (*rec.county)\r
- new = 0;\r
- else\r
- if (*rec.post)\r
- new = 0;\r
- else\r
- if (*rec.telephone)\r
- new = 0;\r
- else\r
- if (*rec.fax)\r
- new = 0;\r
- result = tell(handle);\r
- lseek(handle,0,SEEK_END);\r
- end = tell(handle);\r
-\r
- /* Back to original position */\r
- lseek(handle,result,SEEK_SET);\r
-\r
- /* If not at end of file, && !new rewind one\r
-rec */\r
- if (result != end || ! new)\r
- result = lseek(handle,0 -\r
-recsize,SEEK_CUR);\r
- result = tell(handle);\r
- gotoxy(left + 22,21);\r
- print(" Enter address details ");\r
- GETDATA(option - 17);\r
- if (*rec.name || *rec.company)\r
- result = write(handle,&rec,recsize);\r
- option = -1;\r
- break;\r
-\r
- }\r
- }\r
- \r
- while(option != 8);\r
- }\r
- \r
- void exec()\r
- {\r
- gettext(1,1,80,25,scr);\r
- setvideo(3);\r
- textbackground(WHITE);\r
- textcolor(BLACK);\r
- clrscr();\r
- recsize = sizeof(data);\r
- \r
- OPENDATA();\r
- \r
- TRUESHADE(left,3,79,5);\r
- window(left - 2,2 ,78, 4);\r
- textcolor(YELLOW);\r
- textbackground(MAGENTA);\r
- clrscr();\r
- DBOX(left - 3, 1, 77, 3);\r
- gotoxy(3,2);\r
- print("Servile Software PC ADDRESS BOOK 5.2\r
- (c) 1994");\r
- \r
- TRUESHADE(left,8,left + 43,18);\r
- window(left - 2,7 , left + 42, 17);\r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- clrscr();\r
- DBOX(left - 3, 6, left + 41, 16);\r
- \r
- TRUESHADE(left + 48,8,79,18);\r
- window(left + 46, 7 , 78, 17);\r
- textbackground(BLUE);\r
- textcolor(YELLOW);\r
- clrscr();\r
- DBOX(left + 45,6,77,16);\r
- \r
- TRUESHADE(left ,21,79,24);\r
- window(left - 2, 20 , 78, 23);\r
- textbackground(RED);\r
- textcolor(WHITE);\r
- clrscr();\r
- DBOX(left - 3,19,77,22);\r
- \r
- window(1,1,80,25);\r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- DISPDATA();\r
- \r
- MENU();\r
- \r
- CLOSEDATA();\r
- puttext(1,1,80,25,scr);\r
- return;\r
- }\r
- \r
- \r
- INTERFACING C WITH CLIPPER\r
-\r
-The Clipper programming language is a popular xBase environment for the\r
-PC. However, it lacks many of the facilities available to programmers of\r
-other languages, and it is quite slow compared to C. Because of this\r
-there are a large number of third party add-on libraries available for\r
-Clipper which provide the facilities lacked.\r
-\r
-As a programmer you probably want to write your own library for Clipper,\r
-or perhaps individual functions to cater for circumstances which Clipper\r
-cannot handle, such as high resolution graphics.\r
-\r
-Throughout this section, Clipper refers to the Summer `87 Clipper\r
-compiler, although initial tests show that the functions described here\r
-work perfectly well with the new Clipper 5 compiler also, we are not in a\r
-position to guarrantee success!\r
-\r
-\r
-COMPILING AND LINKING\r
-The Clipper extend functions allow user defined functions to be written\r
-in C, linked with and used by the Clipper application. The first\r
-problem a programmer must address when writing functions in C to link\r
-with a Clipper application is that of the C compiler's run time\r
-libraries.\r
-\r
-If one is writing functions with Microsoft C, then most of the required\r
-run time library functions will be found in the Clipper.lib and\r
-Extend.lib libraries which are part of Clipper.\r
-\r
-If, however, one is using a different C compiler, such as Borland's\r
-Turbo C then the run time library routines must be supplied on the link\r
-line.\r
-\r
-All C functions must be compiled using the large memory model the\r
-following line is used with Microsoft C\r
-\r
-\r
- cl /c /AL /Zl /Oalt /FPa /Gs <program.c>\r
-\r
-and this compile line may be used with Turbo C\r
-\r
- tcc -c -ml <program>\r
-\r
-simply substitute <program> for the program name to be compiled.\r
-\r
-Having compiled a C function it must be linked in with the application.\r
-If the C function was compiled with Microsoft C then the link line will\r
-look a little like this;\r
-\r
-\r
- LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend\r
-\r
-If the C function was linked with another C compiler you will also need\r
-to link in the C run time libraries, for example to link in the Turbo C\r
-large memory mode library use the following link line;\r
-\r
-\r
- LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend cl\r
-\r
-If one is using a number of separately compiled C functions it is a good\r
-idea to collect them in a library. If you are using Microsoft C then\r
-you can simply create the library by using Microsoft Lib.exe with\r
-the following command line;\r
-\r
-\r
- LIB mylib +prog1 +prog2, NUL, NUL\r
-\r
-This tells the librarian to add prog1.obj and prog2.obj to a library\r
-called mylib.lib, creating it if it does not exist. The NUL\r
-parameter is for supressing the listing file.\r
-\r
-\r
-If you have been using another C compiler you should copy the C large\r
-memory model run time library before adding your functions to it for\r
-example;\r
-\r
-\r
- COPY C:\TURBOC\LIB\cl.lib mylib.lib\r
- LIB mylib +prog1 +prog2, NUL, NUL\r
-\r
-Then when you link your Clipper application you will use a link line\r
-similar to;\r
-\r
- LINK /SE:500 /NOE myprog,,,Clipper Extend Mylib\r
-\r
-Often when linking C functions with Clipper applications link errors\r
-will occur such as those shown below;\r
-\r
- \r
- Microsoft (R) Overlay Linker Version 3.65\r
- Copyright (C) Microsoft Corp 1983-1988. All rights reserved.\r
- \r
- \r
- LINK : error L2029: Unresolved externals:\r
- \r
- \r
- FIWRQQ in file(s):\r
- M:SLIB.LIB(TEST)\r
- FIDRQQ in file(s):\r
- M:SLIB.LIB(TEST)\r
- \r
- There were 2 errors detected\r
- \r
-\r
-Example Link Errors\r
-\r
-The errors shown here are `Unresolved externals', that is they\r
-are references to functions which are not found in any of the object\r
-modules or libraries specified on the link line. These occur because\r
-the C compilers often scatter functions and variables through a number\r
-of libraries. In tracking these functions down use may be made of\r
-the Microsoft librarian list file option. If you run Lib.Exe on the\r
-Turbo C `emu.lib' library file and specify a listing file as follows;\r
-\r
-\r
- LIB emu,emu.lst\r
-\r
-The librarian will create an ascii file which contains the names of\r
-each object module contained in the specified library file, and the names\r
-of each function and public variable declared in each object module, as\r
-shown in this listing of Borland's EMU.LIB library.\r
-\r
-\r
- e086_Entry........EMU086 e086_Shortcut.....EMU086\r
- e087_Entry........EMU087 e087_Shortcut.....EMU087\r
- FIARQQ............EMUINIT FICRQQ............EMUINIT\r
- FIDRQQ............EMUINIT FIERQQ............EMUINIT\r
- FISRQQ............EMUINIT FIWRQQ............EMUINIT\r
- FJARQQ............EMUINIT FJCRQQ............EMUINIT\r
- FJSRQQ............EMUINIT __EMURESET........EMUINIT\r
- \r
- \r
- EMUINIT Offset: 00000010H Code and data size: 1a2H\r
- FIARQQ FICRQQ FIDRQQ FIERQQ\r
- FISRQQ FIWRQQ FJARQQ FJCRQQ\r
- FJSRQQ __EMURESET\r
- \r
- EMU086 Offset: 00000470H Code and data size: 2630H\r
- e086_Entry e086_Shortcut\r
- \r
- EMU087 Offset: 00003200H Code and data size: 417H\r
- e087_Entry e087_Shortcut\r
- \r
-\r
-\r
-Receiving Parameters\r
-\r
-Clipper provides six different functions for receiving parameters in a C\r
-function. These functions are;\r
-\r
-\r
- Receive a string char * _parc(int,[int])\r
- Receive a Date string char * _pards(int,[int])\r
- Receive a logical int _parl(int,[int])\r
- Receive an integer int _parni(int,[int])\r
- Receive a long long _parnl(int,[int])\r
- Receive a double double _parnd(int,[int])\r
-\r
-\r
-\r
-To illustrate simple parameter receiving in a C function I offer\r
-the following simple C function which receives two numeric parameters\r
-from the calling Clipper program, and uses these two numeric\r
-parameters to set the size of the cursor.\r
-\r
-\r
- #include <nandef.h> /* Clipper header files */\r
- #include <extend.h>\r
- #include <dos.h> /* Header file to define REGS */\r
- \r
- CLIPPER s_curset()\r
- {\r
- /* Demonstration function to set cursor shape */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 0x01;\r
- inreg.h.ch = _parni(1); /* Get integer parameter 1 */\r
- inreg.h.cl = _parni(2); /* Get integer parameter 2 */\r
- int86(0x10,&inreg,&outreg);\r
- _ret(); /* Return to Clipper */\r
- }\r
-\r
-Clipper provides four more functions for dealing with received\r
-parameters;\r
-\r
-_parclen(int,[int]) which returns the length of a string including\r
-imbedded `\0's, _parcsiz(int[int]) which returns the length of a\r
-character string passed by reference from Clipper, _parinfa(int,[int])\r
-which returns the type of a specified array element or the length of\r
-an array, and finally _parinfo(int) whic returns the type of a\r
-parameter.\r
-\r
-The following example function uses _parinfa() to determine both the\r
-length of an array passed from a Clipper program, and the type of each\r
-element in the array. The function then returns to Clipper an integer\r
-representing the number of defined elements in the array.\r
-\r
-\r
-\r
- #include <nandef.h>\r
- #include <extend.h>\r
- \r
- CLIPPER s_alen()\r
- {\r
- int total;\r
- int n;\r
- int defined;\r
- int type;\r
- \r
- /* Return the number of defined elements in an array */\r
- /* From Clipper use defined = s_alen(arr) */\r
- \r
- total = _parinfa(1,0); /* Get declared number of elements in\r
- array */\r
- \r
- defined = 0;\r
- \r
- for (n = 1; n <= total; n++){\r
- type = _parinfa(1,n); /* Get array parameter type */\r
- if (type)\r
- defined++;\r
- }\r
- _retni(defined); /* Return an integer to Clipper\r
- */\r
- }\r
- \r
-\r
-This function goes one step further to return the mean average of\r
-all numeric values in an array. Notice the use of _parnd() to\r
-retrieve the numeric values as doubles. You may find that because of\r
-the floating point arithmetic in this function that it will only\r
-work if compiled with Microsoft C.\r
-\r
-\r
- #include <nandef.h>\r
- #include <extend.h>\r
- \r
- CLIPPER s_aave()\r
- {\r
- int total;\r
- int defined;\r
- int n;\r
- int type;\r
- double sum;\r
- \r
- /* Return the mean average value of numbers in array */\r
- /* From Clipper use mean = s_aave(arr)\r
- \r
- \r
- total = _parinfa(1,0); /* Get declared number of\r
- elements */\r
- \r
- defined = 0;\r
- \r
- for (n = 1; n <= total; n++){ /* Determine number of defined\r
- */\r
- type = _parinfa(1,n); /* elements */\r
- if (type == 2)\r
- defined++;\r
- }\r
- \r
- sum = 0;\r
- \r
- for (n = 1; n <= total; n++){\r
- type = _parinfa(1,n);\r
- if (type == 2) /* Only sum numeric values\r
- */\r
- sum += _parnd(1,n);\r
- }\r
- _retnd(sum / defined); /* Return a double to\r
- Clipper */\r
- }\r
- \r
- \r
-\r
-Returning Values\r
-\r
-The Clipper manual lists seven functions for returning from a\r
-function written in another language. These return functions for C are as\r
-follows;\r
-\r
- character _retc(char *)\r
- date _retds(char *)\r
- logical _retl(int)\r
- numeric (int) _retni(int)\r
- numeric (long) _retnl(long)\r
- numeric (double) _retnd(double)\r
- nothing _ret(void)\r
-\r
-Omitted from the Clipper manual is the information that you may\r
-return different types of value back from a function! For example, you\r
-may wish to return a character string under normal circumstances, fine\r
-use _retc(). On error occurences however you can return a logical using\r
-_retl(). The Clipper program will assign the received value to the\r
-receiving variable in the correct manner.\r
-\r
-The following simple C function returns a random number. Notice the use\r
-of integers which limits the range of the function to +-32767. For\r
-larger values you should use longs instead of integers.\r
-\r
-\r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- CLIPPER s_random()\r
- {\r
- /* Returns a random number between 0 and param1 - 1 */\r
- /* From Clipper use x = s_random(param1) */\r
- \r
- int param1;\r
- int x;\r
- \r
- param1 = _parni(1);\r
- \r
- x = rand() % param1;\r
- _retni(x);\r
- }\r
- \r
-\r
-This function receives a string from Clipper, and passes back an upper\r
-case copy of the string, leaving the original unchanged. The maximum\r
-length of the string which can be processed is determined by the size\r
-of target[], here set to 5000 characters.\r
-\r
-\r
- #include <nandef.h>\r
- #include <extend.h>\r
- \r
- CLIPPER s_upper()\r
- {\r
- /* Returns an upper case copy of string */\r
- /* From Clipper use ? s_upper("this is a string")\r
- \r
- char *p;\r
- char *q;\r
- char *string;\r
- char target[5000];\r
- int n;\r
- \r
- string = _parc(1);\r
- \r
- p = string;\r
- q = target;\r
- \r
- while(*string){\r
- *q++ = toupper(*string);\r
- string++;\r
- }\r
- *q = '\0';\r
- string = p;\r
- _retc(target);\r
- }\r
- \r
- \r
-This function may be used to change the current DOS directory. If it\r
-is successful it returns .T. to the calling Clipper program,\r
-otherwise it returns .F.\r
-\r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- CLIPPER s_chdir()\r
- {\r
- /* Attempts to change the current DOS directory */\r
- /* From Clipper use result = s_chdir(path) */\r
- \r
- union REGS inreg,outreg;\r
- struct SREGS segreg;\r
- \r
- char *path;\r
- int x;\r
- \r
- path = _parc(1); /* Retrieve string from Clipper\r
- */\r
- \r
- inreg.h.ah = 0x3b;\r
- segreg.ds = FP_SEG(path);\r
- inreg.x.dx = FP_OFF(path);\r
- intdosx(&inreg,&outreg,&segreg);\r
- \r
- x = outreg.x.ax;\r
- \r
- if (x == 3)\r
- _retl(0); /* Return logical .F. back to Clipper */\r
- else\r
- _retl(1); /* Return logical .T. back to Clipper */\r
- }\r
- \r
-\r
-\r
-Avoiding Unresolved Externals\r
-\r
-As we have already seen, a common problem plaguing the programmer\r
-interfacing C functions with Clipper programs is Unresolved Externals.\r
-\r
-The following example C function called s_print() will not link\r
-with Clipper.\r
-\r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <stdio.h>\r
- \r
- CLIPPER s_print()\r
- {\r
- char *x;\r
- \r
- x = _parc(1);\r
- \r
- printf("\nI received %s from Clipper.\n",x);\r
- \r
- _ret();\r
- }\r
- \r
-\r
-The linker gives you the following reply;\r
-\r
- Microsoft (R) Overlay Linker Version 3.65\r
- Copyright (C) Microsoft Corp 1983-1988. All rights reserved.\r
- \r
- M:SLIB.LIB(IOERROR) : error L2025: __doserrno : symbol defined more\r
- than once\r
- pos: 16C6F Record type: 53C6\r
- \r
- LINK : error L2029: Unresolved externals:\r
- \r
- \r
-\r
- __RealCvtVector in file(s):\r
- M:SLIB.LIB(REALCVT)\r
- _abort in file(s):\r
- M:SLIB.LIB(CVTFAK)\r
- \r
- There were 3 errors detected\r
- \r
-\r
-The error L2025 `symbol defined more than once' can in this case be\r
-ignored. However, the unresolved externals `RealCvtVector' and\r
-`abort' cannot be ignored. These two functions are referenced by the\r
-function printf() which has been included in the C function. The\r
-answer is to use as few of the compiler's run time library functions\r
-as possible, use ROM calls instead with INT86() and INTDOSX() etc.\r
-\r
-\r
-Adding High Resolution Graphics To Clipper With C\r
-\r
-The most annoying omission from Clipper, in my opinion, is the lack of\r
-high resolution graphics facilities. The following functions, written in\r
-Turbo C, provide high resolution graphics to Clipper.\r
-\r
-First we require a means to change the video display mode to a high\r
-resolution graphics mode, and back to text mode. The IBM PC BIOS provides\r
-the means for this and can be called from C as follows;\r
-\r
-\r
-\r
-\r
- /* Servile Software Library For Clipper */\r
- \r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- CLIPPER s_smode()\r
- {\r
- /* Set Video Mode */\r
- /* From Clipper use s_smode(mode) */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.al = _parni(1);\r
- inreg.h.ah = 0x00;\r
- int86 (0x10, &inreg, &outreg);\r
- \r
- \r
- /* 1 40x25 colour text\r
- 2 40x25 bw text\r
- 3 80x25 colour text\r
- 4 320x200 4 colour graphics\r
- 5 320x200 4 colour graphics colour burst off\r
- 6 640x200 2 colour graphics\r
- etc\r
- */\r
- _ret();\r
- }\r
-\r
-Having set the computer into graphics mode, how about setting pixels to a\r
-specified colour?\r
-\r
- \r
- /* Servile Software Library For Clipper */\r
- \r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- CLIPPER s_plot()\r
- {\r
- \r
- union REGS inreg,outreg;\r
- \r
- /* Sets a pixel at the specified coordinates to the specified\r
- colour. */\r
- \r
- inreg.h.bh = 0x00;\r
- inreg.x.cx = _parni(1);\r
- inreg.x.dx = _parni(2);\r
- inreg.h.al = _parni(3);\r
- inreg.h.ah = 0x0C;\r
- int86(0x10, &inreg, &outreg);\r
- }\r
- \r
-Line drawing and circles are handled by these two functions;\r
-\r
- \r
- /* Servile Software Library For Clipper */\r
- \r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- CLIPPER s_line()\r
- {\r
- \r
- union REGS inreg,outreg;\r
- \r
- /* Draws a straight line from (a,b) to (c,d) in colour col */\r
- \r
- int a;\r
- int b;\r
- int c;\r
- int d;\r
- int col;\r
- int u;\r
- int v;\r
- int d1x;\r
- int d1y;\r
- int d2x;\r
- int d2y;\r
- int m;\r
- int n;\r
- int s;\r
- int i;\r
- \r
- a = _parni(1);\r
- b = _parni(2);\r
- c = _parni(3);\r
- d = _parni(4);\r
- col = _parni(5);\r
- \r
- u = c - a;\r
- v = d - b;\r
- if (u == 0)\r
- {\r
- d1x = 0;\r
- m = 0;\r
- }\r
- else\r
- {\r
- m = abs(u);\r
- if (u < 0)\r
- d1x = -1;\r
- else\r
- if (u > 0)\r
- d1x = 1;\r
- }\r
- if ( v == 0)\r
- {\r
- d1y = 0;\r
- n = 0;\r
- }\r
- else\r
- {\r
- n = abs(v);\r
- if (v < 0)\r
- d1y = -1;\r
- else\r
- if (v > 0)\r
- d1y = 1;\r
- }\r
- if (m > n)\r
- {\r
- d2x = d1x;\r
- d2y = 0;\r
- }\r
- else\r
- {\r
- d2x = 0;\r
- d2y = d1y;\r
- m = n;\r
- n = abs(u);\r
- }\r
- s = (m / 2);\r
- \r
- inreg.h.al = (unsigned char)col;\r
- inreg.h.bh = 0x00;\r
- inreg.h.ah = 0x0C;\r
- for (i = 0; i <= m; i++)\r
- {\r
- inreg.x.cx = (unsigned int)(a);\r
- inreg.x.dx = (unsigned int)(b);\r
- int86(0x10, &inreg, &outreg);\r
- s += n;\r
- if (s >= m)\r
- {\r
- s -= m;\r
- a += d1x;\r
- b += d1y;\r
- }\r
- else\r
- {\r
- a += d2x;\r
- b += d2y;\r
- }\r
- }\r
- }\r
- \r
- \r
-\r
-This circle drawing function uses in-line assembler to speed up the\r
-drawing process. It can easily be replaced with inreg and outreg\r
-parameters as in the other functions, or the other functions can be\r
-changed to in-line assembler. Both methods are shown to illustrate\r
-different ways of achieving the same result.\r
-\r
-\r
- /* Servile Software Library For Clipper */\r
- \r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- \r
- void plot(int x, int y, unsigned char colour)\r
- {\r
- asm mov al , colour;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- \r
- int getmode()\r
- {\r
- /* Returns current video mode and number of columns in ncols\r
- */\r
- \r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- return(_AL);\r
- }\r
- \r
- \r
- CLIPPER s_circle()\r
- {\r
- int x_centre;\r
- int y_centre;\r
- int radius;\r
- int colour;\r
- int x,y,delta;\r
- int startx,endx,x1,starty,endy,y1;\r
- int asp_ratio;\r
- \r
- x_centre = _parni(1);\r
- y_centre = _parni(2);\r
- radius = _parni(3);\r
- colour = _parni(4);\r
- \r
- \r
- \r
- \r
- if (getmode() == 6)\r
- asp_ratio = 22;\r
- else\r
- asp_ratio = 13;\r
- \r
- y = radius;\r
- delta = 3 - 2 * radius;\r
- \r
- for(x = 0; x < y; )\r
- {\r
- starty = y * asp_ratio / 10;\r
- endy = (y + 1) * asp_ratio / 10;\r
- startx = x * asp_ratio / 10;\r
- endx = (x + 1) * asp_ratio / 10;\r
- \r
- for(x1 = startx; x1 < endx; ++x1)\r
- {\r
- plot(x1+x_centre,y+y_centre,colour);\r
- plot(x1+x_centre,y_centre - y,colour);\r
- plot(x_centre - x1,y_centre - y,colour);\r
- plot(x_centre - x1,y + y_centre,colour);\r
- }\r
- \r
- for(y1 = starty; y1 < endy; ++y1)\r
- {\r
- plot(y1+x_centre,x+y_centre,colour);\r
- plot(y1+x_centre,y_centre - x,colour);\r
- plot(x_centre - y1,y_centre - x,colour);\r
- plot(x_centre - y1,x + y_centre,colour);\r
- }\r
- \r
- if (delta < 0)\r
- delta += 4 * x + 6;\r
- else\r
- {\r
- delta += 4*(x-y)+10;\r
- y--;\r
- }\r
- x++;\r
- }\r
- \r
- \r
- \r
- \r
- if(y)\r
- {\r
- starty = y * asp_ratio / 10;\r
- endy = (y + 1) * asp_ratio / 10;\r
- startx = x * asp_ratio / 10;\r
- endx = (x + 1) * asp_ratio / 10;\r
- for(x1 = startx; x1 < endx; ++x1)\r
- {\r
- plot(x1+x_centre,y+y_centre,colour);\r
- plot(x1+x_centre,y_centre - y,colour);\r
- plot(x_centre - x1,y_centre - y,colour);\r
- plot(x_centre - x1,y + y_centre,colour);\r
- }\r
- \r
- for(y1 = starty; y1 < endy; ++y1)\r
- {\r
- plot(y1+x_centre,x+y_centre,colour);\r
- plot(y1+x_centre,y_centre - x,colour);\r
- plot(x_centre - y1,y_centre - x,colour);\r
- plot(x_centre - y1,x + y_centre,colour);\r
- }\r
- }\r
- }\r
- \r
-\r
-The Clipper facilities for displaying text on the screen, @....SAY and ?\r
-do not work when the monitor is in graphics mode. You then need the\r
-following function to allow text to be displayed in a graphics mode;\r
-\r
- \r
- /* Servile Software Library For Clipper */\r
- \r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- int sgetmode(int *ncols)\r
- {\r
- /* Returns current video mode and number of columns in ncols\r
- */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 0x0F;\r
- int86(0x10, &inreg, &outreg);\r
- *ncols = outreg.h.ah;\r
- return(outreg.h.al);\r
- }\r
- \r
- void at(int row, int col)\r
- {\r
- asm mov bh , 0;\r
- asm mov dh , row;\r
- asm mov dl , col;\r
- asm mov ah , 02h;\r
- asm int 10h;\r
- }\r
- \r
- \r
- \r
- \r
- CLIPPER s_say()\r
- {\r
- char *output;\r
- int p = 0;\r
- unsigned char page;\r
- unsigned char text;\r
- int n;\r
- int r;\r
- int c;\r
- int attribute;\r
- \r
- output = _parc(1);\r
- r = _parni(2);\r
- c = _parni(3);\r
- attribute = _parni(4);\r
- \r
- asm mov ah , 0Fh;\r
- asm int 10h;\r
- asm mov page, bh;\r
- \r
- sgetmode(&n);\r
- \r
- at(r,c);\r
- \r
- while (output[p])\r
- {\r
- text = output[p++];\r
- asm mov bh , page;\r
- asm mov bl , attribute;\r
- asm mov cx , 01h;\r
- asm mov ah , 09h;\r
- asm mov al , text;\r
- asm int 10h;\r
- c++;\r
- if (c < (n-1))\r
- at( r, c);\r
- else\r
- {\r
- c = 0;\r
- at(++r,0);\r
- }\r
- }\r
- }\r
- \r
-\r
-\r
-\r
-When drawing graphs, it is often required to fill in areas of the graph\r
-in different patterns. This is a graphics function to fill boundered\r
-shapes with a specified hatching pattern providing a means to achieve\r
-more usable graphs;\r
-\r
-\r
- \r
- /* Servile Software Library For Clipper */\r
- \r
- #include <nandef.h>\r
- #include <extend.h>\r
- #include <dos.h>\r
- \r
- int pixset(int x, int y)\r
- {\r
- /* Returns the colour of the specified pixel */\r
- \r
- asm mov cx ,x;\r
- asm mov dx ,y;\r
- asm mov ah ,0Dh;\r
- asm int 10h;\r
- return(_AL);\r
- }\r
- \r
- CLIPPER s_fill()\r
- {\r
- /* Fill a boundered shape using a hatch pattern */\r
- \r
- int mode;\r
- int xa;\r
- int ya;\r
- int bn;\r
- int byn;\r
- int x;\r
- int y;\r
- int col;\r
- int pattern;\r
- int maxx;\r
- int maxy;\r
- int hatch[10][8] = { 255,255,255,255,255,255,255,255,\r
- 128,64,32,16,8,4,2,1,\r
- 1,2,4,8,16,32,64,128,\r
- 1,2,4,8,8,4,2,1,\r
- 238,238,238,238,238,238,238,238,\r
- 170,85,170,85,170,85,170,85,\r
- 192,96,48,24,12,6,3,1,\r
- 62,62,62,0,227,227,227,0,\r
- 129,66,36,24,24,36,66,129,\r
- 146,36,146,36,146,36,146,36};\r
- \r
- /* Patterns for fill, each integer describes a row of dots */\r
- \r
- \r
- \r
- \r
- x = _parni(1);\r
- y = _parni(2);\r
- col = _parni(3);\r
- pattern = _parni(4);\r
- \r
- mode = getmode();\r
- \r
- switch(mode)\r
- {\r
- case 0:\r
- case 1:\r
- case 2:\r
- case 3: break;\r
- case 4:\r
- case 9:\r
- case 13:\r
- case 19:\r
- case 5: maxx = 320;\r
- maxy = 200;\r
- break;\r
- case 14:\r
- case 10:\r
- case 6: maxx = 640;\r
- maxy = 200;\r
- break;\r
- case 7: maxx = 720;\r
- maxy = 400;\r
- break;\r
- case 8: maxx = 160;\r
- maxy = 200;\r
- break;\r
- case 15:\r
- case 16: maxx = 640;\r
- maxy = 350;\r
- break;\r
- case 17:\r
- case 18: maxx = 640;\r
- maxy = 480;\r
- break;\r
- \r
- }\r
- \r
- xa = x;\r
- ya = y; /* Save Origin */\r
- \r
- if(pixset(x,y))\r
- return;\r
- \r
- bn = 1;\r
- byn = 0;\r
- \r
- \r
- \r
- \r
- do\r
- {\r
- if (hatch[pattern][byn] != 0)\r
- { /* If blank ignore */\r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x--;\r
- bn <<= 1;\r
- if (bn > 128)\r
- bn = 1;\r
- }\r
- while(!pixset(x,y) && (x > -1));\r
- \r
- x = xa + 1;\r
- bn = 128;\r
- \r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x++;\r
- bn >>=1;\r
- if (bn <1)\r
- bn = 128;\r
- }\r
- while((!pixset(x,y)) && (x <= maxx));\r
- }\r
- x = xa;\r
- y--;\r
- bn = 1;\r
- byn++;\r
- if (byn > 7)\r
- byn = 0;\r
- \r
- \r
- \r
- \r
- }\r
- while(!pixset(x,y) && ( y > -1));\r
- \r
- /* Now travel downwards */\r
- \r
- y = ya + 1;\r
- \r
- byn = 7;\r
- bn = 1;\r
- do\r
- {\r
- /* Travel left */\r
- if (hatch[pattern][byn] !=0)\r
- {\r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x--;\r
- bn <<= 1;\r
- if (bn > 128)\r
- bn = 1;\r
- }\r
- while(!pixset(x,y) && (x > -1));\r
- \r
- /* Back to x origin */\r
- x = xa + 1 ;\r
- bn = 128;\r
- \r
- /* Travel right */\r
- do\r
- {\r
- if ((bn & hatch[pattern][byn]) == bn)\r
- {\r
- asm mov al , col;\r
- asm mov bh , 00;\r
- asm mov cx , x;\r
- asm mov dx , y;\r
- asm mov ah , 0Ch;\r
- asm int 10h;\r
- }\r
- x++;\r
- bn >>=1;\r
- \r
- \r
- \r
- \r
- if (bn <1)\r
- bn = 128;\r
- }\r
- while((!pixset(x,y)) && (x <= maxx));\r
- }\r
- x = xa;\r
- bn = 1;\r
- y++;\r
- byn--;\r
- if (byn < 0)\r
- byn = 7;\r
- }\r
- while((!pixset(x,y)) && (y <= maxy));\r
- }\r
- \r
- \r
- \r
- \r
- SPELL - AN EXAMPLE PROGRAM\r
-\r
-It has been said that example programs provide a good way of learning a\r
-new computer language. On that basis the following simple program is\r
-offered as an example of making use of the dynamic memory allocation\r
-provided by DOS.\r
-\r
-This is a spell checker for ASCII (text) documents, written in, and\r
-making use of Borland's Turbo C text graphics facilities for displaying\r
-windows of text.\r
-\r
-\r
- /* Spell checker for ascii documents */\r
- /* Compile with -mc (compact memory model) and unsigned characters\r
- */\r
- \r
- \r
- #include <stdio.h>\r
- #include <conio.h>\r
- #include <fcntl.h>\r
- #include <io.h>\r
- #include <dos.h>\r
- #include <string.h>\r
- #include <alloc.h>\r
- #include <ctype.h>\r
- #include <stdlib.h>\r
- #include <bios.h>\r
- #include <dir.h>\r
- #include <stat.h>\r
- \r
- #define ON 0x06\r
- #define OFF 0x20\r
- #define MAXIMUM 15000\r
- #define WORDLEN 20\r
- #define LEFTMARGIN 1\r
- \r
- union REGS inreg,outreg;\r
- \r
- char *dicname;\r
- char *dic[MAXIMUM]; /* Array of text lines */\r
- char word[31];\r
- char comp[31];\r
- char fname[160];\r
- int lastelem;\r
- char changed;\r
- char *ignore[100];\r
- int lastign;\r
- int insert;\r
- int n;\r
- int bp;\r
- int mp;\r
- int tp;\r
- int result;\r
- \r
- \r
- \r
- \r
- char *text;\r
- char *textsav;\r
- \r
- void AT(int, int);\r
- void BANNER(void);\r
- int COMPARE(void);\r
- void CORRECT(void);\r
- void FATAL(char *);\r
- void FILERR(char *);\r
- void GETDIC(void);\r
- void IGNORE(void);\r
- void INSERT(void);\r
- int MATCHSTR(char *, char *);\r
- void SPELL(void);\r
- void UPDATE(void);\r
- \r
- void CURSOR(char status)\r
- {\r
- /* Toggle cursor display on and off */\r
- \r
- union REGS inreg,outreg;\r
- \r
- inreg.h.ah = 1;\r
- inreg.h.ch = (unsigned char)status;\r
- inreg.h.cl = 7;\r
- int86(0x10,&inreg,&outreg);\r
- }\r
- \r
- void DISPLAY(char *text)\r
- {\r
- /* Display 'text' expanding tabs and newline characters */\r
- \r
- while(*text)\r
- {\r
- switch(*text)\r
- {\r
- case '\n': cputs("\r\n");\r
- break;\r
- case '\t': cputs(" ");\r
- break;\r
- default: putch(*text);\r
- }\r
- text++;\r
- }\r
- }\r
- \r
- \r
- \r
- \r
- void GETDIC()\r
- {\r
- /* Read dictionary into memory */\r
- \r
- FILE *fp;\r
- char *p;\r
- int poscr;\r
- int handle;\r
- \r
- window(1,22,80,24);\r
- clrscr();\r
- gotoxy(28,2);\r
- cprintf("Reading Dictionary....");\r
- \r
- changed = 0;\r
- lastelem = 0;\r
- \r
- dicname = searchpath("spell.dic");\r
- handle = open(dicname,O_RDWR);\r
- if (handle < 0)\r
- FILERR("spell.dic");\r
- \r
- fp = fdopen(handle,"r");\r
- if (fp == NULL)\r
- FILERR("spell.dic");\r
- \r
- do\r
- {\r
- dic[lastelem] = calloc(WORDLEN,1);\r
- if (dic[lastelem])\r
- {\r
- p = fgets(dic[lastelem],79,fp);\r
- /* Remove carriage return from end of text line */\r
- poscr = (int)strlen(dic[lastelem]) - 1;\r
- if (dic[lastelem][poscr] == '\n')\r
- dic[lastelem][poscr] = 0;\r
- }\r
- else\r
- FATAL("Unable To Allocate Memory");\r
- }\r
- while((p != NULL) && (lastelem++ < MAXIMUM));\r
- \r
- lastelem--;\r
- \r
- fclose(fp);\r
- }\r
- \r
- \r
- \r
- \r
- void UPDATE()\r
- {\r
- FILE *fp;\r
- int n;\r
- \r
- if (changed)\r
- {\r
- window(1,22,80,24);\r
- clrscr();\r
- gotoxy(27,2);\r
- cprintf("Updating Dictionary....");\r
- \r
- fp = fopen(dicname,"w+");\r
- if (fp == NULL)\r
- FILERR("spell.dic");\r
- \r
- for(n = 0; n <= lastelem; n++)\r
- fprintf(fp,"%s\n",dic[n]);\r
- \r
- fclose(fp);\r
- }\r
- }\r
- \r
- void IGNORE()\r
- {\r
- /* Add a word to the ignore table */\r
- \r
- if (lastign < 100)\r
- {\r
- ignore[lastign] = calloc(strlen(word) + 1,1);\r
- if (ignore[lastign])\r
- strcpy(ignore[lastign++],comp);\r
- else\r
- {\r
- clrscr();\r
- cprintf("No available memory for new words!\r\nPress\r
- A key....");\r
- bioskey(0);\r
- }\r
- }\r
- else\r
- {\r
- clrscr();\r
- cprintf("No available memory for new words!\r\nPress A\r
- key....");\r
- bioskey(0);\r
- }\r
- }\r
- \r
- \r
- \r
- \r
- void FATAL(char *text)\r
- {\r
- /* Fatal error drop out */\r
- \r
- textcolor(LIGHTGRAY);\r
- textbackground(BLACK);\r
- window(1,1,80,25);\r
- clrscr();\r
- printf("SERVILE SOFTWARE\n\nSPELL V1.7\nFATAL ERROR:\r
- %s\n\n",text);\r
- CURSOR(ON);\r
- exit(0);\r
- }\r
- \r
- void FILERR(char *fname)\r
- {\r
- char text[60];\r
- \r
- strcpy(text,"Unable To Access: ");\r
- strcat(text,fname);\r
- FATAL(text);\r
- }\r
- \r
- int COMPARE()\r
- {\r
- char **p;\r
- \r
- /* Check Ignore table */\r
- for(p = ignore; p <= &ignore[lastign]; p++)\r
- if (strcmp(comp,*p) == 0)\r
- return(1);\r
- \r
- /* Binary search of dictionary file */\r
- bp = 0;\r
- tp = lastelem;\r
- mp = (tp + bp) / 2;\r
- \r
- \r
- \r
- \r
- while((result = strcmp(dic[mp],comp)) != 0)\r
- {\r
- if (mp >= tp)\r
- {\r
- /* Not found! */\r
- insert = mp;\r
- if (result > 0)\r
- insert--;\r
- return(0);\r
- }\r
- if (result < 0)\r
- bp = mp + 1;\r
- else\r
- tp = mp - 1;\r
- \r
- mp = (bp + tp) / 2;\r
- }\r
- return(1);\r
- }\r
- \r
- void INSERT()\r
- {\r
- int n;\r
- \r
- changed = 1;\r
- lastelem++;\r
- n = lastelem;\r
- \r
- dic[n] = calloc(WORDLEN,1);\r
- \r
- if (dic[n] == NULL)\r
- {\r
- clrscr();\r
- cprintf("No available memory for new words!\r\nPress A\r
- key....");\r
- bioskey(0);\r
- free(dic[n]);\r
- lastelem--;\r
- return;\r
- }\r
- \r
- while(n > (insert + 1))\r
- {\r
- strcpy(dic[n],dic[n-1]);\r
- n--;\r
- };\r
- \r
- strcpy(dic[insert + 1],comp);\r
- }\r
- \r
- \r
- \r
- \r
- void SPELL()\r
- {\r
- FILE *target;\r
- FILE *source;\r
- char *p;\r
- char *x;\r
- char temp[256];\r
- char dat1[1250];\r
- char dat2[1250];\r
- int c;\r
- int m;\r
- int found;\r
- int curpos;\r
- int key;\r
- int row;\r
- int col;\r
- int srow;\r
- int scol;\r
- \r
- window(1,1,80,20);\r
- textcolor(BLACK);\r
- textbackground(WHITE);\r
- \r
- /* Open temporary file to take spell checked copy */\r
- target = fopen("spell.$$$","w+");\r
- \r
- source = fopen(fname,"r");\r
- \r
- if (source == NULL)\r
- FILERR(fname);\r
- \r
- lastign = 0;\r
- \r
- do\r
- {\r
- clrscr();\r
- \r
- text = dat1;\r
- \r
- p = text;\r
- \r
- textsav = dat2;\r
- \r
- strcpy(text,"");\r
- \r
- /* Display read text */\r
- row = wherey();\r
- col = wherex();\r
- \r
- \r
- \r
- \r
- for(m = 0; m < 15; m++)\r
- {\r
- x = fgets(temp,200,source);\r
- if (x)\r
- {\r
- strcat(text,temp);\r
- DISPLAY(temp);\r
- }\r
- if (wherey() > 18)\r
- break;\r
- }\r
- \r
- /* return cursor to start position */\r
- gotoxy(col,row);\r
- \r
- do\r
- {\r
- memset(word,32,30);\r
- curpos = 0;\r
- do\r
- {\r
- c = *text++;\r
- if ((isalpha(c)) || (c == '-') && (curpos != 0))\r
- word[curpos++] = c;\r
- }\r
- while(((isalpha(c)) || (c == '-') && (curpos != 0))\r
- && (curpos < 30));\r
- word[curpos] = 0;\r
- strcpy(comp,word);\r
- strupr(comp);\r
- \r
- if (*comp != 0)\r
- {\r
- found = COMPARE();\r
- if (!found){\r
- textbackground(RED);\r
- textcolor(WHITE);\r
- }\r
- }\r
- else\r
- found = 1;\r
- \r
- srow = wherey();\r
- scol = wherex();\r
- \r
- cputs(word);\r
- textbackground(WHITE);\r
- textcolor(BLACK);\r
- \r
- \r
- \r
- \r
- \r
- switch(c)\r
- {\r
- case '\n': cputs("\r\n");\r
- break;\r
- case '\t': cputs(" ");\r
- break;\r
- default: putch(c);\r
- }\r
- \r
- row = wherey();\r
- col = wherex();\r
- \r
- if (!found)\r
- {\r
- window(1,22,80,24);\r
- clrscr();\r
- cputs("Unknown word ");\r
- textcolor(BLUE);\r
- cprintf("%s ",word);\r
- textcolor(BLACK);\r
- cputs("[A]dd [I]gnore [C]orrect [S]kip");\r
- do\r
- {\r
- key = toupper(getch());\r
- if (key == 27)\r
- key = 'Q';\r
- }\r
- while(strchr("AICSQ",key) == NULL);\r
- \r
- switch(key)\r
- {\r
- case 'A':INSERT();\r
- break;\r
- \r
- case 'C':CORRECT();\r
- break;\r
- \r
- case 'I':IGNORE();\r
- break;\r
- }\r
- \r
- \r
- \r
- \r
- if (key == 'C')\r
- {\r
- clrscr();\r
- gotoxy(1,1);\r
- strcpy(textsav,--text);\r
- /* Delete old word */\r
- text -= strlen(comp);\r
- *text = 0;\r
- /* Insert new word */\r
- strcat(text,word);\r
- /* Append remainder of text */\r
- strcat(text,textsav);\r
- text += strlen(word);\r
- text++;\r
- /* Length of text may have changed ! */\r
- if (strlen(word) < strlen(comp))\r
- col -= (strlen(comp) - strlen(word));\r
- window(1,1,80,20);\r
- clrscr();\r
- DISPLAY(p);\r
- }\r
- else\r
- {\r
- clrscr();\r
- gotoxy(29,2);\r
- cputs("Checking Spelling....");\r
- window(1,1,80,20);\r
- gotoxy(scol,srow);\r
- cputs(word);\r
- }\r
- window(1,1,80,20);\r
- gotoxy(col,row);\r
- }\r
- }\r
- while((*text) && (key != 'Q'));\r
- fprintf(target,"%s",p);\r
- }\r
- while((x != NULL) && (key != 'Q'));\r
- \r
- window(1,22,80,24);\r
- clrscr();\r
- gotoxy(27,2);\r
- cprintf("Writing Updated File....");\r
- \r
- \r
- \r
- \r
- do\r
- {\r
- p = fgets(temp,200,source);\r
- if (p)\r
- fprintf(target,"%s",temp);\r
- }\r
- while(p);\r
- \r
- fclose(target);\r
- fclose(source);\r
- \r
- /* Now transfer spell.$$$ to fname */\r
- unlink(fname);\r
- rename("SPELL.$$$",fname);\r
- }\r
- \r
- void CORRECT()\r
- {\r
- /* Locate a good match and return word */\r
- \r
- char text[51];\r
- int m;\r
- int n;\r
- int key;\r
- \r
- window(1,22,80,24);\r
- clrscr();\r
- gotoxy(25,2);\r
- cprintf("Searching For Alternatives....");\r
- \r
- /* Remove any pending key strokes from keyboard buffer */\r
- while(kbhit())\r
- getch();\r
- \r
- for(n = 0; n <= lastelem; n++)\r
- {\r
- if (MATCHSTR(dic[n],comp))\r
- {\r
- strcpy(text,dic[n]);\r
- if (strlen(word) <= strlen(text))\r
- {\r
- for (m = 0; m < strlen(word); m++)\r
- {\r
- if (isupper(word[m]))\r
- text[m] = toupper(text[m]);\r
- else\r
- text[m] = tolower(text[m]);\r
- }\r
- \r
- \r
- \r
- \r
- for(m = strlen(word); m < strlen(text); m++)\r
- if (isupper(word[strlen(word)]))\r
- text[m] = toupper(text[m]);\r
- else\r
- text[m] = tolower(text[m]);\r
- }\r
- else\r
- {\r
- for (m = 0; m < strlen(text); m++)\r
- {\r
- if (isupper(word[m]))\r
- text[m] = toupper(text[m]);\r
- else\r
- text[m] = tolower(text[m]);\r
- }\r
- }\r
- clrscr();\r
- cprintf("Replace ");\r
- textcolor(BLUE);\r
- cprintf("%s ",word);\r
- textcolor(BLACK);\r
- cprintf("With ");\r
- textcolor(BLUE);\r
- cprintf("%s",text);\r
- textcolor(BLACK);\r
- cprintf(" Yes No Continue");\r
- do\r
- {\r
- key = toupper(getch());\r
- }\r
- while(strchr("YNC",key) == NULL);\r
- if (key == 'Y')\r
- {\r
- strcpy(word,text);\r
- return;\r
- }\r
- clrscr();\r
- gotoxy(25,2);\r
- cprintf("Searching For Alternatives....");\r
- \r
- /* Remove any pending key strokes from keyboard\r
- buffer */\r
- while(kbhit())\r
- getch();\r
- \r
- if (key == 'C')\r
- return;\r
- }\r
- }\r
- clrscr();\r
- gotoxy(23,2);\r
- \r
- \r
- \r
- \r
- cprintf("NO ALTERNATIVES FOUND! (Press a key)");\r
- bioskey(0);\r
- return;\r
- }\r
- \r
- \r
- int MATCHSTR(char *src, char *tgt)\r
- {\r
- /* Compare two words and return non zero if they are similar */\r
- \r
- int match;\r
- int result;\r
- int strsrc;\r
- int strtgt;\r
- int longest;\r
- \r
- strtgt = strlen(strupr(tgt));\r
- strsrc = strlen(strupr(src));\r
- \r
- longest = max(strtgt,strsrc);\r
- \r
- match = 0;\r
- \r
- if(strtgt > strsrc)\r
- {\r
- for(; *src ; match += (*src++ == *tgt++))\r
- ;\r
- }\r
- else\r
- {\r
- for(; *tgt ; match += (*src++ == *tgt++))\r
- ;\r
- }\r
- \r
- result = (match * 100 / longest);\r
- \r
- /* result holds percentage similarity */\r
- \r
- if (result > 50)\r
- return(1);\r
- return(0);\r
- }\r
- \r
- void AT(int row, int col)\r
- {\r
- /* Position the text cursor */\r
- inreg.h.bh = 0;\r
- inreg.h.dh = row;\r
- inreg.h.dl = col;\r
- inreg.h.ah = 0x02;\r
- int86 (0x10, &inreg, &outreg);\r
- }\r
- \r
- void WRTCHA (unsigned char ch, unsigned char attrib, int num)\r
- {\r
- /* Display a character num times in colour attrib */\r
- /* via the BIOS */\r
- \r
- inreg.h.al = ch;\r
- inreg.h.bh = 0;\r
- inreg.h.bl = attrib;\r
- inreg.x.cx = num;\r
- inreg.h.ah = 0x09;\r
- int86 (0x10, &inreg, &outreg);\r
- }\r
- \r
- void SHADE_BLOCK(int left,int top,int right,int bottom)\r
- {\r
- int c;\r
- \r
- AT(bottom,right);\r
- WRTCHA(223,56,1);\r
- AT(top,right);\r
- WRTCHA('á',7,1);\r
- for (c = top+1; c < bottom; c++)\r
- {\r
- AT(c,right);\r
- WRTCHA(' ',7,1);\r
- }\r
- AT(bottom,left+1);\r
- WRTCHA('\9a',7,right-left);\r
- }\r
- \r
- \r
- \r
- \r
- void BOX(int l, int t, int r, int b)\r
- {\r
- /* Draws a single line box around a described area */\r
- \r
- int n;\r
- char top[81];\r
- char bottom[81];\r
- char tolc[5];\r
- char torc[5];\r
- char bolc[5];\r
- char borc[5];\r
- char hoor[5];\r
- \r
- sprintf(tolc,"%c",218);\r
- sprintf(bolc,"%c",192);\r
- sprintf(hoor,"%c",196);\r
- sprintf(torc,"%c",191);\r
- sprintf(borc,"%c",217);\r
- \r
- strcpy(top,tolc);\r
- strcpy(bottom,bolc);\r
- for(n = l + 1; n < r; n++)\r
- {\r
- strcat(top,hoor);\r
- strcat(bottom,hoor);\r
- }\r
- strcat(top,torc);\r
- strcat(bottom,borc);\r
- \r
- window(1,1,80,25);\r
- gotoxy(l,t);\r
- cputs(top);\r
- for (n = t + 1; n < b; n++)\r
- {\r
- gotoxy(l,n);\r
- putch(179);\r
- gotoxy(r,n);\r
- putch(179);\r
- }\r
- gotoxy(l,b);\r
- cputs(bottom);\r
- }\r
- \r
- \r
- \r
- \r
- void BANNER()\r
- {\r
- window (2,2,78,4);\r
- textcolor(BLACK);\r
- textbackground(GREEN);\r
- clrscr();\r
- SHADE_BLOCK(1,1,78,4);\r
- BOX(2,2,78,4);\r
- gotoxy(4,3);\r
- cprintf("Servile Software SPELL CHECKER V1.7\r
- (c)1992");\r
- }\r
- \r
- \r
- void main(int argc, char *argv[])\r
- {\r
- char *p;\r
- char tmp_name[160];\r
- char tmp_fname[160];\r
- \r
- if (argc != 2)\r
- {\r
- puts("\nERROR: Usage is SPELL document");\r
- exit(1);\r
- }\r
- else\r
- strcpy(fname,argv[1]);\r
- \r
- CURSOR(OFF);\r
- \r
- GETDIC();\r
- \r
- window(1,22,80,24);\r
- clrscr();\r
- gotoxy(28,2);\r
- cprintf("Making Backup File....");\r
- \r
- strcpy(tmp_fname,argv[1]);\r
- \r
- /* Remove extension from tmp_fname */\r
- p = strchr(tmp_fname,'.');\r
- if(p)\r
- *p = 0;\r
- \r
- /* Create backup file name using DOS */\r
- sprintf(tmp_name,"copy %s %s.!s! > NUL",argv[1],tmp_fname);\r
- \r
- system(tmp_name);\r
- \r
- window(1,1,80,25);\r
- \r
- \r
- \r
- \r
- textcolor(WHITE);\r
- textbackground(BLACK);\r
- clrscr();\r
- gotoxy(29,2);\r
- cprintf("Checking Spelling....");\r
- \r
- SPELL();\r
- \r
- UPDATE();\r
- window(1,1,80,25);\r
- textcolor(LIGHTGRAY);\r
- textbackground(BLACK);\r
- clrscr();\r
- CURSOR(ON);\r
- }\r
- \r
- \r
- APPENDIX A - USING LINK\r
-\r
-\r
-\r
-General Syntax:\r
-\r
- LINK [options] obj[,[exe][,[map][,[lib]]]][;]\r
-\r
-`obj' is a list of object files to be linked. Each obj file name must be\r
-separated by a + or a space. If you do not specify an extension, LINK\r
-will assume .OBJ. `exe' allows you to specify a name for the executable\r
-file. If this file name is ommited, LINK will use the first obj file name\r
-and suffix it with .EXE. `map' is an optional map file name. If you\r
-specify the name `NUL', no map file is produced. `lib' is a list of\r
-library files to link. LINK searches each library file and only links in\r
-modules which are referenced.\r
-\r
-\r
-eg:\r
-\r
- LINK filea+fileb,myfile,NUL;\r
-\r
-Links .obj files `filea.obj' and `fileb.obj' into .exe file `myfile.exe'\r
-with no map file produced. The ; at the end of the line tells LINK that\r
-there are no more parameters.\r
-\r
-\r
-\r
-Using Overlays\r
-\r
-Overlay .obj modules are specified by encasing the .obj name in\r
-parenthesis in the link line.\r
-\r
-eg:\r
-\r
- LINK filea + (fileb) + (filec),myfile,NUL;\r
-\r
-Will link filea.obj fileb.obj and filec.obj with modules fileb.obj and\r
-filec.obj as overlay code.\r
-\r
-\r
-Overlay modules must use FAR call/return instructions.\r
-\r
-\r
-Linker Options\r
-\r
-All LINK options commence with a forward slash `/'. Options which accept\r
-a number can accept a decimal number or a hex number prefixed 0X. eg:\r
-0X10 is interpreted as 10h, decimal 16.\r
-\r
-\r
-\r
-Pause during Linking (/PAU)\r
-\r
- Tells LINK to wait before writing the .exe file to disk. LINK\r
-displays a\r
- message and waits for you to press enter.\r
-\r
-Display Linker Process Information (/I)\r
-\r
- Tells LINK to display information about the link process.\r
-\r
-Pack Executable File (/E)\r
-\r
- Tells LINK to remove sequences of repeated bytes and to optimise the\r
-load-time\r
- relocation table before creating the executable file. Symbolic debug\r
- information is stripped out of the file.\r
-\r
-List Public Symbols (/M)\r
-\r
- Tells LINK to create a list of all public symbols defined in the\r
- object files\r
- in the MAP file.\r
-\r
-Include Line Numbers In Map File (/LI)\r
-\r
- Tells LINK to include line numbers and associated addresses of the\r
-source\r
- program in the MAP file.\r
-\r
-Preserve Case Sensitivity (/NOI)\r
-\r
- By default LINK treats uppercase and lowercase letters as the same.\r
-This\r
- option tells LINK that they are different.\r
-\r
-Ignore Default Libraries (/NOD)\r
-\r
- Tells LINK not to search any library specified in the object files\r
-to resolve\r
- external references.\r
-\r
-Controlling Stack Size (/ST:n)\r
-\r
- Specifies the size of the stack segment where 'n' is the number of\r
-bytes.\r
-\r
-Setting Maximum Allocation Space (/CP:n)\r
-\r
- Tells LINK to write the parameter 'n' into the exe file header. When\r
-the exe\r
- file is executed by DOS, 'n' 16 byte paragraphs of memory are\r
-reserved. If 'n'\r
- is less than the minimum required, it will be set to the minimum.\r
-This option\r
- is ESSENTIAL to free memory from the program. C programs free memory\r
- automatically on start-up, assembly language programs which want to\r
-use\r
- dynamic memory allocation must be linked with this option set to a\r
-minimum.\r
-\r
-Setting Maximum Number Of Segments (/SE:n)\r
-\r
- Tells LINK how many segments a program is allowed to have. The\r
-default is 128\r
- but 'n' can be any number between 1 and 3072.\r
-\r
-\r
-Setting Overlay Interrupt (/O:n)\r
-\r
- Tells LINK which interrupt number will be used for passing control\r
-to\r
- overlays. The default is 63. Valid values for 'n' are 0 through 255.\r
-\r
-Ordering Segments (/DO)\r
-\r
- Tells LINK to use DOS segment ordering. This option is also enabled\r
-by the\r
- MASM directive .DOSSEG.\r
-\r
-Controlling Data Loading (/DS)\r
-\r
- By default LINK loads all data starting at the low end of the data\r
-segment. At\r
- run time the DS register is set to the lowest possible address to\r
-allow the\r
- entire data segment to be used. This option tells LINK to load all\r
-data\r
- starting at the high end of the data segment.\r
-\r
-Control Exe File Loading (/HI)\r
-\r
- Tells LINK to place the exe file as high as possible in memory.\r
-\r
-Prepare for Debugging (/CO)\r
-\r
- Tells LINK to include symbolic debug information for use by\r
-codeview.\r
-\r
-Optimising Far Calls (/F)\r
-\r
- Tells LINK to translate FAR calls to NEAR calls where possible. This\r
-results\r
- in faster code.\r
-\r
-Disabling Far Call Optimisation (/NOF)\r
-\r
- Tells LINK not to translate FAR calls. This option is specified by\r
-default.\r
-\r
-Packing Contiguous Segments (/PAC:n)\r
-\r
- Tells LINK to group together neighbouring code segments, providing\r
-more\r
- oportunities for FAR call translation. 'n' specifies the maximum\r
-size of a\r
- segment. By default 'n' is 65530. This option is only relevant to\r
-obj files\r
- created using FAR calls.\r
-\r
-Disabling Segment Packing (/NOP)\r
-\r
- Disables segment packing. This option is specified by default.\r
-\r
-\r
-\r
-Using Response Files\r
-\r
-Linker options and file names may be specified in a response file. Each\r
-file list starting on a new line instead of being separated by a comma.\r
-\r
-eg:\r
-\r
- filea.obj fileb.obj\r
- myfile.exe\r
- NUL\r
- liba.lib libb.lib\r
- \r
-A response file is specified to LINK by prefixing the response file name\r
-with '@'.\r
-eg:\r
-\r
- LINK @response\r
-\r