home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / JSAGE / ZSUS / PROGPACK / SCZ01BET.LBR / -SCZ01.ZZZ / -SCZ01.
Text File  |  1989-05-24  |  43KB  |  799 lines

  1. SmallC/Z  05/07/89
  2.  
  3.  
  4.  
  5.        SmallC/Z Compiler - Usage Documentation and Miscellaneous Notes
  6.  
  7.  
  8.  
  9.  
  10. The following is some preliminary documentation on SmallC/Z. It covers 
  11. whatever information it seemed would be useful to get one started using the 
  12. compiler and any other inanities that came to mind while writing it. As such, 
  13. it will hit on quite a variety of topics, hopefully with some semblance of 
  14. coherence. If this isn't the case, I accept responsibility, and can only say 
  15. do your best with it, and maybe somebody will add to or improve on it.
  16.  
  17. Topics covered:
  18.  
  19.                Running the compiler
  20.                Topics I'd like opinions on
  21.                ZCPR Support and the Configuration Header
  22.                Program installation and use of debuggers.
  23.                Use of assembler code with SmallC/Z
  24.                Argument counts
  25.                The Entry and Exit Vectors
  26.                Using CZVLIB
  27.                The new memory management scheme
  28.                Basic memory layout
  29.                Installing the compiler (optional in most cases.)
  30.                Compiler and library version numbers
  31.  
  32.                copyright (c) Al Grabauskas, 1989
  33.  
  34.                                 ---===*===---
  35.  
  36.  
  37.  
  38.                          Topics I'd like opinions on
  39.  
  40. I'd like to hear opinions on anything you'd like to discuss regarding 
  41. SmallC/Z. The following are a couple things I'd in particular like to get some 
  42. opinions on. Most are further discussed elsewhere in this file, so if 
  43. something doesn't make sense or ring a bell now, read on.
  44.  
  45.                - should the option switches stay of the form "-s",
  46.                  or should they go to the more standard zcpr "/s" format?
  47.                - how does the "assembler configuration header" idea sit
  48.                  with folks? pros? cons?
  49.                - should the entry and exit vectors be handled differently?
  50.                  one could, at the expense of code space, make the exit vector
  51.                  a stack, for instance...
  52.                - is the dynamic memory management scheme reasonable? would
  53.                  a first fit allocation scheme be better than best fit?
  54.                - are the existing CZVLIB functions reasonable in terms of 
  55.                  programmer interface, or could their usage be simplified?
  56.                - does the external file control block contain disk and user
  57.                  in versions of zcpr prior to 3.4? this is important to the
  58.                  way du: is handled currently in argv[0].
  59.  
  60.                                 ---===*===---
  61.  
  62.  
  63.  
  64.                             Running the compiler
  65.  
  66.  
  67. The syntax for invoking the compiler is briefly as follows:
  68.  
  69.     scz [<input-redirector or filelist...] [>output-redirector] [options]
  70.  
  71.           where: input-redirector or output-redirector is a file name and 
  72.           file type, a file list is a list of comma delimited input files,    
  73.           and options are described below.
  74.  
  75. All elements of the command tail are optional, and indeed, the compiler can be 
  76. run without any command tail at all.
  77.  
  78. Like its precursor, SmallC/Z supports redirection of input and output, and 
  79. this has a significant impact on the way the user interacts with the compiler 
  80. and programs created with it. We'll just look at this as it relates to using 
  81. the compiler, and you can draw further conclusions based on that. One useful 
  82. side effect is this: by starting the compiler with nothing except perhaps 
  83. option switches in the command tail, you instruct it to take its input from 
  84. stdin (the keyboard) and write its output to stdout (the terminal). In other 
  85. words, if you need to quickly see what assembler code the compiler will 
  86. generate for a given set of statements, just crank it up and type them in, and 
  87. it'll spit the assembler source to the screen. When typing input at the 
  88. keyboard a ^S pauses screen output, a ^C aborts execution, and a ^Z (the cp/m 
  89. and zcpr "end of text file" byte) indicates end of file. Redirection can also 
  90. be used to imbed the original C source code as comments in the assembler 
  91. source output, so you can study the compilation of various statements at your 
  92. liesure. This is described below under the "-l#" option switch.
  93.  
  94. By default, input is obtained from stdin, but if either an input redirector, 
  95. one or a list of filenames is given, then the file(s) named will be used as 
  96. input. A filetype of "C" is defaulted to if none is specified for names in an 
  97. input file list. Output normally goes to the screen, unless an output 
  98. redirector or input file(s) are specified. The action for redirection is 
  99. self-explanatory. In the case that one or more input files are given, then the 
  100. output will be written to disk using the first filename in the list with a 
  101. type of Z80. Redirection specifications get no default types.
  102.  
  103. Options must each begin with a dash ("-"), and can occur anywhere in the 
  104. command tail. Short descriptions of the option switches follow: 
  105.  
  106.     -m        Monitors compiler progress by echoing each function
  107.               declaration to stderr (screen, non-redirectable).
  108.  
  109.     -a        Alarm - rings the console bell on errors.
  110.  
  111.     -p        Pause on each error. A carriage return from the
  112.               keyboard continues processing.
  113.  
  114.     -l#       Listing control, where # is a file descriptor number
  115.               for the file to which the listing should be sent.
  116.               Standard file descriptors are as follows:
  117.                     0=stdin (not applicable here)
  118.                     1=stdout
  119.                     2=stderr
  120.               These are also listed in the stdio.h #include file.
  121.               If you specify 1 (stdout), the listing is intermixed
  122.               with the output. SmallC/Z then precedes each line with
  123.               a semicolon so the assembler sees it as a comment.
  124.               Stderr (2), not being redirectable, always goes to
  125.               the screen. No listing is produced if the -l switch
  126.               isn't specified.
  127.  
  128.     -o        Optimize for size, even at the expense of speed.
  129.  
  130.     -z#       This one is an oddball, and will require a bit of
  131.               explanation. By default, at the end of any compile
  132.               SmallC/Z checks to see if main() is defined. If it
  133.               is, the compiler goes back to the front of its output
  134.               file and writes a (patchable) string. The default is
  135.               "$INCLUDE SYSCCFG1.CCZ", and causes the assembler source
  136.               to pull in a file of that name (the default one supplied
  137.               has a zcpr "Z3ENV" header and some linkage information).
  138.               In order to provide some flexibility here, the character
  139.               following the -z switch is taken and inserted in the 
  140.               position occupied by '0' in the default file name. Both
  141.               the default string and the position the -z switch affects
  142.               are patchable. More on this under "ZCPR Support", below.
  143.               See also "Installing the Compiler"
  144.  
  145.     -         The null switch, or any unrecognized or erroneous
  146.               option will display a (very) short help on the screen.
  147.  
  148.                                 ---===*===---
  149.  
  150.  
  151.  
  152.  
  153. The stuff that follows may or may not be more involved with compiler library 
  154. and the runtime code internals than many folks care about. If so, don't bother 
  155. with it too much, but you may want to scan it somewhat in order to pick up 
  156. some of the basic things about the compiled code's link to zcpr and general 
  157. operation.
  158.  
  159.                                 ---===*===---
  160.  
  161.  
  162.  
  163.  
  164.                   ZCPR Support and the Configuration Header
  165.  
  166.  
  167. The -z switch originally told the compiler to include zcpr support by writing 
  168. a header that was built into the compiler to the output file. This approach 
  169. was dropped for several reasons. Firstly, it meant the switch had to be 
  170. specified for modules containing main() that were intended to run under zcpr, 
  171. and this was easy to forget, which would require a recompile. Secondly, it 
  172. chiselled the header that was output in stone, so to speak.
  173.  
  174. Outputting an assembler include directive allows the programmer control over 
  175. the configuration header used, and allows the header to be customized or added 
  176. to without need to change the compiler. If you do need a header other than the 
  177. standard one for a particular program, and you forgot to specify the switch, 
  178. you can modify the output assembler source with a file patch utility - you 
  179. don't even need to crank up an editor (which is nice, since the assembler code 
  180. can be quite large). Admittedly, you could have done something similar with a 
  181. simple #include in C, or even just a #asm...#endasm sequence, but this seemed 
  182. more elegant, especially since 99% of the programs probably won't need or use 
  183. anything other than a standard header - you can just forget about it and write 
  184. your code.
  185.  
  186. Once I decided to go this route, I realized that the header include file could 
  187. serve a couple of purposes. The net result was this: the included assembler 
  188. configuration header file contains any operating system specific program base 
  189. code, plus data that provides linkage between your main() and the runtime 
  190. support code. This is done by making references to certain names the linker 
  191. will find in two small arrays: the "entry and exit vectors". More on these 
  192. later, but the gist of this is that the compiler no longer contains any names 
  193. assumed to be in the run-time support library except the arithmetic and 
  194. logical operations routines in the CALL module. Those it simply can't do 
  195. without - they provide the virtual machine that the compiler is written for.
  196.  
  197. All other runtime support (that performed on entry prior to executing main() 
  198. and that performed on exit, in particular) is connected to your program 
  199. through the staring jump and the entry and exit vectors (which are processed 
  200. by Uentry() and exit(), respectively) in the configuation header. (You can 
  201. offload the entry and exit vectors to a linkable module, if you like, but the 
  202. initial linkage between your program is still provided by the configuration 
  203. header file.) The implications of this approach are that you are not 
  204. necessarily tied to the standard library if you want to use this compiler - 
  205. you just need CALL and some replacements or modified versions, as the case may 
  206. be, of Uentry() and exit() as a minimum. Some of the stuff in the CSYSLIB 
  207. module would likely also be desirable. You could, for example, completely 
  208. replace the i/o subsystem fairly straightforwardly, or set up a runtime 
  209. package that supports some Z80 based controller board that doesn't have 
  210. anything resembling a cp/m or zcpr environment. You can also fairly 
  211. straightforwardly add initialization and de-initialization code at points in 
  212. the execution program that are at a "system level", i.e. before execution of 
  213. and after leaving main(), with a minimum of hassle. More on this in the next 
  214. section.
  215.  
  216. Three very similar configuration headers are supplied:
  217.                       SYSCCFG0.CCZ - vanilla cp/m header
  218.                       SYSCCFG1.CCZ - zcpr type 1 header
  219.                       SYSCCFG3.CCZ - zcpr type 3 header
  220.  
  221. The compiler is set up to put the assembler code
  222.                             $INCLUDE SYSCCFG1.CCZ
  223. into the front of assembler output files containing main(). This default is 
  224. patchable, so you can change the $INCLUDE to whatever your assembler supports 
  225. (I think most assemblers capable of handling the code output by SmallC/Z 
  226. should like $INCLUDE just fine), the file name and/or the byte affected by the 
  227. -Z option flag.
  228.  
  229. SYSCCFG1.CCZ is deemed the most generic, since it will run fine on cp/m and 
  230. zcpr systems of various revision levels, and will supply you with the ability 
  231. to use the zcpr based functions by default. The cp/m header is supplied mainly 
  232. for completeness. The type 3 header works as type 3 should for zcpr 3.3 or 
  233. better. (It may, however, be desirable to relocate free memory - stack and 
  234. heap space - UNDER the program if you link it high in core - more on this 
  235. later too). You don't want to use that last one on systems that don't support 
  236. type 3 zcpr programs.
  237.  
  238. By the way, you can do custom headers with overlay areas for programs, if you 
  239. like. I suspect that sort of thing is better done by the #include approach 
  240. however, since its use is limited to a particular program. Better to save use 
  241. of configuration header include files for run-time support related things, if 
  242. only to maintain a logical consistency.
  243.  
  244. One note on the standard configuration header. Since part of its intent is to 
  245. support type 3 programs, it cannot be left to the linker to place an initial 
  246. jump at 100h and start the cseg at 103h, since you may want a type 3 program 
  247. to run elsewhere in memory. As a result, it contains a jump to Uentry(), which 
  248. is the initial runtime setup routine in CLIB, and you'll have to supply the 
  249. linker with an address to put the program at (/p: linker switch for L80, and 
  250. /a: is recommended for SLRLNK). However, if you always plan to compile for 
  251. execution at 100h, you can remove or comment out the jump, perhaps make sure 
  252. the program environment type is 1, and NOT specify an address to the linker.  
  253. Uentry() is the only routine in CLIB or CZVLIB whose end card contains its 
  254. name, so the linker will know where to jump to.
  255.  
  256. The important thing here is, if the jump is in the include file, specify an 
  257. address, because otherwise the linker will generate an extra jump, and even 
  258. though it will go to the right place, the Z3ENV header will be displaced and 
  259. as a result the environment descriptor pointer won't be installed properly.  
  260. Conversely, if the jump is not in the header file, don't specify an address, 
  261. because then the linker will omit generating a jump and the first instruction 
  262. you execute will be 'Z3ENV', which is highly unlikely to yield the desired 
  263. results. I personally prefer the more generic solution, and since I use 
  264. aliases, scripts and the MAKE rcp, it is really no extra hassle to specify the 
  265. execution address at link time, but whatever suits your fancy and works is 
  266. what you should do. That's at least partly what we do this stuff for, eh?
  267.  
  268.                                 ---===*===---
  269.  
  270.  
  271.  
  272.  
  273.                   Program installation and use of debuggers.
  274.  
  275.  
  276. If you are running zcpr 3.0, you'll have to install the programs you compile 
  277. with z3ins.com. Alternatively, you can edit the header files and put in a 
  278. pointer to the environment for your system. This has an advantage even for 
  279. zcpr 3.3 or later command processors, because the program is already installed 
  280. when you go into it with a debugger like z8e.com. The default headers supplied 
  281. have the environment pointer set to zero.
  282.  
  283. Assuming you know Z80 assembler, you'll find that once you know the kind of 
  284. code put out by SmallC/Z and the function of library code (and what the 
  285. arithmetic and logical routines in the CALL module do in particular), that you 
  286. can do reasonably meaningful debugging in assembler debuggers like z8e.
  287.  
  288. If you generate *.sym files from the link, your symbol set will include the 
  289. entry points of all functions (yours and the libraries'), and quite a few 
  290. runtime system variable addresses. Non-global tags for specific functions you 
  291. may be interested in can be obtained from assembler listings. Read the 
  292. documentation for z8e - it is incredibly flexible in its handling of symbols, 
  293. and can use multiple files to build its symbol table for a given run.
  294.  
  295. If you understand the basic structure of the runtime support in the libraries, 
  296. you can track down almost any bug this way.
  297.  
  298.                                 ---===*===---
  299.  
  300.  
  301.  
  302.  
  303.                      Use of assembler code with SmallC/Z
  304.  
  305.  
  306. You can write C callable routines in assembler and vice versa, either as 
  307. separate modules or in C source files. You can also throw in-line assembler 
  308. code into C source files anyplace, but this requires a good understanding of 
  309. the kind of code produced by SmallC/Z, and assumes that it won't change. I 
  310. wouldn't generally recommend this practice unless there is no alternative.
  311.  
  312. Putting in-line assembler in C source files is as simple as surrounding it 
  313. with #asm and #endasm compiler directives. The code between them is copied 
  314. verbatim by the compiler to its output file. If you do this inside function 
  315. definition braces, the compiler will add the function name to its symbol 
  316. table, otherwise you may have problems with it generating spurious extrn 
  317. statements. The closing brace will generate a "ret" instruction, so you can 
  318. omit that if your function exits from the bottom of you assembler code.
  319.  
  320. SmallC/Z places function arguments on the stack. They are pushed in the order 
  321. they occur in the call, so on entry to your assembler based function, the 
  322. stack contains the return address and the arguments of the call in "reverse" 
  323. order. In general, if you are recovering arguments further in than 2 or 3 
  324. pops, you are probably better off getting them with a 
  325.                     ld  hl,stkoffset       ; bytes to offset into the stack
  326.                     add hl,sp              ; pointer to argument
  327. sequence of instructions.
  328.  
  329. Your assembler function should return the stack in the same condition it got 
  330. it (at least the stack pointer should be the same, allowing for the return 
  331. address popped by the "ret" instruction). The contents of the hl register are 
  332. considered the return value by C. Other registers are ignored and need not be 
  333. preserved.
  334.  
  335.                                 ---===*===---
  336.  
  337.  
  338.  
  339.  
  340.                                Argument counts
  341.  
  342.  
  343. If you are writing a function that needs to know how many arguments are on the 
  344. stack for it, it will be in the accumulator IF the calling C function didn't 
  345. have the symbol NOCCARGC #defined at he biginning of its source. #defining 
  346. NOCCARGC is a compiler directive that shuts this feature off (and as a result 
  347. generates slightly smaller and faster code).
  348.  
  349. If you need this count in C code, make the first executable line of your 
  350. function something like:
  351.                               argcount=CCARGC();
  352.  
  353. This needs to be first because the count in the accumulator would soon be lost 
  354. through normal operations.
  355.  
  356. Be careful with NOCCARGC. Currently four CLIB functions and two CZVLIB 
  357. functions will not operate properly if called from routines compiled with 
  358. argument counts suppressed:
  359.  
  360.      CLIB   -  printf(), fprintf(), scanf(), fscanf().
  361.      CZVLIB -  Vwrite(), Vrcwrite()
  362.  
  363.                                 ---===*===---
  364.  
  365.  
  366.  
  367.  
  368.                          The Entry and Exit Vectors
  369.  
  370.  
  371. These are processed by the Uentry() and exit() routines, respectively. The 
  372. motivation for them was to decouple the library from the compiler and 
  373. subsystem chunks of it from the library. The idea was originally developed to 
  374. allow using the older, more primitive memory management routines for older, 
  375. existing code that makes assumptions the new routines can't live with. I 
  376. needed a means to do different initializations early in the runtime code, 
  377. depending which routines were in use. While that by itself is straightforward 
  378. enough, a more generic solution seemed desirable because I expected to run 
  379. into this sort of thing again, and didn't want to have to keep going into the 
  380. library source to accomodate it.
  381.  
  382. After a little thought, I realized that I could provide a means for sensibly 
  383. adding and/or substituting what I've since come to regard as runtime support 
  384. subsystems, without having to abuse the standard library source. It was only a 
  385. tiny step from that to modularizing and pretty much decoupling the standard 
  386. library entirely (with the exception of the arithmetic and logical module 
  387. CALL, as I mentioned above).
  388.  
  389. The entry and exit vectors are just two small linear arrays of pointers to 
  390. callable routines. The vector bases are named with the global tags Uinvec and 
  391. Uexvec respectively. The last routine in each of them should never return to 
  392. its caller.
  393.  
  394. The basic operation goes like this. At program startup, the initial jump goes 
  395. to Uentry(), which does some basic things - it saves the initial value of the 
  396. HL register and the address on the stack that returns to the system in runtime 
  397. variables that can be referenced globally. It then determines the top (under 
  398. the CCP or any RSX's beneath it) and bottom of memory (initially at the global 
  399. tag Upgmend), and likewise saves these in global system variables. Uentry() 
  400. then loads HL with a pointer to Uinvec and proceeds to loop, emulating calls 
  401. to each routine pointed to by Uinvec with a "push" of the return address and 
  402. "jp (hl)" instructions. Other than the push of the return address, it stores 
  403. nothing on the stack, since at least one of those routines will set up the 
  404. runtime stack.
  405.  
  406. The routines on Uinvec in the default configuration are for memory management, 
  407. and o/s interface initialization, followed by a call to Umain(), the main 
  408. routine in CSYSLIB, which sets up a couple other things like the command tail 
  409. in argv[] and initializes argc, then excutes your main() function.
  410.  
  411. A couple related points:
  412.  
  413. - the i/o subsystem doesn't need any initialization, so no vector is included 
  414.   for it. It does, however, have a vector in Uexvec, so it can close any files 
  415.   you neglected to prior to returning to the o/s.
  416.  
  417. - Uentry(), because of the way its code is written, doesn't care what order 
  418.   the routines in Uinvec are in, or even how long it is. However, I will point 
  419.   out that the memory management subsystem should typically be first (as it 
  420.   is in the default setup), since it should decide where the stack goes, and 
  421.   since other subsystems (i/o in particular) are logically likely to use it.
  422.  
  423.   Similarly, i/o de-initilization in the exit vector should be last, just 
  424.   prior to the "return to o/s" routine pointer, because the other routines in 
  425.   the exit vector (the exit code handler, for instance) may want to use 
  426.   console or other i/o, and typical i/o de-initializers include closing the 
  427.   stdin, stdout and stderr streams. (Yes, it is necessary to close these, 
  428.   because they might be files - remember that redirection is supported).
  429.  
  430.   The "exit trap" (described below) vector should be first, for reasons 
  431.   explained below.
  432.  
  433. - As long as the topic of the length of entry and exit vectors came up, I may 
  434.   as well clarify the point made earlier, that the last routine in each 
  435.   should never return. That's the only thing that defines how long they are. 
  436.   Uentry() will quite contentedly keep churning through and calling whatever 
  437.   is pointed to as long as it returns. The reason that Uinvec can be 
  438.   considered "terminated" is that it calls Umain(), whose last statement is a 
  439.   call to the exit() function, and hence will NEVER return to Uentry(). By the 
  440.   way, zero is a perfectly valid value in these vectors, and results in a warm 
  441.   boot. That's why Uexvec is followed by zero - in the case that some program 
  442.   has a dysfunctional exit trap or some such, it's an attempt to cover for a 
  443.   worst case situation (though the likelihood there is that exit() would never 
  444.   regain control to execute that warm boot).
  445.  
  446. The operation of exit() as regards Uexvec is similar. exit() takes the value 
  447. passed to it as an argument, stores it in a global variable, then proceeds to 
  448. emulate calls to the routines on the exit vector.
  449.  
  450. By default, the first of these is a user exit trap, which is a null routine - 
  451. a simple "ret" instruction. A call to the CLIB function setxtrap(addr) inserts 
  452. addr, which should be a pointer to an exit trap routine, into this word, and 
  453. setxtrap(0) restores the pointer to the null routine. The reason it's first is 
  454. straightforward: the user's exit trap routine might be coded to abort the exit 
  455. under certain circumstances, in which case you wouldn't want to have done any 
  456. other exit processing. One hopes that the exit trap routine allows SOME 
  457. circumstance that completes exit processing, of course, since otherwise there 
  458. would be no way to terminate the program.
  459.  
  460. The purpose of the user exit trap is to allow you to perform any cleanup you 
  461. need prior to terminating the program, even if it was being exited from a 
  462. library routine that isn't really under your control, and to selectively abort 
  463. exits if you so desire. Executing function exabrt() in your exit trap will 
  464. quietly and cleanly abort the exit (continue the program fom the point exit() 
  465. was called). It's up to you to make sure that there are no side effects to 
  466. using this - most code isn't written expecting a return from a call to exit(). 
  467. As far as the library exits go, I feel you can safely abort ^C exits, but no 
  468. others. There are very few, so that shouldn't present a problem.
  469.  
  470. A C #include file called SYSEXITS.H has been provided, with #defines that give 
  471. the values of exit() codes used by routines in CLIB. These are all unique, so 
  472. that you can determine the context of the exit. For example, there are two 
  473. CLIB routines that respond to a ^C at the keyboard by calling exit() - 
  474. Uconin() and poll(). Each has a distinct code in SYSEXITS.H, so that if you 
  475. wish, you could abort all exits due to ^C, or just those from one but not the 
  476. other, or you could allow them, but perform some cleanup prior to doing so. 
  477. (Incidentally, the standard exit code handler supplied in CLIB and invoked 
  478. AFTER this, equalizes the two, so that for zcpr "if" purposes there is only 
  479. one code for user aborts - 255).
  480.  
  481. The convention I've sort of adopted is that a 0 exit code means no error, 
  482. 1-127 are for programs to use as they see fit, and 128-255 (actually -1 to 
  483. -128 if defined as character due to sign extension) are reserved for runtime 
  484. system codes. That should be enough for everybody concerned, with room to 
  485. spare, and should help avoid confusion.
  486.  
  487. The user exit trap vector is followed by an exit code interpreter. The CLIB 
  488. exit code interpreter simply kicks out a message to the console for the exits 
  489. defined in SYSEXITS.H. If you linked with CZVLIB preceeding CLIB, the error 
  490. code interpreter will do the same, and if it's running in a zcpr environment, 
  491. will also set the program error flag to the value of the code passed to 
  492. exit(). (Note that the zcpr program error flag is one byte, which is why there 
  493. are only 255 codes described above).
  494.  
  495. The exit code interpreter is followed by an i/o subsystem exit routine, which 
  496. consists of closing all unclosed files.
  497.  
  498. These are finally followed by the return to system vector (which, as its 
  499. description implies, doesn't return to exit()). This points to a routine that 
  500. jumps to the address that Uentry() took from the stack as a system return 
  501. address when the program started up. If, at any time during execution of your 
  502. program, some condition arises that warrants a warm boot (such as a call to 
  503. mrelease() the CCP for use as free core by the memory management routines), 
  504. you should call the CLIB function setwbt(), which will zero this word, causing 
  505. a warm boot at exit(). It is often a good idea to setwbt() while debugging 
  506. rpograms still under development.
  507.  
  508. A little thought will show just how flexible this scheme is. There are likely 
  509. more possibilities here than anybody, or at least most folks including myself, 
  510. will probably fully utilize. It will make many things that would otherwise be 
  511. a pain, in particular the tight coupling of code packages to the runtime 
  512. support, including opportunities for self-initialization and exit 
  513. housekeeping, quite simple. One could, for instance, if one needed to use the 
  514. SYSLIB routines for some reason, set up a set of C callable front ends for 
  515. them, an initialization and de-initialization routine, and a proper 
  516. configuration header include file, then be off and running (assuming, of 
  517. course that there are no name clashes with the standard library at link time, 
  518. which I'm almost sure there are). Likewise, if you were writing code for some 
  519. Z80 based controller board, you could write your own runtime support package 
  520. and corresponding header, even still using parts of CLIB as apropos, develop 
  521. and compile controller code on your system, then move it to the controller, 
  522. essentially constructing a cross-compiler for the other environment. The 
  523. possibilities are limited mainly by the imagination.
  524.  
  525.                                 ---===*===---
  526.  
  527.  
  528.  
  529.  
  530.                                  Using CZVLIB
  531.  
  532.  
  533. CZVLIB contains zcpr dependent functions. One rule to observe with it (or any 
  534. modules to be found outside CLIB itself) is that you must link everything 
  535. you'll need PRIOR to searching CLIB for its modules. That's because CLIB 
  536. contains the Upgmend module, which the runtime uses to find the end of the 
  537. program, and hence must be linked last.
  538.  
  539. Given use of the default configuration header or a similar one, and that the 
  540. program is linked with CZVLIB being searched before CLIB, the Usysinit() 
  541. routine in the entry vector will be taken from CZVLIB rather than CLIB, and 
  542. certain things in the runtime o/s interface initialization will be different. 
  543. These will be described here.
  544.  
  545. In general, Usysinit() and the exit vector based routines in CZVLIB will check 
  546. to see that they are running in a zcpr environment before excuting zcpr 
  547. dependent code. However, this is not true of the other routines in CZVLIB like 
  548. the TCAP-based console output functions and so forth in most cases, because it 
  549. would have bloated and slowed down the code too much. Therefore, the rule of 
  550. thumb is that code that gets executed automatically before or after your code 
  551. has control will check to see if it is in a zcpr environment, and won't 
  552. perform any zcpr based code if not. However, once control is passed to your 
  553. code, you should explicitly check in the beginning if the environment is zcpr 
  554. if you need it, and perform some appropriate action such as terminating with a 
  555. message if not. You can do this by calling Zenvck(), which returns 0 if the 
  556. environment pointer in the Z3ENV header doesn't point to a valid environment 
  557. descriptor and returns a pointer to the env if it does. Zenvck() also sets or 
  558. resets the zero flag based on the value it's returning to facilitate simpler 
  559. calls from assembler. This test will of course fail if the program is not 
  560. installed for your system.
  561.  
  562. Note that you can do different initialization processing (and thus modify 
  563. anything described here) by either supplying your own Usysinit() prior to 
  564. searching either of the libs or by modifying the configuration header to call 
  565. a different name and supplying your routine with that name. The latter is 
  566. probably better, since then you can also call Usysinit() from within it, and 
  567. simply do additional initialization of your own. That way you'll know any 
  568. setup the library routines need has been performed and you won't have to worry 
  569. about doing it yourself.
  570.  
  571. Some other differences:
  572.  
  573. Argv[0] for programs linked to run under zcpr is taken from the external fcb, 
  574. and as such, reflects the name the program was actually invoked under. 
  575. Programs linked to run under cp/m simply have 'CMDNAME' for argv[0]. So will 
  576. programs linked with CZVLIB, but run under vanilla cp/m.
  577.  
  578. The code passed to exit(code) will be placed in the zcpr program error flag. 
  579. Zero means "no error". This brings up the point that you should always pass a 
  580. code to exit() as a parameter, even if it is only zero, since exit() will take 
  581. whatever is on the stack as a code, and if it isn't, you may be turning on the 
  582. o/s program error flag when you don't intend to.
  583.  
  584. See the CZVLIB docs for more about currently available functions.
  585.  
  586.                                 ---===*===---
  587.  
  588.  
  589.  
  590.  
  591.                        The new memory management scheme
  592.  
  593.  
  594. One of the major deficiencies of Small-C was its lack of a flexible approach 
  595. to memory management. The scheme it had, while adequate for many purposes, 
  596. definitely made it difficult to use complex dynamic memory structures like 
  597. trees. What was needed was a flexible approach to dynamic use of ram, without 
  598. regard to the order that it was allocated and freed in. This led to a complete 
  599. redesign of the memory management routines in the library. The old scheme is 
  600. still available as a separate linkable module for backward compatibility and 
  601. situations where the couple hundred bytes are more crucial than the extended 
  602. functions. It can be accessed by simply linking SMALLALC.REL to your code 
  603. prior to searching and linking CLIB.
  604.  
  605. The new scheme is both far more flexible and presents some very interesting 
  606. possibilities, I think. Some of its features are:
  607.  
  608.     - Allocations and deallocations can be performed in a completely arbitrary 
  609.       order. the currently free core is kept in a linked list, so later 
  610.       allocations are not lost in the process of freeing earlier ones.
  611.  
  612.     - The free list is kept as defragmented as possible, i.e. as allocated 
  613.       ram is freed, adjacent nodes in the free list are merged to keep the 
  614.       available memory chunks as large as possible.
  615.  
  616.     - The runtime stack now grows downward from the heap, so that it is easier 
  617.       to make sure it isn't corrupting anything, and so that it will be 
  618.       possible to do things like write RSX loaders in SmallC/Z.
  619.  
  620.     - Allocations in the heap are performed downwards from the top, so that 
  621.       the largest piece of free core tends to stay just above the stack, which 
  622.       allows relocation of the stack upwards as well as downwards for purposes 
  623.       of adjusting the relative sizes of the two.
  624.  
  625.       Note:
  626.                the routines for resizing the stack vs. heap aren't yet 
  627.                written, but should be simple, and will happen soon. additional 
  628.                functions like checking if the stack has overrun its bounds, 
  629.                and one additional system pointer that will make it possible to 
  630.                independently relocate either or both anyplace are planned too.
  631.  
  632.     - The functions xalloc(), xfree(), and xainit() now provide the ability to 
  633.       use the same basic memory management primitives temporarily within any 
  634.       allocated block (as long as it is large enough to contain the several 
  635.       words of control information necessary), so that sporadic very small 
  636.       allocations can be specifically taken from one area without fragmenting 
  637.       the overall core space. This "in block" memory mangement has the full 
  638.       functionality of the overall scheme (free nodes kept in a list, 
  639.       defragmentation on the fly, etc). This is highly recommended for such 
  640.       small requests that are made at arbitrary points during the program's 
  641.       execution, because otherwise a best fit allocation scheme will tend to 
  642.       fragment your core.
  643.  
  644.     - Since the free list is now available, an mrelease() function can now be 
  645.       provided to release the space taken by code used only once in programs 
  646.       for consideration by the memory management routines to satisfy requests. 
  647.       Similarly, since the CCP is not overwritten now by default, mrelease() 
  648.       can be used to add it to the free memory pool. (Be sure to execute a 
  649.       setwbt() function if you do this, so that the program warm boots at 
  650.       exit, instead of executing a return to your data areas...)
  651.  
  652.                                 ---===*===---
  653.  
  654.  
  655.  
  656.  
  657.                              Basic memory layout
  658.  
  659.  
  660. There is a stack, used for function call frames and parameters, and a heap, 
  661. used for dynamic allocation. The heap takes the form of a singly linked list 
  662. ABOVE the stack, with free nodes occurring in descending order by address. The 
  663. first two bytes of each node is an unsigned size of the node itself, and if 
  664. the node is in the free list, this is followed by a two byte pointer to the 
  665. next node, or zero. If the node is currently allocated, the size bytes are 
  666. still there, and the pointer returned to the user points to the byte following 
  667. the size bytes. When the node is freed, the size is used to merge it to any 
  668. adjoining nodes, and the resulting node is linked into the list (if not 
  669. already in it as an effect of merging adjacent nodes.) Granted, there is some 
  670. space overhead involved here, but the flexibility afforded is worthwhile in 
  671. cases where complex data structures are being built dynamically.
  672.  
  673. The algorithm for allocation is to search for a best fit, highest address 
  674. node, then to allocate the minimum fragment of the node necessary. Since no 
  675. node smaller than four bytes can exist in the free list (two for size, two for 
  676. pointer to next), this means that if the remainder of the node is three bytes 
  677. or less, the entire node is allocated. This will only occur if the node was 
  678. created using the mrelease() function or in the case where the entire 
  679. remaining core will not be enough to constitute a free node in itself. These 
  680. are the only situations that would lead to this, since the normal low level 
  681. allocate function always rounds the node size up to the next multiple of four 
  682. in anticipation of memory fragmentation. In general, for any request of "n" 
  683. bytes, the actual memory usage if granted is:
  684.  
  685.                             (n+2) + ((n+2)mod(4))
  686.  
  687. unless the remaining free core after the above would be less than 3.
  688.  
  689. Stack and heap are separated by a pointer to a "fence" location.  The stack 
  690. itself bottoms out at the top of the program, and the top of the heap is set 
  691. to just below the CCP or an RSX if one or more are present. SmallC/Z avoids 
  692. overwriting the CCP so that it can terminate by executing a return rather than 
  693. a warm boot. The setwbt() function is provided to "set warm boot on" if the 
  694. programmer wishes to do so, which could be necessitated by a variety of 
  695. things, including mrelease()'ing the CCP itself.
  696.  
  697.  
  698. -----------------------------------------------------------------------------
  699.     Umemtop    ------>    CCP or RSX base -------------------------------------
  700.         Uheaptop -----> Top of heap
  701.                     |    space for the heap to live. initially half
  702.         ^    |    of free core. 
  703.         |    |
  704.     Ufrechn ------>    |       pointer to free node list
  705.         |    |    this is initialized to one node the size of
  706.         v    |    free core, then varies as memory fragments.
  707.             |    the last last is free core above fence.
  708.             |
  709.             v
  710.     Uheapbot --/-->    fence address --------------------------------------
  711.     Ustktop --/    ^    The stack grows down from here, and the heap
  712.             |    is placed above it.
  713.             |
  714.         Ustkbot ------> Bottom of stack
  715.     Umembot ------> Top of program -------------------------------------
  716.         Upgmtop:        |
  717.                         |
  718.         Uloadpt:        Bottom of program ----------------------------------
  719. -----------------------------------------------------------------------------
  720.  
  721. Figure: A rough layout of the memory structure and its associated pointers in 
  722.         the default setup. Customization is possible for unusual situations. 
  723.         Both the stack and heap have separate sets of top and bottom pointers 
  724.         so that they can be independently placed anywhere in core. By default 
  725.         they occupy the area between program top and either the bottom of the 
  726.         CCP or the lowest memory protected by an RCP, with the heap above the 
  727.         stack and the core split evenly between the two. However, each can be 
  728.         relocated independent of the other. In general, one wouldn't want to 
  729.         relocate the heap once it is in use, since the program has pointers 
  730.         into it. One could do it through the entry vector routine, however, 
  731.         since it excutes before almost anything else. It's fine to relocate 
  732.         the stack any time, if one is sensible about it.
  733.  
  734. -----------------------------------------------------------------------------
  735.  
  736.  
  737. Initialization of the above scheme is done in runtime routine Umeminit(), 
  738. which is called by Uentry after it determines and stores the top and bottom of 
  739. usable core. The Umeminit() routine is responsible for any initialization of 
  740. its associated memory mangement scheme, and in particular, it MUST set the 
  741. stack pointer. The older scheme has its own Umeminit() routine which 
  742. initializes that system more according to the original Small C's style.  
  743. Module UPGMTOP contains the global tag Upgmtop:, and should be the last 
  744. routine linked, i.e.  CLIB should be the last searched library.
  745.  
  746.                                 ---===*===---
  747.  
  748.  
  749.  
  750.  
  751.                            Installing the compiler
  752.  
  753.  
  754. The compiler should be usable as is on most systems. However, you may want to 
  755. change some modifiable defaults. You can do so with an appropriate patching 
  756. tool. Once the compiler has stabilized, I'll release the source, and you'll 
  757. also have the option of recompiling the compiler.
  758.  
  759. Current patch point(s):
  760.  
  761.      011dh     offset into header include string affected by -z flag
  762.                This is a two byte value, low order byte first. It is an
  763.                offset (C array index), so the first byte is zero, etc.
  764.                Default is 0100h (decimal 16).
  765.  
  766.      011fh     header include string. This is written to the beginning of
  767.                of assembler source files output by SmallC/Z that contain
  768.                a main() function, and is affected by the above patch.          
  769.                Default is "$INCLUDE SYSCCFG1.CCZ". The character affected
  770.                by -z# is "1". You have 25 bytes, including the necessary
  771.                trailing null.
  772.  
  773.                                 ---===*===---
  774.  
  775.  
  776.  
  777.  
  778.                      Compiler and library version numbers
  779.  
  780.  
  781. The compiler and libraries "bury" their version numbers in programs you 
  782. compile and link with them, so that you can determine the versions of the the 
  783. compiler and libraries used to create any program. The version numbers are 
  784. preceeded by eye-catchers so you can find them in binary executables, and are 
  785. given global tags and terminated by nulls so you can refer to the version 
  786. number strings in program code if you like. For any *.COM file created with 
  787. SmallC/Z, just go into a patch utility do repeated searches for the string 
  788. 'SCZ'. Some may be spurious, but some will count. The version number is an 
  789. ascii string of the form "#.#", possibly followed by some character, then a 
  790. null terminator, so it's displayable from in the program with standard library 
  791. functions.
  792.  
  793.                        eye-catcher  global tag  current version
  794.                        -----------  ----------  ---------------
  795.      Compiler version  'SCZ'        Usczver:    0.1b
  796.      CLIB     version  'SCZCLB'     Uclibver:   0.1
  797.      CZVLIB   version  'SCZCZV'     Uczvlver:   0.1
  798.  
  799.