home *** CD-ROM | disk | FTP | other *** search
/ Jason Aller Floppy Collection / 85.img / AMAG1289.ZIP / AMAG1289.TXT
Text File  |  1989-12-16  |  113KB  |  3,148 lines

  1.  
  2.  
  3.  
  4.  
  5. THE ASSEMBLY LANGUAGE "MAGAZINE"                  VOL 1 NUMBER 4
  6.                                                   December 1989
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.   ##     ####    ####   ####### ##   ## ######  ####    ##  ##
  25.  ####   ##  ##  ##  ##   ##   # ### ###  ##  ##  ##     ##  ##
  26. ##  ##  ###     ###      ## #   #######  ##  ##  ##     ##  ##
  27. ##  ##   ###     ###     ####   #######  #####   ##      ####
  28. ######     ###     ###   ## #   ## # ##  ##  ##  ##   #   ##
  29. ##  ##  ##  ##  ##  ##   ##   # ##   ##  ##  ##  ##  ##   ##
  30. ##  ##   ####    ####   ####### ##   ## ######  #######  ####
  31.  
  32.         ####      ##    ##   ##   ####  ##  ##    ##      ####  #######
  33.          ##      ####   ###  ##  ##  ## ##  ##   ####    ##  ##  ##   #
  34.          ##     ##  ##  #### ## ##      ##  ##  ##  ##  ##       ## #
  35.          ##     ##  ##  ## #### ##      ##  ##  ##  ##  ##       ####
  36.          ##   # ######  ##  ### ##  ### ##  ##  ######  ##  ###  ## #
  37.          ##  ## ##  ##  ##   ##  ##  ## ##  ##  ##  ##   ##  ##  ##   #
  38.         ####### ##  ##  ##   ##   ##### ######  ##  ##    ##### #######
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  Written by and for assembly language programmers.
  63.  
  64.  
  65.  
  66.                                  Table of Contents
  67.  
  68.           Editorial.......................................2
  69.           Policy and Guide Lines..........................3
  70.           Beginners' Corner...............................5
  71.           Structure, Speed and Size
  72.                By Thomas J. Keller........................7
  73.                Editorial Rebuttal........................11
  74.           Accessing the Command Line Arguments
  75.                By Thomas J. Keller.......................13
  76.           Original Vector Locator
  77.                by Rick Engle.............................15
  78.           How to call DOS from within a TSR
  79.                by David O'Riva...........................22
  80.           Environment Variable Processor
  81.                by David O'Riva...........................26
  82.           Program Reviews................................35
  83.                Multi-Edit ver 4.00a .....................35
  84.                SHEZ......................................36
  85.                4DOS......................................36
  86.           Book Reviews...................................37
  87.                Assembly Language Quick Reference
  88.                     Reviewed by George A. Stanislav......37
  89.           GPFILT.ASM.....................................39
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.                                    ;Page 1
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.                                   Editorial
  136.  
  137.  
  138.           It has been much too long since the last issue of the Magazine was
  139.      published. Much of this time was due to the lack of submissions but
  140.      there has been enough to assemble since early November. I hope that
  141.      it will not be as long till the next one is ready for distribution.
  142.      You can help make that possible by writing up and sending in an
  143.      article.
  144.  
  145.           I'm trying out a new editor for this issue.  That makes it four
  146.      editors for 4 issues. There is a review of it in the review section.
  147.  
  148.           There is a continueing and probably insoluable problem in
  149.      formatting the 'Magazine'.  The readability of the text portions is
  150.      enhanced with wider margins and is more easily bound with a wide
  151.      left margin.  The difficulty arises when source code is included. 80
  152.      columns is little enough in which to fit the code and comments,
  153.      allowing nothing for margins. So this time we'll try a 5 space
  154.      margin on the left for the text portion. Further offset should
  155.      be done with your printer.
  156.  
  157.           A couple of quick notes here as I don't know where else to put
  158.      them.
  159.  
  160.           For the assembly programmer the principle difference in writing
  161.      for DOS4+ is that there is a possible disk structure using 32 bit
  162.      FAT entries.  This of course has no effect as long as you use only
  163.      the DOS calls for disk access, but if you are going to do direct
  164.      disk editing this must be checked for.
  165.  
  166.           The occasional   ~  is for the use of my spelling checker.
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.                                       ;Page 2
  189.      
  190.  
  191.  
  192.  
  193.                          Policy and Guide Lines
  194.  
  195.  
  196.           The Assembly Language 'Magazine' is edited by Patrick and David
  197.      O'Riva. We also operate the AsmLang and CFS BBS to distribute the
  198.      'Magazine' and to make available as much information as possible to
  199.      the assembly language programmer. On FidoNet the address is
  200.      1:143/37. Address:
  201.                          2726 Hostetter Rd
  202.                          San Jose, CA 95132
  203.                          408-259-2223
  204.  
  205.           Most Shareware mentioned is available on the AsmLang board if local
  206.      sources cannot be found
  207.  
  208.           Name and address must be included with all articles and files.
  209.      Executable file size and percent of assembly code (when available)
  210.      should be included when a program is mentioned and is required from
  211.      an author or publisher.  Any article of interest to Assembly
  212.      language programmers will be considered for inclusion.  Quality of
  213.      writing will not be a factor, but I reserve the right to try and
  214.      correct spelling errors and minor mistakes in grammar, and to remove
  215.      sections.
  216.  
  217.           Non-exclusive copyright must be given.  No monetary
  218.      compensation will be made.
  219.  
  220.           Outlines of projects that might be undertaken jointly are
  221.      welcome.  For example:  One person who is capable with hardware
  222.      needs support from a user friendly programmer and a math whiz.
  223.  
  224.           Advertisements as such are not acceptable.  Authors and
  225.      publishers wishing to contribute reviews of their own products will
  226.      be considered and included as space and time permit.  These must
  227.      include executable file size, percent of assembly code and time
  228.      comparisons.
  229.  
  230.           Your editor would like information on math libraries, and
  231.      reviews of such.
  232.  
  233.           Articles must be submitted in pclone readable format or sent
  234.      E-mail.
  235.  
  236.  
  237.           Money:  Your editor has none.  Therefore no compensation can be
  238.      made for articles included.  Subscription fees obviously don't
  239.      exist.  Publication costs I expect to be nil (NUL).  Small
  240.      contributions will be accepted to support the BBS where back issues
  241.      are available as well as files and programs mentioned in articles(if
  242.      PD or Shareware ONLY).
  243.  
  244.           Shareware-- Many of the programs mentioned in the "Magazine"
  245.      are Shareware. Most of the readers are prospective authors of
  246.      programs that can be successfully marketed as Shareware.  If you
  247.      make significant use of these programs the author is entitled to his
  248.      registration fee or donation.  Please help Shareware to continue to
  249.  
  250.  
  251.                                    ;Page 3
  252.  
  253.  
  254.  
  255.      be a viable marketing method for all of us by urging everyone to
  256.      register and by helping to distribute quality programs.
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.                                    ;Page 4
  315.  
  316.  
  317.  
  318.                             Beginners' Corner
  319.  
  320.           I finished up the last column by saying I would discuss more
  321.      techniques this time. I have entirely forgotten what they were. So
  322.      without dwelling on that we will just move on the means of getting
  323.      your program ready to run.  The two formats (.com and .exe) are very
  324.      different and so will be discussed separately.
  325.  
  326.      COM Programs
  327.  
  328.           On Entry all of your segment registers are set to the same
  329.      value, that of the start of the PSP. Your stack pointer is set to
  330.      the top of the segment, and your instruction pointer is set to 100h.
  331.      You need to make a generous estimate of the maximum amount of stack
  332.      that your program can use (or count it exactly)  Each level of Call
  333.      uses 2 bytes (for the address of the next instruction). An INT uses
  334.      6 bytes. (2 for the IP, 2 for the CS, and 2 for the Flags). Each push
  335.      of course uses 2. So if your subroutines can go 4 levels deep and
  336.      contain 7 pushes (without intervening pops) and the deepest contains
  337.      an INT21h, then you would need at least 28 bytes of stack.  But
  338.      stack space is cheap, and you might need to change things. So use a
  339.      nice round number of 128 bytes. BIOS also uses YOUR stack in the
  340.      earlier versions of DOS, and the guideline for that is at least 128
  341.      bytes. Result: 256 bytes is safe for a modest program. To implement
  342.      this the following lines of code could be used at the start of the
  343.      program:                                                           ~
  344.                org            100h
  345.                               jmp main
  346.                defstack       db   32 dup('stack   ')
  347.                stacktop       label     byte
  348.  
  349.                ;other data
  350.  
  351.                main:
  352.                               cli
  353.                               mov       sp,offset stacktop
  354.                               sti
  355.                                                                          ~
  356.  
  357.           The db statement is 32 times the string of 8 characters
  358.      totaling 256 bytes. It could just all well be db   256, but it is
  359.      kind of nice when looking at it with a debugger to see the stack
  360.      area and how much has been used all nicely labeled.  The  cli and
  361.      sti aren't really necessary here because it is only one instruction,
  362.      but you are dealing with the stack, and it's well to remember that.
  363.  
  364.           At the end of your program you need a label e.g.
  365.      ~
  366.                progend        label     byte
  367.  
  368.           Then following your stack adjustment above:
  369.  
  370.                               mov       bx,offset progend
  371.                               mov       cl,4
  372.                               shr       bx,cl
  373.                               inc       bx
  374.      ~
  375.  
  376.  
  377.                                    ;Page 5
  378.  
  379.  
  380.  
  381.           These instructions change the offset value into a number of
  382.      paragraphs (16 bytes) and to the end of the last paragraph. This is
  383.      the total number of paragraphs that will be occupied by your
  384.      program. Then it is necessary to inform DOS of this information:
  385.      ~
  386.                               mov       ah,4ah
  387.                               int       21h
  388.      ~
  389.           4a is the DOS function to modify allocated memory. It needs the
  390.      new number of paragraphs in BX (which is where it was put)
  391.  
  392.           At this point, your program is in an orderly condition. Your
  393.      data as well as that in the PSP is available with the DS and ES
  394.      registers, The stack is large enough and well mannered, and all
  395.      surplus memory is available to you or other programs.
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.  
  404.  
  405.  
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438.  
  439.  
  440.                                    ;Page 6
  441.  
  442.  
  443.  
  444.  
  445.  
  446.                         Structure, Speed and Size
  447.                                      as
  448.                         Elements of Programming Style
  449.  
  450.                              By Thomas J. Keller
  451.                                P.O. Box 14069
  452.                             Santa Rosa, CA, 95402
  453.  
  454.  
  455.           Let us examine the reasons for choosing to implement a given
  456.      program in assembly language as opposed to some high level language.
  457.      The reasons most commonly given are execution speed and memory image
  458.      size.
  459.  
  460.           Execution speed, except in certain highly critical realtime
  461.      applications, or certain high resolution graphics applications, is
  462.      probably not a realistic reason to opt for assembly language.  For
  463.      example, a good C compiler with optimization (which precludes use of
  464.      Turbo or Quick C) produces code which only suffers a 10-15% speed
  465.      penalty, over typical hand crafted assembly language code.  It is
  466.      possible to write assembly language code which will run faster than
  467.      this, but few programmers have the requisite skills.
  468.  
  469.           In most applications, a 10-15% speed penalty is simply
  470.      irrelevant.  It is unlikely that the typical user would even notice
  471.      such a difference.   In particular, programs which are highly
  472.      interactive, and thus spend far and away the greatest amount of time
  473.      waiting for user input are highly insensitive to such speed
  474.      penalties.  Many people don't realize that even assuming that the
  475.      user is typing at a rate of 100 wpm (approximately 500
  476.      keystrokes/minute), the CPU is still spending the bulk of its time
  477.      idling, waiting for the next keystroke.
  478.  
  479.           There are, of course, always exceptions to virtually any rule,
  480.      and there are most certainly exceptions to this rule.  Word
  481.      processors, for example, while actually accepting text input, are
  482.      not speed critical.  When performing global search and replace, or
  483.      spell checking, for example, even a 10% penalty can become expensive
  484.      on large documents.  So there is a tradeoff to be made.
  485.  
  486.           Assembly language programs cost considerably more than 10-15%
  487.      more to develop than high level programs.  The minutiae involved in
  488.      managing a massive assembly language programming effort are
  489.      overwhelming.  Assembly language programs take MUCH longer to
  490.      complete, in almost all cases, than high level programs do, a major
  491.      contributory factor in the overall cost of development.  Finally,
  492.      projects developed in high level programming languages are much more
  493.      likely to be easily ported to platforms based on processors other
  494.      than the platform on which the project is developed, and very
  495.      important consideration for a major project.  The ability to port a
  496.      project easily to other platforms increases the market for a
  497.      product, thereby not only increasing the profitability of the
  498.      product, but also helping to reduce the sale price of the product
  499.      (larger market generally translates to lower per unit cost).
  500.  
  501.  
  502.  
  503.                                    ;Page 7
  504.  
  505.  
  506.  
  507.      So the vendor or developer must analyze the relative impact of a
  508.      small improvement in execution speed vs a large increase in
  509.      development time and cost, which consequently translates to higher
  510.      selling prices, thereby reducing the anticipated market for their
  511.      product.  In many cases, the tradeoffs do not merit choosing
  512.      assembly language.
  513.  
  514.      Let us turn now to binary image size (memory size).  The
  515.      advantages of small programs are clear, when examining programs
  516.      which are, in the DOS world, TSRs (The MAC and AMIGA worlds have
  517.      similar cases, though I am not sufficiently familiar with them to
  518.      know what they are called).  These programs are loaded into memory,
  519.      and remain there until explicitly removed, which means that the
  520.      memory they use is NOT available for other uses.  Device drivers
  521.      similarly use memory, precluding its use for other programs, and
  522.      therefore also clearly benefit from small size.   In the
  523.      multi-tasking world (DeskView or PC/MOS, in the PC clone market),
  524.      small executables also have an advantage, permitting more programs
  525.      to be run "simultaneously" in a given memory configuration, though
  526.      running multi-taskers in severely restricted memory configurations
  527.      probably qualifies as a technical error.
  528.  
  529.      What of normal, single tasking, single user environments (such as
  530.      DOS, the MAC and AMIGA environments)?  Besides the ego boost of
  531.      creating a very small, very tight utility or application, what benefit
  532.      is there in generating very small programs?
  533.  
  534.      They take less disk space to store, but realistically, at least
  535.      under DOS, lots of very small utilities may actually not achieve a
  536.      significant savings in disk space, due to granularity of storage
  537.      allocation.  They load a little faster, in most cases.
  538.  
  539.      But once again, the economics of the issue comes back to haunt us.
  540.      It is not clear that the effort and expense of writing most
  541.      applications in assembly language due to size considerations is an
  542.      economically rational decision.  The same economic pressures and
  543.      considerations apply as do to the execution speed issue discussed
  544.      above.
  545.  
  546.      On to structure.  I must take issue with Patrick O'Riva regarding
  547.      their purpose and nature of "structured programming."  While much of
  548.      his definition is true, it is incomplete, and appears to reflect a
  549.      misunderstanding of certain aspects of the structured approach to
  550.      programming.
  551.  
  552.      Firstly, it is entirely possible (and not altogether a rare
  553.      occurrence) to write thoroughly unstructured code in PASCAL or C.  One
  554.      must take care to recognize the difference between references to a
  555.      "block structured" language, as PASCAL and C both are, and "structured
  556.      programming," which is really a totally separate issue.
  557.  
  558.      Structured programming is an approach to programming that is
  559.      thoroughly applicable to whatever language a project is being
  560.      implemented in.  It implies firstly a step-wise refinement approach
  561.      to defining the solution to a problem which the program is to address
  562.      (in other words, determining the nature of the desired goal, and an
  563.      at least rational approach to reaching said goal).  Secondly, it
  564.  
  565.  
  566.                                    ;Page 8
  567.  
  568.  
  569.  
  570.      involves determining, to the extent possible, the nature and structure
  571.      of the data that is to be processed by the program.  Finally, it
  572.      involves a top-down approach to the actual coding process.
  573.  
  574.      Just what is a top-down approach?  Essentially, this means that we
  575.      code the high level functionality of the program first, programming
  576.      simple "do nothing" stubs for the lower levels of the program.  As
  577.      necessary to test the high level code, we implement lower level
  578.      functions, again, if needed, programming still lower level stubs.
  579.      Assuming that the structured design approach of step-wise refinement
  580.      was used to begin with, the actual coding should really amount to
  581.      translating the logic flow diagrams, or pseudo-code, or whatever means
  582.      of recording the refinement process was used, into actual program
  583.      code.  In the ideal situation, the program almost literally codes
  584.      itself at this point.
  585.  
  586.      There is a myth that "structured programming" means "goto-less"
  587.      programming.  In fact, this is not the case.  This myth came into
  588.      being through misunderstanding of the rather harsh criticism of the
  589.      "go to" which occurred in the computer science journals beginning in
  590.      approximate the mid to late sixties.  This criticism was based
  591.      primarily upon the typically excessive use of the "go to" in FORTRAN
  592.      and BASIC programming at the time.  Such indiscriminate use of "goto"
  593.      led to what has been called "spaghetti" code, code which is virtually
  594.      impossible to trace or analyze.
  595.  
  596.      In fact, there are many cases in programming where the goto is most
  597.      structured solution available.  Structured coding techniques are
  598.      intended to clarify and make easier the process of analysis, design
  599.      and implementation of computer programs, not to define rigid, strictly
  600.      enforced rules in the face of all reason.
  601.  
  602.      Structured programming is ALWAYS the best approach to ANY computer
  603.      program.  If the internal requirements of the program, as regards
  604.      speed or memory utilization, dictate the use of goto's, then use them.
  605.      A properly documented GOTO can be far more "structured" than an
  606.      undocumented string of modular function calls.
  607.  
  608.      So, back to assembly language programming.  When is it appropriate
  609.      to choose assembly language to implement a program?  First, and most
  610.      obviously, when the speed or memory utilization requirements of the
  611.      application demand the capabilities that well crafted assembly
  612.      language offers.  Second, perhaps not so obviously, when it is
  613.      necessary to work at the hardware level a great deal.  High level
  614.      languages, even C, do not generally manipulate hardware registers
  615.      efficiently.  So, if your program makes frequent or widespread use of
  616.      direct hardware manipulation, it is a likely candidate for assembly
  617.      language.
  618.  
  619.      Finally, and probably the most gratifying reason of all to choose
  620.      assembly language, is when you want the satisfaction of having tackled
  621.      a project in assembly and pushed the bits around to suit your purpose.
  622.      There is little I can imagine that is more satisfying than to reach
  623.      down into the microprocessor chip and twiddle those bits.  Just be
  624.      sure that you don't let your ego cloud your judgment, when the
  625.      economics of the project are important (e.g., when a project is to be
  626.      distributed commercially, or there is an urgent need for speedy
  627.  
  628.  
  629.                                    ;Page 9
  630.  
  631.  
  632.  
  633.      completion).
  634.  
  635.      I believe that all PROGRAMMERS (as opposed to casual computer
  636.      users) should learn the assembly language for the machines on which
  637.      they work.  Besides offering the flexibility of shifting to assembly
  638.      to meet a specific goal, learning assembly intimately familiarizes the
  639.      programmer with the hardware on which s/he is working.  The more you
  640.      know about your hardware environment, the better off you are.
  641.  
  642.  
  643.  
  644.  
  645.  
  646.  
  647.  
  648.  
  649.  
  650.  
  651.  
  652.  
  653.  
  654.  
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.  
  668.  
  669.  
  670.  
  671.  
  672.  
  673.  
  674.  
  675.  
  676.  
  677.  
  678.  
  679.  
  680.  
  681.  
  682.  
  683.  
  684.  
  685.  
  686.  
  687.  
  688.  
  689.  
  690.  
  691.  
  692.                                    ;Page 10
  693.  
  694.  
  695.  
  696.                              Editorial Rebuttal
  697.  
  698.           I thank Mr. Keller very much for his article and agree with
  699.      many of the points he has made.  However I must still argue the
  700.      points of size and speed and justification.
  701.  
  702.           Whenever a program is user limited and will not be used in a
  703.      multi-tasking environment as is often the case with a word processor
  704.      and certain drawing programs, there may be little to be gained in
  705.      assembly programming. Also there are programs which are DOS limited
  706.      and little speed increase is possible.
  707.  
  708.           Mr. Keller uses a figure of 10 to 15 percent speed penalty. My
  709.      experience indicates a value closer to 300 to 400 percent though
  710.      direct comparisons are difficult to make because the same programs
  711.      are usually not written in both assembly and in C. The size
  712.      difference seems to be a factor of 5 to 10.  The two prime examples
  713.      I can offer are both by Microsoft, and it can be assumed they make
  714.      use of an optimizing compiler. Their assembler is approximately 110k
  715.      in size. A86 while not compatible in syntax has comparable features.
  716.      It's size is 22k and assembles code in about one eighth the time.
  717.  
  718.           Microsoft's programmers' editor is vaguely 250k.  Qedit is
  719.      about 50k and is a mix of high level and assembly. You can grow gray
  720.      hairs waiting for the MS editor to do a search and replace, but if
  721.      you blink you'll miss it with Qedit. A fully capable full screen
  722.      editor without the extras that make it a pleasure to use can easily
  723.      be written in less that 5k. Give another 5k for features. What has
  724.      MS gained with the extra 240k of code?
  725.  
  726.           David has recently completed (though they are still adding
  727.      modules) a database and accounting program for a multi-office
  728.      company. A much abbreviated version was threatening to overflow their
  729.      384k limit. Investigation of a Dbase implementation indicated in
  730.      excess of 500k. Data base sorts used to take 10 hours. They now take
  731.      20 minutes. Savings in processing time and entry time plus increased
  732.      functionality suggest a savings of $5000 to $10,000 per month PER
  733.      OFFICE. Code size? 35k. Are they unhappy about the $15,000 they've
  734.      been charged for a program that will get lost in a single floppy
  735.      disk?
  736.  
  737.           Given the above examples, I must maintain that the use of high
  738.      level language, when there is significant processing to be done, and
  739.      when it will be used on a regular and continuing basis, benefits
  740.      only the software corporation, and is detrimental to the end user.
  741.  
  742.           On Structured Programming I fully agree with Mr. Keller and
  743.      hope that he clarified any misconceptions I left you with. I prefer
  744.      a bottom up construction, but that is only preference and has no
  745.      effect on the end product.
  746.  
  747.           Dave's notes:  Mr. Keller mentions that it is possible to get
  748.      great size/speed reductions, but that few programmers have the requisite
  749.      skills.  But to a large extent, it isn't the skill that makes the
  750.      program, it's the toolbox.  The C language is extremely close to
  751.      assembly - MSC does a very good job of optimizing - and it takes care of
  752.      the minutiae for you.  The problem with this is that the libraries
  753.  
  754.  
  755.                                    ;Page 11
  756.  
  757.  
  758.  
  759.      supplied with the compilers were written to handle very general cases.
  760.      The printf() function is an extreme example, but it typifies the
  761.      problem:  If you use printf once in your program to print "Hello", it
  762.      adds 30K of code!
  763.  
  764.           Another concern is that many high-level-language programmers
  765.      don't even realize that with a tweak here, using putc instead
  766.      of printf there, they can get much(!) better performance from their
  767.      programs.  Familiarity with the quirks of the compiler being used is a
  768.      necessity... And even that isn't enough to get good performance out of a
  769.      large program.  AND, it decreases portability.  So you're right back
  770.      into the twiddling usually associated only with assembly.
  771.  
  772.           I've found that if I use C for anything except flow control and
  773.      one-shot tools, my programs start to get huge and slow, relative to
  774.      anything that I've banged out in assembly.  The database is a great
  775.      example - it's a very complicated application, with a completely
  776.      separated data engine & OS interface.  If it had been written in C, it
  777.      would be working in multiple code segments on a 286 with 4 megs and
  778.      STILL take hours to run a balance, instead of 35K of code on an XT
  779.      network terminal with half-hour runs.
  780.  
  781.           The database was indeed a massive effort, but at this point it
  782.      would be possible to strip out the engine and write with ease (and
  783.      macros - lots of macros) anything that could be done in C or Dbase, and
  784.      do it much better.  And average runtime is cut at least in half, size by
  785.      50-90%.  With a reasonably solid and application-specific toolbox, the
  786.      advantages TO THE CUSTOMER of assembly programming completely eclipse
  787.      those of any other language and the disadvantages of assembly itself.
  788.  
  789.           Portability is another issue entirely.  If you NEED
  790.      portability and fast development, and IF run time and general
  791.      productivity are not a concern, then C probably makes more sense.
  792.      There's this nagging feeling, though, that if the UNIX OS core had been
  793.      written in assembly by a reasonably good programmer, and been ported to
  794.      new systems in kind, that the university systems would be clipping
  795.      instead of slogging.
  796.  
  797.           As far as structured programming goes, I usually design as I go
  798.      along, and end up with a functional (even rational) structure.  Call it
  799.      "random-access programming."  This is probably because I find it
  800.      difficult to call a routine until I've laid out the calling conventions
  801.      for it, and while I'm doing that I'll remember another routine that
  802.      should be written for another module...  This is not the generally
  803.      recommended method, I gather.
  804.  
  805.  
  806.  
  807.  
  808.  
  809.  
  810.  
  811.  
  812.  
  813.  
  814.  
  815.  
  816.  
  817.  
  818.                                    ;Page 12
  819.  
  820.  
  821.  
  822.                Accessing the Command Line Arguments
  823.                         in Assembly Language Programs
  824.  
  825.                             By Thomas J. Keller
  826.                                P.O. Box 14069
  827.                             Santa Rosa, CA, 95402
  828.  
  829.  
  830.           If you're like me, you program in several languages, under
  831.      several different operating systems.  Under DOS, one very useful
  832.      feature is the capability to pass arguments to a program as part of
  833.      the invocation command line.  The use of command line arguments
  834.      significantly increases the power and flexibility of your programs,
  835.      as well as improving the "professional look."  Many languages
  836.      support this capability with intrinsic or library routines which
  837.      facilitate access to these command line arguments.  Assembly
  838.      language, of course, does not.  What is a programmer to do?
  839.  
  840.           As it turns out, it is quite simple to access the command line
  841.      arguments under DOS.  DOS places the so-called "command tail" (the
  842.      command line less the actual program name) into a buffer area
  843.      reserved in the PSP (Program Segment Prefix).  This buffer area is
  844.      known as the DTA (Disk Transfer Area).
  845.  
  846.           It is extremely important that you parse the command tail, if you
  847.      plan to do so at all, immediately upon entering your program.  DOS
  848.      does some particularly obscure and insidious things with this DTA
  849.      buffer, which will destroy the command tail information.
  850.  
  851.           In a .COM format program, the PSP is the first 100h (256) bytes
  852.      of the program memory image, making access quite straightforward.
  853.      How do we locate the PSP in a .EXE format program, however?
  854.  
  855.           Fortunately, DOS sets the ES segment register to point to the
  856.      beginning of the PSP under both .COM and .EXE programs.  It happens
  857.      to be the case that DOS also sets all other segment registers to the
  858.      same location for a .COM program, simply because .COM programs
  859.      reside in one and only one segment.  In an .EXE invocation, the DS
  860.      and ES registers are set to point to the segment in which the PSP
  861.      resides as the first 100h bytes.  This is the default data segment
  862.      as well.
  863.  
  864.           The DTA begins at offset 80h (128d) from the beginning of the
  865.      PSP.  When it contains a command tail, the byte at 80h contains the
  866.      count of the number of bytes actually in the command tail, and the
  867.      command tail string begins at offset 81h (129d) from the beginning
  868.      of the PSP.  The first byte of this string is always a blank (20h),
  869.      and the string is terminated with a <cr> (0dh).
  870.  
  871.           The exact means you use to parse the command line arguments is,
  872.      of course, up to you.  One possible approach is as follows:
  873.           1) Use the data definition directives to set aside any memory you
  874.           will need to store information about command line arguments
  875.           (e.g., buffers for file names, byte or word values for flags and
  876.           numeric arguments, etc.).
  877.  
  878.           2) Design a routine that starts scanning the command tail string for
  879.  
  880.  
  881.                                      ;Page 13
  882.      
  883.  
  884.  
  885.           arguments.  a 'first fit' (the shortest match possible) scheme is
  886.           easiest to program.  As each item is located and identified as to
  887.           type and purpose, store the appropriate information in the data
  888.           areas you have already set aside.
  889.           3) Have a "usage" message defined, and a small routine to print it
  890.           to the screen (a good idea is to print it to STDERR).  Invoke
  891.           this routine when the first argument on the command line is a
  892.           '?,' or, if the program requires arguments, when it is invoked
  893.           without them.
  894.           4) You now have the switches, filenames, and other command line
  895.           arguments available.  Write your program to use them
  896.           appropriately.
  897.  
  898.           Included in this issue of Assembly Language Magazine is an source
  899.      listing which is a sample template GPFILT.ASM for a general purpose
  900.      assembly language filter.  This program provides an excellent sample
  901.      of command line argument parsing and one way of using these
  902.      arguments (though the method used here is not the same as the one
  903.      described above).
  904.  
  905.  
  906.  
  907.  
  908.  
  909.  
  910.  
  911.  
  912.  
  913.  
  914.  
  915.  
  916.  
  917.  
  918.  
  919.  
  920.  
  921.  
  922.  
  923.  
  924.  
  925.  
  926.  
  927.  
  928.  
  929.  
  930.  
  931.  
  932.  
  933.  
  934.  
  935.  
  936.  
  937.  
  938.  
  939.  
  940.  
  941.  
  942.  
  943.  
  944.                                      ;Page 14
  945.      
  946.  
  947.  
  948.                          Original Vector Locator
  949.                               by Rick Engle
  950.  
  951.                                November, 1989
  952.  
  953.      INTTEST is a small assembly program which attempts to find the
  954.      original address of the INT 21h function handler.  This is valuable
  955.      if you need to be able to make calls to the original INT 21h
  956.      function even if a TSR or other program has that interrupt hooked or
  957.      trapped.  This gives your program secure control over the interrupt
  958.      regardless of who is using it.
  959.  
  960.           I did this prototype in an attempt to make certain programs
  961.      somewhat immune to the effects of destructive viruses that may
  962.      intercept INT 21h and use it for their own use.  This technique
  963.      could be used to find the original address of other MS-DOS
  964.      interrupts.  I wrote test programs to dump out the address of MS-DOS
  965.      interrupts (such as INT 21h) and then disassembled portions of
  966.      MS-DOS at those addresses to identify a stable signature of the
  967.      interrupt.  Then by following the chain to MS-DOS through the PSP
  968.      (Program Segment Prefix) at offset 5h, I was able to find the
  969.      segment:offset of the address of the handler for old CP/M calls.
  970.  
  971.           This pointed to the correct segment in memory of MS-DOS and
  972.      from there, after moving my offset backwards about 100h in memory, I
  973.      scanned for my interrupt signature.  Once I got a hit, I calculated
  974.      the address of the interrupt and then could make calls to INT 21h at
  975.      the segment:offset found.  This program is a "brute-force" method of
  976.      finding the original address.  If anyone finds or has a better way,
  977.      I'd be very interested in hearing about it.
  978.  
  979.           NOTE:     I have tested this program successfully on MS-DOS
  980.           2.11, 3.20, and 3.30.
  981.      ~
  982. ; -----------------------------------------------------------------------
  983. ;           INTTEST.ASM                       November, 1989 Rick Engle
  984. ;
  985. ;           Finds the address of the INT 21h function dispatcher to
  986. ;          allow the user to make INT 21h calls to the original
  987. ;          interrupt regardless of who or what has INT 21h hooked.
  988. ;
  989. ; -----------------------------------------------------------------------
  990. ;
  991. print           macro   print_parm
  992.                 push    ax
  993.                 push    dx
  994.                 mov     ah,9
  995.                 mov     dx,offset print_parm
  996.                 int     21h
  997.                 pop     dx
  998.                 pop     ax
  999.                 endm
  1000.  
  1001. ; -----------------------------------------------------------------------
  1002. ; - Start of program                                                    -
  1003. ; -----------------------------------------------------------------------
  1004.  
  1005.  
  1006.  
  1007.                                    ;Page 15
  1008.  
  1009.  
  1010.  
  1011. cseg            segment para public 'code'
  1012.                 assume  cs:cseg,ds:cseg
  1013.  
  1014.                 org     100h
  1015.  
  1016. int_test        proc    far
  1017.  
  1018.                 print   reboot_first
  1019.  
  1020.                 print   int_address
  1021.                 mov     cl,21h
  1022.                 mov     ah,35h          ; get interupt vector
  1023.                 mov     al,cl           ; for interupt in cl
  1024.                 int     21h             ; do it
  1025.  
  1026.                 mov     ax,es                   ; lets display the es
  1027.                 push    cs                      ; set es = cs so that
  1028.                 pop     es                      ; the stosb works
  1029.                 mov     di,offset out_byte
  1030.                 call    conv_word
  1031.                 print   out_byte
  1032.                 print   colon
  1033.  
  1034.                 mov     ax,bx                   ; lets display the bx
  1035.                 mov     di,offset out_byte
  1036.                 call    conv_word
  1037.                 print   out_byte
  1038.                 print   crlf
  1039.  
  1040.                 print   display_header2
  1041.  
  1042.                 mov     ah,byte ptr cs:[05h]    ; Get info from the PSP
  1043.                 mov     al,byte ptr cs:[06h]    ;
  1044.                 push    cs                      ; set es = cs so that
  1045.                 pop     es                      ; the stosb works
  1046.                 mov     di,offset out_byte
  1047.                 call    conv_word
  1048.                 print   out_byte
  1049.                 print   dash
  1050.  
  1051.                 mov     ah,byte ptr cs:[07h]    ;
  1052.                 mov     al,byte ptr cs:[08h]    ;
  1053.                 mov     di,offset out_byte
  1054.                 call    conv_word
  1055.                 print   out_byte
  1056.                 print   dash
  1057.                 mov     ah,byte ptr cs:[09h]    ;
  1058.                 mov     al,byte ptr cs:[0ah]    ;
  1059.                 mov     di,offset out_byte
  1060.                 call    conv_word
  1061.                 print   out_byte
  1062.                 print   crlf
  1063.  
  1064.                 print   display_header
  1065.  
  1066.                 mov     ah,byte ptr cs:[50h]    ; Addess if INT 21 op code
  1067.                 mov     al,byte ptr cs:[51h]    ; in the PSP
  1068.  
  1069.  
  1070.                                    ;Page 16
  1071.  
  1072.  
  1073.  
  1074.                 push    cs                      ; set es = cs so that
  1075.                 pop     es                      ; the stosb works
  1076.                 mov     di,offset out_byte
  1077.                 call    conv_word
  1078.                 print   out_byte
  1079.                 print   dash
  1080.  
  1081.                 mov     ah,byte ptr cs:[52h]    ;
  1082.                 mov     al,byte ptr cs:[53h]    ;
  1083.                 mov     di,offset out_byte
  1084.                 call    conv_word
  1085.                 print   out_byte
  1086.                 print   dash
  1087.                 mov     ah,byte ptr cs:[54h]    ;
  1088.                 mov     al,byte ptr cs:[55h]    ;
  1089.                 mov     di,offset out_byte
  1090.                 call    conv_word
  1091.                 print   out_byte
  1092.                 print   crlf
  1093.  
  1094.                 print   far_address
  1095.                 mov     ax,word ptr cs:[08h]    ;
  1096.                 mov     segm,ax
  1097.                 push    cs                      ; set es = cs
  1098.                 pop     es                      ;
  1099.                 mov     di,offset out_byte
  1100.                 call    conv_word
  1101.                 print   out_byte
  1102.                 print   colon
  1103.  
  1104.                 mov     ax,word ptr cs:[06h]    ;
  1105.                 mov     off,ax
  1106.                 push    cs                      ; set es = cs so that
  1107.                 pop     es                      ; the stosb works
  1108.                 mov     di,offset out_byte
  1109.                 call    conv_word
  1110.                 print   out_byte
  1111.                 print   crlf
  1112.  
  1113.                 mov     ax,segm
  1114.                 mov     es,ax
  1115.                 mov     di,off
  1116.                 inc     di
  1117.  
  1118.                 print   function_jmp
  1119.                 mov     ax,word ptr es:[di+2]   ;
  1120.                 mov     segm2,ax
  1121.                 push    cs                      ; set es = cs so that
  1122.                 pop     es                      ; the stosb works
  1123.                 mov     di,offset out_byte
  1124.                 call    conv_word
  1125.                 print   out_byte
  1126.                 print   colon
  1127.  
  1128.                 mov     ax,segm
  1129.                 mov     es,ax
  1130.                 mov     di,off
  1131.  
  1132.  
  1133.                                    ;Page 17
  1134.  
  1135.  
  1136.  
  1137.                 inc     di
  1138.                 mov     ax,word ptr es:[di]     ;
  1139.                 mov     off,ax                  ; save found offset of int 21h
  1140.                 push    cs                      ; set es = cs so that
  1141.                 pop     es                      ; the stosb works
  1142.                 mov     di,offset out_byte
  1143.                 call    conv_word
  1144.                 print   out_byte
  1145.                 print   crlf
  1146.  
  1147. ;-----------------------------------------------------------------
  1148. ;si = string di = string size es:bx = pointer to buffer to search
  1149. ;ax = number of bytes in buffer to search. Zero flag set if found
  1150. ;-----------------------------------------------------------------
  1151.  
  1152.                 mov     ax,segm2
  1153.                 mov     es,ax                   ;segment
  1154.                 mov     bx,off                  ;offset
  1155.                 sub     bx,0100h                ;backup a bit to catch DOS
  1156.                 mov     si,offset dos_sig       ;start at modified byte
  1157.                 mov     di,dos_sig_len          ;enough of a match
  1158.                 mov     ax,0300h                ;# of bytes to search
  1159.                 call    search                  ;use our search
  1160.                 jnz     sig_not_found           ;didn't find int 21h signature
  1161.                 mov     START_SEGMENT,es        ;set page
  1162.                 mov     START_OFFSET,ax         ;address of found string
  1163.  
  1164.                 print   good_address
  1165.                 mov     ax,START_SEGMENT        ;
  1166.                 push    cs                      ; set es = cs so that
  1167.                 pop     es                      ; the stosb works
  1168.                 mov     di,offset out_byte
  1169.                 call    conv_word
  1170.                 print   out_byte
  1171.                 print   colon
  1172.  
  1173.                 mov     ax,START_OFFSET         ;
  1174.                 mov     off,ax                  ; save found offset of int 21h
  1175.                 push    cs                      ; set es = cs so that
  1176.                 pop     es                      ; the stosb works
  1177.                 mov     di,offset out_byte
  1178.                 call    conv_word
  1179.                 print   out_byte
  1180.                 print   crlf
  1181.  
  1182.                 push    cs                      ; set es = cs
  1183.                 pop     es
  1184.                 mov     bx,START_OFFSET
  1185.                 mov     ax,START_SEGMENT
  1186.                 mov     word ptr [OLDINT21],  bx
  1187.                 mov     word ptr [OLDINT21+2],ax
  1188.  
  1189.                 mov     dx,offset test_message
  1190.                 mov     ah,9
  1191.                 call    dos_function
  1192.                 jmp     terminate
  1193. sig_not_found:
  1194.  
  1195.  
  1196.                                    ;Page 18
  1197.  
  1198.  
  1199.  
  1200.                 print   no_int21_found
  1201. terminate:      mov     ax,4c00h        ; terminate process
  1202.                 int     21h             ; and return to DOS
  1203.  
  1204. out_byte        db      'XXXX'
  1205.                 db      '$'
  1206. colon           db      ':$'
  1207. dash            db      '-$'
  1208. crlf            db      10,13,'$'
  1209. reboot_first    db      13,10,'INTTEST 1.0',13,10
  1210.                 db      'Reboot before running this, or',13,10
  1211.                 db      'make sure INT 21h is not hooked',13,10,13,10,'$'
  1212. display_header  db      'HEX data at PSP address 50h is : $'
  1213. display_header2 db      'HEX data at PSP address 05h is : $'
  1214. int_address     db      'Original INT 21h address is    : $'
  1215. function_jmp    db      'Jump address at DOS dispatcher : $'
  1216. far_address     db      'Far address of DOS dispatcher  : $'
  1217. good_address    db      'Good INT 21h address found at  : $'
  1218. test_message    db      13,10,10,'This message is being printed using the INT '
  1219.                 db      '21h Interrupt',13,10
  1220.                 db      'Found by Brute Force!!!!',13,10,10,'$'
  1221. no_int21_found  db      13,10,'Int 21h address not found!$'
  1222. segm            dw      0
  1223. segm2           dw      0
  1224. off             dw      0
  1225. START_OFFSET    dw      0                       ;top addr shown on screen
  1226. START_SEGMENT   dw      0
  1227. ;dos_sig         db      08Ah, 0E1h, 0EBh        ;  mov  ah,cl
  1228. ;                                                ;  jmp  short  label
  1229. dos_sig         db      080h, 0FCh, 0F8h        ;  cmp  ah,0F8h
  1230. dos_sig_len     equ $ - dos_sig
  1231. OLDINT21        dd      ?                    ; Old DOS function interrupt vector
  1232.  
  1233. int_test          endp
  1234.  
  1235. ; -----------------------------------------------------------------------
  1236. ; -                                                                     -
  1237. ; - Subroutine to convert a word or byte to hex ASCII                   -
  1238. ; -                                                                     -
  1239. ; -   call with AX = binary value                                       -
  1240. ; -             DI = address to store string                            -
  1241. ; -                                                                     -
  1242. ; -----------------------------------------------------------------------
  1243.  
  1244. conv_word       proc    near
  1245.  
  1246.                 push    ax
  1247.                 mov     al,ah
  1248.                 call    conv_byte       ; convert upper byte
  1249.                 pop     ax
  1250.                 call    conv_byte       ; convert lower byte
  1251.                 ret                     ; and return
  1252. conv_word       endp
  1253.  
  1254. conv_byte       proc    near
  1255.  
  1256.                 push    cx              ; save cx
  1257.  
  1258.  
  1259.                                    ;Page 19
  1260.  
  1261.  
  1262.  
  1263.                 sub     ah,ah           ; clear upper byte
  1264.                 mov     cl,16
  1265.                 div     cl              ; divide binary data by 16
  1266.                 call    conv_ascii      ; the quotient becomes the
  1267.                 stosb                   ; ASCII character
  1268.                 mov     al,ah
  1269.                 call    conv_ascii      ; the remainder becomes the
  1270.                 stosb                   ; second ASCII character
  1271.                 pop     cx              ; restore cx
  1272.                 ret
  1273. conv_byte       endp
  1274.  
  1275. conv_ascii      proc    near            ; convert value 0-0Fh in al
  1276.                 add     al,'0'          ; into a "hex ascii" character
  1277.                 cmp     al,'9'
  1278.                 jle     conv_ascii_2    ; jump if in range 0-9
  1279.                 add     al,'A'-'9'-1    ; offset it to range A-F
  1280. conv_ascii_2:   ret                     ; return ASCII character in al
  1281. conv_ascii      endp
  1282.  
  1283. ;-----------------------------------------------------------------------
  1284. ; This routine does a dos function by calling the old interrupt vector
  1285. ;-----------------------------------------------------------------------
  1286.                 assume  ds:nothing, es:nothing
  1287. dos_function    proc
  1288.  
  1289. ;               mov     cl,ah           ;move our function # into cl
  1290.                 pushf                   ;These instructions simulate
  1291.                                         ;an interrupt
  1292.                 cli                     ;turn off interrupts
  1293.                 call    CS:OLDINT21     ;Do the DOS function
  1294.                 sti                     ;enable interrupts
  1295.  
  1296.                 push    cs
  1297.                 pop     ds
  1298.                 push    cs
  1299.                 pop     es
  1300.                 ret
  1301.  
  1302. dos_function    endp
  1303.  
  1304. ;-----------------------------------------------------------------
  1305. ;si = string di = string size es:bx = pointer to buffer to search
  1306. ;ax = number of bytes in buffer to search. Zero flag set if found
  1307. ;-----------------------------------------------------------------
  1308. SEARCH          PROC    NEAR            ;si points at string
  1309.                 PUSH    BX
  1310.                 PUSH    DI
  1311.                 PUSH    SI
  1312.                 XCHG    BX,DI           ;string size, ptr to data area
  1313.                 MOV     CX,AX           ;# chars in segment to search
  1314. BYTE_ADD:
  1315.                 LODSB                   ;char for first part of search
  1316. NEXT_SRCH:
  1317.                 REPNZ   SCASB           ;is first char in string in buffer
  1318.                 JNZ     NOT_FOUND       ;if not, no match
  1319.                 PUSH    DI              ;save against cmpsb
  1320.  
  1321.  
  1322.                                    ;Page 20
  1323.  
  1324.  
  1325.  
  1326.                 PUSH    SI
  1327.                 PUSH    CX
  1328.                 LEA     CX,[BX-1]       ;# chars in string - 1
  1329.                 JCXZ    ONE_CHAR        ;if one char search, we have found it
  1330.                 REP     CMPSB           ;otherwise compare rest of string
  1331. ONE_CHAR:
  1332.                 POP     CX              ;restore for next cmpsb
  1333.                 POP     SI
  1334.                 POP     DI
  1335.                 JNZ     NEXT_SRCH       ;if zr = 0 then string not found
  1336. NOT_FOUND:
  1337.                  LEA    AX,[DI-1]       ;ptr to last first character found
  1338.                  POP    SI
  1339.                  POP    DI
  1340.                  POP    BX
  1341.                  RET                    ;that's all
  1342. SEARCH ENDP
  1343.  
  1344. cseg            ends
  1345.                 end     int_test
  1346.  
  1347.  
  1348.  
  1349.  
  1350.  
  1351.  
  1352.  
  1353.  
  1354.  
  1355.  
  1356.  
  1357.  
  1358.  
  1359.  
  1360.  
  1361.  
  1362.  
  1363.  
  1364.  
  1365.  
  1366.  
  1367.  
  1368.  
  1369.  
  1370.  
  1371.  
  1372.  
  1373.  
  1374.  
  1375.  
  1376.  
  1377.  
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384.  
  1385.                                    ;Page 21
  1386.  
  1387.  
  1388.  
  1389. ~
  1390.  
  1391.                       How to call DOS from within a TSR
  1392.  
  1393.  
  1394.                                by David O'Riva
  1395.  
  1396.  
  1397.           Just a few ramblings on interactions between TSRs & DOS.
  1398.  
  1399.  
  1400.              Cardinal rule:  DON'T CALL DOS UNLESS YOU'RE SURE OF THE
  1401.                              MACHINE STATE!!!
  1402.  
  1403.           There are a few interrupt calls and memory locations you can
  1404.      play with to get this information.  A list & explanation of sorts is
  1405.      below.  The reason you don't call DOS if you've interrupted the
  1406.      machine in the middle of DOS is that:
  1407.  
  1408.              1.  The stack is unstable as far as DOS is concerned, and
  1409.                  you'll probably end up overwriting DOS data or going
  1410.                  into the weeds.
  1411.  
  1412.              2.  DOS only keeps one copy of certain crucial information
  1413.                  as it processes a disk-related request.  i.e.  BPB's,
  1414.                  current sectors, FAT memory images, fun stuff like that.
  1415.                  If you interrupt it in the middle, ask for something
  1416.                  different, then go back, you will probably destroy your
  1417.                  disk, possibly beyond recall.
  1418.  
  1419.              3.  DOS simply was not designed to be re-entrant.  The first
  1420.                  9 or 10 function calls are cool most of the time, the
  1421.                  rest are strictly single-processing-stream functions.
  1422.  
  1423.           However, there is hope.  And, (extra bonus) it happens to be
  1424.      compatible with most true MS-DOS releases, and many, many brand-name
  1425.      DOSes.  As well as most clones.
  1426.  
  1427.           What you need to do is after determining that the user wants to
  1428.      pop your program up, you set a few flags.  One of them prevents your
  1429.      program from being popped up AGAIN while the current DOS call is
  1430.      completing, and the other tells a timer trap routine to start
  1431.      looking for DOS to finish it's current process (usually a matter of
  1432.      split seconds).  When the timer routine detects that DOS is no
  1433.      longer active, it grabs control of the system and runs your TSR.
  1434.  
  1435.           At this point, all DOS calls are as safe as they are for a
  1436.      normal application.
  1437.  
  1438.           What follows is an outline of the code necessary to activate a
  1439.      TSR that uses DOS calls.  Depending on the TSR, other things may
  1440.      need to be done in these routines as well.  Definitely make sure you
  1441.      understand the interactions of the various routines before TSRing
  1442.      your background disk formatter.
  1443.  
  1444.              Okay, nitty-gritty time...
  1445.  
  1446.  
  1447.  
  1448.                                    ;Page 22
  1449.  
  1450.  
  1451.  
  1452.              You need 5 main chunks of code to do this right:
  1453.  
  1454.                      a) a bit of extra initialization code
  1455.  
  1456.                      b) your TSR's main program
  1457.  
  1458.                      c) activation request server (usually a keypress
  1459.                      trap)
  1460.  
  1461.                      d) timer tick inDOS monitor
  1462.  
  1463.                      e) DOS busy loop monitor
  1464.  
  1465.              And here's what they do:
  1466.  
  1467.              a) asks DOS for the location of the inDOS flag, and stores
  1468.                 that away.
  1469.  
  1470.              b) does whatever you want it to.
  1471.  
  1472.              c) when the activation requirement is sensed (the user
  1473.                 pressed the hot-key, the print buffer is empty, the modem
  1474.                 is sending another packet, whatever) the following steps
  1475.                 need to be taken:  1. have we already tried to activate,
  1476.                      and are waiting for DOS to finish?  if so, then
  1477.                         ignore the activation request.
  1478.  
  1479.                      2. check the inDOS flag.  if we're not in DOS, then
  1480.                         activate as usual.
  1481.  
  1482.                      3. set a flag indicating that the TSR wants to
  1483.                         activate, but can't right now
  1484.  
  1485.                      4. return to DOS
  1486.  
  1487.              d) this is linked in AFTER interrupt 08 - that is, when this
  1488.                 interrupt happens, call the original INT 08 handler, then
  1489.                 run your checking code:
  1490.  
  1491.                      1. does the TSR want to run?  if not, return from the
  1492.                         interrupt.
  1493.  
  1494.                      2. check the inDOS flag.  If it's out of DOS, then
  1495.                         run your code as normal
  1496.  
  1497.                      3. return from the interrupt
  1498.  
  1499.            * * NOTE: This code has to run FAST.  If it's poorly coded,
  1500.                      you may very well see downgraded performance of the
  1501.                      entire system.
  1502.  
  1503.  
  1504.              e) link in to the DOS keyboard busy loop - INT 28.  This
  1505.                      interrupt is called when DOS is waiting for a
  1506.                 keystroke via functions 1,3,7,8,0A, and 0C.  If the TSR
  1507.                 takes control from this loop, then DOS functions ABOVE 0C
  1508.                 are safe to use.  Functions 0 - 0C are NOT safe to use.
  1509.  
  1510.  
  1511.                                    ;Page 23
  1512.  
  1513.  
  1514.  
  1515.                      1. Does the TSR want to run?  if not, continue down
  1516.                         the interrupt chain.
  1517.  
  1518.                      2. run the TSR as usual
  1519.  
  1520.                      3. continue down the interrupt chain.
  1521.  
  1522.  
  1523.  
  1524.           NOTES:  The first action your main TSR code should take is to
  1525.              clear the flag that indicates the TSR is trying to run.  If
  1526.              this is not done, your TSR will re-enter itself at least
  1527.              18.2 times per second... i.e. a MESS.
  1528.  
  1529.              The last action your main TSR code should take before
  1530.              leaving is to RESET the flags that prevent the TSR from
  1531.              being activated.  If you forget to do this, your TSR will
  1532.              run once, then never again...  I know from personal
  1533.              experience that this is frustrating to a dangerous degree.
  1534.  
  1535.              Some of this code is really complicated, so don't get
  1536.              discouraged if it takes a few days of tweaking and
  1537.              hair-pulling to get it right.
  1538.  
  1539.              All numbers in this text are in hex.
  1540.  
  1541.              The timer tick routine is really touchy, at least the way I
  1542.              wrote it.  Be very sure yours is reliable if you distribute
  1543.              a program with this structure.
  1544.  
  1545.              The reason that functions 0-0C are separated from the rest
  1546.              of the DOS calls as far as re-entrancy is concerned is that
  1547.              they use an entirely separate stack frame.  I believe this
  1548.              must have been done specifically for the purpose of helping
  1549.              TSR writers.
  1550.  
  1551.              Does anyone know why the hell Microsoft built these neat
  1552.              functions into DOS and then refused to acknowledge their
  1553.              existence?
  1554.  
  1555.  
  1556.  
  1557.                             INTERRUPT & FUNCTION CALLS
  1558.  
  1559.      INT 08
  1560.              Timer tick interrupt.  Called 18.2 times a second on IRQ 0.
  1561.              The interrupt is triggered by timer 0.
  1562.  
  1563.  
  1564.      INT 21, FUNCTION 34
  1565.  
  1566.              inDOS flag address request.  This function returns the
  1567.              address of the "inDOS flag" as a 32 bit pointer in ES:BX.
  1568.              The inDOS flag is a byte that is zero when DOS is not
  1569.              processing a function request, and is non-zero when DOS is
  1570.              in a function.
  1571.  
  1572.  
  1573.  
  1574.                                    ;Page 24
  1575.  
  1576.  
  1577.  
  1578.              NOTE: This function is officially specified as RESERVED.
  1579.              It's use could change in future versions of DOS, and it can
  1580.              only be guaranteed to work in straight IBM PC-DOS or MS-DOS
  1581.              versions 2.0 to 3.30.  Use at your own risk.
  1582.  
  1583.      INT 28
  1584.  
  1585.              DOS keyboard busy loop. This interrupt is called when DOS is
  1586.              waiting for a keystroke in the console input functions.
  1587.              When this interrupt is issued, it is safe to use any DOS
  1588.              call ABOVE 0C.  Calls to DOS functions 0 - 0C will trash the
  1589.              stack and do nasty things.
  1590.  
  1591.              NOTE: This function is officially RESERVED.  See the note
  1592.              for function 34 above.
  1593.  
  1594.  
  1595.  
  1596.  
  1597.           AUTHOR'S NOTE:
  1598.  
  1599.              First, the references I listed are really great.  They've
  1600.           helped me out a lot over the past few years.  Second, if your
  1601.           hard disk gets munched by your TSR, read the disclaimer.
  1602.  
  1603.  
  1604.            ! ! ! ! ! !   C A V E A T   P R O G R A M M E R   ! ! ! ! ! !
  1605.  
  1606.                                  & Disclaimer
  1607.  
  1608.           The techniques described in here are, for the most part,
  1609.      UNDOCUMENTED by Microsoft or IBM.  This means that you CAN NOT BE
  1610.      SURE that they will work on all IBM clones, and could even cause
  1611.      crashes on some!  The timer tick interrupt provides some essential
  1612.      system services, and messing with it incautiously can wreak havoc.
  1613.  
  1614.           The program outlines presented here are what worked for me on
  1615.      my system, and what should work on about 90% of the clones out
  1616.      there.  However, I still suggest that you find a reference for all
  1617.      of the interrupts and functions described here.  This file is meant
  1618.      to be a guideline and aid only.
  1619.  
  1620.  
  1621.      REFERENCES:
  1622.  
  1623.              DOS Programmer's Reference, by Terry R. Dettmann.
  1624.                      $22.95, QUE Corporation
  1625.  
  1626.              IBM DOS Technical Reference, version 3.30
  1627.                      $(?), International Business Machines Corp.
  1628.  
  1629.                      I can't remember how much it cost...
  1630.  
  1631.  
  1632.  
  1633.  
  1634.  
  1635.  
  1636.  
  1637.                                    ;Page 25
  1638.  
  1639.  
  1640.  
  1641.                   Environment Variable Processor
  1642.                           by David O'Riva
  1643. ~
  1644.                 PAGE    60,132
  1645.                 TITLE   Q43.ASM - editor prelude & display manager
  1646. ;
  1647. ;
  1648. COMMENT~***********************************************************************
  1649. *   ---===> All code in this file is copyright 1989 by David O'Riva <===---   *
  1650. *******************************************************************************
  1651. *                                                                             *
  1652. * The above line is there only to prevent people (or COMPANIIES) from         *
  1653. * claiming original authorship of this code and suing me for using it.        *
  1654. * You're welcome to use it anyhow you care to.                                *
  1655. *                                                                             *
  1656. *
  1657. * Environment Variable Finder & Processor -                                   *
  1658. *                                                                             *
  1659. *       The "get_environment_variable" routine is complete in itself, and can *
  1660. * be extracted and used in anything else that needs one.  Just copy the entire
  1661. * routine, from the header to the endp (don't forget the RADIX and DW).
  1662. * Theroutine currently uses 315 (decimal) bytes.
  1663. *
  1664. *
  1665. *       This program's purposeis to invoke an editor (or any program, really,
  1666. * with a specific machine state depending on environment variables. (Yeah!!!)
  1667. * Currently it is set up to change my screen to one of various modes, with
  1668. * the variable ED_SCRMODE being set to:
  1669. *               100/75  = 100 columns by 75 lines
  1670. *               132/44  = 132 by 44
  1671. *               80/44   = 80 by 44
  1672. *       ...and then to EXEC my editor (qedit) with that mode set.  You could
  1673. * set the screen back to the standard 80x25 after the EXEC returns.
  1674. *
  1675. *       Note: The 80/44 set code should work on most (ar all?) EGAs.  The
  1676. * other two high-res text modes use built-in extended BIOS modes in my
  1677. * Everex EV-657 EGA card (the 800x600 version) w/multisync monitor.  If you've
  1678. * got one of those, you're in luck - no mods needed.  It will also work on the
  1679. * EV-673 EVGA card w/appropriate monitor.
  1680. *
  1681. *
  1682. *       Note to BEGINNERS: This is not an example of "good" asm code.  This
  1683. * file is an example of what happens when you're up at 1:00am with too much
  1684. * coffee and a utility that needs to be fixed.
  1685. *
  1686. *
  1687. *
  1688. *       This is a COM program, not an EXE.  Remember to use EXE2BIN.
  1689. *
  1690. *
  1691. *
  1692. ******************************************************************************~
  1693. ; ; TRUE            EQU     0FFH FALSE           EQU     0 ;
  1694. ;******************************************************************************
  1695. ; CODE         SEGMENT PARA PUBLIC 'CODE' ASSUME
  1696.                 CS:CODE,DS:CODE,ES:CODE,SS:CODE ; MAIN            PROC    NEAR
  1697.  
  1698.  
  1699.  
  1700.                                    ;Page 26
  1701.  
  1702.  
  1703.  
  1704.                 ORG     100H
  1705. entry:
  1706. ;------------------------------------------------------------------------------
  1707. ; set the screen to the correct mode
  1708. ;------------------------------------------------------------------------------
  1709.  
  1710.                 call    set_screen_mode
  1711.  
  1712. ;------------------------------------------------------------------------------
  1713. ; check for pathname change in environment
  1714. ;------------------------------------------------------------------------------
  1715.  
  1716.                 call    set_exec_name
  1717.  
  1718. ;------------------------------------------------------------------------------
  1719. ; setup memory and run the program
  1720. ;------------------------------------------------------------------------------
  1721.                 MOV     BX,OFFSET ENDRESIDENT   ;deallocate unnecessary memory
  1722.                 MOV     CL,4
  1723.                 SHR     BX,CL
  1724.                 INC     BX
  1725.                 MOV     AH,04AH
  1726.                 INT     021H
  1727.  
  1728.                 MOV     AX,CS                   ;exec the program
  1729.                 MOV     INSERT_CS1,AX
  1730.                 MOV     INSERT_CS2,AX
  1731.                 MOV     INSERT_CS3,AX
  1732.                 MOV     AX,04B00H
  1733.                 MOV     BX,OFFSET EXECPARMS
  1734.                 MOV     DX,OFFSET PROGNAME
  1735.                 INT     021H
  1736. ;------------------------------------------------------------------------------
  1737. ; clean up and leave
  1738. ;------------------------------------------------------------------------------
  1739.                 MOV     AH,04DH                 ;get return code from program
  1740.                 INT     021H
  1741.  
  1742.                 MOV     AH,04CH                 ;leave
  1743.                 INT     021H
  1744. ;
  1745. ;******************************************************************************
  1746. ;
  1747. ; data
  1748. ;
  1749. PROGNAME        DB      'F:\UTILITY\MISC\Q.EXE',0
  1750.                 db      100 dup(' ')
  1751.  
  1752.  
  1753. EXECPARMS       DW      0               ;use current environment
  1754.                 DW      080H            ;use current command tail
  1755. INSERT_CS1      DW      ?
  1756.                 DW      05CH            ;use current FCB's
  1757. INSERT_CS2      DW      ?
  1758.                 DW      06CH
  1759. INSERT_CS3      DW      ?
  1760.  
  1761.  
  1762.  
  1763.                                    ;Page 27
  1764.  
  1765.  
  1766.  
  1767. ENDRESIDENT:
  1768.  
  1769. ;******************************************************************************
  1770. ; more data - used only for setup & checks
  1771. ;
  1772. valid_modes     db      '80/44 '
  1773.                 db      '132/44'
  1774.                 db      '100/75'
  1775. screen_mode     db      '      '
  1776.  
  1777. mode_jump       dw      goto_43
  1778.                 dw      goto_132
  1779.                 dw      goto_100
  1780.  
  1781.  
  1782. ev_mode         db      'ED_SCRMODE',0
  1783. ev_pathname     db      'ED_PATH',0
  1784.  
  1785.  
  1786.  
  1787.                 PAGE
  1788. ;******************************************************************************
  1789. ; set_screen_mode -
  1790. ;
  1791. ;
  1792. ;     ENTRY:
  1793. ;
  1794. ;      EXIT:
  1795. ;
  1796. ; DESTROYED:
  1797. ;
  1798. ;------------------------------------------------------------------------------
  1799. set_screen_mode:
  1800.                 MOV     AH,012H                 ;check for presence of EGA/VGA
  1801.                 MOV     BL,010H
  1802.                 INT     010H
  1803.                 CMP     BL,010H                 ;BL changed? (should have # of
  1804.                                                 ;  bytes of EGA memory)
  1805.                 JE      ssm_no_ega              ;This is no EGA!
  1806. ;------------------------------------------------------------------------------
  1807. ; check environment for correct mode set -
  1808. ;       don't set mode if none specified
  1809. ;------------------------------------------------------------------------------
  1810.                 mov     si,offset ev_mode
  1811.                 mov     di,offset screen_mode
  1812.                 mov     cx,6                    ;accept 6 chars
  1813.                 mov     ax,4                    ;get fixed-length string
  1814.                 call    get_environment_variable
  1815.  
  1816.                 and     ax,0feh
  1817.                 jne     ssm_no_env_mode
  1818. ;------------------------------------------------------------------------------
  1819. ; look up the variable's value in my mode table
  1820. ;------------------------------------------------------------------------------
  1821.                 mov     bx,0
  1822.                 mov     di,offset valid_modes
  1823.  
  1824.  
  1825.  
  1826.                                    ;Page 28
  1827.  
  1828.  
  1829.  
  1830. ssm_check_mode: mov     dx,di
  1831.                 mov     si,offset screen_mode
  1832.                 mov     cx,6
  1833.                 repe    cmpsb
  1834.                 je      ssm_found_mode
  1835.                 mov     di,dx
  1836.                 add     di,6
  1837.                 inc     bx
  1838.                 cmp     bx,3
  1839.                 jne     ssm_check_mode
  1840.                 jmp     ssm_bad_mode
  1841. ;------------------------------------------------------------------------------
  1842. ; set the correct screen mode
  1843. ;------------------------------------------------------------------------------
  1844. ssm_found_mode: shl     bx,1
  1845.                 jmp     mode_jump[bx]
  1846.  
  1847. goto_100:       mov     ax,0070h
  1848.                 mov     bx,8
  1849.                 int     010h
  1850.                 jmp     ssm_leave
  1851.  
  1852. goto_132:       mov     ax,0070h
  1853.                 mov     bx,0bh
  1854.                 int     010h
  1855.                 jmp     ssm_leave
  1856.  
  1857.  
  1858. goto_43:        MOV     AX,3
  1859.                 INT     010H
  1860.                 MOV     AX,01112H               ;set to 8x8 chars (43/50 lines)
  1861.                 MOV     BL,0
  1862.                 INT     010H
  1863. ssm_no_env_mode:
  1864. ssm_bad_mode:
  1865. ssm_no_ega:
  1866. ssm_leave:
  1867.                 ret
  1868.  
  1869.  
  1870.  
  1871.                 PAGE
  1872. ;******************************************************************************
  1873. ; set_exec_name -
  1874. ;
  1875. ;
  1876. ;     ENTRY:
  1877. ;
  1878. ;      EXIT:
  1879. ;
  1880. ; DESTROYED:
  1881. ;
  1882. ;------------------------------------------------------------------------------
  1883. set_exec_name:
  1884. ;
  1885. ;       If you want, write a chunk here that will read an alternate pathname
  1886. ; for the editor to be executed from a different variable (like ED_PATH)
  1887.  
  1888.  
  1889.                                    ;Page 29
  1890.  
  1891.  
  1892.  
  1893. ; I was going to do it, but ran out of time and need. (My editor never wanders
  1894. ; around!)
  1895. ;
  1896.                 ret
  1897.  
  1898.  
  1899.  
  1900.  
  1901.  
  1902.  
  1903.  
  1904.  
  1905.  
  1906.                 PAGE
  1907. ;******************************************************************************
  1908. ; Get_environment_variable -
  1909. ;
  1910. ;
  1911. ;
  1912. ;
  1913. ;     ENTRY:    ds:[si] -> ASCIIZ environment variable name
  1914. ;               ds:[di] -> (up to) 129 byte buffer for string
  1915. ;               es = segment of program's PSP
  1916. ;               cx = maximum # of characters to accept
  1917. ;               al = variable return format
  1918. ;                 0 - return string in ASCIIZ format
  1919. ;                       xxxxx 0 ........
  1920. ;
  1921. ;                 1 - return string in DOS string ('$' terminated) format
  1922. ;                       xxxxxxxx $ ........
  1923. ;
  1924. ;                 2 - return string in DOS input buffer format
  1925. ;                       maxchrs,numchrs,xxxxxxxx CR ............
  1926. ;
  1927. ;                 3 - return string in command tail format
  1928. ;                       numchrs,xxxxxxxxxxx CR ..........
  1929. ;
  1930. ;                 4 - return string in fixed-length (CX chars) format
  1931. ;                       xxxxxx
  1932. ;
  1933. ;      EXIT:    al = return codes:
  1934. ;                   bit 0 - if set, string was longer than max, truncated
  1935. ;                       1 - if set, string did not exist
  1936. ;                       2 - if set, invalid return format requested
  1937. ;
  1938. ; DESTROYED:    ah is undefined
  1939. ;
  1940. ;------------------------------------------------------------------------------
  1941. .RADIX 010h
  1942. gev_flags       dw      ?
  1943.  
  1944. Get_environment_variable:
  1945.  
  1946.                 push    bx
  1947.                 push    cx
  1948.                 push    dx
  1949.                 push    si
  1950.  
  1951.  
  1952.                                    ;Page 30
  1953.  
  1954.  
  1955.  
  1956.                 push    di
  1957.                 push    es
  1958.  
  1959.                 mov     cs:gev_flags,ax
  1960.                 mov     es,es:[02c]             ;es -> program's environment
  1961. ;------------------------------------------------------------------------------
  1962. ; make sure the environment has at least one variable in it
  1963. ;------------------------------------------------------------------------------
  1964.                 mov     ax,es:[0]
  1965.                 cmp     ax,0
  1966.                 jne     gev_exists
  1967.                 mov     ax,2
  1968.                 jmp     gev_leave
  1969. ;------------------------------------------------------------------------------
  1970. ; find length of search string
  1971. ;------------------------------------------------------------------------------
  1972. gev_exists:     push    cx
  1973.                 push    di
  1974.                 mov     di,si
  1975.                 mov     cx,0ffff
  1976. gev_sourcelen:
  1977.                 inc     cx
  1978.                 mov     al,[di]
  1979.                 inc     di
  1980.                 cmp     al,0
  1981.                 jne     gev_sourcelen
  1982.  
  1983.                 cmp     cx,0
  1984.                 jne     gev_startfind
  1985.  
  1986.                 pop     di
  1987.                 pop     cx
  1988.                 mov     ax,2
  1989.                 jmp     gev_leave
  1990. ;------------------------------------------------------------------------------
  1991. ; find string
  1992. ;------------------------------------------------------------------------------
  1993. gev_startfind:  mov     bx,cx
  1994.                 mov     dx,si
  1995.                 mov     di,0
  1996. gev_checknext:
  1997.                 mov     cx,bx
  1998.                 mov     si,dx
  1999.                 repe    cmpsb
  2000.                 je      gev_found?
  2001.  
  2002. gev_tonextvar:  mov     cx,0ffff
  2003.                 mov     al,0
  2004.                 repne   scasb
  2005.  
  2006.                 cmp     es:[di],al
  2007.                 jne     gev_checknext
  2008.  
  2009.                 mov     ax,2
  2010.                 pop     di
  2011.                 pop     cx
  2012.                 jmp     gev_leave
  2013.  
  2014.  
  2015.                                    ;Page 31
  2016.  
  2017.  
  2018.  
  2019. gev_found?:     cmp     byte ptr es:[di],'='
  2020.                 jne     gev_tonextvar
  2021. ;------------------------------------------------------------------------------
  2022. ; found the string in the environment
  2023. ;------------------------------------------------------------------------------
  2024. gev_found:      inc     di
  2025.                 mov     si,di
  2026.                 pop     di
  2027.                 pop     cx
  2028.                 cmp     cs:gev_flags,1
  2029.                 ja      gev_ibufform
  2030. ;------------------------------------------------------------------------------
  2031. ; move normal string with 0 or $ terminator
  2032. ;------------------------------------------------------------------------------
  2033. gev_nextchar0:  mov     al,es:[si]
  2034.                 cmp     al,0
  2035.                 je      gev_setterm0
  2036.                 mov     ds:[di],al
  2037.                 inc     si
  2038.                 inc     di
  2039.                 dec     cx
  2040.                 jne     gev_nextchar0
  2041.                 mov     al,es:[si]
  2042.                 cmp     al,0
  2043.                 je      gev_setterm0
  2044.                 mov     al,1
  2045.  
  2046. gev_setterm0:   cmp     cs:gev_flags,0
  2047.                 jne     gev_setterm1
  2048.                 mov     byte ptr ds:[di],0              ;ASCIIZ string
  2049.                 jmp     gev_leave
  2050.  
  2051. gev_setterm1:   mov     byte ptr ds:[di],'$'            ;DOS string
  2052.                 jmp     gev_leave
  2053. ;------------------------------------------------------------------------------
  2054. ; move string into DOS input buffer format (int 21 function 0A)
  2055. ;------------------------------------------------------------------------------
  2056. gev_ibufform:   cmp     cs:gev_flags,2
  2057.                 jne     gev_ctailform
  2058.  
  2059.                 mov     ds:[di],cl                      ;set max length
  2060.                 inc     di
  2061.                 mov     bx,di
  2062.                 inc     di
  2063.                 mov     dx,0
  2064.  
  2065. gev_nextchar2:  mov     al,es:[si]
  2066.                 cmp     al,0
  2067.                 je      gev_setterm2
  2068.                 mov     ds:[di],al
  2069.                 inc     si
  2070.                 inc     di
  2071.                 inc     dx
  2072.                 dec     cx
  2073.                 jne     gev_nextchar2
  2074.                 mov     al,es:[si]
  2075.                 cmp     al,0
  2076.  
  2077.  
  2078.                                    ;Page 32
  2079.  
  2080.  
  2081.  
  2082.                 je      gev_setterm2
  2083.                 mov     al,1
  2084.  
  2085. gev_setterm2:   mov     byte ptr ds:[di],0d             ;add carriage return
  2086.                 mov     ds:[bx],dl                      ;set actual # of chars
  2087.                 jmp     gev_leave
  2088. ;------------------------------------------------------------------------------
  2089. ; move string into command tail format
  2090. ;------------------------------------------------------------------------------
  2091. gev_ctailform:  cmp     cs:gev_flags,3
  2092.                 jne     gev_fixedform
  2093.  
  2094.                 mov     bx,di
  2095.                 inc     di
  2096.                 mov     dx,0
  2097.  
  2098. gev_nextchar3:  mov     al,es:[si]
  2099.                 cmp     al,0
  2100.                 je      gev_setterm3
  2101.                 mov     ds:[di],al
  2102.                 inc     si
  2103.                 inc     di
  2104.                 inc     dx
  2105.                 dec     cx
  2106.                 jne     gev_nextchar3
  2107.                 mov     al,es:[si]
  2108.                 cmp     al,0
  2109.                 je      gev_setterm3
  2110.                 mov     al,1
  2111.  
  2112. gev_setterm3:   mov     byte ptr ds:[di],0d             ;set carriage return
  2113.                 mov     ds:[bx],dl                      ;set # of bytes
  2114.                 jmp     gev_leave
  2115. ;------------------------------------------------------------------------------
  2116. ; move string into fixed-length area (pad it out with spaces)
  2117. ;------------------------------------------------------------------------------
  2118. gev_fixedform:  cmp     cs:gev_flags,4
  2119.                 jne     gev_badform
  2120.  
  2121. gev_nextchar4:  mov     al,es:[si]
  2122.                 cmp     al,0
  2123.                 je      gev_padout4
  2124.                 mov     ds:[di],al
  2125.                 inc     si
  2126.                 inc     di
  2127.                 dec     cx
  2128.                 jne     gev_nextchar4
  2129.                 mov     al,es:[si]
  2130.                 cmp     al,0
  2131.                 je      gev_setterm4
  2132.                 mov     al,1
  2133.                 jmp     gev_setterm4
  2134.  
  2135. gev_padout4:    mov     byte ptr ds:[di],' '
  2136.                 inc     di
  2137.                 dec     cx
  2138.                 jne     gev_padout4
  2139.  
  2140.  
  2141.                                    ;Page 33
  2142.  
  2143.  
  2144.  
  2145.                 mov     al,0
  2146. gev_setterm4:   jmp     gev_leave
  2147.  
  2148.  
  2149. gev_badform:    mov     ax,4
  2150.  
  2151. gev_leave:      pop     es
  2152.                 pop     di
  2153.                 pop     si
  2154.                 pop     dx
  2155.                 pop     cx
  2156.                 pop     bx
  2157.                 ret
  2158. .RADIX 00ah
  2159.  
  2160.  
  2161. MAIN            ENDP
  2162. ;
  2163. ;******************************************************************************
  2164. ;
  2165. CODE            ENDS
  2166. ;
  2167. ;******************************************************************************
  2168. ;
  2169.           END  ENTRY
  2170.  
  2171. ~
  2172.  
  2173.  
  2174.  
  2175.  
  2176.  
  2177.  
  2178.  
  2179.  
  2180.  
  2181.  
  2182.  
  2183.  
  2184.  
  2185.  
  2186.  
  2187.  
  2188.  
  2189.  
  2190.  
  2191.  
  2192.  
  2193.  
  2194.  
  2195.  
  2196.  
  2197.  
  2198.  
  2199.  
  2200.  
  2201.  
  2202.  
  2203.  
  2204.                                    ;Page 34
  2205.  
  2206.  
  2207.  
  2208.                              Program Reviews
  2209.  
  2210.  
  2211.      Multi-Edit ver 4.00a (demo version): Reviewed by Patrick O'Riva.
  2212.  
  2213.           Multi-Edit is a high feature text editor with many word
  2214.      processor features. The demo version is completely functional though
  2215.      some of the reference material is not supplied and there are
  2216.      advertising screens. I consider this fully acceptable as shareware.
  2217.      The complete version with the macro reference library is available
  2218.      for 79.95 and an expanded version with a spelling checker,
  2219.      integrated Communication terminal and phone book is $179.95.
  2220.  
  2221.           I couldn't list all of its features here, but in addition to
  2222.      everything you have come to expect in a quality programming editor
  2223.      (multi meg files, programmable keyboard etc.) there are a number of
  2224.      powerful additions you might not expect. The word processor
  2225.      functions rival most of the specialty ones that I've tried. It
  2226.      won't compete with the major names for those of you who are addicted
  2227.      to them, but it does offer full printer support, preview file, table
  2228.      of contents generation, and extension keyed formatting. It will
  2229.      right or left justify, and supports headers and footers, and auto
  2230.      pagination.
  2231.  
  2232.           It contains a calculator and an Ascii table
  2233.  
  2234.           Saving the best for last: The language support is very strong.
  2235.      It has built in templates for many common constructs, and the
  2236.      assembler/compiler is invoked from within the editor with a single
  2237.      key. It will read the error table generated by a variety of software
  2238.      and with successive key presses move you to each line where an error
  2239.      was found.
  2240.  
  2241.           Something which I found unique is Multi-Edit's help system.  It
  2242.      is a hypertext system, and is wonderfully context sensitive most
  2243.      everywhere in the system. From the Help menu it has a complete table
  2244.      of contents and index. It is also fully user extendible. I have
  2245.      integrated a database I have documenting the full set of interrupts
  2246.      that totals about 400k and the documentation on my spelling checker
  2247.      as well (which integrated into Multi-Edit almost seamlessly).
  2248.  
  2249.           In many ways this is the best editor I've ever used, but it
  2250.      does have a few faults, some of which are very subtle and may not
  2251.      even be problems to most users.  It is a 'tad' slower that what I'm
  2252.      used to with Qedit.  This is seldom noticed except in the execution
  2253.      of complex macros.  It is quite slow in paging through long files.
  2254.      There are some true bugs in this version such as a crash of the
  2255.      program (but not the data or the system) when large deletes from
  2256.      large files are made. Multi-edit's treatment of file windows while
  2257.      very versatile is slightly different and may take some time to get
  2258.      used to.
  2259.  
  2260.           For all of its advantages, until putting this Magazine
  2261.      together, I still found myself reverting to Qedit for the speed and
  2262.      ease of use.  It is the first software that has made this anything
  2263.      other than an exercise in frustration.
  2264.  
  2265.  
  2266.  
  2267.                                    ;Page 35
  2268.  
  2269.  
  2270.  
  2271.      SHEZ
  2272.  
  2273.           Just a quick mention because it isn't programming related.
  2274.      Shez is a compression shell along the lines of ArcMaster and Qfiler.
  2275.      It is a fine and versatile piece of programming, supporting all
  2276.      common compression types. The more recent versions have virus
  2277.      detection when used with the SCANV programs by John McAfee.
  2278.  
  2279.  
  2280.  
  2281.       4DOS
  2282.  
  2283.           This is a program that is an absolute joy to use.  It is a
  2284.      complete and virtually 100% compatible replacement for Command.com.
  2285.      The code size is just slightly larger than MSDOS 3.3 command.com but
  2286.      the added and enhanced functions save many times that amount in
  2287.      TSR's you no longer need to install. Just to mention a few features:
  2288.      An   alias  command whereby you can assign whatever mnemonic you
  2289.      wish to a command or string of commands. Select  is a screen
  2290.      interface that allows you to mark files for use with a command.
  2291.      Except   will execute a command for a set of files excluding one or
  2292.      more. There is an environment editor, built in Help, command
  2293.      and filename completion, Global that will execute through the
  2294.      directory tree, A Timer to keep track of elapsed time, as well as
  2295.      many enhanced batch file commands. Additional features are too
  2296.      numerous to mention. The current version is 4.23 and is available as
  2297.      Shareware, but you should register after your first 10 minutes of
  2298.      use. You will be hooked forever.
  2299.  
  2300.  
  2301.      The above 3 programs should all be available on your local BBS's.
  2302.      Please be sure and register programs you use.
  2303.  
  2304.  
  2305.  
  2306.  
  2307.  
  2308.  
  2309.  
  2310.  
  2311.  
  2312.  
  2313.  
  2314.  
  2315.  
  2316.  
  2317.  
  2318.  
  2319.  
  2320.  
  2321.  
  2322.  
  2323.  
  2324.  
  2325.  
  2326.  
  2327.  
  2328.  
  2329.  
  2330.                                    ;Page 36
  2331.  
  2332.  
  2333.  
  2334.                                 Book Reviews
  2335.  
  2336.  
  2337.      Assembly Language Quick Reference
  2338.  
  2339.           by Allen L. Wyatt, Sr.
  2340.           Reviewed by George A. Stanislav
  2341.  
  2342.      This 1989 book published by QUE is a nice and handy reference for
  2343.      assembly language programmers.
  2344.  
  2345.      Instruction sets for six microprocessors and numeric coprocessors
  2346.      are listed:
  2347.  
  2348.              8086/8088       8087
  2349.              80286           80287
  2350.              80386           80387
  2351.  
  2352.      I could find no reference to the 80186 microprocessor, not even a
  2353.      suggestion that it uses the 80286 instruction set but does not
  2354.      multitask. Because the 80186 was the brain of Tandy 2000, quite a
  2355.      popular computer in its own time, its omission from the book is
  2356.      surprising.
  2357.  
  2358.      There is no division into chapters. This makes it somewhat hard to
  2359.      figure out where the instruction sets of individual processors
  2360.      start. Each higher processor set contains only the list of
  2361.      instructions that are new for the processor or that changed
  2362.      somewhat.
  2363.  
  2364.      After a  brief introduction, the book starts by listing,
  2365.      alphabetically, all 8086/8088 instructions. The listing itself is
  2366.      very well done. Each instruction stands out graphically from the
  2367.      rest of the text. For every code there is some classification, e.g.
  2368.      arithmetic, bit manipulation, data-transfer.
  2369.  
  2370.      This is followed by a very brief description ended with a colon.
  2371.      Next, a more detailed explanation gives sufficient information to
  2372.      any assembly language programmer what the instruction does.
  2373.  
  2374.      If applicable, the book lists flags affected by the instruction.
  2375.      Most instructions also contain some coding examples.
  2376.  
  2377.      The 8086/8088 instruction set is followed by the 80286 set, or
  2378.      rather subset as it only contains the instructions new to this
  2379.      microprocessor. Similarly, the 80386 section contains only those
  2380.      instructions not found in the 8086/8088 and 80286 sections as well
  2381.      as those that changed somewhat.
  2382.  
  2383.      I find it puzzling that among those instructions considered changed
  2384.      in the 80386 microprocessor we can find AND, NEG, POP - because they
  2385.      can be used as 32-bit instructions in addition to their original
  2386.      usage - but cannot find JE, JNE, and all other conditional jumps.
  2387.      These did indeed change in the 80386 processor inasmuch they can be
  2388.      used either as SHORT or as NEAR while on the older microprocessors
  2389.      they could only jump within the SHORT range.
  2390.  
  2391.  
  2392.  
  2393.                                    ;Page 37
  2394.  
  2395.  
  2396.  
  2397.      The rest of the book contains instructions for the math
  2398.      coprocessors, the 8087, 80287 and 80387. This section is divided in
  2399.      the same way as the microprocessor part, i.e. describing first the
  2400.      8087 set, then the one new instruction for the 80286, followed by
  2401.      the new 80387 instructions.
  2402.  
  2403.      There are several possibilities of improvement QUE might consider
  2404.      for future editions of this book:
  2405.  
  2406.              o Make it easier to find the start of each section by color
  2407.                coding the side of the paper;
  2408.  
  2409.              o Include references to the instructions of the older
  2410.                processors within the listing for the new processors.
  2411.                Small print of the instruction with the page number where
  2412.                a more detailed description can be found would be a nice
  2413.                enhancement;
  2414.  
  2415.              o At least a brief mention of the 80186 microprocessor and
  2416.                perhaps the V-20 and V-30 would be useful.
  2417.  
  2418.      Despite the possibility of improvement, this is an excellent
  2419.      reference for any assembly language programmer. Its small size makes
  2420.      it very handy to keep it next to the computer as well as to take it
  2421.      along when travelling.
  2422.  
  2423.      The book costs $6.95 in USA and $8.95 in Canada.
  2424.  
  2425.  
  2426.  
  2427.  
  2428.  
  2429.  
  2430.  
  2431.  
  2432.  
  2433.  
  2434.  
  2435.  
  2436.  
  2437.  
  2438.  
  2439.  
  2440.  
  2441.  
  2442.  
  2443.  
  2444.  
  2445.  
  2446.  
  2447.  
  2448.  
  2449.  
  2450.  
  2451.  
  2452.  
  2453.  
  2454.  
  2455.  
  2456.                                    ;Page 38
  2457.  
  2458.  
  2459.  
  2460.                              GPFILT.ASM
  2461.  ~
  2462.         page    ,132
  2463.         TITLE   GPFILT
  2464.         subttl  General Purpose Filter Template
  2465. ;
  2466. ; GPFILT.ASM
  2467. ; This file contains a template for a general-purpose assembly language
  2468. ; filter program.
  2469. ;
  2470. ; Fill in the blanks for what you wish to do.  The program is set up to
  2471. ; accept a command line in the form:
  2472. ;       COMMAND [{-|/}options] [infile [outfile]]
  2473. ;
  2474. ; If infile is not specified, stdin is used.
  2475. ; If outfile is not specified, stdout is used.
  2476. ;
  2477. ; To compile and link:
  2478. ;    MASM GPFILT ;
  2479. ;    LINK GPFILT ;
  2480. ;    EXE2BIN GPFILT GPFILT.COM
  2481. ;
  2482. ; Standard routines supplied in the general shell are:
  2483. ;
  2484. ; get_arg - returns the address of the next command line argument in
  2485. ;           DX.  Since this is a .COM file, the routine assumes DS will
  2486. ;           be the same as the command line segment.
  2487. ;           The routine will return with Carry set when it reaches the end
  2488. ;           of the command line.
  2489. ;
  2490. ; err_msg - displays an ASCIIZ string on the STDERR device.  Call with the
  2491. ;           address of the string in ES:DX.
  2492. ;
  2493. ; do_usage- displays the usage message on the STDERR device and exits
  2494. ;           with an error condition (errorlevel 1).  This routine will
  2495. ;           never return.
  2496. ;
  2497. ; getch   - returns the next character from the input stream in AL.
  2498. ;           It will return with carry set if an error occurs during read.
  2499. ;           It will return with the ZF set at end of file.
  2500. ;
  2501. ; putch   - writes a character from AL to the output stream.  Returns with
  2502. ;           carry set if a write error occurs.
  2503. ;
  2504. cseg    segment
  2505.         assume cs:cseg, ds:cseg, es:cseg, ss:cseg
  2506.  
  2507.         org     0100h                   ;for .COM files
  2508.  
  2509. start:  jmp     main                    ;jump around data area
  2510.  
  2511. ;
  2512. ; Equates and global data area.
  2513. ;
  2514. ; The following equates and data areas are required by the general filter
  2515. ; routines.  User data area follows.
  2516. ;
  2517.  
  2518.  
  2519.                                    ;Page 39
  2520.  
  2521.  
  2522.  
  2523. STDIN   equ     0
  2524. STDOUT  equ     1
  2525. STDERR  equ     2
  2526. STDPRN  equ     3
  2527. cr      equ     0dh
  2528. lf      equ     0ah
  2529. space   equ     32
  2530. tab     equ     9
  2531.  
  2532. infile  dw      STDIN                   ;default input file is stdin
  2533. outfile dw      STDOUT                  ;default output file is stdout
  2534. errfile dw      STDERR                  ;default error file is stderr
  2535. prnfile dw      STDPRN                  ;default print file is stdprn
  2536. cmd_ptr dw      0081h                   ;address of first byte of command tail
  2537. PSP_ENV equ     002ch                   ;The segment address of the environment
  2538.                                         ;block is stored here.
  2539.  
  2540. infile_err      db      cr, lf, 'Error opening input file', 0
  2541. outfile_err     db      cr, lf, 'Error opening output file', 0
  2542. aborted         db      07, cr, lf, 'Program aborted', 0
  2543. usage           db      cr, lf, 'Usage: ', 0
  2544. crlf            db      cr, lf, 0
  2545.  
  2546. ;************************************************************************
  2547. ;*                                                                      *
  2548. ;* Buffer sizes for input and output files.  The buffers need not be    *
  2549. ;* the same size.  For example, a program that removes tabs from a text *
  2550. ;* file will output more characters than it reads.  Therefore, the      *
  2551. ;* output buffer should be slightly larger than the input buffer.  In   *
  2552. ;* general, the larger the buffer, the faster the program will run.     *
  2553. ;*                                                                      *
  2554. ;* The only restriction here is that the combined size of the buffers   *
  2555. ;* plus the program code and data size cannot exceed 64K.               *
  2556. ;*                                                                      *
  2557. ;* The easiest way to determine maximum available buffer memory is to   *
  2558. ;* assemble the program with minimum buffer sizes and examine the value *
  2559. ;* of the endcode variable at the end of the program.  Subtracting this *
  2560. ;* value from 65,536 will give you the total buffer memory available.   *
  2561. ;*                                                                      *
  2562. ;************************************************************************
  2563. ;
  2564. INNBUF_SIZE     equ     31              ;size of input buffer (in K)
  2565. OUTBUF_SIZE     equ     31              ;size of output buffer (in K)
  2566.  
  2567. ;
  2568. ;************************************************************************
  2569. ;*                                                                      *
  2570. ;* Data definitions for input and output buffers.  DO NOT modify these  *
  2571. ;* definitions unless you know exactly what it is you're doing!       *
  2572. ;*                                                                      *
  2573. ;************************************************************************
  2574. ;
  2575. ; Input buffer
  2576. ibfsz   equ     1024*INNBUF_SIZE        ;input buffer size in bytes
  2577. inbuf   equ     endcode                 ;input buffer
  2578. ibfend  equ     inbuf + ibfsz           ;end of input buffer
  2579. ;
  2580.  
  2581.  
  2582.                                    ;Page 40
  2583.  
  2584.  
  2585.  
  2586. ; ibfptr is initialized to point past end of input buffer so that the first
  2587. ; call to getch will result in a read from the file.
  2588. ;
  2589. ibfptr  dw      inbuf+ibfsz
  2590.  
  2591. ; output buffer
  2592. obfsz   equ     1024*OUTBUF_SIZE        ;output buffer size in bytes
  2593. outbuf  equ     ibfend                  ;output buffer
  2594. obfend  equ     outbuf + obfsz          ;end of output buffer
  2595. obfptr  dw      outbuf                  ;start at beginning of buffer
  2596.  
  2597. ;************************************************************************
  2598. ;*                                                                      *
  2599. ;*                            USER DATA AREA                            *
  2600. ;*                                                                      *
  2601. ;*      Insert any data declarations specific to your program here.     *
  2602. ;*                                                                      *
  2603. ;* NOTE:  The prog_name, use_msg, and use_msg1 variables MUST be        *
  2604. ;*        defined.                                                      *
  2605. ;*                                                                      *
  2606. ;************************************************************************
  2607. ;
  2608. ; This is the program name.  Under DOS 3.x, this is not used because we
  2609. ; can get the program name from the environment.  Prior to 3.0, this
  2610. ; information is not supplied by the OS.
  2611. ;
  2612. prog_name       db      'GPFILT', 0
  2613. ;
  2614. ; This is the usage message.  The first two lines are required.
  2615. ; The first line is the programs title line.
  2616. ;   Make sure to include the 0 at the end of the first line!!
  2617. ; The second line shows the syntax of the program.
  2618. ; Following lines (which are optional), are discussion of options, features,
  2619. ; etc...
  2620. ; The message MUST be terminated by a 0.
  2621. ;
  2622. use_msg db      ' - General Purpose FILTer program.', cr, lf, 0
  2623. use_msg1        label byte
  2624.         db      '[{-|/}options] [infile [outfile]]', cr, lf
  2625.         db      cr, lf
  2626.         db      'If infile is not specified, STDIN is used', cr, lf
  2627.         db      'If outfile is not specified, STDOUT is used', cr, lf
  2628.         db      0
  2629. ;
  2630. ;************************************************************************
  2631. ;*                                                                      *
  2632. ;* The main routine parses the command line arguments, opens files, and *
  2633. ;* does other initialization tasks before calling the filter procedure  *
  2634. ;* to do the actual work.                                               *
  2635. ;* For a large number of filter programs, this routine will not need to *
  2636. ;* be modified.  Options are parsed in the get_options proc., and the   *
  2637. ;* filter proc. does all of the 'filter' work.                          *
  2638. ;*                                                                      *
  2639. ;************************************************************************
  2640. ;
  2641. main:   cld
  2642.         call    get_options             ;process options
  2643.  
  2644.  
  2645.                                    ;Page 41
  2646.  
  2647.  
  2648.  
  2649.         jc      gofilter                ;carry indicates end of arg list
  2650.         mov     ah,3dh                  ;open file
  2651.         mov     al,0                    ;read access
  2652.         int     21h                     ;open the file
  2653.         mov     word ptr ds:[infile], ax ;save file handle
  2654.         jnc     main1                   ;carry clear indicates success
  2655.         mov     dx,offset infile_err
  2656.         jmp     short err_exit
  2657. main1:  call    get_arg                 ;get cmd line arg in DX
  2658.         jc      gofilter                ;carry indicates end of arg list
  2659.         mov     ah,3ch                  ;create file
  2660.         mov     cx,0                    ;normal file
  2661.         int     21h                     ;open the file
  2662.         mov     word ptr ds:[outfile],ax ;save file handle
  2663.         jnc     gofilter                ;carry clear indicates success
  2664.         mov     dx,offset outfile_err
  2665.         jmp     short err_exit
  2666. gofilter:
  2667.         call    filter                  ;do the work
  2668.         jc      err_exit                ;exit immediately on error
  2669.         mov     ah,3eh
  2670.         mov     bx,word ptr [infile]
  2671.         int     21h                     ;close input file
  2672.         mov     ah,3eh
  2673.         mov     bx,word ptr [outfile]
  2674.         int     21h                     ;close output file
  2675.         mov     ax,4c00h
  2676.         int     21h                     ;exit with no error
  2677. err_exit:
  2678.         call    err_msg                 ;output error message
  2679.         mov     dx,offset aborted
  2680.         call    err_msg
  2681.         mov     ax,4c01h
  2682.         int     21h                     ;and exit with error
  2683. ;
  2684. ;************************************************************************
  2685. ;*                                                                      *
  2686. ;* get_options processes any command line options.  Options are         *
  2687. ;* preceeded by either - or /.  There is a lot of flexibility here.     *
  2688. ;* Options can be specified separately, or as a group.  For example,    *
  2689. ;* the command "GPFILT -x -y -z" is equivalent to "GPFILT -xyz".        *
  2690. ;*                                                                      *
  2691. ;* This routine MUST return the address of the next argument in DX or   *
  2692. ;* carry flag set if there are no more options.  In other words, return *
  2693. ;* what was returned by the last call to get_arg.                       *
  2694. ;*                                                                      *
  2695. ;************************************************************************
  2696. ;
  2697. get_options     proc
  2698.         call    get_arg                 ;get command line arg
  2699.         jnc     opt1
  2700. ; If at least one argument is required, use this line
  2701. ;       call    do_usage                ;displays usage msg and exits
  2702. ; If there are no required args, use this line
  2703.         ret                             ;if no args, just return
  2704. opt1:   mov     di, dx
  2705.         mov     al,byte ptr ds:[di]
  2706.  
  2707.  
  2708.                                    ;Page 42
  2709.  
  2710.  
  2711.  
  2712.         cmp     al,'-'                  ;if first character of arg is '-'
  2713.         jz      opt_parse
  2714.         cmp     al,'/'                  ;or '/', then get options
  2715.         jz      opt_parse
  2716.         ret                             ;otherwise exit
  2717. opt_parse:
  2718.         inc     di
  2719.         mov     al,byte ptr ds:[di]
  2720.         or      al,al                   ;if end of options string
  2721.         jz      nxt_opt                 ;get cmd. line arg
  2722.         cmp     al,'?'                  ;question means show usage info
  2723.         jz      do_usage
  2724. ;
  2725. ;************************************************************************
  2726. ;*                                                                      *
  2727. ;* Code for processing other options goes here.  The current option     *
  2728. ;* character is in AL, and the remainder of the option string is pointed*
  2729. ;* to by DS:DI.                                                         *
  2730. ;*                                                                      *
  2731. ;************************************************************************
  2732. ;
  2733.         jmp     short opt_parse
  2734.  
  2735. nxt_opt:
  2736.         call    get_arg                 ;get next command line arg
  2737.         jnc     opt1                    ;if carry
  2738. vld_args:                               ;then validate arguments
  2739. ;
  2740. ;************************************************************************
  2741. ;*                                                                      *
  2742. ;* Validate arguments.  If some options are mutually exclusive/dependent*
  2743. ;* use this area to validate them.  Whatever the case, if you must      *
  2744. ;* abort the program, call the do_usage procedure to display the usage  *
  2745. ;* message and exit the program.                                        *
  2746. ;*                                                                      *
  2747. ;************************************************************************
  2748. ;
  2749.         ret                             ; no more options
  2750. ;
  2751. ;************************************************************************
  2752. ;*                                                                      *
  2753. ;* Filter does all the work.  Modify this routine to do what it is you  *
  2754. ;* need done.                                                           *
  2755. ;*                                                                      *
  2756. ;************************************************************************
  2757. ;
  2758. filter  proc
  2759.         call    getch                   ;get a character from input into AL
  2760.         jbe     filt_done               ;exit on error or EOF
  2761.         and     al, 7fh                 ;strip the high bit
  2762.         call    putch                   ;and output it
  2763.         jc      filt_ret                ;exit on error
  2764.         jmp     short filter
  2765. filt_done:
  2766.         jc      filt_ret                ;carry set is error
  2767.         call    write_buffer            ;output what remains of the buffer
  2768. filt_ret:
  2769.  
  2770.  
  2771.                                    ;Page 43
  2772.  
  2773.  
  2774.  
  2775.         ret
  2776. filter  endp
  2777. ;
  2778. ;************************************************************************
  2779. ;*                                                                      *
  2780. ;*              Put any program-specific routines here                  *
  2781. ;*                                                                      *
  2782. ;************************************************************************
  2783.  
  2784. ;
  2785. ;************************************************************************
  2786. ;*                                                                      *
  2787. ;* For most programs, nothing beyond here should require modification.  *
  2788. ;* The routines that follow are standard routines used by almost every  *
  2789. ;* filter program.                                                      *
  2790. ;*                                                                      *
  2791. ;************************************************************************
  2792. ;
  2793. ;************************************************************************
  2794. ;*                                                                      *
  2795. ;* This routine outputs the usage message to the STDERR device and      *
  2796. ;* aborts the program with an error code.  A little processing is done  *
  2797. ;* here to get the program name and format the output.                  *
  2798. ;*                                                                      *
  2799. ;************************************************************************
  2800. ;
  2801. do_usage:
  2802.         mov     dx, offset crlf
  2803.         call    err_msg                 ;output newline
  2804.         mov     ah,30h                  ;get DOS version number
  2805.         int     21h
  2806.         sub     al,3                    ;check for version 3.x
  2807.         jc      lt3                     ;if carry, earlier than 3.0
  2808. ;
  2809. ; For DOS 3.0 and later the full pathname of the file used to load this
  2810. ; program is stored at the end of the environment block.  We first scan
  2811. ; all of the environment strings in order to find the end of the env, then
  2812. ; scan the load pathname looking for the file name.
  2813. ;
  2814.         push    es
  2815.         mov     ax, word ptr ds:[PSP_ENV]
  2816.         mov     es, ax                  ;ES is environment segment address
  2817.         mov     di, 0
  2818.         mov     cx, 0ffffh              ;this ought to be enuf
  2819.         xor     ax, ax
  2820. getvar: scasb                           ;get char
  2821.         jz      end_env                 ;end of environment
  2822. gv1:    repnz   scasb                   ;look for end of variable
  2823.         jmp     short getvar            ;and loop 'till end of environment
  2824. end_env:
  2825.         inc     di
  2826.         inc     di                      ;bump past word count
  2827. ;
  2828. ; ES:DI is now pointing to the beginning of the pathname used to load the
  2829. ; program.  We will now scan the filename looking for the last path specifier
  2830. ; and use THAT address to output the program name.  The program name is
  2831. ; output WITHOUT the extension.
  2832.  
  2833.  
  2834.                                    ;Page 44
  2835.  
  2836.  
  2837.  
  2838. ;
  2839.         mov     dx, di
  2840. fnloop: mov     al, byte ptr es:[di]
  2841.         or      al, al                  ;if end of name
  2842.         jz      do30                    ;then output it
  2843.         inc     di
  2844.         cmp     al, '\'                 ;if path specifier
  2845.         jz      updp                    ;then update path pointer
  2846.         cmp     al, '.'                 ;if '.'
  2847.         jnz     fnloop
  2848.         mov     byte ptr es:[di-1], 0   ;then place a 0 so we don't get ext
  2849.         jmp     short fnloop            ; when outputting prog name
  2850. updp:   mov     dx, di                  ;store
  2851.         jmp     short fnloop
  2852. ;
  2853. ; ES:DX now points to the filename of the program loaded (without extension).
  2854. ; Output the program name and then go on with rest of usage message.
  2855. ;
  2856. do30:   call    err_msg                 ;output program name
  2857.         pop     es                      ;restore
  2858.         jmp     short gopt3
  2859. ;
  2860. ; We arrive here if the current DOS version is earlier than 3.0.  Since the
  2861. ; loaded program name is not available from the OS, we'll output the name
  2862. ; entered in the 'prog_name' field above.
  2863. ;
  2864. lt3:    mov     dx, offset prog_name
  2865.         call    err_msg                 ;output the program name
  2866. ;
  2867. ; After outputting program name, we arrive here to output the rest of the
  2868. ; usage message.  This code assumes that the usage message has been
  2869. ; written as specified in the data area.
  2870. ;
  2871. gopt3:  mov     dx, offset use_msg
  2872.         call    err_msg                 ;output the message
  2873.         mov     dx, offset usage
  2874.         call    err_msg
  2875.         mov     dx, offset use_msg1
  2876.         call    err_msg
  2877.         mov     ax,4c01h
  2878.         int     21h                     ;and exit with error
  2879. get_options     endp
  2880.  
  2881. ;
  2882. ;************************************************************************
  2883. ;*                                                                      *
  2884. ;* Output a message (ASCIIZ string) to the standard error device.       *
  2885. ;* Call with address of error message in ES:DX.                         *
  2886. ;*                                                                      *
  2887. ;************************************************************************
  2888. ;
  2889. err_msg proc
  2890.         cld
  2891.         mov     di,dx                   ;string address in di
  2892.         mov     cx,0ffffh
  2893.         xor     ax,ax
  2894.         repnz   scasb                   ;find end of string
  2895.  
  2896.  
  2897.                                    ;Page 45
  2898.  
  2899.  
  2900.  
  2901.         xor     cx,0ffffh
  2902.         dec     cx                      ;CX is string length
  2903.         push    ds
  2904.         mov     ax,es
  2905.         mov     ds,ax                   ;DS is segment address
  2906.         mov     ah,40h
  2907.         mov     bx,word ptr cs:[errfile]
  2908.         int     21h                     ;output message
  2909.         pop     ds
  2910.         ret
  2911. err_msg endp
  2912.  
  2913. ;
  2914. ;************************************************************************
  2915. ;*                                                                      *
  2916. ;* getch returns the next character from the file in AL.                *
  2917. ;* Returns carry = 1 on error                                           *
  2918. ;*         ZF = 1 on EOF                                                *
  2919. ;* Upon exit, if either Carry or ZF is set, the contents of AL is       *
  2920. ;* undefined.                                                           *
  2921. ;*                                                                      *
  2922. ;************************************************************************
  2923. ;
  2924. ; Local variables used by the getch proc.
  2925. eof     db      0                       ;set to 1 when EOF reached in read
  2926. last_ch dw      ibfend                  ;pointer to last char in buffer
  2927.  
  2928. getch   proc
  2929.         mov     si,word ptr ds:[ibfptr] ;get input buffer pointer
  2930.         cmp     si,word ptr ds:[last_ch];if not at end of buffer
  2931.         jz      getch_eob
  2932. getch1: lodsb                           ;character in AL
  2933.         mov     word ptr ds:[ibfptr],si ;save buffer pointer
  2934.         or      ah,1                    ;will clear Z flag
  2935.         ret                             ;and done
  2936.  
  2937. getch_eob:                              ;end of buffer processing
  2938.         cmp     byte ptr ds:[eof], 1    ;end of file?
  2939.         jnz     getch_read              ;nope, read file into buffer
  2940. getch_eof:
  2941.         xor     ax, ax                  ;set Z to indicate EOF
  2942.         ret                             ;and return
  2943.  
  2944. getch_read:                     ; Read the next buffer full from the file.
  2945.         mov     ah,3fh                  ;read file function
  2946.         mov     bx,word ptr ds:[infile] ;input file handle
  2947.         mov     cx,ibfsz                ;#characters to read
  2948.         mov     dx,offset inbuf         ;read into here
  2949.         int     21h                     ;DOS'll do it for us
  2950.         jc      read_err                ;Carry means error
  2951.         or      ax,ax                   ;If AX is zero,
  2952.         jz      getch_eof               ;we've reached end-of-file
  2953.         add     ax,offset inbuf
  2954.         mov     word ptr ds:[last_ch],ax;and save it
  2955.         mov     si,offset inbuf
  2956.         jmp     short getch1            ;and finish processing character
  2957.  
  2958.  
  2959.  
  2960.                                    ;Page 46
  2961.  
  2962.  
  2963.  
  2964. read_err:                               ;return with error and...
  2965.         mov     dx,offset read_err_msg  ; DX pointing to error message string
  2966.         ret
  2967. read_err_msg    db      'Read error', cr, lf, 0
  2968. getch   endp
  2969.  
  2970. ;
  2971. ;************************************************************************
  2972. ;*                                                                      *
  2973. ;* putch writes the character passed in AL to the output file.          *
  2974. ;* Returns carry set on error.  The character in AL is retained.        *
  2975. ;*                                                                      *
  2976. ;************************************************************************
  2977. ;
  2978. putch   proc
  2979.         mov     di,word ptr ds:[obfptr] ;get output buffer pointer
  2980.         stosb                           ;save the character
  2981.         mov     word ptr ds:[obfptr],di ;and update buffer pointer
  2982.         cmp     di,offset obfend        ;if buffer pointer == buff end
  2983.         clc
  2984.         jnz     putch_ret
  2985.         push    ax
  2986.         call    write_buffer            ;then we've got to write the buffer
  2987.         pop     ax
  2988. putch_ret:
  2989.         ret
  2990. putch   endp
  2991.  
  2992. ;
  2993. ;************************************************************************
  2994. ;*                                                                      *
  2995. ;* write_buffer writes the output buffer to the output file.            *
  2996. ;* This routine should not be called except by the putch proc. and at   *
  2997. ;* the end of all processing (as demonstrated in the filter proc).      *
  2998. ;*                                                                      *
  2999. ;************************************************************************
  3000. ;
  3001. write_buffer    proc                    ;write buffer to output file
  3002.         mov     ah, 40h                 ;write to file function
  3003.         mov     bx, word ptr ds:[outfile];output file handle
  3004.         mov     cx, word ptr ds:[obfptr]
  3005.         sub     cx, offset outbuf       ;compute #bytes to write
  3006.         mov     dx, offset outbuf       ;from this buffer
  3007.         int     21h                     ;DOS'll do it
  3008.         jc      write_err               ;carry is error
  3009.         or      ax,ax                   ;return value of zero
  3010.         jz      putch_full              ;indicates disk full
  3011.         mov     word ptr ds:[obfptr],offset outbuf
  3012.         clc
  3013.         ret
  3014.  
  3015. putch_full:                             ;disk is full
  3016.         mov     dx,offset disk_full
  3017.         stc                             ;exit with error
  3018.         ret
  3019.  
  3020. write_err:                              ;error occured during write
  3021.  
  3022.  
  3023.                                    ;Page 47
  3024.  
  3025.  
  3026.  
  3027.         mov     dx,offset write_err_msg
  3028.         stc                             ;return with error
  3029.         ret
  3030. write_err_msg   db      'Write error', cr, lf, 0
  3031. disk_full       db      'Disk full', cr, lf, 0
  3032.  
  3033. write_buffer    endp
  3034.  
  3035. ;
  3036. ;************************************************************************
  3037. ;*                                                                      *
  3038. ;* get_arg - Returns the address of the next command line argument in   *
  3039. ;* DX.  The argument is in the form of an ASCIIZ string.                *
  3040. ;* Returns Carry = 1 if no more command line arguments.                 *
  3041. ;* Upon exit, if Carry is set, the contents of DX is undefined.         *
  3042. ;*                                                                      *
  3043. ;************************************************************************
  3044. ;
  3045. get_arg proc
  3046.         mov     si,word ptr [cmd_ptr]
  3047. skip_space:                             ;scan over leading spaces and commas
  3048.         lodsb
  3049.         cmp     al,0                    ;if we get a null
  3050.         jz      sk0
  3051.         cmp     al,cr                   ;or a CR,
  3052.         jnz     sk1
  3053. sk0:    stc                             ;set carry to indicate failure
  3054.         ret                             ;and exit
  3055. sk1:    cmp     al,space
  3056.         jz      skip_space              ;loop until no more spaces
  3057.         cmp     al,','
  3058.         jz      skip_space              ;or commas
  3059.         cmp     al,tab
  3060.         jz      skip_space              ;or tabs
  3061.  
  3062.         mov     dx,si                   ;start of argument
  3063.         dec     dx
  3064. get_arg1:
  3065.         lodsb                           ;get next character
  3066.         cmp     al,cr                   ;argument seperators are CR,
  3067.         jz      get_arg2
  3068.         cmp     al,space                ;space,
  3069.         jz      get_arg2
  3070.         cmp     al,','                  ;comma,
  3071.         jz      get_arg2
  3072.         cmp     al,tab                  ;and tab
  3073.         jnz     get_arg1
  3074.  
  3075. get_arg2:
  3076.         mov     byte ptr ds:[si-1], 0   ;delimit argument with 0
  3077.         cmp     al, cr                  ;if char is CR then we've reached
  3078.         jnz     ga2                     ; the end of the argument list
  3079.         dec     si
  3080. ga2:    mov     word ptr ds:[cmd_ptr], si ;save for next time 'round
  3081.         ret                             ;and return
  3082. get_arg endp
  3083.  
  3084.  
  3085.  
  3086.                                    ;Page 48
  3087.  
  3088.  
  3089.  
  3090. endcode equ     $
  3091.  
  3092. cseg    ends
  3093.         end     start
  3094. 
  3095. ~
  3096.  
  3097.  
  3098.  
  3099.  
  3100.  
  3101.  
  3102.  
  3103.  
  3104.  
  3105.  
  3106.  
  3107.  
  3108.  
  3109.  
  3110.  
  3111.  
  3112.  
  3113.  
  3114.  
  3115.  
  3116.  
  3117.  
  3118.  
  3119.  
  3120.  
  3121.  
  3122.  
  3123.  
  3124.  
  3125.  
  3126.  
  3127.  
  3128.  
  3129.  
  3130.  
  3131.  
  3132.  
  3133.  
  3134.  
  3135.  
  3136.  
  3137.  
  3138.  
  3139.  
  3140.  
  3141.  
  3142.  
  3143.  
  3144.  
  3145.  
  3146.  
  3147.                                    ;Page 49
  3148.