home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 163_02 / smallcv2.doc < prev    next >
Text File  |  1991-01-07  |  23KB  |  464 lines

  1. .* My thanks to Dick Dievendorff for fixing up this file to format with
  2. .* GML.  Now, if someone could do the same with SCLIB DOC . . .
  3. :gdoc.
  4. :frontm.
  5. :titlep.
  6. :title.Small-c Version 2.0
  7. :title.for the IBM Personal Computer
  8. :title.Release 1.01
  9. :date.
  10. :author.Daniel R. Hicks
  11. :address.
  12. :aline.
  13. :aline.RCH38DB(HICKS)
  14. :eaddress.
  15. :etitlep.
  16. :preface.
  17. :p.Portions of Small-c code Copyright 1982 by J. E. Hendrix.
  18. :p.Converted to the IBM Personal Computer by D. R. Hicks.
  19. :p.This document was developed entirely by D. R. Hicks, and is
  20. :hp1.not:ehp1. copyrighted.
  21. :body.
  22. :h1.Why Small-c?
  23. :p.
  24. Why bother with a limited version of the C programming language when
  25. complete versions of the language are available?  The main reason is
  26. price.  The cheapest "full" C implementations run about $75, and
  27. prices increase from there up to the $500 range.  Small-c is free, and
  28. this means that many users who would not want to pay for a full
  29. implementation will have access to this one.  Other users who may not
  30. be sure that they want pay for a full C implementation can use this
  31. one to determine if they like the language.
  32. :p.
  33. Another reason for the existence of Small-c is control:  You get all
  34. of the source for Small-c, and you can, if you wish, maintain or
  35. modify it yourself.  You can also change the I/O support package to
  36. suit your needs, and you can even port the compiler to a different
  37. processor if you wish (this version is converted from an 8080
  38. version).
  39. :h1.What can Small-c do?
  40. :p.
  41. Small-c takes a source file, consisting of C-subset statements, and
  42. creates an assembler source file.  This assembler source file can be
  43. processed by either the IBM Small Assembler or the IBM Macro Assembler
  44. to produce an IBM/Intel format OBJ file.  The OBJ file can, in turn,
  45. be combined with other OBJ files and LIB files via the DOS LINK
  46. function to produce an executable EXE module.  An I/O & utility
  47. function library is provided for this purpose, implementing an
  48. environment which quite closely mimics the UNIX environment.
  49. :p.
  50. This version of Small-c can be executed on an IBM PC with 128K of
  51. storage, one single-sided diskette drive, and running IBM DOS 1.1 or
  52. 2.0.  In all likelihood, the compiler will execute on a 96K system,
  53. but this has never been tried.  Since at least 96K is needed to run
  54. either of the assemblers, there is little merit in executing in less
  55. than 96K.  Although dual diskette drives are necessary for large
  56. compilations (where the assembler source can occupy most of one
  57. diskette), they are useful but not essential otherwise.  Where a hard
  58. file is available, this may be used in place of diskettes.  The
  59. compiler runs equally well on DOS 1.1 and DOS 2.0, although it does
  60. not utilize the increased function of DOS 2.0.  Release 1.01 has added
  61. up to 2 dimensional arrays of all supported data types (D. Lang 12/90)
  62. :p.
  63. Programs created with the compiler (and suitably assembled and linked)
  64. can be executed on any IBM DOS system with sufficient storage.
  65. Normally, the programs must be linked with the associated Small-c
  66. library which, in addition to providing I/O support, sets up the
  67. operating environment.  However, a reasonably adept programmer should
  68. be able to link Small-c procedures with procedures from other
  69. compilers, if necessary coding assembler "glue" via the the Small-c
  70. "#asm" statement.
  71. :p.
  72. Although it has never (to my knowledge) been attempted, there is no
  73. known reason why this version of Small-c (and the programs it
  74. compiles) should not execute equally well on any suitably configured
  75. IBM PC-compatible MS DOS system.
  76. :h1.What WON'T it do?
  77. :p.
  78. Small-c was originated by Ron Cain, and was originally published in
  79. Dr. Dobb's Journal, number 45.  The original intent was to provide
  80. users of small microprocessor systems with a compact yet powerful
  81. systems programming language, one which could be both used and
  82. maintained on a small system.  For this reason, many of the features
  83. of full C implementations were omitted, resulting in a restricted but
  84. usable language.  Small-c Version 2.0 was developed by J. E. Hendrix
  85. and published in Dr. Dobb's Journal, numbers 74 & 75.  This version,
  86. slightly larger to account for the tendency toward larger systems, has
  87. fewer restrictions and is considerably more powerful.  Nonetheless, it
  88. has significant restrictions relative to full C implementations.  The
  89. following are the principle restrictions:
  90. :ol.
  91. :li.Structures and unions are not supported.
  92. :li.Up to 2 dimensions arrays supported in Release 1.01
  93. :li.Floating point is not supported.
  94. :li."Long" integers are not supported.
  95. :li.Only functions returning integers are supported.
  96. :li.Pointers to pointers, arrays of pointers, and several of the other
  97. exotic forms of declarations are not supported.
  98. :eol.
  99. :h1.Running Small-c
  100. :p.
  101. The compiler may be invoked with the parameters specified on the
  102. command line, with prompting for the parameters, or a combination of
  103. the two.  To invoke the compiler with prompting, type:
  104. :sl.
  105. :li.CC
  106. :esl.
  107. :pc.The compiler will display the prompt:
  108. :sl.
  109. :li.Input file [CON.C]:
  110. :esl.
  111. :pc.Type in the name of the file containing your C program.  A
  112. filename extension of "C" is assumed.  If you enter no filename, input
  113. will be accepted from the keyboard.
  114. :p.The compiler will then display the prompt:
  115. :sl.
  116. :li.Output file [CON.ASM]:
  117. :esl.
  118. :p.
  119. Type in the file name of the file to contain the assembler source.  A
  120. filename identical to the source file name and an extension of "ASM"
  121. is assumed.
  122. :p.
  123. The compiler will then display the prompt:
  124. :sl.
  125. :li.Listing file [NUL.LST]:
  126. :esl.
  127. :p.
  128. Normally, the listing file is either suppressed (by routing to the NUL
  129. device) or is routed to the printer (by specifying "prn", for
  130. example).  If no filename is specified, NUL is assumed.
  131. :pc.
  132. The compiler then asks the following yes/no questions:
  133. :sl.
  134. :li.Interleave C source?
  135. :li.Monitor function headers?
  136. :li.Sound alarm on errors?
  137. :li.Pause on errors?
  138. :esl.
  139. :p.
  140. "Interleave C source" means to place the C source statements into the
  141. assembler source as comments.  This is very useful if the assembler
  142. source is to be examined or modified after compilation, but it
  143. increases the size of the assembler source file (by slightly more than
  144. the size of the C source file).
  145. :p.
  146. "Monitor function headers" means to display each function header (and
  147. include statement) as it is processed.  This is useful both for
  148. monitoring the progress of the compilation and for interpreting the
  149. context of error messages from the compiler.  This question is not
  150. asked (and function headers and include statements are not displayed)
  151. if output is being routed to the display screen.
  152. :p.
  153. "Sound alarm on errors" means to sound the PC's "bell" (beeper)
  154. whenever an error message is displayed (also, when the compilation
  155. ends).  This is useful for long compilations where one might wish to
  156. leave the computer unattended but be alerted when attention was
  157. needed.
  158. :p.
  159. "Pause on errors" means to stop after displaying each error message
  160. and wait for an "ENTER" before proceeding.  This prevents the error
  161. message from being scrolled off the screen before it can be examined.
  162. :p.
  163. After these questions have been answered, the compilation will begin.
  164. C source statements will be read from the input file and written to
  165. the output file.  Function headers and include statements will be
  166. displayed if selected.  When the compilation is finished, the
  167. following message is displayed (where n is a number):
  168. :sl.
  169. :li.There were n errors in this compilation
  170. :esl.
  171. :p.
  172. If the number of errors is zero (or if you are willing to accept
  173. whatever the errors are that have occurred), you may process the
  174. produced assembler source with ASM (IBM Small Assembler) or MASM (IBM
  175. Macro Assembler).  Once the program has been successfully compiled and
  176. assembled, it should be linked, using the DOS LINK program, with the
  177. CC.LIB library supplied, thereby producing the executable EXE file.
  178. :p.
  179. When parameters are specified via the DOS command line, the file names
  180. must be entered in order -- source file, assembler file, listing file
  181. -- followed by or interspersed with the processing options.  Roughly,
  182. the syntax is:
  183. :sl.
  184. :li.CC [<source_file> [<assembler_file> [<listing_file>
  185. ]]][-[n]<option> . . .][;]
  186. :esl.
  187. :p.
  188. Each file name, option, or the ";", must be separated by spaces from
  189. adjacent entries.  The options are identified by a leading "-" and
  190. contain an optional "n" which indicates the "not" of the option.  The
  191. options are:
  192. :sl.
  193. :li.i -- interleave C source
  194. :li.m -- monitor function headers
  195. :li.a -- sound alarm on errors
  196. :li.p -- pause on errors
  197. :esl.
  198. :p.
  199. The ";" indicates that defaults are to be taken for the remaining
  200. options or file names (rather than prompting for them).
  201. :h1.Data representations
  202. :p.
  203. Small-c recognizes seven different data types:
  204. :sl.
  205. :li.Integers
  206. :li.Characters
  207. :li.Integer arrays
  208. :li.Character arrays
  209. :li.Integer pointers
  210. :li.Character pointers
  211. :li.Integer functions
  212. :esl.
  213. :pc.
  214. No other combinations are recognized.
  215. :p.
  216. Integers are signed binary numbers ranging between -32768 and +32767
  217. and occupying 16 bits (two bytes) of data storage on a byte boundary.
  218. The low-order eight bits of the number is stored in the byte with the
  219. lower address.
  220. :p.
  221. Characters are signed binary numbers ranging between -128 and +127 and
  222. occupying 8 bits (one byte) of data storage on a byte boundary.
  223. :p.
  224. Integer arrays can be up to a maximum of 2 dimensions of 16 bit
  225. signed integers.  Each element occupies two bytes of data storage on a
  226. byte boundary, with the low-order eight bits of each element occupying
  227. the byte with the lower address. The first element (the one
  228. corresponding to an index value of zero) occupies the two-byte area
  229. with the lowest address, the next element occupies the adjacent higher
  230. address, etc.
  231. :p.
  232. Character arrays can be up to a maximum of 2 dimensions of 8 bit
  233. signed integers.  Each element occupies one byte of data storage on a
  234. byte boundary.  The first element (the one corresponding to an index
  235. value of zero) occupies the one-byte area with the lowest address, the
  236. next element occupies the adjacent higher address, etc.
  237. :p.
  238. Integer and character pointers are both 16 bit unsigned numbers,
  239. ranging between 0 and 65535 and occupying two bytes of data storage on
  240. a byte boundary.  Like integers, the low-order eight bits of the
  241. number is stored in the byte with the lower address.  The only
  242. distinction between the two is in the semantics of their use:
  243. Dereferencing (via "*") an integer pointer causes a two byte quantity
  244. to be fetched or stored, while dereferencing a character pointer
  245. causes a one byte quantity to be fetched and stored.  Similarly,
  246. incrementing (decrementing) an integer pointer causes two to be added
  247. to (subtracted from) the pointer, while incrementing (decrementing) a
  248. character pointer causes one to be added to (subtracted from) the
  249. pointer.
  250. :p.
  251. Functions are variable length areas of code storage beginning on a
  252. byte boundary.  Functions are presumed to consist of 8088 instructions
  253. arranged in a sequence which is meaningful and which conforms to
  254. Small-c linkage and usage protocols.
  255. :p.
  256. The C language is fairly unique among "modern" languages in that the
  257. value of a named entity (i.e., variable or function) is the contents of
  258. the entity only when the entity is a scalar.  In other cases, the
  259. value of the entity is its address.  Thus, the value of an integer or
  260. character array is the offset in the data segment to the first element
  261. (the one with an index of zero) and the value of a function (when its
  262. name is not followed by a parameter list) is the offset in the code
  263. segment to the entry point of the function.
  264. :p.
  265. Since C is a loosely-typed language, much of the semantics of a named
  266. entity is dependent upon the context of its use.  Any of the above
  267. data types may be dereferenced with a "*", for instance (although
  268. dereferencing a character variable or a function name is guaranteed to
  269. be meaningless -- and dangerous).  Likewise, any of the above data
  270. types may be treated as a function and called by appending a parameter
  271. list.  (In this case, character variables and array names are
  272. meaningless -- and even more dangerous.)
  273. :p.
  274. Another unusual aspect of C is that character values are extended to
  275. integer values when used in an expression, when passed as an argument,
  276. or when referenced by a switch or return statement.  The C language
  277. standard permits this conversion to be done either via sign extension,
  278. or by filling the high-order bits of the integer with zeros.  Small-c
  279. uses sign extension.  Thus an expression such as:
  280. :sl.
  281. :li.c == 255
  282. :esl.
  283. :pc.(where c is a character variable) will never be true.
  284. :h1.Language features and restrictions
  285. :p.
  286. In addition to the restrictions stated above, the following
  287. restrictions hold:
  288. :sl.
  289. :li.Lower-case and upper-case symbols are synonymous.
  290. :li.Local declarations at a block level and goto statements may not be
  291. used in the same function.
  292. :li.The sizeof operator is not supported.
  293. :li.The cast operator is restricted to four cases:
  294. :sl.
  295. :li.(int)
  296. :li.(char)
  297. :li.(int *)
  298. :li.(char *)
  299. :esl.
  300. :pc.No extraneous blanks are allowed within the cast operator.
  301. :li.Initializers are only permitted on global or static declarations,
  302. and only literal values may be used for initializers.
  303. :esl.
  304. :h1.Storage and linkage conventions
  305. :p.
  306. This version of Small-c utilizes the "small" storage model:  The CS
  307. register addresses a single code segment (of up to 64K), while the DS,
  308. ES, and SS registers address a single data segment (also of up to
  309. 64K).  The code segment comes first (lowest) in storage, followed
  310. immediately by the data segment.  Static storage and string constants
  311. are allocated at the low end of the data segment, with a heap growing
  312. up from the top of the statics, and a stack growing down from the top
  313. of the data segment.  The initialization code contained in C.LIB
  314. initializes the data segment to be as large as available storage, up
  315. to the 64K maximum.  Although the function is so far unused, provision
  316. is made for allocating I/O buffers above the data segment when extra
  317. storage is available.  In theory, the above allows a single program to
  318. utilize up to 128K of storage, although, in practice, one will usually
  319. use up the code segment roughly twice as fast as the data segment, so
  320. that 96K (not counting DOS) is a more practical limit.
  321. :p.
  322. Subroutine linkage and automatic storage allocation utilizes the
  323. stack.  To call a subroutine, parameters are first pushed onto the
  324. stack.  In standard C fashion, scalar parameters (char or int) are
  325. passed by value, while arrays and strings are passed by address.  Char
  326. and int values both occupy two bytes on the stack.  Char values are
  327. are sign-extended to 16 bits.  Address values are also passed as
  328. two-byte quantities:  The interpretation of the address as an offset
  329. into the code segment or and offset into the data segment depends on
  330. the way it is used.  (In fact, Small-c does not recognize pointers to
  331. functions as a data type, but reference to a function name without
  332. following "()" yields its offset into the code segment, and reference
  333. to a variable followed by "()" results in a call to the location in
  334. the code segment indicated by the variable.)
  335. :p.
  336. Parameters are pushed in order of
  337. occurrence:  The first parameter in
  338. a list is the first one pushed and therefore the deepest one in the
  339. stack.  This is opposite the order of many C compilers, and it
  340. prevents some C library functions (such as "printf") from being able
  341. to determine how many parameters are present by examining the first or
  342. second one.  For this reason, the compiler, prior to a CALL, loads
  343. register DL with the parameter count, thus allowing functions such as
  344. "printf" to be implemented.  This feature (the loading of DL) can be
  345. disabled in cases where it is not needed, thereby generating more
  346. compact code.
  347. :p.
  348. After the parameters have been pushed into the stack, the function is
  349. called (as a NEAR procedure).  This causes the return address to be
  350. pushed onto the stack.  The called function then saves the current BP
  351. register value by pushing it onto the stack, loads BP from the current
  352. SP register value, and increments SP by the size of automatic storage
  353. needed.  Thus, automatic storage can be addressed by using negative
  354. offsets from BP, while parameters can be addressed using positive
  355. offsets.
  356. :p.
  357. To return from a function, the result, if any is first loaded into the
  358. AX register.  Then, SP is loaded from BP (to pop any automatic storage
  359. present), and the old value of BP is restored by popping it from the
  360. stack.  Finally, a NEAR return is executed which pops the saved return
  361. address from the stack and returns to the calling function.  It is the
  362. responsibility of the calling routine to pop the parameters from the
  363. stack (this is an area of possible incompatibility with other
  364. languages).
  365. :p.
  366. Although the effect can be negated by the inappropriate use of global
  367. or static variables, all code generated by this compiler is reentrant.
  368. :h1.Execution environment
  369. :p.
  370. It is the intent of this implementation to mimic the standard UNIX C
  371. operating environment as accurately as possible and reasonable, given
  372. the limitations of Small-c and the difference in operating systems.
  373. Thus, :hp1.main:ehp1. is passed a pair of parameters, as in UNIX C,
  374. with the first parameter indicating the number of arguments, and the
  375. second parameter pointing to an array of integers which can be cast
  376. into character pointers to the arguments.  These arguments are the
  377. blank-separated arguments parsed from the DOS command line, except
  378. that the first argument (which in UNIX C is the name of the program)
  379. is always "main".
  380. :p.
  381. Also optionally parsed from the command line are redirections of
  382. :hp1.stdin:ehp1. and :hp1.stdout:ehp1..  If an argument on the command
  383. line is preceeded by the character "<", it is treated as the file name
  384. for stdin rather than being added to the argument list.  And if an
  385. argument on the command line is preceded by the character ">", it is
  386. treated as the file name for stdout rather than being added to the
  387. argument list.  (Caution:  Under DOS 2.0, these arguments are
  388. intercepted and the redirection is performed by DOS rather than by
  389. C.LIB.  Unfortunately, there are bugs in the DOS 2.0 redirection
  390. support.)  If not redirected, stdin, stdout, and stderr all refer to
  391. the keyboard/display.  stderr is not redirectable.
  392. :h1.I/O system
  393. :p.
  394. It is the intent of this implementation to mimic the standard UNIX C
  395. I/O interfaces as accurately as possible and reasonable, given the
  396. limitations of Small-c and the difference in environment.  Thus, both
  397. I/O layers are implemented:  The "standard I/O" layer and the UNIX
  398. system call layer.  The principle difference between the two layers is
  399. not so much function as it is of performance:  The standard I/O
  400. interface provides an extra level of buffering which is useful to many
  401. applications which process input or generate output a character at a
  402. time, while the UNIX system call interface is more efficient when this
  403. extra buffering is not needed.  In addition, the UNIX system call
  404. interface permits slightly more precise control over the I/O, while
  405. the standard I/O interface functions are slightly easier to use when
  406. processing character data.
  407. :p.
  408. As with UNIX C, access to the "standard I/O" layer generally requires
  409. the user to include stdio.h, so that various values are defined and
  410. various externals are declared.  Also as with UNIX C, the include
  411. errno.h contains error codes that are stored in the external variable
  412. errno by the I/O routines.
  413. :p.
  414. The UNIX system call layer reserves the filenames "kbd", "scrn",
  415. "con", and "prn" (all lower case) and treats files opened to these
  416. names specially.  The first three names are intercepted and routed to
  417. the appropriate keyboard/ display interfaces, while the fourth is
  418. routed to the printer.  All other file names are passed to DOS to be
  419. treated as DOS disk/diskette file names or DOS reserved file names.
  420. :p.
  421. One unusual aspect of this I/O system is that it provides a "bridge"
  422. between the UNIX C file scheme which uses newline as a record
  423. terminator, and the IBM/MS-DOS file scheme which uses Carriage
  424. Return/Line Feed as a record terminator.  This conversion is
  425. implicitly performed for all I/O.  If non-character data is to be
  426. processed, this conversion must be disabled via :hp1.fbinary:ehp1.
  427. or :hp1._ioctl:ehp1..
  428. :p.
  429. The implemented portions of the two levels of I/O interfaces are
  430. described in a separate document.  In general, the standard I/O
  431. functions have the same names as their UNIX counterparts, while the
  432. UNIX system call functions have underline ("_") prefixed on their
  433. names.  For more detail, users are advised to refer to
  434. :cit.The Unix System:ecit. by S. R. Bourne, or
  435. :cit.UNIX Programmer's Manual:ecit. by Bell Laboratories.
  436. :h1.Debugging
  437. :p.
  438. This Small-c implementation contains very little in the way of
  439. debugging aids.  In many cases, this is of no significance, since the
  440. C language has a fascinating tendency to produce programs which run
  441. without error on the first attempt.  However, since even the best
  442. programmer will eventually produce errors if his programs are large
  443. enough, some debugging facilities and techniques are usually
  444. necessary.  The standard IBM/MS-DOS facilities, combined with common
  445. debugging techniques (such as debug "print" statements) and the few
  446. facilities provided by the language, have proved adequate for
  447. debugging thus far.
  448. :p.
  449. Someone who plans to use the compiler extensively should take some
  450. time to familiarize himself with code generated by the compiler.  This
  451. will make it easier to associate code sequences from DEBUG
  452. "unassemble" with the correct C source statements, thus reducing or
  453. eliminating the need to obtain assembler listings.
  454. :p.
  455. When using LINK to produce a C program's EXE module, it is a good idea
  456. to obtain a link MAP if debugging of the EXE module is likely to be
  457. necessary.  To do this, respond to the LINK "List File" prompt with
  458. "PRN" or a file name AND specify the "/m" option so that the map is
  459. really produced, rather than just obtaining a list of segment sizes.
  460. This map can then be used when dumping global variables or setting
  461. DEBUG breakpoints.
  462. :egdoc.
  463.  
  464.