IMD 1.18: 22/01/1996 5:35:14 micro cornucopia kaypro user group disk 7 small c version 2 compiler     SMALLC DOC SMALLC DOCSMALLC2 DOC/SAMPLE DOC6 !IOLIB ASM"#$%&'()*+,-./01IOLIB ASMJ23456789:;CRC COM<=>CALL ASML?@ABCDEFGHHELLO C IABS C JLIB H KSTDIO H LMNVM H OVML H PSTD H QSTDIOA H R c80.doc >>> Small C-compiler documentation <<< Available in the directory is a compiler for a subset of the language C. It consists of the two files C80.C (compiler) and C80LIB.I80 (runtime library) It is in source form and is free to anyone wishing to use it. Characteristics of the compiler are as follows: (1) It supports a subset of the language C. (see the book "C A Programming Language", by Brian Kernighan and Dennis Ritchie.) (2) It is written in C itself. (3) ItLIBASM C SSMC COMTUVWXYZ[\]^_`abcSMC COMdefghijklmnopqrsSMC COMtuvDTOI C wITOD C xITOU C yITOX C zLEFT C {OUT C |PRINTF C }~SIGN C STRCMP C UTOI C XTOI C 7-DISK DOC  is syntactically identical to the C on UNIX (unlike some other small C compilers and interpreters). (4) It produces as output a text file suitable for input to an 8080 assembler. (5) It is a stand-alone single-pass compiler (which means it does its own syntax checking and parsing and produces no intermediate files). (6) It can compile itself. This means any processor supporting C can be used to develop this small C compiler for any other processor. The intention behind the writing of CRCKLISTCRC   this compiler was to bring the C language to small computers. It was developed primarily on a 8080 system with 40 K bytes and a single mini-floppy. Consequently, an effort was made to keep the compiler small in order to fit within limited memory, and intermediate files were avoided in order to conserve floppy space. COMPILER SPECIFICATIONS As of this writing, the compiler supports the following: (1) Data type declarations can be: - "char" (8 bits) - "int" (16 bits) - (by placing anion calls are defined as any primary followed by an open paren, so legal forms include: variable(); array[expression](); constant(); function()(); -Pointer arithmetic takes into account the data type of the destination (e.g. pointer++ will increment by two if pointer was declared "int *pointer"). -Pointer compares generated unsigned compares (since addresses are not signed numbers). -Often used pieces of code (i.e. storing the primary register indirect through the or 'ab') -local variable (or pointer) -global (static) variable (or pointer) (4) Program control: -if(expression)statement; -if(expression) statement; else statement; -while (expression) statement; -break; -continue; -return; -return expression; -; (null statement) -{statement; statement; ... statement;} (compound statement) (5) Pointers: -local and static pointers can contain the address of "char" or "int" data elements. (6) Compiler commands: - #define name string (prbinary operators "&&", "||", and "?:". (7) The declaration specifiers "auto", "static", "extern", and "register". (8) The statements "for", "switch", "case", and "default." (9) The use of arguments within a "#define" command. Compiler restrictions include: (1) Since it is a single-pass compiler, undefined names are not detected and are assumed to be function names not yet defined. If this assumption is incorrect, the undefined reference will not appear until the compiled program is ass "*" before the variable name, a pointer can be formed to the respective type of data element). (2) Arrays: -single dimension (vector) arrays can be of type "char" or "int". (3) Expressions: -unary operators: "-" (minus) "*" (indirection) "&" (address of) "++" (increment, either prefix or postfix) "--" (decrement, either prefix of postfix) -binary operators: "+" (addition) "-" (subtraction) "*" (multiplication) "/" (division) "%" (mod, i.e. remainder from divis top of the stack) generate calls to library routines to shorten the amount of code generated. -Generated code is "pure" (i.e. the code may be placed in Read Only Memory). Code, literals, and variables are kept in separate sections of memory. -The generated code is re-entrant. Everytime a function is called, its local variables refer to a new stack frame. By way of example, the compiler uses recursive-descent for most of its parsing, which relies heavily on re-entrant (recurse-processor will replace name by string throughout text.) - #include filename (allows program to include other files within this compilation.) - #asm (not supported by standard C) Allows all code between "#asm" and "#endasm" to be passed unchanged to the target assembler. This command is actually a statement and may appear in the context: "if (expression) #asm...#endasm else..." (7) Miscellaneous: -Expression evaluation maintains the same hierarchy as standard C. -Functembled. (2) No optimizing is done. The code produced is sound and capable of re-entrancy, but no attempt is made to optimize either for code size or speed. It was assumed a post-processor optimizer would later be written for the target machine. (3) Since the target assembler is of unknown characteristics, no attempt is made to produce pseudo-ops to declare static variables as internal or external. (4) Constants are not evaluated by the compiler. That is, the line of code: X = 1+2; wion) "|" (inclusive 'or') "^" (exclusive 'or') "&" (logical 'and') "==" (test for equal) "!=" (test for not equal) "<" (test for less than) "<=" (test for less than or equal to) ">" (test for greater than) ">=" (test for greater than or equal to) "<<" (arithmetic left shift) ">>" (arithmetic right shift) -primaries: -array[expression] -function(arg1, arg2,...,argn) -constant -decimal number -quoted string ("sample string") -primed string ('a' or 'Z' ive) functions. COMPILER RESTRICTIONS Since recent stages of compiler check-out have been done both on an 8080 system and on UNIX, language syntax appears to be identical (within the given subset) between this small C compiler and the standard UNIX compiler. Not supported yet are: (1) Structures. (2) Multi-dimensional arrays. (3) Floating point, long integer, or unsigned data types. (4) Function calls returning anything but "int". (5) The unaries "!", "~", and "sizeof". (6) The control   ould generated code to add "1" and "2" at runtime. The results are correct, but unnecessary code is the penalty. ASSEMBLY LANGUAGE INTERFACE Interfacing to assembly language is relatively straight-forward. The "#asm ... #endasm" construct allows the user to place assembly language code directly into the control context. Since it is considered by the compiler to be a single statement, it may appear in such forms as: while(1) #asm ... #endasm or if (expression) #asm...#endasm else... tatement: function(X, Y, z()); LHLD X PUSH H LHLD Y PUSH H CALL z PUSH H CALL function POP B POP B POP B Notice, the compiler cleans up the stack after the call using a simple algorithm to use the least number of bytes. Local variables allocate as much stack space as is needed, and are then assigned the current value of the stack pointer (after the allocation) as their address. int X; would produce: PUSH B which merely allocates room on the stack for 2 bytes (n from the c-code have access to all registers and do not have to restore them prior to exit. They may push items on the stack as well, but must pop them off before exit. It is the responsibility of the calling program to remove arguments from the stack after a function call. This must not be done by the function itself. There is no limit to the number of bytes the function may push onto the stack, providing they are removed prior to returning. Since parameters are passed by value, the paramters o to ask questions than to pull arguments from a command line (which is in no way similar between the 8080 developmental system and UNIX), this was the preferred method. The questions asked are as follows: Do you want the c-text to appear? This gives the user the option of interleaving the source code into the output file. Response is Y or N. If Y, a semicolon will be placed at the start of each input line (to force a comment to the 8080 assembler) and the input lines will be printed where app Due to the workings of the preprocessor which must be suppressed in this construct, the pseudo-op "#asm" must be the last item before the carriage return on the end of the line (i.e. the text between #asm and the is thrown away), and the "#endasm" pseudo-op must appear on a line by itself (i.e. everything after #endasm is also thrown away). Since the parser is completely free-format outside of these execeptions, the expected format is as follows: if (expression) #asm ... ... #endasm ot initialized to any value). References to the local variable X will now be made to the stack pointer + 0. If another declaration is made: char array[3]; the code would be: DCX SP PUSH B Array[0] would be at SP+0, array[1] would be at SP+1, array[2] would be at SP+2, and X would now be at SP+3. Thus, assembly language code using "#asm...#endasm" cannot access local variables by name, but must know how many intervening bytes have been allocated between the declaration of the variablen the stack may be modified by the called program. STACK FRAME The stack is used extensively by the compiler. Function arguments are pushed onto the stack as they are encountered between parentheses (note, this is opposite that of standard C, which means routines expressly retrieving arguments from the stack rather than declaring them by name must beware). By the definition of the language, parameter passing is "call by value". For example the following code would be produced for the C sropriate. If the answer is N, only the generated 8080 code will be output. Do you wish the globals to be defined? This question is primarily a developmental aid between machines. If the answer is Y, all static symbols will allocate storage within the module being compiled. This is the normal method. If N, no storage will be allocated, but symbol references will still be made in the normal way. Essentially, this question allows the user to specify all or none of the static symbols external.else statement; Note a semicolon is not required after the #endasm since the end of context is obvious to the compiler. Assembly language code within the "#asm ... #endasm" context has access to all global symbols and functions by name. It is up to the programmer to know the data type of the symbol (whether "char" or "int" implies a byte access or a word access). Stack locals and arguments may be retrieved by offset (see STACK FRAME). External assembly language routines invoked by function calls and its use. It is worth pointing out local declarations allocate only as much stack space as is required, including an odd number of bytes, whereas function arguments always consist of two bytes apiece. In the event the argument was type "char" (8 bits), the most significant byte of the 2-byte value is a sign-extension of the lower byte. OPERATING THE COMPILER The small C compiler begins by asking the user for a number of options regarding the expected compilation. Since it was easier   It is to be considered a temporary measure. Starting number for labels? This lets the user supply the first label number generated by the compiler for it internal labels (which will typically be "ccXXXXX", where XXXXX is a decimal number increasing with each label). This option allows modules to be compiled separately and later appended on the source level without generating multi-defined labels. Output filename? This question gets from the user the name of the file to be created. A  (6) Execute the created run file. Currently, the 8080 assembler used must possess the abilities to handle symbol names unique to 8 characters and to recognize lower-case symbol names as unique from their upper-case equivalent. This is due to the fact the compiler recognizes 8-character names and passes all static variable and function names intact to the assembler. There are a few symbol names within the compiler which are not unique until the 7th character and which have "upper-case twins". Thdestination processor. The procedure currently used to create the compiler for my 8080 system is as follows: (1) Edit the file C80.c to modify two lines of code: -change the line of code #include to #define NULL 0 (this is done since the "stdio.h" I/O header file contains unparsable lines for the small compiler, and the line defining NULL is the only line of "stdio.h" needed by the compiler). -change the line of code #define eol 10 to #define eol 13 (this i (the secondary register). (3) An assembler (or cross-assembler). Since the compiler is just now on its feet and subject to feedback from users, it is expected many changes will be made to it. Already planned changes (in order of expected addition) are: (1) Constants will be pre-evaluated by the compiler. Something like x=1+2*3 will become x=7 prior to generating any code. (2) Structures will be added. This is one of the powers of C. Its omission has always been considered temporary. null line sends output to the user's terminal. Input filename? This question gets from the user the name of the C module to use as input. The question will be repeated each time a name is supplied, allowing the user to create an output file consisting of many separate input files (it behaves as if the user had appended them together and submitted only the one file). A null line response ends the compilation process. COMPILING THE COMPILER The power of the compiler lies in the fact itese discourage the use of the KL-10's MACN80 since it folds lower-case to upper case and does not recognize 8-character names. It may be used, however, if the user is aware of these limitations and chooses symbol names within these restrictions. THE FUTURE OF THE COMPILER That part of the compiler which produces code for the 8080 is all together in the final section of the compiler. Routines used by the compiler to produce code are kept short and are commented. Changing this compiler to prs done since my 8080 system uses for the end of line character, and UNIX uses the "newline" character). (2) Invoke the compiler (by typing "a.out" or whatever other name it was given. (3) Answer the questions by the compiler to use the file C80.c as input and to produce the file C80.I80 as output. (4) Append the files C80.I80 and C80LIB.I80 (the code for the compiler and the code for the runtime library, respectively). (5) Assemble the combined file using some 8080 assembler. (3) Assignment operators (+=, &=, etc.) will be added. (4) Missing unary and binary operators and statements will be added. (5) The expression parser will create intermediate tree-structures of the expressions and will walk through them before generating any code. This will allow some optimization and will allow the function arguments to be passed on the stack in the same sequence as UNIX. (6) A peep-hole optimizer will be added to improve the generated code. Many of these things represen can compile itself. This allows a user to "bootstrap" the compiler onto a new machine without excessive recoding. To compile the compiler under the UNIX operating system, the appropriate command is: % cc C80.c -lS which will invoke the UNIX C-compiler and the UNIX linker to create the runnable file "a.out". This file may be renamed as needed and used. No other files are needed. In order to create a compiler for a new machine, the user will need to compile the compiler into the language of the oduce code for any other machine is a matter of changing only these few routines, and does not entail digging around through the internals of the program. I would expect the change to another machine could be made in an afternoon providing the target machine had the following attributes: (1) A stack, preferably running backwards as items are pushed onto it. (2) Two sixteen-bit registers. In the 8080 these are the HL register pair (the primary register to the compiler) and the DE register pair  t a wish-list. Time will be spent only when it becomes available. Any volunteer help in any of these areas would be appreciated. Questions should be directed to Ron Cain here at SRI either at extension 3860 or at CAIN@SRI-KL. $ clude" command: #include "stdiol.h" If you are using the "printf" function, the C-library needs to be included as well: #include "libl.h" When command line processing is selected (the default), the compiler is invoked with a command like: smc [switches] [file...] The output of the compiler is sent to standard out and can be redirected to a file, e.g.: smc -c file.c >b:file.mac The following compiler switches are available: -a signal alarm (logical 'and') "||" (logical 'or') "|=" "^=" "&=" "+=" "-=" "*=" "/=" "%=" ">>=" "<<=" 2. Program Control for (expr1 ; expr2 ; expr3) statement; do statement while (expression); switch (expression) { case constant-expr: default: } goto label; label: 3. External Declarations extern type name...; 4. Preprocessor  the file "stdioa.h" should be included instead of the file "stdiol.h". The first few lines of the C program, then would look like: #include "stdioa.h" #include "iolib.asm" #include "call.asm" To compile and run would require something similar to: smc file.c >b:file.asm asm file.xyz If you are using the C library routines - such as printf - you want to append the contents of libasm.c onto the END of the source file. IV. I/O and Runtime Library (bell) on error -b# bump starting label numbers by # -c generate M80 compatable code -l# list device file descriptor (0-4) -m monitor. print function name as it's compiled -p pause on error, waiting for keyboard input Case is not significant and is internally converted to upper case for comparisons. The output of the compiler must then be run through an assembler and linker or loader. Using M80 and the above example, it would lookCommands #ifdef name #endif (note: #ifdef's may be nested) 5. Initializers Initializers are accepted for global declarations only: type name = {init_list}; 6. I/O Version 2 supports stdin, stdout, stderr and stdlst. I/O redirection is also supported for stdin and stdout (see I/O library section, below). III. Using the Compiler One of the first lines in your program should include the I/O library by using the "#in The I/O and runtime library supports the following functions: cpm(cpm_function#, input_parameter) exit() abort(error_code) fopen(name, mode) fclose(file_des) unlink(name) getc(file_des) fgetc(file_des) getchar() gets(string) fgets(string, length, file_des) putc(char, file_des) fputc(char, file_des) putchar(char) puts(string) fputs(string, file_des) lddr( SMALL-C VERSION 2.02 USERS NOTES I. Introduction Small-C version 2 is an extension of the version 1 compiler, written by J.E. Hendrix and placed in the public domain. The Small-C compiler supports a subset of the C programming language and is configurable to systems with different memory capacities by deselecting some of the features. II. Features Small-C version 2 has all of the features of the version 1 compiler, with the following additions: 1. Expressions "&&"  like: m80 =file.mac l80 iolib,file,call,file/n/e The file 'iolib.rel' contains the I/O library and runtime support. The file 'call.rel' contains the compiler library. If the "printf" function is being used, the C-library must also be linked: l80 iolib,file,call,lib,file/n/e IIIa. Using the Compiler With ASM When using the ASM assembler, the I/O and compiler librarys must be included in the source as include files, since no linking facility is available. In addition,  source, dest, count) ldir(source, dest, count) The functions "lddr" and "ldir" perform the Z-80 instructions LDDR and LDIR respectively. The function "fgets" keeps the terminating linefeed as part of the string, the other functions should behave as you would expect them to. The names "stdin", "stdout", "stderr" or "stdlist" can be substituted for "file_des" in any of the above functions. If standard I/O has been redirected, the appropriate file will be read from or written to. "Stderr" for the ASM assembler that comes with CP/M. You will find this assembler on your CP/M distribution diskette. The output of ASM is the program in Intel Hex Format. To create a .COM file you must use another utility that is on your CP/M distribution diskette called LOAD.COM. The final result is that the program you write in C can be translated by the compiler and these utilities into a .COM file that may be run on any machine running standard CP/M. If you know nothing about C, the documentation itches required for C80 and Small-C, respectively. Another change that needs to be made when compiling with C80 is to change the #include "stdiol.h" to #include "std.h". This affects the files CC1.C, CC2.C, CC3.C and CC4.C. If a linker is not available and the compiler is to be compiled as a single entity, comment out the #define SEPERATE line in CC.DEF. his disk and is titled HELLO.C. You may either compile this program or re-write the program yourself using a text editor. ED.COM is supplied with CP/M and is suitable for program creation but it is somewhat awkward to use. If you are interested in another line editor, Micro Cornucopia has a public domain text editor (EDIT.COM) on User Disk K6. This is what the program looks like: #include #include #include main() { printf("hello, world\n"); } The always writes to the console. "Fgetc" is identical to "getc" and "fputc" is identical to "putc". Don't forget to #include "stdiol.h". (The compiler will also accept #include or just #include stdiol.h with no delimiters.) V. Bugs All of the bugs that I am aware of have been fixed. This includes the while statement fixes that were submitted to Dr. Dobbs and the optimizer bug. VI. Compiling The Compiler There are #ifdef's in the code to allow bootstrapping a version of the Son this disk will seem incomplete to you. You will need to get a book about the language. The definitive source is: The C Programming Language Brian W. Kernighan & Dennis Ritchie Prentice-Hall Publlishers ISBN 0-13-110163-3 But until then you may still try this sample program. YOUR C SYSTEM DISK The first thing you should do is make a copy of this disk. You will need to have most of the files on the same disk as the C compiler (SMC.COM) when you compile a C program. You do not need th SAMPLE COMPILATION FOR BEGINNERS IN C This file describes step by step the creation of a program using this Small C compiler. This example is by no means graceful or elegant but will provide the novice with model of a successful C compilation. The C programming language is a vehicle for writing a program in a high level language and then translating it into machine language. The output of this C compiler is an assembly language program. This version of SmallC2 produces assembly code suitable program uses a function called "printf" which is in the C function library. To use this function the library must be appended to the program. This library is called LIBASM.C and is also on this disk. To append it to HELLO.C using the CP/M utility PIP type the following command: PIP HELLO2.C=HELLO.C,LIBASM.C This creates a file called HELLO2.C which has LIBASM.C following the program HELLO.C. Now we are ready to compile! The compiler will create a large assembly language file. This is becausemall-C compiler by using Software Toolworks C80 C compiler. [This should no longer be necessary as the Small-C compiler will compile itself, but is included as convenience to the developer.] The #define's for the bootstrapping compiler are in the file "cc.def" and is currently setup to compile using the Small-C compiler itself. The file "cc80.def" is a cc.def file for compiling with C80; "smcc.def" is the cc.def file for compiling with Small-C. The files MAKE.SUB and MAKES.SUB show the compiler swe .DOC files to compile programs so you may want to omit them from your system disk. After you have made your copy, put this disk in a safe place and use your copy for compiling. Label your new copy C SYSTEM DISK and use SYSGEN to install your CP/M system on it. This is important since many of the programs you will run to create C programs end with a Warm Boot (including the compiler itself). THE PROGRAM The sample program is the first example in The C Programming Language. The program is on t   all the I/O routines are attached to the program, even though this program does not use them all. As you learn more about C you will learn how to shorten the compiler's output but this example will not go into that. The reason this needs to be mentioned is that the output file created by the compiler will be approximately 60K! The best way to handle it is to put a blank,formatted disk into drive B and your C system disk into drive A. Then redirect the output file to the blank disk. 1. DRIVE A:  drive B also. For this example we will skip the print file. So, put ASM.COM and LOAD.COM on the blank disk with HELLO2.ASM (and leave it in drive B). Then log on to drive B (the B> prompt should be displayed) and enter the command: ASM HELLO2.BBZ This will take some time but when it is finished you should have a file on the disk in B called HELLO2.HEX. Now we almost have an executable program. LOAD.COM will translate the .HEX file into a .COM file. To do this to your program enter: LO example if you would type: ASM HELLO2 ASM would produce a hex file called HELLO2.HEX and a print file called HELLO2.PRN. The .PRN file for HELLO2 is over 100K long and the hex file is also large. To make everything fit on a diskette you may need to direct the files to another disk. To do that you must add an extension to your command input file call: ASM HELLO2.123 where the numbers must be replaced with single letters, each one representing something different. 1 can be #asm ; ;------------------------------------------------------------------ ; Small-C Run-time Library (ASM Version) ; ; ; V3b As of June 9, 1980 12pm (rj) ; corrected cp to chp in @gets ; changed lower case to hex constants in @fopen and fcb ; V4 As of June 26, 1980 12:15pm (gtf) ; Changed all @'s and ?'s to "QZ" for ASM compatibility ; V4b As of July 7, 1980 3:00 pm (gtf) ; Changed putc() to test code returned by cput() ; V4c As of July 9, 1980 9:15 pm (gtf) ; Fixed bug Your C SYSTEM DISK 2. DRIVE B: Blank Formatted Disk 3. Log on to drive A--(the A> prompt should be displayed) 4. Enter the command: SMC HELLO2.C >B:HELLO2.ASM This will place the output file (which will be named HELLO2.ASM) on the disk in drive B. It will take the compiler quite some time to compile your program. When it is done you will get the A> prompt again. Look at drive B's directory and you should see the output file. If you are having problems or are getting errors from theAD HELLO2 LOAD will look for a file named HELLO2 with a .HEX extension. When it is finished, look at the directory on B and you should see a file called HELLO2.COM This is what we are after. Now don't laugh when you run it. If it was really a great program this wouldn't be as much fun. After all, it is really amazing how much work your computer did for this small program. Any way you may run it like any other .COM file by entering: HELLO2 As I mentioned at the start, as you grow with C a letter from A to Y which will represent the disk that has the .ASM file 2 can be a letter from A to Y representing the disk destination of the .HEX file; or a Z will suppress generation of a hex file 3 can be a letter from A to Y pointing to the destination for the .PRN file; or a Z will cause the assembler not to produce the print file. Thus the command ASM HELLO2.ABB will cause the assembler to look on drive A for HELLO2.ASM and to output HELLO2.HEX to drive B and HELLO2.PRN to in CPMIO which returned wrong error status. ; Added PUTS() function ; Un-hardwired I/O buffer count. ; Made GETCHAR() print LF after reading CR. ; Made GETCHAR() return -1 on EOF (=CTRL-Z) ; Added EOL and LF equates, instead of magic numbers ; V4d As of July 16, 1980 9:00 pm (gtf) ; Added EXIT() function ; V5 As of April 12, 1983 12:30 am (br) ; Modified to reuse package for Small-C, ver 2. ; Removed "QZ" in front of names. ; Added runtime initialization and compiler, recheck your work making sure you did not misspell the commands or omitted anything. If the compilation was successful and you have the HELLO2.ASM file, you are ready to assemble the program. ASM.COM translates an assembly language file into machine code. To assemble a file you only need to type ASM filename. For this program we will need to take advantage of the parameters available with ASM. ASM produces two files with the same name as your .ASM file but with different extensions. For you will learn how to reduce the size of your final programs. I think you will enjoy C and gain a better understanding of the transformation of programs from your keyboard to the .COM files on your disk. May you always C clearly!    command line ; processing from RUNTIM.MAC [by Bill Danielson, 3/83]. ; Added I/O redirection for stdin and stdout. ; Added FPUTS(), FGETS(), DOLDDR(), DOLDIR(), ; UNLINK() and ABORT() functions. ; V5a As of April 14, 1983 12:55 pm (br) ; Masked parity bit in compares for EOL, LF & ^Z. ; V5b As of July 19, 1983 9:50 pm (br) ; Fixed bug in FCLOSE(). ; V5c As of July 27, 1983 9:55 pm (br) ; Fixed bug in initialization of RSTDIN and RSTDOUT. ; Fixed bug in PUTS().  STDERR EQU 2 ;Default for stderr STDLIST EQU 4 ;Default for stdlist ; DFLTDSK: DS 1 ;drive to use if no drive is named UNIT: DS 2 ;I/O structure address to act on IP: DS 2 ;int *ip; CHP: DS 2 ;char *chp; DP: DS 2 ;char *dp; FILE: DS 2 ;file name MODE: DS 2 ;char *mode;(read or write) ZCH: DS 2 ;char ch; ZT: DS 2 ;int t; FN: DS 2 ;int fn; i/o function (for cpmio) ; SVCHP: DS 2 ;char *svchp; saved character pointer RSTDIN: DS 2 ;int rstdin; unit of redirected stdin RSTDOUT: DS 2 ;int rstdout; open for writing BUFSIZ EQU 128 ;how long the sector buffer is NBUFS EQU 4 ;number of I/O buffers (change buffer declarations, too) ; CP/M system call codes CLOSE EQU 16 ;close a file CPMSTR EQU 9 ;print '$' delimited string on console CREATE EQU 22 ;make a file DMA EQU 26 ;set DMA (I/O address) DELETE EQU 19 ;delete a file GETCH EQU 1 ;read character from console GETSTR EQU 10 ;read string from console LSTOUT EQU 5 ;write character to list device OPEN EQU 15 ;open a file PUTCH EQU 2 ;write cit to a ascii string with 'pgmname' in it LXI D,PGM ; Pointer to 'pgmname' string CALL SVARG ; Save next argument ; Ok, now for the real stuff. Set DE pair to point to ; CPM command line and start searching for arguments LXI D,CPMARG ; Pointer to CPM arg line LDAX D ; Load # character in line MOV B,A ; Save it in B NXTSP: INX D ; Point to next character DCR B ; Decrement character count JM ENDCMD ; End of cmd line LDAX D ; Load next character in line CPI ' ' ; Space? JZ NXTSP ;; V5d As of July 29, 1983 7:47am (br) ; Fixed GETC()/CGET() so it skips check for CR when ; calling GETCHAR(). ;------------------------------------------------------------------ ; ; *** may need following statement when using ASM *** ORG 100H ; *** ; JMP START ; ; *** delete following extern when using ASM *** ; EXTRN MAIN, CCDSGI, CCSXT ; *** ; ; ======================================== ; I/O subroutines for CP/M ; By Glen Fisher ; The Code Works(tm) ; [modified by Bill Rand unit of redirected stdout ; ; First thing, we save CPM's stack pointer and current disk and ; init stdin and stdout. ; Second thing, we run through the CPM input line and ; modify it so that we can pass the C program the ; command line in the argc, argv form that it expects ; HL = pointer to next argv entry ; DE = pointer to next character in command line ; B = number of characters left in line ; C = argument count (argc) START: LXI H,0 ; Get CPM's stack pointer DAD SP SHLD STACK ; Savharacter to console QUERY EQU 25 ;get logged-in drive id READ EQU 20 ;read a sector SELECT EQU 14 ;log-in a drive WRITE EQU 21 ;write a sector ; LF EQU 10 ;line feed EOL EQU 13 ;end-of-line character (=carriage return) CTRLZ EQU 26 ;end-of-file mark for text files TBUFF EQU 80H ;address of default I/O address BDOS EQU 5 ;Entry point to CP/M BDOS CPMARG EQU 80H ;CP/M command line MAXARG EQU 32 ;Maximum number of args in input line STDIN EQU 0 ;Default for stdin STDOUT EQU 1 ;Default for stdout  Yes...continue searching CPI '>' ; Redirect output? JZ RDOUT ; Yes...open the file for output CPI '<' ; Redirect input? JZ RDINP ; Yes...open the file for input CALL SVARG ; Nope, save starting point of this arg ; Loop looking for either end of line of a space NXTCH: INX D ; Point to next character DCR B ; Decrement character count JM ENDWRD ; End of cmd line, but need to end arg LDAX D ; Load next character in line CPI ' ' ; Space? JNZ NXTCH ; Nope...keep looking MVI A,0 ; Yes,le] ; ======================================== ; NULL EQU 0 ;pointer to nothing FCBSIZE EQU 36 ;size, in bytes, of an FCB NEXTP EQU 0 ;offset to next-character pointer in I/O structure UNUSED EQU 2 ;offset to unused-positions-count in I/O structure BUFFER EQU 4 ;offset to disk sector buffer in I/O structure FLAG EQU 33 ;file-type flag byte (in unused part of FCB) FREEFLG EQU 128 ;This I/O structure is available for the taking EOFFLG EQU 2 ;The end of this file has been hit WRTFLG EQU 1 ;This filee it for later MVI C,QUERY ; get logged-in disk CALL BDOS INR A ; make it so it will work in fcb STA DFLTDSK LDA BDOS+2 ; Get base of BDOS MOV H,A ; Save page in HL MVI L,0 SPHL ; Set stack pointer LXI H,STDIN SHLD RSTDIN ; Init rstdin LXI H,STDOUT SHLD RSTDOUT ; Init rstdout MVI C,0 ; Init argc LXI H,ARGV ; Pointer to first entry of argv array ; Unfortunately, CPM does not tell us what the first word of ; the command line was (the name of pgm), so we fake ; it by pointing    replace it with a zero byte STAX D JMP NXTSP ; Look for start of next arg ENDWRD: MVI A,0 STAX D ENDCMD: MVI B,0 ; Zero B (BC now is 16 bit argc) PUSH B ; First arg to main procedure LXI H,ARGV ; Point to argv array PUSH H ; Second argument to main procedure MVI A,2 ; Load up the argument count CALL MAIN ; Transfer to the C world.... JMP EXIT ; Return to CPM SVARG: MOV M,E ; Save pointer to start of string INX H MOV M,D INX H INR C ; Increment argc RET ARGV: DS MAXARG*2 PGM Load source DB 0EDH, 0B0H ; Do LDIR instruction PUSH H ; Restore stack PUSH D PUSH B DCX SP DCX SP RET ; End of memory function ; Returns top memory location in HL TOPOFMEM: LDA BDOS+2 ; Get base of BDOS MOV H,A ; Save page in HL MVI L,0 RET ; Return the first free location FIRSTFREE: LHLD MEMRY$ RET MEMRY$:DW 0 ; This assembly routine allows CPM calls from Small C. ; ; cpm(cpmfunction#, inputparameter) ; ; Since this function returns whatever is returned JMP NXTSP ; Get next argument DEBLNK: INX D ; Skip leading spaces DCR B RM ; End of line reached LDAX D CPI ' ' JZ DEBLNK RET CPYNAM: LXI H,NAMBUF ; Copy filename to temp buffer PUSH B ; Save reg C MVI C,16 ; Maximum filename length CPY1: MOV M,A INX D INX H DCR B JM ENDNAM DCR C JZ RDERR LDAX D CPI ' ' JNZ CPY1 ENDNAM: MVI M,0 POP H MOV C,L ; Restore reg C RET RDERR: LXI D,RDEMSG ; Error message MVI C,CPMSTR CALL BDOS ; Make sure it gets put on the te B POP B LXI H,2 CALL CCDSGI PUSH H ; Someday this should write out the correct error code ; CALL OUTDEC## ; Inside C compiler POP B JMP EXIT ABTMSG: DB 0DH, 0DH, 'Aborted, reason = ',0 ; ; grabio() ; ; find an input buffer, and return its address. ; if there isn't one, return a NULL. ; GRABIO: ;6 May 80 rj MVI B,NBUFS LXI H,IOBUFS+FLAG LXI D,FCBSIZE+BUFFER+BUFSIZ MVI A,FREEFLG GRAB2: CMP M ;flag byte == freeflg? JZ GRAB3 ;if so, found a free buffer DAD D ;on: DB 'PGMNAME',0 RDINP: CALL DEBLNK ; Skip leading spaces JM RDERR ; End of line reached PUSH H CALL CPYNAM ; Copy filename to temp buffer PUSH D ; Save registers PUSH B LXI H,NAMBUF ; Begining of filename PUSH H LXI H,RDOPN ; Mode PUSH H CALL FOPEN ; Open the file POP D POP D MOV A,H ORA L ; Check return status JZ RDERR SHLD RSTDIN ; Save unit for redirected input POP B ; Restore registers POP D POP H MVI A,0FFH CMP B ; End of command line? JZ ENDCMD JMP NXTin register ; it cannot be used to call ReturnVersionNumber, ReturnLoginVector, ; WriteProtectDisk, or GetAddr. CPM: POP H ; Pop rtn address POP D ; Pop input parameter in DE register pair POP B ; Pop function code into C register PUSH B ; Restore stack PUSH D PUSH H CALL BDOS ; Call CPM MOV L,A ; Sign extend A into HL register pair RLC SBB A MOV H,A RET ; exit() ; ; Stop execution of the program, ; restore the logged-in drive, ; and re-boot CP/M ; EXIT: LHLD RSTDOUT MOrminal JMP EXIT RDOPN: DB 'r',0 WROPN: DB 'w',0 NAMBUF: DS 16 RDEMSG: DB 'iolib: Unable to open < or > file$' ; lddr(source, dest, n) DOLDDR: INX SP ; Skip over return address INX SP POP B ; Load n POP D ; Load destination POP H ; Load source DB 0EDH, 0B8H ; Do LDDR instruction PUSH H ; Restore stack PUSH D PUSH B DCX SP DCX SP RET ; doldir(source, dest, n) DOLDIR: INX SP ; Skip over return address INX SP POP B ; Load n POP D ; Load destination POP H ; to next buffer DCR B JNZ GRAB2 ;if there is one... LXI H,NULL ;there ain't RET ;give up GRAB3: MVI M,0 ;mark buffer as taken LXI D,-FLAG ;back up to buffer start DAD D RET ;and hand it back ; ; freeio(unit) ; ; mark a buffer as free. ; FREEIO: ;Mod 6 May 80 rj POP B ;save rtn addr POP H ;get buffer addr PUSH H ;put the stack back together PUSH B LXI D,FLAG ;find flag byte DAD D MVI M,FREEFLG ;mark buffer as 'free' LXI H,NULL ;return somethSP ; Get next argument RDOUT: CALL DEBLNK ; Skip leading spaces JM RDERR ; End of line reached PUSH H CALL CPYNAM ; Copy filename to temp buffer PUSH D ; Save registers PUSH B LXI H,NAMBUF ; Begining of filename PUSH H LXI H,WROPN ; Mode PUSH H CALL FOPEN ; Open the file POP D POP D MOV A,H ORA L ; Check return status JZ RDERR SHLD RSTDOUT ; Save unit for redirected input POP B ; Restore registers POP D POP H MVI A,0FFH CMP B ; End of command line? JZ ENDCMD V A,H ORA A ; See if stdout has been redirected JZ EXIT1 PUSH H CALL FCLOSE ; If so, close the file POP B EXIT1: LDA DFLTDSK ; Grab orig. logged-in disk MOV E,A DCR E ; (cvt. back to 0-n) MVI C,SELECT ; and log it in again CALL BDOS LHLD STACK ; Load stack pointer SPHL JMP 0 ; return to CP/M STACK: DW 0 ; ; abort(reason) ; ABORT: POP B POP D PUSH D PUSH B PUSH D ; error code LXI H,ABTMSG ; Load abort message PUSH H LXI H,STDERR PUSH H CALL FPUTS POP  ing RET IOBUFS: DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ DS FCBSIZE-3 ;mod 4 May 80 rj DB FREEFLG,0,0 DS BUFFER+BUFSIZ ; ; fopen(name,mode) ; FOPEN: POP B ;get args POP H ;mode SHLD MODE POP D XCHG SHLD FILE PUSH H PUSH D PUSH B CALL GRABIO ; unit = grabio(); SHLD UNIT MOV A,H ; if(unit==NULL) ORA L ; return(NULL); RZ LXI D,FCBSIZE ; ip = unit POP H LHLD UNIT ; ip = unit + FCBSIZE; LXI D,FCBSIZE DAD D SHLD IP LHLD IP ; cp = ip[NEXTP]; LXI D,NEXTP DAD D MOV E,M INX H MOV D,M XCHG SHLD CHP LHLD IP ; dp = &ip[BUFFER]+BUFSIZ; LXI D,BUFFER+BUFSIZ DAD D SHLD DP FCLWH1: ; while(cp'a'-'A') /* lower case? */ 9 Jun 80 rj JC FCBIF2 SUI 61H-41H ; A -= 'a'-'A'; 9 Jun 80 rj JMP FCBIF2 ; } FCBIF1: LDA DFLTDSK ; else A = default_drive; FCBIF2: STAX B ; *fp++ = A; INX B MVI H,' ' ; fp = fcbfill(fp,name,' ',8); MVI L,8 CALL FCBFILL MVI L,3 ; fp = fcbfill(fp,name,' ',3); CALL FCBFILL MVI H,0 ; fp = fcbpad(fp,0,4); MVI L,4 CALL FCBPAD LXI H,16 ; fp[16] = 0; DAD B MVI M,0 RET ; r+FCBSIZE; DAD D SHLD IP LHLD IP ; ip[NEXTP] = &ip[BUFFER]; LXI D,BUFFER DAD D XCHG LHLD IP LXI B,NEXTP DAD B MOV M,E INX H MOV M,D LHLD UNIT ; fcb(unit,name); PUSH H LHLD FILE PUSH H CALL FCB POP H POP H LHLD UNIT ; cpmdisk(*unit); MOV L,M MVI H,0 PUSH H CALL CPMDISK POP H LHLD MODE ; if(*mode=='r' || *mode=='R'){ MOV A,M CPI 72H ; 'r' ? 9 Jun 80 rj JZ FOPIF0 CPI 52H ; 'R' ? 9 Jun 80 rj JNZ FOPIF1 FOPIF0: MVI C,OPEN ; if(cpm(OPEmio(WRITE,unit)<0) PUSH H LHLD UNIT PUSH H CALL CPMIO POP D POP D MOV A,H ORA A JP FCLIF4 LXI H,0 ; t = 0; SHLD ZT FCLIF4: ; } FCLIF3: ; } FCLIF1: MVI C,CLOSE ; if(cpm(CLOSE,unit)<0) LHLD UNIT XCHG CALL BDOS ORA A JP FCLIF5 LXI H,0 ; t = 0; SHLD ZT FCLIF5: LHLD UNIT ; freeio(unit); PUSH H CALL FREEIO POP H LHLD ZT ; return(NULL+t); RET ; ; unlink(name) ; UNLINK: POP B POP D PUSH D PUSH B PUSH D ; name ; /* open filFREEIO POP H LXI H,NULL ; return(NULL); RET ; } FOPIF4: LHLD UNIT ; return(unit); RET ; ; fclose(unit) ; FCLOSE: POP B POP H SHLD UNIT PUSH H PUSH B MOV A,H ; if (unit<256) ORA A ; /* assume stdin, stdout, etc. */ MVI L,0 RZ ; return NULL; LXI H,1 ; t = 1; SHLD ZT LHLD UNIT ; if(unit[FLAG] & WRITE_FL){ LXI D,FLAG DAD D MOV A,M ANI WRTFLG JZ FCLIF1 LXI H,CTRLZ ; putc(CTRL_Z,unit); PUSH H LHLD UNIT PUSH H CALL PUTC POP H eturn; ; ; fcbfill(dest,name,pad,size) ; B D H L ; FCBFILL: MOV A,L ; while(L>0 && (A= *D)~='.' && A~=0){ ORA A JZ FILL2 LDAX D CPI '.' JZ FILL2 CPI 0 JZ FILL2 CPI 61H ; if(A>='a' && A<='z') JC FILL1 CPI 7AH+1 ; 'z' 9 Jun 80 rj JNC FILL1 SUI 61H-41H ; A = A - 'a' + 'A'; FILL1: STAX B ; *B++ = A; INX B INX D ; D++; DCR L ; L--; JMP FCBFILL ; } FILL2: LDAX D ; while(*D~='.' && *D~=0) CPI '.' JZ FILL3 CPI 0 JZ FILL3 INX D ; D+N,unit)<0){ LHLD UNIT XCHG CALL BDOS ORA A JP FOPIF2 LHLD UNIT ; freeio(unit); PUSH H CALL FREEIO POP H LXI H,NULL ; return(NULL); RET ; } FOPIF2: LHLD IP ; ip[UNUSED] = 0; LXI D,UNUSED DAD D LXI D,0 MOV M,E INX H MOV M,D ; } JMP FOPIF4 FOPIF1: ; else if(*mode=='w' || *mode=='W'){ LHLD MODE MOV A,M CPI 77H ; 'w' 9 Jun 80 rj JZ FOPIFA CPI 57H ; 'W' 9 Jun 80 rj JNZ FOPIF5 FOPIFA: MVI C,DELETE ; cpm(DELETE,unit); LHLD UNIe to unlink in LXI H,RDOPN ; Mode ; order to get fcb for it. */ PUSH H CALL FOPEN ; unit = fopen(name,'r'); POP D POP D SHLD UNIT MVI C,DELETE ; cpm(DELETE,unit); CALL BDOS LHLD UNIT ; freeio(unit); PUSH H CALL FREEIO POP D RET ; return; ; ; fcb(fp,name) ; FCB: POP H ;get args POP D ;name POP B ;fp PUSH B PUSH D PUSH H INX D ; if(name[1]==':'){ LDAX D DCX D CPI ':' JNZ FCBIF1 LDAX D ; A = *name - '@'; SUI 40H ; '@' 9 Jun 80 rj   +; JMP FILL2 FILL3: CPI '.' ; if(*D=='.') JNZ FILL4 INX D ; D++; FILL4: ; fall into... ; ; fcbpad(dest,pad,size) ; B H L ; FCBPAD: MOV A,L ; while(L>0){ ORA A JZ PAD2 MOV A,H ; *B++ = H; STAX B INX B DCR L ; L--; JMP FCBPAD ; } PAD2: RET ; return; ; ; getc(unit) ; FGETC: GETC: POP B POP H ; get args PUSH H PUSH B ; c=cget(unit); PUSH H CALL CGET POP D MOV A,L ; if(c=='\r') ANI 7FH ; /* mask parity in compare */ CPI EOre */ CPI CTRLZ ; if(t==CTRLZ) JNZ GET1CHAR LXI H,-1 ; t = -1; GET1CHAR: CPI EOL ; if(t==EOL) JNZ GET2CHAR PUSH H ; putchar('\n'); MVI C,PUTCH MVI E,LF CALL BDOS POP H GET2CHAR: ; return(t); RET ; } ; ; gets(buff) ; GETS: POP B POP H PUSH H PUSH B PUSH H ; buff LHLD RSTDIN ; if(rstdin >= 256) { MOV A,H ORA A JZ GETS1 XCHG LXI H,80 PUSH H ; len PUSH D ; unit CALL FGETS ; return(fgets(buff, 80, rstdin)); POP B POP B POP B RET GETSSIZ MOV M,E INX H MOV M,D LHLD IP ; cp = &ip[BUFFER]; LXI D,BUFFER DAD D SHLD CHP ; } ; } GTCIF2: LHLD IP ; ip[UNUSED]--; LXI D,UNUSED DAD D MOV E,M INX H MOV D,M DCX D MOV M,D DCX H MOV M,E LHLD CHP ; ip[NEXTP] = cp+1; INX H XCHG LHLD IP LXI B,NEXTP DAD B MOV M,E INX H MOV M,D LHLD CHP ; if(*cp==CTRL_Z){ MOV A,M ANI 7FH ; /* mask parity in compare */ CPI CTRLZ JNZ GTCIF4 LHLD UNIT ; unit[FLAG] |= EOF_FL; LXI D,FLAGp PUSH B ; unit CALL GETC ; c = getc(unit); POP B MOV A,H ; if(c==EOF) /* c>255 */ ORA A JZ FGETS3 POP D ; cp LHLD SVCHP ; if (cp<>save_cp) XCHG ; /* read something */ MOV A,H CMP D JNZ FGETS4 ; goto fgets4; MOV A,L CMP E JNZ FGETS4 ; else LXI H,0 ; /* no characters */ POP D ; fix stack RET ; return (NULL); FGETS3: MOV A,L ; else { POP H MOV M,A ; *cp++ = c; INX H ANI 7FH ; /* mask parity in compare */ CPI LF ; if(L JNZ GETCRET PUSH H ; cget(unit); PUSH D ; /* to skip LF */ CALL CGET POP H POP H GETCRET: RET ; ; cget(unit) ; CGET: POP D POP H PUSH H PUSH D MOV A,H ORA A ; if(unit < 256) { JNZ CGET1 ; /* assume stdin */ CALL GETCHAR ; getchar(); POP D ; /* return to caller of getc() POP D ; to bypass CR check */ RET ; return; } CGET1: SHLD UNIT LXI D,FLAG ; if(unit[FLAG] & EOF_FL) DAD D MOV A,M ANI EOFFLG JZ GTCIF1 LXI H,-1 ; return(-1); RET 1: POP H ; } else { SHLD CHP DCX H ; save = buff[-1]; save2 = buff[-2]; MOV D,M ; buff[-1] = 0; buff[-2] = 79; MVI M,0 DCX H MOV E,M MVI M,79 ;6 May 80 rj PUSH H PUSH D XCHG ; cpm(GETSTR,buff-2); MVI C,GETSTR CALL BDOS LHLD CHP ; buff[buff[-1]] = 0; (9 Jun 80. Was cp) DCX H MOV E,M INX H MVI D,0 DAD D MVI M,0 POP D ; buff[-1] = save; buff[-2] = save2; POP H MOV M,E INX H MOV M,D INX H MVI C,PUTCH ; putchar('\n'); MVI E,LF CALL BDOS  DAD D MOV A,M ORI EOFFLG MOV M,A LXI H,-1 ; return(-1); RET ; } GTCIF4: MOV A,M MOV L,A ; return(*cp & 0377); MVI H,0 RET ; ; getchar() ; GETCHAR: LHLD RSTDIN ; if(rdstdin >= 256) { MOV A,H ; /* stdin has been redirected */ ORA A JZ GETCHR1 PUSH H CALL GETC ; getc(rdstdin); POP B ; return; RET GETCHR1: ; } else { /* read from console */ MVI C,GETCH ; t = cpm(GETCH,0) & 0377; CALL BDOS MOV L,A MVI H,0 ANI 7FH ; /* mask parity in compac=='\n') JNZ FGETS2 FGETS4: MVI M,0 ; *cp='\0'; POP D ; fix stack LHLD SVCHP ; return save_cp; RET ; } } } } ; ; putc(c,unit) ; FPUTC: PUTC: POP B ;rtn addr POP D ;unit POP H ;c PUSH H PUSH D PUSH B MOV A,D ORA A ; if(unit < 256) { JNZ PUTC4 ; /* assume stdout, stderr */ MOV A,E ; /* or stdlist. */ CPI STDOUT ; if(unit == stdout) { JNZ PUTC1 PUSH H CALL PUTCHAR ; putchar(c); POP H RET ; return;} PUTC1: CPI STDERR ; elseif(unit == stderr) { J GTCIF1: LHLD UNIT ; ip = unit + FCBSIZE; LXI D,FCBSIZE DAD D SHLD IP LXI D,NEXTP ; cp = ip[NEXTP]; DAD D MOV E,M INX H MOV D,M XCHG SHLD CHP LHLD IP ; if(ip[UNUSED]==0){ LXI D,UNUSED DAD D MOV A,M INX H ORA M JNZ GTCIF2 LXI H,READ ; if(cpmio(READ,unit)~=0) PUSH H LHLD UNIT PUSH H CALL CPMIO POP D POP D MOV A,H ORA L JZ GTCIF3 LXI H,-1 ; return(-1); RET GTCIF3: LHLD IP ; else { ip[UNUSED] = BUFSIZ; LXI D,UNUSED DAD D LXI D,BUF LHLD CHP ; return(buff); RET ; } ; ; fgets(cp,len,unit) ; FGETS: INX SP ; skip rtn addr INX SP POP B ; unit POP D ; length POP H ; cp PUSH H PUSH D PUSH B DCX SP DCX SP MOV A,B ; if(unit < 256) { ORA A ; /* assume stdin */ JNZ FGETS1 PUSH H CALL GETS ; gets(cp) POP B ; return (cp); RET ; } else { FGETS1: SHLD SVCHP ; save_cp = cp; PUSH D ; keep stack right FGETS2: POP D DCX D ; while (--len) { PUSH D MOV A,D ORA E JZ FGETS4 PUSH H ; save c  NZ PUTC2 CALL PUTCON ; putconsole(c); RET ; return;} PUTC2: CPI STDLIST ; elseif(unit == stdlist) { JNZ PUTC3 PUSH H CALL PUTLST ; putlist(c); POP H RET ; return;} PUTC3: JMP PTCER1 ; else goto putcerr; } PUTC4: PUSH H ; if(cput(c,unit)<0) PUSH D ; goto putcerr; CALL CPUT POP D MOV A,H ORA A JM PUTCERR MOV A,L ; if(c=='\r') CPI EOL JNZ PUTCRET LXI H,LF ; cput('\n',unit); PUSH H PUSH D CALL CPUT POP D POP D MOV A,H ORA A JM PUTCERR PS: POP B ; get args POP H PUSH H PUSH B PUSH H ; cp LHLD RSTDOUT MOV A,H ; if(rstdout >= 256) { ORA A JZ PUTS1 PUSH H CALL FPUTS ; return (fputs(cp, rstdout)); POP B POP B RET PUTS1: POP H ; } else { MOV A,M ; while(*cp) ORA A JZ PUTSRET MOV E,M ; putchar(*cp++); INX H PUSH H MVI C,PUTCH CALL BDOS JMP PUTS1 PUTSRET: ; return; RET ; } ; ; fputs(cp,unit) ; FPUTS: POP B POP D ; unit POP H ; cp PUSH H PUSH D PUSH B FPUTS1: MOV A, LHLD IP ; cp = &ip[BUFFER]; LXI D,BUFFER DAD D SHLD CHP ; } ; } PTCIF1: LHLD IP LXI D,UNUSED ; ip[UNUSED]--; DAD D MOV E,M INX H MOV D,M DCX D MOV M,D DCX H MOV M,E LHLD CHP ; ip[NEXTP] = cp+1; INX H XCHG LHLD IP LXI B,NEXTP DAD B MOV M,E INX H MOV M,D LDA ZCH ; return((*cp = c) & 0377); LHLD CHP MOV M,A MVI H,0 MOV L,A RET ; ; putchar(c) ; PUTCHAR: POP B POP H PUSH H PUSH B PUSH H LHLD RSTDOUT ; if(rdstdout >= 2RA H JZ DISKIF1 XCHG ; cpm(SELECT,d-1); DCX D MVI C,SELECT CALL BDOS DISKIF1: RET ; ;----------- End of Small-c library ----------- ; #endasm LECT,d-1); DCX D MVI C,SELECT CALL BDOS DISKIF1: RET ; ;----------- End of Small-UTCRET: POP H ; return(c); RET PUTCERR: ;putcerr: POP B ; return(-1); PTCER1: LXI H,-1 RET ; ; putlist(c) ; PUTLST: POP B POP D PUSH D PUSH B SHLD ZCH MVI C,LSTOUT ; cpm(LSTOUT,c); CALL BDOS LDA ZCH CPI EOL ; if(c==EOL) JNZ PUTLS1 MVI E,LF ; cpm(LSTOUT,LF); MVI C,LSTOUT CALL BDOS PUTLS1: LHLD ZCH ; return(c & 0377) MVI H,0 RET ; ; cput(c,unit) ; CPUT: POP B POP D POP H PUSH H PUSH D PUSH B SHLD ZCH XCHG SHLD UNIT LXI D,FCBSIZM ; while((c=*cp++) <> NULL) { INX H ORA A JZ FPUTS3 PUSH H MOV C,A MVI B,0 PUSH B PUSH D CALL PUTC ; if(putc(c,unit)==EOF) POP D POP B MOV A,H ORA A JZ FPUTS2 POP B RET ; return(EOF); FPUTS2: POP H JMP FPUTS1 ; } FPUTS3: LXI H,0 RET ; return(NULL); ; ; cpmio(fn,unit) ; CPMIO: POP B POP D POP H SHLD FN XCHG SHLD UNIT PUSH D PUSH H PUSH B LHLD UNIT ; cpmdisk(*unit); MOV L,M MVI H,0 PUSH H CALL CPMDISK POP H LHLD UNIT ; ip =56) { MOV A,H ; /* stdout has been redirected */ ORA A JZ PUTCHR1 PUSH H CALL PUTC ; putc(c, rdstdout); POP B POP B ; return; RET PUTCHR1: ; } else { POP H PUTCON: SHLD ZCH ; /* send to console */ XCHG ; cpm(PUTCH,c); MVI C,PUTCH CALL BDOS LDA ZCH ; if(c==EOL) ANI 7FH ; /* mask parity in compare */ CPI EOL JNZ PUTCHIF1 MVI E,LF ; cpm(PUTCH,LF); MVI C,PUTCH CALL BDOS PUTCHIF1: LHLD ZCH ; return(c & 0377); MVI H,0 RET ; } ; ; puts(cp) ; PUTE ; ip = unit + FCBSIZE; DAD D SHLD IP LXI D,NEXTP ; cp = ip[NEXTP]; DAD D MOV E,M INX H MOV D,M XCHG SHLD CHP LHLD IP ; if(ip[UNUSED]==0){ LXI D,UNUSED DAD D MOV A,M INX H ORA M JNZ PTCIF1 LXI H,WRITE ; if(cpmio(WRITE,unit)~=0) PUSH H LHLD UNIT PUSH H CALL CPMIO POP D POP D MOV A,H ORA L JZ PTCIF2 LXI H,-1 ; return(-1); RET PTCIF2: LHLD IP ; else { ip[UNUSED] = BUFSIZ; LXI D,UNUSED DAD D LXI D,BUFSIZ MOV M,E INX H MOV M,D  unit+FCBSIZE; LXI D,FCBSIZE ; cpm(DMA,&ip[BUFFER]); DAD D LXI D,BUFFER DAD D XCHG MVI C,DMA CALL BDOS LHLD FN ; t = cpm(fn,unit); MOV C,L LHLD UNIT XCHG CALL BDOS CALL CCSXT SHLD ZT MVI C,DMA ; cpm(DMA,TBUFF); LXI D,TBUFF CALL BDOS LHLD ZT ; if(t~=0) return(-1); MOV A,H ; else return(0); ORA L JNZ CPMIF1 LXI H,0 JMP CPMIF2 CPMIF1: LXI H,-1 CPMIF2: RET ; ; cpmdisk(disk) ; CPMDISK: POP D POP H PUSH H PUSH D MOV A,L ; if(d~=0) O  #CRC.COM 5.0 6/18/82CRCKFILE???!9" 1 M @ CRC Ver 5.0 CTL-S pauses, CTL-C aborts :] O@ ++Searching for CRCKLIST file++@ Now searching for "CRCKFILE" file++ !  1 .) F!  ͡]͸! ͔0CRCKLIST???CRCKFILE???!9" M à*,*.}|ډ!".*,{z{** u*.".G*.",!".***,}>*.#".ɯ22)! ",". <  NO FILECRC FILE$!   >. @ Checking wi match - : ̓ : J M @ Quantity of lines failed parse test - : ̓ : ~ M @ Quantity of file(s) not found - : ̓ * d͔  ͔ 0T  Җ Wyʩ 0T 0ztiil*#"ɯ2i2}! "!"]]2 s : ~# ++FILE NOT FOUND++$: F*}’">͇†]<  CANNOT CLOSE CRCFILE$CRCKLISTCRCSK FULL: CRCFILE$  w# !]͐ DONE$!e S!]Q !eZ @ --> FILE: XXXXXXXX.XXX CRC = oS) \<‘@ ++OPEN FAILED++ !" !" * | ʹ) \!~2 #" Ý: ) > T : ) @ ++FILE READ EIN HL ;INTO HL ; CCGINT: MOV A,M INX H MOV H,M MOV L,A RET ; CCDECC: INX H INX H DAD SP MOV D,H MOV E,L CALL CCGCHAR DCX H MOV A,L STAX D RET ; CCINCC: INX H INX H DAD SP MOV D,H MOV E,L CALL CCGCHAR INX H MOV A,L STAX D RET ; ; th file - ! ~T #M M 2 2 2 2 ! >2 3o: : @ ***No CRC Files found***$> ʉ ʉw#: <2 P6: .6@ Can not parse string ! ~ʽT #ñM ! > 6 #6^#6! ~T #! 4M #x] #e w~ T #>2 @ - s M@ File not found ! 4 >2 o: G: „: G: „@ *Match* 2 ! 4@ <-- is, was --> : ) > T : ) M 2 ! 4<2 ~# @ Not a space between CRC values<2 G-CATALOG??? #asm ; ;----- call.a: Small-C ver. 2 arithmetic and logical library ; (ASM Version 7-28-83, Bill Randle) ; ; M80 EQU 1 ; Compiler compiled with #define M80 ; to generate short names (unique in 6 chars). ; Set to 0 if compiler NOT compiled with M80 ; switch. ; CCDDGC: DAD D JMP CCGCHAR ; CCDSGC: INX H INX H RROR++ <* |): o% |g}o" 2  ; 0T ~T #~A > T > _h : F{͇2h2|: ʲ !\   :\2 ! \  \ ! \  \! \  \<7=Ɓo&   ]  2h2|2   ~$#~# x  : F} *}= ">͇1 ].”#””͡”››tK››͸›* =: =͔="  ʳ ª~#.  ¿~  #~  .+~#0:0~#!A.O.Gy~#0M0 K MMɷ:m2 FSÄCRCKLIST$$$››tK›, › **}|!"*{z*~]*"Ü  DISK FULL: CRCFILE$!"*~ DAD SP ; ;FETCH A SINGLE BYTE FROM THE ADDRESS IN HL AND ;SIGN EXTEND INTO HL ; CCGCHAR: MOV A,M ; ;PUT THE ACCUM INTO HL AND SIGN EXTEND THROUGH H. ; CCARGC: CCSXT: MOV L,A RLC SBB A MOV H,A RET ; CCDDGI: DAD D JMP CCGINT ; CCDSGI: INX H INX H DAD SP ; ;FETCH A FULL 16-BIT INTEGER FROM THE ADDRESS   ALL CCGINT DCX H JMP CCPINT ; CCINCI: INX H INX H DAD SP MOV D,H MOV E,L CALL CCGINT INX H JMP CCPINT ; ; IF M80 ; use short symbol name CCDDPI: ENDIF IF NOT M80 ; use full length name CCDDPDPI: ENDIF ; DAD D CCPDPI: POP B ;;RET ADDR POP D PUSH B ; ;STORE A  CMP H ;;COMPARE MSBS JNZ CCCMP1 ;;DONE IF NEQ MOV A,E ;;COMPARE LSBS CMP L CCCMP1: LXI H,1 ;;PRESET TRUE COND RET ; ;TEST IF DE >= HL (UNSIGNED) ; CCUGE: CALL CCUCMP RNC DCX H RET ; ;TEST IF DE < HL (UNSIGNED) ; CCULT: CALL CCUCMP RC DCX H RET ; ;TEST IF DE > HL (UNSIGNED)  CALL CCCMP RNZ DCX H RET ; ;TEST IF DE > HL (SIGNED) ; CCGT: XCHG CALL CCCMP RC DCX H RET ; ;TEST IF DE <= HL (SIGNED) ; CCLE: CALL CCCMP RZ RC DCX H RET ; ;TEST IF DE >= HL (SIGNED) ; CCGE: CALL CCCMP RNC DCX H RET ; ;TEST IF DE < HL (SIGNED) : MOV A,E SUB L MOV L,A MOV A,D SBB H MOV H,A RET ; ;FORM THE TWO'S COMPLEMENT OF HL ; CCNEG: CALL CCCOM INX H RET ; ;FORM THE ONE'S COMPLEMENT OF HL ; CCCOM: MOV A,H CMA MOV H,A MOV A,L CMA MOV L,A RET ; ;MULTIPLY DE BY HL AND RETURN IN HL ;(SIGNED MULTIPLY) ; C16-BIT INTEGER IN HL AT THE ADDRESS IN DE ; CCPINT: PINT: MOV A,L STAX D INX D MOV A,H STAX D RET ; ;INCLUSIVE "OR" HL AND DE INTO HL ; CCOR: MOV A,L ORA E MOV L,A MOV A,H ORA D MOV H,A RET ; ;EXCLUSIVE "OR" HL AND DE INTO HL ; CCXOR: MOV A,L XRA E MOV L,A MOV A,H XRA D MOV H,A ; CCUGT: XCHG CALL CCUCMP RC DCX H RET ; ;TEST IF DE <= HL (UNSIGNED) ; CCULE: CALL CCUCMP RZ RC DCX H RET ; ;COMMON ROUTINE TO PERFORM UNSIGNED COMPARE ;CARRY SET IF DE < HL ;ZERO/NONZERO SET ACCORDINGLY ; CCUCMP: MOV A,D CMP H JNZ CCUCP1 MOV A,E CMP L CCUCP1: LXI H,1 RET ;  ; CCLT: CALL CCCMP RC DCX H RET ; ;COMMON ROUTINE TO PERFORM A SIGNED COMPARE ; OF DE AND HL ;THIS ROUTINE PERFORMS DE - HL AND SETS THE CONDITIONS: ; CARRY REFLECTS SIGN OF DIFFERENCE (SET MEANS DE < HL) ; ZERO/NON-ZERO SET ACCORDING TO EQUALITY. ; CCCMP: MOV A,H ;;INVERT SIGN OF HL XRI 80H MOV H,A MOV A,D ;;INVERT SIGN OF DE XRI 80H CMULT: MULT: MOV B,H MOV C,L LXI H,0 CCMLT1: MOV A,C RRC JNC CCMLT2 DAD D CCMLT2: XRA A MOV A,B RAR MOV B,A MOV A,C RAR MOV C,A ORA B RZ XRA A MOV A,E RAL MOV E,A MOV A,D RAL MOV D,A ORA E RZ JMP CCMLT1 ; ;DIVIDE DE BY HL AND RETURN QUOTIENT IN HL, REMAINDER IN DE ;(SIGNED RET ; ;"AND" HL AND DE INTO HL ; CCAND: MOV A,L ANA E MOV L,A MOV A,H ANA D MOV H,A RET ; ;IN ALL THE FOLLOWING COMPARE ROUTINES, HL IS SET TO 1 IF THE ;CONDITION IS TRUE, OTHERWISE IT IS SET TO 0 (ZERO). ; ;TEST IF HL = DE ; CCEQ: CALL CCCMP RZ DCX H RET ; ;TEST IF DE <> HL ; CCNE:  ;SHIFT DE ARITHMETICALLY RIGHT BY HL AND RETURN IN HL ; CCASR: XCHG DCR E RM MOV A,H RAL MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A JMP CCASR+1 ; ;SHIFT DE ARITHMETICALLY LEFT BY HL AND RETURN IN HL ; CCASL: XCHG DCR E RM DAD H JMP CCASL+1 ; ;SUBTRACT HL FROM DE AND RETURN IN HL ; CCSUB   DIVIDE) ; CCDIV: DIV: MOV B,H MOV C,L MOV A,D XRA B PUSH PSW MOV A,D ORA A CM CCDENEG MOV A,B ORA A CM CCBCNEG MVI A,16 PUSH PSW XCHG LXI D,0 CCDIV1: DAD H CALL CCRDEL JZ CCDIV2 CALL CCCMPBCDE JM CCDIV2 MOV A,L ORI 1 MOV L,A MOV A,E SUB C MOV E,A MOV A,D SBB B ; ; LOGICAL NEGATION ; CCLNEG: MOV A,H ORA L JNZ $+6 MVI L,1 RET LXI H,0 RET ; ; EXECUTE "SWITCH" STATEMENT ; ; HL = SWITCH VALUE ; (SP) -> SWITCH TABLE ; DW ADDR1, VALUE1 ; DW ADDR2, VALUE2 ; ... ; DW 0 ; [JMP default] ; continuation ; CCSWITCH: X /* ** abs -- returns absolute value of nbr */ abs(nbr) int nbr; {if(nbr<0) return -nbr; else return nbr;}  MOV D,A CCDIV2: POP PSW DCR A JZ CCDIV3 PUSH PSW JMP CCDIV1 CCDIV3: POP PSW RP CALL CCDENEG XCHG CALL CCDENEG XCHG RET ; ;NEGATE THE INTEGER IN DE ;(INTERNAL ROUTINE) ; CCDENEG: MOV A,D CMA MOV D,A MOV A,E CMA MOV E,A INX D RET ; ;NEGATE THE INTEGER IN BC ;(INTERNAL ROUTINE)#include #include #include main() { printf("hello, world\n"); } CHG ;;DE = SWITCH VALUE POP H ;;HL -> SWITCH TABLE SWLOOP: MOV C,M INX H MOV B,M ;;BC -> CASE ADDR, ELSE 0 INX H MOV A,B ORA C JZ SWEND ;;DEFAULT OR CONTINUATION CODE MOV A,M INX H CMP E MOV A,M INX H JNZ SWLOOP CMP D JNZ SWLOOP MOV H,B ;;CASE MATCHED MOV L,C SWEND: PCHL ; #endasm  CMP D  ; CCBCNEG: MOV A,B CMA MOV B,A MOV A,C CMA MOV C,A INX B RET ; ;ROTATE DE LEFT ONE BIT ;(INTERNAL ROUTINE) ; CCRDEL: MOV A,E RAL MOV E,A MOV A,D RAL MOV D,A ORA E RET ; ;COMPARE BC TO DE ;(INTERNAL ROUTINE) ; CCCMPBCDE: MOV A,E SUB C MOV A,D SBB B RET    /* ** lib.h -- invoke a local copy of the function library */ #define PASSARGC /* pass arg count to called functions */ #asm LIB.R:LIBRY ABS:EXTRN DTOI:EXTRN ITOD:EXTRN ITOU:EXTRN ITOX:EXTRN LEFT:EXTRN PRINTF:EXTRN SIGN:EXTRN STRCMP:EXTRN UTOI:EXTRN XTOI:EXTRN #endasm CULE: EQU STDIO+00F2H CCUCMP: EQU STDIO+00F9H CCASR: EQU STDIO+0104H CCASL: EQU STDIO+0112H CCSUB: EQU STDIO+0119H CCNEG: EQU STDIO+0120H CCCOM: EQU STDIO+0125H CCMULT: EQU STDIO+012CH CCDIV: EQU STDIO+014CH CCLNEG: EQU STDIO+01AAH CCSWITCH:EQU STDIO+01B6H GETC: EQU STDIO+01D0H PUTC: EQU STDIO+01D3H FFLUSH: EQU STDIO+01D6H ABORT: EQU STDIO+01D9H EXIT: EQU STDIO+01DCH UNLINK: EQU STDIO+01DFH FGETC: EQU STDIO+01E2H GETCHAR:EQU STDIO+02D9H FGETS: EQU STDIO+02E2H FPUTC: EQE:EQU STDIO+001BH CCSEEK: EQU STDIO+001EH CCMODE: EQU STDIO+0021H CCFILE: EQU STDIO+0024H CCPURGE:EQU STDIO+0027H CCFLUSH:EQU STDIO+002AH CCALLOC:EQU STDIO+002DH CCFREE: EQU STDIO+0030H CCAVAIL:EQU STDIO+0033H CCDELIM:EQU STDIO+0036H CCDDGC: EQU STDIO+0039H CCDSGC: EQU STDIO+003DH CCGCHAR:EQU STDIO+0040H CCARGC: EQU STDIO+0041H CCSXT: EQU STDIO+0041H CCDDGI: EQU STDIO+0046H CCDSGI: EQU STDIO+004AH CCGINT: EQU STDIO+004DH CCDECC: EQU STDIO+0052H CCINCC: EQU STDIO+005EH CCDDPDPC:EQU STD /* ** vm.h -- header for resident VM interface */ #asm VM: EQU 0B000H;;VIRTUAL MACHINE BASE CCBOJ: ENTRY JMP VM+08H CCEXIT: ENTRY JMP VM+0BH CCPOLL: ENTRY JMP VM+0EH CCHALT: ENTRY JMP VM+11H CCOPEN: ENTRY JMP VM+14H CCCLOSE:ENTRY JMP VM+17H CCREAD: ENTRY JMP VM+1AH CCWRITE:ENTRY JMP VM+1DH CCSEEK: ENTRY JMP VM+20H CCMODE: ENTRY JMP VM+23H CCFILE: ENTRY JMP VM+26H CCPURGE:ENTRY JU STDIO+050DH PU$CHAR:EQU STDIO+0579H FPUTS: EQU STDIO+058AH PUTS: EQU STDIO+0699H FOPEN: EQU STDIO+06B5H FCLOSE: EQU STDIO+078BH GETARG: EQU STDIO+07E7H #endasm IO+006AH CCPDPC: EQU STDIO+006BH CCPCHAR:EQU STDIO+006EH CCDECI: EQU STDIO+0071H CCINCI: EQU STDIO+007DH CCDDPDPI:EQU STDIO+0089H CCPDPI: EQU STDIO+008AH CCPINT: EQU STDIO+008DH CCOR: EQU STDIO+0093H CCXOR: EQU STDIO+009AH CCAND: EQU STDIO+00A1H CCEQ: EQU STDIO+00A8H CCNE: EQU STDIO+00AEH CCGT: EQU STDIO+00B4H CCLE: EQU STDIO+00BBH CCGE: EQU STDIO+00C2H CCLT: EQU STDIO+00C8H CCCMP: EQU STDIO+00CEH CCUGE: EQU STDIO+00DFH CCULT: EQU STDIO+00E5H CCUGT: EQU STDIO+00EBH CMP VM+29H CCFLUSH:ENTRY JMP VM+2CH CCALLOC:ENTRY JMP VM+2FH CCFREE: ENTRY JMP VM+32H CCAVAIL:ENTRY JMP VM+35H CCDELIM:ENTRY JMP VM+38H #endasm  /* ** stdio.h -- header for resident STDIO/CALL, and VM interfaces */ #define stdin 0 #define stdout 1 #define stderr 2 #define stdport 3 #define stdlist 4 #define CCEOM 45062 #define ERR -2 #define EOF -1 #define YES 1 #define NO 0 #define NULL 0 #define CR 13 #define LF 10 #asm STDIO:EQU 0E00H;;STDIO BASE CCBOJ: EQU STDIO+0006H CCEXIT: EQU STDIO+0009H CCPOLL: EQU STDIO+000CH CCHALT: EQU STDIO+000FH CCOPEN: EQU STDIO+0012H CCCLOSE:EQU STDIO+0015H CCREAD: EQU STDIO+0018H CCWRIT   /* ** vml.h -- header for local VM interface */ #asm VM.R: LIBRY CCBOJ: EXTRN CCEXIT: EXTRN CCPOLL: EXTRN CCHALT: EXTRN CCOPEN: EXTRN CCCLOSE:EXTRN CCDELIM:EXTRN CCREAD: EXTRN CCWRITE:EXTRN CCSEEK: EXTRN CCMODE: EXTRN CCFILE: EXTRN CCPURGE:EXTRN CCFLUSH:EXTRN CCALLOC:EXTRN CCFREE: EXTRN CCAVAIL:EXTRN #endasm !9"<2:g.!"!"!͍G~ R><͍{ j>R>!>G"s#r# PGMNAME5[?!j!f|[">~R5[?!j!h|[">~R 5!jw#V [ E6Mz rwT:6 0'{2,6iolib: Unable to open < or > file$33;;33;;:g.*og*|:_*!! /* ** lib.c -- function library ** ** Copyright 1982 J. E. Hendrix */ #define NOCCARGC /* don't pass arg count to functions */ /* called by these functions */ #include "abs.c" #include "dtoi.c" #include "itod.c" #include "itou.c" #include "itox.c" #include "left.c" #include "out.c" #include "printf.c" #include "sign.c" #include "strcmp.c" #include "utoi.c" #include "xtoi.c"  /* ** stdioa.h -- header for resident STDIO/CALL, and VM interfaces (ASM) */ #define stdin 0 #define stdout 1 #define stderr 2 #define stdport 3 #define stdlist 4 #define CCEOM 45062 #define ERR -2 #define EOF -1 #define YES 1 #define NO 0 #define NULL 0 #define CR 13 #define LF 10 define CCEOM 45062 #define ERR -2 #define EOF -1 #define YES 1 #define NO 0 # !͑ Aborted, reason = !p>:-!6!6!~8/(#~d&(~w,'T(l(~w#w#w#w"<6c(:6 xfx 2+6*<6"86> x:7Ä"<6c(:6 xf*<6#~l~@ w#s#r#*6s#r.C:E8R*:6ă*!64~=J)!7~(!7~(#7~# ( (( ( (!7p"86Ga#!CE"e84 1)"86~w#~@F*~ )#^#VG )*6 G *6{Gc*?6!".6"06!*u!7>4!*̀8>4͏** 6|ʥ):8<2727ë)!&*ͫ*!**ͫ** 6| /* ** std.h -- header for local STDIO for C/80 */ #define stdin 32 #define stdout 32 #define stderr 32 #define stdport 34 #define stdlist 35 #define CCEOM 45062 #define ERR -2 #define EOF -1 #define YES 1 #define NO 0 #define NULL 0 #define CR 13 #define LF 10   )>,C:8<>,dB:8<2727!:*ͫ*͏*)!G*7|*)Gͭ:8*>CQCNo Fatal error(s) Warning(s):6GG )ͨ*!a*ͫ*Ï*REPT/IRP/IRPC/MACROUnterminated ͨ*!*ͫ*> > :8<> dB> ""#"|$" * *  s#r**͌*n&h *~rDRm*]*A!* s#r*~w{W¸***A!* s#r*!>w*A!*"|.!"*!~N!*͜ *$" * ͜ | !""*n&h *$*M*͈"*|d !g !}x G"^#V" * " * * z*6{6* 6#" !* |N!"*a!"*A*!f"*A:©@ ڬ ì:& ..&.! 6}.a{ -..} |-&} %&|5"!~F!*$" ^#V" * ~#•!* |~!* s#r* " * ^#Vr+s* #*  s#r* ~*!~w!~o&*| o&!  *| !PJ " +V6+^6O * +^#6s#r# * 33;;x_ "zʕ |ʊ *|• }• !}w# c 6*z {³ ͓ ¼ ͨ   | } !  | !":  *&""$" ^#V" * ~#k !* |T !* s#r* " * ^#Vr+s* #*  s#r:* w&o*|ʧ ͜ ":  *&*| ~ ^# ~# O  !*!!`"!! "!!"9""7""+""/""-""%"" """"""!"!!!!}!"?"""!!""!!!"}!"3""1"%ͳ'K!"!!";"!"="Zwi(#ͧ%pw*5"too many command argumentsSmall-C Ver. 2.03 (ASM) (7/28/83)*/"|`$!a$!T|$!q+Z$!q+|$Z$!h$͕T|1$ͅ@Z$!m$͕T|D$ *Z$!v$͕T|W$kNZ$1V#extern#asm#include#define!9!ԇ* "|#%!9͔&|!9! ԇ!9T]͔+ԇ#| %!s!9͔DP!9!9͔ԇ!]! !ԇ!=! !ԇ!]!!Ȁԇ!=!!iԇ!]!!ԇ!=!!ԇ!]!!=!!F~ԇԇ!]!!=!!U~ԇԇ!]!!=!!}ԇԇ!]!!=!!}ԇԇ!]!!=!!}ԇԇ!]!!=!!}ԇԇ!]!!=!!~ԇԇ!9*3"!|/*!1+cRH!9V*!͇!"*!͇!<ڇ|*!9!ԇ!9!9ԇ!|*!9*!#"!͇}!9͇!"!9͇!>ڇ|ʿ*!9!ԇ*!9T]͔#ԇ+!9͇}m*!9͔!}!9!9ԇ+!9*!* " ڇ|%HR %!,JQï$Ë$!9͔%ʥ%!9͔&|!9!ԇ!9T]͔+ԇ#|ʢ%!P!9T]͔+ԇ!!ڇ|ʗ%HRâ%!,JQN%'%*+"|ʷ%!%cRno closing bracket!9!"""A"ԇ!"5"!}2 }2 }2~ }2} }2| !J "!!9T]͔#ԇ*!|t'!!)͔"!*!͇!-|q'*!͇|EM'*!͇BF*!͇! |ʏ&*!͇!0`"A"q'Ò&'!}2} q'!}2~ q'!}2 q'!}2 q'!}2| q'*!͇BF*!͇! |'!G!G!!9͔! |p"!#!Q!!9͔"!!9!ԇ!9T]͔+ԇ#|"!!!9T]͔#ԇ+)!9T]͔##ԇ++͔ԇÅ"!#!Q!#!Q!#!Q!#!Q! "!9͔!R+"3"|(+!"3"!T+cRH!9nested include files not allowedropen failure on include file!+!T|ʛ+!!9͔+8!!+!T!9͔!ڇ|+!!9͔+8!!charintA|+!=-͕T|,!9!ԇ!9!ԇ.,!9!ԇ!9!ԇ!0!!D|C,5A!0!ͦA|X,!0!PA!?-͕T|s,!9!ԇì,!B-͕T|ʬ,!9͂1ԇ!|ʡ,!D-cR!9!ԇ!9͔!|,!0!;w,!9! 9͔!K!9͔!9͔I/ԇ!0!"+t|'q'!v'!Q!'!Q!'!Q!'!Q!q'K&L&A&M&C&P&O&B'&usage: cc [file]... [-c] [-m] [-a] [-p] [-l#] [-o] [-b#] !"1"!"!*9"#"9"*!|:(!!*9")͔"!*!͇!-|'ÿ'*!!Z("1"|-(!\(!Q*!!Q!!"7"Hÿ'*7"#"7"+|P(!"/"V(!"1"Hropen error: !]!!=!! ~ԇԇ!]!!=!!~ԇԇ!]!!=!!'~ԇԇ!]!!=!!~ԇԇ!]!!=!!$ԇԇ!]! !ԇ!=! !ԇ!  ! 9͔!9͔!!!9͔[B! 9!a-͕T|7-+*()[declaration type not allowed,*;!|u-!.cR*"-!/cRA|ʐ-!#/͕T|ʫ-!9!ԇö-!9!ԇ!0!!D|-5A!9!ԇ!%/͕T|S.!9͂1ԇ|E.!| .!'/cR!9!ԇ!9͔!|B.!9!9͔!YԇP.!9!ԇÛ.!D/͕T|n.!9!ԇÛ.!9͔!!|ʛ.!9!ԇ*"""!0!! 9͔*%"*"`!!![B! 9.Å-!G/͕T|.Å-not allowed wit9!"C"Å8!8!T|(7:!"C"Å8!8!T|F7*;!"C"Å8!8!T|d7͢;! "C"Å8!8!T|ʂ7͆! "C"Å8!8!T|7͢>! "C"Å8?|7Å8!8!T|8?8!"C"Å8!8!T|)8?8!"C"Å8!8!T|J8-@8!"C"Å8!8͕T|`8!"-"Å8!8͕T|y8ͅ@!"C"Å8v:8!"C"*C"charint{ifwhiledoforswitchcasedefaultgotoreturnbreakcontinue;#asm!8͕T|8!8cR8!"-";no se"'"!n4͕T|­3!0!!D|j3!0!A|;3!0!PAg3!0!!!*'"!!![B! 9*'""'"u3!p4cR@V*!!4cS|Ÿ3!4͕T|Ÿ3!4cRA|ʪ3í33!"%"*'"")"*'"|4!4!T|3!Ͷ48 4!4!T|4!Ͷ48 4!4cR4ù3H6!|!4n{* "|<4*!"EZR!~$illegal function or declaration(no open paren)illegal argument name),no commacharintwrong number of arguments!9*'"|4!9! 6͕T|4!9!ԇ4!9!ԇ!9!0!!D{H6! 9͔ͨ{!9͔EA{*!,|x=!&|!9T]͔##ԇ++͔E!,JQ!9T]͔##ԇ++͔PHR%=!&|!PHR*!|ʛ=*!ͨ{! 9͔E@G"!!9͔"!!9͔"!!9()*!|=!*>cR*!*!2|=!8>cR*!##"!++ͺEԇE*!##"!++r!G>iAnot in switchtoo many cases:*!|d>*!|a>!>cRl>!>cR!>iAͺE"!Emultiple defaultsnot in switch:*9!>ʹ>!>cRÿ>!";!!0!!D|>`?ͨ{>!?cR8not allowed with block-lh gotomust declare first in block*[declaration type not allowed(),!" "!9͔|g/!9!ԇ!9!9͔ԇͯw!e0͕T|/!g0͕T|/!9͔|/!9͔!9͔!9m0!i0͕T|//Ó/!k0iA/!9͔!9͔!9m0!9͔!!9͔|:0!! 9!ԇu!9!ԇ!9͔~$!9͔!9͔&%!9͔={,}!9qu|0!9͔!! 9͔!ڇ|ʪ0!D1cR!9͔!9͔͔* "!9͔``ԇ!9͔!|0Q|B1!9r|B1!9͔micolon!9*%"ԇ!9*!ԇ!""*+"#"+"!9͕T|V9*/"|P9!9cRV9S9H6-9*+"+"+"!a|"%""!!*!!*!,|9*!!C"#!*!!͇|9*!!*#!,|9!9T]͔#ԇ+*!!#"!!+͇}Ø99*#!"!!s9"!!""}no final }!9ͺEԇ!͉pH6!q:!T|*:E!9ͺEԇ*C"!*C"! |[:ͨ{EH6Eelse!9!9!9O!9!9__O*"!,|ʺ::!G{:!9!9!9͂F!9͔E!9͔!͉pHԇ| 55A!6͕T|]50H!]|75A|45755!9͔!|R5!6cR!9!ԇ!9͔|5!9!0!Aԇ|5!9͔}! 9͔}*)"!D`!̓D5!-6cR*'"!`"'"A|5!9!=6͕T|6!?6cRû4!9*[declaration type not allowednot an argument,no comma*"!*/"|_6!8!T|6!c-8Å8!8!T|ʟ6!c-8Å8*"6*+"!|ʼ6*""9!*%"*"`!a|"%"!""!8͕T|69Å8!8!T| 7ocalsbad labelV!9*!ԇ!0!!D|Z?G!:|M?`?E!*!`G!!0!A"!!|ʆ?*!!͇|ʃ?!?cRë?!0!!!ͺE!!![B! 9"!!*!!!Dnot a labelA|?v:!!a|?!!a|n{!9*"]Gԇ|@͔!a|͔ͨ{!9*"ԇ!9]Gԇ|R@͔|c@f@9@͔!a|͔ͨ{!"?"NH!@͕T|ʞ@ù@*/"|ʩ@ù@*!*5"QË@H!"?"#endasm@0HdF|@*"dF|@G@A*"dF|A*!|1!i1cR! 9͔u!9͔!9͔͔!`ԇmust assign to char pointer or arraycannot assign to pointer!1͕T|ʕ1!!9r|®1!9!ԇ寴1!1cR!9gԇ!1iA]negative size illegal]!";!"9!!" ""C"ͺE"!"!"!:~ ͈|(2*!!Q!0!!D|G2!>4cRH*"|]2*#"E!""!9!0!ͦAԇ|ʾ2͇!|ʏ2!0!PAû2͇!|ʯ2!0!PAû2!}2!0!!!!!!![B! 9!^4͕T|2!`4cRͯw!"!!6!9͔ͨ{!9͔E@G!9!9!9͂F!9ͺEԇEH6!;iA!9͔E!9͔!͉pͨ{!9͔E@G8! 9while!9!9͂F!9ͺEԇ!9ͺEԇ!|VVBV!9!9!ԇ!9!9͔!9͔Xԇ!9͔U|V*!G!9͔|¬V!9!ԇ!9ͺEԇ!9͔!9͔!9͔!9͔ͭW!9äW!9͔|ʕW!9͔!9͔!9͔!_!9͔s!9͔!}|6_!9͔͓!9͔!~|Z_!9͔͓!!9!9_|{_!9Fp!9͔|ʳ_!9͔!ԇ!9͔!9͔ԇ_!9͔!ԇ!9!9!9!9!tb!9͔Xԇ!9͔͔|`!9͔͔os!Rb͕T|+`!9! ~ԇHa!Ub͕T|F`!9!~ԇHa!Xb͕T|a`!9!'~ԇHa![b͕T||`!9!}ԇHa!^b͕T|ʗ`!9!}ԇHa!ab͕T|ʲ`!9!}ԇHa!db͕T|`!9!}ԇHa!gb͕T|`!9!~ԇHa!jb͕T|a!9!F~ԇ!9͔!}!9͔!}ڇ|z[!9͔!9͔͖n|D[ͷ}!9͔! 9͔͖n|z[͑yͷ}!9͔!}|z[͑y!9͔|(]!9͔! 9͔͔! 9͔͔ԇ|\!9͔! 9͔͔!9͔! 9͔͔+]ԇ!O!9͔ !ԇØ\!9͔͔!!9͔͔!|u\!9͔!\\!9͔ !9͔ԇØ\! 9͔!\!9͔ ! 9͔ԇ!9͔!}|\!9͔͔!!9͔͔!|\͑y!osF~!9͔!}!9g͕T|Re!9͔,d|(e!9͔Fp6~!9͔!9͔͔ԇ!!g͕T|ʦe!9͔,d||e!9͔Fpd~!9͔!9͔͔gԇ!!g͕T|Bf!9͔,d|e!9͔Fp!9!9͔͔ԇ|f!9͔͇ԇf!9͔!ԇ!9͔!ԇ!9͔!ԇ!!g͕T|f!9͔,d|nf!gcR!!9!9͔͔ԇ!9͔͇ԇ!9͔͔|ʵf!t!9͔͇ԇ!!9!9͔gԇ!g͕T|Bg9͔ͭW!9!9͔os!9ͺEԇͨ{E!9͔osE! 9͔! 9͔!9͔!9͔!ԇԇԇԇ!!9!9͔!9RV!9!9͔|WFpW͔|W͔os!9͔!9͔!W!9!9!9͔!9͔Xԇ!9͔U|@X!9͔!9!9͔|XX!9͔Fp!9͔U|ʼX*!G*}!!9͔"}!!=!*}!)͔!]!*}!)͔!9͔!9͔!9/Y! 9X!!9XX!9!9!9O!9! 9͔! 9͔!XHa!nb͕T|a!9!U~ԇHa!rb͕T|9a!9!ԇHa!9͔!9!9͔|ba͎A!!9!9͔͔|a|ʍay!9͔Fp!9͔!_!9͔! 9/Y! 9|a{6b|b!9͔Fp!9͔!_!9͔! 9/Y! 96b!9_|b!9Fp!9͔ !9 ͔ԇ!9͔ p!!9!9|=^=&=+=-=*=/=%=>>=<<==!b!~!!!9!b!9͔BV! 9||!b!2!!!9!b!9͔BV! 9&&!b!!9!b! 9͔W!9|!͔!}ڇ|(]!9͔!9͔n!9͔! ~|N]!9͔ڇ!9͔!~|q]!9͔!9͔!'~|ʔ]!9͔!9͔!~|ʷ]!9͔!9͔!$|]!9͔!9͔!|]!9͔!9͔!| ^!9͔ !9͔!i|C^!9͔!9͔!|f^!9͔!9͔!F~|ʉ^!9͔K!9͔!U~|ʬ^!9͔Y!9͔!}|^!9͔!9͔!}|^!9͔`!9͔!}||g͎A!!~!9͔xo!9͔͔!KͰ~!!g͕T|ʒg|ag͎A!!~!9͔xo!9͔͔!K͂~!++--~!-*&illegal address++--!9!9! 9͔jԇ!9! 9͔͔ԇV*"![*"!(ڇ|hj!9͔ !ԇ!j͕T|i!9͔|Yh!jcR@!jiA!!9!9͔͇!|~h!9͔Fpçh!9͔͇!|ʧh!jcR!9!ԇ!9!9O!9!ԇ!!!_! 9!9/Y! 9!jiA!9͔|eiԇ!9͔͔|&Y!9͔!O!9!9O!9͔ !ԇ!9͔!ԇ!9͔͔|Y! 9͔!9͔X|ʝY!9͔Fp!9͔͔|Y!9͔*!ԇ!9͔͔!9͔! 9͔͖nYsz[y! 9͔!9͔X|Z!9͔Fp!9͔͔|Z!9͔͔|RZ!9͔ԇ!9͔!}|ʭZ*%""%"!O!9͔͔! 9͔! 9͔͖nYsZ!9͔͔! 9͔! 9͔͖nYos!9͔yz[!9͔y$c!!9!&c! 9͔W!9^!Lc!!9!Nc! 9͔W!9&!tc!!9!zc! 9͔W!9== !=!c!!9!c! 9͔W!9<= >= < >!c! !9!c! 9͔W!9>> <<!c! !9!d! 9͔W!9+ -!&d! !9!,d! 9͔W!9* / %!g͕T|ld!9͔,d|Ud͎A!!~!9͔xo!!g͕T|ʪd!9͔,d|“d͎A!!~!9͔xo!!g͕T|d!9͔,d|d!9͔Fps~!9͔!9͔͔lԇ!!  !O!9͔|bi!9͔͇!|Oi!9͔!Ys_i!9͔s}Åi!9͔͇!|ʂiͷ}}!9͔! 9͔!ԇԇ!9͔!9͔͇ԇ!9!ԇej!j͕T|Vj!9͔|i!͐m,j!9͔͇!| j!9͔Fp!͐m,j!9͔͐m!9! 9͔!"9͔!ԇԇԇej!9͔!9"h!9͔|ƒj!9͔!9!9͔͇!|ʰj!9͔t!!9!9͔!9!9[can't subscript]can't subscript](!em͕T|k!9!9!9O!9! 9͔__!O!9͔|Es!SscR!9͔must be constant expression͡yPHRͱyPHR!9!9͔ԇ!ԇ!9T]͔##ԇ+t|s͡ytt|s͡ytqu|t!`!ԇ͡y*!"E!+JQt!͔PHR!!9!9!ԇԇ!t͕T|Qtot!t͕T|lt!9!ԇotrtAt*"BF|…t!*"BF|ʹt!9!9͔! s0H!0`ԇÅt|t!9gԇ!9͔ԇ!+-͡yQHR!!9͔!ԇwo͔|wo!9͔!9͔͔ԇ!9͔!9͔͔ԇ!9͔!9͔͔ԇ͔|o ͔|oyFp!9͔!9͔͔!K!o{ ppy !ԇFp!9͔!9͔͔!K!p p͔|z!9T]͔#ԇ+!9T]͔#ԇ+͇}z!9*!ԇ!9T]͔+ԇ!9͔2|z!{cS|z!9T]͔+ԇ!9!ԇ!9T]͔+ԇ͇BF|z͇!9͔`}!0|z͇ }!9!ԇz!9!ԇÏzIz*%"!9͔_ԇ!gmiA!!9͔!̓D!0!!D|Im!9!0!Aԇ|&l͇|pkim!x!9͔ԇ!9͔!9͔͇ԇ͇!|k!9͔!ԇ!9͔!9͔͇ԇ͇!| l!9͔!9͔͇ԇ!!!9!0!ͦAԇ|l͇!|l!9͔ԇ!9͔!ԇ͇!|ʺl͇!|ʴl!9͔!9͔͇ԇ!t!9͔!9͔!9͔͇ԇԇ!!9!0!9!ԇ!ou͕T|u!*"!'|Uu!9!!s@v!ԇu*!#"!!9͔ԇ!';!!͕T|„u!3!9͔* "ԇ*"!"|ʼu*"|¬uüu@v!uÒuG!s* "#" "+!}!33* "! |u!)vcR!!9͔!s* "!9͔̓D* "" "literal queue overflow*"!\*"!ڇ|fvGG*"!n|ʁvG! *"!t|ʙvG! *"!b|ʱvG!*"!f|vG! !9!ԇ!9!ԇ!9T]͔+ԇ#!*"!0 !9͔|Gq!O!9͔|5q!9!9͔ͨ{!9!9͔|r!9 ͔""*"!~*"!ڇ|ʝq!~!9͔!9{r*"!$*"!ڇ|q!2!9͔!9{r*"!|q!*!9͔!9{r*"!|'r!!9͔!9{r*"!|Nr!9͔!Or*"!i|wr!w!9͔!9{r*"!Ȁ|ʠr!׀!9͔!9{r*"!|r!!9͔!9{r!9͔{r!9͔{O!9(,)"%" XCHG;; DAD SP!-{Q*%""%"POP D!<{QXTHL!J{S{CCSWITCH!h{QQHRCALL !w{QRET͡y!{QHR3{!{Q*%""%"$+5PCHL!{QEHRJMP !{Q!{Q!{QEHRMOV A,HORA LJZ ͔!O!9͔!9͔!$|!|@|!I|QH|!M|QDB DW !Z|QDW $+2!9!9͔*%"`ԇ|‰|!9͔寴|!||!||!}Q!9T]͔+ԇ#||!}Q!9!`ԇ|!9͔寴Q}!|!!!!!![B! 9ԇ!9͔ԇ!9͔!ԇ!!9͔͏s|]mim!()!}mcR!os@invalid expression!9!ԇV!9͔|°my*!!ncS|nA|mn!9!9__!9͔|m3{y!9!9͔ԇ!n͕T|nnðm!niA!9͔!ncS|Jn!9͔!K}w!9͔|in!9͔S{ln{{*%"!9͔!a|"%"),)CCARGC!9͔͔!|ʴn!͔|n!!!9͔͔!!9͔͔!|o*"!7|:w!9!YG!0`ԇv!|OwGZwͺE"#"*#"!|ow!ywQEND!w)}o{_zW=ɈéԈԈz/W{/_x/Gy/O{_zW{z|.!NS|'!Q!9!9͔;ԇn!cS|Y!.Q!9!9͔@ԇn!:cS|ʋ!zQ!9!9͔@ԇn!cS|ʽ!Q!9!9͔ԇn!cS|!̆Q!9!9͔ԇn!؆cS|!!Q!9!9͔ԇn!cS|S!Q!9!9͔ԇn!9T]͔#ԇ+͇*5"ͳQÌ!9T]͔#ԇ+͇*5"ͳQ LXI H,0 DAD SP CALL CCGINT XCHG;; LXI H,2 DAD SP CALL CCGINT XCHG;; DAD SP CALL CCGINTCALL CCDSGI DA /* ** dtoi -- convert signed decimal string to integer nbr ** returns field length, else ERR on error */ dtoi(decstr, nbr) char *decstr; int *nbr; { int len, s; if((*decstr)=='-') {s=1; ++decstr;} else s=0; if((len=utoi(decstr, nbr))<0) return ERR; if(*nbr<0) return ERR; if(s) {*nbr = -*nbr; return ++len;} else return len; } QEHRMOV A,HORA LJNZ !-S{CCNE!WQ!_Q!eQEHRMOV A,HORA LJZ !rS{CCLT!Q!Q!QEHRXRA AORA HJP !S{CCLE!Q!Q!Q! Q!Q!QEHRMOV A,HORA LJZ $+8XRA AORA HJP !%S{CCGT!kQ!qQ!wQEHR!{Q!QEHRXRA AORA HJM ORA LJZ !S{CCGE!Q!Q!ĀQEHRXRA AORA HJM !рS{CCULT!QEHRJMP !S{CCULE#F#x~#~#`izԈx܈>)}o{_zW=ɈéԈԈz/W{/_x/Gy/O{_zW{z|.!ND D CALL CCGINTCALL CCDDGI DAD SP CALL CCGCHARCALL CCDSGC DAD D CALL CCGCHARCALL CCDDGC DAD SP MOV D,H MOV E,L CALL CCGINT INX H CALL CCPINTCALL CCINCI DAD SP MOV D,H MOV E,L CALL CCGINT DCX H CALL CCPINTCALL CCDECI DAD SP MOV D,H MOV E,L CALL CCGCHAR INX H MOV A,L STAX DCALL CCINCC DAD SP MOV D,H MOV E,L CALL CCGCHAR DCX H MOV A,L STAX DCALL CCDECC DAD D POP D CALL CCPINTCALL CCDDPI DAD D POP D MOV A,L STAX DCALL CCDDPC POP D CALL CCPINTCALL CCPDPI POP D ! S{CCUGT!S{CCUGE͇|ʏ!cS|ʈ!cS|n:!9!9͔&ԇÅ!9!9͔ԇÌ!cS|!ԄcS|с!:X!9!9͔&ԇ!X!9!9͔ԇÌ:| ͈|q!܄cS|-!Q!9!9͔ԇn!cS|_!Q!9!9͔ԇn!cS|ʑ!3Q!9!9͔ԇn!?cS|Â!TQ!9!9͔ԇn!`cS|!Q!9!9͔;ԇn!c   /* ** itod -- convert nbr to signed decimal string of width sz ** right adjusted, blank filled; returns str ** ** if sz > 0 terminate with null byte ** if sz = 0 find end of string ** if sz < 0 use last byte for data */ itod(nbr, str, sz) int nbr; char str[]; int sz; { char sgn; if(nbr<0) {nbr = -nbr; sgn='-';} else sgn=' '; if(sz>0) str[--sz]=NULL; else if(sz<0) sz = -sz; else while(str[sz]!=NULL) ++sz; while(sz) { str[--sz]=(nbr%10+'0');=55; str[--sz]=digit+offset; if(nbr==0) break; } while(sz) str[--sz]=' '; return str; } =((nbr%5)<<1)+lowbit+'0'; if((nbr=nbr/5)==0) break; } while(sz) str[--sz]=' '; return str; } #define ERRCODE 7 /* op sys return code */ cout(c, fd) char c; int fd; { if(fputc(c, fd)==EOF) xout(); } sout(string, fd) char *string; int fd; { if(fputs(string, fd)==EOF) xout(); } lout(line, fd) char *line; int fd; { sout(line, fd); cout('\n', fd); } xout() { fputs("output error\n", stderr); abort(ERRCODE); }  if((nbr=nbr/10)==0) break; } if(sz) str[--sz]=sgn; while(sz>0) str[--sz]=' '; return str; }  /* ** left -- left adjust and null terminate a string */ left(str) char *str; { char *str2; str2=str; while(*str2==' ') ++str2; while(*str++ = *str2++); }  /* ** itox -- converts nbr to hex string of length sz ** right adjusted and blank filled, returns str ** ** if sz > 0 terminate with null byte ** if sz = 0 find end of string ** if sz < 0 use last byte for data */ itox(nbr, str, sz) int nbr; char str[]; int sz; { int digit, offset; if(sz>0) str[--sz]=NULL; else if(sz<0) sz = -sz; else while(str[sz]!=NULL) ++sz; while(sz) { digit=nbr&15; nbr=(nbr>>4)&4095; if(digit<10) offset=48; else offset /* ** itou -- convert nbr to unsigned decimal string of width sz ** right adjusted, blank filled; returns str ** ** if sz > 0 terminate with null byte ** if sz = 0 find end of string ** if sz < 0 use last byte for data */ itou(nbr, str, sz) int nbr; char str[]; int sz; { int lowbit; if(sz>0) str[--sz]=NULL; else if(sz<0) sz = -sz; else while(str[sz]!=NULL) ++sz; while(sz) { lowbit=nbr&1; nbr=(nbr>>1)&32767; /* divide by 2 */ str[--sz]   /* ** printf(controlstring, arg, arg, ...) -- formatted print ** operates as described by Kernighan & Ritchie ** only d, x, c, s, and u specs are supported. */ printf(argc) int argc; { int i, width, prec, preclen, len, *nxtarg; char *ctl, *cx, c, right, str[7], *sptr, pad; i = CCARGC(); /* fetch arg count from A reg first */ nxtarg = &argc + i - 1; ctl = *nxtarg; while(c = *ctl++) { if(c!='%') {cout(c, stdout); continue;} if(*ctl=='%') {cout(*ctl++, stdout) /* ** utoi -- convert unsigned decimal string to integer nbr ** returns field size, else ERR on error */ utoi(decstr, nbr) char *decstr; int *nbr; { int d,t; d=0; *nbr=0; while((*decstr>='0')&(*decstr<='9')) { t = *nbr;t=(10*t) + (*decstr++ - '0'); if ((t>=0)&(*nbr<0)) return ERR; d++; *nbr=t; } return d; } ; continue;} cx=ctl; if(*cx=='-') {right=0; ++cx;} else right=1; if(*cx=='0') {pad='0'; ++cx;} else pad=' '; if((i=utoi(cx, &width)) >= 0) cx=cx+i; else continue; if(*cx=='.') { if((preclen=utoi(++cx, &prec)) >= 0) cx=cx+preclen; else continue; } else preclen=0; sptr=str; c = *cx++; i = *(--nxtarg); if(c=='d') itod(i, str, 7); else if(c=='x') itox(i, str, 7); else if(c=='c') {str[0]=i; str[1]=NULL;} else if(c=='s') sptr=i; else /* ** strcmp -- return -1, 0, +1 depending on str1 <, =, > str2 */ strcmp(str1, str2) char *str1, *str2; { char c1, c2; while((c1 = *str1++) == (c2 = *str2++)) if(c1==NULL) return 0; return sign(c1-c2); }  /* ** sign -- return -1, 0, +1 depending on the sign of nbr */ sign(nbr) int nbr; { if(nbr>0) return 1; else if(nbr==0) return 0; else return -1; }  if(c=='u') itou(i, str, 7); else continue; ctl=cx; /* accept conversion spec */ if(c!='s') while(*sptr==' ') ++sptr; len=-1; while(sptr[++len]); /* get length */ if((c=='s')&(len>prec)&(preclen>0)) len=prec; if(right) while(((width--)-len)>0) cout(pad, stdout); while(len) {cout(*sptr++, stdout); --len; --width;} while(((width--)-len)>0) cout(pad, stdout); } }    /* ** xtoi -- convert hex string to integer nbr ** returns field size, else ERR on error */ xtoi(hexstr, nbr) char *hexstr; int *nbr; { int d,t; d=0; *nbr=0; while(1) { if((*hexstr>='0')&(*hexstr<='9')) t=48; else if((*hexstr>='A')&(*hexstr<='F')) t=55; else if((*hexstr>='a')&(*hexstr<='f')) t=87; else break; if(d<4) ++d; else return ERR; *nbr = *nbr<<4; *nbr = *nbr+(*hexstr++)-t; } return d; } -------------------| ----------------------------------------------------------------- Enjoy!! eatures as I/O redirection on STDIN and STDOUT, separate compilation of version 2 modules and some new library functions. See the SMALLC.DOC file on the disk for the features of the original version and SMALLC2.DOC file for the extensions contained in this version. Notice at the end of the SMALLC.DOC file are some suggestions by Ron Cain on extensions to SmallC. There are still some to go (Structures...) so if you want to try your hand feel free to bang at it a little. Then pass it back and perhap87 --> FILE: 7-DISK .DOC CRC = 09 DB --> FILE: SMALLC .DOC CRC = 65 72 --> FILE: SMALLC2 .DOC CRC = 8D 3B --> FILE: SAMPLE .DOC CRC = 37 A2 --> FILE: IOLIB .ASM CRC = C3 C0 --> FILE: CRC .COM CRC = B2 07 --> FILE: CALL .ASM CRC = FC EE --> FILE: HELLO .C CRC = 33 20 --> FILE: ABS .C CRC = EB FA --> FILE: LIB .H CRC = 88 ED --> FILE: STDIO .H CRC = 3B 4B --> FILE: VM .H CRC = 1A F5 --> FILE: VML .H CRC = 9C 85 --> FILE: STD .H CRC = B4 23 --> FILE: STDIOs someone else will continue on where you leave off. Who knows, maybe we'll have a full-blown C compiler in a couple of years. ----------------------------------------------------------------- |---------------------------------------------------------------| |If you are new at C programming, there is a sample compilation | |session described in SAMPLE.DOC. By going through this session | |you will learn some things about SMALLC2 and C in general. | |--------------------------------------------MICRO CORNUCOPIA THE SINGLE BOARD SYSTEMS JOURNAL PO BOX 223 BEND, OREGON 97709 KAYPRO DISK #K7 SMALL C VERSION 2 Contributed by Bill Randle SMC.COM SMALLC2.DOC CC??.C . . This entire disk contains the substantially expanded version of Ron Cain's Small C. The additions to the language are substantial and include work both by J.E. Hendrix who released this version to the public domain via Dr Dobbs, and by Bill Randle, a Big Board user in Bend, Oregon who added such important fA .H CRC = 34 B9 --> FILE: LIBASM .C CRC = 00 A4 --> FILE: SMC .COM CRC = 16 12 --> FILE: DTOI .C CRC = 54 08 --> FILE: ITOD .C CRC = 9E B7 --> FILE: ITOU .C CRC = FC A6 --> FILE: ITOX .C CRC = 5A 83 --> FILE: LEFT .C CRC = DF 66 --> FILE: OUT .C CRC = C4 80 --> FILE: PRINTF .C CRC = 16 DE --> FILE: SIGN .C CRC = 53 E7 --> FILE: STRCMP .C CRC = B0 2C --> FILE: UTOI .C CRC = 82 7D --> FILE: XTOI .C CRC = A1           !  "  #  $  %  &  '