home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / beehive / program / tm2not10.arc / TM2NOTES.DOC < prev   
Encoding:
Text File  |  1990-07-22  |  23.4 KB  |  595 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.                              Notes on Turbo Modula-2
  9.  
  10.                                 by Steven Hirsch
  11.  
  12.         Original release: 01/25/87
  13.  
  14.         Intro:
  15.  
  16.         My company, a contractor in the Audio-visual and communications 
  17.         field, had undertaken a project for a large industrial client 
  18.         which would involve micro-computer based scheduling and control 
  19.         of video recorders. In late September we made the decision on a 
  20.         Z80 CP/M environment, and had chosen Modula-2 as the development 
  21.         language.
  22.  
  23.         We began with FTL Modula-2 from Workman & Assoc., as it was the 
  24.         only native-code compiler available at that time. By late 
  25.         October, panic was starting to set in. I had uncovered many bugs 
  26.         in the compiler/linker implementation and, despite Workman's 
  27.         polite concern, no fixes were ever brought to this programmer's 
  28.         attention. I was on the verge of re-writing the program (some 
  29.         5,000 lines+ at this point..) in Pascal MT+ when the impending 
  30.         release of Borland's TM-2 was announced. Echelon, Inc., when 
  31.         they learned of my predicament, agreed to help out; shipping me a 
  32.         copy of the software, with a 3" stack of galley-proof sheets for 
  33.         documentation. 
  34.  
  35.         I planned on moving into the computer room for an extended stay, 
  36.         figuring that the conversion process would be painful... It's 
  37.         nice to be wrong once in a while. The conversion to TM-2 was 
  38.         accomplished in one Saturday afternoon, with the largest effort 
  39.         being the implementation of 'SET OF CHAR'. FTL supports sets with 
  40.         up to 1024 members, while TM2 has the more traditional (for 
  41.         micro's) limitation of 16! 
  42.  
  43.         To make a long story short, we delivered the controller on-
  44.         schedule. The software has been absolutely bullet-proof (so 
  45.         far..), and the customer is delighted! In the course of this 
  46.         ordeal, I found myself being the first individual in the field 
  47.         to:
  48.  
  49.         a) Develop a complete product in TM-2.
  50.         b) Use the assembler interface heavily.
  51.         c) Use interrupt driven task-switching.
  52.         d) Decipher the manual..
  53.  
  54.         The assember interface, in particular, seems to be causing a 
  55.         great deal of confusion in the user's community. I therefore am 
  56.         writing this (somewhat rambling) tome to help those wrestling 
  57.         with the compiler as we speak!
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.                             Assembly Language Modules
  76.  
  77.         As mentioned in the manual, TM-2 will allow one to create modules 
  78.         written in Z80 assembly language. I will limit this discussion to 
  79.         the technique of converting .REL files to .MCD, as the 'CODE' 
  80.         method (p.194 of the manual) has some fatal constraints (though 
  81.         it contains some valuable hints on parameter-passing!).
  82.  
  83.         The use of an assembler capable of generating a Microsoft-format 
  84.         REL file is essential (I use the SLR Systems Z80ASM tool, and 
  85.         have found it to be the best of the genre. Be sure to specify the 
  86.         /6 or /7 switch when using this program!). 
  87.  
  88.         In certain sections of the low-level I/O code, I was able to 
  89.         cross-assemble 6502 code using the Microsoft ALDS assembler, 
  90.         which digests 8080, Z80, and 6502 mnemonics - producing a 
  91.         standard .REL file. The target environment is a dual-processor 
  92.         system, and some of the I/O primitives needed to be handled by 
  93.         the 6502 host... Intricacies of the control project will be dealt 
  94.         with in a future article, to be published (I hope..) by one of 
  95.         the hacker-oriented mags.
  96.  
  97.         Parameters are passed to the assembly routine on the stack, 
  98.         pushed on in left-to-right sequence (as they appear in the 
  99.         DEFINITION module). If the parameter is passed by value, (ie. the 
  100.         called procedure is not able to return a value or affect the 
  101.         parameter..) and is word-sized, it is placed on directly. Things 
  102.         are thus straight-forward for CARDINAL, INTEGER, ADDRESS, WORD, 
  103.         and CHAR types. In the case of BYTE or CHAR, the most signifigant 
  104.         byte of the word will be cleared to zero. Note that ARRAY[0..1] 
  105.         OF CHAR, although two bytes in length, will not be treated this 
  106.         way! It must be noted that I have not worked at all with floating 
  107.         point, or longs. A general technique for determination of the 
  108.         passing method will be described later.
  109.  
  110.         Any parameter passed by reference (called procedure able to 
  111.         affect it's value..) gets a one-word pointer to itself placed on 
  112.         the stack. This applies across the board to all types, although 
  113.         open array types are passed with additional information, as we'll 
  114.         see.
  115.  
  116.         Enumerated types are passed by their ORD value (word length!) 
  117.         within the enumeration, ie. if we have this situation:
  118.  
  119.         TYPE
  120.           Junk = (foo, bar, other, things);
  121.  
  122.         And the procedure:
  123.         PROCEDURE DoSomething( formalparm : Junk );
  124.         exists.
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.         DoSomething(bar);
  142.         Will cause the word 01h to be placed on the stack! 
  143.  
  144.         Following the above trend:
  145.         DoSomething(foo);
  146.  
  147.         would generate the value 0h, etc.
  148.  
  149.         To reiterate, and clarify things, the procedure:
  150.         PROCEDURE DoSomething( VAR formalparm: Junk );
  151.  
  152.         Is called from this situation:
  153.  
  154.         VAR
  155.           stuff : Junk;
  156.  
  157.         BEGIN
  158.           stuff := foo;
  159.           DoSomething(stuff);
  160.  
  161.         The DoSomething routine will find a one-word pointer to the 
  162.         location of the variable 'stuff' pushed on the stack. At that 
  163.         location will be the word-value '0000h'.
  164.  
  165.         Structured types such as ARRAY, RECORD, etc. are always passed 
  166.         via pointer. I believe that, when passed by value, the compiler 
  167.         creates a 'copy' of the variable prior to the call and passes a 
  168.         pointer to it; although I am not 100% sure of this fact. 
  169.         Responsibility for knowing the physical size and internal 
  170.         structure of the referenced object lies completely on the 
  171.         programmer, when dealing with assembler modules. The manual 
  172.         outlines the storage sizes of the various types, although the 
  173.         information is scattered across several sections. 
  174.  
  175.         The exact method of declaration is extremely important when 
  176.         dealing with ARRAY types; a declaration of the form:
  177.  
  178.         PROCEDURE WorkWithString( VAR formalparm : ARRAY OF CHAR );
  179.  
  180.         will be called with an extra parameter on the stack. First a 
  181.         pointer to the array is pushed on, followed by a word value 
  182.         representing the index (normal to zero) of the last element in 
  183.         the array. Note that the programmer is responsible for 
  184.         translating this index into a relative byte-offset. If one is 
  185.         dealing with ARRAY OF WORD, it is necessary to double the index. 
  186.         I believe that the same method is used for multi-dimensional open 
  187.         array's, although this programmer has not investigated it. 
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.  
  206.         If a declaration specifies the size of the array, the extra 
  207.         parameter is not present - only the address is passed. Be very 
  208.         careful when passing an odd-length text string (or ARRAY OF 
  209.         BYTE!) to a procedure expecting ARRAY OF WORD. The compiler will 
  210.         round up it's idea of the string-length to the next word boundry, 
  211.         which may cause your assembly code to step on an adjacent, and 
  212.         unrelated, variable!
  213.  
  214.         After all parameters are placed on the stack, the return address 
  215.         is pushed on over them. The actual 'call' to the routine is 
  216.         performed by a 'JP (HL)' instruction, which means that 'HL' 
  217.         contains a pointer to the start of your assembly routine upon 
  218.         entry. This correlates to the explanation of 'CODE'! Although 
  219.         your assembly code need not preserve any registers, the return 
  220.         address must be kept track of. 
  221.  
  222.         My favorite method for dealing with parameters and return address 
  223.         is outlined in the accompanying file 'CLOCKIO.MAC'. Along with 
  224.         it's .DEF file, this is the low-level module used to communicate 
  225.         with the real-time clock in our controller. The RTC talks to us 
  226.         via a PIO chip, which is memory mapped into the Z80's address 
  227.         space. Without getting sidetracked into the hardware, this code 
  228.         should serve as an outline for interested programmers. It is 
  229.         called with two strings variables as parameters, returning with 
  230.         time and date loaded into them as ASCII strings (MM/DD/YR, and 
  231.         HH:MM). Note that we are not making use of the length index, as 
  232.         it is assumed to be a known constant -  this does not represent 
  233.         the worlds greatest programming (Blush..), and the module ought 
  234.         to trap this situation.
  235.  
  236.         All procedure entry points are defined as public symbols. I 
  237.         prefer the '::' method, but 'PUBLIC symbol1, symbol2, etc.' may 
  238.         suit some folk's style better. One difficulty arises, in that 
  239.         there is no apparent way to cause the 'module body' intialization 
  240.         code (in our case the clock locator) to be executed before pas-
  241.         sing control to the main program. I solved this by creating a 
  242.         'dummy' module (in standard non-assembler Modula) containing only 
  243.         the call to 'INITCLK' in it's body, thus guaranteeing that 
  244.         'INITCLK' is entered prior to any read-time calls. If the clock 
  245.         is not found, a message is displayed on the screen, and the 
  246.         program terminates to CP/M.
  247.  
  248.         If the assembler routine has been defined as a function 
  249.         procedure, it's value (which **must** be a non-structured type, 
  250.         of one-word length!) is pushed onto the stack before executing 
  251.         the return.
  252.  
  253.  
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.         The easiest way to get a feel for what is passed to a procedure, 
  273.         and how, is to write a dummy assembler module which pops a number 
  274.         of words off the stack, stores them in a known 'safe' area of 
  275.         RAM, and terminates with a RST 38H. The assembly code is linked
  276.         together with an M-code module, which feeds it known parameters. 
  277.         The .COM file is executed under a debugger (I used the excellent 
  278.         Z8E tool, which is available in the public domain). When the RST 
  279.         break-point is hit, one may inspect the assigned storage area to 
  280.         find out what's what. DO NOT make the mistake of immediately 
  281.         terminating with a RST and inspecting the stack with the 
  282.         debugger, as it's stack becomes intertwined with the program 
  283.         being handled, causing endless confusion.
  284.  
  285.         No discussion of the assembler interface is complete without 
  286.         mentioning the fact that the REL.MCD utility requires GOBS of TPA 
  287.         to run, and will not successfully link to a .COM file either! A 
  288.         standard Z3 system does not have the TPA to convert even the 
  289.         smallest .REL file. I was forced to re-boot the computer under 
  290.         vanilla CP/M (what's that?), in order to perform this function. 
  291.         Borland has been notified of this anomoly, but I wouldn't hold 
  292.         one's breath for a solution.
  293.  
  294.         Some other miscellaneous points:
  295.  
  296.         - One must limit the name of any assembler routines to seven 
  297.         characters or less (public symbol limitation of the Microsoft 
  298.         assemblers!), or REL.MCD cannot figure out where to assign entry 
  299.         points. 
  300.  
  301.         - Case does not seem to be an issue. I defined all entry points 
  302.         in upper case, in the assembly code, and in mixed case in the 
  303.         definition module. No problems occured, although if you have a 
  304.         procedure Foo and FOO in the same program it may get confused!
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.  
  317.  
  318.  
  319.  
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.  
  338.                          Interrupt-driven Task Switching
  339.  
  340.         This was another venture into the great unknown of incomplete 
  341.         documentation. After digesting some half-dozen books on Z80 
  342.         assembly language, and at least that many on Modula-2, I 
  343.         discovered an incredible dearth of hard information on 
  344.         interrupts. Most texts on assembly coding gloss over it, or 
  345.         ignore it completely - while the Modula texts sort of mention 
  346.         that it 'can be done but you really don't need to know about 
  347.         it'. After many days of frustration, during which I became 
  348.         intimately aquainted with the reset button on the target machine, 
  349.         I figured out how it 'can be done'. 
  350.  
  351.         The Z80 has three modes of interrupt response, IM0, IM1, and IM2. 
  352.         On reset, the CPU comes up with non-maskable interrupts disabled, 
  353.         and in mode 0. Your BIOS may have other ideas on the mode, so be 
  354.         **careful** with any assumptions here! In the case of our target 
  355.         machine, the BIOS made no use of interrupts whatever, and 
  356.         executed a DI instruction (no interrupts) on warm-boot.
  357.  
  358.         Z80 mode 0, and mode 2, expect a portion of the interrupt vector 
  359.         to appear on the data buss, generally placed there by the 
  360.         interrupting device. Our clock had no means of doing so, and 
  361.         these methods were subsequently ruled out. Mode 1 seemed to fit 
  362.         the bill; it causes a transfer to the vector at 38h (RST 7), 
  363.         pushing the address of the next pending instruction on the stack 
  364.         before doing so (no information is needed from the data buss). 
  365.         The TM2 manual points out that the programmer is responsible for 
  366.         setting up the correct interrupt mode, and indicates that the 
  367.         IOTRANSFER procedure will initialize the interrupt vector. 
  368.         Unfortunately, it does not bother to mention what interrupt mode 
  369.         it is assuming! 
  370.  
  371.         A bit of investigation disclosed that IOTRANSFER places a one-
  372.         word vector to the interrupt code at the address specified in 
  373.         it's third calling parameter (see the manual!). That's all, just 
  374.         the address, no jump instruction. This seemed to imply that TM2 
  375.         was designed to operate with Mode 2, using vectored interrupts. 
  376.         After some head-scratching, I realized that it would be possible 
  377.         to create a short section of code ORG'ed at 38h, then bias the 
  378.         vector passed to IOTRANSFER, causing it to patch the interrupt 
  379.         address into a 'JP' (or 'CALL') instruction in the stub. This 
  380.         would then enable the use of Mode 1 interrupts. The files 
  381.         INTHANDL (.def and .mod), and MAIN.MOD illustrate one method for 
  382.         doing this, along with a suggested method for setting up an 
  383.         interrupt-driven co-routine. Also, some handy primitives appear 
  384.         in the CLOCKIO.MAC listing. 
  385.  
  386.  
  387.  
  388.  
  389.  
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.  
  404.         Once the IOTRANSFER vector has been established, clock interrupts 
  405.         cause immediate transfer of control to the instruction following 
  406.         the IOTRANSFER statement. TM2 saves all registers and, more 
  407.         critically, resets it's stack and heap to opposite ends of the 
  408.         'workspace' established for it. Any references to static objects 
  409.         (at the module level) will be valid, and any Modula procedures 
  410.         may be re-entered at will if they rely upon local parameters 
  411.         (remember you will permanently affect statics!). The function 
  412.         DISPOSE will operate properly for dynamic variables in the non-
  413.         interrupt heap, however NEW will grab it's space from the 
  414.         (probably limited!) interrupt heap. The previous fact will hasten 
  415.         a fatal stack collision, if not minded carefully!
  416.  
  417.         Although TM2 routines may be re-entered, under the defined 
  418.         conditions, CP/M cannot. As an interrupt may occur from within 
  419.         the operating system, one must learn to think of the o/s as a 
  420.         sleeping giant - not to be disturbed. It is assumed that any 
  421.         time-sensitive BIOS code would lock out interrupts completely. In 
  422.         our controller, we used our own handlers to 'talk' to the 
  423.         hardware peripherals, avoiding the o/s completely! Just remember 
  424.         that the foreground task needs to resume from the interrupt as if 
  425.         nothing had occured. Anything that does occur to static data (or 
  426.         peripheral devices) must be thought through carefully, or 
  427.         unexpected side-effects will manifest themselves. 
  428.  
  429.         Delightfully, TM2 takes care of all housekeeping with regard to 
  430.         which modules may receive interrupts. Generally the main module 
  431.         will not be a 'monitor', ie. it will have interrupts enabled. 
  432.         This is the normal state for a TM2 module. If one desires to lock 
  433.         out interrupts, the IMPLEMENTATION module is declared like this:
  434.  
  435.         IMPLEMENTATION MODULE NoInterruptsAllowed[n];
  436.  
  437.         Any postive integer between 1 and 9 may be used, all will have 
  438.         the effect of locking out interrupts within the module. Any non-
  439.         monitor modules called from the above module inherit it's 
  440.         interrupt status. Unless your interrupt scheme is re-entrant, it 
  441.         is suggested that the interrupt handler loop be in a module which 
  442.         declares as a monitor. Naturally, one may use the Z80EI and Z80DI 
  443.         routines in my CLOCKIO code. Any mayhem created is then your 
  444.         responsibility!
  445.  
  446.  
  447.  
  448.  
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469.  
  470.                                  Other Subjects!
  471.  
  472.         Version control can easily become a headache in a large project. 
  473.         One is always able to remember the chronological sequence of 
  474.         compilation when only a few modules are in existance. My project 
  475.         grew to 30+ modules, and tracking the effects of definition 
  476.         module changes became a nightmare. This deserves a whole tirade 
  477.         to itself:
  478.  
  479.         For the past year and a half I have made extensive use of Plu-
  480.         Perfect software's DateStamper program. It is one of those 
  481.         utilities that, once you use it, you can't believe you ever got 
  482.         along without it. Theoretically one could simply look for and 
  483.         re-compile all modules in a given project which post-dated the 
  484.         modified definition module (being sure to note it's date-stamp 
  485.         prior to changing it!). Wrong. For reasons which are still under 
  486.         investigation, TM2 breaks so many rules of civilized CP/M 
  487.         behavior that the DateStamper becomes absolutely unreliable. From 
  488.         what I have been able to determine, the editor (at least) will 
  489.         open a file with a specific FCB referenced, and then close it 
  490.         with a reference to an FCB in a different location. Whether it 
  491.         moves the FCB, copies it, or whatever, is unknown. This causes no 
  492.         problems with CP/M, however DateStamper is unable to track this 
  493.         sort of thing, and files will not reliably get their 'modify' 
  494.         stamp updated! This leads to files with blank modify fields, 
  495.         although they have a create date (for the most part, more later). 
  496.  
  497.         A little digression is necessary here. I do most of my 
  498.         development programming on a one-meg ramdisk, moving all the work 
  499.         files (and the compiler) to it on startup. Periodically during a 
  500.         day's work, my usual habit is to run DateSweep, selecting for all 
  501.         files modified that day, and copy them to a back-up diskette. 
  502.         DateSweep will only copy a file if the target disk either doesn't 
  503.         contain that file or, if it does contain it with an earlier 
  504.         datestamp. If no modify stamp is present on source and target, it 
  505.         will not copy it at all. This seemed to prevent the use of 
  506.         DateSweep. 
  507.  
  508.         A quick inspection led me to believe that the 'create' datestamp 
  509.         was being handled correctly, and I contacted Bridger Mitchell to 
  510.         see if a mod could be made to DateSweep, causing it to use the 
  511.         later of 'Create' or 'Modify'. Bridger responded promptly with a 
  512.         version which did this, asking me to Beta test it.. At first 
  513.         everything seemed to go well, files which had been updated were 
  514.         backed up according to their create dates before shutting off the 
  515.         computer (and killing the Ramdisk) - I thought. About one day 
  516.         into this method, I was rudely made aware that source and object 
  517.         code which had been de-bugged were exhibiting old problems again.
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.         Once all the moaning and groaning had subsided (and hardcopy of 
  537.         the correct code was typed in manually), I began a full-scale 
  538.         investigation of this phenomena. The bottom line is BE CAREFUL!!. 
  539.         TM2 is extremely erratic in it's interface with DateStamper. 
  540.         Sometimes all goes well, and the create date jives with when the 
  541.         source or object code originated. Sometimes the output file has 
  542.         no date at all, sometimes it inherits an invalid date from the 
  543.         previous incarnation of the file...  phooey! This is definitely 
  544.         attributable to the compiler. I have not had one bit of such 
  545.         misbehavior through the entire course of previous development 
  546.         projects. 
  547.  
  548.         ZRDOS's archive function does detect writes to files, so I am 
  549.         forced to resort to a kludgy method. The archive bit on all 
  550.         ramdisk files is set during setup. Before backup, I examine them 
  551.         manually to check for valid stamps. This requires keeping an 
  552.         accurate log of all changes, and the time which one performed 
  553.         them at! Why don't I just use the AC utility to copy any changed 
  554.         files? AC does not copy the stamp, and it will make it seem as if 
  555.         all modified files were copied at the same time, no use for 
  556.         tracking compilation order.
  557.  
  558.         If versions get out-of-sync on a many moduled project, it's a 
  559.         bear! The FTL compiler is supplied with a poorly documented and 
  560.         buggy utility called PRECEDEN.COM. After some modification, I 
  561.         have found it useful in creating the re-compilation sequence for 
  562.         any given definition file which becomes changed. Workman's manual 
  563.         states that 'the supplied utility programs may be freely 
  564.         distributed as linked programs'. This seems fairly generous, and 
  565.         I will be uploading the corrected object code for these files 
  566.         (with instructions) in the near future. I am not expecting any 
  567.         change in TM2's file protocol, but Bridger has a copy of it and 
  568.         if anyone can create a workaround, he can.
  569.  
  570.         If the reader has had the patience to stay with this so far, 
  571.         please advise me of any areas demanding additional explanation. 
  572.         If I have mis-represented any aspect of the above subjects, bring 
  573.         this to my attention also. I check in regularly with ZNODE 
  574.         Central, Downspout, Lilliput (System 1), and Sage's boards. New 
  575.         facts brought to light will help all of us!
  576.  
  577.         Good Modularity!
  578.  
  579.         Steve
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.