# Developing Your Own 32-Bit Operating System — Chapter-29: CM32: A 32-Bit C Compiler - **Source**: /library/Developing Your Own 32-Bit Operating System/source-file.pdf - **PDF pages**: 719–740 - **Pages without text**: 719 --- CM32: A 32-Bit C Compiler [698 Part V ■ Additional Software CM stands for C-Minus. CM32 was written specifically to use while I was building the MMURTL computer operating system. Every change and fix that was made to the compiler, and some of the nonstandard extensions, are there specifically to support the operating system. I will not apologize for its incomplete state, because my real goal Was to build an operating system. CM32 was a necessary detour. As the name implies, this version of the C language is missing some pieces. CM32 started life as an early version of Dave Dunfield's Micro-C. I liked Mr. Dunfield's style, and I learned a lot from his code. The compiler is not very portable now, but portability wasn't my goal. He has given permission to include the source code on the CD-ROM. Permission is required from the authors as listed in the source code in order to use the compiler for any commercial pur- poses. Mr. Dunfield also let me include his introductory document on the C language (CIntro.doc in the \Dunfield directory on the CD-ROM). If you are not familiar with the C programming language, CIntro.doc is a very good introduction. It is geared to a subset of the C language, but CM32 is also a subset, so it should be valuable if you need to learn or brush-up on C. All of Mr. Dunfield's demo products are also included in the \Dunfield directory on CD-ROM. If you, or someone you know, does embedded systems work on 8- and 16-bit processors, you should take a look at these products. There are complete working tools included, along with a catalog of all his other goodies. The extentions I made to Micro-C all follow the guidelines presented in The C Programming Language, Second Edition (ANSI Q, by Brian W. Kernighan and Dennis M. Ritchie. You don't need this book to use CM32, but it was my guide for modifications to the compiler. After all, Kernighan and Ritchie (K&R) invented the language. There is a small amount of irony here, however, as CM32 doesn't support the original K&R style of function declarations. ANSI function prototypes are required. To write an operating system with a complete development environment, you have to start somewhere. I started with the Microsoft 5.1 Assembler, eventually gravitated to the Borland Turbo Assembler, still using MASM conventions, and eventually ended up using DASM, our own assembler. All the MMURTL kernel and hardware handling code is done in hand-coded assembler. Once the kernel was done, and I started on the device drivers, I knew I would need a 32-bit compiler to cut down development time on the rest of the software. Several compa- nies make excellent 32-bit compilers, but I also wanted to eventually port the compiler to the MMURTL environment. Quite frankly, the cost of the source code would be exorbitant, so I took a user-supported 16-bit compiler that generated assembler and worked from there. Talk about re-inventing the wheel. This set the MMURTL development effort back about four months, but what the heck. I certainly learned a whole lot. CM32 produces assembly language that is compatible with DASM, the assembler in the CD-ROM. Chapter 29 ■ CM32: A 32-Bit C Compiler Memory Usage and Addressing The code and library source are set up for the MMURTL OS. MMURTL's memory model allows 2GB segment addressing with 32-bit flat pointers. For compatibility with MS-DOS programming tools and source code, I went with the following conventions in CM32: char = 8 Bits (Byte) short = 16 Bits (word) int =16 Bits (word) long = 32 Bits (dword) I did this because of the wealth of source code available for MS-DOS. There is also a wealth of code available for UNIX, and in most UNIX compilers, an int is 32 bits. But far too much of it is so heavily UNIX specific, that rewriting it would be almost as fast as porting some of it. Important Internal Differences As you may know, C began on a machine with a flat memory architecture. The Intel 32-bit processors are built on a segmented architecture even though you can choose to ignore the segmentation. As described in the previous chapters, MMURTL pretty much ignores the seg- mentation capabilities, with one exception. The code and data segments are different even though they share the same linear address space in the operating system. This is why CM32 has support for 48-bit far calls, 16-bit selector, and 32-bit offsets. Something that has given the Intel world a fit is C stack handling, especially Pascal and PLM- 86 users. The Intel 32-bit processors have certain instructions to make stack operations easier, as well as making function-handling almost a pleasure. One such instruction is the RET XX, where XX is the number of bytes to remove from the stack prior to returning from the call. The UNIX/C convention has always been to have the caller remove his parameters (args) from the stack after the return. This allows for variable length argument lists. I didn't need this in MMURTL, as all the parameter lists to operating system calls are fixed length. This is a waste of good time-saving instructions. The MMURTL OS uses the RET XX, and removes the bytes before returning from all calls, so that's how I wrote the compiler. | 699 | Variable-Length Argument Lists CM32 does allow you to prototype a function with an ellipsis (...), in which case CM32 will build the calling function to remove the arguments. The included stdarg.h, along with the supporting library code, handles variable length arguments. Please follow the conventions for variable arguments documented in The C Programming Language, Second Edition. Details can be found in the in stdarg.h file with the included library source code. H Part V ■ Additional Software Some early C compilers were a little goofy when it came to retrieving parameters from the stack. They pop, pop, pop, until they get the parameter they want, or pop CX forever to remove them on return. Intel gave us a very clean way to get at stack parameters, and I use it. It's the frame pointer — EBP. Other C compilers also save certain registers before the call, and restore them after. CM32 saves none. One last stack-related point, and probably the most notorious, is the order in which param- eters are pushed on the stack. The C convention has been from right to left. I opted for left to right. This is called the PLM, or Pascal calling convention. Remember, if you use C in the recommended portable fashion, none of this will bother or concern you, we just thought you'd like to know. Of course, if you intend to add additional library support for CM32, you will have to know and understand it all. CM32 Language Specifics The CM32 compiler is a subset of the full ANSI-C language. It has several extentions, as well as short-comings. These are documented in the following sections. Far Calls One important extension CM32 has made to the C language is for the far function calls. All C compilers that work on Intel platforms usually have some type of far extensions. CM32 supports 48-bit far calls, and RETF XX instructions for local functions designated as far, that will be called from outside your current segment. See the following section, Far External Functions, for specifics on how to use the far storage class modifier with functions. Because MMURTL uses a separate virtual memory space for each program, we don't need far data pointers and don't support them. The only reason far calls are supported is to allow access to the MMURTL OS through 386/486 call gates. Interrupt Functions The interrupt class is used to designate the function as an interrupt service routine. It changes the standard entry and exit code for a function, producing the following: PUSHAD ;Your code POPAD IRETD Needless to say, don't call an interrupt function from your program. The results would be rather nasty. The PUSHAD and POPAD instructions save all registers and flags and restore them. Chapter 29 ■ CM32: A 32-Bit C Compiler Supported C Language Features The following C statements are supported by CM-32: if /else while do/while for break continue return goto switch /case /default {} The following operators are supported: / /= % %= ?: (tertiary operators) , (comma) 0 [ ] (array indexing) . -> (structure field access operators) sizeof () * for Indirection up to 7 levels & for "ADDRESS OF" including structures The following data types and modifiers are supported: struct (all members are packed) char (signed and unsigned) 8 bits short (signed and unsigned) 16 bits int (signed and unsigned) 16 bits long (signed and unsigned) 32 bits arrays (single and multidimensional) pointers (to all types including pointers) void The following storage classes, or modifiers to classes, are supported: signed (the default as per ANSI) unsigned extern -7Q2 Part V ■ Additional Software static const register far (for functions only) interrupt (for functions only) The following character constants are supported. Decimal, octal, and hex constants are also supported as character constants in expressions (127, 0177, 0x7f, \n, or Some Text\t, for ex- ample): \n Newline \r Carriage return \t Tab \b Backspace \f Formfeed \v Vertical Tab \177 Octal constants \x7f Hexadecimal constants \0 Null The following preprocessor commands are supported: #define (fully parameterized) #undef #include (nested to 5 levels) #ifdef #ifndef #else #endif #asm/#endasm (for inline assembly language) #pragma (properly ignored) In-line assembly code is supported with the #asm operator as defined above. It is simply placed line-for-line into the generated assembly language output file. Library Header File Access The standard include files which are listed inside angle brackets ( for example) should be located in the \CM32\INCLUDE directory on your current disk. Quoted includes ("MyHeader.h" for example) should specify the full path if the file is not lo- cated in the current directory. Limitations as Compared to ANSI The following items are not supported: Real numbers unions enumerated types bit fields Chapter 29 ■ CM32: A 32-Bit C Compiler type casts #if (with separate defined) #elif Structure Limitations Structures are supported including tags. Structure arrays are also supported, but the following limitations apply: Structures cannot be passed as arguments to functions. They cannot be returned from functions. They cannot be assigned to each other. Structures cannot be nested. The use of pointers to structures gets around all of these problems except the lack of nested structures. The length of a structure is exactly the sum of each member's size. The register class modifier doesn't do a lot, but it's recognized for compatibility. Automatic variables, variables local to functions, cannot be initialized. Far External Functions The far type modifier can be quite confusing to someone that has always worked on a machine with a flat memory architecture. If you are one of those people, the best analogy of far is that you are actually working on several machines, each with their own large flat memory space. Any time you want to access a function to some other machine's memory, you need the far modifier. For example, if you are calling a function that is not in your code segment in your machine's memory space if you will, you will have to tell the compiler that it is a far function. Far functions are those that you must call in another segment, or those in your segment that must be called from outside your segment. For example, the function GetMoney, a fictitious but useful system call that resides outside your current code segment, would have to be defined as being far in its function prototype in order for the compiler to generate a far call in assem- bler. Example follows of a prototype for a far call outside your segment: extern long far GetMoney ( void ) ; /* a useful but ficticious function */ Far functions cannot return far pointers. This is an intentional limitation of CM32 compiler. In fact, the only reason CM32 supports far calls is because MMURTL calls are made through call gates which require a unique selector to identify each call. 704 Part V ■ Additional Software This means that if you defined GetMoney as: extern far long GetMoney ( void ) ; the far modifier still applies to the function and not the long returned variable. Type Conversion and Pointers As mentioned above, CM32 does not support type casts. Consequently, type checking is re- laxed with pointer assignments. Also, type conversions are automatic, and follow these rules, which are ANSI compatible: 8 bit var = 1 6 bit var — The low-order eight bits of the 1 6-bit variable will be placed in the 8-bit variable. 16 bit var = 32 bit var — The low order 16 bits of the 32-bit variable will be placed in the 16-bit variable. 32 bit var = 1 6 bit var and 1 6 bit var = 8 bit var — The value is properly sign or zero extended before assignment. unsigned var = signed var — Signed variable is sign or zero extended if required, and placed into the unsigned variable. A char with the value of -1 assigned to an unsigned long will result in OxFFFFFFFF as you would expect. Through the conversion, -1 equals -1. All pointers are 32 bit and may be assigned, manipulated, and compared as unsigned long in- tegers. Initialization of Static Variables CM32 supports bracketed initialization with braces ({ }), as specified by ANSI. I have seen some compilers that simply can't seem to get it right. The following examples show my implemen- tation so there should be no confusion. If I'm doing it wrong, at least you'll how I do it. Examples of initialization ANSI style as defined in Kernighan and Ritchie's book. The following example defines a 3x5 array of characters: char c[][5] = { {"abc"}, {"def"}, {"ghi"} }; Stored in memory, it looks like this (15 bytes): 'a"b"c'0 0 'd' 'e' TOO 'g' 'h' 7 0 0 The next example defines a 3x1 array of integers. Chapter 29 ■ CM32: A 32-Bit C Compiler int x[] = {1,3,5}; Stored in memory, it looks like this (three integers) : 1 3 5 Both of the following examples define a 4x3 array of integers. Note the inside braces are op- tional, but serve a purpose if you can't define all the elements in each sub-array: int y1[4][3] = {1, 3, 5, 2, 4, 6, 3, 5, 7}; int y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; Both look like this stored in memory (12 integers) : 1 3 5 2 4 6 3 5 7 0 0 0 (Last index zeroed automatically) The following example is also a 4x3 array, but only the first value of each of the first indexes is initialized. This is where the inside braces are required: int y2[4][3] = { {1}, {2}, {3}, {4} }; Stored in memory, it looks like this (three integers): 1 0 0 2 0 0 3 0 0 4 0 0 Both of the following examples are 3x7 character arrays. They can be defined either way. The first dimension of the array is determined by how many initializers there are: char rgReserved[ ] [7] = { {"THE"}, {"BIG"}, {"DOGS"}, }; char rgTest[ ] [7] = { "THE", "BIG", "DOGS", }; H 706 Part V ■ Additional Software In memory, each of the strings is zero, filled out to the length specified in the second dimen- sion: T 'H' 'E' 0 0 0 0 'B' T 'G' 0 0 0 0 'D' 'O' 'G' 'S'0 0 0 The following is a 3x3 array of pointers. They point to literal strings that are stored in another location in memory: char *paTest[3] [3] = { {"ALIGN", "TEST",}, {"BYTE"}, {"CODE"}, }; In memory, this declaration stores an array of pointers, and it stores the strings that they point to. The undefined pointers in the array will be 0 (Null). This is how it looks in memory, where paTest[x] are points to the null-terminated strings stored in memory: pi p2 0 p3 0 0 p4 0 0 The next example stores a string. Note that with single character arrays, braces ({ }) are not needed. The characters are stored in memory terminated with a null. The dimension (size) of the string is determined by how many characters there are in the string. One null is added to the length. char testc[] = "This is a test! "; The following is also stored in memory as an array of characters, except the string is zero pad- ded out to the length specified in the index, which is 17. It is null terminated and the null is included in its length. char teste! [17] = "This is a test! "; Other Implementation-Dependent Information The shift right function zero fills on all operations. Structures are packed. There are no fills for word, or double word, alignment. This means you can use a memory copy function to make copies of structures. You may not assign structures, and they may not be passed as parameters. Chapter 29 ■ CM32: A 32-Bit C Compiler 707 Using CM32 To compile a C program with CM32 you enter the name of the source file first, and option- ally, the name of the destination ASM file next, then any command line options (switches), for example: CM32 MyFile.c MyFile.asm /L /S The compiler will then process the file and produce the assembly language file you specified. If you left out the destination assembly file name, it will use the source file name with .ASM as the output file. Errors will be sent to stdout, which is the screen, unless you use the /L switch which directs all errors to a list file named SourceFile.LST. Command Line Options CM32 has the following command line options available. They are not case sensitive (/s = /S for example): /E (Embedded source mode) This will embed the C source code in the assembly language output. The data declarations from your source will all be packed after the ASM data declara- tions, while each line of code precedes the assembly language statements that it translates into. Using this option helps to see how the compiler handles things, which is good for additional hand optimization. /G (Generate separate files) This tells the compiler to generate a separate code and data as- sembly language file as the output. This is useful if you are including the compiler output into other assembly language files. Output of all segment definition and all assume statements is suppressed with this option. The files are named .DAS and .CAS (data and code, respectively). / L (List file) This tells the compiler to direct all errors to a file. The filename is the same as your source file, except the file extension is .LST. /N (No Optimization) This tells the compiler to skip the optimizer phase of the compilation. I recommend you always optimize. This optimization is for both speed and size and to be honest, the generated code stinks without it. It is just barely acceptable with it. /S (Suppress herald) This suppresses the compiler name and version herald that is displayed when the compiler is first executed. If you use the /L option, the only text displayed on the standard output device, the screen, will be fatal compiler errors. /W (Warnings ON) This causes the compiler to output warnings for certain practices such as variables that are never referenced, and type conversions that look odd. 708 Part V ■ Additional Software CM32 searches the \CM32\INCLUDE directory on your current disk path for standard #include files. Library Functions CM32 is limited in the library functions that are provided. The implementation is closer to a free-standing version than a hosted version. You can add library functions if you like. The sup- ported functions and macros are listed under each header file in Listing 29.1. Listing 29.1. Supported library functions. extern FILE extern long extern long extern char extern long extern long extern long extern long extern long extern long objsize, extern long objsize, *fopen(char *name, char *mode); *fclose(FILE *stream); fgetc(FILE *stream); *fgets(char *s, long n, FILE *stream) fputc(long c, FILE *stream) ; fputs(const char *s, FILE *stream) ; printf (char *fmt, ...); sprintf (char *s, char *fmt, ...); fprintf (FILE *stream, char *fmt, ...) fread(void pData, size_t site_t nobj, FILE *stream); fwrite(void pData, size_t site_t nobj, FILE *stream); extern exit(long signal) extern extern extern extern extern extern extern extern extern extern extern extern long long long long long long long long long long long long iscntrl( isspace( isdigit( isupper( islower( ispunctj isalpha( isxdigit isalnum( isgraph( toupper( tolower( long c) long c) long c) long c) long c) long c) long c) (long c long c) long c) long c) long c) extern char *strcpy(char *s, const char *ct); extern char *strncpy(char *s, const char *ct, long n); extern char *strcat (char *s, const char *ct); extern char *strncat (char *s, const char *ct, long n); extern long strcmp( const char *cs, const char *ct); extern long strncmp(const char *cs, const char *ct, long n) Chapter 29 ■ CM32: A 32-Bit C Compiler extern long strlen(char *cs); extern char *strchr(const char *cs, int *c, long n); (The following are macros in stdarg.h ) va_list unsigned long * va_start(ap,arg) va_arg(ap,type) va_end(ap) extern void exit(long error); extern void stdentry(void) ; Library Code Examples Two samples of the library code are shown below to demonstrate the interface with the MMURTL operating system. Fileio.c Fileio.c implements many of the file calls in stdio.h. It is a good example of the proper use of the MMURTL file system service. See Listing 29.2. Listing 29.2. Standard file I/O for MMURTL. /* fileio.c #include #include #include "C: \OSSource\MFiles.h" #define ErcDupName 226 /* Name exists as a file or dir already */ static long error_num = 0; /*************** f0pen *************** opens a file in stream mode for reading, or reading and writing (modify mode). FILE *fopen(char *name, char *mode) { long handle, lmode, fcreate, fdiscard, ere, fappend; ere = 0; handle = 0; fappend = 0; fcreate = 0; fdiscard = 0; ■71^ PartV ■ Additional Software Listing 29.2. continued if ( *mode== ' r ' ) lmode = 0; else if (*mode=='w' ) { lmode = 1 ; f create = 1 ; f discard = 1 ; } else if (*mode=='a' ) { f append = 1 ; f create = 1 ; lmode = 1 ; } else return (0); /* see if they want to update also */ if ((mode[1] == '+') || (mode[2] == '+')) lmode = 1 ; if (fcreate) /* try to create with archive bit set */ ere = CreateFile(name, strlen(name) , 0x20); if (ere == ErcDupName) ere = 0; if (!erc) ere = OpenFile(name, strlen(name) , lmode, 1, &handle); if (ere) { error_num = ere; handle = 0; } if ((!erc) && (fappend)) { ere = SetFileLFA(handle, Oxffffffff); /* EOF */ } if ((!erc) && (fdiscard)) { ere = SetFileSize(handle, 0); /* */ } return (handle); } /*************** rename ***************/ int rename(char *oldname, char *newname) { long ere; ere = RenameFile(oldname, strlen(oldname) , newname, strlen(newname) ) if (ere) { errornum = ere; return( -1 ) ; } Chapter 29 ■ CM32: A 32-Bit C Compiler \m\ return(0) ; } I*************** rem0ve ***************/ int remove(char *name) { long handle, ere; ere = OpenFile(name, strlen(name) , 1, 0, &handle); if (ere) { error_num = ere; return( -1 ) ; } ere = DeieteFile(handle) ; if (ere) { error_num = ere; CloseFile(handle) ; return( -1 ) ; } return(0) ; } /*************** fCiose ***************/ long fclose(FILE *stream) { long ere, i; ere = CloseFile( stream) ; if (ere) { error_num = ere; i = EOF; } else i = 0; return stream; } /********** fgetc *************************************** * Read a char and return from function. Return EOF (-1) * If EOF. This returns char as an int. long fgetc(FILE *stream) { long ere, i, chl; unsigned char ch; ere = ReadBytes (stream, &ch, 1, &i); if (ere) return EOF; else chl = ch; return (chl); } Part V ■ Additional Software Listing 29.2. continued Read chars into string (s) until we get a LF or n-1=0. Null terminate the string. char *fgets(char *s, long n, FILE *stream) { long ch; char *ss, c; ss = s; while (n > 1 ) { ch = fgetc( stream) ; if (ch == EOF) { *s = 0; /* null terminate */ return(0) ; } else { c = ch; *s = c; n- - ; if (c == 0x0A) n = 0; s++; } } *s = 0; return (ss); /*************** fputc ***************/ /* writes the char (int) in c to the stream. No translation is done to the char. */ long fputc(long c, FILE *stream) { long ere, i, ch; ere = WriteBytes (stream, &c, 1, &i) ; if (ere) ch = EOF; else ch = c; return (ch); } /********************* fpUts ************************* Writes the string pointed to by *s to file *stream No EOL translation is done. The terminating NULL is not placed in the file. Chapter 29 ■ CM32: A 32-Bit C Compiler long fputs(const char *s, FILE *stream) { long ere, i, ch; ere = 0; while ((*s) && (!erc)) { ere = WriteBytes (stream, s++, 1, &i); } if (ere) ch = EOF; else ch = 0; return (ch); } /*************** f+oii ******************************/ /* returns the file pointer (Logical File Address) */ /* of stream. */ long ftell(FILE *stream) { long ere, i; ere = GetFileLFA(stream, &i); if (ere) i = EOF; return (i); } /*************** rewinc| *****************************/ /* Set file LFA to 0. */ void rewind(FILE *stream) { long ere; ere = SetFileLFA(stream, 0); } /*************** fseek *****************************/ /* Set file LFA to desired position. */ void fseek(FILE *stream, long offset, long origin) { long ere, crnt; if (origin==SEEK_CUR) ere = GetFileLFA(stream, &crnt); else if (origin==SEEK_END) { offset = 0; crnt - -1; /* MMURTL Seeks to EOF */ } else if (origin==SEEK_SET) crnt = 0; else return 1 ; 714 Part V ■ Additional Software Listing 29.2. continued ere = SetFileLFA(stream, off set+crnt) ; if (ere) return (1); else return (0); } /* ********* fread *************************************** * reads nobjects of nsize from stream and returns them * to ptr. long fread(char *ptr, long size, long nobj , FILE *stream) { long ere, i, j ; ere = ReadBytes (stream, ptr, size*nobj , &i); if (ere < 2) j = i/size; /* number of objects of size */ else j = 0; /* nothing! */ return (j); } /********** fyyrite *************************************** * writes nobjects of nsize to stream from ptr. long fwrite(char *ptr, long size, long nobj, FILE *stream) { long ere, i, j ; ere = WriteBytes (stream, ptr, size*nobj , &i); if (!erc) j = i/size; /* number of objects of size */ else { error_num = ere; j = 0; /* nothing! */ } return (j); } /*.*****.*********** End of Fiieio.c **************/ string.c String.c implements the supported string functions. Listing 29.3 provides a good example of how to use an assembly language interface with MMURTL. Chapter 29 ■ CM32: A 32-Bit C Compiler Listing 29.3. Standard string functions for MMURTL. * The most commonly used string functions * are contained in this libarary source file. * strcmp strncmp strcpy strncpy, strlen, * strncat, strchr */ long strcmp(char *stn , char *str2) { #asm MOV ESI,[EBP+12] MOV EDI,[EBP+8] strcmpO: MOV AL,[ESI] CMP AL.BYTE PTR [EDI] JG strcmpl 2 JL strcmp2 CMP AL, 0 JE strcmp3 INC DI INC SI JMP SHORT strcmpO strcmpl : MOV EAX, 1 JMP SHORT strcmp4 strcmp2: MOV EAX, -1 JMP SHORT strcmp4 strcmp3: XOR EAX, EAX strcmp4: #endasm } /* same as strcmp except only compares up to set length */ long strncmp(char *str1 , { char *str2, Ion #asm MOV ESI,[EBP+16] stn MOV EDI,[EBP+12] str2 MOV ECX,[EBP+8] n strncmpO: CMP ECX, 0 Equal so far? JE strncmp3 Yes, we're done DEC ECX One less MOV AL,[ESI] CMP AL.BYTE PTR [EDI JG strncmpl JL strncmp2 CMP AL, 0 End of String? PartV Additional Sofiwa Listing 29.3. continued JE strncmp3 ;Equal up to here! INC EDI ;Next chars INC ESI JMP SHORT strncmp0 ;Back again strncmpl : MOV EAX, 1 JMP SHORT strncmp4 strncmp2: MOV EAX, -1 JMP SHORT strncmp4 strncmp3: XOR EAX, EAX strncmp4: #endasm } /* strcpy - String Copy ct to s */ /* including null, return s */ char *strcpy(char *s, char *ct) { #asm MOV EDI,[EBP+12] MOV ESI,[EBP+8] CLD strcpyO: MOVSB CMP BYTE PTR [ESI], 0 JNZ strcpyO MOV EAX,[EBP+12] #endasm } destination s source ct /* strncpy - String Copy ct to s */ /* upto max of n chars, pad with */ /* nulls if s < ct, return s */ char *strncpy(char *s, char *ct, long n) { #asm MOV EDI,[EBP+16] MOV ESI,[EBP+12] MOV ECX,[EBP+8] CLD strncpyO: CMP ECX, 0 JE strncpy2 DEC ECX MOVSB CMP BYTE PTR [ESI], 0 JNZ strncpy0 strncpyl : CMP ECX.0 [destination ; source *ct ;max mov n ;End yet?? ;Yes ;0ne less ;Move it ;End of Source? ;No, go back Chapter 29 ■ CM32: A 32-Bit C Compiler JE strncpy2 INC EDI MOV BYTE PTR [EDI], 0 DEC ECX JMP SHORT strncpyl strncpy2: MOV EAX,[EBPh 16] #endasm } long strlen(char { *cs) #asm XOR EAX, EAX MOV ESI,[EBP^ 8] strlenO: CMP BYTE PTR [ESI],0 JE strlenl INC ESI INC EAX JMP SHORT _strlen0 _strlen1 : #endasm } char *strncat(char *s, char *ct, long n) { #asm MOV EDI,[EBP+16] MOV ESI,[EBP+12] MOV ECX,[EBP+8] CLD strncat00: CMP BYTE PTR [EDI], 0 JE strncat© INC EDI JMP SHORT strncat00 strncat0: CMP ECX, 0 JE strncat2 DEC ECX MOVSB CMP BYTE PTR [ESI], 0 JNZ strncat0 strncatl : CMP ECX.0 JE strncat2 INC EDI MOV BYTE PTR [EDI], 0 DEC ECX JMP SHORT strncatl strncat2: MOV EAX,[EBP+16] ;destination *s ; source *ct ;max mov n ;get to end of s ;End yet?? ;Yes ;One less ;Move it ;End of Source? ;No, go back ;Return s PartV Additional Software Listing 29.3. continued #endasm } char *strcat(char *s, char *ct) { #asm MOV EDI,[EBP+12] MOV ESI,[EBP+8] ; CLD strcatO: CMP BYTE PTR [EDI], 0 JE strcatl INC EDI JMP SHORT strcate strcatl : MOVSB CMP BYTE PTR [ESI], 0 JNZ Strcatl MOVSB MOV EAX,[EBP+12] #endasm } ; destination ; source *ct ;get to end of s ;Move it ;End of Source? ;No, go back for more ;Yes, Null terminte ;Return s ?******: strchr ******** /* Find first occurence of 'chr' in 'string char *strchr(char *string, char chr) { do if(*string == chr) return string; while(*string++) ; return 0; }