home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / assemblr / tutorial / y_asm / y_assemb.doc < prev    next >
Encoding:
Text File  |  1994-07-03  |  1.1 MB  |  26,075 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1. Introduction N.1
  2. ================
  3.  
  4.              Hi!
  5.  
  6.              Welcome to "The PC Assembler Helper" and "The PC Assembler
  7.              Tutor". Both the program and the tutorial are designed to help
  8.              those who are just starting to learn assembler language as well
  9.              as those who know some assembler instructions but want to have a
  10.              firmer grasp of the complete instruction set for the 8086.
  11.  
  12.              There are two significant problems to learning assembler
  13.              language. First, it is difficult to do either input or output at
  14.              the assembler level. Imagine trying to learn BASIC if you were
  15.              not allowed the following two instructions:
  16.  
  17.                  PRINT   RESULT
  18.                  INPUT   NEW.DATA
  19.  
  20.              Without PRINT and INPUT, you might be able to write a program but
  21.              you would not be able to see the results. You would never be sure
  22.              that the results were what you wanted. Also, you would not be
  23.              able to vary the data. It would have to be coded into the
  24.              program.
  25.  
  26.              "The PC Assembler Helper" has taken care of this problem. It
  27.              provides input and output of all standard 8086/8087 integral data
  28.              types. These include 1 byte, 2 byte, 4 byte and 8 byte signed and
  29.              unsigned numbers along with 1 byte and 2 byte hex, ASCII and
  30.              binary data. Lastly, there is i/o for 10 byte BCD numbers. The
  31.              interface has been designed so that beginners can use it with a
  32.              minimum of trouble.
  33.  
  34.              The second major problem is that most assembler books regard the
  35.              8086 as a black box. There is no way of seeing the workings of
  36.              the chip itself. What exactly happens when you add two numbers?
  37.              What about multiplication?
  38.  
  39.              Once again, "The PC Assembler Helper" has come up with the
  40.              solution. It allows you to view all the registers and flags at
  41.              will. These registers can be independently formatted. If one
  42.              register holds ASCII data while another has binary information
  43.              and yet a third has a signed number, then each register can be
  44.              set to display the appropriate type of data. This, you will find,
  45.              is invaluable.
  46.  
  47.              A third, though less important problem, is assembler overhead.
  48.              There is a certain structure that must be followed to get the
  49.              program to assemble and run correctly. This has been provided in
  50.              template files. All you need to do is copy the appropriate
  51.              template file and put the code in a predefined location to get
  52.              your program to run correctly. For simple programs this can cut
  53.              your work in half. It also minimizes the number of typos.
  54.  
  55.              "The PC Assembler Tutor" is built around the Helper. It
  56.              systematically goes through the 8086 instruction set, having you
  57.              write small programs to illustrate how each instruction works. At
  58.  
  59.  
  60.              The PC Assembler Tutor                                          2
  61.              ______________________
  62.  
  63.              the end you should have a feeling for all the instructions except
  64.              a few which involve the 8087 or peripheral hardware. These will
  65.              be mentioned, but not used.
  66.  
  67.              You will be a better programmer if you know how all the
  68.              instructions work. There are times when one specific instruction
  69.              is just what you want. If you don't know that it exists or how it
  70.              works, you won't use it. This way, if you run across a situation
  71.              where you think that a certain instruction might be useful to
  72.              you, you can go back to the Tutor to refresh your memory and be
  73.              able to put the instruction to use almost immediately.
  74.  
  75.              If you are a beginner, I feel confident that you will learn
  76.              faster and more thoroughly than with any other method. If you
  77.              know some assembler but would like to know more, I'm sure that
  78.              there is lots that would interest you.
  79.  
  80.                  AAD
  81.                  SBB
  82.                  XLAT
  83.                  REPNE
  84.                  SCAS
  85.  
  86.              Do you know what these are? What about segment overrides? Do you
  87.              know when to use them and when to avoid them? Do you know ALL the
  88.              allowable addressing modes? What actually is an ASSUME statement?
  89.  
  90.              In order to let you see if you find the material interesting, you
  91.              may go through chapters 0 - 4 chapters without any obligation.
  92.              You may also make an archival copy of the disks (you are urged to
  93.              do so). If you continue after the fourth chapter then please
  94.              register by sending $9.95 (or $10.60 for Californians) to
  95.              Nelsoft. The registration form is at the end of this
  96.              introduction.
  97.  
  98.              If I followed the pricing structure of other people I would be
  99.              charging several times as much. My goal is different. I want
  100.              everyone who can benefit from the program and tutorial to use
  101.              them, and I want everyone who uses them to do so legally.
  102.              Therefore, I have priced them so that everyone can pay for them
  103.              without any inconvenience. If you use them, you can certainly
  104.              afford my minimal price.
  105.  
  106.              The material is sequential. Chapter 0 should be read before
  107.              starting on the other chapters and the chapters should be read in
  108.              order. Appendix 1 contains all the subroutine calls in "The
  109.              Assembler Helper" and how to access them. Appendix 2 is an
  110.              alphabetical list of all the 8086 instructions, telling what they
  111.              do and showing all allowable syntaxes. Appendix 3 gives the speed
  112.              of all instructions along with a list of which flags are affected
  113.              (you will learn what this means in the Tutor).
  114.  
  115.              It is to your benefit to start at the beginning and work your way
  116.              through. Chapter 0 contains material that you need to know, so
  117.              you must read it. The text has been broken up into sections so
  118.              that no printout is longer than 10 pages or so. All text files
  119.              have a file extension .DOC. If a chapter is much longer than
  120.  
  121.  
  122.              Introduction                                                    3
  123.              ____________
  124.  
  125.              that, it will be divided into parts, indicated by -1, -2, -3
  126.              after the chapter number. These files should be printable with
  127.              the DOS 'print' command. The only imbedded printer command code
  128.              is form feed for the next page. The text runs about 2500
  129.              characters a page, so you can estimate the size of the printout
  130.              from the size of the text file.
  131.  
  132.              Curly brackets in the text denote a footnote.{1}  Some of the
  133.              footnotes are technical and will be understood by only a quarter
  134.              of the people. If you are one of that quarter, fine. If not, the
  135.              important thing is not that you understand the outline of the
  136.              proof, but that you believe that what is being proved is true.
  137.  
  138.              The assembler level is for those who have some degree of
  139.              intellegence. You have an unparalleled opportunity to screw
  140.              things up at this level. If you got Cs and Ds in high school
  141.              algebra because you didn't quite understand what was going on,
  142.              then you probably shouldn't do assembler programming.{2}
  143.  
  144.              In addition, I assume that you have done a lot of programming,
  145.              preferably in either Pascal or C. BASIC is a nice language, but
  146.              it is missing a certain type of structure which is vital for
  147.              creating robust code in assembler language. If BASIC is all you
  148.              know, I would recommend that you learn C first and then come back
  149.              to assembler. You will be a better programmer for it.{3}
  150.  
  151.              Finally, "The Assembler Helper" assumes that it has control of
  152.              the screen. If you are hooked up to a debugger, there may be a
  153.              conflict. There is a subroutine in the Helper called "set_timer"
  154.              which may help minimize this conflict. You need to be in chapter
  155.              5 or so before you will be able to use it. See \APPENDIX\APP1.DOC
  156.              for details.
  157.  
  158.              If you are ready to go, please look at the following two pages
  159.              and then read INTRO2.DOC. It will explain a little about what an
  160.              assembler is. I hope you enjoy using the Helper and the Tutor as
  161.              much as I enjoyed writing them.
  162.  
  163.  
  164.              Chuck Nelson
  165.  
  166.  
  167.  
  168.  
  169.              ____________________
  170.  
  171.                 1. Like this one.
  172.  
  173.                 2. If you got Cs and Ds because you were too busy reading
  174.              "Tales from the Crypt" and Isaac Asimov, that's something
  175.              entirely different.
  176.  
  177.                 3. On re-reading this I decided that it is true, but
  178.              pretensious. If you like BASIC and program well in BASIC, then
  179.              you should learn assembler and continue using BASIC. There are
  180.              certain inherent difficulties with BASIC, so before you start you
  181.              should read BAS1.DOC. This is on DISK2.
  182.  
  183.  
  184.              The PC Assembler Tutor                                          4
  185.              ______________________
  186.  
  187.  
  188.  
  189.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  190.                                      All rights reserved
  191.  
  192.  
  193.              Microsoft (R) Macro Assembler and Microsoft (R) Overlay Linker
  194.              are registered trademarks of Microsoft Corporation.
  195.  
  196.              This manual contains screen output of the Macro Assembler and the
  197.              Overlay Linker. Screen shots (C) 1981-1988 Microsoft Corporation.
  198.  
  199.              It also contains excerpts from Macro Assembler .LST files and
  200.              Overlay Linker .MAP files. Portions of these files are Copyright
  201.              (C) 1981-1988 Microsoft Corporation.
  202.  
  203.              Used with permission of Microsoft Corporation.
  204.  
  205.  
  206.  
  207.                            TRADEMARK ACKNOWLEDGEMENT
  208.  
  209.              IBM is a registered trademark of International Business Machines
  210.                  Inc.
  211.              Intel is a registered trademark of Intel Corporation.
  212.              Macintosh is a registered trademark of Apple Computer, Inc.
  213.              Microsoft is a registered trademark of Microsoft Corporation.
  214.              Motorola is a registered trademark of Motorola, Inc.
  215.              8086 is a trademark of Intel Corporation.
  216.              Codeview is a registered trademark of Microsoft Corporation.
  217.              QuickC is a registered trademark of Microsoft Corporation.
  218.              Turbo Pascal, Turbo Assembler and Turbo Debugger are registered
  219.                  trademarks of Borland International.
  220.  
  221.  
  222.  
  223.  
  224.              The PC Assembler Helper was designed as a learning tool. It is
  225.              meant to be used in conjunction with simple assembler programs to
  226.              display the results of individual assembler instructions. It
  227.              should not be used with high-level languages nor with programs
  228.              that modify the screen.
  229.  
  230.              HELPMEM.COM, the memory resident version, uses the same
  231.              interrupts as a debugger. Therefore, if there is a debugger
  232.              attatched to any program that is being used, HELPMEM.COM should
  233.              not be loaded into memory.
  234.  
  235.  
  236.                                    WARRANTY
  237.  
  238.              THIS PROGRAM, INSTRUCTION MANUAL, AND REFERENCE MATERIALS ARE
  239.              SOLD "AS IS", WITHOUT WARRANTY AS TO THEIR PERFORMANCE,
  240.              MERCHANTABILITY, OR FITNESS FOR ANY PARTICULAR PURPOSE. THE
  241.              ENTIRE RISK AS TO THE RESULTS AND PERFORMANCE OF THESE PROGRAMS
  242.              IS ASSUMED BY YOU.
  243.  
  244.  
  245.  
  246.  
  247.  
  248.  
  249.           *****************************************************************
  250.                                      REGISTRATION
  251.  
  252.           Hey, Chuck, I'm no chump!
  253.  
  254.           I'm using your programs/manual, and I want to pay my fair share.
  255.           Please make me a registered user of "The PC Assembler Tutor" and
  256.           "The PC Assembler Helper". Enclosed is a check for $9.95 (plus
  257.           6.5% tax or $10.60 for California residents). Say, that's cheaper
  258.           than a large pizza!
  259.  
  260.  
  261.           Name_________________________________________________________
  262.                       Last                First               Initial
  263.  
  264.           Address______________________________________________________
  265.                       Street Address
  266.  
  267.                    _______________________________________________________
  268.                       City, State, and Zip Code
  269.  
  270.           I got my copy from ___________________________________________
  271.  
  272.           Make checks payable to NELSOFT and send your registration to:
  273.  
  274.                  NELSOFT
  275.                  P.O. Box 21389
  276.                  Oakland, CA  94620
  277.  
  278.           ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  279.                              REGISTRATION BENEFITS
  280.  
  281.           As a registered user of "The PC Assembler Helper" and "The PC
  282.           Assembler Tutor" you are entitled to:
  283.  
  284.           1) Use asmhelp.obj and helpmem.com for personal use.
  285.           2) Make 1 (one) printer copy of "The PC Assembler Tutor".
  286.           3) Use all programs in "The PC Assembler Tutor" for personal use.
  287.           4) Make an archival copy of the disks.
  288.           5) Distribute UNALTERED disks to friends for their perusal.
  289.           6) Use any updates to either "The PC Assembler Helper" or "The PC
  290.              Assembler Tutor" under the same registration conditions.
  291.  
  292.           Though copies of the disk may be given away if there is no
  293.           charge, it is illegal to charge for redistribution of the disk or
  294.           its contents without permission of the author. Under no
  295.           circumstances may you distribute printed copies of "The PC
  296.           Assembler Tutor". If you intend to charge for distributing the
  297.           disk or its information, please read and sign the following
  298.           distribution agreement.
  299.  
  300.           *****************************************************************
  301.  
  302.  
  303.  
  304.  
  305.                         DISTRIBUTION LICENSING AGREEMENT FOR
  306.                             THE PC ASSEMBLER HELPER AND
  307.                                THE PC ASSEMBLER TUTOR
  308.  
  309.  
  310.           Anyone wishing to charge people a fee for giving them a copy of
  311.           The PC Assembler Helper and/or The PC Assembler Tutor must have
  312.           the written authorization of the author, without which the
  313.           distributor is guilty of copyright violation. To receive such
  314.           authorization, send this completed application, along with a copy
  315.           of your software library's order form to:
  316.  
  317.                 NELSOFT
  318.                 P.O. Box 21389
  319.                 Oakland, CA  94620
  320.  
  321.           If you want a distribution disk with the latest copy of these
  322.           programs, please include $7.00 to cover the cost of the disks,
  323.           mailing and handling. (This offer is for bona fide user groups
  324.           and shareware distributors only).
  325.  
  326.  
  327.           NAME OF ORGANIZATION ___________________________________________
  328.  
  329.           YOUR NAME ______________________________________________________
  330.  
  331.           ADDRESS ________________________________________________________
  332.  
  333.           CITY, STATE ____________________________________________________
  334.  
  335.  
  336.                                TERMS OF DISTRIBUTION
  337.  
  338.              1. The fee charged for each disk may not exceed $7.00. On
  339.              high-density disks, the fee may not be over $10.00.
  340.  
  341.              2. Your library's catalog or listing must state that this
  342.              material is not free, but is copyrighted material that is
  343.              provided to allow the user to evaluate it before paying.
  344.  
  345.              3. The offering and sale of disks containing The PC Assembler
  346.              Helper and The PC Assembler Tutor will be stopped at any time
  347.              the author so requests.
  348.  
  349.              4. The Tutor and the Helper must be distributed together. The
  350.              compressed files and the information document must remain in
  351.              the subdirectory \PCTUTOR. There may be no additional files in
  352.              this subdirectory. Both the name and the contents of
  353.              \PCREADME.DOC must remain unaltered.
  354.  
  355.              5. Problems or complaints will be reported to the author.
  356.  
  357.           In return for the right to charge a fee for the distribution of
  358.           The PC Assembler Helper and The PC Assembler Tutor, I agree to
  359.           comply with the above terms of distribution.
  360.  
  361.           Signed,
  362.  
  363.  
  364.           __________________________________________    __________________
  365.                       Your Signature                           Date
  366. Introduction N.2
  367. ================
  368.  
  369.  
  370.                                                                              5
  371.  
  372.  
  373.              WHAT'S AN ASSEMBLER?
  374.  
  375.              What is the difference between a compiler and an assembler?
  376.  
  377.              A compiler is a program that takes the source code you have
  378.              written and turns it into machine language instructions that are
  379.              usable by the computer. A machine language instruction is a
  380.              binary number that tells the computer to do one specific thing.
  381.              This is something very specific like: add 1 to a specific
  382.              variable. The compiler does this in two steps. First, the
  383.              compiler takes each line of source code and turns the expression
  384.              on the line into a number of simple tasks to accomplish what is
  385.              desired, generating a number of assembler TEXT instructions and
  386.              data definitions. When the compiler is through with the source
  387.              code, it then takes these TEXT instructions and assembles them to
  388.              form an object module. An object module is a file that can be
  389.              linked with other files to form a larger program.
  390.  
  391.              Why two steps instead of one? There are several reasons. This
  392.              allows a compiler writer to write a first part for any language
  393.              like Pascal, C, BASIC, etc., and then use the same assembler
  394.              part. This saves development time in a company that has more than
  395.              one type of compiler. You can insert a new assembler part without
  396.              worrying about the text generation part, or you can insert a new
  397.              text generation part without worrying about the assembler part.
  398.              Secondly, though an assembler is not a simple program, the
  399.              compiler's text generator is even more complicated. Putting the
  400.              two together is like trying to juggle eight balls instead of four
  401.              balls.
  402.  
  403.              This leads to the most vital reason. Not only would a unified
  404.              text-generator/assembler be more error prone, it would be almost
  405.              impossible to debug. If you are getting an error due to one type
  406.              of Pascal instruction, is this because it is being misunderstood
  407.              by the compiler or because the compiler is giving it the wrong
  408.              machine codes? In the two part system, the compiler writer can
  409.              look at the intermediate text code and isolate the problem into
  410.              one of the two halves.
  411.  
  412.              An assembler is a program that takes a TEXT file where each line
  413.              corresponds to a specific machine instruction or type of data,
  414.              calculates the address in memory where each piece of data or
  415.              machine instruction will be, translates each instruction and
  416.              piece of data into machine readable form, and inserts the
  417.              addresses of data and labels into machine instructions where
  418.              appropriate.{1}
  419.  
  420.  
  421.  
  422.              ____________________
  423.  
  424.                 1. A label is just a name which marks a certain spot in the
  425.              assembler code.
  426.  
  427.  
  428.  
  429.              The PC Assembler Tutor                                          6
  430.              ______________________
  431.  
  432.              The text name for a machine instruction is called a mnemonic.{2}
  433.              It indicates what is being done by the instruction. Which would
  434.              you rather use for multiplication, 'MUL' or '11110111xx100xxx'?
  435.              These 'x's indicate a digit whose value depends on where
  436.              something is in memory. For each mnemonic there is a single
  437.              machine instruction which performs the operation.
  438.  
  439.              This means that the assembler's task is relatively simple. It
  440.              only needs to allocate space for all the variables and
  441.              instructions, to translate each mnemonic and data value into its
  442.              corresponding machine code, and finally put it into a machine
  443.              usable file.
  444.  
  445.              Here you need to know what different forms of file there are.
  446.  
  447.                  1) An executable (.EXE) file contains certain information
  448.                  for the operating system when the program is started. This
  449.                  allows the program to be as large as is wanted.
  450.  
  451.                  2) A .COM file contains no information for the operating
  452.                  system. When the operating system starts a .COM file it
  453.                  simply puts it in memory and starts it. Files with a .COM
  454.                  extension are limited to a length of 64k bytes.
  455.  
  456.                  3) Binary files are files which must be loaded into a .COM
  457.                  or .EXE program before being run. They cannot be used by
  458.                  themselves. They are archaic. They are a crutch for those
  459.                  compilers which don't support .OBJ files, and are
  460.                  disappearing.
  461.  
  462.                  4) An object (.OBJ) file is a section of a program. It
  463.                  contains code and variables, but also contains information
  464.                  that can be used to combine it with other object files into
  465.                  a larger program. A linker can convert one or more object
  466.                  files into an executable file.
  467.  
  468.  
  469.              Things have moved along in the past few years. TurboPascal 3.0
  470.              generated .COM files. This wasn't because .COM files were
  471.              superior but because it was too difficult to generate the extra
  472.              information needed to produce an .OBJ file. Interpreted BASIC
  473.              required binary files because it did not have the ability to use
  474.              .OBJ files. The situation now is:
  475.  
  476.                  If you want to link with the current Turbo Pascal, you
  477.                  should use an .OBJ file. If you want to link with QuickC,
  478.                  you need an .OBJ file. If you want to combine an external
  479.                  subprogram with QuickBASIC, you need an .OBJ file. Get the
  480.                  picture?
  481.  
  482.              No assembler makes .EXE files. If you have a single file that you
  483.              want made onto a stand alone .EXE file, you first make an .OBJ
  484.              file and then use that single file with LINK.
  485.  
  486.              ____________________
  487.  
  488.                 2. You don't pronounce that first 'm'.
  489.  
  490.  
  491.              Introduction                                                    7
  492.              ____________
  493.  
  494.              When making a .COM file, the normal route is to make .OBJ files,
  495.              link them together into an EXE file, and then convert them to a
  496.              .COM file with a program called EXE2BIN. This allows you to
  497.              divide the problem into a number of subproblems and put them all
  498.              together at the end.
  499.  
  500.              As you can see from the above, the job for the assembler is to
  501.              take a text file and convert it into an .OBJ file. The three
  502.              assemblers that you are most likely to have are MASM, A86, and
  503.              TurboAssembler. They all produce object files. Since assemblers
  504.              simply supply the machine code for each instruction, they will
  505.              all produce the exact same code.{3}
  506.  
  507.              This is one of the differences between assemblers and compilers.
  508.              The text generation phase of the compiler requires creativity. It
  509.              is the compiler writer's idea of how to solve a certain problem
  510.              in a specific language. This generated text is copyrighted, and
  511.              you need a license to distribute a program that includes this
  512.              generated text. An assembler. on the other hand, is just a
  513.              drudge. If you had a book with the machine codes and had enough
  514.              time, you could produce the same file byte for byte that the
  515.              assembler would produce for a .COM file. There is no creativity
  516.              involved in the generated code, and there is no license involved
  517.              in its distribution.
  518.  
  519.              There is some difference in the speed of those three assemblers,
  520.              but I'll have a comment about that after I give you the numbers.
  521.              These numbers are from the time of hitting the ENTER key to the
  522.              return of the DOS prompt ('>'). These are on a low speed machine
  523.              so your numbers should be better, but won't be any worse.
  524.  
  525.                                           A86       TurboASM       MASM
  526.  
  527.                  one page of code         3.2          8.8         10.3
  528.                  20 pages of code         7.3         12.7         20.4
  529.                  60 pages of code        12.5         22.9         43.3
  530.  
  531.              All these numbers are in seconds. A86 is the fastest. It loads
  532.              faster because it itself is a .COM file, and it works faster. But
  533.              even the slowest (MASM), is fast enough. How long does it take to
  534.              write 60 pages of code? Probably a week or two. Writing assembler
  535.              code is normally slower than writing code for a high-level
  536.              language. Even the slowest finishes the assembly in well under a
  537.              minute. Think of the time it would take to compile 60 pages of
  538.              Pascal code.
  539.  
  540.              In fact, the normal length of a file will be from 10 to 20 pages,
  541.              so these are the numbers you need to think of. These will be the
  542.              smaller .OBJ files which are linked together by the linker.
  543.  
  544.              If you have one of these assemblers you don't need anything
  545.              different. If you need to get one, you can use this information
  546.              ____________________
  547.  
  548.                 3. Or functionally the same code. Sometimes there are two
  549.              different instructions that do the same thing, just as 6+1, 5+2
  550.              and 4+3 all produce 7.
  551.  
  552.  
  553.              The PC Assembler Tutor                                          8
  554.              ______________________
  555.  
  556.              to help you in your selection. The following prices are as of
  557.              mid-1990.
  558.  
  559.              A86 is available through shareware. It costs $50.00 for the
  560.              assembler alone, $80.00 with the debugger. Add another $10.00 if
  561.              you want a printed manual.
  562.  
  563.              Both MASM and Turbo Assembler come with assembler, debugger, a
  564.              number of utility programs and several manuals. They both retail
  565.              for $150.00, but even a quick glance at BYTE magazine will find
  566.              you a place that is selling them for $105.00 - $110.00. They come
  567.              bundled, so you cannot buy the assembler without the debugger
  568.              (The Microsoft debugger is Codeview and the Borland debugger is
  569.              Turbo Debugger).
  570.  
  571.              Speaking of debuggers, you may be thinking, "Well, I have DEBUG,
  572.              so why do I want another debugger?" DEBUG has been outdated for
  573.              several years now. It has been supplanted by symbolic debuggers
  574.              which associate code with specific lines in the original text
  575.              file. You give the symbolic debugger the .EXE file along with the
  576.              text files that produced it, and it shows you your source code as
  577.              you go along. Here's a section of code we will meet later in the
  578.              Tutor:
  579.  
  580.                  ; - - - - - - - - - - - - - - - - - - - -
  581.                  start: push  ds               ; set up for return
  582.                       sub   ax,ax
  583.                       push  ax
  584.                       mov   ax, DATASTUFF    ; load ds
  585.                       mov   ds,ax
  586.  
  587.                  outer_loop:
  588.                       lea  ax, multiplicand    ; load multiplicand
  589.                       call get_unsigned_8byte
  590.                       call print_unsigned_8byte
  591.                       call get_unsigned        ; unsigned word to multiplier
  592.                       mov  multiplier, ax
  593.  
  594.                       lea  si, multiplicand    ; load pointers
  595.                       lea  bx, result
  596.                  ; - - - - - - - - - - - - - - - - - - - -
  597.  
  598.              Don't worry about what these instructions do. You'll learn that
  599.              later. Here is DEBUG's idea of what is going on:
  600.  
  601.              ******************** DEBUG SCREEN SHOT ************************
  602.              -r
  603.              AX=0000 BX=0000 CX=2749 DX=0000 SP=0A00 BP=0000 SI=0000 DI=0000
  604.              DS=0D7E ES=0D7E SS=0D8E CS=0E7F  NV UP DI PL NZ NA PO NC
  605.              0E7F:0000 1E            PUSH    DS
  606.              -u0E7F:0000 2A
  607.              0E7F:0000 1E            PUSH    DS
  608.              0E7F:0001 2BC0          SUB     AX,AX
  609.              0E7F:0003 50            PUSH    AX
  610.              0E7F:0004 B82E0E        MOV     AX,0E2E
  611.              0E7F:0007 8ED8          MOV     DS,AX
  612.              0E7F:0009 8D060800      LEA     AX,[0008]
  613.  
  614.  
  615.              Introduction                                                    9
  616.              ____________
  617.  
  618.              0E7F:000D E80C14        CALL    141C
  619.              0E7F:0010 E84B11        CALL    115E
  620.              0E7F:0013 E8F505        CALL    060B
  621.              0E7F:0016 A31000        MOV     [0010],AX
  622.              0E7F:0019 8D360800      LEA     SI,[0008]
  623.              0E7F:001D 8D1E1200      LEA     BX,[0012]
  624.              ********************** END DEBUG *******************************
  625.  
  626.              Part of this is understandable, but a lot of it is confusing, and
  627.              you have lost the concept of what you are trying to do. To see
  628.              what a symbolic debugger does with the same code, here is the
  629.              Turbo Debugger's idea of what is happening:
  630.  
  631.  
  632.  
  633.  ************************* TURBO SCREEN SHOT {4} *****************************
  634.    File   View   Run   Breakpoints   Data   Window   Options             READY
  635.  .Module: debugtst  File: debugtst.asm 74....................................1
  636.  .
  637.  .  start: push  ds               ; set up for return      .Registers......3.
  638.  .         sub   ax,ax                                     .  ax 5C94   .c=0.
  639.  .         push  ax                                        .  bx 0000   .z=1.
  640.  .                                                         .  cx 0000   .s=0.
  641.  .         mov   ax, DATASTUFF    ; load ds                .  dx 0000   .o=0.
  642.  .         mov   ds,ax                                     .  si 0000   .p=1.
  643.  .                                                         .  di 0000   .a=0.
  644.  .  outer_loop:                                            .  bp 0000   .i=1.
  645.  .          lea     ax, multiplicand        ; load multipli.  sp 09FA   .d=0.
  646.  .          call    get_unsigned_8byte                     .  ds 4AD6   .   .
  647.  .          call    print_unsigned_8byte                   .  es 4A26   .   .
  648.  .          call    get_unsigned            ; unsigned word.  ss 4A36   .   .
  649.  .          mov     multiplier, ax                         .  cs 4B27   .   .
  650.  .                                                         .  ip 0019   .   .
  651.  .                                                         ..................
  652.  .          lea     si, multiplicand        ; load pointers
  653.  .............................................................................
  654.  .Watches....................................................................2
  655.  .multiplier,d                  23700
  656.  .multiplicand                  qword 00000042E843515D
  657.  .............................................................................
  658.   F1-Help F2-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run
  659.  *****************************************************************************
  660.  
  661.  
  662.  
  663.              DEBUG really doesn't meet our needs. Of course, those of you who
  664.              still use EDLIN to write 20 page documents should feel free to
  665.              use DEBUG.
  666.  
  667.              Modern language compilers have their own debuggers in their
  668.              environments, so we only need a debugger for the assembler. Turbo
  669.              Debugger and Code View do the job very well. D86 is better than
  670.              DEBUG but it has some problems.
  671.              ____________________
  672.  
  673.                 4. Turbo Debugger is (C) Copyright 1988-1989 Borland
  674.              International.
  675.  
  676.  
  677.              The PC Assembler Tutor                                         10
  678.              ______________________
  679.  
  680.  
  681.              If you actually want a debugger that will symbolically debug
  682.              everything that supports symbolic debugging, you might want to
  683.              take a look at the Turbo Debugger. It has more power than you are
  684.              ever likely to need.
  685.  
  686.              "The Assembler Helper", the program which comes with the Tutor,
  687.              is NOT a debugger. Debuggers are designed to show you what is
  688.              happening with your code; the Helper is designed to show you what
  689.              is happening with the 8086. It's a fundamental difference in
  690.              outlook.
  691.  
  692.              If you want to use a debugger while you are doing this tutorial
  693.              it is possible but the results are not guaranteed. Please see
  694.              DEBUGGER.DOC in \COMMENTS for some information about the
  695.              different debuggers and how to use ASMHELP with a debugger. You
  696.              should not try to do this before you are in chapter 5 or 6.
  697.  
  698.              You may have noticed that CHASM, an assembler distributed through
  699.              shareware, was not listed. There is a reason for this. It can't
  700.              produce .OBJ files, so it cannot produce the standard files for
  701.              use with current compilers, including QuickBASIC.  CHASM is also
  702.              unusable with this tutorial because it cannot produce files to
  703.              link with ASMHELP.OBJ, the i/o interface program. If you are
  704.              getting a shareware assembler, get A86. It's a quality assembler.
  705.  
  706.              This tutorial was originally written for those using MASM. In
  707.              order to allow those using the Turbo Assembler and A86 to follow
  708.              along, there is a document for each one in \COMMENTS which
  709.              explains any differences between what is in the chapters (which
  710.              use MASM as an example) and what the respective assemblers do.
  711.              There aren't that many differences. The pathnames are
  712.              \COMMENTS\TURBO.DOC and \COMMENTS\A86.DOC.
  713.  
  714.  
  715.  
  716.                                                                              1
  717.  
  718.                                    TABLE OF CONTENTS
  719.  
  720.  
  721.              Chapter 0.1 - Numbers And Arithmetic  . . . . . . . . . . .  i
  722.                  Base 10 Machine . . . . . . . . . . . . . . . . . . . .  i
  723.                  Negative Numbers  . . . . . . . . . . . . . . . . . . . ii
  724.                  10's Complement . . . . . . . . . . . . . . . . . . .  iii
  725.                  Addition  . . . . . . . . . . . . . . . . . . . . . . .  v
  726.                  Subtraction . . . . . . . . . . . . . . . . . . . . . .  v
  727.                  Modular Math  . . . . . . . . . . . . . . . . . . . . . vi
  728.                  Sign Extension  . . . . . . . . . . . . . . . . . . . . ix
  729.                  Overflow  . . . . . . . . . . . . . . . . . . . . . .  xii
  730.                  Multiplication  . . . . . . . . . . . . . . . . . . . xiii
  731.                  Division  . . . . . . . . . . . . . . . . . . . . . .  xiv
  732.  
  733.              Chapter 0.2 - Bases 2 And 16  . . . . . . . . . . . . . . . xv
  734.                  Base Conversion . . . . . . . . . . . . . . . . . . . . xv
  735.                  Binary Math . . . . . . . . . . . . . . . . . . . . .  xvi
  736.                  2's Complement  . . . . . . . . . . . . . . . . . . . xvii
  737.                  Sign Extension  . . . . . . . . . . . . . . . . . .  xviii
  738.  
  739.              Chapter 0.3 - Logic . . . . . . . . . . . . . . . . . . .  xxi
  740.                  AND . . . . . . . . . . . . . . . . . . . . . . . . .  xxi
  741.                  OR  . . . . . . . . . . . . . . . . . . . . . . . . . xxii
  742.                  XOR . . . . . . . . . . . . . . . . . . . . . . . . . xxii
  743.                  NOT . . . . . . . . . . . . . . . . . . . . . . . .  xxiii
  744.  
  745.              Chapter 0.4 - Memory  . . . . . . . . . . . . . . . . . .  xxv
  746.                  Segmentation  . . . . . . . . . . . . . . . . . . . .  xxv
  747.                  Numbers In Memory . . . . . . . . . . . . . . . . .  xxvii
  748.  
  749.              Chapter 0.5 - Style . . . . . . . . . . . . . . . . . . . xxix
  750.  
  751.  
  752.  
  753.              Chapter 1 - Some Simple Programs  . . . . . . . . . . . . .  1
  754.                  Label . . . . . . . . . . . . . . . . . . . . . . . . .  3
  755.                  CALL  . . . . . . . . . . . . . . . . . . . . . . . . .  3
  756.                  JMP . . . . . . . . . . . . . . . . . . . . . . . . . .  3
  757.  
  758.              Chapter 2 - Data  . . . . . . . . . . . . . . . . . . . . . 11
  759.                  DB, DW, DD, DQ, DT, DF  . . . . . . . . . . . . . . . . 11
  760.                  Definition of Constants . . . . . . . . . . . . . . . . 13
  761.  
  762.              Chapter 3 - Asmhelp . . . . . . . . . . . . . . . . . . . . 16
  763.                  Registers . . . . . . . . . . . . . . . . . . . . . . . 16
  764.                  Show_regs . . . . . . . . . . . . . . . . . . . . . . . 17
  765.                  MOV . . . . . . . . . . . . . . . . . . . . . . . . . . 19
  766.  
  767.              Chapter 4 - Show_regs . . . . . . . . . . . . . . . . . . . 24
  768.                  Show_reg Codes  . . . . . . . . . . . . . . . . . . . . 29
  769.  
  770.              Chapter 5 - Addition and Subtraction  . . . . . . . . . . . 31
  771.                  LOOP  . . . . . . . . . . . . . . . . . . . . . . . . . 31
  772.  
  773.  
  774.  
  775.              The PC Assembler Tutor                                          2
  776.              ______________________
  777.  
  778.                  OF, ZF, SF, CF  . . . . . . . . . . . . . . . . . . . . 32
  779.                  ADD . . . . . . . . . . . . . . . . . . . . . . . . . . 33
  780.                  PUSH  . . . . . . . . . . . . . . . . . . . . . . . . . 33
  781.                  POP . . . . . . . . . . . . . . . . . . . . . . . . . . 34
  782.                  SUB . . . . . . . . . . . . . . . . . . . . . . . . . . 37
  783.                  JC, JNC, JO, JNO  . . . . . . . . . . . . . . . . . . . 38
  784.                  INTO  . . . . . . . . . . . . . . . . . . . . . . . . . 39
  785.                  INTO.COM  . . . . . . . . . . . . . . . . . . . . . . . 39
  786.  
  787.              Chapter 6 - Multiplication and Division . . . . . . . . . . 41
  788.                  MUL . . . . . . . . . . . . . . . . . . . . . . . . . . 41
  789.                  IMUL  . . . . . . . . . . . . . . . . . . . . . . . . . 41
  790.                  DIV . . . . . . . . . . . . . . . . . . . . . . . . . . 44
  791.                  IDIV  . . . . . . . . . . . . . . . . . . . . . . . . . 44
  792.  
  793.              Chapter 7 - Logic . . . . . . . . . . . . . . . . . . . . . 47
  794.                  AND . . . . . . . . . . . . . . . . . . . . . . . . . . 47
  795.                  TEST  . . . . . . . . . . . . . . . . . . . . . . . . . 49
  796.                  OR  . . . . . . . . . . . . . . . . . . . . . . . . . . 50
  797.                  XOR . . . . . . . . . . . . . . . . . . . . . . . . . . 50
  798.                  NEG . . . . . . . . . . . . . . . . . . . . . . . . . . 51
  799.                  NOT . . . . . . . . . . . . . . . . . . . . . . . . . . 51
  800.                  Masks . . . . . . . . . . . . . . . . . . . . . . . . . 52
  801.  
  802.              Chapter 8 - Shift and Rotate  . . . . . . . . . . . . . . . 56
  803.                  SAL, SHL  . . . . . . . . . . . . . . . . . . . . . . . 56
  804.                  INC, DEC  . . . . . . . . . . . . . . . . . . . . . . . 57
  805.                  SHR . . . . . . . . . . . . . . . . . . . . . . . . . . 58
  806.                  SAR . . . . . . . . . . . . . . . . . . . . . . . . . . 59
  807.                  ROL, ROR  . . . . . . . . . . . . . . . . . . . . . . . 60
  808.                  RCL, RCR  . . . . . . . . . . . . . . . . . . . . . . . 61
  809.  
  810.              Chapter 9 - Jumps . . . . . . . . . . . . . . . . . . . . . 68
  811.                  CMP . . . . . . . . . . . . . . . . . . . . . . . . . . 68
  812.                  Signed and Unsigned Conditional Jumps . . . . . . . . . 70
  813.                  Flag Conditional Jumps  . . . . . . . . . . . . . . . . 75
  814.                  JCXZ  . . . . . . . . . . . . . . . . . . . . . . . . . 75
  815.  
  816.              Chapter 10 - Templates  . . . . . . . . . . . . . . . . . . 78
  817.                  .LST File . . . . . . . . . . . . . . . . . . . . . . . 78
  818.                  SEGMENTS  . . . . . . . . . . . . . . . . . . . . . . . 84
  819.                  PUBLIC (SEGMENTS) . . . . . . . . . . . . . . . . . . . 85
  820.                  CLASS . . . . . . . . . . . . . . . . . . . . . . . . 85ff
  821.                  ENDS  . . . . . . . . . . . . . . . . . . . . . . . . . 92
  822.                  ASSUME  . . . . . . . . . . . . . . . . . . . . . . . . 93
  823.                  Segment Overrides . . . . . . . . . . . . . . . . . . . 93
  824.                  Subroutines . . . . . . . . . . . . . . . . . . . . . . 96
  825.                  END . . . . . . . . . . . . . . . . . . . . . . . . . . 97
  826.                  RET . . . . . . . . . . . . . . . . . . . . . . . . . . 98
  827.                  EXTRN . . . . . . . . . . . . . . . . . . . . . . . . . 99
  828.                  STACK . . . . . . . . . . . . . . . . . . . . . . . .  101
  829.  
  830.              Chapter 11 - Addressing Modes . . . . . . . . . . . . . .  104
  831.                  EQU . . . . . . . . . . . . . . . . . . . . . . . . .  110
  832.                  All Addressing Modes  . . . . . . . . . . . . . . . .  114
  833.                  OFFSET  . . . . . . . . . . . . . . . . . . . . . . .  118
  834.                  SEG . . . . . . . . . . . . . . . . . . . . . . . . .  118
  835.  
  836.  
  837.              Table Of Contents                                               3
  838.              _________________
  839.  
  840.                  LEA . . . . . . . . . . . . . . . . . . . . . . . . .  118
  841.  
  842.              Chapter 12 - Multiple Word Arithmetic I . . . . . . . . .  122
  843.                  ADC . . . . . . . . . . . . . . . . . . . . . . . . .  123
  844.                  CLC . . . . . . . . . . . . . . . . . . . . . . . . .  124
  845.                  SBB . . . . . . . . . . . . . . . . . . . . . . . . .  126
  846.  
  847.              Chapter 13 - Multiple Word Arithmetic II  . . . . . . . .  129
  848.                  Unsigned Multiplication . . . . . . . . . . . . . . .  129
  849.                  Unsigned Division . . . . . . . . . . . . . . . . . .  130
  850.  
  851.              Chapter 14 - Zoom . . . . . . . . . . . . . . . . . . . .  134
  852.  
  853.              Chapter 15 - Subroutines  . . . . . . . . . . . . . . . .  137
  854.                  PUSHREGS.MAC  . . . . . . . . . . . . . . . . . . . .  137
  855.                  EXTRN in Subroutines  . . . . . . . . . . . . . . . .  142
  856.                  Passing Data  . . . . . . . . . . . . . . . . . . . .  144
  857.                  Near and Far Procedures . . . . . . . . . . . . . . .  145
  858.                  The Stack . . . . . . . . . . . . . . . . . . . . . .  148
  859.                  Types of Returns  . . . . . . . . . . . . . . . . . .  153
  860.                  PUSHREGS  . . . . . . . . . . . . . . . . . . . . . .  154
  861.                  POPREGS . . . . . . . . . . . . . . . . . . . . . . .  154
  862.                  LDS . . . . . . . . . . . . . . . . . . . . . . . . .  157
  863.                  LES . . . . . . . . . . . . . . . . . . . . . . . . .  157
  864.                  Towers of Hanoi . . . . . . . . . . . . . . . . . . .  162
  865.                  Summary . . . . . . . . . . . . . . . . . . . . . . .  166
  866.  
  867.              Chapter 16 - Long Signed Multiplication And Division  . .  170
  868.                  Long Negation . . . . . . . . . . . . . . . . . . . .  170
  869.  
  870.              Chapter 17 - Interrupts . . . . . . . . . . . . . . . . .  177
  871.                  INT . . . . . . . . . . . . . . . . . . . . . . . . .  177
  872.                  NMI . . . . . . . . . . . . . . . . . . . . . . . . .  181
  873.                  IEF . . . . . . . . . . . . . . . . . . . . . . . . .  181
  874.                  STI . . . . . . . . . . . . . . . . . . . . . . . . .  181
  875.                  CLI . . . . . . . . . . . . . . . . . . . . . . . . .  181
  876.                  INT 3 . . . . . . . . . . . . . . . . . . . . . . . .  182
  877.  
  878.              Chapter 18 - Ports  . . . . . . . . . . . . . . . . . . .  185
  879.                  IN  . . . . . . . . . . . . . . . . . . . . . . . . .  185
  880.                  OUT . . . . . . . . . . . . . . . . . . . . . . . . .  186
  881.                  Parity  . . . . . . . . . . . . . . . . . . . . . . .  186
  882.  
  883.              Chapter 19 - Strings  . . . . . . . . . . . . . . . . . .  191
  884.                  SCAS  . . . . . . . . . . . . . . . . . . . . . . . .  191
  885.                  DF  . . . . . . . . . . . . . . . . . . . . . . . . .  191
  886.                  REP/REPE/REPNE  . . . . . . . . . . . . . . . . . . .  195
  887.                  STOS  . . . . . . . . . . . . . . . . . . . . . . . .  196
  888.                  LODS  . . . . . . . . . . . . . . . . . . . . . . . .  198
  889.                  MOVS  . . . . . . . . . . . . . . . . . . . . . . . .  199
  890.                  CMPS  . . . . . . . . . . . . . . . . . . . . . . . .  204
  891.                  Segment Overrides . . . . . . . . . . . . . . . . . .  207
  892.                  REP and Overrides . . . . . . . . . . . . . . . . . .  209
  893.  
  894.              Chapter 20 - Control Structures . . . . . . . . . . . . .  212
  895.                  IF  . . . . . . . . . . . . . . . . . . . . . . . . .  212
  896.                  WHILE . . . . . . . . . . . . . . . . . . . . . . . .  214
  897.  
  898.  
  899.              The PC Assembler Tutor                                          4
  900.              ______________________
  901.  
  902.                  DO-WHILE  . . . . . . . . . . . . . . . . . . . . . .  215
  903.                  BREAK . . . . . . . . . . . . . . . . . . . . . . . .  215
  904.                  CONTINUE  . . . . . . . . . . . . . . . . . . . . . .  215
  905.                  FOR . . . . . . . . . . . . . . . . . . . . . . . . .  216
  906.                  SWITCH  . . . . . . . . . . . . . . . . . . . . . . .  217
  907.  
  908.              Chapter 21 - .COM Files . . . . . . . . . . . . . . . . .  219
  909.                  .COM Template . . . . . . . . . . . . . . . . . . . .  219
  910.                  PSP . . . . . . . . . . . . . . . . . . . . . . . . .  220
  911.                  ASSUME  . . . . . . . . . . . . . . . . . . . . . . .  221
  912.                  Phase Errors  . . . . . . . . . . . . . . . . . . . .  221
  913.  
  914.              Chapter 22 - BCD Numbers  . . . . . . . . . . . . . . . .  229
  915.                  Unpacked BCD  . . . . . . . . . . . . . . . . . . . .  229
  916.                  Packed BCD  . . . . . . . . . . . . . . . . . . . . .  230
  917.                  DAA . . . . . . . . . . . . . . . . . . . . . . . . .  232
  918.                  DAS . . . . . . . . . . . . . . . . . . . . . . . . .  233
  919.                  AAA . . . . . . . . . . . . . . . . . . . . . . . . .  240
  920.                  AAS . . . . . . . . . . . . . . . . . . . . . . . . .  242
  921.                  AAM . . . . . . . . . . . . . . . . . . . . . . . . .  242
  922.                  AAD . . . . . . . . . . . . . . . . . . . . . . . . .  243
  923.                  Unpacking . . . . . . . . . . . . . . . . . . . . . .  245
  924.                  Packing . . . . . . . . . . . . . . . . . . . . . . .  246
  925.  
  926.              Chapter 23 - XLAT . . . . . . . . . . . . . . . . . . . .  253
  927.                  EBCDIC Numbers  . . . . . . . . . . . . . . . . . . .  253
  928.                  XLAT  . . . . . . . . . . . . . . . . . . . . . . . .  253
  929.                  Translation Table . . . . . . . . . . . . . . . . . .  253
  930.  
  931.              Chapter 24 - Miscellaneous Instructions . . . . . . . . .  264
  932.                  XCHG  . . . . . . . . . . . . . . . . . . . . . . . .  264
  933.                  ESC . . . . . . . . . . . . . . . . . . . . . . . . .  264
  934.                  WAIT  . . . . . . . . . . . . . . . . . . . . . . . .  264
  935.                  FWAIT . . . . . . . . . . . . . . . . . . . . . . . .  264
  936.                  LOCK  . . . . . . . . . . . . . . . . . . . . . . . .  265
  937.                  LOOPE/LOOPNE  . . . . . . . . . . . . . . . . . . . .  265
  938.                  HALT  . . . . . . . . . . . . . . . . . . . . . . . .  266
  939.                  CMC . . . . . . . . . . . . . . . . . . . . . . . . .  266
  940.                  LAHF  . . . . . . . . . . . . . . . . . . . . . . . .  266
  941.                  SAHF  . . . . . . . . . . . . . . . . . . . . . . . .  267
  942.                  NOP . . . . . . . . . . . . . . . . . . . . . . . . .  267
  943.  
  944.              Chapter 25 - What Does It All Mean? . . . . . . . . . . .  268
  945.                  Interrupts  . . . . . . . . . . . . . . . . . . . . .  269
  946.                  Data Bus  . . . . . . . . . . . . . . . . . . . . . .  273
  947.                  Alignment Type  . . . . . . . . . . . . . . . . . . .  274
  948.  
  949.              Chapter 26 - Simplifying The Template . . . . . . . . . .  276
  950.                  INT 21h Function 4Ch  . . . . . . . . . . . . . . . .  276
  951.                  Exit Code . . . . . . . . . . . . . . . . . . . . . .  276
  952.                  Standardized Segments . . . . . . . . . . . . . . . .  277
  953.                  _DATA . . . . . . . . . . . . . . . . . . . . . . . .  279
  954.                  _BSS  . . . . . . . . . . . . . . . . . . . . . . . .  279
  955.                  CONST . . . . . . . . . . . . . . . . . . . . . . . .  280
  956.                  Literals  . . . . . . . . . . . . . . . . . . . . . .  280
  957.                  STACK . . . . . . . . . . . . . . . . . . . . . . . .  280
  958.                  Groups  . . . . . . . . . . . . . . . . . . . . . . .  280
  959.  
  960.  
  961.              Table Of Contents                                               5
  962.              _________________
  963.  
  964.                  DGROUP  . . . . . . . . . . . . . . . . . . . . . . .  281
  965.                  Groups and OFFSET . . . . . . . . . . . . . . . . . .  284
  966.                  Standardized Segment Names  . . . . . . . . . . . . .  287
  967.                  Standardized Segment Directives . . . . . . . . . . .  288
  968.                  .MODEL Names  . . . . . . . . . . . . . . . . . . . .  291
  969.                  Summary . . . . . . . . . . . . . . . . . . . . . . .  293
  970.  
  971.              APPENDIX
  972.  
  973.              Appendix I - The PC Assembler Helper  . . . . . . . . . . .  i
  974.              Appendix II - The 8086 Instruction Set  . . . . . . . . . xiii
  975.              Appendix III - Instruction Speed And Flags  . . . . . .  xxvii
  976.  
  977.              ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  978.  
  979.                                 ANCILLARY MATERIAL
  980.  
  981.  
  982.              DEBUGGERS (DEBUGGER.DOC)
  983.              TASM (TASM.DOC)
  984.              A86 (A86.DOC)
  985.  
  986.  
  987.  
  988.              BASIC (BASIC1.DOC, BASIC2-1.DOC, BASIC2-2.DOC)
  989.              Using Basic . . . . . . . . . . . . . . . . . . . . . . .  mmi
  990.                  Variable Typing . . . . . . . . . . . . . . . . . . .  mmi
  991.                  Default Type  . . . . . . . . . . . . . . . . . . . . mmii
  992.  
  993.              Interfacing Basic With Assembler  . . . . . . . . . . .  mmvii
  994.                  Memory Allocation . . . . . . . . . . . . . . . . .  mmvii
  995.                  String Allocation . . . . . . . . . . . . . . . . . mmviii
  996.                  Data Output . . . . . . . . . . . . . . . . . . . . .  mmx
  997.                  FIELD . . . . . . . . . . . . . . . . . . . . . . .  mmxii
  998.                  LSET/MID$/RSET  . . . . . . . . . . . . . . . . . . mmxiii
  999.                  MKI$, MKL$, MKS$, MKD$  . . . . . . . . . . . . . . mmxiii
  1000.                  CVI, CVL, CVS, CVD  . . . . . . . . . . . . . . . . mmxiii
  1001.                  STR$, VAL . . . . . . . . . . . . . . . . . . . . .  mmxiv
  1002.                  VARPTR  . . . . . . . . . . . . . . . . . . . . . .  mmxvi
  1003.                  PTR86 (Basic 3.0) . . . . . . . . . . . . . . . . . mmxvii
  1004.                  BUILDLIB.EXE  . . . . . . . . . . . . . . . . . . . mmxvii
  1005.                  VARSEG (Basic 4.0)  . . . . . . . . . . . . . . .  mmxviii
  1006.                  Basic Calling Conventions . . . . . . . . . . . . .  mmxix
  1007.                  INT86 . . . . . . . . . . . . . . . . . . . . . .  mxxviii
  1008.                  SADD  . . . . . . . . . . . . . . . . . . . . . . . mmxxix
  1009.                  BLOAD . . . . . . . . . . . . . . . . . . . . . . .  mmxxx
  1010.                  Summary . . . . . . . . . . . . . . . . . . . . . . mmxxxi
  1011.                  Interfacing Basic With Assembler  . . . . . . . . .  mmvii
  1012.  
  1013.  
  1014.              Ancillary Programs (MISHMASH.DOC)
  1015.                  General Block Move  . . . . . . . . . . . . . . . . . .  1
  1016.                  Block Multiplication  . . . . . . . . . . . . . . . . .  3
  1017.                  Binary Multiplication . . . . . . . . . . . . . . . . .  6
  1018.                  Binary Division . . . . . . . . . . . . . . . . . . . .  9
  1019. Chapter 0.1 - Numbers and Arithmetic
  1020. ====================================
  1021.                                                                              i
  1022.  
  1023.  
  1024.  
  1025.              You don't habitually use the base two system to balance your
  1026.              checkbook, so it would be counterproductive to teach you machine
  1027.              arithmetic on a base two system. What number systems have you had
  1028.              a lot of experience with? The base 10 system springs to mind. I'm
  1029.              going to show you what happens on a base 10 system so you will
  1030.              understand the structure of what happens with computer
  1031.              arithmetic.
  1032.  
  1033.  
  1034.              BASE 10 MACHINE
  1035.  
  1036.              Each place inside the microprocessor that can hold a number is
  1037.              called a REGISTER. Normally there are a dozen or so of these. Our
  1038.              base 10 machine has 4 digit registers.  They can represent any
  1039.              number from 0000 to 9999. They are exactly like an industrial
  1040.              counters or the counters on your tape machines.{1} If you add 27
  1041.              to a register, the microprocessor counts forward 27; if you
  1042.              subtract 153 from a register, the microprocessor counts backwards
  1043.              153.   Every time you add 1 to a register, it increments by 1 -
  1044.              that is 0245, 0246, 0247, 0248. Every time you subtract 1 from a
  1045.              register, it decrements by 1 - that is 3480, 3479, 3478, 3477.
  1046.  
  1047.              Let's do some more incrementing.  9997, 9998, 9999, 0000, 0001,
  1048.              0002. Whoops! That's a problem. When the register reaches 9999
  1049.              and we add 1, it changes to 0000, not 10,000. How can we tell the
  1050.              difference between 0000 and 10,000? We can't without a little
  1051.              help from the CPU.{2}  Immediately after an arithmetical
  1052.              operation, the CPU knows whether you have gone through 10,000
  1053.              (9999->0000). The CPU has something called a carry flag. It is
  1054.              internal to the CPU and can have the value 0 or 1. After each
  1055.              arithmetical operation, the CPU sets the CARRY FLAG to 1 if you
  1056.              went through the 9999/0000 boundary, and sets the carry flag to 0
  1057.              if you didn't.{3}
  1058.  
  1059.              Here are some examples, showing addition, the result, and the
  1060.              carry flag. The carry flag is normally abbreviated by CF.
  1061.  
  1062.                     number 1       number 2        result     CF
  1063.  
  1064.                       0289           4782           5071      0
  1065.                       4398           2964           7382      0
  1066.                       8177           5826           4003      1
  1067.              ____________________
  1068.  
  1069.                 1. Exactly like industrial counters that have several hundred
  1070.              thousand parts, that is.
  1071.  
  1072.                 2. The CPU (central processing unit) is the chip(s) that does
  1073.              all the arithmetic. In the case of the PC, it is the 8086.
  1074.  
  1075.                 3. When you set a flag to 0, it is called CLEARING the flag.
  1076.  
  1077.  
  1078.  
  1079.              The PC Assembler Tutor                                         ii
  1080.              ______________________
  1081.  
  1082.                       6744           4208           0952      1
  1083.  
  1084.              Note that you must check the carry flag immediately after the
  1085.              arithmetical operation. If you wait, the CPU will reset it after
  1086.              the next arithmetical operation.
  1087.  
  1088.              Now let's do some decrementing. 0003, 0002, 0001, 0000, 9999,
  1089.              9998. Golly gosh! Another problem. When we got to 0000, rather
  1090.              than getting -1, -2, we got 9999, 9998. Apparently 9999 stands
  1091.              for -1, 9998 stands for -2. Yes, that's the system on this, on
  1092.              the 8086, and on all computers. (Back to that in a moment.) How
  1093.              do we tell that the number went through 0 ; i.e. 0000->9999? The
  1094.              carry flag comes to the rescue again. If the number goes through
  1095.              the 9999/0000 boundary in either direction, the CPU sets the CF
  1096.              to 1; if it doesn't, the CPU sets the CF to 0. Here's some
  1097.              subtraction, with the result and the carry flag.
  1098.  
  1099.                     number 1       number 2       result     CF
  1100.  
  1101.                       8473           2752           5721      0
  1102.                       2836           4583           1747      1
  1103.                       0654           9281           8627      1
  1104.                       9281           0654           8627      0
  1105.  
  1106.              Look at examples 3 and 4. The numbers are reversed. The results
  1107.              are the same but they have different signs. But that is as it
  1108.              should be. When you reverse the order in a subtraction, you get
  1109.              the same absolute value, only a different sign (15 - 7 = 8 but
  1110.              7 - 15 = -8). Remember, the CF is reliable only immediately after
  1111.              the operation.
  1112.  
  1113.  
  1114.              NEGATIVE NUMBERS
  1115.  
  1116.              The negative numbers go 9999=-1, 9998=-2, 9997=-3, 9996=-4,
  1117.              9995=-5 etc. A more negative number is denoted by a smaller
  1118.              number in the register; -5 = 10,000 -5 = 9995; -498 = 10,000 -498
  1119.              = 9502, and in general, -x = 10,000 -x. Here are some negative
  1120.              numbers and their representations on our machine.
  1121.  
  1122.                      number     machine no              number     machine no
  1123.  
  1124.                         -27          9973                -4652          5348
  1125.                       -8916          1084                -6155          3845
  1126.  
  1127.              As you will notice, these numbers look exactly the same as the
  1128.              unsigned numbers. They ARE exactly the same as the unsigned
  1129.              numbers. The machine has no way of knowing whether a number in a
  1130.              register is signed or unsigned. Unlike BASIC or PASCAL which will
  1131.              complain whenever you try to use a number in an incorrect way,
  1132.              the machine will let you do it. This is the power and the curse
  1133.              of machine language. You are in complete control. It is your
  1134.              responsibility to keep track of whether a number is signed or
  1135.              unsigned.
  1136.  
  1137.              Which signed numbers should be positive and which negative? This
  1138.              has already been decided for you by the computer, but let's think
  1139.  
  1140.  
  1141.              Chapter 0.1 - Numbers and Arithmetic                          iii
  1142.              ____________________________________
  1143.  
  1144.              out what a reasonable solution might be. We could have from 0000
  1145.              to 8000 positive and from 9999 to 8001 negative, but that would
  1146.              give us 8001 positive numbers and 1999 negative numbers. That
  1147.              seems unbalanced. More importantly, if we take -(3279) the
  1148.              machine will give us 6721, which is a POSITIVE number. We don't
  1149.              want that. For reasons of symmetry, the positive numbers are
  1150.              0000-4999 and the negative numbers are 9999-5000.{4} Our most
  1151.              negative number is -5000 = 10,000 -5000 = 5000.
  1152.  
  1153.  
  1154.              10'S COMPLEMENT
  1155.  
  1156.              It's time for a digression. If we are going to be using negative
  1157.              numbers like -(473), changing from an external number to an
  1158.              internal number is going to be a bother: i.e. -473 -> 9527. Going
  1159.              the other way is going to be a pain too: i.e. 9527 -> -473. Well,
  1160.              it would be a problem except that we have some help.
  1161.  
  1162.                  0000 =    10,000    =     9999     +1
  1163.                                           - 473
  1164.              result                        9526     +1   = 9527
  1165.  
  1166.              Let's work this through carefully. On our machine, 0000  and
  1167.              10000 (9999+1) are the same thing, so 0 - 473 is the same as
  1168.              9999+1-473 which is the same as 9999-473+1. But when we have all
  1169.              9s, this is a cinch. We never have to borrow - all we have to do
  1170.              is subtract each digit from 9 and then add 1 to the total. We may
  1171.              have to carry at the end, but that is a lot better than all those
  1172.              borrows. We'll do a few examples:
  1173.  
  1174.              (-4276)
  1175.                  0000 =    10,000    =     9999     +1
  1176.                                           -4276
  1177.              result                        5723     +1   = 5724
  1178.  
  1179.  
  1180.              (-3982)
  1181.                  0000 =    10,000    =     9999     +1
  1182.                                           -3982
  1183.              result                        6017     +1   = 6018
  1184.  
  1185.  
  1186.              (-2400)
  1187.                  0000 =    10,000    =     9999     +1
  1188.                                           -2400
  1189.              result                        7599     +1   = 7600
  1190.  
  1191.  
  1192.              (-1989)
  1193.                  0000 =    10,000    =     9999     +1
  1194.              ____________________
  1195.  
  1196.                 4. That way, if we tell the machine that we are working with
  1197.              signed numbers, all it has to do is look at the left digit. If
  1198.              the digit is 5-9, we have a negative number, if it is 0-4, we
  1199.              have a positive number. Note that 0000 is considered to be
  1200.              positive. This is true on all computers.
  1201.  
  1202.  
  1203.              The PC Assembler Tutor                                         iv
  1204.              ______________________
  1205.  
  1206.                                           -1989
  1207.              result                        8010     +1   = 8011
  1208.  
  1209.              This is called 10s complement. Subtract each digit from 9, then
  1210.              add 1 to the total. One thing we should check is whether we get
  1211.              the same number back if we negate the negative result; i.e. does
  1212.              -(-1989)) = 1989?  From the last example, we see that -1989 =
  1213.              8011, so:
  1214.  
  1215.              (-8011)
  1216.                  0000 =    10,000    =     9999     +1
  1217.                                           -8011
  1218.              result                        1988     +1   = 1989
  1219.  
  1220.              It seems to work. In fact, it always works. See the footnote for
  1221.              the proof.{5} You are going to use this from time to time, so you
  1222.              might as well practice some. Here are 10 numbers to put into 10s
  1223.              complement form. The answers are in the footnote. (1) -628, (2)
  1224.              -4194, (3) -9983, (4) -1288, (5) -4058, (6) -6952, (7) -162, (8)
  1225.              -9, (9) -2744, (10) -5000.{6}
  1226.  
  1227.  
  1228.              The computer keeps track of whether a number is positive or
  1229.              negative. After an arithmetical operation, it sets a flag to tell
  1230.              whether the result is positive or negative. This flag has no
  1231.              meaning if you are using unsigned numbers. The computer is
  1232.              saying, "If the last arithmetical operation was with signed
  1233.              numbers, then this is the sign of the result." The flag is called
  1234.              the sign flag (SF). It is 0 if the number is positive and 1 if
  1235.              the number is negative. Let's decrement again and look at both
  1236.              the sign flag and carry flag.
  1237.  
  1238.                         NUMBER    SIGN     CARRY
  1239.  
  1240.                            3         0         0
  1241.                            2         0         0
  1242.                            1         0         0
  1243.                            0         0         0
  1244.                         9999         1         1
  1245.              ____________________
  1246.  
  1247.                 5. Let x be any number. Then:
  1248.              -x     = ( 10,000 - x)     = ( 9999 + 1 - x ) ;
  1249.  
  1250.              -(-x)  = ( 10,000 - (-x) ) = ( 9999 + 1 - (-x) )
  1251.                                        = ( 9999 + 1 - ( 9999 + 1 - x ) )
  1252.                                        = ( 9999 + 1 - 9999 - 1 + x )
  1253.                                        = x
  1254.  
  1255.                 6.   (1) -628 = 9372 , (2) -4194 = 5806 , (3) -9983 = 0017,
  1256.                      (4) -1288 = 8712 , (5) -4058 = 5942 , (6) -6952 = 3048
  1257.                      (7) -162 = 9838 , (8) -9 = 9991 , (9) -2744 = 7256,
  1258.                      (10) -5000 = 5000. This last one is a little strange. It
  1259.              changes 5000 into itself. In our system, 5000 is a negative
  1260.              number and it winds up as a negative number. This happens on all
  1261.              computers. If you take the maximum negative number and take its
  1262.              negative, you get the same number back.
  1263.  
  1264.  
  1265.              Chapter 0.1 - Numbers and Arithmetic                            v
  1266.              ____________________________________
  1267.  
  1268.                         9998         1         0
  1269.                         9997         1         0
  1270.                         9996         1         0
  1271.  
  1272.              That worked pretty well. The sign flag changed from 0 to 1 when
  1273.              we went from 0 to 9999 and the carry flag was set to 1 for that
  1274.              one operation so we could see that we had gone through the
  1275.              9999/0000 boundary.
  1276.  
  1277.              Let's do some more decrementing.
  1278.  
  1279.                         NUMBER    SIGN     CARRY
  1280.  
  1281.                         5003         1         0
  1282.                         5002         1         0
  1283.                         5001         1         0
  1284.                         5000         1         0
  1285.                         4999         0         0
  1286.                         4998         0         0
  1287.                         4997         0         0
  1288.                         4996         0         0
  1289.  
  1290.              This one didn't work too well. 5000 is our most negative number
  1291.              (-5000) and 4999 is our most positive number; when we crossed the
  1292.              4999/5000 boundary, the sign changed but there was nothing to
  1293.              tell us that the sign had changed. We need to make another flag.
  1294.              This one is called the overflow flag. We check the carry flag
  1295.              (CF) for the 0000/9999 boundary and we check the overflow flag
  1296.              for the 5000/4999 boundary. The last decrementing example with
  1297.              the overflow flag:
  1298.  
  1299.                         NUMBER    SIGN     CARRY     OVERFLOW
  1300.  
  1301.                         5003         1         0         0
  1302.                         5002         1         0         0
  1303.                         5001         1         0         0
  1304.                         5000         1         0         0
  1305.                         4999         0         0         1
  1306.                         4998         0         0         0
  1307.                         4997         0         0         0
  1308.                         4996         0         0         0
  1309.  
  1310.              This time we can find out that we have gone through the boundary.
  1311.              We'll come back to how the computer sets the overflow flag later,
  1312.              but let's do some addition and subtraction now.
  1313.  
  1314.  
  1315.              UNSIGNED ADDITION AND SUBTRACTION
  1316.  
  1317.              Unsigned addition is done the same way as normally. The computer
  1318.              adds the two numbers. If the result is over 9999, it sets the
  1319.              carry flag and drops the left digit (i.e. 14625 -> 4625, CF = 1,
  1320.              19137 -> 9137 CF = 1, 10000 -> 0000 CF = 1). The largest possible
  1321.              addition is 9999 + 9999 = 19998. This still has a 1 in the left
  1322.              digit. If the carry flag is set after an addition, the result
  1323.              must be between 10000 and 19998.
  1324.  
  1325.  
  1326.  
  1327.              The PC Assembler Tutor                                         vi
  1328.              ______________________
  1329.  
  1330.              Since this is unsigned addition, we won't worry about the sign
  1331.              flag or the overflow flag for the moment. Here are some examples
  1332.              of unsigned addition.
  1333.  
  1334.                       NUMBER 1       NUMBER 2       RESULT         CF
  1335.  
  1336.                         5147           2834          7981           0
  1337.                         6421           8888          5309           1
  1338.                         2910           6544          9454           0
  1339.                         6200           6321          2521           1
  1340.  
  1341.              Directly after the addition, the computer has complete
  1342.              information about the number. If the carry flag is set, that
  1343.              means that there is an extra 10,000, so the result of the second
  1344.              example is 15309 and the result of the fourth example is 12521.
  1345.              There is no way to store all that information in 4 digits in
  1346.              memory so that extra information will be lost if it is not used
  1347.              immediately.
  1348.  
  1349.              Subtraction is similar. The machine subtracts, and if the answer
  1350.              is below 0000, it sets the carry flag, borrows 10000 and adds it
  1351.              to the result. -3158 -> -3135 + 10000 -> 6842 CF = 1 ; -8197 ->
  1352.              -8197 + 10000 -> 1803  CF = 1. After a subtraction, if the carry
  1353.              flag is set, you know the number is 10000 too big. Once again,
  1354.              the carry flag information must be used immediately or it will be
  1355.              lost. Here are some examples:
  1356.  
  1357.                       NUMBER 1       NUMBER 2       RESULT         CF
  1358.  
  1359.                         3872           2655          1217           0
  1360.                         9826           5967          3859           0
  1361.                         4561           7143          7418           1
  1362.                         2341           4907          7434           1
  1363.  
  1364.              If the carry flag is set, the computer borrowed 10000, so example
  1365.              3 is 7418 - 10000 = -2582 and example 4 is 7434 - 10000 = -2566.
  1366.  
  1367.  
  1368.              MODULAR ARITHMETIC
  1369.  
  1370.              What the computer is doing is modular arithmetic. Modular
  1371.              arithmetic is like a clock. If it is 11 o'clock and you go
  1372.              forward 1 hour it's now 12 o'clock; if it's 11 and you go
  1373.              backwards 1 hour it's now 10. If it's 11 and you go forward 4
  1374.              hours it's not 15, it's 3. If it's 11 and you go backward 15
  1375.              hours it's not -4, it's 8.
  1376.  
  1377.              The clock is doing  mod 12  arithmetic.{7}
  1378.  
  1379.                  (A+B) mod 12
  1380.                  (A-B) mod 12
  1381.  
  1382.              From the clock's viewpoint, 11 o'clock today, 11 o'clock
  1383.              yesterday and 11 o'clock, June 8, 1754 are all the same thing. If
  1384.              ____________________
  1385.  
  1386.                 7. To be a perfect analogy 12 o'clock should be 0 o'clock.
  1387.  
  1388.  
  1389.              Chapter 0.1 - Numbers and Arithmetic                          vii
  1390.              ____________________________________
  1391.  
  1392.              you go forward 200 hours (that's 12X16 + 8) you will have the
  1393.              same result as going forward 8 hours. If you go backwards 200
  1394.              hours (that's -(12X16 + 8) = -(12X16) -8) you get the same result
  1395.              as going backwards 8 hours. If you go forward 4 hours from 11
  1396.              (11+4) mod 12 = 3 you get the same result as going backwards 8
  1397.              hours (11-8) mod 12 = 3. In fact, these come in pairs. If A + B =
  1398.              12, then going forward A hours gives the same result as going
  1399.              backwards B hours. Forwards 9 = backwards 3; forwards 7 =
  1400.              backwards 5; forwards 11 = backwards 1.
  1401.  
  1402.              In the mod 12 system, the following things are equivalent:
  1403.  
  1404.                  (+72 + 4)      (+72 - 8)
  1405.                  (+60 + 4)      (+60 - 8)
  1406.                  (+48 + 4)      (+48 - 8)
  1407.                  (+36 + 4)      (+36 - 8)
  1408.                  (+24 + 4)      (+24 - 8)
  1409.                  (+12 + 4)      (+12 - 8)
  1410.                  (  0 + 4)      (  0 - 8)
  1411.                  (-12 + 4)      (-12 - 8)
  1412.                  (-24 + 4)      (-24 - 8)
  1413.                  (-36 + 4)      (-36 - 8)
  1414.                  (-48 + 4)      (-48 - 8)
  1415.                  (-60 + 4)      (-60 - 8)
  1416.  
  1417.              They form what is known as an equivalence class mod 12. If you
  1418.              use any one of them for addition or subtraction, you will get the
  1419.              same result (mod 12) as with any other one. Here's some
  1420.              addition:{8}
  1421.  
  1422.                  (+48 + 4) + 7 = (48 + 11) mod 12 = 11
  1423.                  (-48 - 8) + 7 = (48 - 1 ) mod 12 = 11
  1424.                  (  0 - 8) + 7 = ( 0 - 1 ) mod 12 = 11
  1425.                  (-60 + 4) + 7 = (-60 +11) mod 12 = 11
  1426.  
  1427.              And some subtraction:
  1428.  
  1429.                  (+48 + 4) - 2 = (48 + 2 ) mod 12 = 2
  1430.                  (-48 - 8) - 2 = (48 - 10) mod 12 = 2
  1431.                  (  0 - 8) - 2 = ( 0 - 10) mod 12 = 2
  1432.                  (-60 + 4) - 2 = (-60 + 2) mod 12 = 2
  1433.  
  1434.  
  1435.              Our pretend computer doesn't cycle every 12 numbers, it cycles
  1436.              every 10,000 numbers - it is a mod 10,000 machine. On our
  1437.              machine, the number 6453 has the following equivalence class:
  1438.  
  1439.                  (+30000 + 6453)               (+30000 - 3547)
  1440.                  (+20000 + 6453)               (+20000 - 3547)
  1441.                  (+10000 + 6453)               (+10000 - 3547)
  1442.                  (     0 + 6453)               (     0 - 3547)
  1443.                  (-10000 + 6453)               (-10000 - 3547)
  1444.                  (-20000 + 6453)               (-20000 - 3547)
  1445.                  (-30000 + 6453)               (-30000 - 3547)
  1446.              ____________________
  1447.  
  1448.                 8. (-10) mod 12 = 2 ;   (-11) mod 12 = 1
  1449.  
  1450.  
  1451.              The PC Assembler Tutor                                       viii
  1452.              ______________________
  1453.  
  1454.  
  1455.              Any one of these will act the same as any other one. Notice that
  1456.              10000 - 3547 is the subtraction that we did to get the
  1457.              representation of -3547 on the machine.
  1458.  
  1459.              -3547    = 9999 + 1
  1460.                         3547
  1461.                         6452 + 1 = 6453
  1462.  
  1463.              6453 and -3547 act EXACTLY the same on this machine. What this
  1464.              means is that there is no difference in adding signed or unsigned
  1465.              numbers on the machine. The result will be correct if interpreted
  1466.              as an unsigned number; it will also be correct if interpreted as
  1467.              a signed number.
  1468.  
  1469.                  6821 + 3179 = 10000  so  -3179 = 6821   and  3179 = -6821
  1470.                  5429 + 4571 = 10000  so  -4571 = 5429   and  4571 = -5429
  1471.  
  1472.              Since -3179 and 6821 act the same on our machine and since -4571
  1473.              and 5429 act the same, let's do some addition. Take your time so
  1474.              you understand why the signed and unsigned numbers are giving the
  1475.              same results mod 10000:
  1476.  
  1477.                   ---------------------------------------------------------
  1478.                   6821 + 497 = 7318
  1479.                  -3179 + 497 = (10000 - 3179) + 497 = 10000 -2682  = -2682
  1480.  
  1481.                   7318 + 2682 = 10000      so    -2682 = 7318
  1482.  
  1483.                   ----------------------------------------------------------
  1484.  
  1485.                   5429 + 876 = 6305
  1486.                  -4571 + 876 = (10000 - 4571) + 876 = 10000 - 3695 = -3695
  1487.  
  1488.                   6305 + 3695 = 10000      so    -3695 = 6305
  1489.  
  1490.                   ----------------------------------------------------------
  1491.  
  1492.              Here's some subtraction:
  1493.  
  1494.                   -----------------------------------------------------------
  1495.  
  1496.                   6821 - 507 = 6314
  1497.                  -3179 - 507 = (10000 - 3179) - 507 = 10000 - 3686 = -3686
  1498.  
  1499.                   6314 + 3686 = 10000     so     -3686 = 6314
  1500.  
  1501.                   ----------------------------------------------------------
  1502.  
  1503.                   5429 - 178 = 5251
  1504.                  -4571 - 178 = (10000 - 4571) - 178 = 10000 - 4749 = -4749
  1505.  
  1506.                   5251 + 4749 = 10000    so      -4749 = 5251
  1507.  
  1508.                   -----------------------------------------------------------
  1509.  
  1510.              It is the same addition or subtraction. Interpreted one way it is
  1511.  
  1512.  
  1513.              Chapter 0.1 - Numbers and Arithmetic                           ix
  1514.              ____________________________________
  1515.  
  1516.              signed addition or subtraction; interpreted another way it is
  1517.              unsigned addition or subtraction.
  1518.  
  1519.              The machine could have one operation for signed addition and
  1520.              another operation for unsigned addition, but this would be a
  1521.              waste of computer resources. These operations are exactly the
  1522.              same. This machine, like all computers, has only one integer
  1523.              addition operation and one integer subtraction operation. For
  1524.              each operation, it sets the flags of importance for both signed
  1525.              and unsigned arithmetic.
  1526.  
  1527.              For unsigned addition and subtraction, CF, the carry flag tells
  1528.              whether the 0000/9999 boundary has been crossed.
  1529.  
  1530.              For signed addition and subtraction, SF, the sign flag tells the
  1531.              sign of the result and OF, the overflow flag tells whether the
  1532.              result was too negative or too positive.
  1533.  
  1534.  
  1535.              SIGN EXTENSION
  1536.  
  1537.              Although our base 10 machine is set up for 4 digit numbers, it is
  1538.              possible to use it for numbers of any size by writing the
  1539.              appropriate software. We'll use 12 digit numbers as an example,
  1540.              though they could be of any length. The first problem is
  1541.              converting 4 digit numbers into 12 digit numbers. If the number
  1542.              is an unsigned number, this is no problem (we'll write the number
  1543.              in groups of 4 digits to keep it readable):
  1544.  
  1545.                  4816      ->   0000 0000 4816
  1546.                  9842      ->   0000 0000 9842
  1547.                   127      ->   0000 0000 0127
  1548.  
  1549.              what if it is a signed number? The first thing we need to know
  1550.              about signed numbers is, what is positive and what is negative?
  1551.              Once again, for reasons of symmetry, we choose positive to be
  1552.              0000 0000 0000  to  4999 9999 9999 and negative to be 5000 0000
  1553.              0000 to 9999 9999 9999.{9}  This longer number system cycles from
  1554.  
  1555.              9999 9999 9999 to 0000 0000 0000. Therefore, for longer numbers,
  1556.              0000 0000 0000 = 1 0000 0000 0000. They are equivalent.
  1557.              0000 0000 0000 = 9999 9999 9999 + 1.
  1558.  
  1559.              If it is a positive signed number, it is still no problem (recall
  1560.              that in our 4 digit system, a positive number is between 0000 and
  1561.              4999, a negative signed number is between 5000 and 9999). Here
  1562.              are some positive signed numbers and their conversions:
  1563.  
  1564.                  1974      ->   0000 0000 1974
  1565.                     1      ->   0000 0000 0001
  1566.                  3909      ->   0000 0000 3909
  1567.  
  1568.              ____________________
  1569.  
  1570.                 9. Once again, the sign will be decided by the left hand
  1571.              digit. If it is 0-4 it is a positive number; if it is 5-9 it is a
  1572.              negative number.
  1573.  
  1574.  
  1575.              The PC Assembler Tutor                                          x
  1576.              ______________________
  1577.  
  1578.              If it is a negative number, where did its representation come
  1579.              from in our 4 digit system? -x -> 9999 + 1 -x = 9999 - x + 1.
  1580.              This time it won't be 9999 + 1 but 9999 9999 9999 + 1. Let's have
  1581.              some examples.
  1582.  
  1583.                  4 DIGIT SYSTEM       12 DIGIT SYSTEM
  1584.  
  1585.              -1964
  1586.                   9999     + 1        9999 9999 9999 + 1
  1587.                  -1964                         -1964
  1588.                   8035   -> 8036      9999 9999 8035 + 1 -> 9999 9999 8036
  1589.  
  1590.              -2867
  1591.                   9999     + 1        9999 9999 9999 + 1
  1592.                  -2867                         -2867
  1593.                   7132   -> 7133      9999 9999 7132 + 1 -> 9999 9999 7133
  1594.  
  1595.              -182
  1596.                   9999     + 1        9999 9999 9999 + 1
  1597.                   -182                          -182
  1598.                   9817   -> 9818      9999 9999 9817 + 1 -> 9999 9999 9818
  1599.  
  1600.              As you can see, all you need to do to sign extend a negative
  1601.              number is to put 9s to the left.
  1602.  
  1603.              Can't those 9s on the left become 0s when we add that 1 at the
  1604.              end?  No. In order for that to happen, the right four digits must
  1605.              be 9999. But that can only happen if the number to be negated is
  1606.              0000:
  1607.  
  1608.                   9999 9999 9999 + 1
  1609.                            -0000
  1610.                   9999 9999 9999 + 1 -> 0000 0000 0000
  1611.  
  1612.              In all other cases, adding 1 does not carry anything out of the
  1613.              right four digits.
  1614.  
  1615.  
  1616.              It is impossible to truncate one of these 12 digit numbers to a 4
  1617.              digit number without making the results unreliable. Here are two
  1618.              examples:
  1619.  
  1620.              (number)      0000 0168 7451 ->   7451  (now a negative number)
  1621.              (actual value)     +168 7451     -2549
  1622.  
  1623.              (number)      9999 9643 2170 ->   2170  (now a positive number)
  1624.              (actual value)     -356 7830     +2170
  1625.  
  1626.  
  1627.              We now have 12 digit numbers. Is it possible to add them and
  1628.              subtract them? Yes but only 4 digits at a time. When you add with
  1629.              pencil and paper you carry left from each digit. The computer can
  1630.              carry left from each group of 4 digits. We'll do the following
  1631.              addition:
  1632.  
  1633.                            0138 6715 6037
  1634.                          + 2514 2759 7784
  1635.  
  1636.  
  1637.              Chapter 0.1 - Numbers and Arithmetic                           xi
  1638.              ____________________________________
  1639.  
  1640.  
  1641.              Do this with pencil and paper and write down all the carries. The
  1642.              computer is going to do this in 3 parts:
  1643.  
  1644.                  1) 6037 + 7784
  1645.                  2) 6715 + 2759 + carry (if any)
  1646.                  3) 0138 + 2514 + carry (if any)
  1647.  
  1648.              The first addition is our regular addition. It will set the carry
  1649.              flag if the 0000/9999 boundary was crossed (i.e. the result was
  1650.              larger than 9999). In our case CF = 1 since the result is 13821.
  1651.              The register holds 3821. We store 3821. Next, we need to add
  1652.              three things: 6715 + 2759 + CF (=1). There is an instruction like
  1653.              this on all computers. It adds two numbers plus the value of the
  1654.              carry flag. Our first addition was ADD (add two numbers). This
  1655.              time the machine instruction is ADC (add two numbers and the
  1656.              carry). The result of our second addition is 9475. The register
  1657.              holds 9475 and CF = 0. We store 9475. Finally, we need to add
  1658.              three more things: 0138 + 2514 + CF (=0). Once again we use ADC.
  1659.              The result is 2652, CF = 0. We store the 2652. That is the whole
  1660.              result:
  1661.  
  1662.                  2652 9475 3821
  1663.  
  1664.              If CF = 1 at this point, the number has crossed the
  1665.              9999,9999,9999/0000,0000,0000 boundary. This will work for signed
  1666.              numbers also. The only difference is that at the very end we
  1667.              don't check CF, we check OF to see if the
  1668.              4999,9999,9999/5000,0000,0000 boundary has been crossed.
  1669.  
  1670.  
  1671.              Just to give you one more example we'll do a subtraction using
  1672.              the same numbers:
  1673.  
  1674.                            0138 6715 6037
  1675.                            2514 2759 7784
  1676.  
  1677.              Notice that in order for you to do this with pencil and paper
  1678.              you'll have to put the larger number on top before you subtract.
  1679.              With the machine this is unnecessary. Go ahead and do the
  1680.              subtraction with pencil and paper.
  1681.  
  1682.              The machine can do this 4 digits at a time, so this is a three
  1683.              step process:
  1684.  
  1685.                  1) 6037 - 7784
  1686.                  2) 6715 - 2759 - borrow (if any)
  1687.                  3) 0138 - 2514 - borrow (if any)
  1688.  
  1689.              The first one is a regular subtraction and since the bottom
  1690.              number is larger, the result is 8253, CF = 1. (Perhaps you are
  1691.              puzzled because that's not the result that you got. Don't worry,
  1692.              it all comes out in the wash). Step two subtracts but also
  1693.              subtracts any borrow (We had a borrow because CF = 1). There is a
  1694.              special instruction called SBB (subtract with borrow) that does
  1695.              just that. 6715 - 2759 - 1 = 3955, CF = 0. We store the 3955 and
  1696.              go on to the third part. This also is SBB, but since we had no
  1697.  
  1698.  
  1699.              The PC Assembler Tutor                                        xii
  1700.              ______________________
  1701.  
  1702.              borrow, we have 0138 - 2514 - 0 = 7624, CF = 1. We store 7624.
  1703.              This is the end result, and since CF = 1, we have crossed the
  1704.              9999,9999,9999/0000,0000,0000 boundary. This is going to be the
  1705.              representation of a negative number mod 1,0000,0000,0000. With
  1706.              pencil and paper, your result was:
  1707.  
  1708.                  -2375 6044 1747
  1709.  
  1710.              The machine result was:
  1711.  
  1712.                   7624 3955 8253
  1713.  
  1714.              But CF was 1 at the end, so this represents a negative number.
  1715.              What number does it represent? Let's take its negative to get a
  1716.              positive number with the same absolute value:
  1717.  
  1718.                  9999 9999 9999  + 1
  1719.                  7624 3955 8253
  1720.                  2375 6044 1746  + 1  = 2375 6044 1747
  1721.  
  1722.              This is the same thing you got with pencil and paper. The reason
  1723.              it looked wierd is that a negative number is always stored as its
  1724.              modular equivalent. If you want to read a negative number, you
  1725.              need to take its negative to get a positive number with the same
  1726.              absolute value.
  1727.  
  1728.              If we had been working with signed numbers, we wouldn't have
  1729.              checked CF at the very end, we would have checked OF to see if
  1730.              the 4999,9999,9999/5000,0000,0000 boundary had been crossed. If
  1731.              OF = 1 at the end, then the result was either too negative or too
  1732.              positive.
  1733.  
  1734.  
  1735.  
  1736.              OVERFLOW
  1737.  
  1738.              How does the machine decide that overflow has occured? First,
  1739.              what exactly is overflow and when is it possible for overflow to
  1740.              occur?
  1741.  
  1742.              Overflow is when the result of a signed addition or subtraction
  1743.              is either larger than the largest positive number or more
  1744.              negative than the most negative number. In the case of the 4
  1745.              digit machine, larger than +4999 or more negative than -5000.
  1746.  
  1747.              If one number is negative and the other is positive, it is not
  1748.              possible for overflow to occur. Take +32 and -4791 as examples.
  1749.              If we start with the positive number (+32) and add the negative
  1750.              number (-4791), the result can't possibly be too positive.
  1751.              Similarly, if we start with the negative number (-4791) and add
  1752.              the positive number (+32), the result can't be too negative.
  1753.              Therefore, the result can be neither too positive nor too
  1754.              negative. Make sure you understand this before going on.
  1755.  
  1756.              What if both are positive? Then overflow is possible. Here are
  1757.              some examples:
  1758.  
  1759.  
  1760.  
  1761.              Chapter 0.1 - Numbers and Arithmetic                         xiii
  1762.              ____________________________________
  1763.  
  1764.                  (+3500) + (+4500) = 8000 = -2000
  1765.                  (+2872) + (+2872) = 5744 = -4256
  1766.                  (+1799) + (+4157) = 5956 = -4044
  1767.  
  1768.              In each case, two positive numbers give a negative result. How
  1769.              about two negative numbers?
  1770.  
  1771.                                 (7154) + (6000) = 3154 = +3154
  1772.              (actual value)     -2946    -4000
  1773.  
  1774.                                 (5387) + (5826) = 1213 = +1213
  1775.              (actual value)     -4613    -4174
  1776.  
  1777.                                 (8053) + (6191) = 4244 = +4244
  1778.              (actual value)     -1947    -3809
  1779.  
  1780.              The numbers underneath are the negative numbers that the numbers
  1781.              above them represent. In these cases, adding two negative numbers
  1782.              gives a positive result.
  1783.  
  1784.              This is what the machine checks for. Before the addition, it
  1785.              checks the signs of the numbers. If the signs are the same, then
  1786.              the result must also be the same sign or overflow has
  1787.              occurred.{10}  Thus + and + must have a + result; - and - must
  1788.              have a - result. If not, OF (the overflow flag) is set (OF = 1).
  1789.              Otherwise OF is cleared (OF = 0).
  1790.  
  1791.  
  1792.              MULTIPLICATION
  1793.  
  1794.              Unsigned multiplication is easy. The machine simply multiplies
  1795.              the two numbers. Since the result can be up to 8 digits (the
  1796.              maximum result is 9999 X 9999 = 9998 0001) the machine uses two
  1797.              registers to hold the result. We'll call them R1 and R2.
  1798.  
  1799.                  5436 X 174     R1   0094
  1800.                                 R2   5864
  1801.  
  1802.                  2641 X 2003    R1   0528
  1803.                                 R2   9923
  1804.  
  1805.              You need to know which register holds which half of the result,
  1806.              but besides that, everything is straightforward. On this machine
  1807.              R1 holds the left four digits and R2 holds the right four digits.
  1808.  
  1809.              Notice that our machine has changed the modular base from N to
  1810.              N*N (from 1 0000 to 1 0000 0000). What this means is that two
  1811.              things which are modularly equivalent under addition and
  1812.              subtraction are not necessarily equivalent under multiplication
  1813.              and division.  6281 and -3719 will not work the same.
  1814.              ____________________
  1815.  
  1816.                 10. The machine checks something considerably more obscure
  1817.              because it is easier to implement in semiconductor logic, but
  1818.              what it is actually doing is checking to see if the two numbers
  1819.              being added have the same sign. If they do, the result must be
  1820.              the same sign or overflow has occurred.
  1821.  
  1822.  
  1823.              The PC Assembler Tutor                                        xiv
  1824.              ______________________
  1825.  
  1826.  
  1827.              The machine can't do signed multiplication. What it actually does
  1828.              is convert the numbers to positive numbers (if necessary),
  1829.              perform unsigned multiplication, and then do sign adjustment of
  1830.              the results (if necessary). It uses 2 registers for the result.
  1831.  
  1832.                            SIGNED MULTIPLICATION      REGS         RESULT
  1833.  
  1834.              (number)           (5372) X (3195)     R1   8521  =  -1478 6460
  1835.              (actual value)     -4628  X +3195      R2   3540
  1836.  
  1837.              (number)           (9164) X (8746)     R1   0104  =   +104 8344
  1838.              (actual value)      -836  X -1254      R2   8344
  1839.  
  1840.              (number)           (9927) X (0013)     R1   9999  =        -949
  1841.              (actual value)      -73  X   +13       R2   9051
  1842.  
  1843.              Looking at the last example, if we performed unsigned
  1844.              multiplication on those two numbers, we would have
  1845.              9927 X 0013 = 0012 9051, a completely different answer from the
  1846.              one we got. Therefore, whenever you do multiplication, you have
  1847.              to tell the machine whether you want unsigned or signed
  1848.              multiplication.
  1849.  
  1850.  
  1851.              DIVISION
  1852.  
  1853.              Unsigned division is easy too. The machine divides one number by
  1854.              the other, puts the quotient in one register and the remainder in
  1855.              another. Once again, the only problem is remembering which
  1856.              register has the quotient and which register has the remainder.
  1857.              For us, the quotient is R1 and the remainder is R2.
  1858.  
  1859.                  6190 / 372          R1   0016           16  remainder 238
  1860.                                      R2   0238
  1861.  
  1862.                  9845 / 11           R1   0895           895 remainder 0
  1863.                                      R2   0000
  1864.  
  1865.              As with multiplication, signed division is handled by the machine
  1866.              changing all numbers to positive numbers, performing unsigned
  1867.              division, then putting back the appropriate signs.
  1868.  
  1869.  
  1870.                          SIGNED DIVISION         REGS            RESULT
  1871.  
  1872.              (number)      (7192) / (9164)     R1   0003      +3  rem. -300
  1873.              (actual value)-2808  /  -836      R2   9700
  1874.  
  1875.              (number)      (3753) / (9115)     R1   9996      -4  rem. +213
  1876.              (actual value)+3753  /  -885      R2   0213
  1877.  
  1878.              Looking at the last example, 3753 / 9115, if that were unsigned
  1879.              multiplication the answer would be 0 remainder 3753, a completely
  1880.              different answer from the signed division. Every time you do a
  1881.              division, you have to state whether you want unsigned or signed
  1882.              division.
  1883. Chapter 0.2 - Bases 2 and 16
  1884. ============================
  1885.                                                                             xv
  1886.  
  1887.  
  1888.  
  1889.              I'm making the assumption that if you are along for the ride you
  1890.              already know something about binary and hex numbers. This is a
  1891.              review only.
  1892.  
  1893.  
  1894.              BASE 2 AND BASE 16
  1895.  
  1896.              Base 2 (binary) allows only 0s and 1s. Base 16 (hexadecimal)
  1897.              allows 0 - 9, and then makes up the next six numbers by using the
  1898.              letters A - F. A = 10, B=11, C=12, D=13, E=14 and F=15. You can
  1899.              directly translate a hex number to a binary number and a binary
  1900.              number to a hex number. A group of four digits in binary is the
  1901.              same as a single digit in hex. We'll get to that in a moment.
  1902.  
  1903.              The binary digits (BITS) are the powers of 2. The values of the
  1904.              digits (in increasing order) are 1, 2, 4, 8, 16, 32, 64, 128, 256
  1905.              and so on. 1 + 2 + 4 + 8 = 15, so the first four digits can
  1906.              represent a hex number. This repeats itself every four binary
  1907.              digits. Here are some numbers in binary, hex, and decimal
  1908.  
  1909.                  BINARY         HEX      DECIMAL
  1910.  
  1911.                  0100            4          4
  1912.                  1111            F         15
  1913.                  1010            A         10
  1914.                  0011            3          3
  1915.  
  1916.              Let's go from binary to hex. Here's a binary number.
  1917.  
  1918.                  0110011010101101
  1919.  
  1920.              To go from binary to hex, first divide the binary number up into
  1921.              groups of four starting from the right.
  1922.  
  1923.                  0110 0110 1010 1101
  1924.  
  1925.              Now simply change each group into a hex number.
  1926.  
  1927.                  0110 ->   4 + 2     ->   6
  1928.                  0110 ->   4 + 2     ->   6
  1929.                  1010 ->   8 + 2     ->   A
  1930.                  1101 ->   8 + 4 + 1 ->   D
  1931.  
  1932.              and we have 66AD as the result. Similarly, to go from hex to
  1933.              binary:
  1934.  
  1935.                  D39F
  1936.  
  1937.              change each hex digit into a set of four binary digits:
  1938.  
  1939.                  D = 13    ->   8 + 4 + 1 ->   1101
  1940.  
  1941.              ______________________
  1942.  
  1943.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  1944.  
  1945.  
  1946.  
  1947.              The PC Assembler Tutor                                        xvi
  1948.              ______________________
  1949.  
  1950.                  3         ->   2 + 1     ->   0011
  1951.                  9         ->   8 + 1     ->   1001
  1952.                  F = 15    ->   8+4+2+1   ->   1111
  1953.  
  1954.              and then put them all together:
  1955.  
  1956.                  1101001110011111
  1957.  
  1958.              Of course, having 16 digits strung out like that makes it totally
  1959.              unreadable, so in this book, if we are talking about a binary
  1960.              number, it will always be separated every 4 digits for
  1961.              clarity.{1}
  1962.  
  1963.              All computers operate on binary data, so why do we use hex
  1964.              numbers? Take a test. Copy these two binary numbers:
  1965.  
  1966.                  1011 1000 0110 1010 1001 0101 0111 1010
  1967.                  0111 1100 0100 1100 0101 0110 1111 0011
  1968.  
  1969.              Now copy these two hex numbers:
  1970.  
  1971.                  B86A957A
  1972.                  7C4C56F3
  1973.  
  1974.              As you can see, you recognize hex numbers faster and you make
  1975.              fewer mistakes in transcription with hex numbers.
  1976.  
  1977.  
  1978.              ADDITION AND SUBTRACTION
  1979.  
  1980.              The rules for binary addition are easy:
  1981.  
  1982.                  0 + 0 = 0
  1983.                  0 + 1 = 1
  1984.                  1 + 0 = 1
  1985.                  1 + 1 = 0  (carry 1 to the next digit left)
  1986.  
  1987.              similarly for binary subtraction:
  1988.  
  1989.                  0 - 0 = 0
  1990.                  0 - 1 = 1  (borrow 1 from the next digit left)
  1991.                  1 - 0 = 1
  1992.                  1 - 1 = 0
  1993.  
  1994.  
  1995.              On the 8086, you can have a 16 bit (binary digit) number
  1996.              represent a number from 0 - 65535. 65535 + 1 = 0 (65536). For
  1997.              binary numbers, the boundary is 65535/0. You count up or down
  1998.              through that boundary. The 8086 is a mod 65536 machine. That
  1999.              means the things that are equivalent to 35631 mod 65536 are:{2}
  2000.  
  2001.              ____________________
  2002.  
  2003.                 1. This will not be true of the actual assembler code, since
  2004.              the assembler demands an unseparated number.
  2005.  
  2006.                 2. 35631 + 29905 = 65536.  -29905 = 35631 (mod 65536)
  2007.  
  2008.  
  2009.              Chapter 0.2 - Binary and Hex Numbers                         xvii
  2010.              ____________________________________
  2011.  
  2012.                  (3*65536 + 35631)        (3*65536 - 29905)
  2013.                  (2*65536 + 35631)        (2*65536 - 29905)
  2014.                  (1*65536 + 35631)        (1*65536 - 29905)
  2015.                  (      0 + 35631)        (      0 - 29905)
  2016.                  (-1*65536 + 35631)       (-1*65536 - 29905)
  2017.                  (-2*65536 + 35631)       (-2*65536 - 29905)
  2018.                  (-3*65536 + 35631)       (-3*65536 - 29905)
  2019.  
  2020.              The unsigned number 35631 and the signed number -29905 look the
  2021.              same. They ARE the same. In all addition, they will operate in
  2022.              the same fashion. The unsigned number will use CF (the carry
  2023.              flag) and the signed number will use OF (the overflow flag).
  2024.  
  2025.              On all 16 bit computers, 0-32767 is positive and 32768 - 65535 is
  2026.              negative. Here's 32767 and 32768.
  2027.  
  2028.                  32767     0111 1111 1111 1111
  2029.                  32768     1000 0000 0000 0000
  2030.  
  2031.              32768 and all numbers above it have the left bit 1. 32767 and all
  2032.              numbers below it have the left bit 0. This is how to tell the
  2033.              sign of a signed number. If the left bit is 0 it's positive and
  2034.              if the left bit is 1 it's negative.
  2035.  
  2036.  
  2037.              TWO'S COMPLEMENT
  2038.  
  2039.              In base 10 we had 10s complement to help us with negative
  2040.              numbers. In base 2, we have 2s complememt.
  2041.  
  2042.                  0 = 65536 = 65535 + 1
  2043.  
  2044.              so we have:
  2045.  
  2046.                  1 0000 0000 0000 0000 =  1111 1111 1111 1111 + 1
  2047.  
  2048.              To get the negative of a number, we subtract:
  2049.  
  2050.                  -49 = 0 - 49 = 65536 - 49 = 65535 - 49 + 1
  2051.  
  2052.              (65536)  1111 1111 1111 1111 + 1
  2053.                 (49)  0000 0000 0011 0001
  2054.              result   1111 1111 1100 1110 + 1 -> 1111 1111 1100 1111  (-49)
  2055.              ; - - - - -
  2056.  
  2057.              -21874
  2058.              (65536)  1111 1111 1111 1111 + 1
  2059.              (21874)  0101 0101 0101 0111
  2060.              result   1010 1010 1010 1000 + 1 -> 1010 1010 1010 1001 (-21847)
  2061.              ; - - - - -
  2062.  
  2063.              -11628
  2064.              (65536)  1111 1111 1111 1111 + 1
  2065.              (11628)  0010 1101 0110 1100
  2066.              result   1101 0010 1001 0011 + 1 -> 1101 0010 1001 0100 (-11628)
  2067.              ; - - - - -
  2068.  
  2069.  
  2070.  
  2071.              The PC Assembler Tutor                                      xviii
  2072.              ______________________
  2073.  
  2074.              -1764
  2075.              (65536)  1111 1111 1111 1111 + 1
  2076.               (1764)  0000 0110 1110 0100
  2077.              result   1111 1001 0001 1011 + 1 -> 1111 1001 0001 1100 (-1764)
  2078.              ; - - - - -
  2079.  
  2080.              Notice that since:
  2081.  
  2082.                  1 - 0 = 1
  2083.                  1 - 1 = 0
  2084.  
  2085.              when you subtract from 1, you are simply switching the value of
  2086.              the subtrahend (that's the number that you subtract).
  2087.  
  2088.                  1    ->   0
  2089.                  0    ->   1
  2090.  
  2091.              1 becomes 0 and 0 becomes 1. You don't even have to think about
  2092.              it. Just switch the 1s to 0s and switch the 0s to 1s, and then
  2093.              add 1 at the end. Well do one more:
  2094.  
  2095.              -348
  2096.              (65536) 1111 1111 1111 1111 + 1
  2097.               (348)  0000 0001 0101 1100
  2098.              result  1111 1110 1010 0011 + 1 ->  1111 1110 1010 0100 (-348)
  2099.  
  2100.              Now two more, this time without the crutch of having the top
  2101.              number visible. Remember, even though you are subtracting, all
  2102.              you really need to do is switch 1s to 0s and switch 0s to 1s, and
  2103.              then add 1 at the end.
  2104.  
  2105.              -658
  2106.  
  2107.               (658)  0000 0010 1001 0010
  2108.              result  1111 1101 0110 1101 + 1 -> 1111 1101 0110 1110 (-658)
  2109.              ; - - - - -
  2110.  
  2111.              -31403
  2112.  
  2113.              (34103) 0111 1010 0100 0111
  2114.              result  1000 0101 1011 1000 + 1 -> 1000 0101 1011 1001 (-31403)
  2115.  
  2116.  
  2117.  
  2118.              SIGN EXTENSION
  2119.  
  2120.              If you want to use larger numbers, it is possible to use multiple
  2121.              words to represent them.{3}  The arithmetic will be done 16 bits
  2122.              at a time, but by using the method described in Chapter 0.1, it
  2123.              is possible to add and subtract numbers of any length. One normal
  2124.              length is 32 bits. How do you convert a 16 bit to a 32 bit
  2125.              number? If it is unsigned, simply put 0s to the left:
  2126.  
  2127.                0100 1100 1010 0111 ->  0000 0000 0000 0000 0100 1100 1010 0111
  2128.              ____________________
  2129.  
  2130.                 3. On the 8086, a word is 16 bits.
  2131.  
  2132.  
  2133.              Chapter 0.2 - Binary and Hex Numbers                          xix
  2134.              ____________________________________
  2135.  
  2136.  
  2137.  
  2138.              What if it is a signed number? The first thing we need to know
  2139.              about signed numbers is what is positive and what is negative.
  2140.              Once again, for reasons of symmetry, we choose positive to be
  2141.                  from 0000 0000 0000 0000 0000 0000 0000 0000
  2142.                  to   0111 1111 1111 1111 1111 1111 1111 1111
  2143.                  (hex 00000000 to 7FFFFFFF)
  2144.              and we choose negative to be
  2145.                  from 1000 0000 0000 0000 0000 0000 0000 0000
  2146.                   to   1111 1111 1111 1111 1111 1111 1111 1111
  2147.                  (hex 10000000 to FFFFFFFF).{4}
  2148.              This longer number system cycles
  2149.                  from 1111 1111 1111 1111 1111 1111 1111 1111
  2150.                  to   0000 0000 0000 0000 0000 0000 0000 0000
  2151.                  (hex FFFFFFFF to 00000000).
  2152.              Notice that by using binary numbers we are innundating ourselves
  2153.              with 1s and 0s.
  2154.  
  2155.  
  2156.              If it is a positive signed number, it is still no problem (recall
  2157.              that in our 16 bit system, a positive number is between 0000 0000
  2158.              0000 0000 and 0111 1111 1111 1111, a negative signed number is
  2159.              between 1000 0000 0000 0000 and 1111 1111 1111 1111). Just put 0s
  2160.              to the left. Here are some positive signed numbers and their
  2161.              conversions:
  2162.  
  2163.                 (1974)
  2164.                 0000 0111 1011 0110 -> 0000 0000 0000 0000 0000 0111 1011 0110
  2165.                 (1)
  2166.                 0000 0000 0000 0001 -> 0000 0000 0000 0000 0000 0000 0000 0001
  2167.                 (3909)
  2168.                 0000 1111 0100 0101 -> 0000 0000 0000 0000 0000 1111 0100 0101
  2169.  
  2170.              If it is a negative number, where did its representation come
  2171.              from in our 16 bit system? -x -> 1111 1111 1111 1111 + 1 -x =
  2172.              1111 1111 1111 1111 - x + 1. This time it won't be FFFFh + 1 but
  2173.              FFFFFFFFh + 1. Let's have some examples. (Here we have 8 bits to
  2174.              the group because there is not enough space on the line  to
  2175.              accomodate 4 bits to the group).
  2176.  
  2177.  
  2178.  
  2179.                16 BIT SYSTEM                  32 BIT SYSTEM
  2180.  
  2181.               -1964
  2182.              11111111 11111111 + 1     11111111 11111111 11111111 11111111 + 1
  2183.              00000111 10101100         00000000 00000000 00000111 10101100
  2184.  
  2185.              11111000 01010011 + 1     11111111 11111111 11111000 01010011 + 1
  2186.  
  2187.              11111000 01010100         11111111 11111111 11111000 01010100
  2188.              ____________________
  2189.  
  2190.                 4. Once again, the sign will be decided by the left hand
  2191.              digit. If it is 0 it is a positive number; if it is 1 it is a
  2192.              negative number.
  2193.  
  2194.  
  2195.              The PC Assembler Tutor                                         xx
  2196.              ______________________
  2197.  
  2198.  
  2199.  
  2200.  
  2201.              -2867
  2202.              11111111 11111111 + 1     11111111 11111111 11111111 11111111 + 1
  2203.              00001011 00110011         00000000 00000000 00001011 00110011
  2204.  
  2205.              11110100 11001100 + 1     11111111 11111111 11110100 11001100 + 1
  2206.  
  2207.              11110100 11001101         11111111 11111111 11110100 11001101
  2208.  
  2209.  
  2210.  
  2211.              -182
  2212.              11111111 11111111 + 1     11111111 11111111 11111111 11111111 + 1
  2213.              00000000 10110110         00000000 00000000 00000000 10110110
  2214.  
  2215.              11111111 01001001 + 1     11111111 11111111 11111111 01001001 + 1
  2216.  
  2217.              11111111 01001010         11111111 11111111 11111111 01001010
  2218.  
  2219.              As you can see, all you need to do to sign extend a negative
  2220.              number is to put 1s to the left.
  2221.  
  2222.              Can't those 1s on the left become 0s when we add that 1 at the
  2223.              end?  No. In order for that to happen, the right 16 bits must be
  2224.              1111 1111 1111 1111. But that can only happen if the number to be
  2225.              negated is 0:
  2226.  
  2227.                   1111 1111 1111 1111 1111 1111 1111 1111 + 1
  2228.                                      -0000 0000 0000 0000
  2229.                   1111 1111 1111 1111 1111 1111 1111 1111 + 1 ->
  2230.  
  2231.                                      0000 0000 0000 0000 0000 0000 0000 0000
  2232.  
  2233.              In all other cases, adding 1 does not carry anything out of the
  2234.              right 16 bits.
  2235.  
  2236.  
  2237.              It is impossible to truncate one of these 32 bit numbers to a 16
  2238.              bit number without making the results unreliable. Here are two
  2239.              examples:
  2240.  
  2241.              +1,687,451
  2242.              00000000 00011001 10111111 10011011 -> 10111111 10011011 (-16485)
  2243.  
  2244.              -3,524,830
  2245.              11111111 11001010 00110111 00100010 -> 00110111 00100010 (+14114)
  2246.  
  2247.              Truncating has changed both the sign and the absolute value of
  2248.              the number.
  2249.  
  2250.  
  2251.  
  2252.  
  2253.  
  2254.  
  2255. Chapter 0.3 - LOGIC
  2256. ===================
  2257.                                                                            xxi
  2258.  
  2259.  
  2260.  
  2261.              Programs use numbers a lot. But they also ask questions that
  2262.              require a yes/no answer. Is there an 8087 chip in the computer?
  2263.              Is there a color monitor; how about a monochrome monitor? Is
  2264.              there keyboard input waiting to be processed? Are you going to
  2265.              get lucky on your date on Friday? Or, since you are a computer
  2266.              programmer, are you going to have a date this month? Did the file
  2267.              open correctly? Have we reached end of file?
  2268.  
  2269.              In order to combine these logical questions to our heart's
  2270.              content, we need a few operations: AND, OR, XOR (exclusive or),
  2271.              and NOT.
  2272.  
  2273.  
  2274.              AND
  2275.  
  2276.              If we have two sentences "A" and "B", then ("A" AND "B") is true
  2277.              if (and only if) both "A" and "B" are true. "It is raining and I
  2278.              am wet" is true only if both "It is raining" and "I am wet" are
  2279.              true. If one or both are false, then 'A and B' is false. A
  2280.              shortcut for writing this is to use a truth table. A truth table
  2281.              tells under what conditions an expression is true or false. All
  2282.              we need to know is whether each component expression is true or
  2283.              false. T stands for true, F for false.
  2284.  
  2285.                  "A"  "B"       "A" AND "B"
  2286.  
  2287.                   T    T             T
  2288.                   T    F             F
  2289.                   F    T             F
  2290.                   F    F             F
  2291.  
  2292.              Notice that the truth table does NOT say anything about whether
  2293.              the expression makes sense. The sentence:
  2294.  
  2295.                  "It's hot and I am sweating."
  2296.  
  2297.              is a reasonable expression which may or may not be true. It will
  2298.              be true if both "It is hot" and "I am sweating" are true. But the
  2299.              sentence:
  2300.  
  2301.                  "The trees are green and Quito is the Capital of Ecuador."
  2302.  
  2303.              is pure garbage. It does not satisfy our idea of what a sensible
  2304.              expression should be, and should NEVER be evaluated by means of a
  2305.              truth table. The warranty on a truth table is, if the expression
  2306.              makes sense, then the truth table will tell you under what
  2307.              conditions it is true or false. If the expression does not make
  2308.              sense, then the truth table tells you nothing.
  2309.  
  2310.              Fortunately, this problem really belongs to philisophical logic.
  2311.              When you use logical operators in your program, there will be a
  2312.  
  2313.              ______________________
  2314.  
  2315.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  2316.  
  2317.  
  2318.              The PC Assembler Tutor                                       xxii
  2319.              ______________________
  2320.  
  2321.              well defined reason for each use. If you start doing screwy
  2322.              things, your program probably won't run.
  2323.  
  2324.  
  2325.              OR
  2326.  
  2327.              There are two different types of OR alive and kicking in the
  2328.              English language - the exclusive OR (A or B but not both) and the
  2329.              inclusive OR (A or B or both).
  2330.  
  2331.              A mother tells her child "You can have a piece of cake or a piece
  2332.              of candy." Does this mean that he can have both if he wants? Of
  2333.              course not. He can have one or the other, but not both. This is
  2334.              XOR, the exclusive or. The truth table for this is:
  2335.  
  2336.                  "A"  "B"       "A" XOR "B"
  2337.  
  2338.                   T    T             F
  2339.                   T    F             T
  2340.                   F    T             T
  2341.                   F    F             F
  2342.  
  2343.              'A XOR B' is true if exactly one of them is true. If they both
  2344.              are true 'A XOR B' is false. If neither is true, 'A XOR B' is
  2345.              false. Examples of XOR are:
  2346.  
  2347.                  1) We will either go to Italy or to Japan on our vacation.
  2348.                  2) I'll either have a tuna salad or a chef's salad.
  2349.                  3) He'll either buy a Lamborghini or a BMW.
  2350.  
  2351.  
  2352.              Consider this sentence: "To go to Harvard, you need to have
  2353.              connections or to be very smart." Do we want this to mean that if
  2354.              you have connections but are very smart, you are automatically
  2355.              excluded from going to Harvard?  No. We want this to mean one or
  2356.              the other or perhaps both. Sometimes you write this as 'and/or'
  2357.              if you want to be absolutely clear. This is the inclusive OR. The
  2358.              truth table for OR is:
  2359.  
  2360.                  "A"  "B"       "A" OR "B"
  2361.  
  2362.                   T    T             T
  2363.                   T    F             T
  2364.                   F    T             T
  2365.                   F    F             F
  2366.  
  2367.              'A OR B' is true if one or both of them are true. If both are
  2368.              false, then it is false. Examples of OR are:
  2369.  
  2370.                  1) They'll either go to Italy or to Austria on their
  2371.                  vacation.
  2372.                  2) I'll have either steak or shrimp at The Sizzler.
  2373.                  3) He'll buy either a paisley tie or a rep tie.
  2374.  
  2375.              The three sentences for XOR and OR mimic each other on purpose.
  2376.              In the English language, you know which type of OR is being used
  2377.              by what is being talked about. You know intuitively which one
  2378.  
  2379.  
  2380.              Chapter 0.3 - Logic                                         xxiii
  2381.              ___________________
  2382.  
  2383.              applies. If someone buys two different ties you are not suprised.
  2384.              If someone buys two expensive cars at the same time you are quite
  2385.              surprised.{1}  With very few exceptions, if you confuse the two
  2386.              you are doing it on purpose. If your father says "You can have
  2387.              the car on Friday night or on Saturday night." and you don't
  2388.              understand which OR applies, it's not his fault.
  2389.  
  2390.  
  2391.              NOT
  2392.  
  2393.              The final logical operation is NOT. The sentence: "It is not
  2394.              raining." is false if it is raining, and true otherwise. The
  2395.              truth table is:
  2396.  
  2397.                  "A"    NOT "A"
  2398.  
  2399.                   T        F
  2400.                   F        T
  2401.  
  2402.              This is self-explanatory.
  2403.  
  2404.              Amazingly enough, this is all you need to know about logic to be
  2405.              a quality programmer. Trying to make very complex combinations of
  2406.              these logical operations may be fun for philosophy, but it is
  2407.              death to a program. KISS is the operative word in programming.{2}
  2408.  
  2409.  
  2410.  
  2411.  
  2412.  
  2413.  
  2414.  
  2415.  
  2416.  
  2417.  
  2418.  
  2419.  
  2420.  
  2421.  
  2422.  
  2423.  
  2424.  
  2425.  
  2426.  
  2427.  
  2428.  
  2429.  
  2430.  
  2431.  
  2432.  
  2433.  
  2434.              ____________________
  2435.  
  2436.                 1. Especially if his job is working the cash register at
  2437.              Sears.
  2438.  
  2439.                 2. Which, if you don't know, is Keep It Simple, Stupid.
  2440.  
  2441. Chapter 0.4 - MEMORY
  2442. ====================
  2443.                                                                           xxiv
  2444.  
  2445.  
  2446.  
  2447.              The basic unit of memory on 8086 machines is the byte.{1} This
  2448.              means that in every memory cell we can store a number from 0 to
  2449.              255. Each memory cell has an address. The first one in memory has
  2450.              address 0, then address 1, then 2, then 3, etc.
  2451.  
  2452.              The registers on the 8086 are one word (two bytes) long. This
  2453.              means that any register can store and operate on a number from 0
  2454.              to 65,535. (It also has some registers which can operate on bytes
  2455.              and can store and operate on numbers from 0 to 255.)
  2456.  
  2457.              Memory is physically external to the 8086. Registers are
  2458.              physically internal to the 8086; they are actually on the chip.
  2459.  
  2460.              One of the ways that we can access memory on the 8086 is by
  2461.              putting the address of a memory cell in a register and then
  2462.              telling the 8086 that we want to use the data at that memory
  2463.              address.
  2464.  
  2465.              Since each byte has its own address, and since we can't have a
  2466.              number larger that 65,535 in any one register, it is impossible
  2467.              to address more than 65,535 bytes with a single register. Back in
  2468.              the dark ages, that might have been enough memory, but it sure
  2469.              isn't enough nowdays.
  2470.  
  2471.              Intel solved the problem by creating SEGMENTS. Each segment is
  2472.              65,536 bytes long, going from address 0 to address 65,535.{2} You
  2473.              tell the 8086 where you want to go in memory by telling it which
  2474.              segment you are in and what the address is within that segment.
  2475.              Segments are numbered from 0 to 65,535. That is, there are 65,536
  2476.              of them.
  2477.  
  2478.              As a design decision, Intel decided that a segment should start
  2479.              every 16 bytes. This decision was made in the late 70's and is
  2480.              cast in stone. On the 8086, a segment starts every 16 bytes. Here
  2481.              is a list of some segments, with the segment number and the
  2482.              segment starting address in both decimal and hex.
  2483.  
  2484.  
  2485.                       SEGMENT NUMBER                   STARTING ADDRESS
  2486.  
  2487.                        0d        0h                     0d          00h
  2488.                        1d        1h                    16d          10h
  2489.                        2d        2h                    32d          20h
  2490.                        3d        3h                    48d          30h
  2491.                        4d        4h                    64d          40h
  2492.              ____________________
  2493.  
  2494.                 1. As it is on all computers.
  2495.  
  2496.                 2. The last segments are actually less that 65,535, as will be
  2497.              explained later.
  2498.  
  2499.              ______________________
  2500.  
  2501.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  2502.  
  2503.  
  2504.              Chapter 0.4 - Memory                                          xxv
  2505.              ____________________
  2506.  
  2507.                      200d       C8h                 3,200d         C80h
  2508.                    21694d     54BEh               347,104d       54BE0h
  2509.                    51377d     C8B1h               822,032d       C8B10h
  2510.  
  2511.  
  2512.              One thing you should note is that in hex, the segment number is
  2513.              the same as the starting address, except that the starting
  2514.              address has an extra 0 digit on the right.
  2515.  
  2516.              These segments overlap. A new segment starts every 16 bytes, and
  2517.              they are all 65,535 bytes long. This means that with the
  2518.              exception of the first 16 bytes, any address in memory is in more
  2519.              that one segment. Here are some addresses. The word "offset"
  2520.              means the number of bytes from the beginning of the segment. (It
  2521.              is possible for a memory cell to have an offset 0). The offset is
  2522.              shown in both decimal (d) and hex (h):
  2523.  
  2524.              memory address  55    (37h)
  2525.  
  2526.                          Seg #        Offset        Offset
  2527.                             0            55d          37h
  2528.                             1            39d          27h
  2529.                             2            23d          17h
  2530.                             3             7d           7h
  2531.  
  2532.              Thus the address 55 is in 4 different segments, and can be
  2533.              addressed relative to any one of them.
  2534.  
  2535.              memory address  17,946   (461Ah)
  2536.  
  2537.                         Seg #         Offset         Offset
  2538.                             0        17,946d          461Ah
  2539.                             1        17,930d          460Ah
  2540.                             2        17,914d          45FAh
  2541.                           ...            ...
  2542.                          1120            26d            1Ah
  2543.                          1121            10d            0Ah
  2544.  
  2545.              The address 17,946 is in 1122 different segments, and can be
  2546.              addressed relative to any of them. Notice that as the segment
  2547.              number goes up one segment, the offset address goes down 16 bytes
  2548.              (10h).
  2549.  
  2550.              Above the address 65,519, every memory cell can be addressed by
  2551.              4,096 different segments.
  2552.  
  2553.              Because of the way the addresses are generated on the 8086, the
  2554.              maximum memory address possible is 1,048,575 (FFFFF hex.) This
  2555.              means that the final segments are less than 65,536 bytes long. In
  2556.              this table "Address" is the starting address of the segment. All
  2557.              the following numbers are decimal:
  2558.  
  2559.                     Segment #        Address           Max Offset
  2560.                       65,535d     1,048,560d                  15d
  2561.                       65,534d     1,048,544d                  31d
  2562.                       65,533d     1,048,528d                  47d
  2563.                           ...            ...                  ...
  2564.  
  2565.  
  2566.              The PC Assembler Tutor                                       xxvi
  2567.              ______________________
  2568.  
  2569.                       64,000d     1,024,000d              24,575d
  2570.  
  2571.              Let's look at these same numbers in hex:
  2572.  
  2573.                     Segment #        Address           Max Offset
  2574.                         FFFFh         FFFF0h                   Fh
  2575.                         FFFEh         FFFE0h                  1Fh
  2576.                         FFFDh         FFFD0h                  2Fh
  2577.                           ...            ...                  ...
  2578.                         FA00h         FA000h                5FFFh
  2579.  
  2580.              The maximum addressable number is FFFFFh, which is why these
  2581.              segments are cut short. There are 4,095 segments at the top which
  2582.              are less than 65,536 bytes long. Don't worry, though, because
  2583.              this top section of memory belongs to the operating system, and
  2584.              your programs will never be put there. It will never affect you.
  2585.  
  2586.              Back in the late 70s, a million bytes of memory seemed like a
  2587.              lot. In the 60s, large mainframe computers had only a
  2588.              half-million bytes of memory. In the 70s memory was still
  2589.              exhorbitantly expensive. Nowdays, however, you practically need a
  2590.              megabyte just to blow your nose. But this segmentation system is
  2591.              cast in stone, so there is no way to get more memory on the
  2592.              8086.{3}
  2593.  
  2594.              In the beginning, when we make a program, we will use one segment
  2595.              for the machine code, one segment for permanant data, and one
  2596.              segment for temporary data. If we need it, this gives us 196,608
  2597.              bytes of usable memory right off the bat. As you will see by the
  2598.              time we are finished, ALL memory is addressable - we just need to
  2599.              do more work to get to it all.
  2600.  
  2601.              All this talk about segments and offsets may have you concerned.
  2602.              If you have to keep track of all these offsets, programming is
  2603.              going to be very difficult.{4}  Not to worry. It is the
  2604.              assembler's job to keep track of the offsets of every variable
  2605.  
  2606.  
  2607.  
  2608.  
  2609.              ____________________
  2610.  
  2611.                 3. This megabyte rule is unalterable. EMS (extended memory) is
  2612.              actually a memory swapping program. On the 28086 and 80386 you
  2613.              can have more than one megabyte of memory but the program can
  2614.              only access one megabyte. The program reserves a section of its
  2615.              one megabyte for a transfer area. It then calls EMS which
  2616.              transfers the data from this extended memory to the transfer
  2617.              area. It is in effect a RAM disk. It is like using a hard disk
  2618.              but is much faster. If Intel had bitten the bullet with the 80286
  2619.              and said that a segment would start every 256 bytes instead of
  2620.              every 16 bytes, we would have 16 megabytes of directly accessible
  2621.              memory instead of 1 megabyte. Hindsight is such a wonderful
  2622.              thing.
  2623.  
  2624.                 4. Remember, an offset is just how many bytes a memory cell is
  2625.              from the beginning of the segment.
  2626.  
  2627.  
  2628.              Chapter 0.4 - Memory                                        xxvii
  2629.              ____________________
  2630.  
  2631.              and every label in your program.{5}
  2632.  
  2633.              Which segments your program uses is decided by the operating
  2634.              system when it loads your program into memory. It puts some of
  2635.              this information into the 8086. At the start of the program, you
  2636.              put the rest of the information into the 8086, and then you can
  2637.              forget about segments.
  2638.  
  2639.  
  2640.              NUMBERS IN MEMORY
  2641.  
  2642.              The largest number you can store in a single byte is 255. If you
  2643.              are calculating the distance from the sun to Alpha Centauri in
  2644.              inches, obviously one byte is not enough. Unfortunately, the 8086
  2645.              can't really handle large numbers like that.{6} It can handle
  2646.              numbers which are 16 bits (2 bytes) long. However, with
  2647.              subprograms supplied with all compilers, we can handle large
  2648.              numbers, though rather slowly if we don't use an 8087. All these
  2649.              different programs need a standard way to write numbers in
  2650.              memory, and this standard is supplied by Intel. The standard is :
  2651.  
  2652.                (1)  integers can be 1, 2, or 4 bytes long. This corresponds
  2653.                to -128 to +127 , -32,768 to +32,767, and -2,147,483,648 to
  2654.                +2,147,483,647.
  2655.  
  2656.                (2)  scientific floating point numbers which have an exponent
  2657.                and can be very large. They come as 4 byte and 8 byte numbers.
  2658.                We will not deal with them at all, but we need to know how
  2659.                they are stored.
  2660.  
  2661.                (3)  Commercial or BCD numbers which occupy 1/2 byte per
  2662.                digit. Since some of the 8086 instructions are concerned with
  2663.                these we will cover them, but if you are not curious about
  2664.                them, you can skip that section. The standard is a 10 byte
  2665.                number.
  2666.  
  2667.              Let's look at a number. For the rest of this section, all numbers
  2668.              will be hex, and if a number is longer than one byte, we will
  2669.              display it with a blank space between each byte. If it is a one
  2670.              byte number - e.g. 3C, we know exactly where we are going to put
  2671.              it. But what if a number is 4 bytes long - e.g. 2D F5 33 0A - and
  2672.              we want to put it in memory starting at offset 264. We have two
  2673.              choices:
  2674.  
  2675.              2D F5 33 0A
  2676.                     Address          Choice 1     Choice 2
  2677.                       267               2D           0A
  2678.                       266               F5           33
  2679.                       265               33           F5
  2680.              ____________________
  2681.  
  2682.                 5. As in other languages, a label is a name that marks a place
  2683.              in the code. In BASIC, labels are actually numbers (such as 500
  2684.              in the instruction GOTO 500). Labels are frowned on in Pascal and
  2685.              C, but are the lifeblood of assembler language.
  2686.  
  2687.                 6. But fortunately, the 8087 can.
  2688.  
  2689.  
  2690.              The PC Assembler Tutor                                     xxviii
  2691.              ______________________
  2692.  
  2693.                       264               0A           2D
  2694.  
  2695.              Neither choice is better than the other. Choice 1 puts the
  2696.              right-most byte in low memory, choice 2 puts the right-most byte
  2697.              in high memory.{7} The right-most byte is called the LEAST
  2698.              SIGNIFICANT BYTE because it has the least effect on a number,
  2699.              while the left-most byte is called the MOST SIGNIFICANT BYTE
  2700.              because it has the most effect on a number. In fact, Intel picked
  2701.              choice #1 for the 8086 (which has the least significant byte in
  2702.              low memory), and Motorola picked choice #2 for the 68000 (which
  2703.              has the most significant byte in low memory).
  2704.  
  2705.              This is consistent for both the 8086 and the 8087: THE LEAST
  2706.              SIGNIFICANT BYTE IS ALWAYS IN LOW MEMORY: EACH NUMBER IN MEMORY
  2707.              STARTS WITH THE LEAST SIGNIFICANT BYTE. Remember that, and you'll
  2708.              save yourself some trouble.
  2709.  
  2710.              One problem you will run up against is that when we draw pictures
  2711.              of memory, we often draw from left to right, that is:
  2712.  
  2713.                ADDRESS        264  265  266  267
  2714.  
  2715.              When we do that, things start looking wierd. For 2D F5 33 0A we
  2716.              have:
  2717.  
  2718.                ADDRESS        264  265  266  267
  2719.                VALUE          0A   33   F5   2D
  2720.  
  2721.              This is exactly backwards. Remember, memory doesn't go from left
  2722.              to right, it goes UP from 0, and THE LEAST SIGNIFICANT BYTE IS
  2723.              ALWAYS IN LOW MEMORY. You will certainly make some mistakes till
  2724.              you get used to being consistent about this. The right hand digit
  2725.              of a number is always in low memory. If you think of memory as
  2726.              being VERTICAL:
  2727.  
  2728.              1E A3 07 B5
  2729.                             Value       Address
  2730.                               1E         4782
  2731.                               A3         4781
  2732.                               07         4780
  2733.                               B5         4779
  2734.  
  2735.              rather than being LEFT TO RIGHT:
  2736.  
  2737.                Address   4779 4780 4781 4782
  2738.                Value      B5   07   A3   1E
  2739.  
  2740.              you will be much better off.
  2741.  
  2742.  
  2743.  
  2744.  
  2745.  
  2746.              ____________________
  2747.  
  2748.                 7. Low memory always means the low addresses and high memory
  2749.              always means the high addresses.
  2750.  
  2751. Chapter 0.5 - STYLE
  2752. ===================
  2753.                                                                           xxix
  2754.  
  2755.  
  2756.  
  2757.              Finally, it is time to say a word about style. Assembler code is
  2758.              by its nature difficult to read. It divides any concept into a
  2759.              number of sequential steps. If you have the BASIC statement:
  2760.  
  2761.                  MINUTES = DAYS * 1440
  2762.  
  2763.              You get the idea because you can scan the line to see what is
  2764.              wanted. The assembler code for the above line is: {1}
  2765.  
  2766.                  mov  ax, days
  2767.                  mov  bx, 1440
  2768.                  mul  bx
  2769.                  mov  minutes, ax
  2770.  
  2771.              In BASIC, the concept was imbedded in the expression. In
  2772.              assembler it was lost. This means two things. First, you must be
  2773.              religious about documenting every step. If you come back to
  2774.              something two or three months later and you haven't documented
  2775.              what you are doing, it may take you longer to figure out what you
  2776.              did than it would to completely rewrite what you did.
  2777.  
  2778.              Secondly, if you are a person who likes code like this:
  2779.  
  2780.                  x = (y + k) / (z - 4)
  2781.  
  2782.              you are headed for big trouble. At the assembler level it is
  2783.              CRITICAL that you give every variable a name that signifies
  2784.              exactly what it is. If you are counting the number of correct
  2785.              answers, then the variable should be:
  2786.  
  2787.                  correct_answers
  2788.  
  2789.              not 'K'. If you are looking at the remainder from a division,
  2790.              then the variable should be:
  2791.  
  2792.                  remainder
  2793.  
  2794.              not 'R'. If you try to use short names like 'x', 'k' and 'y', I
  2795.              will guarantee that for every minute you save by not having to
  2796.              type in long variable names, you will lose 10 minutes by not
  2797.              being able to figure out what is going on when you reread the
  2798.              code. In this tutorial we will use multiple words connected by
  2799.              underscores:
  2800.  
  2801.                  first_positive_prime
  2802.                  median_age
  2803.                  oldest_student
  2804.              ____________________
  2805.  
  2806.                 1. Don't worry about what this code does. You will learn soon
  2807.              enough.
  2808.  
  2809.              ______________________
  2810.  
  2811.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  2812.  
  2813.  
  2814.              The PC Assembler Tutor                                        xxx
  2815.              ______________________
  2816.  
  2817.  
  2818.              The Microsoft assembler allows 31 significant characters in a
  2819.              name. Even though there are several other characters allowed, we
  2820.              will use only letters, the underscore, and numbers (where
  2821.              appropriate):
  2822.  
  2823.                  approximation1
  2824.                  approximation2
  2825.                  approximation3
  2826.  
  2827.              This should make your code similar to well written C or Pascal
  2828.              code, and greatly increase the readability of the code.
  2829.  
  2830. Chapper  1 - Some Simple Programs
  2831. ================================
  2832.                                                                              1
  2833.  
  2834.  
  2835.  
  2836.              It is now time to start writing assembler code. One of the
  2837.              problems with writing in assembler is that there is no way to get
  2838.              input into the program or output from the program until you are
  2839.              very far along with learning assembler language. This is a
  2840.              Catch-22 situation. You can't learn assembler easily without
  2841.              access to input and output, and you can't write i/o routines till
  2842.              you know assembler.{1}  Help is at hand. Included on this disk is
  2843.              a file called asmhelp.obj. It is actually a series of programs
  2844.              that will allow you to get input from the keyboard and print
  2845.              output to the screen. It has some other features which will be
  2846.              explained later.
  2847.  
  2848.              The second problem at the start is that every assembler program
  2849.              has a lot of overhead. These are standard instructions and
  2850.              formats that you need to get the program to work AT ALL. This
  2851.              disk contains templates that contain all the overhead, so to
  2852.              write a program you just make a copy of the template and enter
  2853.              the code and data at the appropriate place. By the end of this
  2854.              sequence of lessons, you will know how to make templates yourself
  2855.              and know the meaning of each word in the template. For now, you
  2856.              have to have faith that what is written is necessary, and that
  2857.              you will learn the meaning of everything later.
  2858.  
  2859.              Let's start. At the end of this chapter is the template we will
  2860.              use for now - temp1.asm. These templates are in the subdirectory
  2861.              \template.
  2862.  
  2863.              Let's call the first program prog1.asm (very original). All
  2864.              programs in assembler must have the file extension .asm so make a
  2865.              copy by giving the command:
  2866.  
  2867.                  >copy \template\temp1.asm  prog1.asm
  2868.  
  2869.              You are now ready to enter code. Open up prog1.asm with your
  2870.              editor, and take a look at it. It should look the same as
  2871.              temp1.asm.
  2872.  
  2873.              Where it says "put name of program here" - that is for your
  2874.              personal use so you can see the program name while in the editor.
  2875.              The assembler ignores everything after a semicolon. All the lines
  2876.              that start with a semicolon are there for visual separation or
  2877.              for comments. The lines with asterisks separate segments. Yes,
  2878.              the assembler is going to make this program into three segments.
  2879.              You should put all code between the line labeled "START CODE
  2880.              BELOW THIS LINE" and the line labeled "END CODE ABOVE THIS LINE".
  2881.              ____________________
  2882.  
  2883.                   1 Just to give you an idea of how contradictory the
  2884.              situation is, asmhelp.obj was written in assembler language and
  2885.              consists of about 3500 lines (that's about 50 pages), yet you
  2886.              need to be using it from day one.
  2887.  
  2888.              ______________________
  2889.  
  2890.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  2891.  
  2892.  
  2893.              The PC Assembler Tutor                                          2
  2894.              ______________________
  2895.  
  2896.              Later you will get more flexibility.
  2897.  
  2898.              Also notice the lines starting with the word EXTRN. Those lines
  2899.              tell the assembler that the subroutines (such as print_num) are
  2900.              in a different file and must be found when this program is
  2901.              linked. The assembler enters each EXTRN name in a list and
  2902.              records each place that an EXTRN subroutine is requested. It is
  2903.              possible that one of these subroutines is never requested. That's
  2904.              fine. However, every one of these subroutines must be present at
  2905.              link time or you will get a link error. This is true even if the
  2906.              subroutine is on the list but never requested.
  2907.  
  2908.              Since the overhead is so long, and I am so lazy, the template
  2909.              will never be included in the description of the program. What
  2910.              you will get is the name of the template, then any data you need,
  2911.              then the code. It will look like this:
  2912.  
  2913.              TEMP1.ASM
  2914.              ; - - - - - START DATA BELOW THIS LINE
  2915.              the data is written here
  2916.              ; - - - - - END DATA ABOVE THIS LINE
  2917.  
  2918.              ; - - - - - START CODE BELOW THIS LINE
  2919.              the code is written here
  2920.              ; - - - - - END CODE ABOVE THIS LINE
  2921.  
  2922.  
  2923.              If there is no data, no data section will be included. If there
  2924.              is data, it should be written in the segment named DATASTUFF
  2925.              between the lines "START DATA..." and "END DATA ...". The code
  2926.              should be written between the lines that say "START CODE ..." and
  2927.              "END CODE ...".
  2928.  
  2929.              For our first program, the description will look like this:
  2930.  
  2931.              TEMP1.ASM
  2932.              ; - - - - - START CODE BELOW THIS LINE
  2933.  
  2934.              first_label:
  2935.                  call get_num
  2936.                  call print_num
  2937.                  jmp  first_label
  2938.  
  2939.              ; - - - - - END CODE ABOVE THIS LINE
  2940.  
  2941.              That's all we need. If we needed to write all the overhead
  2942.              (starting with the line "main proc far") we would have:
  2943.  
  2944.              main     proc far
  2945.              start:   push ds
  2946.                       sub  ax, ax
  2947.                       push ax
  2948.  
  2949.                       mov  ax, DATASTUFF
  2950.                       mov  ds, ax
  2951.  
  2952.              ; + + + + + + + + + + +
  2953.  
  2954.  
  2955.              Chapter 1 - Some Simple Programs                                3
  2956.              ________________________________
  2957.  
  2958.  
  2959.              first_label:
  2960.                  call get_num
  2961.                  call print_num
  2962.                  jmp  first_label
  2963.  
  2964.              ; + + + + + + + + + + +
  2965.  
  2966.                  ret
  2967.  
  2968.              mainendp
  2969.              etc.
  2970.  
  2971.              You can see that a simple four line program has blossomed into a
  2972.              monster. I'm assuming some intelligence on your part. Until
  2973.              further notice, the code goes between the "START CODE' and the
  2974.              "END CODE" lines and the data goes in the DATASTUFF segment
  2975.              between the "START DATA" and the "END DATA" lines.
  2976.  
  2977.              It's time to type in the program listed above. Be careful when
  2978.              you type. When you are done I'll explain it.
  2979.  
  2980.              In assembler we need a way to label different spots in the code.
  2981.              We use labels. A LABEL is a name (at the beginning of a line)
  2982.              which is immediately followed by a colon. A label doesn't
  2983.              generate any code. The assembler merely keeps track of where the
  2984.              label is for future use. The label we are using is named
  2985.              first_label.
  2986.  
  2987.              The CALL instruction tells the assembler to call the subroutine
  2988.              listed after the call.{2} We are calling two subroutines; first
  2989.              get_num which gets a number, then print_num, which prints a
  2990.              number in a variety of styles.
  2991.  
  2992.              Finally, JMP tells the assembler that you want to jump to the
  2993.              label listed after it. It is the same as GOTO in BASIC.
  2994.  
  2995.              If you look at the program, you will notice that we have an
  2996.              infinite loop. It was designed that way. It takes a fair amount
  2997.              of code to exit gracefully, so we will always exit ungracefully.
  2998.              When you are tired of the program, simply press Control-C. That
  2999.              should get you out. That way you can try out something an
  3000.              indefinite number of times, and when you have finished you can
  3001.              press CTRL-C to quit the program.
  3002.  
  3003.              One warning about machine language before we start. There is no
  3004.              safety net, so before you start a machine language program, make
  3005.              sure all files are closed (i.e. that you have no other programs
  3006.              in memory). We will NEVER open a file in one of our programs.
  3007.              ____________________
  3008.  
  3009.                   2 I am using the words subroutine, routine, program and
  3010.              procedure (the technical word) interchangebly throughout the
  3011.              book. A program is actually a group of one or more procedures,
  3012.              but I'm not going to be too strict about it. Context should tell
  3013.              whether we are talking about a single procedure or a whole
  3014.              program.
  3015.  
  3016.  
  3017.              The PC Assembler Tutor                                          4
  3018.              ______________________
  3019.  
  3020.  
  3021.              Your programs are almost certain to wind up in zombie space from
  3022.              time to time. If that happens, your choices are (1) hit CTRL-C.
  3023.              If that doesn't work, then (2) hit CTRL-ALT-DEL. As a last
  3024.              resort, you can (3) hit a reset button or shut the machine down.
  3025.  
  3026.              For that reason, memorize this mantra: BACKUP YOUR PROGRAMS AND
  3027.              BACKUP YOUR BACKUPS.
  3028.  
  3029.              Double check that you have typed in the assembler code correctly.
  3030.              Now it's time to assemble it. I am assuming that you have the
  3031.              Microsoft assembler.{3}  Type:
  3032.  
  3033.                  >masm  prog1 ;
  3034.  
  3035.              The first thing you will see is the copyright notice:
  3036.  
  3037.                 Microsoft (R) Macro Assembler Version 5.10
  3038.                 Copyright (C) Microsoft Corp 1981, 1988.  All rights reserved.
  3039.  
  3040.  
  3041.              The file extension .asm is unnecessary; it's understood.
  3042.              The semicolon is to speed things up. If you don't use it, the
  3043.              assembler will ask you if you want to change any of the default
  3044.              choices. If you type just masm:
  3045.  
  3046.                  >masm
  3047.  
  3048.              then you need to give the name of the assembler text file on the
  3049.              first line:
  3050.  
  3051.                  Source filename [.ASM]: prog1
  3052.                  Object filename [prog1.OBJ]:
  3053.                  Source listing  [NUL.LST]:
  3054.                  Cross-reference [NUL.CRF]:
  3055.  
  3056.              but press ENTER for the other options. If you type  masm prog1:
  3057.  
  3058.                  >masm prog1
  3059.  
  3060.              You don't want to change any of the default settings:
  3061.  
  3062.                  Object filename [prog1.OBJ]:
  3063.                  Source listing  [NUL.LST]:
  3064.                  Cross-reference [NUL.CRF]:
  3065.  
  3066.              When the assembler asks you about options, hit the ENTER key.
  3067.  
  3068.              If you have made any errors, the assembler will tell you which
  3069.              line they are on and give you a description of the problem. Make
  3070.              a hard copy of them on the printer, then use your editor and find
  3071.              the line. Unfortunately, at this stage of the game, it will be
  3072.              ____________________
  3073.  
  3074.                   3 If you are using A86, then consult A86.APP. If you are
  3075.              using Turbo Assembler, then consult TASM.APP. They are both
  3076.              located in the \APPENDIX subdirectory on disk 3.
  3077.  
  3078.  
  3079.              Chapter 1 - Some Simple Programs                                5
  3080.              ________________________________
  3081.  
  3082.              very difficult for you to figure out what the problem is. You
  3083.              will have to struggle through the first 4 or 5 programs before
  3084.              things start getting easier. All the programs on these disks have
  3085.              been compiled on a Microsoft v5.1 assembler. They have assembled.
  3086.              They have been run, and they work. Don't tamper with the template
  3087.              and copy the code exactly and everything should work.
  3088.  
  3089.              If you haven't made any errors, the assembler will say:
  3090.  
  3091.                    0 Warning Errors
  3092.                    0 Severe  Errors
  3093.  
  3094.  
  3095.  
  3096.              LINKING
  3097.  
  3098.              The assembler has given you back another program named prog1.obj
  3099.              - the same name with the extension .obj. I am assuming that you
  3100.              have all used the linker with compiled programs. If you haven't,
  3101.              you may be getting in over your head by using machine language.
  3102.              All the extra subroutines are in a program called asmhelp.obj.
  3103.              Its pathname is \asmhelp\asmhelp.obj. You want to put it in the
  3104.              root directory of your current drive. In the whole book, we will
  3105.              assume that its pathname is:
  3106.  
  3107.                  \asmhelp.obj
  3108.  
  3109.              If you put it somewhere else, you will have to modify the
  3110.              pathname whenever it appears. Link the two modules by writing:
  3111.  
  3112.                  >link prog1+\asmhelp ;
  3113.  
  3114.              The copyright notice will appear:
  3115.  
  3116.                 Microsoft (R) Overlay Linker  Version 3.61
  3117.                 Copyright (C) Microsoft Corp 1983-1987.  All rights reserved.
  3118.  
  3119.              This time the file extensions are understood to be .obj. The
  3120.              semicolon is to avoid having to make default choices. If you
  3121.              type:
  3122.  
  3123.                  >link
  3124.  
  3125.              then you need to put the module names after the first prompt:
  3126.  
  3127.                  Object Modules [.OBJ]: prog1+\asmhelp
  3128.                  Run File [PROG1.EXE]:
  3129.                  List File [NUL.MAP]:
  3130.                  Libraries [.LIB]:
  3131.  
  3132.              but press ENTER for the other choices. If you type:
  3133.  
  3134.                  link prog1+\asmhelp
  3135.  
  3136.              You need to do nothing extra:
  3137.  
  3138.                  Run File [PROG1.EXE]:
  3139.  
  3140.  
  3141.              The PC Assembler Tutor                                          6
  3142.              ______________________
  3143.  
  3144.                  List File [NUL.MAP]:
  3145.                  Libraries [.LIB]:
  3146.  
  3147.              When the linker asks for choices, simply press the ENTER key.
  3148.  
  3149.              The linker gives the executable file the name of the first object
  3150.              file on the line, so you should always put your program first and
  3151.              asmhelp.obj second.
  3152.  
  3153.              If there are no errors, you are ready to go. If there are errors,
  3154.              once again, they will be very difficult to trace. Go back and
  3155.              check everything from the beginning.
  3156.  
  3157.              You are now ready to run the program. Type:
  3158.  
  3159.                  >prog1
  3160.  
  3161.              The program will start. The first thing you will see is a
  3162.              copyright notice.
  3163.  
  3164.                  The PC Assembler Helper   Version 1.0
  3165.                  Copyright (C) 1989  Chuck Nelson   All rights reserved.
  3166.  
  3167.              It appears the first time you call a subprogram in the module
  3168.              asmhelp.obj.
  3169.  
  3170.              The program will request a number. Give it any legal signed or
  3171.              unsigned number. It should be no longer than 5 digits. Press
  3172.              ENTER, and it will display the possible ways that that number can
  3173.              be thought of by the computer.
  3174.  
  3175.                  Enter any decimal number  4410
  3176.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3177.                   113AH   +04410    04410     11  : **  0001000100111010
  3178.  
  3179.                  Enter any decimal number  30486
  3180.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3181.                   7716H   +30486    30486      w 16 **  0111011100010110
  3182.  
  3183.              If the signed or unsigned number doesn't look the same as what
  3184.              you entered, then the number you entered is too big for a 16 bit
  3185.              computer. For signed numbers, the limits are +32767 to -32768 and
  3186.              for unsigned numbers, the limits are 0 to 65535.
  3187.  
  3188.                  Enter any decimal number  -64661
  3189.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3190.                   036BH   +00875    00875     03  k **  0000001101101011
  3191.  
  3192.                  Enter any decimal number  94547
  3193.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3194.                   7153H   +29011    29011      q  S *   0111000101010011
  3195.  
  3196.  
  3197.              Lets look at the numbers. Each type of output is labeled. After a
  3198.              hex number, there is an 'H' and after the characters, there is a
  3199.              '*'. This is always true. Every time you print a hex number,
  3200.              there will be an 'H', and every time you print a character, there
  3201.  
  3202.  
  3203.              Chapter 1 - Some Simple Programs                                7
  3204.              ________________________________
  3205.  
  3206.              will be a '*'. This is so you will always know what is being
  3207.              printed. Also notice that a signed integer ALWAYS has a sign and
  3208.              an unsigned integer NEVER has a sign.
  3209.  
  3210.              Not all characters are visible. Ascii 0 - 32 are invisible (32 is
  3211.              a blank). On the PC, ascii 33-255 are visible, but ascii 127 and
  3212.              ascii 255 are problematic. Therefore, if the ascii code is 0-32,
  3213.              127 or 255, that character will be printed as a hex number, not a
  3214.              character, and print_num will signal the event by printing a
  3215.              double asterisk '**' instead of a single one. This has happened
  3216.              in the first two examples. ( 11  : ** )  and  (  w 16 ** ). The
  3217.              first one is the hex number 11 followed by the character ':' and
  3218.              the second one is the character 'w' followed by the hex number
  3219.              16. Both are signalled by the double asterisk '**' instead of the
  3220.              single asterisk '*'.
  3221.  
  3222.              Do a few examples. When you are done looking at the numbers,
  3223.              press CTRL-C and you will exit the program.
  3224.  
  3225.                  Enter any decimal number  ^C
  3226.  
  3227.  
  3228.              PROGRAM 2
  3229.  
  3230.              The second program is almost the same as the first one. The
  3231.              program takes input from the keyboard and displays it in a
  3232.              variety of styles. This time, however, it is going to ask for
  3233.              different inputs: ascii, hex, binary and decimal. If you make an
  3234.              error in the input, the subroutine will prompt you again for the
  3235.              input. Here's the program:
  3236.  
  3237.  
  3238.              TEMP1.ASM
  3239.              ;+ + + + + + + + + + START CODE BELOW THIS LINE
  3240.  
  3241.              first_label:
  3242.                  call get_num             ; 1 to 5 digit signed or unsigned
  3243.                  call print_num
  3244.  
  3245.                  call get_ascii           ; 1 or 2 characters
  3246.                  call print_num
  3247.  
  3248.                  call get_binary          ; a 1 to 16 bit binary number
  3249.                  call print_num
  3250.  
  3251.                  call get_hex             ; a 1 to 4 digit hex number
  3252.                  call print_num
  3253.  
  3254.                  jmp  first_label
  3255.  
  3256.              ;+ + + + + + + + + + END CODE ABOVE THIS LINE
  3257.  
  3258.              The things to the right of the semicolons are comments. You do
  3259.              not need to type them in if you don't want to. Once again,
  3260.              assemble the program. (There should be no warning or severe
  3261.              errors. If something is wrong, it is most likely a typing error.)
  3262.              Then link it with asmhelp.obj. Remember - your program should be
  3263.  
  3264.  
  3265.              The PC Assembler Tutor                                          8
  3266.              ______________________
  3267.  
  3268.              the first one listed.{4}
  3269.  
  3270.              If all is well, run the program. It will ask you for a number
  3271.              (that is a signed or unsigned number), ascii characters, a binary
  3272.              number, and a 4 digit hex number (0-9,A-F).
  3273.  
  3274.                  Enter any decimal number  27959
  3275.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3276.                   6D37H   +27959    27959      m  7 *   0110110100110111
  3277.  
  3278.                  Enter one or two ascii characters  $%
  3279.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3280.                   2425H   +09253    09253      $  % *   0010010000100101
  3281.  
  3282.                  Enter a two byte binary number  0101111001100010
  3283.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3284.                   5E62H   +24162    24162      ^  b *   0101111001100010
  3285.  
  3286.                  Enter a two byte hex number  784d
  3287.                    HEX    SIGNED   UNSIGNED   CHAR          BINARY
  3288.                   784DH   +30797    30797      x  M *   0111100001001101
  3289.  
  3290.  
  3291.              Once again, this is an infinite loop, so in order to quit, you
  3292.              need to hit CTRL-C.
  3293.  
  3294.              The purpose of these first two programs is to remind you that the
  3295.              computer doesn't care whether you think you are storing binary
  3296.              numbers, characters, hex numbers, signed numbers or unsigned
  3297.              numbers. They all wind up in the computer as a series of 1s and
  3298.              0s, and you can use these 1s and 0s any way you like. It's up to
  3299.              you to keep track of them.
  3300.  
  3301.              If you feel comfortable with the way we are writing, assembling
  3302.              and linking programs, you are ready to start looking at the 8086
  3303.              itself.
  3304.  
  3305.  
  3306.  
  3307.  
  3308.  
  3309.  
  3310.  
  3311.  
  3312.  
  3313.              ____________________
  3314.  
  3315.                   4 For your convenience, there is a batch file on the disk
  3316.              called asmlink.bat. Its pathname is \asmhelp\asmlink.bat. It is
  3317.              one line long and looks like this:
  3318.  
  3319.                     link %1+\asmhelp ;
  3320.  
  3321.              If you use this batch file, you will never have an order problem.
  3322.              If your file is named myfile.asm, then type:
  3323.  
  3324.                     >asmlink myfile
  3325.  
  3326.  
  3327.              Chapter 1 - Some Simple Programs                                9
  3328.              ________________________________
  3329.  
  3330.              ; TEMP1.ASM        The first assembler template
  3331.  
  3332.              ; put name of program here
  3333.  
  3334.              ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3335.              STACKSEG    SEGMENT   STACK  'STACK'
  3336.  
  3337.                           dw     100 dup (?)
  3338.  
  3339.              STACKSEG    ENDS
  3340.              ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3341.              DATASTUFF    SEGMENT   PUBLIC  'DATA'
  3342.  
  3343.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  3344.  
  3345.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  3346.  
  3347.              DATASTUFF    ENDS
  3348.              ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3349.              CODESTUFF    SEGMENT   PUBLIC  'CODE'
  3350.  
  3351.                     EXTRN  print_num:NEAR , get_num:NEAR
  3352.                     EXTRN  get_ascii:NEAR , get_hex:NEAR , get_binary:NEAR
  3353.  
  3354.                     ASSUME cs:CODESTUFF, ds:DATASTUFF
  3355.  
  3356.              main   proc far
  3357.              start: push  ds               ; set up for return
  3358.                     sub   ax,ax
  3359.                     push  ax
  3360.  
  3361.                     mov   ax, DATASTUFF
  3362.                     mov   ds,ax
  3363.  
  3364.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  3365.  
  3366.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  3367.  
  3368.                     ret
  3369.  
  3370.              main   endp
  3371.  
  3372.              CODESTUFF    ENDS
  3373.              ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3374.  
  3375.                     END     start
  3376.  
  3377.  
  3378.  
  3379.  
  3380.  
  3381.  
  3382.  
  3383.  
  3384.  
  3385.  
  3386.  
  3387.  
  3388.  
  3389.              The PC Assembler Tutor                                         10
  3390.              ______________________
  3391.  
  3392.                             SUMMARY
  3393.  
  3394.              CALL
  3395.                  CALL calls a subroutine.
  3396.  
  3397.                       call get_num
  3398.  
  3399.              JMP
  3400.                  JMP jumps to the indicated label.
  3401.  
  3402.                       JMP  label47
  3403.  
  3404.              LABEL
  3405.                  A label is a name at the beginning of a line which is
  3406.                  followed by a colon. It is used to mark a spot in the
  3407.                  program.
  3408.  
  3409.                       label47:
  3410.  
  3411.  
  3412.              There are three different things which will be mentioned from
  3413.              time to time, so it's best to define them now.
  3414.  
  3415.              ASSEMBLER INSTRUCTIONS (CODE) is the text that you type in and
  3416.              give to the assembler.
  3417.  
  3418.              MACHINE CODE is the code that the assembler generates. After some
  3419.              adjustment by the linker, it is readable by the 8086. It is the
  3420.              actual code that controls the program.
  3421.  
  3422.              MICROCODE is the code that is imbedded in the 8086 itself. Each
  3423.              instruction has its own set of mini instructions within the 8086.
  3424.              This is the MICROCODE.
  3425. Chapter  2 - Data
  3426. ================
  3427.                                                                             11
  3428.  
  3429.  
  3430.  
  3431.              Before you start using data, you need to know what data looks
  3432.              like. It is not necessary for the data to have a name. For
  3433.              instance, the following definition is perfectly legal:
  3434.  
  3435.                  db   "Mary had a little lamb."
  3436.  
  3437.              Unfortunately, the assembler has no way to find it. The normal
  3438.              thing is to start the line with a name, and then give the
  3439.              definition of the data. The assembler processes the data line by
  3440.              line, so a definition on one line does not carry over to another
  3441.              line. We can have:
  3442.  
  3443.                  poem      db   "Mary had a little lamb,"
  3444.  
  3445.              Notice that names for data don't have colons after them. What if
  3446.              we wanted to continue the poem? It isn't going to fit all on one
  3447.              line. No problem. All we need to do is define the following lines
  3448.              without a name.
  3449.  
  3450.                  poem      db   "Mary had a little lamb,"
  3451.                            db   "It's fleas were white as snow,"
  3452.                            db   "And everywhere that Mary went,"
  3453.                            db   "She scratched and scratched and scratched."
  3454.  
  3455.              The assembler still can't find lines 2-4, but starting at the
  3456.              first byte of "poem", it can go all the way through the poem one
  3457.              byte after the other. By the way, there are no carriage returns
  3458.              in the poem right now. They will come later.
  3459.  
  3460.              So we have the name part, the db part, and the data part. What is
  3461.              that db anyway. It stands for Define Byte. Whenever you give the
  3462.              name "poem" to the assembler, it knows that you want to deal with
  3463.              the data one byte at a time. If you try working a word at a time,
  3464.              you will get an assembler error. The legal definitions are:
  3465.  
  3466.                  DB   define byte         [ 1 byte ]
  3467.                  DW   define word         [ 2 bytes ]
  3468.                  DD   define doubleword   [ 2X2 bytes = 4 bytes ]
  3469.                  DQ   define quadword     [ 4X2 bytes = 8 bytes ]
  3470.                  DT   define ten-byte     [ 10 bytes ]
  3471.                  DF   define farword      [ 6 bytes - used for 80386 only ]
  3472.  
  3473.              Every time you use one of these directives, the assembler
  3474.              allocates the number of bytes in brackets for EACH variable. For
  3475.              instance in:
  3476.  
  3477.                  db        "Mary had a little lamb,"
  3478.  
  3479.              each character inside the quotes is a variable. That's 23
  3480.              variables X 1 byte = 23 bytes. In:
  3481.  
  3482.              ______________________
  3483.  
  3484.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  3485.  
  3486.  
  3487.              The PC Assembler Tutor                                         12
  3488.              ______________________
  3489.  
  3490.  
  3491.                  dq        0, 1, 2, 3, 4
  3492.  
  3493.              each number is a variable. 5 variables X 8 bytes = 40 bytes.
  3494.              Notice from these examples that you can have more than one
  3495.              variable on a line but they all share the same defining type.
  3496.              What do you do if you have an uninitialized variable, i.e. you
  3497.              don't know its starting value? Easy as pie. Here's a four byte
  3498.              variable:
  3499.  
  3500.                  some_data      dd   ?
  3501.  
  3502.              The question mark lets the assembler know that you didn't forget
  3503.              the number but rather you didn't know the number.
  3504.  
  3505.              The commas are separators. When you write a comma, the assembler
  3506.              expects another piece of data on the line. If it doesn't get the
  3507.              number, it is an error. That means there can be no commas inside
  3508.              a number.
  3509.  
  3510.                  dw   32,421
  3511.  
  3512.              is two variables: 32 and 421.
  3513.  
  3514.              What if you want to make an array? The assembler has a directive
  3515.              for that too:
  3516.  
  3517.                  dw   150  dup ( 400 )
  3518.  
  3519.              The 'dup' is for duplicate. This makes 150 two byte copies and
  3520.              puts the number 400 in each one.
  3521.  
  3522.                  db   273  dup ( 'c' )
  3523.  
  3524.              This makes 273 one byte copies and puts the letter 'c' in each
  3525.              one.
  3526.  
  3527.                  dd   459  dup ( 1, 2, 3, 4, 5 )
  3528.  
  3529.              This makes 459 copies of what is inside the parentheses. That
  3530.              means  ( 5 variables X 4 bytes ) X 459 for a total of 9180 bytes.
  3531.              Starting from the beginning of the array, we will have the
  3532.              sequence: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3,...
  3533.  
  3534.                  dq   20000 dup ( 455 )
  3535.  
  3536.              This makes 20000 eight byte copies and causes an assembler error
  3537.              because there is a limit of 65,536 bytes for the data and you
  3538.              have used 160,000 bytes (20,000 X 8).
  3539.  
  3540.                  db   7  dup ( 'Mary had a little lamb,')
  3541.  
  3542.              This makes 7 copies of 'Mary had ..' which is 23 bytes, for a
  3543.              total of 161 bytes.
  3544.  
  3545.                  dw   39 dup ( 28 dup ( 0 ) )
  3546.  
  3547.  
  3548.  
  3549.              Chapter 2 - Data                                               13
  3550.              ________________
  3551.  
  3552.              The assembler even supports nesting, so you can make a
  3553.              multi-dimensional array. This is a 39 X 28 array initialized to
  3554.              zero. 39 copies of 28 two byte numbers is 2184 bytes.
  3555.  
  3556.              The standard form for arrays is (1) first define the data type,
  3557.              (2) then say how long the array is followed by the keyword "dup"
  3558.              and (3) put the initial value inside the parentheses. What if you
  3559.              don't know the initial value? Simple:
  3560.  
  3561.                  dw   347  dup ( ? )
  3562.  
  3563.              The question mark lets the assembler know that you don't know.
  3564.  
  3565.  
  3566.              DEFINING NUMBERS
  3567.  
  3568.              What kinds of data can you have?
  3569.  
  3570.              1. A single character inside single or double quotes:
  3571.                       'a' , "&" , '|'
  3572.  
  3573.              2. A string inside single or double quotes:
  3574.  
  3575.                  "Mary had a little lamb,"
  3576.                  'Mary had a little lamb,'
  3577.  
  3578.              Each character is stored as a byte, and the bytes are stored
  3579.              consecutively. If the array starts at address 2743, 2743 = 'M',
  3580.              2744 = 'a', 2745 = 'r', 2746 = 'y', 2747 = ' ', etc. As usual in
  3581.              these instances, if you want a double quote inside a double
  3582.              quoted string or a single quote inside a single quoted string,
  3583.              you need to use a pair:
  3584.  
  3585.                  "Mary asked her fleas ""Why don't you join the circus?"""
  3586.                  'Mary asked her fleas "Why don''t you join the circus?"'
  3587.  
  3588.              3. A decimal number. Decimal is the default:
  3589.  
  3590.                  27, 44, 641, 89
  3591.  
  3592.              4. A hex number. A hex number must start with a number, so if the
  3593.              highest digit is A - F, there must be a 0 in front.{1} b77h is
  3594.              illegal, 0b77h is legal. All hex numbers must be followed by an
  3595.              'h':
  3596.  
  3597.                  0a162H , 0329H , 0DDDh , 7h
  3598.  
  3599.              5. An octal (base 8) number. An octal is followed either by the
  3600.              ____________________
  3601.  
  3602.                   1 When the assembler looks at something it needs to know
  3603.              whether it is a name or a number. Is 'A7' a name or a hex number?
  3604.              Is '3D' a name or a number? To solve this problem, all assemblers
  3605.              and all compilers insist that -> if the first character is a
  3606.              number, it's a number; if the first character is not a number, it
  3607.              is not a number. That is why you can't start a variable name with
  3608.              a number.
  3609.  
  3610.  
  3611.              The PC Assembler Tutor                                         14
  3612.              ______________________
  3613.  
  3614.              letter q or the letter o:
  3615.  
  3616.                  641q , 2345o , 1472o
  3617.  
  3618.              6. A binary number. A binary number is followed by a b:
  3619.  
  3620.                  0100100b , 1b , 01001000111010b
  3621.  
  3622.  
  3623.  
  3624.              Any of these types can be mixed on a line. For instance:
  3625.  
  3626.                  db   "Mary had a little lamb," , 13 , 10
  3627.  
  3628.              13 followed by 10 is CRLF, the PC signal for a carriage return. A
  3629.              string in the C language ends with the number 0. If we wanted a C
  3630.              string with CRLF, we would have:
  3631.  
  3632.                  db   "Mary had a little lamb," , 13 , 10 , 0
  3633.  
  3634.              Another mixed example:
  3635.  
  3636.                  dw   7 , 010010b , 0FFFFh , 037q
  3637.  
  3638.              is dopey but legal.
  3639.  
  3640.  
  3641.              You can also have an equation, as long it resolves to a number.
  3642.              This calculation is done by the assembler, so the values of
  3643.              variables are not allowed:
  3644.  
  3645.                       dw   ( ( 19 * 7 * 25 ) + 6 ) / ( 9 + 7 )
  3646.  
  3647.              is legal, but:
  3648.  
  3649.              data1    dw   25
  3650.              data2    dw   7
  3651.                       dw   ( ( 19 * 7 * data1 ) + 6 ) / ( 9 + data2 )
  3652.  
  3653.              is illegal. Everything must be a constant. Remember that when the
  3654.              assembler starts calculating it might truncate the partial
  3655.              answers, so don't get too fancy.
  3656.  
  3657.  
  3658.  
  3659.              Chapter 2 - Data                                               15
  3660.              ________________
  3661.  
  3662.                                           SUMMARY
  3663.  
  3664.              The assembler works one line at a time. Each line with data must
  3665.              start with a data type declaration (after an optional name.)
  3666.  
  3667.                                         DATA TYPES
  3668.  
  3669.                  DB   define byte         ( 1 byte )
  3670.                  DW   define word         ( 2 bytes )
  3671.                  DD   define doubleword   ( 2X2 bytes = 4 bytes )
  3672.                  DQ   define quadword     ( 4X2 bytes = 8 bytes )
  3673.                  DT   define ten-byte     ( 10 bytes )
  3674.                  DF   define farword      ( 6 bytes - used for 80386 only )
  3675.  
  3676.  
  3677.                                    COMMON INTEGER TYPES
  3678.  
  3679.                  TYPE                MAX SIGNED               MAX UNSIGNED
  3680.  
  3681.                  byte                -128/+127                255
  3682.                  word              -32768/+32767              65535
  3683.                  doubleword     -2147483648/+2147483647       4294967295
  3684.  
  3685.                  Note that the max. negative integer is 1 larger than the
  3686.                  max. positive integer.
  3687.  
  3688.  
  3689.                                POSSIBLE BASES FOR CONSTANTS
  3690.  
  3691.                  b         binary data
  3692.                  o,q       octal data
  3693.                  d         decimal data  (default)
  3694.                  h         hex data  (must start with a number 0 - 9)
  3695.  
  3696.  
  3697.                                      ARRAY DEFINITIONS
  3698.  
  3699.                  d*        num1      dup ( data1 )
  3700.  
  3701.              Using the d* data type (db, dw, dd, dq, etc.) make num1 copies of
  3702.              data1 (data1 may be either a single piece of data or a group of
  3703.              data.)
  3704.  
  3705.                                  MULTIPLE DATA ON ONE LINE
  3706.  
  3707.              Different data elements on the same line are separated by commas.
  3708.              All elements on the same line have the same data type.
  3709. Chapter  3 - ASMHELP
  3710. ===================
  3711.                                                                             16
  3712.  
  3713.  
  3714.              We are now going to introduce both the 8086 registers and a
  3715.              program for looking at them. You are going to get information
  3716.              flying at you at a rapid pace, so read both this and the next
  3717.              chapter carefully and slowly.
  3718.  
  3719.  
  3720.              REGISTERS
  3721.  
  3722.              The 8086 has a number of registers. Remember, registers are
  3723.              places for storing data that are internal to the 8086 chip. They
  3724.              are much faster, but there are very few of them.
  3725.  
  3726.              There are 6 registers that you can use for addition and subtrac-
  3727.              tion of word (2 byte) sized numbers, as well as logical opera-
  3728.              tions on word (2 byte) numbers or data. These registers are AX,
  3729.              BX, CX, DX, SI, DI. In addition, there is a register which works
  3730.              the same way, but has a special function in all high-level
  3731.              languages (Basic, Pascal, C, etc.). This is BP, the base pointer.
  3732.  
  3733.              There is one more register that performs the same operations as
  3734.              the above seven, but it is RESERVED for special use and should
  3735.              never be used for anything. It is called SP (the stack pointer).
  3736.  
  3737.              There are 4 registers that tell the 8086 which memory segments
  3738.              you are in. They just sit there and help the 8086 find things in
  3739.              memory. You will learn how they work later. They are CS, DS, SS,
  3740.              ES. (That is Code Segment, Data Segment, Stack Segment and Extra
  3741.              Segment respectively).
  3742.  
  3743.              There is the flags register which contains all the information
  3744.              the 8086 needs to evaluate its state. We will learn about this
  3745.              later. The flags register has no name, and there are machine
  3746.              instructions for manipulating individual flags in the register.
  3747.  
  3748.              Finally, there is IP, the instruction pointer, which points to
  3749.              the machine instructions. You have no direct access to this,
  3750.              which is good because you would screw it up for certain. The 8086
  3751.              handles the IP automatically and correctly.
  3752.  
  3753.              One word (two bytes or 16 bits) is the largest piece of data that
  3754.              the 8086 can handle naturally. It is possible to handle larger
  3755.              things, but we do it through software (which is slower), not
  3756.              hardware (which is faster). Sometimes we want to handle things
  3757.              one byte at a time as when we work with characters. The 8086
  3758.              gives us this possibility by letting us divide the AX, BX, CX,
  3759.              and DX registers into an upper half and a lower half. For any or
  3760.              all of these registers, we can replace one 2 byte register by two
  3761.              1 byte registers. The data in the full register stays the same,
  3762.              but we can look at each half. The two parts of AX are called AH
  3763.              (for A high) and AL (for A low).
  3764.  
  3765.                  |------AX------|
  3766.                  0000000000000000
  3767.                  |--AH--||--AL--|
  3768.  
  3769.  
  3770.  
  3771.              Chapter 3 - ASMHELP.OBJ                                        17
  3772.              _______________________
  3773.  
  3774.              This is the 16 bit binary number 0 in the AX register. Using AX
  3775.              allows us to manipulate all 16 bits. Using AH allows us to
  3776.              manipulate the upper 8 bits (without affecting the lower 8 bits),
  3777.              and using AL allows us to manipulate the lower 8 bits without
  3778.              affecting the upper 8 bits. Similarly, for BX we have BH, BL, for
  3779.              CX we have CH, CL, and for DX we have DH, DL.
  3780.  
  3781.  
  3782.              SHOW_REGS
  3783.  
  3784.              We have named all the registers, now let's take a look at them.
  3785.              Included in the module asmhelp.obj is a program called show_regs.
  3786.              It shows all the above registers on the top 10 lines of your
  3787.              screen and allows you to enter data normally underneath.
  3788.  
  3789.              When you call show_regs, it puts the current value of all the
  3790.              registers on the screen. Those values stay on the screen until
  3791.              the next call - i.e. the program does not change what is on the
  3792.              screen even though the registers may be changing value. You need
  3793.              to call show_regs every time that you want to see the current
  3794.              values of the registers.
  3795.  
  3796.              The first time you call show_regs, it clears the screen so you
  3797.              should call it right at the beginning of the program in order to
  3798.              initialize the screen.
  3799.  
  3800.              This time we want temp2.asm for a template; we will call this
  3801.              program prog3.asm, so make a copy of temp2.asm and give it the
  3802.              new name.  Let's take a look at it. The only differences are (1)
  3803.              there are a lot more programs in the EXTRN statements and (2) in
  3804.              the data segment DATASTUFF there are these definitons:
  3805.  
  3806.              ax_byte  db   2
  3807.              bx_byte  db   2
  3808.              cx_byte  db   2
  3809.              etc.
  3810.  
  3811.              These will be used for show_regs later, but you need to learn a
  3812.              few assembler instructions first.
  3813.  
  3814.              Here's our next program:
  3815.  
  3816.              TEMP2.ASM
  3817.              + + + + + + + + START CODE BELOW THIS LINE
  3818.                  call show_regs
  3819.  
  3820.              label_one:
  3821.                  call get_hex
  3822.                  call show_regs
  3823.  
  3824.                  call get_num
  3825.                  call show_regs
  3826.  
  3827.                  jmp  label_one
  3828.  
  3829.              + + + + + + + + END CODE ABOVE THIS LINE
  3830.  
  3831.  
  3832.  
  3833.              The PC Assembler Tutor                                         18
  3834.              ______________________
  3835.  
  3836.              That's all the program does. It asks for a number, then calls
  3837.              show_regs to show us what is in the registers. Note that one of
  3838.              the numbers is hex while the other number is decimal.
  3839.  
  3840.              Compile this, and link it with
  3841.  
  3842.              >link prog3+\asmhelp ;
  3843.  
  3844.              and we're ready to go.
  3845.  
  3846.              The program reads information in the computer to find out what
  3847.              kind of monitor you have and where the screen output goes. It
  3848.              then puts the register information on the top lines. If it
  3849.              doesn't appear there, we have a screwup somewhere. The text
  3850.              should appear in black and white, but if you have a color monitor
  3851.              you can make it a blue background with white letters.{1}
  3852.  
  3853.  
  3854.              *********************** SCREEN SHOT ***************************
  3855.  
  3856.                  AX  19825                             SI  00000
  3857.                  BX  00000                             DI  00256
  3858.                  CX  00255                             BP  17113
  3859.                  DX  02596                             SP  00508
  3860.  
  3861.                  CS  0AA4H   DS  0A54H   ES  0A24H   SS  0A34H   IP  0018H
  3862.  
  3863.                  OF   DF  IEF   TF   SF   ZF   AF   PF   CF
  3864.                        +    x         +    x         E            COUNT  00003
  3865.  
  3866.              -----------------------------------------------------------------
  3867.  
  3868.              The PC Assembler Helper   Version 1.0
  3869.              Copyright (C) 1989  Chuck Nelson   All rights reserved.
  3870.              Enter a two byte hex number  4df9
  3871.              Enter any decimal number  +19825
  3872.              Enter a two byte hex number
  3873.  
  3874.  
  3875.              *****************************************************************
  3876.  
  3877.              This is how the screen looks after entering first a hex number,
  3878.              then a decimal number. The numbers in the registers will probably
  3879.              be different for you. Note that AX contains the last number that
  3880.              was entered. On the left side you will see the AX, BX, CX and DX
  3881.              ____________________
  3882.  
  3883.                 1 To make a blue background and white letters, insert the code
  3884.              "call  set_blue" before the FIRST "call show_regs". i.e.:
  3885.  
  3886.                       call  set_blue
  3887.                       call  show_regs
  3888.  
  3889.                  label_one:
  3890.                       etc.
  3891.  
  3892.              This only works if it is allowed by the video board.
  3893.  
  3894.  
  3895.              Chapter 3 - ASMHELP.OBJ                                        19
  3896.              _______________________
  3897.  
  3898.              registers. For the time being, these registers will display
  3899.              unsigned numbers. On the right are the SI, DI, BP and SP
  3900.              registers. They are also unsigned numbers for the moment. Below
  3901.              that are the segment registers CS, DS, ES, and SS and the
  3902.              instruction pointer (IP). These are hex numbers and will always
  3903.              be hex numbers. The bottom line has OF, DF, IEF, etc.  These are
  3904.              the flags, and the marking underneath them (either a blank or
  3905.              some character) tells how they are set. Finally we have COUNT.
  3906.              This has nothing to do with the 8086. It is a counter that is
  3907.              incremented each time you call show_regs.
  3908.  
  3909.              Keep entering numbers and watch the registers. You will notice
  3910.              that three things are changing - AX, IP and COUNT. AX has the
  3911.              last number you entered and IP keeps changing. Write down the
  3912.              value of IP each time it changes. It goes back and forth between
  3913.              two numbers. That is because you call show_regs in two different
  3914.              places in the loop, {2} and those are two different places in
  3915.              memory where the 8086 is reading the machine code.
  3916.  
  3917.              Why is AX changing? You may have wondered in prog1.asm and
  3918.              prog2.asm how that information was going back and forth between
  3919.              your program and asmhelp.obj. The answer is that in all the
  3920.              programs in asmhelp.obj, if you need to pass information, it is
  3921.              passed via register AX. This is not the normal way to pass
  3922.              information. The normal way is more elegant but more complicated.
  3923.              We will cover that much later. The counter, of course, increases
  3924.              by 1 each time you call show_regs.
  3925.  
  3926.              Try entering a few more numbers and then it's time to go on to
  3927.              the next program.
  3928.  
  3929.  
  3930.              MOVING DATA
  3931.  
  3932.              Obviously, we want to move data from memory to the 8086, from the
  3933.              8086 to memory, and between registers. We have the following
  3934.              possibilities:
  3935.  
  3936.                  (1)  move from a register to memory
  3937.                  (2)  move from memory to a register
  3938.                  (3)  move a constant to memory
  3939.                  (4)  move a constant to a register
  3940.                  (5)  move from one register to another register
  3941.  
  3942.              That's it. There is no 8086 instruction that moves a single word
  3943.              or a byte from one place in memory to another.
  3944.  
  3945.              The move mnemonic for the 8086 is "mov".{3}  We need some sample
  3946.              data:
  3947.              ____________________
  3948.  
  3949.                 2 IP actually has three different values, since you call
  3950.              show_regs once before you enter the loop.
  3951.  
  3952.                 3 A mnemonic is a name for a machine instruction, which sounds
  3953.              like what the instruction is supposed to do - MOV for move, SUB
  3954.              for subtract, IMUL for integer multiplication, etc.
  3955.  
  3956.  
  3957.              The PC Assembler Tutor                                         20
  3958.              ______________________
  3959.  
  3960.  
  3961.              EXAMPLE DATA
  3962.  
  3963.              this_year     dw   1989
  3964.              total         dw   ?
  3965.              average       dw   ?
  3966.              time          dw   7
  3967.  
  3968.              age           db   ?         ; I hope you aren't older than 255
  3969.              poem          db   "In 1492 Columbus played a mean kazoo."
  3970.              secret_code   db   3Bh
  3971.              character     db   ?
  3972.  
  3973.              Here is some sample code. To move from register to memory, we
  3974.              have:
  3975.  
  3976.                       mov  total, ax
  3977.                       mov  time, si
  3978.                       mov  age, cl
  3979.                       mov  character, bh
  3980.  
  3981.              The first thing that strikes the eye is that the destination is
  3982.              on the left and the source is on the right.{4}  This is standard
  3983.              for the 8086 instruction set, and it's going to take some getting
  3984.              used to. DESTINATION ON LEFT, SOURCE ON RIGHT. You are going to
  3985.              blow this one from time to time, so always double check to make
  3986.              sure that they are in the right order.
  3987.  
  3988.              Also note that the 1 byte registers are matched up with 1 byte
  3989.              variables, and 2 byte registers are matched up with 2 byte data.
  3990.              If the sizes don't match, the assembler will complain.{5}  Thus:
  3991.  
  3992.                       mov  age, ax
  3993.  
  3994.              is an illegal instruction.
  3995.  
  3996.              For examples of memory to register moves, we have:
  3997.  
  3998.                       mov  ch, secret_code
  3999.                       mov  di, this_year
  4000.                       mov  dl, poem       ; moves 'I' to dl
  4001.                       mov  bx, time
  4002.              ____________________
  4003.  
  4004.                 4 The computer community likes the words "destination" and
  4005.              "source". "Source" means FROM, and "destination" means TO. The
  4006.              8086 instruction set is designed:
  4007.  
  4008.                  MOV  TO, FROM
  4009.  
  4010.              which is exactly opposite to the way you would say it in an
  4011.              English sentence. For the 8086, it is always destination on the
  4012.              left, source on the right.
  4013.  
  4014.                 5 Half register and full register operations have different
  4015.              machine codes, and the assembler needs to know which code to use,
  4016.              so the two things must be the same number of bytes.
  4017.  
  4018.  
  4019.              Chapter 3 - ASMHELP.OBJ                                        21
  4020.              _______________________
  4021.  
  4022.  
  4023.              Once again: (1) DESTINATION ON LEFT, SOURCE ON RIGHT, and (2) the
  4024.              sizes of the two objects must match.
  4025.  
  4026.              For constant to memory we have:
  4027.  
  4028.                       mov  total, 2986
  4029.                       mov  age, 36h
  4030.                       mov  secret_code, 0110100b
  4031.                       mov  average, (27/5) + 3
  4032.  
  4033.              The arithmetic is done by the assembler and anything that is made
  4034.              up totally of constants is legal. Thus:
  4035.  
  4036.                       mov  average, (((64+27)*51 )/(196-82))
  4037.  
  4038.              is legal but:
  4039.  
  4040.                       mov  average, this_year/time
  4041.  
  4042.              is illegal. The assembler makes either a one byte or a two byte
  4043.              constant to match the size of the destination. The constants for
  4044.              "total" and "average" are two byte constants while those for
  4045.              "age" and "secret_code" are one byte constants. The constants
  4046.              must be within range, that is -129 is too negative for a byte,
  4047.              256 is too positive for a byte, -32769 is too negative for a
  4048.              word, 65536 is too positive for a word. The assembler will give
  4049.              an error if the constant is out of range.
  4050.  
  4051.              You can also move a constant to a register:
  4052.  
  4053.                       mov  al, 'c'
  4054.                       mov  ax, 'c'
  4055.                       mov  di, 46280
  4056.                       mov  bl, 99
  4057.  
  4058.              The same rules apply. The constant must be within range and the
  4059.              assembler will make a constant the same size as the destination
  4060.              register (one or two bytes). These constants are actually
  4061.              imbedded in the machine code by the assembler, and are
  4062.              unchangable.
  4063.  
  4064.              Lastly, you can move data from one register to another:
  4065.  
  4066.                       mov  ax, cx         ; from cx to ax
  4067.                       mov  ah, bl         ; from bl to ah
  4068.                       mov  dl, dh         ; from dh to dl
  4069.                       mov  di, dx         ; from dx to di
  4070.  
  4071.              All this is summarized at the end of the chapter.
  4072.  
  4073.  
  4074.              It's time for program #4. All this program is going to do is get
  4075.              input and the move it to different registers. We are still using
  4076.              temp2.asm. Here's the program:
  4077.  
  4078.              TEMP2.ASM
  4079.  
  4080.  
  4081.              The PC Assembler Tutor                                         22
  4082.              ______________________
  4083.  
  4084.              ;+ + + + + + + + + + START DATA BELOW THIS LINE
  4085.  
  4086.              byte_data  db ?
  4087.              word_data  dw ?
  4088.  
  4089.              ;+ + + + + + + + + + END DATA ABOVE THIS LINE
  4090.  
  4091.              ;+ + + + + + + + + + START CODE BELOW THIS LINE
  4092.  
  4093.                  call show_regs
  4094.  
  4095.              This_is_a_very_long_label_name:
  4096.                  call get_hex                       ; (1)
  4097.                  call show_regs_and_wait
  4098.  
  4099.                  mov  dx, ax                        ; (2)
  4100.                  call show_regs_and_wait
  4101.  
  4102.                  mov  byte_data, al                 ; (3)
  4103.                  mov  ch, byte_data
  4104.                  call show_regs_and_wait
  4105.  
  4106.                  mov  word_data, ax                 ; (4)
  4107.                  mov  di, word_data
  4108.                  call show_regs
  4109.  
  4110.                  jmp  This_is_a_very_long_label_name
  4111.  
  4112.              ;+ + + + + + + + + + END CODE ABOVE THIS LINE
  4113.  
  4114.              There is a data section in this one, so copy those variables into
  4115.              your data section. Here is what the program does. (1) it gets a
  4116.              hex number from the keyboard, (2) it moves the number in ax to
  4117.              dx, (3) it moves one byte from al to ch via the variable
  4118.              byte_data, and (4) it moves two bytes from ax to di via
  4119.              word_data.
  4120.  
  4121.              There are two different subprograms - show_regs and
  4122.              show_regs_and_wait. They do the same thing except that
  4123.              show_regs_and_wait waits for you to hit the ENTER key before
  4124.              continuing. The computer works so fast that we wouldn't be able
  4125.              to see the changes in the screen if we didn't have a way of
  4126.              pausing. You can use COUNT on the screen to keep track of exactly
  4127.              where you are in the loop.
  4128.  
  4129.              Assemble program 4, link it to asmhelp.obj, and watch it work.
  4130.              There are two things to notice here. First, we are entering a hex
  4131.              number, but AX is displaying an unsigned number. It is not
  4132.              self-evident that the unsigned number in AX is the same as the
  4133.              hex number that you are entering. Secondly, though CH is
  4134.              changing, there doesn't seem to be any relationship between the
  4135.              number in AX and the number in CX. We will solve both problems in
  4136.              the next chapter.
  4137.  
  4138.  
  4139.  
  4140.              Chapter 3 - ASMHELP.OBJ                                        23
  4141.              _______________________
  4142.  
  4143.                                           SUMMARY
  4144.  
  4145.  
  4146.                            MOVING DATA
  4147.  
  4148.              You can:
  4149.                  (1)  move from a register to memory
  4150.                  (2)  move from memory to a register
  4151.                  (3)  move a constant to memory
  4152.                  (4)  move a constant to a register
  4153.                  (5)  move from one register to another register
  4154.  
  4155.                  The constants are actual constants which are imbedded in the
  4156.                  machine code.
  4157.  
  4158.                            REGISTERS
  4159.  
  4160.              The normal registers are AX, BX, CX, DX, SI, DI AND BP. AX, BX,
  4161.              CX, and DX can be divided into AH-AL, BH-BL, CH-CL AND DH-DL. The
  4162.              'H' is for high and the 'L' is for low.
  4163.              SP is committed and may not be used.
  4164.              The segment registers are CS, DS, ES, SS.
  4165.              The instruction pointer (IP) is not available to you.
  4166.              The register that holds the flags is manipulated by special
  4167.              machine instructions.
  4168.  
  4169.  
  4170.  
  4171.                            ASMHELP.OBJ
  4172.  
  4173.              Call show_regs to see what is in the 8086 registers. Count
  4174.              increments by one every time you call show_regs.
  4175.  
  4176.              show_regs_and_wait is the same as show_regs except that it waits
  4177.              for you to hit the ENTER key to allow you time to look at the
  4178.              screen.
  4179.  
  4180.              Call set_blue at the outset if you have a color card and a color
  4181.              monitor and you want to have a blue background.
  4182.  
  4183.              get_num gets a signed or unsigned number from the keyboard. (1-5
  4184.              digits). It does no range checking to see whether the number is
  4185.              too big. All other input routines check to see if a number is too
  4186.              large for its data size.
  4187.  
  4188.              get_hex gets a hex number from the keyboard.  (1-4 digits)
  4189.  
  4190.              get_ascii gets characters from the keyboard.  (1 or 2 characters)
  4191.  
  4192.              get_binary gets a binary number from the keyboard (1 - 16 digits)
  4193.  
  4194.              print_num prints a number as hex, signed, unsigned, char, and
  4195.              binary.
  4196.  
  4197.              All data transfer to or from ASMHELP.OBJ is via the AX register.
  4198. Chapter  4 - SHOW_REGS
  4199. =====================
  4200.                                                                             24
  4201.  
  4202.  
  4203.  
  4204.              We got started using the program show_regs in the last chapter,
  4205.              but we have already run into problems. The hex number doesn't
  4206.              look the same once we put it in the register - that's because
  4207.              what we are seeing in the arithmetic registers is an unsigned
  4208.              number. Also, when we moved a byte from AL to CH, it was clear
  4209.              that something had moved, but it wasn't clear what the number
  4210.              was. There are two problems here:
  4211.  
  4212.                  (1) We want to use data in hex, binary, ascii, unsigned and
  4213.                  signed format depending on what we are doing in the program.
  4214.  
  4215.                  (2) Some of the registers can be used as half registers, so
  4216.                  we want a whole register when we need it and a half register
  4217.                  when we need it.
  4218.  
  4219.              Nooooooo problem. There are eight registers whose formats we want
  4220.              to be able to change: AX, BX, CX, DX, SI, DI, BP and SP. We need
  4221.              to give each one a code to tell it what to display. The code is
  4222.              the following:
  4223.  
  4224.                  signed number       1
  4225.                  unsigned number     2
  4226.                  binary number       3
  4227.                  hex number          4
  4228.                  ascii               5
  4229.  
  4230.              Also, we need to know whether AX, BX, CX and DX are half or full
  4231.              registers. The code for that is:
  4232.  
  4233.                  half register       128       (80 hex)
  4234.                  full register       0
  4235.  
  4236.              We will need to do two things - set up the codes, then tell
  4237.              show_regs about the code. We'll begin by setting up the codes.
  4238.  
  4239.              First let's start with SI, DI, BP and SP. They must be full
  4240.              registers, so the half register information is irrelevant.{1}  In
  4241.              the data section is a set of data starting ax_byte, bx_byte ...
  4242.              sp_byte. That is where you need to put the code. Don't change the
  4243.              order of these variables. Just put the correct formatting code in
  4244.              the appropriate byte.
  4245.  
  4246.                  mov  si_byte, 3
  4247.  
  4248.              will display SI as a binary number.
  4249.  
  4250.              ____________________
  4251.  
  4252.                 1 show_regs is very forgiving. It only recognizes half
  4253.              registers where appropriate, and if you screw up on the format
  4254.              code, it just makes it an unsigned number.
  4255.  
  4256.              ______________________
  4257.  
  4258.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  4259.  
  4260.  
  4261.              Chapter 4 - SHOW_REGS                                          25
  4262.              _____________________
  4263.  
  4264.                  mov  bp_byte, 4
  4265.  
  4266.              will display BP as a hex number.
  4267.  
  4268.                  mov  di_byte, 1
  4269.  
  4270.              will display DI as a signed number.
  4271.  
  4272.              That's pretty easy. If you are using AX, BX, CX, or DX as a full
  4273.              register, they are exactly the same.
  4274.  
  4275.                  mov  dx_byte, 5     ; full register, character format
  4276.                  mov  ax_byte, 2     ; full register, unsigned format
  4277.                  mov  bx_byte, 3     ; full register, binary
  4278.  
  4279.              If we use half registers we need to pass more information.
  4280.              show_regs needs to know that it is half registers, not full
  4281.              registers; it also needs to know what the left format is and what
  4282.              the right format is. This is easier than it sounds but will take
  4283.              a little getting used to. The right nibble (half byte) gets the
  4284.              right format and the left nibble (half byte) gets the left
  4285.              format. Then you add 128 to the total. This works easily in hex.
  4286.              13h means that the left half register is (1 = signed) and the
  4287.              right register is (3 = binary). Then we add (128 = 80h) for a
  4288.              total of 93h. Here are some more examples. Remember, 8 + 1 = 9, 8
  4289.              + 2 = A, 8 + 3 = B, 8 + 4 = C, 8 + 5 = D
  4290.  
  4291.                  C4h       ; 80h + 44h  left and right are hex
  4292.                  A5h       ; 80h + 25h  left is unsigned, right is ascii
  4293.                  B1h       ; 80h + 31h  left is binary, right is signed
  4294.                  D2h       ; 80h + 52h  left is ascii, right is unsigned
  4295.                  94h       ; 80h + 14h  left is signed, right is hex
  4296.  
  4297.              There is a summary at the end of the chapter giving all the
  4298.              commands and codes for show_regs. It is important to take some
  4299.              time here and learn to make the registers look the way we want,
  4300.              because later on we have machine instructions for signed numbers,
  4301.              for ascii characters, for binary numbers, and you need to see
  4302.              what the registers look like in the appropriate formats.
  4303.              Spending a little time right now will save you a lot of time
  4304.              later on.
  4305.  
  4306.              If you don't like using hex numbers you can use decimal numbers:
  4307.  
  4308.                  code = type_of_register + left code + right code
  4309.  
  4310.              where full register = 0, half register = 128d and:
  4311.  
  4312.                  NUMBER FORMAT        LEFT CODE     RIGHT CODE
  4313.  
  4314.                  signed                   16d            1d
  4315.                  unsigned                 32d            2d
  4316.                  binary                   48d            3d
  4317.                  hex                      64d            4d
  4318.                  ascii                    80d            5d
  4319.  
  4320.              Therefore, left binary, right hex = 128 + 48 + 4 = 180d. Some
  4321.  
  4322.  
  4323.              The PC Assembler Tutor                                         26
  4324.              ______________________
  4325.  
  4326.              more examples:
  4327.  
  4328.                  left ascii, right signed = 128 + 80 + 1 = 209d
  4329.                  left signed, right binary = 128 + 16 + 3 = 147d
  4330.                  left hex, right unsigned = 128 + 64 + 2 = 192d
  4331.  
  4332.              We can put in the code the same way as before.
  4333.  
  4334.                  mov  ax_byte, 192d
  4335.                  mov  dx_byte, 147d
  4336.                  mov  cx_byte, 0D2h
  4337.                  mov  bx_byte, 94h
  4338.  
  4339.              We now have the codes in out program, but show_regs doesn't know
  4340.              about them. In order to give the information to show_regs, we
  4341.              call set_reg_style. set_reg_style makes a copy of your
  4342.              information for use by show_regs. The next time that you call
  4343.              show_regs, it will use the new register formats. There is a small
  4344.              problem, however. The information we have is eight bytes long,
  4345.              and ax is only two bytes long. How do we pass the information?
  4346.              The answer is: we don't pass the information. Instead, we pass
  4347.              the address of the information. If you look in the data segment
  4348.              of temp2.asm, you will see that ax_byte is the first byte of this
  4349.              data. We pass the address of ax_byte (the first byte) and
  4350.              set_reg_style knows that that address plus the following 7
  4351.              addresses have the information that it needs. There is a special
  4352.              machine instruction for putting an address in a register - it is
  4353.              LEA or load effective address. It looks like this:
  4354.  
  4355.                  lea  ax, ax_byte
  4356.  
  4357.              This instruction says: put the address of ax_byte in the AX
  4358.              register. Combined with the call, we have:
  4359.  
  4360.                  lea  ax, ax_byte
  4361.                  call set_reg_style
  4362.  
  4363.  
  4364.              Before we start writing programs with set_reg_style, we will run
  4365.              a pre-existing program called SETREGS.EXE. Its pathname is
  4366.              \ASMHELP\SETREGS.EXE. It puts the same (pseudo) random number in
  4367.              all arithmetic registers except SP, then requests a formatting
  4368.              code for each register. After cycling through all the registers,
  4369.              it asks you to press ENTER. It then puts a new random number in
  4370.              the registers and starts the cycle again. The hex codes are
  4371.              displayed on the screen before each request. As usual, use
  4372.              Control-C to exit the program.
  4373.  
  4374.              Here is what the screen might look like after the first cycle.
  4375.              The prseudo random number 2571 will be the same, but your
  4376.              formatting might be different:
  4377.  
  4378.  
  4379.  
  4380.  
  4381.  
  4382.  
  4383.  
  4384.  
  4385.              Chapter 4 - SHOW_REGS                                          27
  4386.              _____________________
  4387.  
  4388.              *********************** SCREEN SHOT ***************************
  4389.                  AX +02571                             SI +02571
  4390.                  BH  00001010      BL  0B **           DI  02571
  4391.                  CX  0A 0B **                          BP  0000101000001011
  4392.                  DH  0A **         DL  0B **           SP  00C4H
  4393.  
  4394.                  CS  0AA4H   DS  0A42H   ES  0A25H   SS  0A35H   IP  0115H
  4395.  
  4396.                  OF   DF  IEF   TF   SF   ZF   AF   PF   CF
  4397.                   x    +    x         +              O    x      COUNT  00009
  4398.              ----------------------------------------------------------------
  4399.              hex = C0h or 4h;   ascii = D0h or 5h
  4400.              Enter a code for sp.
  4401.              Enter a one byte hex number  4
  4402.              Press ENTER to continue
  4403.  
  4404.              *****************************************************************
  4405.  
  4406.              The formats I have used are:
  4407.                  AX   full register  (signed)
  4408.                  BX   half registers (binary, ascii)
  4409.                  CX   full register  (ascii)
  4410.                  DX   half registers (ascii, ascii)
  4411.                  SI   full register  (signed)
  4412.                  DI   full register  (unsigned)
  4413.                  BP   full register  (binary)
  4414.                  SP   full register  (hex)
  4415.  
  4416.              Cycle through the registers a couple of times. If you make them
  4417.              binary, they get longer, if you make them hex or ascii they get
  4418.              shorter; a sign appears if they are signed, and you can change
  4419.              from full to half registers for AX, BX, CX and DX.
  4420.  
  4421.              You will always be able to tell what kind of number show_regs is
  4422.              printing because (1) a signed number always has a + or - in front
  4423.              of it, (2) a hex number always has an h after it, (3) a binary
  4424.              number is 8 digits long for a half register or 16 digits long for
  4425.              a full register, and (4) an ascii has an asterisk after it. Just
  4426.              as with print_num, if the ascii character has one of the values
  4427.              0-32, 127 or 255, it will print a hex number and show a double
  4428.              asterisk '**' to signal the event. (5) If none of the above is
  4429.              true, then it is an unsigned decimal number.
  4430.  
  4431.              If you have a feel for what's happening, it is time to take a
  4432.              mini-test. This is an untimed test, so just make sure that it is
  4433.              correct. I'll give you a particular style, and you figure out the
  4434.              code for that style. The answers are at the bottom of the page.
  4435.              You don't have to memorize the codes. You should be using the
  4436.              summary at the end of the chapter for this quiz.
  4437.  
  4438.                  1.   full register, binary
  4439.                  2.   half register, left ascii, right hex
  4440.                  3.   half register, left signed, right unsigned
  4441.                  4.   full register, ascii
  4442.                  5.   half register, left binary, right ascii
  4443.                  6.   half register, left hex, right signed
  4444.                  7.   half register, left unsigned, right binary
  4445.  
  4446.  
  4447.              The PC Assembler Tutor                                         28
  4448.              ______________________
  4449.  
  4450.                  8.   full register, hex
  4451.                  9.   full register, signed
  4452.                  10.  half register, left signed, right binary.
  4453.  
  4454.              If you feel comfortable with what's going on and are able to do
  4455.              set the registers with the help of the summary, we are ready to
  4456.              move on.
  4457.  
  4458.              Here are the answers.{2}
  4459.  
  4460.  
  4461.  
  4462.  
  4463.  
  4464.  
  4465.  
  4466.  
  4467.  
  4468.  
  4469.  
  4470.  
  4471.  
  4472.  
  4473.  
  4474.  
  4475.  
  4476.  
  4477.  
  4478.  
  4479.  
  4480.  
  4481.  
  4482.  
  4483.  
  4484.  
  4485.  
  4486.  
  4487.  
  4488.              ____________________
  4489.  
  4490.                 2 Here are the answers, both in hex and decimal.
  4491.  
  4492.                    PROBLEM      HEX       DECIMAL
  4493.  
  4494.                       1.         3h         3d
  4495.                       2.        D4h       212d
  4496.                       3.        92h       146d
  4497.                       4.         5h         5d
  4498.                       5.        B5h       181d
  4499.                       6.        C1h       193d
  4500.                       7.        A3h       163d
  4501.                       8.         4h         4d
  4502.                       9.         1h         1d
  4503.                       10.       93h       147d
  4504.  
  4505.              These things are slow to calculate. It took me about a minute per
  4506.              problem to do both the hex and binary.
  4507.  
  4508.  
  4509.              Chapter 4 - SHOW_REGS                                          29
  4510.              _____________________
  4511.  
  4512.  
  4513.                                           SUMMARY
  4514.  
  4515.              The registers may be displayed in signed, unsigned, binary, hex,
  4516.              and ascii formats.  The basic codes for this are:
  4517.  
  4518.                  signed    1
  4519.                  unsigned  2
  4520.                  binary    3
  4521.                  hex       4
  4522.                  ascii     5
  4523.  
  4524.              In addition you need to add the register type. They are:
  4525.  
  4526.                  full register  0
  4527.                  half register  128d or 80h
  4528.  
  4529.  
  4530.              For the left half register, we have:
  4531.  
  4532.                  FORMAT            LEFT HEX     LEFT DECIMAL
  4533.  
  4534.                  signed               10h           16d
  4535.                  unsigned             20h           32d
  4536.                  binary               30h           48d
  4537.                  hex                  40h           64d
  4538.                  ascii                50h           80d
  4539.  
  4540.  
  4541.              Since the left code is of interest only when the half register
  4542.              type is being used, we simply add 80h and come up with:
  4543.  
  4544.                  FORMAT            LEFT CODE    RIGHT CODE
  4545.  
  4546.                  signed                90h          1h
  4547.                  unsigned              A0h          2h
  4548.                  binary                B0h          3h
  4549.                  hex                   C0h          4h
  4550.                  ascii                 D0h          5h
  4551.  
  4552.  
  4553.              Or we add 128d and have:
  4554.  
  4555.                  FORMAT            LEFT CODE    RIGHT CODE
  4556.  
  4557.                  signed              144d           1d
  4558.                  unsigned            160d           2d
  4559.                  binary              176d           3d
  4560.                  hex                 192d           4d
  4561.                  ascii               208d           5d
  4562.  
  4563.  
  4564.              SETTING THE FORMATS
  4565.  
  4566.              Formats are set by calling set_reg_style. The address of ax_byte
  4567.              must be in AX. The standard assembler instructions for this are:
  4568.  
  4569.  
  4570.  
  4571.              The PC Assembler Tutor                                         30
  4572.              ______________________
  4573.  
  4574.                  lea  ax, ax_byte
  4575.                  call set_reg_style
  4576.  
  4577.              set_reg_style makes a copy of your format data. It changes
  4578.              nothing on the screen. The next time that you call show_regs, it
  4579.              will use the new formatting data.
  4580.  
  4581.              The correct order for the data in the data segment is:
  4582.  
  4583.              ax_byte, bx_byte, cx_byte, dx_byte, si_byte, di_byte, bp_byte,
  4584.              sp_byte. They are, of course, all byte sized data.
  4585. Chapter  5 - Addition and Subtraction
  4586. ====================================
  4587.                                                                             31
  4588.  
  4589.  
  4590.  
  4591.              The first arithmetic operations we will look at are addition and
  4592.              subtraction, but before we do that, we need to look at one
  4593.              instruction that controls program flow.
  4594.  
  4595.              LOOP
  4596.  
  4597.              We already have JMP which sends you to a label:
  4598.  
  4599.                  jmp  label3
  4600.  
  4601.              sends the program to label3, wherever that is in the code.
  4602.              Sometimes we want to repeat a section of code a specific number
  4603.              of times and then go on. For this, we have LOOP. LOOP decrements
  4604.              the CX register by 1. If CX is not zero after being decremented,
  4605.              LOOP jumps to the label indicated. If CX is zero after being
  4606.              decremented, LOOP falls through.
  4607.  
  4608.              The 8086 does not have general purpose registers. A general
  4609.              purpose register is a register that can be used for ALL
  4610.              instructions. There are a number of instructions on the 8086
  4611.              which must be done with specific registers, and LOOP is the first
  4612.              one we meet. LOOP always looks at the CX register.
  4613.  
  4614.  
  4615.              This first program lets you enter a number and then loops that
  4616.              many times so you can watch the CX register. As usual, you exit
  4617.              the program by hitting Control-C. We use temp2.asm.
  4618.  
  4619.              temp2.asm
  4620.              ; - - - - START CODE BELOW THIS LINE
  4621.                  call show_regs      ; initialize
  4622.  
  4623.              outer_loop:
  4624.                  call get_unsigned
  4625.                  mov  cx, ax         ; number to cx
  4626.  
  4627.              inner_loop:
  4628.                  call show_regs_and_wait
  4629.                  loop inner_loop
  4630.  
  4631.                  jmp  outer_loop
  4632.  
  4633.              ; - - - - END CODE ABOVE THIS LINE
  4634.  
  4635.              A very simple program. As always, link it with asmhelp.obj.
  4636.              Get_unsigned gets a two byte number (less than 65536) and puts it
  4637.              in AX. We put that number in CX, and then watch the program loop.
  4638.              Make sure you use show_regs_and_wait, or everything will happen
  4639.              too fast for you to see. Try entering 0. On the first pass, loop
  4640.              will decrement CX from 0 to 65535. If CX is 0 when you enter, you
  4641.  
  4642.              ______________________
  4643.  
  4644.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  4645.  
  4646.  
  4647.              The PC Assembler Tutor                                         32
  4648.              ______________________
  4649.  
  4650.              have to repeat the loop 65536 times before you exit the loop. Hit
  4651.              Control-C now to exit.
  4652.  
  4653.              Throughout the book, I will use label names that end in '_loop'
  4654.              to indicate that they are the destination of a jump or loop
  4655.              instruction. The single word "loop" is a reserved word and may
  4656.              not be used as a label - it can only be used as an instruction.
  4657.  
  4658.              The addition program will have 4 sections and LOOP will give us
  4659.              the ability to do each section a limited number of times before
  4660.              going on to the next section.
  4661.  
  4662.  
  4663.              ADDITION
  4664.  
  4665.              If you read the introductory section on numbers carefully, you
  4666.              know that it is the same instruction for both signed and unsigned
  4667.              addition. The 8086 sets the flags correctly for both signed and
  4668.              unsigned addition. For signed addition, the following flags are
  4669.              set:
  4670.  
  4671.                  OF   the overflow flag is set (1) if the result is too
  4672.                  negative or too positive, that is, if the result in the
  4673.                  register does not show the correct result of the addition.
  4674.                  It is cleared (0) otherwise.
  4675.  
  4676.                  ZF   the zero flag is 1 if the result is zero, and is
  4677.                  cleared (0) if the result is non-zero.
  4678.  
  4679.                  SF   the sign flag is set (1) if the result is NEGATIVE and
  4680.                  is cleared (0) if the result is POSITIVE. Zero is considered
  4681.                  a positive number.
  4682.  
  4683.              For unsigned addition, the following flags are set:
  4684.  
  4685.                  CF   the carry flag is set (1) if the result is too large
  4686.                  (over 255 for byte and over 65535 for word operations). It
  4687.                  is cleared (0) otherwise.
  4688.  
  4689.                  ZF   the zero flag is the same as above.
  4690.  
  4691.              In addition, there are two more flags (PF the parity flag and AF
  4692.              the auxillary flag) which will be set or cleared; we will learn
  4693.              about them later.
  4694.  
  4695.              Show_regs shows all the flags. The setting for each flag is
  4696.              underneath its name. For the flags OF, ZF and CF, there is an 'X'
  4697.              if the flag is set and a blank if the flag is cleared. SF, the
  4698.              sign flag, is '-' if the flag is set and '+' if the flag is
  4699.              cleared.
  4700.  
  4701.              The addition program is fairly long because there are four things
  4702.              to look at - unsigned word addition, unsigned byte addition,
  4703.              signed word addition and signed byte addition. For that reason,
  4704.              it has already been typed in for you. It is called ADD1.ASM and
  4705.              its pathname is \XTRAFILE\ADD1.ASM. Print out a copy of it.
  4706.  
  4707.  
  4708.  
  4709.              Chapter 5 - Addition and Subtraction                           33
  4710.              ____________________________________
  4711.  
  4712.              There are four blocks of code which are almost identical except
  4713.              the calls are a little different and two blocks refer to whole
  4714.              registers while the other two refer to half registers. At the
  4715.              head of each block is code to set the appropriate register styles
  4716.              for show_regs. SI, DI, and BP are not used and are set to 0 to
  4717.              make the screen easier to read. Here is the first block of code,
  4718.              which is typical.
  4719.  
  4720.              ; - - - CODE
  4721.                     ; UNSIGNED WORD ADDITION
  4722.                     mov   ax_byte, 2          ; ax, bx, dx unsigned
  4723.                     mov   bx_byte, 2
  4724.                     mov   dx_byte, 2
  4725.                     lea   ax, ax_byte         ; call set_reg_style
  4726.                     call  set_reg_style
  4727.  
  4728.                     mov   cx, 3               ; 3 iterations
  4729.              unsigned_loop:
  4730.                     mov   ax, 0      ; clear the registers for visibility
  4731.                     mov   bx, 0
  4732.                     mov   dx, 0
  4733.                     call  show_regs
  4734.                     call  get_unsigned        ; first number to  ax
  4735.                     call  show_regs
  4736.                     push  ax                  ; temporarily save ax
  4737.                     call  get_unsigned        ; second number to bx
  4738.                     mov   bx, ax
  4739.                     pop   ax                  ; get ax back
  4740.                     mov   dx, ax              ; copy of ax to dx
  4741.                     add   dx, bx              ; dx (=ax) + bx
  4742.                     call  show_regs_and_wait
  4743.                     loop  unsigned_loop
  4744.  
  4745.              ; - - - CODE
  4746.  
  4747.              First, we set AX, BX, and DX for the appropriate register style.
  4748.              Here it is unsigned full register. We then put 3 in CX so we can
  4749.              have 3 iterations with loop. Upon entering the loop, AX, BX, and
  4750.              DX are cleared for reasons of visibility. We don't want the
  4751.              screen cluttered up with numbers. Get_unsigned gets a two byte
  4752.              unsigned number and returns it in AX. We want the first number to
  4753.              be visually on the top (which is AX), but there is a problem
  4754.              here. In order to get the second number we need to call
  4755.              get_unsigned again, and it is going to put another number in AX.
  4756.              We need to temporarily store the first number while we bring in
  4757.              the second number and transfer it to bx.
  4758.  
  4759.              There is a special 8086 instruction to do this, it is called
  4760.              PUSH. Push temporarily stores a word. The word can be either a
  4761.              full register or a word (two bytes) in memory. You can have
  4762.              either:
  4763.  
  4764.                  variable1 dw   10000
  4765.  
  4766.                       push ax
  4767.                       push variable1
  4768.  
  4769.  
  4770.  
  4771.              The PC Assembler Tutor                                         34
  4772.              ______________________
  4773.  
  4774.              These are stored in a special place called the stack which we
  4775.              will talk about much later. When you want it back, you use the
  4776.              instruction POP. POP gets back the LAST thing that you pushed
  4777.              onto the stack. Things come off the stack in REVERSE order of how
  4778.              they were put on.
  4779.  
  4780.                  push variable1
  4781.                  push variable2
  4782.                  push variable3
  4783.                  push variable4
  4784.                  pop  variable4
  4785.                  pop  variable3
  4786.                  pop  variable2
  4787.                  pop  variable1
  4788.  
  4789.              is the correct order. This is used for temporary storage only,
  4790.              and the only thing which is accessable is the last thing which
  4791.              you PUSHed on the stack.
  4792.  
  4793.              We push AX to store it temporarily, call get_unsigned again and
  4794.              transfer the number to BX. We then pop AX to get the number back.
  4795.              The situation now is: the first number is in AX, the second
  4796.              number is in BX. For the actual addition, we transfer AX to DX
  4797.              and then add DX and BX. AX and BX contain the two numbers, and DX
  4798.              contains the result. Then you must press ENTER to continue. LOOP
  4799.              will jump to 'unsigned_loop' two times. The third time it will
  4800.              fall through to the next section of code.
  4801.  
  4802.              This program illustrates a hallmark of assembler code. It
  4803.              normally takes scads of code just to do something simple.
  4804.  
  4805.              Assemble add1.asm and link it with asmhelp.obj. Run it:
  4806.  
  4807.              ******************** SCREEN SHOT ******************************
  4808.  
  4809.                  AX  17428                             SI  00000
  4810.                  BX  19755                             DI  00000
  4811.                  CX  00003                             BP  00000
  4812.                  DX  37183                             SP  00508
  4813.  
  4814.                  CS  0AA5H   DS  0A55H   ES  0A25H   SS  0A35H   IP  004DH
  4815.  
  4816.                  OF   DF  IEF   TF   SF   ZF   AF   PF   CF
  4817.                   x    +    x         -              E           COUNT  00004
  4818.              ----------------------------------------------------------------
  4819.  
  4820.              The PC Assembler Helper   Version 1.0
  4821.              Copyright (C) 1989  Chuck Nelson   All rights reserved.
  4822.              Enter a number from 0 to 65535  17428
  4823.              Enter a number from 0 to 65535  19755
  4824.              Press ENTER to continue
  4825.  
  4826.              *****************************************************************
  4827.  
  4828.              This is the screen after the first addition. I have added 17428
  4829.              (AX) and 19755 (BX). The result 37183 is in DX. CX is still 3
  4830.              because it hasn't LOOPed yet.
  4831.  
  4832.  
  4833.              Chapter 5 - Addition and Subtraction                           35
  4834.              ____________________________________
  4835.  
  4836.  
  4837.              Notice that even though it is the same assembler instruction:
  4838.  
  4839.                  add
  4840.  
  4841.              in all four blocks of code, it is doing both signed and unsigned
  4842.              addition correctly. When you are doing signed addition, you want
  4843.              to look at OF, the overflow flag, SF, the sign flag, and ZF, the
  4844.              zero flag after each addition to see how they are set. When you
  4845.              do unsigned addition, you want to look at CF, the carry flag, and
  4846.              ZF, the zero flag to see how they are set. Play around with this
  4847.              for a while, and then it is time for the next program.
  4848.  
  4849.              As in all 8086 instructions, the order is:
  4850.  
  4851.                  add  destination, source
  4852.  
  4853.              We add both numbers, and put the result in the destination, the
  4854.              thing on the left.
  4855.  
  4856.  
  4857.              There are five different types of addition you can do, (just as
  4858.              there are five different types of moves). They are:
  4859.  
  4860.                  1. add two registers
  4861.                  2. add a register to a variable (memory)
  4862.                  3. add a variable (memory) to a register
  4863.                  4. add a constant to a variable (memory)
  4864.                  5. add a constant to a register
  4865.  
  4866.              Here's a program that does all 5 things. Use template.asm to make
  4867.              this program. template.asm is almost the same as the other ones
  4868.              we have used. It has a few changes. First, it now lists ALL the
  4869.              subroutines you can call in asmhelp.obj.{1} Appendix 1
  4870.              (\APPENDIX\APP1.DOC) contains a description of all the
  4871.              subroutines, what they do, and how they are called. Second, the
  4872.              size of STACKSEG is larger. We don't need this large of a stack
  4873.              now; it is for later. Finally, there is a section:
  4874.  
  4875.              ; + + + + + + + + + + PUT SUBROUTINES BELOW THIS LINE
  4876.  
  4877.              ; + + + + + + + + + + PUT SUBROUTINES ABOVE THIS LINE
  4878.  
  4879.              for subroutines. Ignore this. This is for later.
  4880.  
  4881.  
  4882.              From now on, we will always use template.asm unless it is
  4883.              explicitly stated that something else is being used. Here's the
  4884.              program:
  4885.  
  4886.              template.asm
  4887.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  4888.              ____________________
  4889.  
  4890.                 1 This does not change the size of the .EXE file by even
  4891.              one byte, but it adds a lot of information to the .OBJ file, so
  4892.              they are much larger.
  4893.  
  4894.  
  4895.              The PC Assembler Tutor                                         36
  4896.              ______________________
  4897.  
  4898.              variable1    dw     ?
  4899.              variable2    dw     ?
  4900.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  4901.  
  4902.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  4903.                     call  show_regs
  4904.              outer_loop:
  4905.                     call  get_unsigned        ; first number to ax
  4906.                     push  ax                  ; store ax
  4907.                     call  get_unsigned        ; second number to bx
  4908.                     mov   bx, ax
  4909.                     pop   ax                  ; restore ax
  4910.                     mov   variable1, ax       ; first number to variable1
  4911.                     mov   variable2, bx       ; second number to variable2
  4912.                     ; add 2 registers
  4913.                     mov   cx, ax              ; cx + bx
  4914.                     add   cx, bx
  4915.                     ; add register to memory
  4916.                     add   variable1, bx
  4917.                     mov   dx, variable1       ; put in dx for display
  4918.                     ; add memory to register
  4919.                     mov   si, ax
  4920.                     add   si, variable2
  4921.                     ; add a constant to memory
  4922.                     add   variable2, 25
  4923.                     mov   di, variable2       ; put in di for display
  4924.                     ; add a constant to a register
  4925.                     mov   bp, bx
  4926.                     add   bp, 25
  4927.                     call show_regs
  4928.                     jmp   outer_loop
  4929.  
  4930.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  4931.  
  4932.  
  4933.              The program puts the first number in AX and the second number in
  4934.              BX. It then proceeds to do the same addition (first number plus
  4935.              second number) three times. These are:
  4936.  
  4937.                  1. CX = add two registers (CX + BX)
  4938.                  2. DX = add a register to memory (variable1 + BX)
  4939.                  3. SI = add memory to a register (SI + variable2)
  4940.  
  4941.              Finally it adds a constant (second number + 25). These are:
  4942.  
  4943.                  4. DI = add a constant to memory (variable2 + 25)
  4944.                  5. BP = add a constant to a register (BP + 25)
  4945.  
  4946.  
  4947.              On the 8086, it is not possible to add two things in memory. That
  4948.              is:
  4949.  
  4950.                  add  variable1, variable2
  4951.  
  4952.              is an illegal instruction. Instead, you need to write:
  4953.  
  4954.                  mov  ax, variable2
  4955.  
  4956.  
  4957.              Chapter 5 - Addition and Subtraction                           37
  4958.              ____________________________________
  4959.  
  4960.                  add  variable1, ax
  4961.  
  4962.  
  4963.              SUBTRACTION
  4964.  
  4965.              It is now time to do some subtraction. The instruction is SUB:
  4966.  
  4967.                  sub  destination, source
  4968.  
  4969.              subtracts source from destination and stores it in destination,
  4970.              the thing on the left.
  4971.  
  4972.                  sub  ax, cx         ; (ax - cx) -> ax
  4973.  
  4974.              In order to do subtraction we are going to modify add1.asm, so
  4975.              make a copy and call it sub1.asm:
  4976.  
  4977.                  >copy  add1.asm  sub1.asm
  4978.  
  4979.              How many instructions do we need to change to modify the program?
  4980.              Four.
  4981.  
  4982.                  add  dx, bx    ->        sub  dx, bx
  4983.                  add  dl, bl    ->        sub  dl, bl
  4984.  
  4985.              Each of these is changed twice, and we are ready to roll.
  4986.              Assemble it, link it, and run it. Once again we want to look at
  4987.              the flags at the end of each subtraction. For unsigned
  4988.              subtraction, look at ZF, the zero flag, and CF, the carry flag.
  4989.              This time, CF will be set if the result is below zero. For signed
  4990.              subtraction, look at OF, the overflow flag, SF, the sign flag,
  4991.              and ZF, the zero flag. As with addition, subtraction changes PF,
  4992.              the parity flag and AF the auxillary flag. They don't concern us.
  4993.  
  4994.              As with addition, there are five possibilities for subtraction.
  4995.              They are:
  4996.  
  4997.                  1. subtract one register from another
  4998.                  2. subtract a register from a variable (memory)
  4999.                  3. subtract a variable (memory) from a register
  5000.                  4. subtract a constant from a variable (memory)
  5001.                  5. subtract a constant from a register
  5002.  
  5003.              the code for these is:
  5004.  
  5005.                  sub  cx, bx         ; (cx - bx)         ->   cx
  5006.                  sub  variable1, bx  ; (variable1 - bx)  ->   variable1
  5007.                  sub  si, variable2  ; (si - variable2)  ->   si
  5008.                  sub  variable2, 25  ; (variable2 - 25)  ->   variable2
  5009.                  sub  bp, 25         ; (bp - 25)         ->   bp
  5010.  
  5011.              You can copy add2.asm to sub2.asm if you want and change the five
  5012.              ADD instructions to SUB instructions. It will then do those five
  5013.              types of subtraction.
  5014.  
  5015.  
  5016.  
  5017.              The PC Assembler Tutor                                         38
  5018.              ______________________
  5019.  
  5020.              SIGNED AND UNSIGNED NUMBERS
  5021.  
  5022.              What should you do if you are doing unsigned addition or
  5023.              subtraction and the carry flag gets set? It depends. Sometimes it
  5024.              makes a difference, sometimes it doesn't. If you have an error
  5025.              handling routine, then you can call it with the following code:
  5026.  
  5027.                       add  ax, bx
  5028.                       jnc  go_on
  5029.                       call error_handler
  5030.                  go_on:
  5031.  
  5032.              JC and JNC are conditional jump instructions. JC (jump on carry)
  5033.              jumps if the carry flag is set (1) and JNC (jump on not carry)
  5034.              jumps if the carry flag is not set (0). Using reverse logic here,
  5035.              we skip the error handler if everything is ok.
  5036.  
  5037.              For signed numbers, it is certainly an error if there is
  5038.              overflow. You are making mathematical calculations and you now
  5039.              have invalid data. One possibility is to do the same as above but
  5040.              with the overflow flag.
  5041.  
  5042.                       add  ax, bx
  5043.                       jno  go_on
  5044.                       call error_handler
  5045.                  go_on:
  5046.  
  5047.              JO and JNO are two more conditional jump instructions.  JO (jump
  5048.              on overflow) jumps if the overflow flag is set (1) and JNO (jump
  5049.              on not overflow) jumps if the overflow flag is not set (0). We
  5050.              use the same logic here.
  5051.  
  5052.              However, there is one special instruction for signed numbers, and
  5053.              that is INTO (interrupt on overflow). It is possible to have an
  5054.              error handler external to your program. It sits permanantly in
  5055.              memory. When you make a signed arithmetic error, INTO interrupts
  5056.              your program and goes to the external error handler. The code
  5057.              looks like this:
  5058.  
  5059.                  add  ax, bx
  5060.                  into
  5061.  
  5062.              You probably don't have an error handler in your computer right
  5063.              now. In that case, INTO simply goes looking for it and returns
  5064.              when it can't find it.
  5065.  
  5066.              Let's find out if you have an error handler installed. Once
  5067.              again, use template.asm
  5068.  
  5069.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  5070.                     mov   ax_byte, 1          ; signed register style
  5071.                     mov   bx_byte, 1
  5072.                     mov   cx_byte, 1
  5073.                     lea   ax, ax_byte
  5074.                     call  set_reg_style
  5075.                     call  show_regs
  5076.  
  5077.              Chapter 5 - Addition and Subtraction                           39
  5078.              ____________________________________
  5079.  
  5080.              outer_loop:
  5081.                     call  get_signed
  5082.                     push  ax
  5083.                     call  get_signed
  5084.                     mov   bx, ax
  5085.                     pop   ax
  5086.                     mov   cx, ax
  5087.                     add   cx, bx
  5088.                     into
  5089.                     call  show_regs
  5090.                     jmp   outer_loop
  5091.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  5092.  
  5093.              This is basically the same thing as before, but using AX, BX, and
  5094.              CX. They are set for signed style, and then we  get two signed
  5095.              numbers and add them. The result is in CX. Right after the
  5096.              addition instruction is INTO. If the result is too positive or
  5097.              too negative, OF will be set and INTO will look for the error
  5098.              handler.
  5099.  
  5100.              Assemble this program and link it with asmhelp.obj. Try both
  5101.              numbers that do not set the overflow flag and numbers that do set
  5102.              the overflow flag. Did anything different happen when the
  5103.              overflow flag was set? If nothing different happened, you don't
  5104.              have an error handler for INTO.
  5105.  
  5106.              Included on the disks is an error handler. It is called INTO.COM,
  5107.              and it's pathname is \XTRAFILE\INTO.COM. When you run it:
  5108.  
  5109.                  >into
  5110.  
  5111.              it will install itself and then return to the command prompt:
  5112.  
  5113.                  >
  5114.  
  5115.              INTO.COM will stay in memory until you reboot or shut off the
  5116.              machine. INTO.COM provides the type of sophisticated error
  5117.              handling that you might want to use in a real program. Install
  5118.              (run) INTO.COM, and then try the previous program again, both
  5119.              with numbers that cause an overflow and numbers that don't cause
  5120.              an overflow.
  5121.  
  5122.  
  5123.              The PC Assembler Tutor                                         40
  5124.              ______________________
  5125.  
  5126.                                           SUMMARY
  5127.  
  5128.  
  5129.              ADD performs both signed and unsigned addition. It can:
  5130.  
  5131.                  1. add two registers
  5132.                  2. add a register to a variable (memory)
  5133.                  3. add a variable (memory) to a register
  5134.                  4. add a constant to a variable (memory)
  5135.                  5. add a constant to a register
  5136.  
  5137.  
  5138.              SUB performs both signed and unsigned subtraction. It can:
  5139.  
  5140.                  1. subtract one register from another
  5141.                  2. subtract a register from a variable (memory)
  5142.                  3. subtract a variable (memory) from a register
  5143.                  4. subtract a constant from a variable (memory)
  5144.                  5. subtract a constant from a register
  5145.  
  5146.  
  5147.              The flags affected by both ADD and SUB are:
  5148.  
  5149.                  CF   the carry flag (for unsigned). Set if the 0/65535
  5150.                  (0/255) border was crossed.
  5151.  
  5152.                  ZF   the zero flag (for signed and unsigned). Set if the
  5153.                  result is 0.
  5154.  
  5155.                  SF   the sign flag (for signed). Set if the result is
  5156.                  negative.
  5157.  
  5158.                  OF   the overflow flag (for signed). Set if the result was
  5159.                  too negative or too positive.
  5160.  
  5161.                  PF   the parity flag and AF, the auxillary flag
  5162.  
  5163.              The following jump instructions are conditional on the setting of
  5164.              the flags:
  5165.  
  5166.                  JC   jump on carry, JNC, jump on not carry
  5167.                  JO   jump on overflow. JNO, jump on not overflow
  5168.  
  5169.              LOOP
  5170.                  LOOP decrements cx by 1. If cx is then not zero, it jumps to
  5171.                  the named label. If cx is zero, it falls through to the next
  5172.                  instruction.
  5173.  
  5174.              INTO
  5175.                  If the overflow flag is set, INTO (interrupt on overflow)
  5176.                  interrupts the program and goes to an external error handler
  5177.                  if one exists. It returns immediately if one doesn't exist.
  5178.  
  5179.              PUSH and POP
  5180.                  PUSH stores either a register or a word (in memory) in a
  5181.                  temporary storage area. POP retrieves the last word PUSHed.
  5182. Chapter  6 - Multiplication and Division
  5183. =======================================
  5184.                                                                             41
  5185.  
  5186.  
  5187.  
  5188.  
  5189.              Unlike addition and subtraction, where the result can be in
  5190.              either memory or in one of the registers, the multiplication and
  5191.              division instructions have a rigid format.
  5192.  
  5193.  
  5194.              MULTIPLICATION
  5195.  
  5196.              You can multiply a one byte number by a one byte number and get a
  5197.              two byte result, or you can multiply a one word number by a one
  5198.              word number and get a two word result. The first number MUST be
  5199.              in AL for the byte operation or in AX for the word operation. The
  5200.              second number may be a register or a memory location (but not a
  5201.              constant). The result is in AH:AL for the byte operation and
  5202.              DX:AX for the word operation. Our possibilities are:
  5203.  
  5204.                       AL  X  (one byte register or memory)  ->  AH:AL
  5205.                       AX  X  (one word register or memory)  ->  DX:AX
  5206.  
  5207.              Is there a difference between signed and unsigned numbers? Yes, a
  5208.              very big difference. For the byte operation  FFh = -1 signed but
  5209.              FFh = 255 unsigned.  -1 X -1 = 1 = 0001h.  255 X 255 = 65025 =
  5210.              FE01h. These are two completely different answers. You need to
  5211.              tell the 8086 whether you want signed multiplication or unsigned
  5212.              multiplication. The 8086 does the rest. Let's look at both signed
  5213.              and unsigned multiplication. We'll do byte multiplication for
  5214.              unsigned numbers and word multiplication for signed numbers. The
  5215.              instruction for unsigned multiplication is MUL. The instruction
  5216.              for signed multiplication is IMUL. AX or AL is understood to be
  5217.              the register, so it is not in the code. The instructions are:
  5218.  
  5219.                  variable1  db ?
  5220.                  variable2  dw ?
  5221.  
  5222.                       mul  bx             ; unsigned word from a register
  5223.                       mul  variable1      ; unsigned byte from memory
  5224.                       imul ch             ; signed byte from a register
  5225.                       imul variable2      ; signed word from memory
  5226.  
  5227.              No AX or AL. It's understood. Here's our program:
  5228.  
  5229.              template.asm
  5230.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  5231.              answer1      dw     ?
  5232.              answer2      dw     ?
  5233.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  5234.  
  5235.              ; - - - - - START CODE BELOW THIS LINE
  5236.  
  5237.                     mov     cx, 0               ; clear cx for visual effect
  5238.  
  5239.              ______________________
  5240.  
  5241.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  5242.  
  5243.  
  5244.              The PC Assembler Tutor                                         42
  5245.              ______________________
  5246.  
  5247.  
  5248.              outer_loop:
  5249.                     ; unsigned byte multiplication
  5250.                     mov   ax_byte, 0A2h          ; half regs, unsigned
  5251.                     mov   bx_byte, 0A2h          ; half regs, unsigned
  5252.                     lea   ax, ax_byte
  5253.                     call  set_reg_style
  5254.  
  5255.                     mov   ax, 0               ; clear regs
  5256.                     mov   bx, 0
  5257.                     mov   dx, 0
  5258.                     call  show_regs
  5259.  
  5260.                     call  get_unsigned_byte    ; get two unsigned bytes
  5261.                     call  show_regs
  5262.                     push  ax                   ; save the first number
  5263.                     call  get_unsigned_byte
  5264.                     mov   bl, al
  5265.                     pop   ax                   ; restore the first number
  5266.                     call  show_regs_and_wait
  5267.                     mul   bl                   ; unsigned multiplication
  5268.                     call  print_unsigned       ; display the result (ax)
  5269.                     call  show_regs_and_wait
  5270.  
  5271.  
  5272.                     ; signed word multiplication
  5273.                     mov   ax_byte, 01h          ; full reg, signed
  5274.                     mov   bx_byte, 01h
  5275.                     mov   dx_byte, 01h
  5276.                     lea   ax, ax_byte
  5277.                     call  set_reg_style
  5278.  
  5279.                     mov   ax, 0               ; clear regs
  5280.                     mov   bx, 0
  5281.                     call  show_regs
  5282.  
  5283.                     call  get_signed           ; get two numbers
  5284.                     call  show_regs
  5285.                     push  ax                   ; save the first number
  5286.                     call  get_signed
  5287.                     mov   bx, ax
  5288.                     pop   ax                   ; restore the first number
  5289.                     call  show_regs_and_wait
  5290.                     imul  bx                   ; signed multiplication
  5291.                     push  ax                   ; save result
  5292.                     mov   answer1, ax          ; display 4 byte result
  5293.                     mov   answer2, dx
  5294.                     lea   ax, answer1
  5295.                     call  print_signed_4byte
  5296.                     pop   ax                   ; restore result
  5297.                     call  show_regs_and_wait
  5298.  
  5299.                    jmp    outer_loop
  5300.              ; - - - - - END CODE ABOVE THIS LINE
  5301.  
  5302.              If the answer for the unsigned byte multiplication is greater
  5303.              than 255, it will be difficult to read the answer from the half
  5304.  
  5305.  
  5306.  
  5307.              Chapter 6 - Multiplication and Division                        43
  5308.              _______________________________________
  5309.  
  5310.              registers, so we print out the whole AX register.
  5311.  
  5312.              If the answer for the signed word multiplication is greater than
  5313.              +32767 or is less than -32768, the answer will be unreadable in
  5314.              the DX:AX registers. We move the answer to memory, and then call
  5315.              print_signed_4byte. As with set_reg_style, the data is too long
  5316.              to be put in AX, so we pass the address of the first byte of
  5317.              data with:
  5318.  
  5319.                  lea  ax, answer1
  5320.  
  5321.              and then call print_signed_4byte. Everything from PUSH AX to POP
  5322.              AX is designed to do that.
  5323.  
  5324.              Do MUL and IMUL set any flags? Yes. For byte multiplication, if
  5325.              AL contains the total answer, the 8086 clears the OF and CF
  5326.              flags. If part of the answer is in AH, then the 8086 sets both
  5327.              the OF and CF flags.  For word multiplication, if AX contains the
  5328.              total answer, the 8086 clears the OF and CF flags. If part of the
  5329.              answer is in DX, then the 8086 sets both the OF and CF flags.
  5330.  
  5331.              What do we mean by the total answer? This is simple for unsigned
  5332.              multiplication. If AH (or DX for word) is 0, then the total
  5333.              answer is in AL (or AX for word). It is more complicated for
  5334.              signed multiplication. Consider word multiplication. +30000 X +2
  5335.              = +60000. But that's less than 65536, so it is completely
  5336.              contained in AX, right? WRONG. The leftmost bit of AX contains
  5337.              the sign. If the signed result is out of the range -32768 to
  5338.              +32767, information about the absolute value of the number is
  5339.              corrupting information about the sign of the number.  AX will
  5340.              have the wrong number and the wrong sign. Only by combining AX
  5341.              with DX will you get the correct answer. Similarly for byte
  5342.              multiplication with AL, if the result is not in the range -128 to
  5343.              +127, The leftmost (sign) bit will be corrupted, and only by
  5344.              looking at AH:AL will you be able to get the correct result.
  5345.  
  5346.              If CF and OF are set, you need to look at both registers to
  5347.              evaluate the number. You might want to do error handling, so once
  5348.              again, you can have:
  5349.  
  5350.                       mul  bx
  5351.                       jnc  go_on
  5352.                       call error_handler
  5353.                  go_on:
  5354.  
  5355.              using the same reverse logic as before (if nothing is wrong, skip
  5356.              the error handler). We can also use:
  5357.  
  5358.                       mul  bx
  5359.                       into
  5360.  
  5361.              if there is an INTO error handler.
  5362.  
  5363.  
  5364.              DIVISION
  5365.  
  5366.              Division operates in the same way as multiplication. Word
  5367.  
  5368.  
  5369.              The PC Assembler Tutor                                         44
  5370.              ______________________
  5371.  
  5372.              division operates on the DX:AX pair and byte division operates on
  5373.              the AH:AL pair. There are two instructions, DIV for unsigned
  5374.              division and IDIV for signed division. After the division:
  5375.  
  5376.                  byte      AL = quotient, AH = remainder
  5377.                  word      AX = quotient, DX = remainder
  5378.  
  5379.              Both DIV and IDIV operate on BOTH registers. For bytes, they
  5380.              consider AH:AL a single number. This means that AH must be set
  5381.              correctly before the division or you will get an incorrect
  5382.              answer. For words, they consider DX:AX a single number. This
  5383.              means that DX must be set correctly before the division, or the
  5384.              result will be incorrect. Why did Intel include AH and DX in the
  5385.              division? Wouldn't it have been easier to use just AH (or AX for
  5386.              word division) and put the quotient and remainder in the same
  5387.              place? These instructions are actually designed for dividing a
  5388.              long number (4 or 8 bytes). How it works is pretty slick; you'll
  5389.              find out about it later in the book.
  5390.  
  5391.              How do you set AH and DX correctly? For unsigned numbers, that's
  5392.              easy. Make them 0:
  5393.  
  5394.                  mov  al, variable
  5395.                  mov  ah, 0
  5396.                  div  cl        ; unsigned byte division
  5397.  
  5398.              For signed division, set AH or DX to 0 (0000h) if it is a
  5399.              positive number and set them to -1 (FFFFh) if the number is
  5400.              negative. This is just standard sign extension that was covered
  5401.              in the chapter on numbers. Fortunately for us, Intel has provided
  5402.              instructions which do the sign extension for us. CBW (convert
  5403.              byte to word) correctly extends the signed number in AL through
  5404.              AH:AL. CWD (convert word to double) correctly extends the signed
  5405.              number in AX through DX:AX. The code is
  5406.  
  5407.                  mov  ax, variable5
  5408.                  cwd
  5409.                  idiv bx             ; signed word division
  5410.  
  5411.              Of course with these two instructions you can convert a byte to a
  5412.              double word.
  5413.  
  5414.                  mov  al, variable6
  5415.                  cbw
  5416.                  cwd
  5417.                  idiv bx             ; signed word division
  5418.  
  5419.              first converting to a word, then to a double word.
  5420.  
  5421.              For the division program, we are going to use the multiplication
  5422.              program and make some small changes. Make a copy of your
  5423.              multiplication program:
  5424.  
  5425.                  >copy mult.asm  div.asm
  5426.  
  5427.              and then make the following changes:
  5428.  
  5429.  
  5430.  
  5431.              Chapter 6 - Multiplication and Division                        45
  5432.              _______________________________________
  5433.  
  5434.                  MULTIPLICATION                     DIVISION
  5435.  
  5436.                  ; unsigned byte                    ; unsigned byte
  5437.                                                     pop  ax
  5438.                  pop  ax                            mov  ah, 0
  5439.                  call show_regs_and_wait            call show_regs_and_wait
  5440.                  mul  bl                            div  bl
  5441.  
  5442.  
  5443.                  ; signed word                      ; signed word
  5444.                                                     pop  ax
  5445.                  pop  ax                            cwd
  5446.                  call show_regs_and_wait            call show_regs_and_wait
  5447.                  imul bx                            idiv bx
  5448.  
  5449.              The calls to print_unsigned and print_signed_4byte are
  5450.              irrelevant, so you may either delete them or ignore the output.
  5451.              All we did was change the multiplication instruction to division
  5452.              and prepare the upper register correctly (AH for byte, DX for
  5453.              word). That's all.
  5454.  
  5455.              Assemble, link, and run it. Try out both positive and negative
  5456.              numbers and see what the remainder looks like. Also notice the
  5457.              sign extension just before the division. Remember, for division,
  5458.              the results are in the following places:
  5459.  
  5460.                  byte      AL = quotient, AH = remainder
  5461.                  word      AX = quotient, DX = remainder
  5462.  
  5463.              Now divide by 0. Ka-pow! You should have exited the program and
  5464.              gotten an error message. Unlike the other arithmetical errors
  5465.              where you have the option of ignoring them or making an error
  5466.              handler for them, the 8086 considers division by 0 a major no-no.
  5467.              When the 8086 detects division by zero,{1} it interrupts the
  5468.              program and goes to the zero-divide handler (which is external to
  5469.              the program). Normally, this just exits the program since the
  5470.              data is now worthless.
  5471.  
  5472.  
  5473.  
  5474.  
  5475.  
  5476.  
  5477.              ____________________
  5478.  
  5479.              1 What it actually detects is that the quotient is too large to
  5480.              fit in the lower register (AL for byte or AX for word). As long
  5481.              as the upper register is correctly sign extended, the only time
  5482.              this can happen is when you divide by 0. If the upper register is
  5483.              NOT sign extended correctly, you can have zero divide errors all
  5484.              over the place, even though you aren't dividing by 0. As an
  5485.              example, if AH:AL contain 3275 and bl contains 10, then:
  5486.  
  5487.                  div  bl
  5488.  
  5489.              will give a quotient of 327 ( > 255) and will generate a zero
  5490.              divide error.
  5491.  
  5492.  
  5493.              The PC Assembler Tutor                                         46
  5494.              ______________________
  5495.  
  5496.                                           SUMMARY
  5497.  
  5498.              MUL and IMUL
  5499.                  MUL performs unsigned multiplication and IMUL performs
  5500.                  signed multiplication. For bytes, the multiplicand is in AL
  5501.                  and the result is in the AH:AL pair. For words, the
  5502.                  multiplicand is in AX and the result is in the DX:AX pair.
  5503.                  If the total result is contained in the lower register, CF
  5504.                  and OF are cleared (0). If part of the result is in the
  5505.                  upper register, CF and OF are set (1). The multiplier may be
  5506.                  either a register or a variable in memory.
  5507.  
  5508.                       variable1 db ?
  5509.                       variable2 dw ?
  5510.  
  5511.                            mul  variable1      ; unsigned byte
  5512.                            mul  cx             ; unsigned word
  5513.                            imul bl             ; signed byte
  5514.                            imul variable2      ; signed word
  5515.  
  5516.  
  5517.              DIV and IDIV
  5518.                  DIV performs unsigned division. IDIV performs signed
  5519.                  division. For bytes, the dividend is the AH:AL pair. For
  5520.                  words, the dividend is the DX:AX pair. In byte division, AH
  5521.                  must be correctly prepared before the division. For word
  5522.                  division, DX must be correctly prepared before the division.
  5523.                  The divisor may be either a register or a variable in
  5524.                  memory.
  5525.  
  5526.                       variable1 db ?
  5527.                       variable2 dw ?
  5528.  
  5529.                            div  variable1      ; unsigned byte
  5530.                            div  cx             ; unsigned word
  5531.                            idiv bl             ; signed byte
  5532.                            idiv variable2      ; signed word
  5533.  
  5534.                  The quotient and remainder are as follows:
  5535.  
  5536.                       byte      AL = quotient, AH = remainder
  5537.                       word      AX = quotient, DX = remainder
  5538.  
  5539.                  No flags are affected. If the quotient is too large for the
  5540.                  lower register, or if you divide by zero, a zero divide
  5541.                  program interrupt occurs.
  5542.  
  5543.              CORRECT SIGN EXTENSION
  5544.                  To prepare for division, you must correctly sign extend the
  5545.                  lower register into the upper register. For unsigned
  5546.                  division, zero the upper register (AH = 0 or DX = 0). For
  5547.                  signed division, use CBW and CWD. CBW (convert byte to word)
  5548.                  extends a signed number in AL through AH:AL. CWD (convert
  5549.                  word to double) extends a signed number in AX through DX:AX
  5550. Chapter  7 - LOGIC
  5551. =================
  5552.                                                                             47
  5553.  
  5554.  
  5555.  
  5556.              There are a number of operations which work on individual bits of
  5557.              a byte or word. Before we start working on them, it is necessary
  5558.              for you to learn the Intel method of numbering bits. Intel starts
  5559.              with the low order bit, which is #0, and numbers to the left. If
  5560.              you look at a byte:
  5561.  
  5562.                  7 6 5 4 3 2 1 0
  5563.  
  5564.              that will be the ordering. If you look at a word:
  5565.  
  5566.                  15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
  5567.  
  5568.              that is the ordering. The overwhelming advantage of this is that
  5569.              if you extend a number, the numbering system stays the same. That
  5570.              means that if you take the number 45 :
  5571.  
  5572.                  7 6 5 4 3 2 1 0
  5573.                  0 0 1 0 1 1 0 1  (45d)
  5574.  
  5575.              and sign extend it:
  5576.  
  5577.                  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
  5578.                   0  0  0  0  0  0  0  0  0  0  1  0  1  1  0  1
  5579.  
  5580.              each of the bits keeps its previous numbering. The same is true
  5581.              for negative numbers. Here's -73:
  5582.  
  5583.                  7 6 5 4 3 2 1 0
  5584.                  1 0 1 1 0 1 1 1 (-73d)
  5585.  
  5586.                  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
  5587.                   1  1  1  1  1  1  1  1  1  0  1  1  0  1  1  1  (-73d)
  5588.  
  5589.              In addition, the bit-position number denotes the power of 2 that
  5590.              it represents. Bit 7 = 2 ** 7 = 128, bit 5 = 2 ** 5 = 32,
  5591.              bit 0 = 2 ** 0 = 1. {1}.
  5592.  
  5593.              Whenever a bit is mentioned by number, e.g. bit 5, this is what
  5594.              is being talked about.
  5595.  
  5596.  
  5597.              AND
  5598.  
  5599.              We will use AND as the prototype. There are five different ways
  5600.              you can AND two numbers:
  5601.  
  5602.  
  5603.              ____________________
  5604.  
  5605.                 1 I'm using the Fortran convention for showing exponents. That
  5606.              is, 2 ** 7 is 2 to the 7th, 3 ** 19 is 3 to the 19th.
  5607.  
  5608.              ______________________
  5609.  
  5610.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  5611.  
  5612.  
  5613.              The PC Assembler Tutor                                         48
  5614.              ______________________
  5615.  
  5616.                  1.   AND two register
  5617.                  2.   AND a register with a variable
  5618.                  3    AND a variable with a register
  5619.                  4.   AND a register with a constant
  5620.                  5.   AND a variable with a constant
  5621.  
  5622.              That is:
  5623.  
  5624.                  variable1 db   ?
  5625.                  variable2 dw   ?
  5626.  
  5627.                  and  cl, dh
  5628.                  and  al, variable1
  5629.                  and  variable2, si
  5630.                  and  dl, 0C2h
  5631.                  and  variable1, 01001011b
  5632.  
  5633.              You will notice that this time the constants are expressed in hex
  5634.              and binary. These are the only two reasonable alternatives. These
  5635.              instructions work bit by bit, and hex and binary are the only two
  5636.              ways of displaying a number bitwise (bit by bit). Of course, with
  5637.              hex you must still convert a hex digit into four binary digits.
  5638.  
  5639.              The table of bitwise actions for AND is:
  5640.  
  5641.                  1    1    ->   1
  5642.                  1    0    ->   0
  5643.                  0    1    ->   0
  5644.                  0    0    ->   0
  5645.  
  5646.              That is, a bit in the result will be set if and only if that bit
  5647.              is set in both the source and the destination. What is this used
  5648.              for? Several things. First, if you AND a register with itself,
  5649.              you can check for zero.
  5650.  
  5651.                  and  cx, cx
  5652.  
  5653.              If any bit is set, then there will be a bit set in the result and
  5654.              the zero flag will be cleared. If no bit is set, there will be no
  5655.              bit set in the result, and the zero flag will be set. No bit will
  5656.              be altered, and CX will be unchanged. This is the standard way of
  5657.              checking for zero. You can't AND a variable that way:
  5658.  
  5659.                  and  variable1, variable1
  5660.  
  5661.              is an illegal instruction. But you can AND it with a constant
  5662.              with all the bits set:
  5663.  
  5664.                  and  variable1, 11111111b
  5665.  
  5666.              If the bit is set in variable1, then it will be set in the
  5667.              result. If it is not set in variable1, then it won't be set in
  5668.              the result. This also sets the zero flag without changing the
  5669.              variable.
  5670.  
  5671.              AND is also used in masks, which will be covered at the end of
  5672.              the chapter.
  5673.  
  5674.  
  5675.              Chapter 7 - Logic                                              49
  5676.              _________________
  5677.  
  5678.  
  5679.              Finally, there is a variant of AND called TEST. TEST does exactly
  5680.              the same thing as AND but throws away the results when it is
  5681.              done. It does not change the destination. This means that it can
  5682.              check for specific things without altering the data. It has the
  5683.              same possibilities as AND:
  5684.  
  5685.                  variable1 db   ?
  5686.                  variable2 dw   ?
  5687.  
  5688.                  test cl, dh
  5689.                  test al, variable1
  5690.                  test variable2, si
  5691.                  test dl, 0C2h
  5692.                  test variable1, 01001011b
  5693.  
  5694.              will set the flags exactly the same as the similar AND
  5695.              instructions but will not change the destination. We need a
  5696.              concrete example, and for that we'll turn to your video card. In
  5697.              text mode, your screen is 80 X 25. That is 2000 cells. Each cell
  5698.              has a character byte and an attribute byte. The character byte
  5699.              has the actual ascii number of the character. The attribute byte
  5700.              says what color the character is, what color the background is,
  5701.              whether the character is high or low intensity and whether it
  5702.              blinks. An attribute byte looks like this:
  5703.  
  5704.                  7 6 5 4 3 2 1 0
  5705.                  X R G B I R G B
  5706.  
  5707.              Bits 0,1 and 2 are the foreground (character) color. 0 is blue, 1
  5708.              is green, and 2 is red. Bits 4, 5, and 6 are the background
  5709.              color. 4 is blue, 5 is green, and 6 is red. Bit 3 is high
  5710.              intensity, and bit 7 is blinking. If the bit is set (1) that
  5711.              particular component is activated, if the bit is cleared (0),
  5712.              that component is deactivated.
  5713.  
  5714.              The first thing to notice is how much memory we have saved by
  5715.              putting all this information together. It would have been
  5716.              possible to use a byte for each one of these characteristics, but
  5717.              that would have required 8 X 2000 bytes = 16000 bytes. If you add
  5718.              the 2000 bytes for the characters themselves, that would be 18000
  5719.              bytes. As it is, we get away with 4000 bytes, a savings of over
  5720.              75%. Since there are four different screens (pages) on a color
  5721.              card, that is 18000 X 4 = 72000 bytes compared to 4000 X 4 =
  5722.              16000. That is a huge savings.
  5723.  
  5724.              We don't have the tools to access these bytes yet, but let's
  5725.              pretend that we have moved an attribute byte into dl. We can find
  5726.              out if any particular bit is set. TEST dl with a specific bit
  5727.              pattern. If the zero flag is cleared, the result is not zero so
  5728.              the bit was on. If the zero flag is set, the result is zero so
  5729.              that bit was off
  5730.  
  5731.  
  5732.                  test dl, 10000000b       ; is it blinking?
  5733.                  test dl, 00010000b       ; is there blue in the background?
  5734.                  test dl, 00000100b       ; is there red in the foreground?
  5735.  
  5736.  
  5737.              The PC Assembler Tutor                                         50
  5738.              ______________________
  5739.  
  5740.  
  5741.              If we look at the zero flag, this will tell us if that component
  5742.              is on. It won't tell us if the background is blue, because maybe
  5743.              the green or the red is on too. Remember, test alters neither the
  5744.              source nor the destination. Its purpose is to set the flags, and
  5745.              the results go into the Great Bit Bucket in the Sky.
  5746.  
  5747.  
  5748.              OR
  5749.  
  5750.              The table for OR is:
  5751.  
  5752.                  1    1    ->   1
  5753.                  1    0    ->   1
  5754.                  0    1    ->   1
  5755.                  0    0    ->   0
  5756.  
  5757.              If either the source or the destination bit is set, then the
  5758.              result bit is set. If both are zero then the result is zero.
  5759.              OR is used to turn on a specific bit.
  5760.  
  5761.                  or   dl, 10000000b  ; turn on blinking
  5762.                  or   dl, 00000001b  ; turn on blue foreground
  5763.  
  5764.              After this operation, those bits will be on whether or not they
  5765.              were on before. It changes none of the bits where there is a 0.
  5766.              They stay the same as before.
  5767.  
  5768.  
  5769.              XOR
  5770.  
  5771.              The table for XOR is:
  5772.  
  5773.                  1    1    ->   0
  5774.                  1    0    ->   1
  5775.                  0    1    ->   1
  5776.                  0    0    ->   0
  5777.  
  5778.              That is, if both are on or if both are off, then the result is
  5779.              zero. If only one bit is on, then the result is 1. This is used
  5780.              to toggle a bit off and on.
  5781.  
  5782.                  xor  dl, 10000000b  ; toggle blinking
  5783.                  xor  dl, 00000001b  ; toggle blue foreground
  5784.  
  5785.              Where there is a 1, it will reverse the setting. Where there is a
  5786.              0, the setting will stay the same. This leads to one of the
  5787.              favorite pieces of code for programmers.
  5788.  
  5789.                  xor  ax, ax
  5790.  
  5791.              zeros the ax register. There are three ways to zero the ax
  5792.              register:
  5793.  
  5794.                  mov  ax, 0
  5795.                  sub  ax, ax
  5796.                  xor  ax, ax
  5797.  
  5798.  
  5799.              Chapter 7 - Logic                                              51
  5800.              _________________
  5801.  
  5802.  
  5803.              The first one is very clear, but slightly slower. For the second
  5804.              one, if you subtract a number from itself, you always get zero.
  5805.              This is slightly faster and fairly clear.{2}  For the third one,
  5806.              any bit that is 1 will become 0, and and bit that is 0 will stay
  5807.              0. It zeros the register as a side effect of the XOR instruction.
  5808.              You'll never guess which one many programmers prefer. That's
  5809.              right, XOR. Many programmers prefer the third because it helps
  5810.              make the code more obsure and unreadable. That gives a certain
  5811.              aura of technical complexity to the code.
  5812.  
  5813.  
  5814.  
  5815.              NEG and NOT
  5816.  
  5817.              NOT is a logical operation and NEG is an arithmetical operation.
  5818.              We'll do both here so you can see the difference. NOT toggles the
  5819.              value of each individual bit:
  5820.  
  5821.                  1    ->   0
  5822.                  0    ->   1
  5823.  
  5824.              NEG negates the value of the register or variable (a signed
  5825.              operation). NEG performs (0 - number) so:
  5826.  
  5827.                  neg  ax
  5828.                  neg  variable1
  5829.  
  5830.              are equivalent to (0 - AX) and (0 - variable1) respectively. NEG
  5831.              sets the flags in the same way as (0 - number).
  5832.  
  5833.              ; negvsnot.asm
  5834.              ; compares the operations NEG and NOT
  5835.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  5836.                     mov   ax_byte, 1          ; signed
  5837.                     mov   bx_byte, 1          ; signed
  5838.                     mov   cx_byte, 1          ; signed
  5839.                     mov   si_byte, 3          ; binary
  5840.                     mov   di_byte, 3          ; binary
  5841.                     mov   bp_byte, 3          ; binary
  5842.                     mov   dx, 0               ; not used, so clear
  5843.                     lea   ax, ax_byte
  5844.                     call  set_reg_style
  5845.                     call  show_regs
  5846.  
  5847.              outer_loop:
  5848.                     call  get_signed          ; get number
  5849.                     mov   bx, ax              ; move it to all registers
  5850.                     mov   cx, ax
  5851.                     mov   si, ax
  5852.                     mov   di, ax
  5853.                     mov   bp, ax
  5854.  
  5855.                     not   bx                  ; NOT the second row down
  5856.              ____________________
  5857.  
  5858.                 2 This is one of the first instructions in the template files.
  5859.  
  5860.  
  5861.              The PC Assembler Tutor                                         52
  5862.              ______________________
  5863.  
  5864.                     not   di
  5865.                     neg   cx                  ; NEG the third row down
  5866.                     neg   bp
  5867.  
  5868.                     call  show_regs
  5869.                     jmp   outer_loop
  5870.  
  5871.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  5872.  
  5873.  
  5874.              This is set up so the left registers are signed and the right
  5875.              registers are binary. The top registers retain the original
  5876.              value, the second registers down will do NOT and the third
  5877.              registers down will do NEG.
  5878.  
  5879.              Put a number in. On the right side, you will see that NOT (in DI)
  5880.              reverses the bit pattern, while on the left, NEG (in CX) negates
  5881.              the number. Do a few more. This is always true. But they seem to
  5882.              be related. In fact, BX will always be 1 too negative. Why?
  5883.              Remember that in the introduction on numbers, when we changed
  5884.              signs, we had:
  5885.  
  5886.                  negative of number = one's complement + 1
  5887.  
  5888.              but the one's complement is exactly NEG. It switches the value of
  5889.              each bit. To get the values in the third row (CX and BP), simply
  5890.              add 1 to the values in the second row (BX and DI). Remember, NOT
  5891.              is a logical operation, NEG is an arithmetic operation.
  5892.  
  5893.  
  5894.              MASKS
  5895.  
  5896.              To explain masks, we'll need some data, and we'll use the
  5897.              attribute byte for the monitor. Here it is again:
  5898.  
  5899.                  7 6 5 4 3 2 1 0
  5900.                  X R G B I R G B
  5901.  
  5902.              Bits 0,1 and 2 are the foreground (character) color. 0 is blue, 1
  5903.              is green, and 2 is red. Bits 4, 5, and 6 are the background
  5904.              color. 4 is blue, 5 is green, and 6 is red. Bit 3 is high
  5905.              intensity, and bit 7 is blinking.
  5906.  
  5907.              What we want to do is turn certain bits on and off without
  5908.              affecting other bits. What if we want to make the background
  5909.              black without changing anything else? We use and AND mask.
  5910.  
  5911.                  and  video_byte, 10001111b
  5912.  
  5913.              Bits 0, 1, 2, 3 and 7 will remain unchanged, while bits 4, 5 and
  5914.              6 will be zeroed. This will make the background black. What if we
  5915.              wanted to make the background blue? This is a two step process.
  5916.              First we make the background black, then set the blue background
  5917.              bit. This involves first the AND mask, then an OR mask.
  5918.  
  5919.                  and  video_byte, 10001111b
  5920.                  or   video_byte, 00010000b
  5921.  
  5922.  
  5923.              Chapter 7 - Logic                                              53
  5924.              _________________
  5925.  
  5926.  
  5927.              The first instruction shuts off certain bits without changing
  5928.              others. The second turns on certain bits without effecting
  5929.              others. The binary constant that we are using is called a mask.
  5930.              You may write this constant as a binary or a hex number. You
  5931.              should never write it as a signed or unsigned number (unless you
  5932.              are one of those people who just adores making code unreadable).
  5933.  
  5934.              If you want to turn off certain bits in a piece of data, use an
  5935.              AND mask. The bits that you want left alone should be set to 1,
  5936.              the bits that you want zeroed should be set to 0. Then AND the
  5937.              mask with the data.
  5938.  
  5939.              If you want to turn on certain bits in a piece of data, use an OR
  5940.              mask. The bits that you want left alone should be set to 0. The
  5941.              bits that you want turned on should be set to 1. Then OR the mask
  5942.              with the data.
  5943.  
  5944.              Go back to AND and OR to make sure you believe that this is what
  5945.              will happen.
  5946.  
  5947.              The PC Assembler Tutor                                         54
  5948.              ______________________
  5949.  
  5950.                                        SUMMARY
  5951.  
  5952.  
  5953.              For AND, TEST, OR, and XOR you can have the following
  5954.              combinations:
  5955.  
  5956.                  1.   two register
  5957.                  2.   a register with a variable
  5958.                  3    a variable with a register
  5959.                  4.   a register with a constant
  5960.                  5.   a variable with a constant
  5961.  
  5962.  
  5963.              AND is a bitwise logical operation.
  5964.  
  5965.                  1    1    ->   1
  5966.                  1    0    ->   0
  5967.                  0    1    ->   0
  5968.                  0    0    ->   0
  5969.  
  5970.              TEST does the same thing as AND except that the result is
  5971.              discarded. It is used for setting the flags without altering the
  5972.              data.
  5973.  
  5974.              OR is a bitwise logical operation.
  5975.  
  5976.                  1    1    ->   1
  5977.                  1    0    ->   1
  5978.                  0    1    ->   1
  5979.                  0    0    ->   0
  5980.  
  5981.              XOR is a bitwise logical operation.
  5982.  
  5983.                  1    1    ->   0
  5984.                  1    0    ->   1
  5985.                  0    1    ->   1
  5986.                  0    0    ->   0
  5987.  
  5988.  
  5989.  
  5990.              You can use NOT and NEG with either a register or a variable in
  5991.              memory.
  5992.  
  5993.              NOT is a bitwise logical operation
  5994.  
  5995.                  0    ->   1
  5996.                  1    ->   0
  5997.  
  5998.              NEG is an arithmetic operation. NUMBER -> - NUMBER. It gives the
  5999.              negative of a signed number.
  6000.  
  6001.  
  6002.              MASKS
  6003.  
  6004.                  If you want to turn off certain bits of a piece of data, use
  6005.                  an AND mask. The bits that you want left alone should be set
  6006.                  to 1, the bits that you want zeroed should be set to 0. Then
  6007.  
  6008.  
  6009.              Chapter 7 - Logic                                              55
  6010.              _________________
  6011.  
  6012.                  AND the mask with the data.
  6013.  
  6014.                  If you want to turn on certain bits of a piece of data, use
  6015.                  an OR mask. The bits that you want left alone should be set
  6016.                  to 0. The bits that you want turned on should be set to 1.
  6017.                  Then OR the mask with the data.
  6018. Chapter  8 - Shift and Rotate
  6019. ============================
  6020.                                                                             56
  6021.  
  6022.  
  6023.  
  6024.              There are seven instructions that move the individual bits of a
  6025.              byte or word either left or right. Each instruction works
  6026.              slightly differently. We'll make a standard program and then
  6027.              substitute each instruction into that program.
  6028.  
  6029.  
  6030.              SAL - SHL
  6031.  
  6032.              The instructions SHL (shift logical left) and SAL (shift
  6033.              arithmetic left) are exactly the same. They have the same machine
  6034.              code. They shift each bit to the left. How far? That depends.
  6035.              There are two (and only two) forms of this instruction. All other
  6036.              shift and rotate instructions have these two (and only these two)
  6037.              forms as well. The first form is:
  6038.  
  6039.                  shl  al, 1
  6040.  
  6041.              Which shifts each bit to the left one bit. The number MUST be 1.
  6042.              No other number is possible. The other form is:
  6043.  
  6044.                  shl  al, cl
  6045.  
  6046.              shifts the bits in AL to the left by the number in CL. If CL = 3,
  6047.              it shifts left by 3. If CL = 7, it shifts left by 7. The count
  6048.              register MUST be CL (not CX). The bits on the left are shifted
  6049.              out of the register into the bit bucket, and zeros are inserted
  6050.              on the right. The easy way to understand this is to fire up the
  6051.              standard program. Remember, from now on we always use
  6052.              template.asm.
  6053.  
  6054.              ;sal.asm
  6055.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  6056.                     mov   ax_byte, 0A3h       ; half reg, low reg binary
  6057.                     mov   bx_byte, 0A4h       ; half reg, low reg hex
  6058.                     mov   cx_byte, 0A1h       ; half reg, low reg signed
  6059.                     mov   dx_byte, 0A2h       ; half reg, low reg unsigned
  6060.                     lea   ax, ax_byte
  6061.                     call  set_reg_style
  6062.  
  6063.                     mov   ax, 0               ; clear registers
  6064.                     mov   bx, 0
  6065.                     mov   cx, 0
  6066.                     mov   dx, 0
  6067.                     mov   di, 0
  6068.                     mov   bp, 0
  6069.                     call  show_regs
  6070.  
  6071.              outer_loop:
  6072.                     call  get_hex_byte   ; get number and put in registers
  6073.                     mov   bl, al
  6074.                     mov   cl, al
  6075.  
  6076.              ______________________
  6077.  
  6078.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  6079.  
  6080.  
  6081.              Chapter 8 - Shift and Rotate                                   57
  6082.              ____________________________
  6083.  
  6084.                     mov   dl, al
  6085.                     mov   si, 8         ; 8 iterations of the loop
  6086.                     and   al, al        ; set the flags
  6087.                     call  show_regs_and_wait
  6088.              shift_loop:
  6089.                     sal   al, 1
  6090.                     sal   bl, 1
  6091.                     sal   cl, 1
  6092.                     sal   dl, 1
  6093.                     call  show_regs_and_wait
  6094.                     dec   si
  6095.                     jnz   shift_loop
  6096.                     jmp   outer_loop
  6097.  
  6098.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  6099.  
  6100.              This standard program is with bytes, not words. This is because
  6101.              if we had used words we would have performed 16 individual shifts
  6102.              and that would have been time consuming and boring. First we set
  6103.              the style to half registers. Notice that one is binary, one is
  6104.              hex, one is signed and one is unsigned. That covers all bases.
  6105.              All the registers are then cleared. It would be nice to use the
  6106.              loop instruction, but CX is committed, so we make our own loop
  6107.              instruction. We move 8 into SI. The loop instructions are:
  6108.  
  6109.                  dec  si
  6110.                  jnz  shift_loop
  6111.  
  6112.              DEC decrements a register or a variable by 1. Its counterpart INC
  6113.              increments a register or variable by 1. JNZ (jump if not zero)
  6114.              jumps to 'shift_loop' if SI is not zero.
  6115.  
  6116.              We get a hex byte in AL and put the same byte in BL, CL, and DL.
  6117.              This way we will be able to see what is happening in binary, hex,
  6118.              signed and unsigned. Before starting, we have:
  6119.  
  6120.                  and  al, al
  6121.  
  6122.              This is there to set the flags correctly before starting. All
  6123.              four are shifted left one bit each time, and then we look at the
  6124.              result.
  6125.  
  6126.              Assemble, link and run it. Enter the number 7. In binary, that is
  6127.              (0000 0111). Take a look at the flags before starting. It is a
  6128.              positive number so SF shows '+'. ZF is not set. PF shows 'O'. O
  6129.              stands for odd. Every time you perform an arithmetic or logical
  6130.              operation, the 8086 checks parity. Parity is whether the number
  6131.              contains an even or odd number of 1 bits. This contains 3 1 bits,
  6132.              so the parity is odd. The possible settings are 'E' for even and
  6133.              'O' for odd.{1} SAL checks for parity (though some of the other
  6134.              instructions don't).  Now press ENTER. It will shift left 1 and
  6135.              you will have (0000 1110). What does the unsigned number say now?
  6136.              14. Press ENTER again. (0001 1100) What does the unsigned number
  6137.              say? 28. Again (0011 1000) 56. Again (0111 0000) 112. Notice that
  6138.              ____________________
  6139.  
  6140.                 1 This is for use by communications programs.
  6141.  
  6142.  
  6143.              The PC Assembler Tutor                                         58
  6144.              ______________________
  6145.  
  6146.              the signed number reads +112. Look at the CF and OF. They are
  6147.              both cleared. Things are going to change now. Press ENTER again.
  6148.              (1110 0000). SF is now '-'. OF, the overflow flag is set because
  6149.              you changed the number from positive to negative (from +112 to
  6150.              -32). What is the unsigned number now? 224. CF is cleared. PF is
  6151.              '0'. Shift again. (1100 0000) OF is cleared because you didn't
  6152.              change signs. (Remember, the leftmost bit is the sign bit for a
  6153.              signed number). PF is now 'E' because you have two 1 bits, and
  6154.              two is even. CF is set because you shifted a 1 bit off the left
  6155.              end. Keep pressing ENTER and watch SF, OF, CF, and PF.
  6156.  
  6157.              Let's look at the unsigned numbers we had until we started
  6158.              shifting 1 bits off the left end. We started with 7, then had 14,
  6159.              28, 56, 112, 224. This instruction is multiplying by 2. That's
  6160.              right, and it is MUCH faster than multiplication (about 50 times
  6161.              faster). Far and away the fastest way to multiply a register by
  6162.              2, 4 or 8 is to use sal.
  6163.  
  6164.                  ; by 2              ;by 4               ; by 8
  6165.                  sal  di,1           sal  di, 1          sal  di, 1
  6166.                                      sal  di, 1          sal  di, 1
  6167.                                                          sal  di, 1
  6168.  
  6169.              For a register, it is faster to use a series of 1 shifts than to
  6170.              load cl. For a variable in memory, anything over 1 shift is
  6171.              faster if you load cl.
  6172.  
  6173.              Do a few more numbers to see what is happening both with the
  6174.              number and the flags. CF always signals when a 1 bit has been
  6175.              shifted off the end.
  6176.  
  6177.  
  6178.              SAR and SHR
  6179.  
  6180.              Unlike the left shift instruction, there are two completely
  6181.              different right shift instructions. SHR (shift logical right)
  6182.              shifts the bits to the right, setting CF if a 1 bit is pushed off
  6183.              the right end. It puts 0s in the leftmost bit. Make a copy of
  6184.              SAL.ASM and replace the four instructions:
  6185.  
  6186.                     sal   al, 1
  6187.                     sal   bl, 1
  6188.                     sal   cl, 1
  6189.                     sal   dl, 1
  6190.  
  6191.              with SHR. We'll call the new program SHR.ASM. Run this one too.
  6192.              Instead of 7, use E0h (1110 0000) which is 224d. The first time
  6193.              you shift (0111 0000) the OF flag will be set because the sign
  6194.              changed. Keep shifting, noting the flags and the unsigned number.
  6195.              This time we have 224, 112, 56, 28, 14, 7, 3, 1. It is dividing
  6196.              by two and is once again MUCH faster than division. For a single
  6197.              shift, the remainder is in CF. For a shift of more than one bit,
  6198.              you lose the remainder, but there is a way around this which we
  6199.              will discuss in a moment. Do some more numbers till you are
  6200.              comfortable with the flags and the operation.
  6201.  
  6202.              If you want to divide by 16, you will shift right four times, so
  6203.  
  6204.  
  6205.              Chapter 8 - Shift and Rotate                                   59
  6206.              ____________________________
  6207.  
  6208.              you'll lose those 4 bits. But those bits are exactly the value of
  6209.              the remainder. All we need to do is:
  6210.  
  6211.                  mov  dx, ax    ; copy of number to dx
  6212.                  and  dx, 0000000000001111b ; remainder in dx
  6213.                  mov  cl, 4     ; shift right 4 bits
  6214.                  shr  ax, cl    ; quotient in ax
  6215.  
  6216.              Using a mask, we keep only the right four bits, which is the
  6217.              remainder.
  6218.  
  6219.  
  6220.              SAR
  6221.  
  6222.              SAR (shift arithmetic right) is different. It shifts right like
  6223.              SHR, but the leftmost bit always stays the same. This will make
  6224.              more sense when you run the program. Make another copy, call it
  6225.              SAR.ASM, and change the four instructions to SAR. The flags
  6226.              operate the same as for SHR and SHL. The overflow flag will never
  6227.              change since the left bit will always stay the same.
  6228.  
  6229.              First enter 74h (+116). We will be looking at the signed numbers
  6230.              only. Copy down the signed numbers as you go along. They should
  6231.              be: 116, 58, 29, 14, 7, 3, 1, 0, 0. Now try 8Ch (-116). The
  6232.              numbers you should get are: -116, -58, -29, -15, -8, -4, -2, -1,
  6233.              -1. They started out the same, then they got off by one. The
  6234.              negative numbers are one too negative. Try  39h (+57). The
  6235.              numbers here are: 57, 28, 14, 7, 3, 1, 0, 0, 0. Just as it should
  6236.              be for division by 2. Now try C7 (-57). Here the numbers are:
  6237.              -57, -29, -15, -8, -4, -2, -1, -1, -1. This time it went screwy
  6238.              right off the bat. Once again, the negative numbers are one too
  6239.              negative.
  6240.  
  6241.              SAR is an instruction for doing signed division by 2 (sort of).
  6242.              It is, however, an incomplete instruction. The rule for SAR is:
  6243.              SAR gives the correct answer if the number is positive. It gives
  6244.              the correct answer if the number is negative and the remainder is
  6245.              zero. If the number is negative but there is a remainder, then
  6246.              the answer is one too negative. The reason for this is a little
  6247.              complex, but we need to add some code if we want to do signed
  6248.              division.{2} For SHR, the remainder part was optional. Here it is
  6249.              not. We need to know whether the remainder is zero or not. For
  6250.              this example we will do a word shift left by 6. That's dividing
  6251.              by 64.
  6252.  
  6253.                  remainder_mask dw   002Fh     ; 63
  6254.  
  6255.                  call get_signed          ; number in ax
  6256.                  mov  bx, ax              ; copy in bx
  6257.                  and  bx, remainder_mask  ; the remainder
  6258.                  mov  cl,6                ; shift right 6 bits
  6259.                  sar  ax, cl
  6260.                  jns  continue            ; is it positive?
  6261.              ____________________
  6262.  
  6263.                 2 Both the code and the reasons will be explained (but not
  6264.              proved) in the summary.
  6265.  
  6266.  
  6267.              The PC Assembler Tutor                                         60
  6268.              ______________________
  6269.  
  6270.                  and  bx, bx              ; is the remainder zero?
  6271.                  jz   continue
  6272.                  inc  ax
  6273.              continue:
  6274.  
  6275.              We get the remainder, then shift right 6 bits. Upon finishing
  6276.              SAR, the sign flag will be set correctly. Here is yet another
  6277.              jump. This one is JNS (jump on not sign) jumps if the sign flag
  6278.              is NOT set, that is if the number is positive. If it is positive,
  6279.              then everything is ok so we skip ahead. If the number is
  6280.              negative, then we check to see if there was a remainder. If there
  6281.              wasn't, everything is ok, so we go ahead. If there was a
  6282.              remainder, then we INC (add 1) ax.
  6283.  
  6284.              Is the remainder correct? If the number was positive, the
  6285.              remainder is correct, but if the number was negative, then we
  6286.              need to do one more thing. After INC, but before 'continue' we
  6287.              have a SUB instruction:
  6288.  
  6289.                       inc  ax
  6290.                       sub  bx, 64    ; correct the remainder
  6291.                  continue:
  6292.  
  6293.              Why that is the correct number will be explained in the summary.
  6294.              What a lot of work when we could simply write:
  6295.  
  6296.                  mov  cx, 64
  6297.                  call get_signed
  6298.                  cwd                 ; sign extend
  6299.                  idiv cx             ; signed division
  6300.  
  6301.              Is there any advantage to this instruction? Not really. Remember
  6302.              that the more you shift, the longer it takes. If you shift 2,
  6303.              then it's about 1/3 faster than division. If you shift 14, then
  6304.              it is only 15% faster than division. Considering that even a slow
  6305.              PC can do 25000 divisions a second, you must be in serious need
  6306.              of speed to use this. In any case, you will never or almost never
  6307.              use SAR for signed division, while you will find lots of
  6308.              opportunity to use SHR and SHL for unsigned multiplication and
  6309.              division.
  6310.  
  6311.  
  6312.              ROR and ROL
  6313.  
  6314.              ROR (rotate right) and ROL (rotate left) rotate the bits around
  6315.              the register. We will just do one program since they operate the
  6316.              same way, only in opposite directions. Make another copy of
  6317.              SAL.ASM and put in ROR in the appropriate spots.
  6318.  
  6319.              Enter a number. This time you will notice that the bits, rather
  6320.              than dissapearing off the end, reappear on the other side. They
  6321.              rotate around the register. The only flags that are defined are
  6322.              OF and CF. OF is set if the high bit changes, and CF is set if a
  6323.              1 bit moves off the end of the register to the other side. Do a
  6324.              few more, and we'll go on to the last two instructions.
  6325.  
  6326.  
  6327.  
  6328.  
  6329.              Chapter 8 - Shift and Rotate                                   61
  6330.              ____________________________
  6331.  
  6332.              RCR and RCL
  6333.  
  6334.              RCR (rotate through carry right) and RCL (rotate through carry
  6335.              left) rotate the same as the above instructions except that the
  6336.              carry flag is involved. Rotating right, the low bit moves to CF,
  6337.              the carry flag and CF moves to the high bit. Rotating left, the
  6338.              high bit moves to CF and CF moves to the low bit. There are 9
  6339.              bits (or 17 bits for a word) involved in the rotation. Make yet
  6340.              another copy of the program, and change those 4 instructions to
  6341.              RCR. Also, since we have 9 bits instead of 8, change the loop
  6342.              count to 9 from 8:
  6343.  
  6344.                  mov  si, 9
  6345.  
  6346.              Enter a number and watch it move. Before you start moving, look
  6347.              at CF and see if there is anything in it. There are only two
  6348.              flags defined, OF and CF. Obviously, CF is set if there is
  6349.              something in it. OF is wierd. In RCL (the opposite instruction to
  6350.              the one we are using), OF operates normally, signalling a change
  6351.              in the top (sign) bit. In RCR, OF signals a change in CF. Why? I
  6352.              don't have the slightest idea. You really have no need for the OF
  6353.              flag anyways, so this is unimportant.
  6354.  
  6355.  
  6356.              Well, those are the seven instructions, but what can you do with
  6357.              them besides multiply and divide?
  6358.  
  6359.              First, you can work with multiple bit data. The 8087 has a word
  6360.              length register called the status register.  Looking at the upper
  6361.              byte:
  6362.  
  6363.                  15 14 13 12 11 10  9  8
  6364.                         X  X  X
  6365.  
  6366.              bits 11, 12 and 13 contain a number from 0 to 7. The data in this
  6367.              register is not directly accessable. You need to move the
  6368.              register into memory, then into an 8086 register. If you want to
  6369.              find what this number is, what do you do?
  6370.  
  6371.                  mov  bx, status_register_data
  6372.                  mov  cl, 3
  6373.                  ror  bx, cl
  6374.                  and  bh, 00000111b
  6375.  
  6376.              we rotate right 3 and then mask off everything else. The number
  6377.              is now in BH. We could have used SHR if we wanted. Another 8087
  6378.              register is the control register. In the upper byte it has:
  6379.  
  6380.                  15 14 13 12 11 10  9  8
  6381.                               X  X
  6382.  
  6383.              a number from 0 to 3 in bits 10 and 11. If we want the
  6384.              information, we do the same thing:
  6385.  
  6386.                  mov  bx, control_register_data
  6387.                  mov  cl, 2
  6388.                  ror  bx, cl
  6389.  
  6390.  
  6391.              The PC Assembler Tutor                                         62
  6392.              ______________________
  6393.  
  6394.                  and  bh, 00000011b
  6395.  
  6396.              and the number is in BH.
  6397.  
  6398.              You are now going to write a program that inputs an unsigned
  6399.              number and prints out its hex representation. Here it is:
  6400.  
  6401.  
  6402.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  6403.                     mov   ax_byte, 0A5h        ; half regs, right ascii
  6404.                     mov   bx_byte, 4           ; hex
  6405.                     mov   dx_byte, 4           ; hex
  6406.                     lea   ax, ax_byte
  6407.                     call  set_reg_style
  6408.                     call  show_regs
  6409.  
  6410.              outer_loop:
  6411.                     call  get_unsigned
  6412.                     mov   bx, ax
  6413.                     mov   dx, ax
  6414.                     mov   cx, 4
  6415.              inner_loop:
  6416.                     push  cx                  ; save cx
  6417.                     mov   cl, 4
  6418.                     rol   bx, cl              ; rotate left 1/2 byte
  6419.                     mov   al, bl              ; copy to al
  6420.                     and   al, 0Fh             ; mask off upper 1/2 byte
  6421.                     cmp   al, 10              ; < 10, 0 - 9 ;  > 9  A - F
  6422.                     jae   use_letters
  6423.                     add   al, '0'             ; change to ascii
  6424.                     jmp   print_it
  6425.              use_letters:
  6426.                     add   al, 'A' - 10         ;  10 = 'A'
  6427.              print_it:
  6428.                     call  print_ascii_byte
  6429.                     call  show_regs_and_wait
  6430.                     pop   cx
  6431.                     loop  inner_loop
  6432.                     jmp   outer_loop
  6433.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  6434.  
  6435.              AL will be shown in ascii while BX and DX will be in hex. We save
  6436.              the original number in DX. Since the first thing we want to print
  6437.              is the left hex character, we rotate left, not right. We move the
  6438.              low byte to AL, mask off everything but the low hex number and
  6439.              then convert to an ascii character. If it is 0 - 9, we add '0'
  6440.              (the character, not the number). If it is > 9, we add "'A' - 10"
  6441.              and get a letter (if the number is 10, we get 'A'). JAE means
  6442.              jump if above or equal, and is an unsigned comparison.{3}
  6443.  
  6444.  
  6445.              ____________________
  6446.  
  6447.                 3 You are getting innundated with conditional jump
  6448.              instructions. Don't worry. As long as you understand each one
  6449.              when you run across it, you don't have to remember it. All jump
  6450.              instructions will be covered soon.
  6451.  
  6452.  
  6453.              Chapter 8 - Shift and Rotate                                   63
  6454.              ____________________________
  6455.  
  6456.              Finally, we print the ascii character that is in AL.{4}
  6457.  
  6458.              Another thing to notice is that just inside the loop we push CX.
  6459.              That is because we use CL for the ROL instruction. It is then
  6460.              POPped just before the loop instruction. This is typical. CX is
  6461.              the only register that can be used for counting in indexed
  6462.              instructions. It is common for indexing instructions to be
  6463.              nested, so you temporarily store the old value of CX while you
  6464.              are using CX for something different.
  6465.  
  6466.                  push cx        ; typical code for a shift
  6467.                  mov  cl, 7
  6468.                  shr  si, cl
  6469.                  pop  cx
  6470.  
  6471.  
  6472.              Finally, let's multiply large numbers by 2. Here's the code:
  6473.  
  6474.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  6475.              byte1  db    ?
  6476.              byte2  db    ?
  6477.              byte3  db    ?
  6478.              byte4  db    ?
  6479.              error_message  db  "Result is too large.", 0
  6480.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  6481.  
  6482.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  6483.              outer_loop:
  6484.                     lea   ax, byte1           ; get 4 byte number
  6485.                     call  get_unsigned_4byte
  6486.  
  6487.                     shl   byte1, 1
  6488.                     rcl   byte2, 1
  6489.                     rcl   byte3, 1
  6490.                     rcl   byte4, 1
  6491.                     jnc   go_on
  6492.                     lea   ax, error_message
  6493.                     call  print_string
  6494.              go_on:
  6495.                     lea   ax, byte1
  6496.                     call  print_unsigned_4byte
  6497.                     jmp   outer_loop
  6498.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  6499.  
  6500.              This will require some explaination. Get_unsigned_4byte gets a
  6501.              number from 1 to four billion. We put it in memory. Normally, the
  6502.              following instructions would be done word by word. We are doing
  6503.              them byte by byte so you can see the mechanics of the situation.
  6504.              The low byte is shifted left 1 bit. This doubles it, but may
  6505.              shift a 1 bit from the high bit into CF. If it does, then it will
  6506.              be present when we rotate byte2. That moves CF into the low bit
  6507.              and moves the high bit into CF. We do it again. And again. If
  6508.              there is an unsigned overflow, it will be signalled by CF being
  6509.              ____________________
  6510.  
  6511.                 4 Any subroutine in ASMHELP.OBJ that involves a one byte input
  6512.              or output has the data in AL.
  6513.  
  6514.  
  6515.              The PC Assembler Tutor                                         64
  6516.              ______________________
  6517.  
  6518.              set after:
  6519.  
  6520.                  rcl  byte4, 1
  6521.  
  6522.              JNC (jump on not carry) will skip the error message if everything
  6523.              is ok. Print_string prints a zero terminated string, that is a C
  6524.              string which is terminated by the number (not the character) 0.
  6525.              Finally, we print the number.
  6526.  
  6527.              A word about large numbers in ASMHELP.OBJ. It is assumed that you
  6528.              would like to use commas if you could. Any data type over 1 word
  6529.              long allows commas. The following are considered the same by
  6530.              ASMHELP.OBJ in its input routines:
  6531.  
  6532.                  23546787
  6533.                  2,3,5,4,6,7,8,7
  6534.                  23,,5,46,,78,7
  6535.                  23,546787
  6536.                  23,546,787
  6537.  
  6538.              It always prints commas correctly in the print routines.
  6539.  
  6540.  
  6541.  
  6542.              Chapter 8 - Shift and Rotate                                   65
  6543.              ____________________________
  6544.  
  6545.                                           SUMMARY
  6546.  
  6547.              All shift and rotate instructions operate on either a register or
  6548.              on memory. They can be either 1 bit shifts:
  6549.  
  6550.                  sal  cx, 1
  6551.                  ror  variable1, 1
  6552.                  shr  bl, 1
  6553.  
  6554.              or shifts indexed by CL (it must be CL):
  6555.  
  6556.                  rcl  variable2, cl
  6557.                  sar  si, cl
  6558.                  rol  ah, cl
  6559.  
  6560.  
  6561.  
  6562.              SHL and SAL
  6563.  
  6564.                  SHL (shift logical left) and SAL (shift arithmetic left) are
  6565.                  exactly the same instruction. They move bits left. 0s are
  6566.                  placed in the low bit. Bits are shoved off the register (or
  6567.                  memory data) on the left side, and CF indicates whether the
  6568.                  last bit shoved was a 1 or a 0. It is used for multiplying
  6569.                  an unsigned number by powers of 2.
  6570.  
  6571.  
  6572.              SHR
  6573.  
  6574.                  SHR (shift logical right) does the same thing as SHL but in
  6575.                  the opposite direction. Bits are shifted right. 0s are
  6576.                  placed in the high bit. Bits are shoved off the register (or
  6577.                  memory data) on the right side and CF indicates whether the
  6578.                  last bit shoved off was a 0 or a 1. It is used for dividing
  6579.                  an unsigned number by powers of 2.
  6580.  
  6581.  
  6582.              SAR
  6583.  
  6584.                  SAR (shift arithmetic right) shifts bits right. The high
  6585.                  (sign) bit stays the same throughout the operation. Bits are
  6586.                  shoved off the register (or memory data) on the right side.
  6587.                  CF indicates whether the last bit shoved off was a 1 or a 0.
  6588.                  It is used (with difficulty) for dividing a signed number by
  6589.                  powers of 2.
  6590.  
  6591.  
  6592.              ROR and ROL
  6593.  
  6594.                  ROR (rotate right) and ROL (rotate left) rotate the bits of
  6595.                  a register (or memory data) right and left respectively. The
  6596.                  bit which is shoved off one end is moved to the other end.
  6597.                  CF indicates whether the last bit moved from one end to the
  6598.                  other was a 1 or a 0.
  6599.  
  6600.              RCR and RCL
  6601.  
  6602.  
  6603.  
  6604.              The PC Assembler Tutor                                         66
  6605.              ______________________
  6606.  
  6607.                  RCR (rotate through carry right) and RCL (rotate through
  6608.                  carry left) rotate the bits of a register (or of memory
  6609.                  data) right and left respectively. The bit which is shoved
  6610.                  off the register (or data) is placed in CF and the old CF is
  6611.                  placed on the other side of the register (or data).
  6612.  
  6613.  
  6614.              INC
  6615.                  INC increments a register or a variable by 1.
  6616.  
  6617.                       inc  ax
  6618.                       inc variable1
  6619.  
  6620.              DEC
  6621.                  DEC decrements a register or a variable by 1.
  6622.  
  6623.                       dec  ax
  6624.                       dec  variable1
  6625.  
  6626.  
  6627.  
  6628.              The following is fairly technical. It is only for those willing
  6629.              to wade their way through a turgid explaination. If you don't
  6630.              understand it, forget it.
  6631.  
  6632.              CODE FOR SHL
  6633.  
  6634.              If you are shifting an UNSIGNED number right by 'X' bits, it is
  6635.              the same as dividing by (2 ** X)  1 bit = (2**1 = 2), 2 bits =
  6636.              (2**2 = 4), 7 bits = (2**7 = 128). This is the same as dividing
  6637.              by a number which is all 0s except the Xth bit which is 1 (for 0
  6638.              we have 0000 0001, for 1 we have 0000 0010, for 3 we have 0000
  6639.              1000, for 7 we have 1000 0000). The remainder mask will be this
  6640.              number minus 1 (for 0 we have 0000 0000, for 1 we have 0000 0001,
  6641.              for 3 we have 0000 0111, for 7 we have 0111 1111).
  6642.  
  6643.  
  6644.              CODE FOR SAR
  6645.  
  6646.              The order of numbers is important for SAR. If you start with 0
  6647.              and add 1 each time, the actual sequence of signed numbers that
  6648.              you get (from the bottom up) is:
  6649.  
  6650.  
  6651.                  -1
  6652.                  -2
  6653.                   .
  6654.                   .
  6655.                  -32767
  6656.                  -32768
  6657.                  +32767
  6658.                  +32766
  6659.                   .
  6660.                   .
  6661.                   3
  6662.                   2
  6663.                   1
  6664.                   0
  6665.  
  6666.              Chapter 8 - Shift and Rotate                                   67
  6667.              ____________________________
  6668.  
  6669.  
  6670.              The positive numbers are increasing in absolute value while the
  6671.              negative numbers are decreasing in absolute value. If you divide
  6672.              by shifting and there is no remainder, then the quotient is
  6673.              exact. If there is a remainder, the quotient will truncate
  6674.              towards 0 IN THE ABOVE DIAGRAM. This means that positive numbers
  6675.              will truncate down, while the negative numbers will truncate
  6676.              towards -32768, and will be one too negative.
  6677.  
  6678.              If the number was positive, the remainder will be positive and
  6679.              will be exactly the same as for SHR. If the number was negative,
  6680.              then things are more complicated. We'll take division by 32 as an
  6681.              example. If we divide by 32 (0010 0000) the remainder mask will
  6682.              be 31 (0001 1111). If the number is negative, then what we get
  6683.              when we AND the mask:
  6684.  
  6685.                  and  ax, 00011111b
  6686.  
  6687.              is not the remainder but (remainder + 32). In order to get the
  6688.              actual negative remainder, we need to subtract 32. This gives us
  6689.              (remainder + 32 - 32).
  6690.  
  6691.                  remainder mask = divisor - 1
  6692.                  negative remainder correction = NEG divisor.
  6693. Chapter  9 - Jumps
  6694. =================
  6695.                                                                             68
  6696.  
  6697.  
  6698.              So far we have done almost exclusively sequential programming -
  6699.              the order of execution has been from one instruction to the next
  6700.              until the very end, where the jump instruction has brought us
  6701.              back to the top. This is not the normal way programs work. In
  6702.              order to have things like DO loops, FOR loops, WHILE loops, CASE
  6703.              constructions and IF-THEN-ELSE constructs, we need to make
  6704.              decisions and to be able to go to different blocks of code
  6705.              depending on our decisions.
  6706.  
  6707.              Intel has provided a wealth of conditional jumps to answer all
  6708.              our needs. All of them are listed in a summary at the end of this
  6709.              chapter.
  6710.  
  6711.              The thing we will do most often is compare the size of two
  6712.              numbers. In BASIC code:
  6713.  
  6714.                  IF   A < B  THEN
  6715.  
  6716.              we need to see if A < B . If that is true, we do one thing, if it
  6717.              is false, we do something else. One thing that we need to watch
  6718.              out for is whether A and B are signed or unsigned numbers. Let A
  6719.              = F523h (signed -2781; unsigned 62755) and B = 59E0h (signed
  6720.              +23008; unsigned 23008). If A and B are signed numbers, then A <
  6721.              B. However, if they are unsigned numbers, then  A > B. In C and
  6722.              PASCAL, the compiler takes care of whether you want signed or
  6723.              unsigned numbers (BASIC assumes that it is always signed
  6724.              numbers). You are now on the machine level, and must take care of
  6725.              this yourself.
  6726.  
  6727.              To compare two numbers, you subtract one from the other, and then
  6728.              evaluate the result (less than 0,  0, or more than 0). To compare
  6729.              A and B, you do A minus B. To compare AX and BX, you can:
  6730.  
  6731.                  sub  ax, bx
  6732.  
  6733.              and then evaluate it. Unfortunately, if you do that, you will
  6734.              destroy the information in AX. It will have been changed in the
  6735.              process. Intel has solved this problem with the CMP (compare)
  6736.              instruction.
  6737.  
  6738.                  cmp  ax, bx
  6739.  
  6740.              subtracts BX from AX, sets the flags, and throws the answer away.
  6741.              CMP is exactly the same as SUB except that AX remains unchanged.
  6742.              We probably don't want to save the result anyway. If we do, we
  6743.              can always use:
  6744.  
  6745.                  sub  ax, bx
  6746.  
  6747.              We have subtracted BX from AX. There are now three possibilities.
  6748.              (1) AX > BX so the answer is positive, (2) AX = BX so the answer
  6749.              is zero, or (3) AX < BX so the answer is negative. But are these
  6750.  
  6751.              ______________________
  6752.  
  6753.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  6754.  
  6755.  
  6756.              Chapter 9 - Jumps                                              69
  6757.              _________________
  6758.  
  6759.              signed or unsigned numbers? The 8086 uses the same subtract (or
  6760.              CMP) instruction for both signed or unsigned numbers. You have to
  6761.              tell the 8086 in the following instruction whether you were
  6762.              talking about signed or unsigned numbers.
  6763.  
  6764.                  cmp  ax, bx
  6765.                  ja   some_label
  6766.  
  6767.              asks the machine to compare AX and BX. It then says that AX and
  6768.              BX were UNSIGNED numbers and the machine should jump to
  6769.              "some_label" if AX was above BX.
  6770.  
  6771.                  cmp  ax, bx
  6772.                  jg   some_label
  6773.  
  6774.              asks the machine to compare AX and BX. It then says that AX and
  6775.              BX were SIGNED numbers and the machine should jump to
  6776.              "some_label" if AX was greater than BX.
  6777.  
  6778.              The 8086 makes the decision by looking at the flags. They give it
  6779.              complete information about the result. (The decision making rules
  6780.              are in the summary).
  6781.  
  6782.              In our example on the previous page, we had A = -2781 (unsigned
  6783.              62755) and B = +23008 (unsigned 23008). If we put A in AX and B
  6784.              in BX, then the instruction JA will execute the jump, while the
  6785.              instruction JG will not execute the jump. What happens if the
  6786.              8086 doesn't execute the jump? It goes on to the next
  6787.              instruction.
  6788.  
  6789.              As I said before, the 8086 has LOTS of conditional jumps. I am
  6790.              going to give you a list of them now, but be forewarned that the
  6791.              mnemonics for these suckers are confusing. Rather than doing
  6792.              something sensible like JUG for 'jump unsigned greater' and JSG
  6793.              for 'jump signed greater' - things that would be easy to
  6794.              distinguish, they have JA for 'jump above' (unsigned) and JG for
  6795.              'jump greater' (signed).{1}  Therefore, use the summary at the
  6796.              end of the chapter. With enough use, you will remember which is
  6797.              which.
  6798.  
  6799.              The arithmetic jump instructions have two forms, a positive one
  6800.              and a negative one. For instance:
  6801.  
  6802.                  ja        ; jump above
  6803.                  jnbe      ; jump not (below or equal)
  6804.  
  6805.              are actually the same machine instruction. You use whichever one
  6806.              makes the program clearer. We will have examples later to
  6807.              illustrate both positive and negative mnemonics. Here are the
  6808.              signed and unsigned instructions and their meaning. The
  6809.              ____________________
  6810.  
  6811.                 1 This wierd use of the words above and greater, below and
  6812.              less, is so confusing that my copy of the Microsoft assembler
  6813.              manual v5.1 has it reversed on one page. It calls the signed
  6814.              jumps unsigned and the unsigned jumps signed. And that's the one
  6815.              place where it SHOULD be right.
  6816.  
  6817.  
  6818.              The PC Assembler Tutor                                         70
  6819.              ______________________
  6820.  
  6821.              equivalent mnemonics will appear in pairs.
  6822.  
  6823.              THESE ARE THE SIGNED JUMP INSTRUCTIONS
  6824.  
  6825.                  jg        ; jump if greater
  6826.                  jnle      ; jump if not (less or equal){2}
  6827.  
  6828.                  jl        ; jump if less
  6829.                  jnge      ; jump if not (greater or equal)
  6830.  
  6831.                  je        ; jump if equal
  6832.                  jz        ; jump if zero
  6833.  
  6834.                  jge       ; jump if greater or equal
  6835.                  jnl       ; jump if not less
  6836.  
  6837.                  jle       ; jump if less or equal
  6838.                  jng       ; jump if not greater
  6839.  
  6840.                  jne       ; jump if not equal
  6841.                  jnz       ; jump if not zero
  6842.  
  6843.              These are self-explainatory, keeping in mind that these apply
  6844.              only to signed numbers.
  6845.  
  6846.  
  6847.              THESE ARE THE UNSIGNED JUMP INSTRUCTIONS
  6848.  
  6849.                  ja        ; jump if above
  6850.                  jnbe      ; jump if not (below or equal)
  6851.  
  6852.                  jb        ; jump if below
  6853.                  jnae      ; jump if not (above or equal)
  6854.  
  6855.                  je        ; jump if equal
  6856.                  jz        ; jump if zero
  6857.  
  6858.                  jae       ; jump if above or equal
  6859.                  jnb       ; jump if not below
  6860.  
  6861.                  jbe       ; jump if below or equal
  6862.                  jna       ; jump if not above
  6863.  
  6864.                  jne       ; jump if not equal
  6865.                  jnz       ; jump if not zero
  6866.  
  6867.              These apply to unsigned numbers, and should be clear.
  6868.  
  6869.              JZ, JNZ, JE and JNE work for both signed and unsigned numbers.
  6870.              After all, zero is zero.
  6871.              ____________________
  6872.  
  6873.                 2 I was trying to decide whether or not to put in the
  6874.              parentheses. If there are two things after the "not" the "not"
  6875.              applies to both of them. By the rules of logic, not (less or
  6876.              equal) == not less AND not equal. If you don't understand this,
  6877.              try to find someone who can explain it to you.
  6878.  
  6879.  
  6880.              Chapter 9 - Jumps                                              71
  6881.              _________________
  6882.  
  6883.  
  6884.              Before we get going, there is one more thing you need to know.
  6885.              The unconditional jump:
  6886.  
  6887.                  jmp  some_label
  6888.  
  6889.              can jump anywhere in the current code segment. XXXX is the
  6890.              current number in CS, then jmp can go from XXXX offset 0 to XXXX
  6891.              offset 65535.
  6892.  
  6893.              Conditional jumps are something else entirely. ALL conditional
  6894.              jumps (including the loop instruction) are limited range jumps.
  6895.              They are from -128 to +127. That is, they can go backward up to
  6896.              128 bytes and they can go forward up to 127 bytes.{3} You will
  6897.              find that you will get assembler errors because the conditional
  6898.              jumps are too far away, but don't worry because we can ALWAYS fix
  6899.              them. You will find out later how to deal with them.
  6900.  
  6901.  
  6902.              As in the other arithmetic instructions, there are five forms of
  6903.              the CMP instruction.
  6904.  
  6905.                  1.   compare two registers
  6906.                  2.   compare a register with a variable
  6907.                  3.   compare a variable with a register
  6908.                  4.   compare a register with a constant
  6909.                  5.   compare a variable with a constant
  6910.  
  6911.              These look like:
  6912.  
  6913.                  cmp  ah, dl
  6914.                  cmp  si, memory_data
  6915.                  cmp  memory_data, ax
  6916.                  cmp  ch, 127
  6917.                  cmp  memory_data, 14938
  6918.  
  6919.  
  6920.              Here are some decisions we might have to make in programs.
  6921.  
  6922.              (1) We are writing a program to print hex numbers on the screen.
  6923.              We have one routine for 0 - 9 and a different one for A - F.
  6924.              Sound familiar?
  6925.  
  6926.                  cmp  al, 10
  6927.                  jb   decimal_routine
  6928.                  ; here we are past the jump, so we can start the  A - F
  6929.                  ; routine.
  6930.  
  6931.              (2) We want to fire everyone over the age of 55 (this is an
  6932.              unsigned number since there are no negative ages):
  6933.              ____________________
  6934.  
  6935.                 3 But they don't jump from the beginning of the machine
  6936.              instruction, they jump from the END of the machine instruction
  6937.              (which is two bytes long). This means that they have an effective
  6938.              range of from -126 bytes to +129 bytes from the BEGINNING of the
  6939.              instruction.
  6940.  
  6941.  
  6942.              The PC Assembler Tutor                                         72
  6943.              ______________________
  6944.  
  6945.  
  6946.                  cmp  employee_age, 55
  6947.                  ja   find_a_reason_for_termination
  6948.                  ; start of routine for younger employees here.
  6949.  
  6950.              (3) You want to know if you need to go to a loanshark:
  6951.  
  6952.                  mov  di, bank_balance
  6953.                  cmp  unpaid_bills, di
  6954.                  jg   gotta_go_see_Vinnie
  6955.                  ; continue normal routine here
  6956.  
  6957.              (4) Notice that the last one could have also been written:
  6958.  
  6959.                  mov  di, bank_balance
  6960.                  cmp  di, unpaid_bills
  6961.                  jl   gotta_go_see_Vinnie
  6962.                  ; continue normal routine
  6963.  
  6964.              though to my eye the first one seems clearer.
  6965.  
  6966.              (5) You have the results from two calculations, the first one in
  6967.              DI and the second one in CX. You need to go to different routines
  6968.              depending on which is larger. If they are the same, you exit:
  6969.  
  6970.                  cmp  di, cx
  6971.                  jg   routine_one         ; di is greater
  6972.                  jl   routine_two         ; cx is greater
  6973.                  jmp  exit_label          ; they are equal
  6974.  
  6975.              We had two conditional jumps in a row and both of them were able
  6976.              to look at the results of the CMP instruction because the first
  6977.              jump (JG) did not change any of the flags. This is true of all
  6978.              jumps - conditional jumps, the unconditional jump, and LOOP. They
  6979.              never change any of the flags, so you can have two jumps in a row
  6980.              and be certain that the flags will not be altered.
  6981.  
  6982.  
  6983.              (6) Your dad has promised to buy you a Corvette if you don't get
  6984.              suspended from school more than three times this semester. Here's
  6985.              his decision code:
  6986.  
  6987.                  cmp  number_of_suspensions, 3
  6988.                  jng  buy_him_the_corvette
  6989.                  ; better luck next time
  6990.  
  6991.  
  6992.  
  6993.              JUMP OUT OF RANGE
  6994.  
  6995.              If the code you are jumping to is more than -128 or +127 bytes
  6996.              from the end of a conditional jump, you will get an assembler
  6997.              error that the jump is out of range. This can always be fixed.
  6998.              Take this code:
  6999.  
  7000.  
  7001.  
  7002.              Chapter 9 - Jumps                                              73
  7003.              _________________
  7004.  
  7005.                       cmp  ax, bx
  7006.                       jg   destination_label
  7007.                  further_code:
  7008.                       ; continue the program
  7009.  
  7010.              If 'destination_label' is out of range, what you need to do is
  7011.              jump to 'further_code' with a conditional jump (you are within
  7012.              range of 'further_code') and use JMP (which can go anywhere in
  7013.              the segment) to go to 'destination_label'. To switch the jump,
  7014.              you simply negate the jump condition:
  7015.  
  7016.                  jg   ->   jng
  7017.                  je   ->   jne
  7018.                  jne  ->   je
  7019.                  jbe  ->   jnbe
  7020.  
  7021.              We use reverse logic. Originally, if the condition was met we
  7022.              jumped. If the condition was not met we continued. Now, if the
  7023.              condition is NOT met, we jump, and if the condition is NOT not
  7024.              met (yes, there are two NOTs) which means it was met, we
  7025.              continue, and this sends us to the JMP instruction. Make sure you
  7026.              believe this works correctly before going on. The code then looks
  7027.              like this:
  7028.  
  7029.                       cmp  ax, bx
  7030.                       jng  further_code
  7031.                       jmp  destination_label
  7032.                  further_code:
  7033.                       ; continue the program
  7034.  
  7035.              This is the standard way of handling the situation.
  7036.  
  7037.  
  7038.  
  7039.              The PC Assembler Tutor                                         74
  7040.              ______________________
  7041.  
  7042.                                         SUMMARY
  7043.  
  7044.              CMP
  7045.                  CMP performs the same operation as subtraction, but it does
  7046.              not change the registers or variables, it only sets the flags. It
  7047.              is the cousin of TEST. As usual, there are five possibilities:
  7048.  
  7049.                  1.   compare two registers
  7050.                  2.   compare a register with a variable
  7051.                  3.   compare a variable with a register
  7052.                  4.   compare a register with a constant
  7053.                  5.   compare a variable with a constant
  7054.  
  7055.  
  7056.              THESE ARE THE SIGNED JUMP INSTRUCTIONS
  7057.  
  7058.                  jg        ; jump if greater
  7059.                  jnle      ; jump if not (less or equal)
  7060.  
  7061.                  jl        ; jump if less
  7062.                  jnge      ; jump if not (greater or equal)
  7063.  
  7064.                  je        ; jump if equal
  7065.                  jz        ; jump if zero
  7066.  
  7067.                  jge       ; jump if greater or equal
  7068.                  jnl       ; jump if not less
  7069.  
  7070.                  jle       ; jump if less or equal
  7071.                  jng       ; jump if not greater
  7072.  
  7073.                  jne       ; jump if not equal
  7074.                  jnz       ; jump if not zero
  7075.  
  7076.  
  7077.  
  7078.              THESE ARE THE UNSIGNED JUMP INSTRUCTIONS
  7079.  
  7080.                  ja        ; jump if above
  7081.                  jnbe      ; jump if not (below or equal)
  7082.  
  7083.                  jb        ; jump if below
  7084.                  jnae      ; jump if not (above or equal)
  7085.  
  7086.                  je        ; jump if equal
  7087.                  jz        ; jump if zero
  7088.  
  7089.                  jae       ; jump if above or equal
  7090.                  jnb       ; jump if not below
  7091.  
  7092.                  jbe       ; jump if below or equal
  7093.                  jna       ; jump if not above
  7094.  
  7095.                  jne       ; jump if not equal
  7096.                  jnz       ; jump if not zero
  7097.  
  7098.  
  7099.  
  7100.  
  7101.              Chapter 9 - Jumps                                              75
  7102.              _________________
  7103.  
  7104.              THESE JUMPS CHECK A SINGLE FLAG
  7105.  
  7106.              These come in opposite pairs
  7107.  
  7108.                  jc        ; jump if the carry flag is set
  7109.                  jnc       ; jump if the carry flag is not set
  7110.  
  7111.                  jo        ; jump if the overflow flag is set
  7112.                  jno       ; jump if the overflow flag is not set
  7113.  
  7114.                  jp or jpe ; jump if parity flag is set (parity is even)
  7115.                  jnp or jpo  ;jump if parity flag is not set (parity is odd)
  7116.  
  7117.  
  7118.                  js        ; jump if the sign flag is set (negative )
  7119.                  jns       ; jump if the sign flag is not set (positive or 0)
  7120.  
  7121.  
  7122.              THIS CHECKS THE CX REGISTER
  7123.  
  7124.                  jcxz      ; jump if cx is zero
  7125.  
  7126.              Why do we have this instruction? Remember, the loop instruction
  7127.              decrements CX and then checks for 0. If you enter a loop with CX
  7128.              set to zero, the loop will repeat 65536 times. Therefore, if you
  7129.              don't know what the value of CX will be when you enter the loop,
  7130.              you use this instruction just before the loop to skip the loop if
  7131.              CX is zero:
  7132.  
  7133.                       jcxz after_the_loop
  7134.  
  7135.                  loop_start:
  7136.                            .
  7137.                            .
  7138.                            .
  7139.                            .
  7140.                       loop loop_start
  7141.                  after_the_loop:
  7142.  
  7143.  
  7144.              INFORMATION ABOUT JUMPS
  7145.  
  7146.              The unconditional jump (JMP) can go anywhere in the code segment.
  7147.              All other jumps, which are conditional, can only go from -128 to
  7148.              +127 bytes from the END of the jump instruction (that's from -126
  7149.              to +129 bytes from the beginning of the instruction).
  7150.  
  7151.              Jumps have no effect on the 8086 flags.
  7152.  
  7153.              How does the 8086 decide whether something is more, less, or the
  7154.              same? The rules for unsigned numbers are easy. If you subtract a
  7155.              larger number from a smaller, the subtraction will pass through
  7156.              zero and will set the carry flag (CF). If they are the same, the
  7157.              result will be zero and it will set the zero flag (ZF). If the
  7158.              first number is larger, the machine will clear the carry flag and
  7159.              the zero flag will be cleared (ZF = 0). Therefore, for unsigned
  7160.              numbers, we have:
  7161.  
  7162.  
  7163.              The PC Assembler Tutor                                         76
  7164.              ______________________
  7165.  
  7166.  
  7167.              First number is:
  7168.                  above          CF = 0    ZF = 0
  7169.                  equal          CF = 0    ZF = 1
  7170.                  not equal      CF = ?    ZF = 0
  7171.                  below          CF = 1    ZF = 0
  7172.  
  7173.              All other unsigned compares are combinations of those three
  7174.              things:
  7175.  
  7176.                  jae  = ja OR je     (CF = 0 and ZF = 0) or ZF = 1
  7177.                  jbe  = jb OR je     CF = 1 or ZF = 1
  7178.  
  7179.              When you have a negative condition, it is much easier to look at
  7180.              its equivalent positive condition to figure out what is going on:
  7181.  
  7182.                  jnae   is the same as    jb   CF = 1
  7183.                  jnbe   is the same as    ja   CF = 0    ZF = 0
  7184.  
  7185.  
  7186.              SIGNED NUMBERS
  7187.  
  7188.              This section is not for the fainthearted. It is not necessary, so
  7189.              if you find yourself getting confused, just remember that if you
  7190.              see documentation talking about a jump where the overflow flag
  7191.              equals the sign flag or the overflow flag doesn't equal the sign
  7192.              flag, it is talking about SIGNED numbers.
  7193.  
  7194.              Zero is zero, so we won't concern ourselves with it here. It is
  7195.              exactly the same.
  7196.  
  7197.              If A and B are two signed word sized numbers and we have:
  7198.  
  7199.                  cmp  A, B
  7200.  
  7201.              then we can have four different cases:
  7202.  
  7203.                  1) If A is just a little greater than B  [(A - B) <=
  7204.                  +32767],  then the result will be a small positive number,
  7205.                  and there will be no overflow. SF = 0, OF = 0.
  7206.  
  7207.                  2) If A is much greater than B [+32767 < (A - B)], then the
  7208.                  result will be too positive and it will overflow from
  7209.                  positive to negative. This will set both the sign flag (it
  7210.                  is now negative) and the overflow flag. SF = 1, OF = 1.
  7211.  
  7212.                  3) If A is a little less than B [-32768 <= (A - B)], that is
  7213.                  if it is only a little negative, then the result is a small
  7214.                  negative number, and there is no overflow. SF = 1, OF = 0.
  7215.  
  7216.                  4) If A is much less than B [(A - B) < -32768], then the
  7217.                  result is a large negative number. It is too negative and
  7218.                  overflows into a positive number. SF = 0, OF = 1.
  7219.  
  7220.              Recapping, for a positive result:
  7221.  
  7222.                  1) SF = 0, OF = 0
  7223.  
  7224.  
  7225.              Chapter 9 - Jumps                                              77
  7226.              _________________
  7227.  
  7228.                  2) SF = 1, OF = 1
  7229.  
  7230.              and for a negative result:
  7231.  
  7232.                  3) SF = 1, OF = 0
  7233.                  4) SF = 0, OF = 1
  7234.  
  7235.              For positive results (and zero), SF = OF. For negative results,
  7236.              SF is not equal to OF. This, in fact, is how the 8086 decides a
  7237.              signed jump. If SF = OF, it's positive, if SF is not equal to OF,
  7238.              it's negative. If ZF = 1, then obviously they are equal. Here is
  7239.              the list:
  7240.  
  7241.                  greater        SF = OF   ZF = 0
  7242.                  equal          SF = OF   ZF = 1
  7243.                  not equal                ZF = 0
  7244.                  less           SF is not equal to OF
  7245.  
  7246.              As with the unsigned numbers, if you have a negative condition,
  7247.              it is easier to change it into its equivalent positive condition
  7248.              and then figure out the requirements. For instance:
  7249.  
  7250.                  jnge      same as   jl   SF is not equal to OF
  7251.                  jnl       same as   jge  ( SF = OF and ZF = 0 ) or ( ZF = 1)
  7252.  
  7253.              If you think about it, this OF = SF stuff does make sense. We are
  7254.              subtracting two numbers. If the first one is greater, then the
  7255.              answer will be positive. It can either be a little positive as in
  7256.              (cmp 0, -1 ) = 1 or it can be very positive, as in (cmp 32767,
  7257.              -32768) = 65,535 (same as -1). If it is just a little positive,
  7258.              there is no overflow and it has a positive sign (SF = 0, OF = 0).
  7259.              If the difference gets large, then the number overflows from + to
  7260.              -. At that point OF = 1, but it now has a negative sign, so OF =
  7261.              SF. The flags MUST match.
  7262.  
  7263.              In the opposite case where the second number is greater, The
  7264.              answer is negative. It can either be a little negative as in (cmp
  7265.              12, 13)= -1, or it can be very negative (cmp -32768, 32767) =
  7266.              -65535 =1. If it is a small difference, the sign is negative, but
  7267.              there is no overflow (SF = 1, OF = 0). As the difference gets
  7268.              larger, the number overflows from negative to positive so the
  7269.              sign flag is now positive, but the overflow flag is set (SF = 0,
  7270.              OF = 1). Those flags CAN'T match.
  7271. Chapter 10 - Templates
  7272. ======================
  7273.                                                                             78
  7274.  
  7275.  
  7276.  
  7277.  
  7278.              Do you remember when you were younger and you needed to look up a
  7279.              word in the dictionary? It would define the word in terms of a
  7280.              second word which you didn't know so you would look that up too.
  7281.              Most likely that second word was either defined in terms of a
  7282.              third word you didn't know or it referred you back to the first
  7283.              word.
  7284.  
  7285.              This chapter is something like that. The items in the template
  7286.              file are interdependent. If you're lucky, everything will be
  7287.              clear by the time you have finished the chapter. If not, you'll
  7288.              have to reread it.
  7289.  
  7290.              There are four different things which operate on the assembler
  7291.              instructions which you write - the ASSEMBLER, the LINKER, the
  7292.              LOADER and the 8086.
  7293.  
  7294.              1) The ASSEMBLER takes your text and turns it into the machine
  7295.              code that is used by the 8086. It is complete except that the
  7296.              addresses of data and subroutines might change during linking and
  7297.              loading. The assembler generates information called HEADER files
  7298.              which give the LINKER and LOADER the information they need to
  7299.              update these addresses in the machine code. This means that you
  7300.              can move the code anywhere in memory.
  7301.  
  7302.              2) If your program is made up of more than one file, the LINKER
  7303.              links them together. It then makes it ready for running. If there
  7304.              is only one file, the linker makes it ready for running. It does
  7305.              this by updating the addresses of anything it has moved. It still
  7306.              leaves the HEADER files which contain the segment addresses.
  7307.  
  7308.              3) At run time, the LOADER, which is part of the operating
  7309.              system, decides where to put your program in memory. It loads the
  7310.              program, and adjusts any segment addresses in the program to
  7311.              reflect where the program actually is in memory. It then gives
  7312.              control to the program.
  7313.  
  7314.              4) The code is fixed at the time the 8086 takes over. Any
  7315.              addresses are constants and are unchangable.
  7316.  
  7317.              Keep this in mind as we work through the template file.
  7318.  
  7319.  
  7320.              THE .LST FILE
  7321.  
  7322.              The first thing we need to look at is segments. Let's look at a
  7323.              slightly modified version of the template file called segs.asm.
  7324.              Here it is.
  7325.  
  7326.              ;***********************************
  7327.              ; segs.asm
  7328.  
  7329.              ______________________
  7330.  
  7331.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  7332.  
  7333.  
  7334.              Chapter 10 - Templates                                         79
  7335.              ______________________
  7336.  
  7337.  
  7338.              ; - - - - - - - - - - - - -
  7339.              STACKSEG SEGMENT STACK  'STACK'
  7340.  
  7341.              variable4    dw     4444h
  7342.                           dw     100h dup (?)
  7343.  
  7344.              STACKSEG  ENDS
  7345.              ; - - - - - - - - - - - - -
  7346.              MORESTUFF SEGMENT PUBLIC  'HOHUM'
  7347.  
  7348.              variable2    dw   2222h
  7349.  
  7350.              MORESTUFF    ENDS
  7351.              ; - - - - - - - - - - - - -
  7352.              DATASTUFF  SEGMENT PUBLIC  'DATA'
  7353.  
  7354.              variable1    dw     1111h
  7355.  
  7356.              DATASTUFF    ENDS
  7357.              ; - - - - - - - - - - - - -
  7358.              CODESTUFF SEGMENT PUBLIC  'CODE'
  7359.  
  7360.               EXTRN  print_num:NEAR , get_num:NEAR
  7361.  
  7362.               ASSUME cs:CODESTUFF,ds:DATASTUFF
  7363.               ASSUME es:MORESTUFF,ss:STACKSEG
  7364.  
  7365.              variable3    dw     3333h
  7366.  
  7367.              main   proc far
  7368.              start: push  ds
  7369.                     sub   ax,ax
  7370.                     push  ax
  7371.  
  7372.                     mov   ax, DATASTUFF
  7373.                     mov   ds,ax
  7374.                     mov   ax, MORESTUFF
  7375.                     mov   es,ax
  7376.  
  7377.                     mov   cx, variable1
  7378.                     mov   variable1, cx
  7379.  
  7380.                     ret
  7381.  
  7382.              main   endp
  7383.  
  7384.  
  7385.              CODESTUFF    ENDS
  7386.              ; - - - - - - - - - - - -
  7387.  
  7388.                  END     start
  7389.              ;***************************
  7390.  
  7391.              There is an extra segment put in that has the definition
  7392.  
  7393.                  MORESTUFF SEGMENT PUBLIC  'HOHUM'
  7394.  
  7395.  
  7396.              The PC Assembler Tutor                                         80
  7397.              ______________________
  7398.  
  7399.  
  7400.              There is a variable defined in each segment including the stack
  7401.              segment. These variables all have numbers in them, and the
  7402.              numbers are in hex so they will be easy to read. There are only
  7403.              two external subroutines (neither of which is called). It is time
  7404.              to take a look at an assembler listing.
  7405.  
  7406.              ----- THIS IS FROM THE SCREEN -----
  7407.  
  7408.              C>masm segs
  7409.              Microsoft (R) Macro Assembler Version 5.10
  7410.              Copyright (C) Microsoft Corp 1981, 1988.  All rights reserved.
  7411.  
  7412.              Object filename [segs.OBJ]:
  7413.              Source listing  [NUL.LST]: segs
  7414.              Cross-reference [NUL.CRF]:
  7415.  
  7416.              -----------
  7417.  
  7418.              If you don't put a semicolon after the filename with masm, you
  7419.              get some prompts. The first asks you if you want the object file
  7420.              name to be different from the asm file name. You may change
  7421.              either the name or the name and the extension. If you don't want
  7422.              to change either, just press ENTER. The second asks if you want a
  7423.              listing. Normally you don't, so you just press ENTER. This time
  7424.              we do, so we give it the same name as the assembler file. The
  7425.              assembler will generate a file SEGS.LST. Finally, it asks if you
  7426.              want the information needed to create a cross-reference file. We
  7427.              won't cover that. Once again, press ENTER. The assembler
  7428.              generates an object file and a listing. Here's the complete
  7429.              listing.
  7430.  
  7431.              **********************
  7432.              Microsoft (R) Macro Assembler Version 5.10
  7433.              9/2/89 09:50:54
  7434.  
  7435.                                                        Page     1-1
  7436.  
  7437.  
  7438.  
  7439.                                 ; segs.asm
  7440.  
  7441.                                 ; - - - - - - - - - - - - -
  7442.               0000                   STACKSEG SEGMENT STACK  'STACK'
  7443.  
  7444.               0000  4444             variable4    dw     4444h
  7445.               0002  0100[                         dw     100h dup (?)
  7446.                     ????
  7447.                             ]
  7448.  
  7449.  
  7450.               0202                   STACKSEG  ENDS
  7451.                                 ; - - - - - - - - - - - - -
  7452.               0000                   MORESTUFF SEGMENT PUBLIC  'HOHUM'
  7453.  
  7454.               0000  2222             variable2    dw   2222h
  7455.  
  7456.  
  7457.  
  7458.              Chapter 10 - Templates                                         81
  7459.              ______________________
  7460.  
  7461.               0002                   MORESTUFF    ENDS
  7462.                                 ; - - - - - - - - - - - - -
  7463.               0000                   DATASTUFF  SEGMENT PUBLIC  'DATA'
  7464.  
  7465.               0000  1111             variable1    dw     1111h
  7466.  
  7467.               0002                   DATASTUFF    ENDS
  7468.                                 ; - - - - - - - - - - - - -
  7469.               0000                   CODESTUFF SEGMENT PUBLIC  'CODE'
  7470.  
  7471.                                  EXTRN  print_num:NEAR , get_num:NEAR
  7472.  
  7473.                                  ASSUME cs:CODESTUFF,ds:DATASTUFF
  7474.                                  ASSUME es:MORESTUFF,ss:STACKSEG
  7475.  
  7476.               0000  3333             variable3    dw     3333h
  7477.  
  7478.               0002                   main   proc far
  7479.               0002  1E          start: push  ds
  7480.               0003  2B C0                   sub   ax,ax
  7481.               0005  50                 push  ax
  7482.  
  7483.               0006  B8 ---- R               mov   ax, DATASTUFF
  7484.               0009  8E D8                   mov   ds,ax
  7485.               000B  B8 ---- R               mov   ax, MORESTUFF
  7486.               000E  8E C0                   mov   es,ax
  7487.  
  7488.               0010  8B 0E 0000 R            mov   cx, variable1
  7489.               0014  89 0E 0000 R            mov   variable1, cx
  7490.  
  7491.               0018  CB                 ret
  7492.  
  7493.               0019                   main   endp
  7494.  
  7495.  
  7496.               0019                   CODESTUFF    ENDS
  7497.  
  7498.              Microsoft (R) Macro Assembler Version 5.10
  7499.              9/2/89 09:50:54
  7500.  
  7501.                                                        Page     1-2
  7502.  
  7503.  
  7504.                                 ; - - - - - - - - - - - -
  7505.  
  7506.                                     END     start
  7507.  
  7508.  
  7509.              Microsoft (R) Macro Assembler Version 5.10
  7510.              9/2/89 09:50:54
  7511.  
  7512.  
  7513.              Symbols-1
  7514.  
  7515.  
  7516.              Segments and Groups:
  7517.  
  7518.  
  7519.  
  7520.              The PC Assembler Tutor                                         82
  7521.              ______________________
  7522.  
  7523.                        N a m e               Length Align Combine  Class
  7524.  
  7525.              CODESTUFF  . . . . . . . . . . .  0019 PARA PUBLIC    'CODE'
  7526.              DATASTUFF  . . . . . . . . . . .  0002 PARA PUBLIC    'DATA'
  7527.              MORESTUFF  . . . . . . . . . . .  0002 PARA PUBLIC    'HOHUM'
  7528.              STACKSEG . . . . . . . . . . . .  0202 PARA STACK     'STACK'
  7529.  
  7530.              Symbols:
  7531.  
  7532.                      N a m e         Type     Value  Attr
  7533.  
  7534.              GET_NUM  .  . . . .     L NEAR    0000 CODESTUFF External
  7535.  
  7536.              MAIN . . .   . . . . .  F PROC    0002 CODESTUFF Length = 0017
  7537.  
  7538.              PRINT_NUM   . . . .     L NEAR    0000 CODESTUFF External
  7539.  
  7540.              START  . . . . . . . .  L NEAR    0002 CODESTUFF
  7541.  
  7542.              VARIABLE1  . . . . . .  L WORD    0000 DATASTUFF
  7543.              VARIABLE2  . . . . . .  L WORD    0000 MORESTUFF
  7544.              VARIABLE3  . . . . . .  L WORD    0000 CODESTUFF
  7545.              VARIABLE4  . . . . . .  L WORD    0000 STACKSEG
  7546.  
  7547.              @CPU . . . . . . . . . . . . . .  TEXT  0101h
  7548.              @FILENAME  . . . . . . . . . . .  TEXT  segs
  7549.              @VERSION . . . . . . . . . . . .  TEXT  510
  7550.  
  7551.  
  7552.                   54 Source  Lines
  7553.                   54 Total   Lines
  7554.                   21 Symbols
  7555.  
  7556.                48006 + 428261 Bytes symbol space free
  7557.  
  7558.                    0 Warning Errors
  7559.                    0 Severe  Errors
  7560.  
  7561.              **********************
  7562.  
  7563.              As you can see, the listing, even for a short program, is very
  7564.              long. Let's take it apart section by section. The first large
  7565.              section is a copy of the text file except that there is
  7566.              information on the left. The number on the far left tells the
  7567.              offset address (in hex) from the beginning of the segment for
  7568.              each label, variable or instruction. In this section:
  7569.  
  7570.  
  7571.               0000  3333             variable3    dw     3333h
  7572.  
  7573.               0002                   main   proc far
  7574.               0002  1E          start: push  ds
  7575.               0003  2B C0                   sub   ax,ax
  7576.               0005  50                 push  ax
  7577.  
  7578.               0006  B8 ---- R               mov   ax, DATASTUFF
  7579.               0009  8E D8                   mov   ds,ax
  7580.  
  7581.  
  7582.              Chapter 10 - Templates                                         83
  7583.              ______________________
  7584.  
  7585.               000B  B8 ---- R               mov   ax, MORESTUFF
  7586.               000E  8E C0                   mov   es,ax
  7587.  
  7588.               0010  8B 0E 0000 R            mov   cx, variable1
  7589.               0014  89 0E 0000 R            mov   variable1, cx
  7590.  
  7591.               0018  CB                 ret
  7592.  
  7593.               0019                   main   endp
  7594.  
  7595.              "start" is at 0002h ,"mov cx, variable1" is at 0010h and "ret" is
  7596.              at 18h.
  7597.  
  7598.              The second set of numbers is the actual machine instructions in
  7599.              hex. These are the what the 8086 operates on. "push ds" is 1E,
  7600.              "mov ds, ax" is 8E D8, and "ret" is CB. The instructions can be
  7601.              from 1 - 6 bytes long. Notice the "R" after some of the
  7602.              instructions. The "R" stands for relocatable. This means that it
  7603.              is an address that might be changed by either the linker or the
  7604.              loader. We'll talk about that later. In any case, the object file
  7605.              keeps track of these so they can be changed if necessary. Also,
  7606.              go back to the complete listing and look at the four variables;
  7607.              you will see that the values have been put in the object code;
  7608.              that is, 1111h, 2222h, 3333h and 4444h.
  7609.  
  7610.              If we had had an error, the assembler would have placed an error
  7611.              message at the spot of the error in this part of the file.
  7612.  
  7613.  
  7614.              The next part of the .LST file is the segment listing. It tells
  7615.              how the segments are defined.
  7616.  
  7617.                      N a m e                 Length Align Combine  Class
  7618.  
  7619.                  CODESTUFF  . . . . . . . .    0019 PARA PUBLIC    'CODE'
  7620.                  DATASTUFF  . . . . . . . .    0002 PARA PUBLIC    'DATA'
  7621.                  MORESTUFF  . . . . . . . .    0002 PARA PUBLIC    'HOHUM'
  7622.                  STACKSEG . . . . . . . . .    0202 PARA STACK     'STACK'
  7623.  
  7624.  
  7625.              We have the segment name, length, and some other information
  7626.              we'll talk about later. Notice that 'HOHUM' which is an
  7627.              artificial class, is dutifully listed with no complaints.
  7628.  
  7629.  
  7630.              Then comes the listing of all labels, variables, and procedure
  7631.              names.
  7632.  
  7633.                  Symbols:
  7634.  
  7635.                      N a m e         Type     Value  Attr
  7636.  
  7637.                  GET_NUM  . . . .    L NEAR    0000 CODESTUFF External
  7638.                  MAIN   . . . . .    F PROC    0002 CODESTUFF Length = 0017
  7639.                  PRINT_NUM    . .    L NEAR    0000 CODESTUFF External
  7640.                  START  . . . . .    L NEAR    0002 CODESTUFF
  7641.                  VARIABLE1  . . .    L WORD    0000 DATASTUFF
  7642.  
  7643.  
  7644.              The PC Assembler Tutor                                         84
  7645.              ______________________
  7646.  
  7647.                  VARIABLE2  . . .    L WORD    0000 MORESTUFF
  7648.                  VARIABLE3  . . .    L WORD    0000 CODESTUFF
  7649.                  VARIABLE4  . . .    L WORD    0000 STACKSEG
  7650.  
  7651.  
  7652.              It shows the segment and offset, whether they are bytes, words,
  7653.              processes etc. The "L" stands for label. The variables and
  7654.              procedures which are in an external file are so marked. Neither
  7655.              print_num nor get_num was called, but the assembler maintains a
  7656.              listing for them.
  7657.  
  7658.              Finally, some internal info for the assembler.
  7659.  
  7660.              @CPU . . . . . . . . . . . . . .  TEXT  0101h
  7661.              @FILENAME  . . . . . . . . . . .  TEXT  segs
  7662.              @VERSION . . . . . . . . . . . .  TEXT  510
  7663.  
  7664.              We will come back to parts of the .LST file, so make yourself
  7665.              comfortable with it.
  7666.  
  7667.  
  7668.  
  7669.              SEGMENTS
  7670.  
  7671.              It is now time for the nitty-gritty. We need to know what all
  7672.              those statements in the template file mean. Remember that there
  7673.              are four players in the game - (1) MASM, the Microsoft assembler,
  7674.              (2) LINK, the Microsoft linker, (3) the program loader and (4)
  7675.              the 8086 chip itself. Who does what to whom is the subject of
  7676.              this chapter.
  7677.  
  7678.              You will notice that there are three segments in all the template
  7679.              files, one for data, one for code, and one for the stack. How
  7680.              many segments can a program have? An unlimited number for code,
  7681.              an unlimited number for data, and one for the stack.{1}  Although
  7682.              you can have an unlimited number of segments, you can use only
  7683.              four at any one time - two for regular data (referenced by the DS
  7684.              and ES registers), one for code (referenced by the CS register),
  7685.              and one for temporary data (referenced by the SS register).
  7686.  
  7687.              You don't have direct control over CS. You should NEVER change
  7688.              the value in SS. This means that you can only change which
  7689.              segments that ES and DS refer to. How do you do that? The 8086
  7690.              does not allow you to move a constant into a segment register.
  7691.              Therefore it is a two step process. Put the constant into an
  7692.              arithmetic register (AX, BX, CX, DX, SI, DI or BP) and from there
  7693.              to the segment register. Suppose we have 327 different data
  7694.              segments in our file (named SEG1, SEG2, SEG3 ... SEG327) and we
  7695.              wanted to reference data in SEG27. The code would be:
  7696.  
  7697.                  mov  ax, SEG27
  7698.                  mov  ds, ax
  7699.  
  7700.              ____________________
  7701.  
  7702.                 1 Although if you REALLY need more space for a stack it is
  7703.              possible, if a little arcane.
  7704.  
  7705.  
  7706.              Chapter 10 - Templates                                         85
  7707.              ______________________
  7708.  
  7709.              This is the standard way to do it, and this is the same as the
  7710.              fourth and fifth instructions in the code segment of the template
  7711.              files where we are putting the address of DATASTUFF in ds.
  7712.  
  7713.              What is that SEG27 in the instruction (mov  ax, SEG27)? It is a
  7714.              constant. When the assembler assembles the program, it makes note
  7715.              of the fact that you want to have the starting address of SEG27
  7716.              in that instruction (you saw the "R" in the listing for the
  7717.              instruction 'mov ax, DATASTUFF'). Later the linker makes sure
  7718.              there is a SEG27 segment in the complete program, gives it a
  7719.              temporary segment address, and puts this temporary address in
  7720.              every place that references that segment address. This address is
  7721.              guaranteed to be adjusted. You will see why when we look at the
  7722.              linker .MAP file.
  7723.  
  7724.              Finally, the loader (which is the program that puts your program
  7725.              into memory) puts the segment where it wants and updates all
  7726.              references to the segment address to reflect where it now is.
  7727.              Thus, the program is complete only when this information is put
  7728.              in at run time. Each time you run the program SEG27 might be in a
  7729.              different place, but the loader will always update the references
  7730.              correctly.
  7731.  
  7732.              We named the segments SEG1, SEG2, etc. Does SEG have to be part
  7733.              of the segment name? Not on your life. Here are three perfectly
  7734.              acceptable segment definitions:
  7735.  
  7736.                  CURLY     SEGMENT
  7737.                  LARRY     SEGMENT
  7738.                  MOE       SEGMENT
  7739.  
  7740.              It is good practice to have 'SEG' as part of the segment name to
  7741.              remind you that these are segments, not variables, but this is a
  7742.              practice only, it is not a law. Any name you could use for a
  7743.              variable or a label you could use as a segment name. The reserved
  7744.              word SEGMENT after the name tells the assembler that this is the
  7745.              beginning of a segment with that name. You tell the assembler
  7746.              that you are starting a segment with 'SEGMENT'
  7747.  
  7748.                  CURLY     SEGMENT
  7749.  
  7750.              and you tell the assembler that you are finished with that
  7751.              segment with the reserved word ENDS (END [of] Segment):
  7752.  
  7753.                  CURLY     ENDS
  7754.  
  7755.              You need to put the name of the segment before the  ENDS
  7756.              directive.
  7757.  
  7758.              In the template file, the data segment definition reads:
  7759.  
  7760.                  DATASTUFF  SEGMENT  PUBLIC    'DATA'
  7761.  
  7762.              DATASTUFF is the segment name, but what are PUBLIC and 'DATA'
  7763.              there for? To understand this, we need to look at the linker.
  7764.              First, let's assemble temp1.asm (our first template file) just
  7765.              the way it is.
  7766.  
  7767.  
  7768.              The PC Assembler Tutor                                         86
  7769.              ______________________
  7770.  
  7771.  
  7772.              ---------- FROM THE SCREEN ----------
  7773.              C>masm temp1.asm
  7774.              Microsoft (R) Macro Assembler Version 5.10
  7775.              Copyright (C) Microsoft Corp 1981, 1988.  All rights reserved.
  7776.  
  7777.  
  7778.              Object filename [temp1.OBJ]:
  7779.              Source listing  [NUL.LST]: temp1
  7780.              Cross-reference [NUL.CRF]:
  7781.  
  7782.              ----------
  7783.              We have made the listing file so let's look at the segment
  7784.              information.
  7785.  
  7786.  
  7787.              N a m e                         Length Align Combine Class
  7788.  
  7789.              CODESTUFF  . . . . . . . . . . .  000A PARA PUBLIC    'CODE'
  7790.              DATASTUFF  . . . . . . . . . . .  0000 PARA PUBLIC    'DATA'
  7791.              STACKSEG . . . . . . . . . . . .  00C8 PARA STACK     'STACK'
  7792.  
  7793.              You will see that CODESTUFF is Ah (10d) bytes long, DATASTUFF has
  7794.              no data and is 0 bytes long, and STACKSEG is C8h (200d) bytes
  7795.              long.
  7796.  
  7797.              Now let's link temp1.obj and asmhelp.obj.
  7798.  
  7799.              ---------- FROM THE SCREEN -----
  7800.  
  7801.              C>link temp1+asmhelp
  7802.  
  7803.              Microsoft (R) Overlay Linker  Version 3.61
  7804.              Copyright (C) Microsoft Corp 1983-1987.  All rights reserved.
  7805.  
  7806.  
  7807.              Run File [TEMP1.EXE]:
  7808.              List File [NUL.MAP]: temp
  7809.              Libraries [.LIB]:
  7810.  
  7811.              ----------
  7812.  
  7813.              This time we have made a listing file for the link process. It is
  7814.              called TEMP.MAP. Let's look at it.
  7815.  
  7816.                   Start  Stop   Length Name         Class
  7817.                   00000H 000C7H 000C8H STACKSEG     STACK
  7818.                   000D0H 00540H 00471H DATASTUFF    DATA
  7819.                   00550H 01944H 013F5H CODESTUFF    CODE
  7820.  
  7821.                  Program entry point at 0055:0000
  7822.  
  7823.              This is what the map file looks like. There are still only three
  7824.              segments in the final executable file, STACKSEG, DATASTUFF and
  7825.              CODESTUFF. You will notice that the class name is still there,
  7826.              but the PUBLIC is missing. It's job is finished. "Start" says
  7827.              where the segment starts in the executable file, "Stop" says
  7828.  
  7829.  
  7830.              Chapter 10 - Templates                                         87
  7831.              ______________________
  7832.  
  7833.              where the segment ends in the executable file, and "Length" says
  7834.              the length in bytes of the segment. These numbers are 5 digit hex
  7835.              numbers instead of 4. That means that they are showing the total
  7836.              address. The segment number is the left 4 digits of 'Start'.
  7837.  
  7838.              STACKSEG is C8h (200d) bytes long like before. Although DATASTUFF
  7839.              had no data, it is now 471h (1137d) bytes long, and CODESTUFF was
  7840.              Ah (10d) bytes long before but now it is a whopping 13F5h (5109d)
  7841.              bytes long. What happened? The linker did its work.
  7842.  
  7843.              One of the things the linker does is combine things that we want
  7844.              to be in the same segment. It took the DATASTUFF segment from
  7845.              temp1.obj and appended the DATASTUFF segment from asmhelp.obj,
  7846.              combining them into one larger segment.{2}  It took the CODESTUFF
  7847.              segment from temp1.obj and appended the CODESTUFF segment from
  7848.              asmhelp.obj, making them one large segment. Why did it do that?
  7849.              Because we put the word "PUBLIC" in the segment definition. When
  7850.              the assembler sees "PUBLIC" in the segment definition, it passes
  7851.              that information along to the linker in a header file.{3}  When
  7852.              the linker has a segment which is "PUBLIC", it will append any
  7853.              other segment which (1) is "PUBLIC", (2) has the same name (i.e.
  7854.              CODESTUFF or DATASTUFF or CURLY etc.), and (3) has the same class
  7855.              name{4}. All three things must be true for the linker to combine
  7856.              them. We will actually check this out a little later to make sure
  7857.              you believe it.
  7858.  
  7859.              One other thing to notice is that the linker is allocating only
  7860.              as much space as is needed. It could allocate 65536 bytes for
  7861.              each segment defined, but it uses only as much as the program
  7862.              needs and then starts the next segment at the next segment
  7863.              starting address. This is efficient management of memory.
  7864.  
  7865.              What is the advantage of combining the smaller segments into one
  7866.              larger segment? For code, there is no big advantage. But for
  7867.              data, remember that every time we want to access data, we need to
  7868.              have the starting address of that particular segment in register
  7869.              ds. We do this by using:
  7870.  
  7871.                  mov  ax, DATASTUFF
  7872.                  mov  ds, ax
  7873.  
  7874.              If we have a number of data segments, every time we access data
  7875.              ____________________
  7876.  
  7877.                 2 The linker always works from left to right. For each
  7878.              different type of segment, it starts with the first one it finds
  7879.              and then appends each succeeding one it finds.
  7880.  
  7881.                 3 A header is information for the linker or loader which is
  7882.              put in front of the machine code in an object file or an
  7883.              executable file. There are typically a number of headers in front
  7884.              of the machine code.
  7885.  
  7886.                 4 Remember that class names are somewhat arbitrary. I use
  7887.              'CODE', 'DATA' and 'STACK' for clarity and because they are the
  7888.              standard Microsoft class names, but if you are not linking with
  7889.              anyone else's programs, you can use any class name you want.
  7890.  
  7891.  
  7892.              The PC Assembler Tutor                                         88
  7893.              ______________________
  7894.  
  7895.              we need to (1) make sure that ds contains the address of the
  7896.              correct data segment, and (2) if not, we need to write the code
  7897.              to change ds. This entails using a lot of code, can be confusing
  7898.              and is certainly error prone. With one data segment, you simply
  7899.              load ds with the correct address at the beginning of the program
  7900.              and then forget about it. This should be a rule for you. Unless
  7901.              you have truly humongous amounts of data (over 65535 bytes),
  7902.              ALWAYS put all your data in the same segment.
  7903.  
  7904.              Do you remember those dashes '----' in the assembler listing?
  7905.              That was because the assembler didn't have a segment address to
  7906.              put there.
  7907.  
  7908.                   0004  B8 ---- R                mov   ax, DATASTUFF
  7909.                   0007  8E D8                    mov   ds,ax
  7910.  
  7911.                   0009  8B 0E 0000 R             mov   cx, variable1
  7912.                   000D  89 0E 0000 R             mov   variable1, cx
  7913.  
  7914.              The linker now has a temporary address for the start of DATASTUFF
  7915.              (000D0h) so it will put the segment address (the left four hex
  7916.              bytes) in this spot. This is temporary, but will be updated by
  7917.              the loader. If variable1 has been moved, it will update that too.
  7918.  
  7919.              Why am I sure that these temporary segments will be moved? The
  7920.              segment address of STACKSEG is 0000h. The segment address of
  7921.              DATASTUFF is 000Dh (13h) and the segment address of CODESTUFF is
  7922.              0055h (85d). But the operating sysyem owns the first several
  7923.              THOUSAND segments. The loader will load your program in much
  7924.              higher memory. They must move.
  7925.  
  7926.              So the linker combines all the segments we want to combine, and
  7927.              then it looks at the machine code and modifies every reference to
  7928.              the segments and to the variables which have been moved. That is
  7929.              a lot of work. For instance, when the linker appends asmhelp.obj,
  7930.              there are a hundred or so variables which it moves and a thousand
  7931.              or so references to those variables which it modifies. The linker
  7932.              does that every time you link a file with ASMHELP.OBJ. That's not
  7933.              too shabby.
  7934.  
  7935.              Do all three conditions need to be met for the linker to combine
  7936.              segments into one segment?
  7937.  
  7938.                  1)  They have the same name
  7939.                  2)  They have the same class name
  7940.                  3)  They are both defined PUBLIC
  7941.  
  7942.              Joe Bob says check it out. Here are two .ASM files which contain
  7943.              a number of segments. Here's the first file:
  7944.  
  7945.                ;file1.asm
  7946.                ;- - - - - - - - - - - - - - - - - - - -
  7947.                STACKSEG    SEGMENT   STACK  'STACK'
  7948.                             dw     100 dup (?)
  7949.                STACKSEG    ENDS
  7950.                ;- - - - - - - - - - - - - - - - - - - -
  7951.                MORESTUFFA    SEGMENT   PUBLIC
  7952.                variable21    dw     ?
  7953.                MORESTUFFA    ENDS
  7954.                ;- - - - - - - - - - - - - - - - - - - -
  7955.                DATASTUFF    SEGMENT   PUBLIC  'DATA'
  7956.                variable1    dw     ?
  7957.                DATASTUFF    ENDS
  7958.                ;- - - - - - - - - - - - - - - - - - - -
  7959.                MORESTUFF    SEGMENT   PUBLIC  'DATA'
  7960.                variable2    dw     ?
  7961.                MORESTUFF    ENDS
  7962.                ;- - - - - - - - - - - - - - - - - - - -
  7963.                EVENMORESTUFF    SEGMENT   PUBLIC  'DATA'
  7964.                variable3    dw     ?
  7965.                EVENMORESTUFF    ENDS
  7966.                ;- - - - - - - - - - - - - - - - - - - -
  7967.                CODESTUFF    SEGMENT   PUBLIC  'CODE'
  7968.                       ASSUME cs:CODESTUFF, ds:DATASTUFF
  7969.                       ASSUME ds:MORESTUFF, es:MORESTUFFA, ds:EVENMORESTUFF
  7970.                main   proc far
  7971.                start: push  ds
  7972.                       sub   ax,ax
  7973.                       push  ax
  7974.                       ret
  7975.                main   endp
  7976.                CODESTUFF    ENDS
  7977.                ;- - - - - - - - - - - - - - - - - - - -
  7978.                       END     start
  7979.  
  7980.              Here's the other file:
  7981.  
  7982.                ;file2.asm
  7983.                ; - - - - - - - - - - - - - - - - - - - -
  7984.                STACKSEG    SEGMENT   STACK  'STACK'
  7985.                             dw     100 dup (?)
  7986.                STACKSEG    ENDS
  7987.                ; - - - - - - - - - - - - - - - - - - - -
  7988.                NOTDATASTUFF    SEGMENT   PUBLIC  'DATA'
  7989.                variable4    dw     ?
  7990.                NOTDATASTUFF    ENDS
  7991.                ; - - - - - - - - - - - - - - - - - - - -
  7992.  
  7993.  
  7994.              The PC Assembler Tutor                                         90
  7995.              ______________________
  7996.  
  7997.                DATASTUFF    SEGMENT   PUBLIC  'DATA'
  7998.                variable5    dw     ?
  7999.                DATASTUFF    ENDS
  8000.                ; - - - - - - - - - - - - - - - - - - - -
  8001.                MORESTUFFA    SEGMENT   PUBLIC
  8002.                variable61    dw     ?
  8003.                MORESTUFFA    ENDS
  8004.                ; - - - - - - - - - - - - - - - - - - - -
  8005.                MORESTUFF    SEGMENT   PUBLIC  'CLASSOF68'
  8006.                variable6    dw     ?
  8007.                MORESTUFF    ENDS
  8008.                ; - - - - - - - - - - - - - - - - - - - -
  8009.                EVENMORESTUFF    SEGMENT   'DATA'
  8010.                variable7    dw     ?
  8011.                EVENMORESTUFF    ENDS
  8012.                ; - - - - - - - - - - - - - - - - - - - -
  8013.                CODESTUFF    SEGMENT   PUBLIC  'CODE'
  8014.                 ASSUME cs:CODESTUFF, ds:DATASTUFF, ds:NOTDATASTUFF
  8015.                 ASSUME ds:MORESTUFF,ds:MORESTUFFA, ds:EVENMORESTUFF
  8016.                subroutine   proc far
  8017.                       ret
  8018.                subroutine    endp
  8019.                CODESTUFF    ENDS
  8020.                ; - - - - - - - - - - - - - - - - - - - -
  8021.                       END
  8022.  
  8023.  
  8024.  
  8025.              You will notice that the two CODESTUFFs, the two DATASTUFFs, the
  8026.              two MORESTUFFAs and the two STACKSEGs each have the same
  8027.              definitions, but that (1) NOTDATASTUFF has a different name than
  8028.              DATASTUFF, (2) one MORESTUFF has a different class name from the
  8029.              other, (3) one EVENMORESTUFF is PUBLIC and the other is not, and
  8030.              (4) the two MORESTUFFAs have NO class name.
  8031.  
  8032.              Here's the segment information from file1.lst
  8033.  
  8034.  
  8035.  
  8036.                N a m e                       Length Align  Combine  Class
  8037.  
  8038.                CODESTUFF  . . . . . . . . . .  0005 PARA    PUBLIC  'CODE'
  8039.                DATASTUFF  . . . . . . . . . .  0002 PARA    PUBLIC  'DATA'
  8040.                EVENMORESTUFF  . . . . . . . .  0002 PARA    PUBLIC  'DATA'
  8041.                MORESTUFF  . . . . . . . . . .  0002 PARA    PUBLIC  'DATA'
  8042.                MORESTUFFA . . . . . . . . . .  0002 PARA    PUBLIC
  8043.                STACKSEG . . . . . . . . . . .  00C8 PARA    STACK   'STACK'
  8044.  
  8045.  
  8046.  
  8047.  
  8048.              and from file2.lst
  8049.  
  8050.  
  8051.                N a m e                   Length Align  Combine  Class
  8052.  
  8053.                CODESTUFF  . . . . . . .   0001 PARA    PUBLIC  'CODE'
  8054.  
  8055.  
  8056.              Chapter 10 - Templates                                         91
  8057.              ______________________
  8058.  
  8059.                DATASTUFF  . . . . . . .   0002 PARA    PUBLIC  'DATA'
  8060.                EVENMORESTUFF  . . . . .   0002 PARA    NONE    'DATA'
  8061.                MORESTUFF  . . . . . . .   0002 PARA    PUBLIC  'CLASSOF68'
  8062.                MORESTUFFA . . . . . . .   0002 PARA    PUBLIC
  8063.                NOTDATASTUFF . . . . . .   0002 PARA    PUBLIC  'DATA'
  8064.                STACKSEG . . . . . . . .   00C8 PARA    STACK   'STACK'
  8065.  
  8066.  
  8067.  
  8068.              These are in alphabetical order. Before we link them together,
  8069.              let's think about what should happen if all three conditions must
  8070.              be met. Both CODESTUFF segments are PUBLIC with the same class
  8071.              name, so they should merge. Both DATASTUFF segments are PUBLIC
  8072.              with the same class name so they should merge. EVENMORESTUFF is
  8073.              PUBLIC in one file but not public in the other, so they should
  8074.              not merge. MORESTUFF is PUBLIC in both files, but they have
  8075.              different class names, so they should not merge. What about
  8076.              STACKSEG? The STACK combine type is similar to PUBLIC{1}, and
  8077.              they have the same class name, so they should merge. Finally,
  8078.              there are the  MORESTUFFAs. They have the same name and are
  8079.              PUBLIC, but they have no class name. Will they combine?
  8080.  
  8081.              Let's see what happens. Here is the .MAP file from the command
  8082.  
  8083.                  C> link file1+file2
  8084.  
  8085.  
  8086.                   Start  Stop   Length Name                   Class
  8087.                   00000H 0018FH 00190H STACKSEG               STACK
  8088.                   00190H 001A1H 00012H MORESTUFFA
  8089.                   001B0H 001C1H 00012H DATASTUFF              DATA
  8090.                   001D0H 001D1H 00002H MORESTUFF              DATA
  8091.                   001E0H 001E1H 00002H EVENMORESTUFF          DATA
  8092.                   001F0H 001F1H 00002H NOTDATASTUFF           DATA
  8093.                   00200H 00201H 00002H EVENMORESTUFF          DATA
  8094.                   00210H 00220H 00011H CODESTUFF              CODE
  8095.                   00230H 00231H 00002H MORESTUFF              CLASSOF68
  8096.  
  8097.                  Program entry point at 0021:0000
  8098.  
  8099.  
  8100.              STACKSEG, DATASTUFF and CODESTUFF combined. MORESTUFFA combined.
  8101.              The others are separate. Doesn't this confuse the linker if it
  8102.              has more than one segment with the same name? No. The linker
  8103.              knows which variables are in which segments, and the names of the
  8104.              segments are not relevant.
  8105.  
  8106.              If you look at the class information from the linker listing, you
  8107.              will notice that all things in the same class are grouped
  8108.              together. The linker works from left to right on the command
  8109.              line, so for the above, it read file1.obj first and then read
  8110.              ____________________
  8111.  
  8112.                 1 STACK tells the linker to combine any other segments which
  8113.              have STACK and the class type 'STACK' and it tells the loader to
  8114.              set the SS register to that segment, and set the SP register to
  8115.              point to the end of that segment.
  8116.  
  8117.  
  8118.              The PC Assembler Tutor                                         92
  8119.              ______________________
  8120.  
  8121.              file2.obj. It orders things (1) first by class (in the order
  8122.              encountered, and then (2) by segment (in the order encountered).
  8123.              For the linker ordering, a segment is like a subclass.
  8124.  
  8125.              Look through the assembler files to check that if you link in the
  8126.              order file1+file2, the order of encountering classes is 'STACK',
  8127.              empty, 'DATA', 'CODE', and 'CLASSOF68'. check the segment
  8128.              ordering also. What if we link the opposite way?
  8129.  
  8130.                  > link file2+file1
  8131.  
  8132.              Here's the listing:
  8133.  
  8134.  
  8135.                   Start  Stop   Length Name                   Class
  8136.                   00000H 0018FH 00190H STACKSEG               STACK
  8137.                   00190H 00191H 00002H NOTDATASTUFF           DATA
  8138.                   001A0H 001B1H 00012H DATASTUFF              DATA
  8139.                   001C0H 001C1H 00002H EVENMORESTUFF          DATA
  8140.                   001D0H 001D1H 00002H MORESTUFF              DATA
  8141.                   001E0H 001E1H 00002H EVENMORESTUFF          DATA
  8142.                   001F0H 00201H 00012H MORESTUFFA
  8143.                   00210H 00211H 00002H MORESTUFF              CLASSOF68
  8144.                   00220H 00234H 00015H CODESTUFF              CODE
  8145.  
  8146.                  Program entry point at 0022:0010
  8147.  
  8148.              Assure yourself that this is the order the classes are
  8149.              encountered for file2+file1.
  8150.  
  8151.  
  8152.              Before we go on, let's summarize what we have so far.
  8153.  
  8154.                  1) In an .asm file, each segment starts with a name followed
  8155.                  by the word SEGMENT.
  8156.  
  8157.                  2) Each segment ends with the name followed by the word ENDS
  8158.                  (end of segment).
  8159.  
  8160.              This is the minimal segment definition:
  8161.  
  8162.                  ; - - - - -
  8163.                  SEG_A  SEGMENT
  8164.  
  8165.                  SEG_A  ENDS
  8166.                  ; - - - - -
  8167.  
  8168.              In addition, if you want to combine a segment with segments from
  8169.              other files in order to make one large segment, then all the
  8170.              segments to be combined must:
  8171.  
  8172.                  1) have the same name.
  8173.                  2) have the same class name (type)
  8174.                  3) be declared PUBLIC
  8175.  
  8176.  
  8177.  
  8178.  
  8179.  
  8180.              Chapter 10 - Templates                                         93
  8181.              ______________________
  8182.  
  8183.              ASSUME
  8184.  
  8185.              The next thing from the template file is the word ASSUME. Who is
  8186.              assuming what?
  8187.  
  8188.                  ASSUME cs:CODESTUFF, ds:DATASTUFF
  8189.  
  8190.              This is for the assembler. It says that whenever you are working
  8191.              in the CODESTUFF segment, CS will be set to the segment address
  8192.              of the CODESTUFF segment. Whenever you are working in the
  8193.              DATASTUFF segment, DS will be set to the segment address of the
  8194.              DATASTUFF segment. The CS register takes care of itself, but it
  8195.              is your responsibility to make sure that DS actually points to
  8196.              the proper segment.
  8197.  
  8198.  
  8199.              If you just move a word from memory to a register:
  8200.  
  8201.                  mov  cx, variable1
  8202.  
  8203.              the 8086 automatically thinks that it is in the DS segment. But
  8204.              it doesn't have to be that way. The 8086 has something called
  8205.              segment overrides. Here is the list:
  8206.  
  8207.                          SEGMENT       HEX VALUE
  8208.                            CS             2E
  8209.                            DS             3E
  8210.                            ES             26
  8211.                            SS             36
  8212.  
  8213.              An override is a 1 byte machine instruction that tells the 8086
  8214.              that for the next instruction, the memory location will not
  8215.              reference the natural segment register; what it will reference is
  8216.              the segment register named in the override - CS if it is 2Eh, DS
  8217.              if it is 3Eh, ES if it is 26h, and SS if it is 36h.
  8218.  
  8219.              We could plug these in ourselves, but that is a lot of work.
  8220.              Fortunately, the assembler takes care of this for us. Let's look
  8221.              at the code from the very beginning of the chapter.
  8222.  
  8223.              ;***********************************
  8224.              ; segs.asm
  8225.  
  8226.              ; - - - - - - - - - - - - -
  8227.              STACKSEG SEGMENT STACK  'STACK'
  8228.  
  8229.              variable4dw   4444h
  8230.                        dw     100h dup (?)
  8231.  
  8232.              STACKSEG  ENDS
  8233.              ; - - - - - - - - - - - - -
  8234.              MORESTUFF SEGMENT PUBLIC  'HOKUM'
  8235.  
  8236.              variable2    dw   2222h
  8237.  
  8238.              MORESTUFF    ENDS
  8239.              ; - - - - - - - - - - - - -
  8240.  
  8241.  
  8242.              The PC Assembler Tutor                                         94
  8243.              ______________________
  8244.  
  8245.              DATASTUFF  SEGMENT PUBLIC  'DATA'
  8246.  
  8247.              variable1    dw     1111h
  8248.  
  8249.              DATASTUFF    ENDS
  8250.              ; - - - - - - - - - - - - -
  8251.              CODESTUFF SEGMENT PUBLIC  'CODE'
  8252.  
  8253.               EXTRN  print_num:NEAR , get_num:NEAR
  8254.  
  8255.               ASSUME cs:CODESTUFF,ds:DATASTUFF
  8256.               ASSUME es:MORESTUFF,ss:STACKSEG
  8257.  
  8258.              variable3    dw     3333h
  8259.  
  8260.              main   proc far
  8261.              start: push  ds
  8262.                     sub   ax,ax
  8263.                     push  ax
  8264.  
  8265.                     mov   ax, DATASTUFF
  8266.                     mov   ds,ax
  8267.                     mov   ax, MORESTUFF
  8268.                     mov   es, ax
  8269.  
  8270.                     mov   cx, variable1
  8271.                     mov   variable1, cx
  8272.  
  8273.                     ret
  8274.  
  8275.              main   endp
  8276.  
  8277.  
  8278.              CODESTUFF    ENDS
  8279.              ; - - - - - - - - - - - -
  8280.  
  8281.                  END     start
  8282.              ;***************************
  8283.  
  8284.              For the ASSUME statement we have:
  8285.  
  8286.               ASSUME cs:CODESTUFF,ds:DATASTUFF
  8287.               ASSUME es:MORESTUFF,ss:STACKSEG
  8288.  
  8289.              What we want to look at is this section of code:
  8290.  
  8291.                  mov  cx, variable1
  8292.                  mov  variable1, cx
  8293.  
  8294.              Here is the listing of the offset address and machine code:
  8295.  
  8296.  
  8297.                 000E  8E C0                 mov   es,ax
  8298.  
  8299.                 0010  8B 0E 0000 R          mov   cx, variable1
  8300.                 0014  89 0E 0000 R          mov   variable1, cx
  8301.  
  8302.  
  8303.  
  8304.              Chapter 10 - Templates                                         95
  8305.              ______________________
  8306.  
  8307.                 0018  CB                    ret
  8308.  
  8309.              Variable1 is in DATASTUFF (ASSUME ds:DATASTUFF), and DS is the
  8310.              natural segment for variables. Now let's change the code to:
  8311.  
  8312.                  mov  cx, variable2
  8313.                  mov  variable2, cx
  8314.  
  8315.              This is the ONLY change in the file. Variable2 is in MORESTUFF
  8316.              and we have - ASSUME es:MORESTUFF.  Here's the listing when we
  8317.              assemble the modified file.
  8318.  
  8319.                 000E  8E C0                  mov   es,ax
  8320.  
  8321.                 0010  26: 8B 0E 0000 R       mov   cx, variable2
  8322.                 0015  26: 89 0E 0000 R       mov   variable2, cx
  8323.  
  8324.                 001A  CB                     ret
  8325.  
  8326.              The assembler has put 26h as a segment override. When the 8086
  8327.              looks at the machine code, it knows that those two instructions
  8328.              reference the es, not the ds, segment register. Also note that
  8329.              the code is now two bytes longer - one byte for each segment
  8330.              override. The "ret" instruction is at 1Ah (26d) instead of 18h
  8331.              (24d).
  8332.  
  8333.              Let's try it with:
  8334.  
  8335.                  mov  cx, variable3
  8336.                  mov  variable3, cx
  8337.  
  8338.              Variable3 is in CODESTUFF and we have - ASSUME cs:CODESTUFF.
  8339.              Here's the listing:
  8340.  
  8341.                 000E  8E C0                  mov   es,ax
  8342.  
  8343.                 0010  2E: 8B 0E 0000 R       mov   cx, variable3
  8344.                 0015  2E: 89 0E 0000 R       mov   variable3, cx
  8345.  
  8346.                 001A  CB                     ret
  8347.  
  8348.              The assembler put in the CS segment override. Now the 8086 knows
  8349.              that variable3 is in the CS segment. Finally:
  8350.  
  8351.                  mov  cx, varaible4
  8352.                  mov  variable4, cx
  8353.  
  8354.              Variable4 is in STACKSEG and we have - ASSUME ss:STACKSEG. Here's
  8355.              the listing:
  8356.  
  8357.                 000E  8E C0                  mov   es,ax
  8358.  
  8359.                 0010  36: 8B 0E 0000 R       mov   cx, variable4
  8360.                 0015  36: 89 0E 0000 R       mov   variable4, cx
  8361.  
  8362.                 001A  CB                     ret
  8363.  
  8364.  
  8365.  
  8366.              The PC Assembler Tutor                                         96
  8367.              ______________________
  8368.  
  8369.              Once again, the assembler put in a segment override. This time it
  8370.              was the SS override.
  8371.  
  8372.              That's nifty. We simply tell the assembler which segment register
  8373.              we will use for each segment and it does all the work. We will do
  8374.              more with segment overrides in the chapter on addressing modes.
  8375.  
  8376.              Remember, though, that it is your responsibility to see that at
  8377.              the time this code is used, the segment register actually
  8378.              contains the appropriate segment address.
  8379.  
  8380.              Is this ASSUME definition unique? That is, must there be a one to
  8381.              one correspondence between segments and registers, with each
  8382.              segment having its own register? No, not at all. Here are a two
  8383.              ASSUME statements, both of which are legal:
  8384.  
  8385.                  ASSUME cs:COMSEG, ds:COMSEG, es:COMSEG, SS:COMSEG
  8386.  
  8387.              All four registers contain the address of the same segment. In
  8388.              fact, we will meet this statement when we talk about COM files.
  8389.              This is the only appropriate statement for a .COM file
  8390.  
  8391.                  ASSUME  ds:SEG_A, ds:SEG_B, es:SEG_C, es:SEG_D, es:SEG_A
  8392.  
  8393.              Four different segments, two of which are referenced by DS and
  8394.              three of which are referenced by ES. Remember, ASSUME tells the
  8395.              assembler that whenever you access something in that segment, the
  8396.              named register will be set to the starting segment address. What
  8397.              exactly does this mean to the assembler? Let's rearrange this a
  8398.              little:
  8399.  
  8400.                  SEG_A     ds, es
  8401.                  SEG_B     ds
  8402.                  SEG_C     es
  8403.                  SEG_D     es
  8404.  
  8405.              This is the list from the assembler's viewpoint. Suppose it has a
  8406.              variable that is in SEG_C. Does it need an override? Yes, it
  8407.              needs an ES override.  Suppose it has a variable in SEG_A. Does
  8408.              it need an override? No, because DS is set to that segment.
  8409.  
  8410.  
  8411.  
  8412.              SUBROUTINES
  8413.  
  8414.              In assembler parlance, subroutines are called procedures. Why?
  8415.              You got me. In any case, whenever I say subroutine, process,
  8416.              subprogram, or anything like that, I mean a procedure. A
  8417.              procedure can have any name you want. You start a procedure by
  8418.              giving the name, using the reserved word 'proc' and then
  8419.              defining it as either near or far.
  8420.  
  8421.                  my_procedure  proc  near
  8422.  
  8423.              is a near procedure with the name my_procedure. You end a
  8424.              procedure by giving the name and following it with the reserved
  8425.              word 'endp' (for end of procedure).
  8426.  
  8427.  
  8428.              Chapter 10 - Templates                                         97
  8429.              ______________________
  8430.  
  8431.  
  8432.                  my_procedure  endp
  8433.  
  8434.              What is a near procedure? It is one which is ALWAYS in the same
  8435.              segment as the calling program. When you call a near procedure,
  8436.              the value in CS stays the same, but IP (the instruction pointer)
  8437.              changes to the offset of the first byte of the procedure. The
  8438.              next instruction executed will be the first byte of the
  8439.              procedure.
  8440.  
  8441.              If a procedure is called even once from a different segment, then
  8442.              it MUST be a far procedure.
  8443.  
  8444.                  my_procedure  proc  far
  8445.  
  8446.                  my_procedure  endp
  8447.  
  8448.              When you call a far procedure, the CS register is changed to the
  8449.              segment of the called procedure and IP (the instruction pointer)
  8450.              is set to the first byte of the procedure. This will be covered
  8451.              in the chapter on subroutines.
  8452.  
  8453.              How does the loader know where to start the program? The
  8454.              assembler tells the linker which tells the loader. How does the
  8455.              assembler know? You tell it. The last line of the file is the
  8456.              single word 'END'. That tells the assembler that you are done
  8457.              with the assembler code. If there is a word after the word 'END'
  8458.              (on the same line), then the assembler assumes that this word is
  8459.              the name of the label where the program starts. The first
  8460.              instruction executed will be whatever immediately follows that
  8461.              label. In the template files we have:
  8462.  
  8463.                  END start
  8464.  
  8465.              so the label 'start:' indicates where the first instruction is.
  8466.              For an .EXE file, this can be anywhere at all, but we have it at
  8467.              the beginning. The label 'start:' is used for clarity, but we
  8468.              could just as easily have had:
  8469.  
  8470.                  END zzyx4
  8471.  
  8472.              The assembler would then look for the label 'zzyx4:' as the place
  8473.              to start the program. If you look at the link .MAP file from our
  8474.              file1+file2 example you will see:
  8475.  
  8476.                  Program entry point at 0021:0000
  8477.  
  8478.              That says that the starting address is CS = 0021h, IP = 0000h.
  8479.              Note that both CS and IP are different for the file2+file1
  8480.              example:
  8481.  
  8482.                  Program entry point at 0022:0010
  8483.  
  8484.              where CS = 0022h  and IP = 0010h. The initial offset was given to
  8485.              the linker by the assembler. The linker did any adjustment to the
  8486.              offset if it moved the code, and then it calculated the segment
  8487.              address itself.
  8488.  
  8489.  
  8490.              The PC Assembler Tutor                                         98
  8491.              ______________________
  8492.  
  8493.  
  8494.  
  8495.              RET
  8496.  
  8497.              When the loader loads the program, it puts the segment of the
  8498.              starting address in CS and the offset of the starting address in
  8499.              IP. This gives control to your program. When your program is
  8500.              done, how does it get back to the operating system? Good
  8501.              question.
  8502.  
  8503.              When the loader loads the program, it creates something called
  8504.              the PSP (program segment prefix). This is a 100h (256d) byte
  8505.              block of information and code. The first byte (offset 0000) of
  8506.              this block is an 8086 instruction for an orderly exit from a
  8507.              program. What we need to do is set CS to the PSP segment and set
  8508.              IP to 0000. Then the next instruction executed will be the
  8509.              orderly exit code.
  8510.  
  8511.              In talking about procedures, I said that when you call a far
  8512.              procedure, the 8086 puts the procedure's segment in CS and the
  8513.              procedure's offset in IP. But before that, it does two things:
  8514.  
  8515.                  push CS        ; these are the old CS and IP
  8516.                  push IP        ; this is not a real 8086 instruction {2}
  8517.  
  8518.              When you have a RET (return) instruction in a far procedure, the
  8519.              8086 does the following:
  8520.  
  8521.                  pop  IP        ; this is not a real 8086 instruction
  8522.                  pop  CS        ; put back the old CS and IP
  8523.  
  8524.              so RET resets CS and IP to go back where it came from. That is
  8525.              its job.
  8526.  
  8527.              What has been pushed on the stack before starting your program?
  8528.              NOTHING. That's right. That means that if you execute
  8529.  
  8530.                  ret
  8531.  
  8532.              at the end of your program, the 8086 will pop two pieces of
  8533.              garbage into IP and CS.
  8534.  
  8535.              Fortunately, when setting up a program, the loader ALWAYS puts
  8536.              the segment address of the PSP in DS., the data segment. All we
  8537.              need to do is PUSH DS (the PSP) and then PUSH 0 (offset 0000) and
  8538.              we have the address of our orderly exit code. If we then execute
  8539.              RET, it will POP these two items into IP and CS, sending us to
  8540.              our orderly exit code. That is what is at the beginning of the
  8541.              code section of the template file. We cannot PUSH a constant, so
  8542.              we manufacture a 0 with 'sub ax, ax'. The code is:
  8543.  
  8544.                  push ds        ; PSP segment
  8545.                  sub  ax, ax    ; manufacture a 0
  8546.              ____________________
  8547.  
  8548.                 2 This is not actual 8086 code. You have no direct access to
  8549.              IP. This is, however, what the 8086 effectively does.
  8550.  
  8551.  
  8552.              Chapter 10 - Templates                                         99
  8553.              ______________________
  8554.  
  8555.                  push ax        ; offset = 0000
  8556.  
  8557.              and the program is set up for the return.
  8558.  
  8559.              That's a lot of things together, so let's review. To exit a
  8560.              procedure we use RET, but for the starting procedure we need to
  8561.              return to the operating system. The PSP has the code for an
  8562.              orderly return at offset 0000. At load time, the loader puts the
  8563.              segment address of the PSP in DS. We push the PSP segment address
  8564.              and offset 0000 for later use by the RET instruction. We do this
  8565.              with:
  8566.  
  8567.                  push ds        ; PSP segment
  8568.                  sub  ax, ax    ; manufacture a 0
  8569.                  push ax        ; offset = 0000
  8570.  
  8571.              These should be the first instructions in the program.
  8572.  
  8573.              Now that you have stored the PSP, DS is free for other use. You
  8574.              can now use DS to hold the segment address of your data. DS is
  8575.              used because that is the segment register that the 8086 expects
  8576.              unless told otherwise. You can't move a constant to a segment
  8577.              register, so this is a two step process:
  8578.  
  8579.                  mov  ax, DATASTUFF
  8580.                  mov  ds, ax
  8581.  
  8582.  
  8583.              EXTRN
  8584.  
  8585.              Finally, an EXTRN statement tells the assembler that the
  8586.              procedure or data is in another file and you did not forget it.
  8587.              For a procedure, you need to say whether it is NEAR (push old IP
  8588.              and put in new IP) or far (push old CS and IP; put in new CS and
  8589.              IP). Here is the assembler listing for five calls:
  8590.  
  8591.                     E8 09CA R            call  near_routine
  8592.                     9A 15EE ---- R       call  far_routine
  8593.                     E8 0000 E            call  near_external_routine
  8594.                     9A 0000 ---- E       call  far_external_routine
  8595.                     E8 0000 E            call  get_unsigned
  8596.  
  8597.              The first two are in the same file, the next two are in an
  8598.              external file, and we have our friend 'get_unsigned'. 'R' means
  8599.              that the data may be changed, 'E' means that it is external, and
  8600.              will be done by the linker. The first four are labelled whether
  8601.              they are near or far. 'get_unsigned' is a near procedure. Notice
  8602.              that E8 is the near call while 9A is the far call. Also notice
  8603.              that the assembler reserves one word for the new IP in the near
  8604.              calls. If the call is in the same file, the assembler fills in
  8605.              this number, but if it is external the assembler sets it to 0. In
  8606.              the far calls the assembler reserves two words instead of one.
  8607.              The first word is again the new IP, which is either filled in or
  8608.              set to zero. The second word is for the segment address, and will
  8609.              be set by the linker.
  8610.  
  8611.  
  8612.  
  8613.  
  8614.              The PC Assembler Tutor                                        100
  8615.              ______________________
  8616.  
  8617.              Whew!!! It sure took a long time to go through all that and you
  8618.              still probably are unsure about some of this. Read the summary,
  8619.              and if you don't feel good about it, leave it for a day or two
  8620.              and reread it then.
  8621.  
  8622.              At the end of the book I will show you how you can simplify a lot
  8623.              of these things by using standardized segment names and some
  8624.              other standardized instructions. For now, you need to get used to
  8625.              what the structure of programs is, and we will continue using the
  8626.              same type of templates.{3}
  8627.  
  8628.  
  8629.  
  8630.  
  8631.  
  8632.  
  8633.  
  8634.  
  8635.  
  8636.  
  8637.  
  8638.  
  8639.  
  8640.  
  8641.  
  8642.  
  8643.  
  8644.  
  8645.  
  8646.  
  8647.  
  8648.  
  8649.  
  8650.  
  8651.  
  8652.  
  8653.  
  8654.  
  8655.  
  8656.  
  8657.  
  8658.  
  8659.  
  8660.  
  8661.  
  8662.  
  8663.  
  8664.  
  8665.  
  8666.  
  8667.  
  8668.              ____________________
  8669.  
  8670.                 3 Just think of me as the computer equivalent of a woodshop
  8671.              teacher who forces you to use hand tools to make a coffee table
  8672.              rather than allowing you to use what you really want to be
  8673.              using - a chainsaw.
  8674.  
  8675.  
  8676.              Chapter 10 - Templates                                        101
  8677.              ______________________
  8678.  
  8679.  
  8680.                                         SUMMARY
  8681.  
  8682.              SEGMENTS
  8683.  
  8684.              Segments are defined by giving a name followed by the word
  8685.              SEGMENT. The end of a segment is signalled by the segment name,
  8686.              followed by the word ENDS (end of segment).
  8687.  
  8688.                  ; - - - - -
  8689.                  SOME_NAME  SEGMENT
  8690.  
  8691.                  SOME_NAME   ENDS
  8692.                  ; - - - - -
  8693.  
  8694.              (As always, anything after a comma is a comment and is ignored by
  8695.              the assembler). In addition, if you want to combine a segment
  8696.              with other segments, then all the segments to be combined must:
  8697.  
  8698.                  1) have the same name.
  8699.                  2) have the same type (class)
  8700.                  3) be declared PUBLIC
  8701.  
  8702.  
  8703.              THE STACK SEGMENT
  8704.  
  8705.              The stack segment may have any name you want, but should be
  8706.              declared " SEGMENT  STACK 'STACK' ". This forces the loader to do
  8707.              certain initialization for you. If you don't declare it this way,
  8708.              you have to do the initialization yourself.
  8709.  
  8710.                  ANY_NAME  SEGMENT  STACK  'STACK'
  8711.  
  8712.  
  8713.              EXTRN
  8714.  
  8715.              For procedures, an EXTRN statement tells the assembler that the
  8716.              procedure that you want to call is in a different file, that you
  8717.              didn't forget it. Procedures which are EXTRN must be declared
  8718.              either NEAR or FAR. The grammar is name colon NEAR or name colon
  8719.              FAR.
  8720.  
  8721.                  EXTRN     procedure1:NEAR, procedure2:FAR
  8722.  
  8723.              You may declare as many things on one line as will fit, but you
  8724.              need to separate them with commas. There can be no comma at the
  8725.              end.
  8726.  
  8727.  
  8728.              ASSUME
  8729.  
  8730.              An ASSUME statement tells the assembler that when a statement
  8731.              references that particular segment, the corresponding segment
  8732.              register will be set to that segment address.
  8733.  
  8734.                  ASSUME    es:MORESTUFF
  8735.  
  8736.  
  8737.  
  8738.              The PC Assembler Tutor                                        102
  8739.              ______________________
  8740.  
  8741.              tells the assembler that no matter what you do in other parts of
  8742.              the program, every time a variable in MORESTUFF is referenced, es
  8743.              will have the segment address of MORESTUFF. This is for the
  8744.              purpose of correct coding of segment overrides.
  8745.  
  8746.  
  8747.              SEGMENT OVERRIDES
  8748.  
  8749.              Normally, when the 8086 accesses a variable in memory, it does so
  8750.              via the DS segment register. This can be changed with a segment
  8751.              override. The assembler puts the correct segment override code in
  8752.              front of the instruction and the 8086 will use that segment
  8753.              register to access the data in memory. The override codes are:
  8754.  
  8755.                          SEGMENT       HEX VALUE
  8756.                            CS             2E
  8757.                            DS             3E
  8758.                            ES             26
  8759.                            SS             36
  8760.  
  8761.  
  8762.              CS
  8763.  
  8764.              CS is the code segment. When the 8086 processes machine code, it
  8765.              ALWAYS uses CS. There is no override.
  8766.  
  8767.  
  8768.              IP
  8769.  
  8770.              IP, the instruction pointer, gives the offset in CS of the next
  8771.              instruction to be processed. When the 8086 processes an
  8772.              instruction, it looks at IP, gets the next instruction and
  8773.              updates IP. This is totally automatic and internal to the 8086.
  8774.              You have no direct access to IP.
  8775.  
  8776.  
  8777.              PROCEDURES
  8778.  
  8779.              A procedure is declared by giving a name followed by the word
  8780.              'proc' followed by either NEAR or FAR. A procedure is ended by
  8781.              giving the name, followed by 'endp' (end of procedure).
  8782.  
  8783.                  ; - - - - -
  8784.                  square_root    proc far
  8785.  
  8786.                  square_root    endp
  8787.                  ; - - - - -
  8788.  
  8789.              The words NEAR and FAR are for the assembler and the linker so
  8790.              they know whether to change just IP or both IP and CS in RET, the
  8791.              return statement as well as in CALL, the subroutine call.
  8792.  
  8793.  
  8794.              RET
  8795.  
  8796.              The assembler codes a near or a far return depending on whether
  8797.              you have declared a near or a far procedure. A NEAR return POPs
  8798.  
  8799.  
  8800.              Chapter 10 - Templates                                        103
  8801.              ______________________
  8802.  
  8803.              IP off of the stack while a FAR return POPs IP then POPs CS.
  8804.              Thus, a NEAR return stays in the same segment but a FAR return
  8805.              gets a new segment address in CS.{4}
  8806.  
  8807.  
  8808.              END
  8809.  
  8810.              The word END signals to the assembler that you are done with
  8811.              code. The assembler will ignore all following lines, whether they
  8812.              are blank or contain code.
  8813.  
  8814.              If the line with END  has a name after the word END, then the
  8815.              assembler assumes that this is the name of a label where
  8816.              execution will begin at run time. That means that the instruction
  8817.              at 'label:' will be the first instruction executed in the
  8818.              program.
  8819.  
  8820.  
  8821.              SETUP
  8822.  
  8823.              In order to setup the program in the beginning you need to (1)
  8824.              PUSH the segment address of the PSP (which is in DS), then push 0
  8825.              (the offset of the orderly return code). Following this you need
  8826.              to put the segment address of the data segment in DS. The code
  8827.              for all of this is:
  8828.  
  8829.                  push ds             ; PSP seg address is in ds
  8830.                  sub, ax, ax         ; 0
  8831.                  push ax             ; push 0000 offset
  8832.  
  8833.                  mov  ax, DATA_SEG   ; data segment address to ds
  8834.                  mov  ds, ax
  8835.  
  8836.              ____________________
  8837.  
  8838.                 4 Of course, it is possible for CS to keep the same value if
  8839.              the calling procedure is is the same segment.
  8840.  
  8841. Chapter 11 - Addressing Modes and Pointers
  8842. ==========================================
  8843.                                                                      104
  8844.  
  8845.  
  8846.              In this chapter we are going to cover all possible ways of
  8847.              getting data to and from memory with the different addressing
  8848.              modes. Read this carefully, since it is likely this is the only
  8849.              time you will ever see ALL addressing possibilities covered.
  8850.  
  8851.              The easiest way to move data is if the data has a name and the
  8852.              data is one or two bytes long. Take the following data:
  8853.  
  8854.              ; -----
  8855.              variable1 dw  2000
  8856.              variable2 db  -26
  8857.              variable3 dw  -589
  8858.              ; -----
  8859.  
  8860.              We can write:
  8861.  
  8862.                  mov  variable1, ax
  8863.                  mov  cl, variable2
  8864.                  mov  si, variable3
  8865.  
  8866.              and the assembler will write the appropriate machine code for
  8867.              moving the data. What can we do if the data is more than two
  8868.              bytes long? Here is some more data:
  8869.  
  8870.              ; -----
  8871.              variable4 db  "This is a string of ascii data."
  8872.              variable5 dd  -291578
  8873.              variable6 dw  600 dup (-11000)
  8874.              ; -----
  8875.  
  8876.              Variable4 is the address of the first byte of a string of ascii
  8877.              data. Variable5 is a single piece of data, but it won't fit into
  8878.              an 8086 register since it is 4 bytes long. Variable6 is a 600
  8879.              element long array, with each element having the value -11000. In
  8880.              order to deal with these, we need pointers.
  8881.  
  8882.              Some of you will be flummoxed at this point, while those who are
  8883.              used to the C language will feel right at home. A pointer is
  8884.              simply the address of a variable. We use one of the 8086
  8885.              registers to hold the address of a variable, and then tell the
  8886.              8086 that the register contains the address of the variable, not
  8887.              the variable itself. It "points" to a place in memory to send the
  8888.              data to or retrieve the data from. If this seems a little
  8889.              confusing, don't worry; you'll get the hang of it quickly.
  8890.  
  8891.              As I have said before, the 8086 does not have general purpose
  8892.              registers. Many instructions (such as LOOP, MUL, IDIV, ROL) work
  8893.              only with specific registers. The same is true of pointers. You
  8894.              may use only  BX, SI, DI, and BP as pointers. The assembler will
  8895.              give you an error if you try using a different register as a
  8896.              pointer.
  8897.  
  8898.              ______________________
  8899.  
  8900.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  8901.  
  8902.  
  8903.              Chapter 11 - Addressing Modes                                 105
  8904.              _____________________________
  8905.  
  8906.  
  8907.  
  8908.              There are two ways to put an address in a pointer. For variable4,
  8909.              we could write either:
  8910.  
  8911.                  lea  si, variable4
  8912.  
  8913.              or:
  8914.  
  8915.                  mov  si, offset variable4
  8916.  
  8917.              Both instructions will put the offset address of variable4 in
  8918.              SI.{1} SI now 'points' to the first byte (the letter 'T') of
  8919.              variable4. If we wanted to move the third byte of that array
  8920.              (the letter 'i') to CL, how would we do it? First, we need to
  8921.              have SI point to the third byte, not the first. That's easy:
  8922.  
  8923.                  add  si, 2
  8924.  
  8925.              But if we now write:
  8926.  
  8927.                  mov  cl, si
  8928.  
  8929.              we will generate an assembler error because the assembler will
  8930.              think that we want to move the data in SI (a two byte number) to
  8931.              CL (one byte). How do we tell the assembler that we are using SI
  8932.              as a pointer? By enclosing SI in square brackets:
  8933.  
  8934.                  mov  cl, [si]
  8935.  
  8936.              since CL is one byte, the assembler assumes you want to move one
  8937.              byte. If you write:
  8938.  
  8939.                  mov  cx, [si]
  8940.  
  8941.              then the assembler assumes that you want to move a word (two
  8942.              bytes). The whole thing now is:
  8943.  
  8944.                  lea  si, variable4
  8945.                  add  si, 2
  8946.                  mov  cl, [si]
  8947.  
  8948.              This puts the third byte of the string in CL. Remember, if a
  8949.              register is in square brackets, then it is holding the ADDRESS of
  8950.              a variable, and the 8086 will use the register to calculate where
  8951.              the data is in memory.
  8952.  
  8953.              What if we want to put 0s in all the elements of variable6?
  8954.              ____________________
  8955.  
  8956.                 1 LEA stands for load effective address. Note that with LEA,
  8957.              we use only the name of the variable, while with:
  8958.  
  8959.                  mov  si, offset variable4
  8960.  
  8961.              we need to use the word 'offset'. The exact difference between
  8962.              the two will be explained later.
  8963.  
  8964.  
  8965.              The PC Assembler Tutor                                        106
  8966.              ______________________
  8967.  
  8968.              Here's the code:
  8969.  
  8970.                       mov  bx, offset variable6
  8971.                       mov  ax, 0
  8972.                       mov  cx, 600
  8973.                  zero_loop:
  8974.                       mov  [bx], ax
  8975.                       add  bx, 2
  8976.                       loop zero_loop
  8977.  
  8978.              We add 2 to BX each time since each element of variable6 is a
  8979.              word (two bytes) long. There is another way of writing this:
  8980.  
  8981.                       mov  bx, offset variable6
  8982.                       mov  cx, 600
  8983.                  zero_loop:
  8984.                       mov  [bx], 0
  8985.                       add  bx, 2
  8986.                       loop zero_loop
  8987.  
  8988.              Unfortunately, this will generate an assembler error. Why? If the
  8989.              assembler sees:
  8990.  
  8991.                       mov  [bx], ax
  8992.  
  8993.              it knows that you want to move what is in AX to the address in
  8994.              BX, and AX is one word (two bytes) long so it generates the
  8995.              machine code for a word move. If the assembler sees:
  8996.  
  8997.                       mov  [bx], al
  8998.  
  8999.              it knows that you want to move what is in AL to the address in
  9000.              BX, and AL is one byte long, so it generates the machine code for
  9001.              a byte move. If the assembler sees:
  9002.  
  9003.                       mov  [bx], 0
  9004.  
  9005.              it doesn't know whether you want a byte move or a word move. The
  9006.              8086 assembler has implicit sizing. It is the assembler's job to
  9007.              look at each instruction and decide whether you want to operate
  9008.              on a byte or a word. Other microprocessors do things differently.
  9009.              On the Motorola 68000, the assembler uses explicit sizing. Each
  9010.              instruction must explicitly state whether it is a byte or a
  9011.              word.{2} On the 68000 you have:
  9012.  
  9013.                       move.b    #213, (A1)
  9014.                       move.w    #213, (A1)
  9015.  
  9016.              The first instruction says to move a byte (the number 213) to the
  9017.              address in register A1 while the second instruction says to move
  9018.  
  9019.  
  9020.  
  9021.              ____________________
  9022.  
  9023.                 2 Any of you who use the 68000 assembler know that this is
  9024.              fudging the facts a little bit.
  9025.  
  9026.  
  9027.              Chapter 11 - Addressing Modes                                 107
  9028.              _____________________________
  9029.  
  9030.              a word (the number 213) to the address in register A1.{3}
  9031.  
  9032.              Back to the 8086. If the 8086 assembler looks at an instruction
  9033.              and it can't tell whether you want to move a byte or a word, it
  9034.              generates an error. When you use pointers with constants, you
  9035.              should explicitly state whether you want a byte or a word. The
  9036.              proper way to do this is to use the reserved words BYTE PTR or
  9037.              WORD PTR.
  9038.  
  9039.                       mov  [bx], BYTE PTR 213
  9040.                       mov  [bx], WORD PTR 213
  9041.  
  9042.              These stand for byte pointer and word pointer respectively. I
  9043.              find this terminology exceptionally clumsy, but that's life.
  9044.              Whenever you are moving a constant with a pointer, you should
  9045.              specify either BYTE PTR or WORD PTR.
  9046.  
  9047.              The Microsoft assembler makes some assumptions about the size of
  9048.              a constant. If the number is 256 or below (either positive or
  9049.              negative), you MUST explicitly state whether it is a byte or a
  9050.              word operation. If the number is 257 or above (either positive or
  9051.              negative), the assembler assumes that you want a word operation.
  9052.  
  9053.              Here's the previous code rewritten correctly:
  9054.  
  9055.  
  9056.                       mov  bx, offset variable6
  9057.                       mov  cx, 600
  9058.                  zero_loop:
  9059.                       mov  [bx], WORD PTR 0
  9060.                       add  bx, 2
  9061.                       loop zero_loop
  9062.  
  9063.              Let's add 435 to every element in the variable6 array:
  9064.  
  9065.                       mov  bx, offset variable6
  9066.                       mov  cx, 600
  9067.                  add_loop:
  9068.                       add  [bx], WORD PTR 435
  9069.                       add  bx, 2
  9070.                       loop add_loop
  9071.  
  9072.              How about multiplying every element in the array by 12?
  9073.  
  9074.                       mov  di, offset variable6
  9075.                       mov  cx, 600
  9076.                       mov  si, 12
  9077.                  mult_loop:
  9078.                       mov  ax, [di]
  9079.                       imul si
  9080.                       mov  [di], ax
  9081.                       add  di, 2
  9082.                       loop mult_loop
  9083.  
  9084.              ____________________
  9085.  
  9086.                 3 A1 is a 68000 register.
  9087.  
  9088.  
  9089.              The PC Assembler Tutor                                        108
  9090.              ______________________
  9091.  
  9092.              None of these examples did any error checking, so if the result
  9093.              was too large, the overflow was ignored. This time we used DI for
  9094.              a change of pace. Remember, we may use BX, SI, DI or BP, but no
  9095.              others. You will notice that in all these examples, we started at
  9096.              the beginning of the array and went step by step through the
  9097.              array. That's fine, and that's what we normally would do, but
  9098.              what if we wanted to look at individual elements? Here's a sample
  9099.              program:
  9100.  
  9101.              ; + + + + + START DATA BELOW THIS LINE
  9102.              ;
  9103.              poem_array  db "She walks in Beauty, like the night"
  9104.                          db "Of cloudless climes and starry skies;"
  9105.                          db "And all that's best of dark and bright"
  9106.                          db "Meet in the aspect ratio of 1 to 3.14159"
  9107.              character_count  db  149
  9108.              ; + + + + + END DATA ABOVE THIS LINE
  9109.  
  9110.              ; + + + + + START CODE BELOW THIS LINE
  9111.  
  9112.                  mov  bx, offset poem_array
  9113.                  mov  dl, character_count
  9114.  
  9115.              character_loop:
  9116.                  sub  ax, ax              ; clear ax
  9117.                  call get_unsigned_byte
  9118.                  dec  al                  ; character #1 = array[0]
  9119.                  cmp  al, dl              ; out of range?
  9120.                  ja   character_loop      ; then try again
  9121.                  mov  si, ax              ; move char # to pointer register
  9122.                  mov  al, [bx+si]         ; character to al
  9123.                  call print_ascii_byte
  9124.                  jmp  character_loop
  9125.  
  9126.              ; + + + + + END CODE ABOVE THIS LINE
  9127.  
  9128.              You enter a number and the program prints the corresponding
  9129.              character. Before starting, we put the array address in BX and
  9130.              the maximum character count in DL. After getting the number from
  9131.              get_unsigned_byte, we decrement AL since the first character is
  9132.              actually poem_array[0]. The character count has been reduced by 1
  9133.              to reflect this fact. It also makes 0 an illegal entry. Notice
  9134.              that the program checks to make sure you don't go past the end of
  9135.              the poem. This time we use BX to mark the beginning of the array
  9136.              and SI to count the number of the character.
  9137.  
  9138.              Once again, there are only specific combinations of pointers that
  9139.              can be used. They are:
  9140.  
  9141.                  BX with either SI or DI (but not both)
  9142.                  BP with either SI or DI (but not both)
  9143.  
  9144.              My version of the Microsoft assembler (v5.1) recognizes the forms
  9145.              [bx+si], [si+bx], [bx][si], [si][bx], [si]+[bx] and [bx]+[si] as
  9146.              the same thing and produces the same machine code for all six.
  9147.  
  9148.  
  9149.  
  9150.  
  9151.              Chapter 11 - Addressing Modes                                 109
  9152.              _____________________________
  9153.  
  9154.              We can get even more complicated, but to show that, we need
  9155.              structures. In databases they are called records. In C they are
  9156.              called structures; in any case they are the same thing - a group
  9157.              of different types of data in some standard order. After the
  9158.              group is defined, we usually make an array with the identical
  9159.              structure for each element of the array.{4} Let's make a
  9160.              structure for an address book.
  9161.  
  9162.                  last_name  db  15 dup (?)
  9163.                  first_name db  15 dup (?)
  9164.                  age        db  ?
  9165.                  tel_no     db  10 dup (?)
  9166.  
  9167.              In this case, all the data is bytes, but that is not necessary.
  9168.              It can be anything. Each separate piece of data is called a
  9169.              FIELD. We have the last_name field, the first_name field, the age
  9170.              field, and the tel_no field. Four fields in all. The structure is
  9171.              41 bytes long. What if we want to have a list of 100 names in our
  9172.              telephone book? We can allocate memory space with the following
  9173.              definition:
  9174.  
  9175.                  address_book   db  100 dup ( 41 dup (' ')) {5}
  9176.  
  9177.              Well, that allocates room in memory, but how do we get to
  9178.              anything? First, we need the array itself:
  9179.  
  9180.                  mov  bx, offset address_book
  9181.  
  9182.              Then we need one specific entry. Let's take entry 29 (which is
  9183.              address_book[28]). Each entry is 41 bytes long, so:
  9184.  
  9185.                  mov  ax, 28    ; entry (less 1)
  9186.                  mov  cx, 41    ; entry length
  9187.                  mul  cx
  9188.                  mov  di, ax    ; move to pointer
  9189.  
  9190.              That gives us the entry, but if we want to get the age, that's
  9191.              not the first byte of the structure, it's the 31st byte (actually
  9192.              address_book[28] + 30 since the first byte is at +0). We get it
  9193.              by writing:
  9194.  
  9195.                  mov  dl, [bx+di+30]
  9196.  
  9197.              This is the most complex thing we have - two pointers plus a
  9198.              constant. The total code is then:
  9199.  
  9200.                  mov  bx, offset address_book
  9201.                  mov  ax, 28    ; entry (less 1)
  9202.                  mov  cx, 41    ; entry length
  9203.              ____________________
  9204.  
  9205.                 4 If you don't know about structures or records, now would be
  9206.              a good time to stop and go to a reference book about them. They
  9207.              are not actually covered here.
  9208.  
  9209.                 5 Nesting of dup statements is allowed. Rather than having
  9210.              uninitialized data, this has blanks in all the spaces.
  9211.  
  9212.  
  9213.              The PC Assembler Tutor                                        110
  9214.              ______________________
  9215.  
  9216.                  mul  cx        ; entry offset from array[0]
  9217.                  mov  di, ax    ; move entry offset to pointer
  9218.                  mov  dl, [bx+di+30]  ; total address
  9219.  
  9220.              Though the machine code has only one constant in the code, the
  9221.              assembler will allow you to put a number of constants in the
  9222.              assembler instruction. It will add them together for you and
  9223.              resolve them into one number.{6}
  9224.  
  9225.              Once again, there are a limited number of registers - they are
  9226.              the same registers as before:
  9227.  
  9228.                  BX with either SI or DI (but not both) plus constant
  9229.                  BP with either SI or DI (but not both) plus constant
  9230.  
  9231.              We can work with structures on the machine level, but it looks
  9232.              like it's going to be hard to keep track of where each field is.
  9233.              Actually, it isn't so bad because of:
  9234.  
  9235.                                OUR FRIEND, THE EQU STATEMENT
  9236.  
  9237.              The assembler allows you to do substitution. If you write:
  9238.  
  9239.                  somestuff EQU  37 * 44
  9240.  
  9241.              then every place that the assembler finds the word "somestuff",
  9242.              it will substitute what is on the right side of the EQU. Is that
  9243.              a number or text? Sometimes it's a number, sometimes it's text.
  9244.              Here are four statements which are defined totally in terms of
  9245.              numbers. This is from the assembler listing. (The assembler lists
  9246.              how it has evaluated the EQU statement on the left after the
  9247.              equal sign.)
  9248.  
  9249.  
  9250.  
  9251.  
  9252.               = 0023               statement1 EQU  5 * 7
  9253.               = 0025               statement2 EQU  statement1 + 6 - 4
  9254.               = 000F               statement3 EQU  statement2 - 22
  9255.               = 001F               statement4 EQU  statement3 + 16
  9256.  
  9257.              and the assembler thinks of these as numbers (these numbers are
  9258.              in hex). Now in the next set, with only a minor change:
  9259.  
  9260.  
  9261.               = [bp + 3]                    statement1 EQU  [bp + 3]
  9262.               = [bp + 3] + 6 - 4            statement2 EQU  statement1 + 6 - 4
  9263.               = [bp + 3] + 6 - 4 - 22       statement3 EQU  statement2 - 22
  9264.              ____________________
  9265.  
  9266.                 6 And it does it quite well. The assembler correctly evaluated
  9267.              the following:
  9268.  
  9269.                     add   ax, (-3*81)+44/8+[si+27]+6+[bx]-7+(43*96)-2
  9270.  
  9271.              Not bad, huh?
  9272.  
  9273.  
  9274.  
  9275.              Chapter 11 - Addressing Modes                                 111
  9276.              _____________________________
  9277.  
  9278.               = [bp + 3] + 6 - 4 - 22 + 16  statement4 EQU  statement3 + 16
  9279.  
  9280.              the assembler thinks of it as text. Obviously, the fact that it
  9281.              can be either may cause you some problems along the way. Consult
  9282.              the assembler manual for ways to avoid the problem.
  9283.  
  9284.  
  9285.              Now we have a tool to deal with structures. Let's look at that
  9286.              structure again.
  9287.  
  9288.                  last_name  db  15 dup (?)
  9289.                  first_name db  15 dup (?)
  9290.                  age        db  ?
  9291.                  tel_no     db  10 dup (?)
  9292.  
  9293.              We don't actually need a data definition to make the structure,
  9294.              we need equates:
  9295.  
  9296.                  LAST_NAME      EQU  0
  9297.                  FIRST_NAME     EQU  15
  9298.                  AGE            EQU  30
  9299.                  TEL_NO         EQU  31
  9300.  
  9301.              this gives us the offset from the beginning of each record. If we
  9302.              again define:
  9303.  
  9304.                  address_book   db  100 dup ( 41 dup (' '))
  9305.  
  9306.               then to get the age field of entry 87, we write:
  9307.  
  9308.                  mov  bx, offset address_book
  9309.                  mov  ax, 86    ; entry (less 1)
  9310.                  mov  cx, 41    ; entry length
  9311.                  mul  cx        ; entry offset from array[0]
  9312.                  mov  di, ax    ; move entry offset to pointer
  9313.                  mov  dl, [bx+di+AGE]  ; total address
  9314.  
  9315.              This is a lot of work for the 8086, but that is normal with
  9316.              complex structures. The only thing that takes a lot of time is
  9317.              the multiplication, but if you need it, you need it.{7}
  9318.  
  9319.              How about a two dimensional array of integers, 60 X 40
  9320.  
  9321.                  int_array  dw  40 dup  ( 60 dup ( 0 ))
  9322.  
  9323.              These are initialized to 0. For our purposes, we'll assume that
  9324.              the first number is the row number and the second number is the
  9325.              column number; i.e. array [6,13] is row 6, column 13. We will
  9326.              have 40 rows of 60 columns. For ease of calculation, the first
  9327.              array element is int_array [0,0]. (If it is your array, you can
  9328.  
  9329.  
  9330.  
  9331.  
  9332.              ____________________
  9333.  
  9334.                 7 You will see more of the EQU statement.
  9335.  
  9336.  
  9337.              The PC Assembler Tutor                                        112
  9338.              ______________________
  9339.  
  9340.              set it up any way you want {8}). Each row is 60 words (120 bytes)
  9341.              long. To get to int_array [23, 45] we have:
  9342.  
  9343.                  mov  ax, 120   ; length of one row in bytes
  9344.                  mov  cx, 23    ; row number
  9345.                  mul  cx
  9346.                  mov  bx, ax    ; row offset to bx
  9347.                  mov  si, 45    ; column offset
  9348.                  sal  si, 1     ; multiply column offset by 2 (for word size)
  9349.                  mov  dx, [bx+si]    ; integer to dx
  9350.  
  9351.              Using SAL instead of MUL is about 50 times faster. Since most
  9352.              arrays you will be working with are either byte, word, or double
  9353.              word (4 bytes) arrays, you can save a lot of time. Let
  9354.              ELEMENT_NUMBER be the array number (starting at 0) of the desired
  9355.              element in a one-dimensional array. For byte arrays, no
  9356.              multiplication is needed. For a word:
  9357.  
  9358.                  mov  di, ELEMENT_NUMBER
  9359.                  sal  di,1      ; multiply by 2
  9360.  
  9361.              and for a double word (4 bytes):
  9362.  
  9363.                  mov  di, ELEMENT_NUMBER
  9364.                  sal  di, 1
  9365.                  sal  di, 1     ; multiply by 4
  9366.  
  9367.              This means that a one-dimensional array can be accessed very
  9368.              quickly as long as the element length is a power of 2 - either 2,
  9369.              4 or 8. Since the standard 8086 data types are all 1, 2, 4, or 8
  9370.              bytes long, one dimensional arrays are fast. Others are not so
  9371.              fast.
  9372.  
  9373.              As a quick review before going on, these are the legal ways to
  9374.              address a variable on the 8086:
  9375.  
  9376.                  (1) by name.
  9377.  
  9378.                            mov  dx, variable1
  9379.  
  9380.                  It is also possible to have name + constant.
  9381.  
  9382.                            mov  dx, variable1 + 27
  9383.  
  9384.                  The assembler will resolve this into a single offset number
  9385.                  and will give the appropriate information to the linker.
  9386.  
  9387.                  (2) with the single pointers BX, SI, DI and BP (which are
  9388.                  enclosed in square brackets).
  9389.  
  9390.                            mov  cx, [si]
  9391.              ____________________
  9392.  
  9393.                 8 Bearing in mind that all compiled languages have fixed
  9394.              formats for arrays. If you want your array to interact with C,
  9395.              Fortran, Pascal or Basic, you'd better be sure you have the right
  9396.              format.
  9397.  
  9398.  
  9399.              Chapter 11 - Addressing Modes                                 113
  9400.              _____________________________
  9401.  
  9402.                            xor  al, [bx]
  9403.                            add  [di], cx
  9404.                            sub  [bp], dh
  9405.  
  9406.                  (3) with the single pointers BX, SI, DI and BP (which are
  9407.                  enclosed in square brackets) plus a constant.
  9408.  
  9409.                            mov  cx, [si+421]
  9410.                            xor  al, 18+[bx]
  9411.                            add  93+[di]-7, cx
  9412.                            sub  (54/7)+81-3+[bp]-19, dh
  9413.  
  9414.                  (4) with the double pointers [bx+si], [bx+di], [bp+si],
  9415.                  [bp+di]  (which are enclosed in square brackets).
  9416.  
  9417.                            mov  cx, [bx][si]
  9418.                            xor  al, [di][bx]
  9419.                            add  [bp]+[di], cx
  9420.                            sub  [di+bp], dh
  9421.  
  9422.                  (5) with the double pointers [bx+si], [bx+di], [bp+si],
  9423.                  [bp+di]  (which are enclosed in square brackets) plus a
  9424.                  constant.
  9425.  
  9426.                            mov  cx, [bx][si+57]
  9427.                            xor  al, 45+[di+23][bx+15]-94
  9428.                            add  [bp]+[di]-444, cx
  9429.                            sub  [6+di+bp]-5, dh
  9430.  
  9431.              These are ALL the addressing modes allowed on the 8086. As for
  9432.              the constants, it is the ASSEMBLER'S job to resolve all numbers
  9433.              in the expression into a single constant. If your expression
  9434.              won't resolve into a constant, it is between you and the
  9435.              assembler. It has nothing to do with the 8086 chip.
  9436.  
  9437.  
  9438.  
  9439.  
  9440.              The PC Assembler Tutor                                        114
  9441.              ______________________
  9442.  
  9443.              We can consolidate all this information into the following list:
  9444.  
  9445.                  All the following addressing modes can be used with or
  9446.                  without a constant:
  9447.  
  9448.                  variable_name  (+constant)
  9449.                  [bx]     (+constant)
  9450.                  [si]     (+constant)
  9451.                  [di]     (+constant)
  9452.                  [bp]     (+constant)
  9453.                  [bx+si]  (+constant)
  9454.                  [bx+di]  (+constant)
  9455.                  [bp+si]  (+constant)
  9456.                  [bp+di]  (+constant)
  9457.  
  9458.                  This is a complete list.
  9459.  
  9460.              Thus, you can access a variable by name or with one of the eight
  9461.              pointer combinations. There are no other possibilities.
  9462.  
  9463.  
  9464.              One thing that may confuse you about an addressing statement is
  9465.              all the plusses and minuses. As an example:
  9466.  
  9467.                  mov  cx, -45+27[bx+22]+[-195+di]+23-44
  9468.  
  9469.              the total address is:
  9470.  
  9471.                  -45+27[bx+22]+[-195+di]+23-44
  9472.  
  9473.              When the 8086 performs this instruction, it will ADD (1) BX (2)
  9474.              DI and (3) a single constant. That single constant can be a
  9475.              positive or a negative number; the 8086 will ADD all three
  9476.              elements. The '+' in front of  'di' is for convenience of the
  9477.              assembler only;  [-195-di] is illegal and the assembler will
  9478.              generate an error. If you actually want the negative of what is
  9479.              in one of the registers, you must negate it before calling the
  9480.              addressing instruction:
  9481.  
  9482.                  neg  di
  9483.                  mov  cx, -45+27[bx+22]+[-195+di]+23-44
  9484.  
  9485.              once again, the only allowable forms are +[di], [di] or [+di].
  9486.              Either -[di] or [-di] will generate an assembler error.
  9487.  
  9488.  
  9489.              If you ever see a technical description of the addressing modes,
  9490.              you will find a list of 24 different machine codes. The reason
  9491.              for this is that:
  9492.  
  9493.                       [bx]
  9494.                       [bx] + byte constant
  9495.                       [bx] + word constant
  9496.  
  9497.              are three different machine codes. Here is a listing of the same
  9498.              machine instruction with the three different styles:
  9499.  
  9500.  
  9501.  
  9502.              Chapter 11 - Addressing Modes                                 115
  9503.              _____________________________
  9504.  
  9505.  
  9506.                  MACHINE CODE             ASSEMBLER INSTRUCTION
  9507.  
  9508.                   03 04                     add   ax, [si]
  9509.                   03 44 1B                  add   ax, [si+27]
  9510.                   03 44 E5                  add   ax, [si-27]
  9511.                   03 84 5BA7                add   ax, [si+23463]
  9512.                   03 84 A459                add   ax, [si-23463]
  9513.  
  9514.  
  9515.              (27d = 1Bh , 23463d = 5BA7h). The first byte of code (03) is the
  9516.              add (word) instruction. The second byte is the addressing code,
  9517.              and the third and fourth bytes (if any) are the constant (in
  9518.              hex). Addressing code 04 is:  (ax, [si]). Addressing code 44 is:
  9519.              (ax, [si] + byte constant). Addressing code 84 is:  (ax, [si] +
  9520.              word constant). The fact that there are three different machine
  9521.              codes is of concern to the assembler, not to you. It is the
  9522.              assembler's job to make the machine code as efficient as
  9523.              possible. It is your job to write quality, robust code.
  9524.  
  9525.  
  9526.              SEGMENT OVERRIDES
  9527.  
  9528.              So far, we haven't talked about segment registers. You will
  9529.              remember from the last chapter that the 8086 assumes that a named
  9530.              variable is in the DS segment:
  9531.  
  9532.                  mov  ax, variable1
  9533.  
  9534.              If it isn't, the Microsoft assembler puts the correct segment
  9535.              override in the machine code. The segment overrides are:
  9536.  
  9537.                  SEGMENT OVERRIDE         MACHINE CODE (hex)
  9538.                       CS                       2E
  9539.                       DS                       3E
  9540.                       ES                       26
  9541.                       SS                       36
  9542.  
  9543.              As an example:
  9544.  
  9545.                  MACHINE CODE        ASSEMBLER  INSTRUCTIONS
  9546.  
  9547.                  2E: 03 06 0000 R      add   ax, variable3
  9548.                  26: 2B 1E 0000 R      sub   bx, variable2
  9549.                  31 36 0000 R          xor   variable1, si ; no override
  9550.                  36: 21 3E 00C8 R      and   variable4, di
  9551.  
  9552.              when the different variables were in segments with different
  9553.              ASSUME statements. If you don't remember this, you should reread
  9554.              the section on overrides in the last chapter. Remember, the colon
  9555.              is in the listing only to tell you that we have a segment
  9556.              override. The colon is not in the machine code.
  9557.  
  9558.  
  9559.  
  9560.  
  9561.  
  9562.  
  9563.  
  9564.              The PC Assembler Tutor                                        116
  9565.              ______________________
  9566.  
  9567.              What about pointers? The natural segment for anything with [bp]
  9568.              is SS, the stack segment.{1}  Everything else has DS as its
  9569.              natural segment. The natural segments are:
  9570.  
  9571.                  (1) DS
  9572.  
  9573.                       variable + (constant)
  9574.                       [bx] + (constant)
  9575.                       [si] + (constant)
  9576.                       [di] + (constant)
  9577.                       [bx+si] + (constant)
  9578.                       [bx+di] + (constant)
  9579.  
  9580.  
  9581.                  (2) SS
  9582.  
  9583.                       [bp] + (constant)
  9584.                       [bp+si] + (constant)
  9585.                       [bp+di] + (constant)
  9586.  
  9587.              where the constant is always optional. Can you use segment
  9588.              overrides? Yes, in all cases.{2}  Here is some assembler code
  9589.              along with the machine code which was generated.
  9590.  
  9591.  
  9592.                  MACHINE CODE             ASSEMBLER INSTRUCTIONS
  9593.  
  9594.                   26: 03 07                 add   ax, es:[bx]
  9595.                   2E: 01 05                 add   cs:[di], ax
  9596.                   36: 2B 44 11              sub   ax, ss:[si+17]
  9597.                   2E: 29 46 00              sub   cs:[bp], ax
  9598.                   3E: 33 03                 xor   ax, ds:[bp+di]
  9599.                   26: 31 02                 xor   es:[bp+si], ax
  9600.                   26: 89 43 16              mov   es:[bp+di+22], ax
  9601.  
  9602.  
  9603.                   03 04                     add   ax, [si]
  9604.                   03 44 1B                  add   ax, [si+27]
  9605.                   03 84 A459                add   ax, [si-23463]
  9606.                   26: 03 04                 add   ax, es:[si]
  9607.                   26: 03 44 1B              add   ax, es:[si+27]
  9608.                   26: 03 84 A459            add   ax, es:[si-23463]
  9609.  
  9610.  
  9611.              (17d = 11h, 22d = 16h, 27d = 1Bh, -23463d = 0A459h). The first
  9612.              number (which is followed by a colon) is the segment override
  9613.              that the assembler has inserted in the machine code. Remember,
  9614.              the colon is in the listing to inform you that an override is
  9615.              ____________________
  9616.  
  9617.                 1 We will see why when we look at subroutines. BP is called
  9618.              the base pointer [bp] and is used in a special way.
  9619.  
  9620.                 2 There are some special instructions for two independent
  9621.              pointers which we will cover at the end of the book. These allow
  9622.              segment overrides but force the override to refer to the first
  9623.              pointer.
  9624.  
  9625.  
  9626.              Chapter 11 - Addressing Modes                                 117
  9627.              _____________________________
  9628.  
  9629.              involved; it is not in the machine code itself.
  9630.  
  9631.              Unfortunately, when you use pointers you must put the override
  9632.              into the assembler instructions yourself. The assembler has no
  9633.              way of knowing that you want an override. This can cause some
  9634.              truly gigantic errors (if you reference a pointer seven times and
  9635.              forget the override once, the 8086 will access the wrong segment
  9636.              that one time), and those errors are extremely difficult to
  9637.              detect.
  9638.  
  9639.              As you can see from above, you put the override in the
  9640.              instructions by writing the appropriate segment (CS, DS, ES or
  9641.              SS) followed by a colon. As always, it is your responsibility to
  9642.              make sure that the segment register holds the address of the
  9643.              appropriate segment before using an override.
  9644.  
  9645.  
  9646.              We have talked about two different types of constants in the
  9647.              chapter, a constant which is part of the address:
  9648.  
  9649.                  mov  ax, [bx+17]
  9650.                  add  [si+2190], dx
  9651.                  and  [di-8179], cx
  9652.  
  9653.              and a constant which is a number to used for an arithmetical or
  9654.              logical operation:
  9655.  
  9656.                  add  ax, 17
  9657.                  sub  dl, 45
  9658.                  add  dx, 22187
  9659.  
  9660.              They are both part of the machine instruction, and are
  9661.              unchangeable (true constants). This machine code is going to be
  9662.              difficult to read, so just look for (1) the constant DATA and (2)
  9663.              the constant in the ADDRESS. All constants in the assembler
  9664.              instructions are in hex so that they look the same as in the
  9665.              listing of the machine code. Here's a listing of different
  9666.              combinations.
  9667.  
  9668.  
  9669.              1. Pointer + constant as an address:
  9670.  
  9671.                  MACHINE CODE             ASSEMBLER INSTRUCTIONS
  9672.                   01 44 1B                  add   [si+1Bh], ax
  9673.                   29 85 0A04                sub   [di+0A04h], ax
  9674.                   30 5C 1F                  xor   [si+1Fh], bl
  9675.                   20 9E 1FAB                and   [bp+1FABh], bl
  9676.  
  9677.              2. Arithmetic instruction with a constant:
  9678.  
  9679.                  MACHINE CODE             ASSEMBLER INSTRUCTIONS
  9680.                   05 1065                   add   ax, 1065h
  9681.                   2D 6771                   sub   ax, 6771h
  9682.                   80 F3 37                  xor   bl, 37h
  9683.                   80 E3 82                  and   bl, 82h
  9684.  
  9685.              3. Pointer + constant as an address; arithmetic with a constant
  9686.  
  9687.  
  9688.              The PC Assembler Tutor                                        118
  9689.              ______________________
  9690.  
  9691.  
  9692.                  MACHINE CODE             ASSEMBLER INSTRUCTIONS
  9693.                   81 44 1B 1065             add   [si+1Bh], 1065h
  9694.                   81 AD 0A04 6771           sub   [di+0A04h], 6771h
  9695.                   80 74 1F 37               xor   [si+1Fh], BYTE PTR 37h
  9696.                   80 A6 1FAB 82             and   [bp+1FABh], BYTE PTR 82h
  9697.  
  9698.  
  9699.              You will notice that the ADD instruction (as well as the other
  9700.              instructions) changes machine code depending on the complete
  9701.              format of the instruction (byte or word? to a register or from a
  9702.              register? what addressing mode? is AX one of the registers?).
  9703.              That's part of the 8086 machine language encoding, and it makes
  9704.              the 8086 machine code extremely difficult to decipher without a
  9705.              table listing all the options.
  9706.  
  9707.  
  9708.              OFFSET AND SEG
  9709.  
  9710.              There are two special instructions that the assembler has -
  9711.              offset and seg. For any variable or label, offset gives the
  9712.              offset from the beginning of the segment, and seg gives the
  9713.              segment address. If you write:
  9714.  
  9715.                  mov  ax, offset variable1
  9716.  
  9717.              the assembler will calculate the offset of variable1 and put it
  9718.              in the machine code. It also signals the linker and loader; if
  9719.              the linker should change the offset during linking, it will also
  9720.              adjust this number. If you write:
  9721.  
  9722.                  mov  dx, seg variable1
  9723.  
  9724.              The assembler will signal to the linker and the loader that you
  9725.              want the address of the segment that variable1 is in. The linker
  9726.              and loader will put it in the machine code at that spot. You
  9727.              don't need to know the name of the segment. The linker takes care
  9728.              of that. We will use the seg operator later.
  9729.  
  9730.  
  9731.              LEA
  9732.  
  9733.              LEA (load effective address) is a completely different animal. It
  9734.              allows you to use any addressing mode to put an address in a
  9735.              register. One of the addressing modes covered before was for the
  9736.              following code:
  9737.  
  9738.                            xor  dx, 45+[di+23][bx+15]-94
  9739.  
  9740.              The 8086 added DI, BX and the constant to calculate the address.
  9741.              It then XOR'ed the variable at that address with DX. If you
  9742.              write:
  9743.  
  9744.                            lea  dx, 45+[di+23][bx+15]-94
  9745.  
  9746.              the 8086 will add DI, BX and the constant to calculate the
  9747.              address. It will then put the ADDRESS in DX. LEA can use any
  9748.  
  9749.  
  9750.              Chapter 11 - Addressing Modes                                 119
  9751.              _____________________________
  9752.  
  9753.              addressing mode to calculate an address. The machine code looks
  9754.              almost the same:
  9755.  
  9756.                  MACHINE CODE                  ASSEMBLER INSTRUCTIONS
  9757.  
  9758.                   33 51 F5                  xor dx, 45+[di+23][bx+15]-94
  9759.                   8D 51 F5                  lea dx, 45+[di+23][bx+15]-94
  9760.  
  9761.              The first byte of the machine code is the instruction and the
  9762.              second and third byte are the addressing mode.
  9763.  
  9764.              You almost never need LEA. It is slower than:
  9765.  
  9766.                  mov  dx, offset variable1
  9767.  
  9768.              However, when the addressing gets complicated (perhaps 1% of the
  9769.              time), it's nice to have. Remember, it will calculate ANY 8086
  9770.              addressing mode.
  9771.  
  9772.              Let's run a program so we can see what actually happens with LEA
  9773.  
  9774.              ;lea.asm
  9775.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  9776.              variable1 dw ?
  9777.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  9778.  
  9779.              ; + + + + START CODE BELOW THIS LINE
  9780.                     ; reg style
  9781.                     mov   si_byte, 1   ; signed
  9782.                     lea   ax, ax_byte
  9783.                     call  set_reg_style
  9784.  
  9785.                     mov   bp, 0  ; clear unused registers
  9786.                     mov   di, 0
  9787.  
  9788.                    ;lea and mov show the two ways to address variable1
  9789.                     lea   ax, variable1  ; effective address
  9790.                     mov   bx, offset variable1  ; offset
  9791.                     call  show_regs_and_wait
  9792.  
  9793.              lea_loop:
  9794.                     mov   si, 0  ; clear registers
  9795.                     mov   dx, 0
  9796.                     mov   cx, 0
  9797.                     mov   bx, 0
  9798.                     mov   ax, 0
  9799.                     call  show_regs
  9800.  
  9801.                     call  get_unsigned ; unsigned for bx
  9802.                     mov   bx, ax
  9803.                     mov   ax, 0   ; blank ax
  9804.                     call  show_regs
  9805.  
  9806.                     call  get_signed ; signed for si
  9807.                     mov   si, ax
  9808.  
  9809.                     mov   ax, 0   ; blank ax
  9810.  
  9811.  
  9812.              The PC Assembler Tutor                                        120
  9813.              ______________________
  9814.  
  9815.                     lea   cx, [bx+si]+100      ; addresses to cx and dx
  9816.                     lea   dx, [si+bx-100]
  9817.                     call  show_regs_and_wait
  9818.  
  9819.                     jmp   lea_loop
  9820.               ; + + + + END CODE ABOVE THIS LINE
  9821.  
  9822.              The first part of the program shows that LEA and MOV give the
  9823.              same offset address. Then we enter the loop. It gets an unsigned
  9824.              number, puts it in BX, gets a signed number, puts it in SI, then
  9825.              uses LEA to calculate [bx+si+100] and [bx+si-100]. The plus and
  9826.              minus 100 is simply to show you a difference of 200 in the two
  9827.              results. BX and SI could also have contained (1) both signed
  9828.              numbers or (2) both unsigned numbers. It doesn't make any
  9829.              difference. This program has a signed and an unsigned number for
  9830.              variety. Of special interest to you shold be when [bx+si] is
  9831.              within 100 of 65536 (or 0). One of the results will be > 0 while
  9832.              the other result will be < 65536  The address value wraps around
  9833.              from 65535 -> 0. Note that with minor alteration, this program
  9834.              can be used to look at ANY addressing mode that uses pointers.
  9835.  
  9836.              You should make two executable files for this. First:
  9837.  
  9838.                  link  lea+asmhelp
  9839.  
  9840.              and the second:
  9841.  
  9842.                  link asmhelp+lea
  9843.  
  9844.              Give them different names and run them. Note the offset values
  9845.              for:
  9846.  
  9847.                  lea  ax, variable1
  9848.                  mov  bx, offset variable1
  9849.  
  9850.              With lea+asmhelp you should have an offset of 8 for variable1
  9851.              since there are 8 bytes in the array (ax_byte, bx_byte, etc.).
  9852.              This array appears before variable1 in the data segment. When you
  9853.              link it the other way (asmhelp+lea), all the data for asmhelp.obj
  9854.              is in front of your data and the offset should be something
  9855.              completely different for variable1.
  9856.  
  9857.  
  9858.              Chapter 11 - Addressing Modes                                 121
  9859.              _____________________________
  9860.  
  9861.                                         SUMMARY
  9862.  
  9863.              These are the natural (default) segments of all addressing modes:
  9864.  
  9865.                  (1) DS
  9866.  
  9867.                       variable + (constant)
  9868.                       [bx] + (constant)
  9869.                       [si] + (constant)
  9870.                       [di] + (constant)
  9871.                       [bx+si] + (constant)
  9872.                       [bx+di] + (constant)
  9873.  
  9874.  
  9875.                  (2) SS
  9876.  
  9877.                       [bp] + (constant)
  9878.                       [bp+si] + (constant)
  9879.                       [bp+di] + (constant)
  9880.  
  9881.              Where the constant is optional. Segment overrides may be used.
  9882.              The segment overrides are:
  9883.  
  9884.                  SEGMENT OVERRIDE         MACHINE CODE (hex)
  9885.                       CS:                      2E
  9886.                       DS:                      3E
  9887.                       ES:                      26
  9888.                       SS:                      36
  9889.  
  9890.  
  9891.              OFFSET
  9892.  
  9893.              The reserved word 'offset' tells the assembler to calculate the
  9894.              offset of the variable from the beginning of the segment.
  9895.  
  9896.                       mov  ax, offset variable2
  9897.  
  9898.  
  9899.              SEG
  9900.  
  9901.              The reserved word 'seg' tells the assembler, linker and loader to
  9902.              get the segment address of the segment that the variable is in.
  9903.  
  9904.                       mov  ax, seg variable2
  9905.  
  9906.              LEA
  9907.  
  9908.              LEA calculates an address using any of the 8086 addressing modes,
  9909.              then puts the address in a register.
  9910.  
  9911.                       lea  cx, [bp+di+27]
  9912. Chapter 12 - Multiple Word Arithmetic I
  9913. =======================================
  9914.                                                                            122
  9915.  
  9916.  
  9917.  
  9918.  
  9919.              Let's review the LOOP instruction. We often want to repeat an
  9920.              action a specific number of times. In a FOR loop, we write:
  9921.  
  9922.                  FOR I = 1, 10
  9923.  
  9924.              That means we want to repeat the code that follows ten times. The
  9925.              8086 has an instruction for this, called the loop instruction. It
  9926.              looks like this:
  9927.  
  9928.                  mov  cx, 10
  9929.              label17:
  9930.                  ...
  9931.                  (a bunch of code)
  9932.                  ...
  9933.                  loop label17
  9934.  
  9935.              The count MUST be in the CX register. This is the only register
  9936.              you can use for this. When the machine sees the loop instruction,
  9937.              it decrements the CX register by one, LEAVING ALL FLAGS ALONE,
  9938.              and if the result is not zero, it loops back to the label. If the
  9939.              result is zero, it falls through the loop. One problem we might
  9940.              have with this instruction is if you enter it with CX = 0, it is
  9941.              going to loop 65,536 times. Intel provided a second instruction
  9942.              to avoid this - JCXZ (jump if CX is zero). You put it right
  9943.              before the loop for insurance.
  9944.  
  9945.                  jcxz label19
  9946.              label17:
  9947.                  ...
  9948.                  (a bunch of code)
  9949.                  ...
  9950.                  loop label17
  9951.              label19:
  9952.  
  9953.              Obviously, in our first example this instruction is not necessary
  9954.              because we set CX to 10 just before entering the loop.
  9955.  
  9956.  
  9957.  
  9958.              If you have seen the list of 8086 instructions, you will have
  9959.              noticed lots of strange looking add and subtract instructions.
  9960.              Why are they there? In this chapter we will look at ADC and SBB.
  9961.              The others will come in later chapters.
  9962.  
  9963.              How do engineers decide what a reasonable size for a number is?
  9964.              When they started making 8 bit machines (the maximum unsigned
  9965.              number is 255) did they go out on the streets and take a poll to
  9966.              find out if 255 was the maximum number that people used?  No,
  9967.              they didn't even think about what people needed. It was a
  9968.              question of what the technology would allow at that time.
  9969.  
  9970.              ______________________
  9971.  
  9972.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  9973.  
  9974.  
  9975.              Chapter 12 - Multiple Word Arithmetic I                       123
  9976.              _______________________________________
  9977.  
  9978.              Similarly, at the time the 8086 was engineered, 16 bits was
  9979.              pushing the limits of the technology. But 65,535 doesn't really
  9980.              cut the mustard. If those are 65,535 pennies, that isn't even
  9981.              your yearly food bill, let alone the cost of housing.
  9982.  
  9983.              The 8086 instructions give us the option of making integers of
  9984.              whatever size we want. Because it is done in software, it is
  9985.              slower, but if we are doing thousands or tens of thousands of
  9986.              additions instead of hundreds of thousands or millions, it won't
  9987.              be a great inconvenience.{1}
  9988.  
  9989.              ASMHELP.OBJ is set up to input 2 word (4 byte) and 4 word (8
  9990.              byte) numbers so those are what we are going to use. 4 byte
  9991.              numbers are up to +/- 2 billion and 8 byte numbers are up to
  9992.              9X10exp18. Those should be large enough.
  9993.  
  9994.              The first instruction is ADC, add with carry. When you add by
  9995.              hand, you add everything in the right column, then carry to the
  9996.              next column left, repeating this over and over. We don't need to
  9997.              do it column by column, but it is necessary to do it word by
  9998.              word. In the data section we need:
  9999.  
  10000.              variable1  dq  ?
  10001.              variable2  dq  ?
  10002.  
  10003.              and the code for a 4 word (8 byte) addition is the following:
  10004.  
  10005.                  lea  si, variable1
  10006.                  lea  di, variable2
  10007.                  mov  ax, [di]                 ; first addition
  10008.                  add  [si], ax
  10009.                  pushf                    ; save the flags
  10010.                  mov  cx, 3
  10011.                  add  si, 2               ; go to next higher word
  10012.                  add  di, 2
  10013.  
  10014.              long_add_loop:               ; next three additions
  10015.                  mov  ax, [di]
  10016.                  popf                     ; restore the flags
  10017.                  adc  [si], ax
  10018.                  pushf                    ; save the new flags
  10019.                  add  di, 2
  10020.                  add  si, 2
  10021.                  loop long_add_loop
  10022.  
  10023.                  popf                     ; pop the flags off the stack
  10024.  
  10025.  
  10026.              ADC is the same as ADD, but it looks at the carry flag - if the
  10027.              carry flag is 1, it adds 1 to the result; if the carry flag is
  10028.              zero, it does nothing to the result. This works for both signed
  10029.              and unsigned numbers. If you don't believe it, you should go back
  10030.              ____________________
  10031.  
  10032.                 1 As a benchmark, it took a moderately slow PC 6.5 seconds to
  10033.              do 100,000 eight byte additions. The same PC can do over a
  10034.              million two byte additions in 6 seconds.
  10035.  
  10036.  
  10037.              The PC Assembler Tutor                                        124
  10038.              ______________________
  10039.  
  10040.              to the introductory chapter with the base 10 machine and look at
  10041.              long additions.
  10042.  
  10043.              Notice PUSHF and POPF. These are special instructions called push
  10044.              flags and pop flags. Rather than pushing an arithmetic register
  10045.              on the stack, pushf pushes the register containing the flags.
  10046.              Popf pops them back into the flags register. This is necessary
  10047.              because ADC looks at the carry flag and the ADD instructions in
  10048.              the loop:
  10049.  
  10050.                  add  di,2
  10051.                  add  si,2
  10052.  
  10053.              might change the carry flag. The last POPF after the loop is to
  10054.              get it off the stack (anything we put on the stack we need to
  10055.              take off the stack).
  10056.  
  10057.              The first addition is a normal addition, the last three take the
  10058.              carry into account. We are moving the pointers a word at a time.
  10059.              Because the 8086 doesn't allow both operands to be in memory, we
  10060.              need to move one into a register. After the addition is
  10061.              performed, the result is in memory. We can discard what is in the
  10062.              register.
  10063.  
  10064.              Notice that the first half of the code looks almost the same as
  10065.              the code inside the loop. If we could only use ADC instead of
  10066.              ADD, we could put the first addition inside the loop. It is
  10067.              possible to do this. There is another instruction, CLC, which
  10068.              clears the carry flag. Recall that if the carry flag is 0, ADC
  10069.              does nothing different from ADD. Therefore, we can have:
  10070.  
  10071.                  lea  si, variable1
  10072.                  lea  di, variable2
  10073.                  mov  cx, 4               ; 4 additions in loop
  10074.                  clc                      ; set cf to zero
  10075.                  pushf                    ; save the flags
  10076.  
  10077.              long_add_loop:
  10078.                  mov  ax, [di]            ; word to a register
  10079.                  popf                     ; restore the flags
  10080.                  adc  [si], ax            ; register + memory
  10081.                  pushf                    ; save the flags
  10082.                  add  di, 2
  10083.                  add  si, 2
  10084.                  loop long_add_loop
  10085.  
  10086.                  popf                     ; pop the flags off the stack
  10087.  
  10088.              It's the same code. The number of loops was increased from 3 to
  10089.              4, and the carry flag was cleared to insure that the first
  10090.              addition would have nothing extra added. Here is the basic
  10091.              program.
  10092.  
  10093.              ; - - - - - PUT DATA BELOW THIS LINE
  10094.              variable1  dq  ?
  10095.              variable2  dq  ?
  10096.              ; - - - - - PUT DATA ABOVE THIS LINE
  10097.  
  10098.  
  10099.              Chapter 12 - Multiple Word Arithmetic I                       125
  10100.              _______________________________________
  10101.  
  10102.  
  10103.              ; - - - - - PUT CODE BELOW THIS LINE
  10104.  
  10105.                  call  show_regs
  10106.  
  10107.              outer_loop:
  10108.                  lea  ax, variable1            ; get the variables
  10109.                  call get_signed_8byte
  10110.                  call print_signed_8byte
  10111.                  lea  ax, variable2
  10112.                  call get_signed_8byte
  10113.                  call print_signed_8byte
  10114.  
  10115.                  lea  si, variable1            ; set the pointers
  10116.                  lea  di, variable2
  10117.                  mov  cx, 4                    ; loop 4 times (4 words)
  10118.                  clc                           ; clear the cf
  10119.                  pushf                         ; save the flags
  10120.  
  10121.              long_add_loop:
  10122.                  mov  ax, [di]            ; word to a register
  10123.                  popf                     ; restore the flags
  10124.                  adc  [si], ax            ; register + memory
  10125.                  pushf                    ; save the flags
  10126.                  add  di, 2
  10127.                  add  si, 2
  10128.                  loop long_add_loop
  10129.  
  10130.                  popf                     ; restore the flags
  10131.  
  10132.                  lea  ax, variable1
  10133.                  call print_signed_8byte
  10134.  
  10135.                  jmp  outer_loop
  10136.  
  10137.              ; - - - - - PUT CODE ABOVE THIS LINE
  10138.  
  10139.              The calls to get_signed_8byte are followed by print_signed_8byte.
  10140.              This is so you can see what you have actually typed in. It will
  10141.              be neat and with commas, so it will be much easier to read.
  10142.              Everything else is the same as before.
  10143.  
  10144.              As an aside, let's talk about commas. Though we can get along
  10145.              without commas if we have a 4 digit number, There is no reason to
  10146.              do without them when using larger numbers. I find printer output
  10147.              that has 15 digit floating point numbers without commas not only
  10148.              hard to read but also silly. It's not that the computer is
  10149.              incapable of putting in commas, it's that the prople who wrote
  10150.              the programs couldn't be bothered with doing 2 hours of work to
  10151.              save the users lots and lots of aggrivation. Therefore, for all
  10152.              large number input (that is, larger than 1 word)  you can use
  10153.              commas. They don't even have to be in the right place, the
  10154.              computer ignores them. All the following are the same number:
  10155.  
  10156.                  723469746
  10157.                  723,4,69746
  10158.                  72,3,46974,6
  10159.  
  10160.  
  10161.              The PC Assembler Tutor                                        126
  10162.              ______________________
  10163.  
  10164.                  7,2,3,4,6,9,7,4,6
  10165.                  723,469,746
  10166.  
  10167.              The program strips all commas out of the line and then looks at
  10168.              the input. All large output has the commas in the right spot. In
  10169.              order to enlist you in the crusade to stamp out unreadable input
  10170.              and output, the summary at the end of this chapter has the C code
  10171.              necessary for stripping commas. This is my contribution to world
  10172.              culture.
  10173.  
  10174.              If you have played with the signed addition program, all we need
  10175.              to do to make it unsigned is to change all the get_signed_8byte
  10176.              calls to get_unsigned_8byte calls. Change the print calls to
  10177.              unsigned also. Run the program.
  10178.  
  10179.              Now, let's try subtraction. How much code do we have to alter to
  10180.              make it a subtraction program? The answer is - one word. Just
  10181.              change the ADC (add with carry) to SBB (subtract with borrow).
  10182.              SBB learns from the CF flag whether the last subtraction had to
  10183.              borrow, and tells the next subtraction via the CF flag whether it
  10184.              has had to borrow. Change it, and try it out. (Yes, really do it,
  10185.              don't just pretend that you are going to do it).
  10186.  
  10187.              Why does this program work with exactly 8 bytes? Because we tell
  10188.              the loop via CX that it is 4 words long. If we put 2 in CX, the
  10189.              loop will think that the number is 2 words (4 bytes) long, and if
  10190.              we put 17 in CX, the loop will think that the number is 17 words
  10191.              (34 bytes) long. In fact, this little snippet of code can do
  10192.              either signed or unsigned addition or subtraction of any number
  10193.              of words simply by altering one number and one word of code.
  10194.  
  10195.              The code as it stands has only one shortfall. Remember from our
  10196.              earlier subtraction that we might want to have an INTO (interrupt
  10197.              on overflow) instruction after signed addition and subtraction.
  10198.              It needs to be after the last addition (or subtraction). All we
  10199.              need to do is put it after the POPF just below the loop. At this
  10200.              point the flags show the condition right after the last addition
  10201.              or subtraction:
  10202.  
  10203.                  loop long_add_loop
  10204.  
  10205.                  popf                ; pop the flags off the stack
  10206.                  into                ; interrupt if overflow is set
  10207.  
  10208.              Chapter 12 - Multiple Word Arithmetic I                       127
  10209.              _______________________________________
  10210.  
  10211.                                        SUMMARY
  10212.  
  10213.  
  10214.              ADC (add with carry) is used for multiple word arithmetic. It
  10215.              adds two numbers along with plus the value in CF, the carry flag
  10216.              (1 if the flag is set and 0 if the flag is cleared). CLC (clear
  10217.              the carry flag) should be used to clear CF before the first
  10218.              addition. After the addition, the flags register should be pushed
  10219.              in order to store CF unless it is certain that CF will not be
  10220.              effected by the rest of the loop.
  10221.  
  10222.                  popf
  10223.                  adc  [di], ax
  10224.                  pushf
  10225.  
  10226.  
  10227.  
  10228.              SBB (subtract with borrow) is used for multiple word arithmetic.
  10229.              It subtracts one number from the other and subtracts the value in
  10230.              CF to account for any borrows from the right. CLC (clear the
  10231.              carry flag) should be used to clear CF before the first
  10232.              subtraction. After the subtraction, the flags register should be
  10233.              pushed in order to store CF unless it is certain that CF will not
  10234.              be effected by the rest of the loop.
  10235.  
  10236.                  popf
  10237.                  sbb  [di], ax
  10238.                  pushf
  10239.  
  10240.              These operations have the typical 5 possibilities:
  10241.  
  10242.                  1) register, register
  10243.                  2) register, memory
  10244.                  3) memory, register
  10245.                  4) memory, constant
  10246.                  5) register, constant
  10247.  
  10248.  
  10249.  
  10250.              You may manually control the value in CF with STC and CLC. STC
  10251.              (set the carry flag) sets the value to 1, while CLC (clear the
  10252.              carry flag) sets the value to 0. These are used for initial
  10253.              settings of multiple word operations.
  10254.  
  10255.  
  10256.              In order to store the values in the flags register you use PUSHF
  10257.              (push the flags) until you need them again. At that time you can
  10258.              get them back with POPF (pop the flags).
  10259.  
  10260.  
  10261.              The PC Assembler Tutor                                        128
  10262.              ______________________
  10263.  
  10264.                              STRIPPING COMMAS IN C
  10265.  
  10266.              In order to get rid of commas, you need some discipline in what
  10267.              kind of data entry you have. Specifically, you can't enter large
  10268.              numbers on the same line as text strings because text strings are
  10269.              likely to have commas that should be kept. This is hardly a major
  10270.              restriction. You can have as many numbers as you want on the same
  10271.              line since in C you must have whitespace between pieces of data.
  10272.  
  10273.              We will take all commas out of the line. You can retrofit most
  10274.              old programs with almost no change.
  10275.  
  10276.              The only time that you need this capability is if you are getting
  10277.              something from the keyboard, and what you probably have in the
  10278.              program is:
  10279.  
  10280.                  scanf ( "format string", variables );
  10281.  
  10282.              The method is (1) import a text string as a single string, (2)
  10283.              strip the commas, and (3) use sscanf instead of scanf.
  10284.  
  10285.                  char buffer[80] ;
  10286.  
  10287.                  fgets (buffer, 80, stdin) ;
  10288.                  strip_commas (buffer);
  10289.                  sscanf (buffer, "format string", variables) ;
  10290.  
  10291.              Both "format string" and the variables remain unchanged when you
  10292.              switch from scanf to sscanf. You might want to check for EOF with
  10293.              fgets.
  10294.  
  10295.              Heres the program:
  10296.  
  10297.              strip_commas (buffer)
  10298.              char   *buffer ;
  10299.              {
  10300.                  char *ptr1, *ptr2 ;
  10301.  
  10302.                  ptr1 = ptr2 = buffer ;
  10303.                  while (1)
  10304.                  {
  10305.                       if ( *ptr2 == ',')  /* skip commas */
  10306.                       {
  10307.                            ptr2++ ;
  10308.                            continue ;
  10309.                       }
  10310.                       /*move, increment, and check for 0 */
  10311.                       if (!(*ptr1++ = *ptr2++))   /* this is '=', not '==' */
  10312.                            break ;
  10313.                  }
  10314.                  return ;
  10315.              }
  10316. Chapter 13 - Multiple Word Arithmetic II
  10317. ========================================
  10318.                                                                            129
  10319.  
  10320.  
  10321.              We have just done multiple word addition and subtraction, which
  10322.              are easy. Now we have multiplication and division. We are going
  10323.              to multiply and divide long numbers by a one word (2 byte)
  10324.              number. Multiplying multiple-word numbers by multiple-word
  10325.              numbers is complex and time consuming but can be done. Dividing
  10326.              by a multiple-word number is an entirely different ballgame.{1}
  10327.  
  10328.              We'll do unsigned numbers first, then in a later chapter add the
  10329.              code we need for signed numbers. The core routine is the same.
  10330.  
  10331.  
  10332.              UNSIGNED MULTIPLICATION
  10333.  
  10334.              If you multiply an n digit number by an m digit number, there is
  10335.              a possibility of n+m digits in the result. 863 is 3 digits, 4975
  10336.              is 4 digits, 863 X 4975 = 4,293,425 is 7 digits = 4 + 3. We will
  10337.              be multiplying an 8 byte number by a 2 byte number, so we'll need
  10338.              10 bytes for the possible maximum result. Here's the code:
  10339.  
  10340.              ; - - - - - - - - ENTER DATA BELOW THIS LINE
  10341.              multiplicand      dq    ?
  10342.              multiplier        dw    ?
  10343.              result            db    10 dup (?)
  10344.  
  10345.              ; - - - - - - - - ENTER DATA ABOVE THIS LINE
  10346.  
  10347.              ; - - - - - - - - ENTER CODE BELOW THIS LINE
  10348.  
  10349.              outer_loop:
  10350.                  lea  ax, multiplicand    ; load multiplicand
  10351.                  call get_unsigned_8byte
  10352.                  call print_unsigned_8byte
  10353.                  call get_unsigned        ; unsigned word to multiplier
  10354.                  mov  multiplier, ax
  10355.  
  10356.  
  10357.                  lea  si, multiplicand    ; load pointers
  10358.                  lea  bx, result
  10359.  
  10360.                  mov  cx, 4          ; number of words
  10361.                  sub  di,di          ; clear di
  10362.  
  10363.              mult_loop:
  10364.  
  10365.              ____________________
  10366.  
  10367.                 1  For those of you with a hankering for large multiplication
  10368.              and division, I have included subroutines which can multiply and
  10369.              divide numbers of any length in a file called MISHMASH.DOC. It is
  10370.              in \EXTRAFILE. You will need to finish all the chapters before
  10371.              looking at it, since it uses things that you don't know about
  10372.              yet.
  10373.  
  10374.              ______________________
  10375.  
  10376.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  10377.  
  10378.  
  10379.              The PC Assembler Tutor                                        130
  10380.              ______________________
  10381.  
  10382.                  mov  ax, [si]       ; multiplicand to ax
  10383.                  mul  multiplier     ; {2}
  10384.                  add  ax, di         ; high word from last multiplication
  10385.                  jnc  store_result
  10386.                  inc  dx             ; {3}
  10387.              store_result:
  10388.                  mov  [bx], ax       ; store 1 word of result.
  10389.                  mov  di, dx         ; save high word for next multiplication
  10390.                  add  si, 2          ; increment pointers
  10391.                  add  bx, 2
  10392.                  loop mult_loop
  10393.  
  10394.                  mov  [bx], di       ; move last word of result
  10395.  
  10396.                  mov  ax, [bx]
  10397.                  call print_hex
  10398.                  lea  ax, result
  10399.                  call print_unsigned_8byte
  10400.                  jmp  outer_loop
  10401.  
  10402.              ; - - - - - - - - ENTER CODE ABOVE THIS LINE
  10403.  
  10404.              There are two different input calls, an 8 byte one and a 2 byte
  10405.              one. Inside the loop we store the high word from the
  10406.              multiplication in DI and then add it to the next result. This is
  10407.              the same as when you multiply single digits in base 10 (9 X 7 =
  10408.              63 carry the 6). Note that when you add DI, there can be a carry
  10409.              from AX to DX, but there can be no carry out of DX. After we drop
  10410.              out of the loop, we need to put the last word in result. We take
  10411.              it from DI, but we could take it from DX if we wanted. Finally,
  10412.              the printing. Print_unsigned_8byte can't print the whole result,
  10413.              so we are printing the high word in hex form. If those top two
  10414.              bytes are non zero, what 'print_unsigned_8byte' prints will be
  10415.              incorrect because it is missing the top 2 bytes. Note once again
  10416.              that the only thing constraining this program to an 8 byte number
  10417.              is the 4 that we put in CX - change that number and you can do
  10418.              any size number that you want.
  10419.  
  10420.              Run a bunch of numbers through this, including a couple that have
  10421.              more than a 20 digit result.
  10422.  
  10423.  
  10424.              UNSIGNED DIVISION
  10425.  
  10426.              Division is done the same way in the software as it is done with
  10427.              pencil and paper, starting at the left and working right. On the
  10428.              computer, this means starting with the high order word and
  10429.              working down.
  10430.              ____________________
  10431.  
  10432.                 2 It would be about 3% faster to have this in a register, but
  10433.              unfortunately we are out of registers.
  10434.  
  10435.                 3 Do we need to check DX for a carry here?  No. The maximum
  10436.              multiplication is FFFFh X FFFFh. The result is FFFE 0001h. That
  10437.              means that DX is a maximum FFFEh. If you add one, that's FFFFh,
  10438.              and no carry occurs.
  10439.  
  10440.  
  10441.              Chapter 13 - Multiple Word Arithmetic II                      131
  10442.              ________________________________________
  10443.  
  10444.  
  10445.              ; - - - - - - - - ENTER DATA BELOW THIS LINE
  10446.              dividend    dq   ?
  10447.              divisor     dw   ?
  10448.              quotient    dq   ?
  10449.              remainder   dw   ?
  10450.              ; - - - - - - - - ENTER DATA ABOVE THIS LINE
  10451.  
  10452.              ; - - - - - - - - ENTER CODE BELOW THIS LINE
  10453.  
  10454.              outer_loop:
  10455.  
  10456.                  lea  ax, dividend        ; get dividend
  10457.                  call get_unsigned_8byte
  10458.                  call print_unsigned_8byte
  10459.                  call get_unsigned        ; get divisor
  10460.                  mov  divisor, ax
  10461.  
  10462.                  lea  si, dividend + 6    ; start at the top
  10463.                  lea  bx, quotient + 6
  10464.                  mov  di, divisor
  10465.                  mov  cx, 4               ; number of words
  10466.                  sub  dx, dx              ; clear dx for first division
  10467.  
  10468.              division_loop:
  10469.                  mov  ax, [si]            ; dividend word to ax
  10470.                  div  di                  ; {4}
  10471.                  mov  [bx], ax            ; word of result to quotient
  10472.                  sub  si, 2               ; decrement the pointers
  10473.                  sub  bx, 2
  10474.                  loop division_loop
  10475.  
  10476.                  mov  remainder, dx
  10477.                  mov  ax, remainder
  10478.                  call print_unsigned
  10479.                  lea  ax, quotient
  10480.                  call print_unsigned_8byte
  10481.  
  10482.                  jmp  outer_loop
  10483.  
  10484.              ; - - - - - - - - ENTER CODE ABOVE THIS LINE
  10485.  
  10486.              That's it? Yup. The division instruction is designed to work
  10487.              effeciently and simply. We start with the most significant
  10488.              digits, divide, put the quotient in  the variable "quotient",
  10489.              ____________________
  10490.  
  10491.                 4 After this division, the quotient is in AX and the remainder
  10492.              is in DX. Say, aren't we going to do anything with the remainder?
  10493.              There's nothing in the code about DX until we get out of the
  10494.              loop. In fact, we ARE doing something with the remainder. Just
  10495.              like division with pencil and paper, when you have a remainder,
  10496.              you bring it down to the left of the next digits you are going to
  10497.              divide. These get divided the next time around. But we don't need
  10498.              to move the remainder because it's already in the right place.
  10499.              Pretty snappy, huh? You don't need to move anything; it all takes
  10500.              care of itself.
  10501.  
  10502.  
  10503.              The PC Assembler Tutor                                        132
  10504.              ______________________
  10505.  
  10506.              DECREMENT the pointers, and get the next word for division.
  10507.              After the final division, we have the remainder left in DX, so we
  10508.              move it to the variable "remainder". The final instructions print
  10509.              the remainder and the quotient.
  10510.  
  10511.              Notice that we don't need to touch the remainder during the
  10512.              entire operation. The 8086 leaves it exactly where it needs to be
  10513.              for the next division. Using the DX register when you have single
  10514.              word division seems screwy, but using DX for multiple word
  10515.              division is both natural and elegant. The Intel people made one
  10516.              instruction do the work of both.
  10517.  
  10518.              Remember from the earlier chapter on division that you can get a
  10519.              zero divide error if the quotient is larger than 65535. Is it
  10520.              possible to get a quotient larger than 65535 in this routine? NO.
  10521.              It is impossible to get a zero divide on anything other than a
  10522.              zero.{5}
  10523.  
  10524.              Run the program and do several examples. You can even do a 0
  10525.              divide if you feel like interrupting the program.
  10526.  
  10527.  
  10528.              SIGNED NUMBERS
  10529.  
  10530.              For byte or word signed multiplication and division, the 8086
  10531.              changes the signed numbers into unsigned numbers, does unsigned
  10532.              multiplication/division, then adjusts for sign. For long numbers,
  10533.              we have to do these operations ourselves, so we need three
  10534.              sections of code. (1) change the numbers into unsigned numbers,
  10535.              (2) do unsigned multiplication/division and (3) adjust the signs.
  10536.              The routines that we have here are part two of this scheme. It
  10537.              will be easier to implement this once you know about subroutines,
  10538.              so signed division and multiplication will have to wait till
  10539.              later.
  10540.  
  10541.  
  10542.  
  10543.              ____________________
  10544.  
  10545.                 5 This is technical, so if you start getting lost, don't worry
  10546.              about it. How do we know that it's impossible?  What we are
  10547.              putting in DX is the remainder (R). R is always less than the
  10548.              divisor (D). Let Q be the number in AX the next time around. What
  10549.              we are dividing is:
  10550.                  ((R*65536) + Q ) / D  <= (( R*65536 ) + 65535 ) /D
  10551.              since Q is less than to or equal to 65535. This is the maximum.
  10552.              ( ((R*(65535 + 1)) + 65535 ) /D = (((R+1) * 65535) + R)/D (huh?)
  10553.                                           = ((R+1) * 65535)/D + R/D
  10554.              Let's do a few examples: if D = 1, R < D so R = 0 max.
  10555.                                 = (1*65535)/1 + 0/1 = 65535  rem 0
  10556.              where rem = remainder. If D = 2, R < D so R = 1 max.
  10557.                                 = ((1+1)*65535)/2 + 1/2 = 65535 rem 1
  10558.              If D = 3, R < D so R = 2 max.
  10559.                                 = (2+1)*65535)/3 + 2/3 = 65535 rem 2
  10560.              See a pattern here? R/D < 1, so the quotient can never be 65536.
  10561.              The maximum will always be 65535 with the remainder 1 less than
  10562.              the divisor. If you aren't a techie, ignore all this.
  10563.  
  10564.              ________________________________________
  10565.  
  10566.                                      SUMMARY
  10567.  
  10568.  
  10569.              For both signed and unsigned numbers, multiple word division and
  10570.              multiplication are based on an unsigned number routine. Signed
  10571.              numbers are changed into unsigned numbers, the operation is
  10572.              performed, and the signs of the results are adjusted.
  10573.  
  10574.  
  10575.              Multiplication is done the same as for single words except that
  10576.              the high word from one result is saved and added to the low word
  10577.              of the next result, thus adding the two partial results. If this
  10578.              addition gives a carry, DX must be incremented.
  10579.  
  10580.              Division operates from left to right. For the first division, DX
  10581.              is zeroed. After that it always contains the remainder from the
  10582.              last division. The quotients in AX are moved to memory one by
  10583.              one. At the end, the final remainder will be in DX.
  10584. Chapter 14 - Zoom
  10585. =================
  10586.                                                                            134
  10587.  
  10588.  
  10589.  
  10590.              There are only a couple of reasons for working at the assembler
  10591.              level. Perhaps you're curious about how the PC functions at the
  10592.              machine level. Maybe you want to optimize a time consuming
  10593.              section of code. Maybe you want to work easily with the DOS
  10594.              function calls and the BIOS calls (which will be introduced in a
  10595.              later chapter). Or maybe you want raw speed.
  10596.  
  10597.              Every time that I enter:
  10598.  
  10599.                  >dir /w
  10600.  
  10601.              and watch DOS meander its way down the screen at a leisurely
  10602.              pace, I think "Can't the computer go any faster?"  Let's find
  10603.              out. This is going to be a short chapter. Make a copy of
  10604.              template.asm, and we'll call it zoom.asm. This is going to
  10605.              overwrite the entire screen 200 times.{1}
  10606.  
  10607.              Before we write to the screen, we need to know where the screen
  10608.              is. When IBM designed the structure of the PC, they decided to
  10609.              put the memory for the monochrome card in one place and the
  10610.              memory for the color card in another place - apparently they
  10611.              thought you might want to have both a color monitor and a
  10612.              monochrome monitor running at the same time. The color monitor is
  10613.              in segment 0B800 at offset 0, and the monochrome monitor is in
  10614.              segment 0B000 at offset 0. We need to put the correct number into
  10615.              the program, so you need to know whether you have a color card or
  10616.              a monochrome card. If one segment number doesn't work, you can
  10617.              try the other.
  10618.  
  10619.              TEMPLATE.ASM
  10620.              ; - - - - - - - - - -  START CODE BELOW THIS LINE
  10621.                     call  show_regs_and_wait  ; {2}
  10622.                                               ; we are about to start
  10623.                                               ; this marks the time
  10624.  
  10625.                     mov   ax, 0B800h       ; color seg. 0B000h is mono seg
  10626.                     mov   es, ax           ; es is at video card segment
  10627.                     mov   cx, 2            ; do this whole thing twice
  10628.              outer_loop:
  10629.              ____________________
  10630.  
  10631.                 1 It is now time for you to go out and get a book about the
  10632.              internal structure and i/o interface of the PC. One good book is
  10633.              "The Peter Norton Programmer's Guide To The IBM PC", by guess
  10634.              who? It is clearly written and a good introduction. Another
  10635.              quality book is "DOS Programmer's Reference" by Terry Dettmann.
  10636.              It is very systematically laid out and is more techie oriented.
  10637.  
  10638.                 2 We need to initialize the video card to make sure it is in
  10639.              the right place. This is the easiest way. The registers
  10640.              themselves mean nothing to us.
  10641.  
  10642.              ______________________
  10643.  
  10644.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  10645.  
  10646.  
  10647.              Chapter 14 - Zoom                                             135
  10648.              _________________
  10649.  
  10650.                     push  cx               ; save for outer loop instruction
  10651.                     mov   cx, 100          ; 100 repeats ; zoom_loop count
  10652.                     mov   al, '0'          ; 100 characters starting at '0'
  10653.                     mov   ah, 07h          ; black background, white letters
  10654.  
  10655.              zoom_loop:    ; draw the screen 100 times
  10656.                     push  cx
  10657.                     mov   cx, 2000         ; 80 X 25 screen is 2000 words long
  10658.                     mov   si, 0            ; start at offset 0000.
  10659.  
  10660.              inner_loop:    ; inner loop - fill the screen - 2000 words
  10661.                     mov   es:[si], ax
  10662.                     add   si, 2
  10663.                     loop  inner_loop
  10664.  
  10665.                     inc   al               ; next higher ASCII character
  10666.                     pop   cx               ; zoom_loop count
  10667.                     loop  zoom_loop
  10668.  
  10669.                     pop   cx               ; outer_loop count
  10670.                     loop  outer_loop
  10671.  
  10672.                     call  get_continue     ; finished - this is for timing
  10673.              ; - - - - - - - - - - END CODE ABOVE THIS LINE
  10674.  
  10675.  
  10676.              Show_regs_and_wait resets the video card, so we use it to make
  10677.              sure the video memory is set at offset 0000. It then waits for
  10678.              ENTER. At the end, get_continue waits for ENTER.{3} This way you
  10679.              can mark the beginning and the end in order to time it. We set
  10680.              the ES segment to the video segment. This is different depending
  10681.              on whether you have a monochrome card or a color card. The
  10682.              monochrome segment is at 0B000h and the color card is at 0B800h.
  10683.              You need to know which kind of card you have so you can put the
  10684.              right number into ES via AX.
  10685.  
  10686.              Since the normal segment for SI is the DS segment, we need to put
  10687.              in a segment override to use SI with the ES segment.
  10688.  
  10689.              We start with the ASCII character '0' and then do the next 99
  10690.              characters in increasing ASCII sequence. Technically, ASCII
  10691.              characters end at 127, but the PC extended characters go up to
  10692.              255, so we will start with ASCII 48d and end with ASCII 147d. The
  10693.              zoom_loop changes the character 100 different times, and the
  10694.              inner_loop fills the screen. That 07h in AH means that we will
  10695.              have white characters on a black background. The outer loop has a
  10696.              count of 2. This should be adjusted. If you have a medium speed
  10697.              machine make it 4 (400 screen fills) and if you have a high speed
  10698.              machine make it 8 (800 screen fills).
  10699.  
  10700.              ____________________
  10701.  
  10702.                 3 That is its mission in life. It is there so that you can
  10703.              time blocks of code. If you want to find out how long some code
  10704.              takes, repeat it 10,000  (or whatever is appropriate) times and
  10705.              put  get_continue in front of it and behind it. That way you can
  10706.              control the start and mark the finish.
  10707.  
  10708.              When this is done, the screen will be filled with characters so
  10709.              you will need to use the DOS command
  10710.  
  10711.                  > cls
  10712.  
  10713.              to clear the screen for anything else.
  10714.  
  10715.              When it is all assembled and linked, get a cup of coffee and a
  10716.              wristwatch with a second hand and we'll time it. Divide by 200
  10717.              (or 400 or 800) to find out how long it takes to fill one screen.
  10718.              If the characters are not on your screen, you probably have the
  10719.              wrong segment address for your video card, so try the other one.
  10720.              Are you ready to time it? Then ready, set, go.
  10721.  
  10722.              Hmmm. On my machine 200 repeats takes about 4 seconds, while the
  10723.              dir command takes about 1 second for one screen. That means that
  10724.              zoom.asm is about 50 times faster. That's the difference between
  10725.              someone running a 4 minute mile and someone running a 3hr. 20min.
  10726.              mile. This is one of the reasons people like to work at the
  10727.              assembler level.
  10728. Chapter 15 - Subroutines
  10729. ========================
  10730.                                                                            137
  10731.  
  10732.  
  10733.  
  10734.              It is now time to talk about subroutines. If you have only used
  10735.              BASIC this may be difficult for you. It is assumed that you are
  10736.              familiar with subroutines and use them constantly in your
  10737.              programming.
  10738.  
  10739.              You have been using subroutines since the very first program in
  10740.              this manual. When you wrote:
  10741.  
  10742.                  call get_num
  10743.  
  10744.              you called a subroutine in ASMHELP.OBJ. Now you are going to
  10745.              write subroutines yourself and have them call each other. There
  10746.              are different template files for programs with subroutines. They
  10747.              are SUBTEMP1.ASM and SUBTEMP2.ASM. We will start with SUBTEMP1.
  10748.              It has the entry subroutine and a space for additional
  10749.              subroutines. The entry subroutine is the subroutine where the
  10750.              operating system starts the program; it does the necessary
  10751.              initialization and has special code for that.
  10752.  
  10753.              You will see some additions to the normal template file. At the
  10754.              top is the line:
  10755.  
  10756.                  INCLUDE      \pushregs.mac
  10757.  
  10758.              What this is will be explained later, but you must put the file
  10759.              PUSHREGS.MAC in the root directory of your current drive. You
  10760.              will find it in the \TEMPLATE subdirectory.
  10761.  
  10762.              At the end of the SUBTEMP1.ASM is:
  10763.  
  10764.              ; + + + + + + + + + + + + START SUBROUTINES BELOW THIS LINE
  10765.  
  10766.              ; + + + + + + + + + + + + END SUBROUTINES ABOVE THIS LINE
  10767.  
  10768.              This is where you will write all the subroutines except the entry
  10769.              subroutine which is still the same as before. All data for all
  10770.              subroutines still goes in the DATASTUFF segment.
  10771.  
  10772.  
  10773.              Our first program will just call subroutines which will print out
  10774.              messages. Using SUBTEMP1.ASM, it looks like this:
  10775.  
  10776.              ;prog1.asm
  10777.  
  10778.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  10779.              main_message  db   "This is the entry routine.", 0
  10780.              sub1_message  db   "This is subroutine1.", 0
  10781.              sub2_message  db   "This is subroutine2.", 0
  10782.              sub3_message  db   "This is subroutine 3.", 0
  10783.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  10784.  
  10785.  
  10786.              ______________________
  10787.  
  10788.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  10789.  
  10790.  
  10791.              The PC Assembler Tutor                                        138
  10792.              ______________________
  10793.  
  10794.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  10795.                     mov   ax, offset main_message
  10796.                     call  print_string
  10797.                     call  sub1
  10798.                     mov   ax, offset main_message
  10799.                     call  print_string
  10800.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  10801.  
  10802.              ; + + + + + + + + + + + + START SUBROUTINES BELOW THIS LINE
  10803.              ;------------
  10804.              sub1  proc near
  10805.  
  10806.                     push  ax
  10807.                     mov   ax, offset sub1_message
  10808.                     call  print_string
  10809.                     call  sub2
  10810.                     mov   ax, offset sub1_message
  10811.                     call  print_string
  10812.                     pop   ax
  10813.  
  10814.                     ret
  10815.  
  10816.              sub1   endp
  10817.              ;------------
  10818.              sub2  proc near
  10819.  
  10820.                     push  ax
  10821.                     mov   ax, offset sub2_message
  10822.                     call  print_string
  10823.                     call  sub3
  10824.                     mov   ax, offset sub2_message
  10825.                     call  print_string
  10826.                     pop   ax
  10827.  
  10828.                     ret
  10829.  
  10830.              sub2   endp
  10831.              ;------------
  10832.              sub3  proc near
  10833.  
  10834.                     push  ax
  10835.                     mov   ax, offset sub3_message
  10836.                     call  print_string
  10837.                     pop   ax
  10838.  
  10839.                     ret
  10840.  
  10841.              sub3   endp
  10842.              ; ----------
  10843.              ; + + + + + + + + + + + + END SUBROUTINES ABOVE THIS LINE
  10844.  
  10845.              The data consists of messages to be printed by print_string.
  10846.              Print_string prints a zero terminated string (the number zero,
  10847.              not the character '0'), so there must be a zero after each
  10848.              message in the data segment. The entry subroutine prints a
  10849.              message and then calls sub1, the first subroutine, which prints a
  10850.              message and calls sub2 which prints a message and calls sub3.
  10851.  
  10852.  
  10853.              Chapter 15 - Subroutines                                      139
  10854.              ________________________
  10855.  
  10856.              Sub3 prints a message and then returns to sub2 which prints a
  10857.              message and returns to sub1 which prints a message and returns to
  10858.              the entry routine which prints a message and then exits. This
  10859.              program should print 7 messages in all. You will notice that the
  10860.              first thing that each subroutine does is save the value in AX,
  10861.              since it uses the AX register. This is the cardinal rule of
  10862.              robustness at the assembler level.
  10863.  
  10864.                  IF YOU USE A REGISTER, YOU MUST SAVE ITS VALUE BY PUSHING IT
  10865.                  ON THE STACK; YOU MUST THEN RESTORE THE VALUE JUST BEFORE
  10866.                  EXITING.
  10867.  
  10868.              It is impossible to overstress this. The routines which call your
  10869.              routine might rely on the registers remaining unaltered. If you
  10870.              disobey this rule and alter the registers, you'll be sorry.
  10871.  
  10872.              Why doesn't the entry routine push and pop the registers it uses?
  10873.              Well, the operating system assumes the registers will contain
  10874.              trash upon return from the program, so it uses nothing in the
  10875.              data registers.
  10876.  
  10877.              All the subroutines except the entry routine are near routines.
  10878.              We will only use near routines. Assemble this program, link it
  10879.              and run it. If it works ok, it is then time for program 2, which
  10880.              is the same as program1, but is in two files.
  10881.  
  10882.              Often, we want parts of a program in different files. Perhaps
  10883.              parts are standard subprograms which you have already written and
  10884.              assembled, perhaps the total program is too large to be handled
  10885.              comfortably in one file, perhaps different people are writing
  10886.              different parts of the program. Not only must we write the
  10887.              programs, but we must be able to connect them. We will put the
  10888.              entry routine, sub2 and the associated data in subtemp1.asm. We
  10889.              will put sub1, sub3, and the associated data in subtemp2.asm.
  10890.  
  10891.              Take a look at SUBTEMP2.ASM. It is slightly different. First, it
  10892.              does not have the variables that you need for set_reg_style
  10893.              (ax_byte, bx_byte, etc.) but it does have EXTRN statements for
  10894.              them. This means that you can change the register style from this
  10895.              file. SUBTEMP1.ASM has these variables declared PUBLIC so the
  10896.              linker can join them correctly.{1} We will talk about the correct
  10897.              way to declare external data later.
  10898.  
  10899.              SUBTEMP2.ASM has no stack segment, though there could be one.
  10900.              There is no entry subroutine. Therefore at the very end, you have
  10901.              the line:
  10902.  
  10903.                  END
  10904.  
  10905.              with nothing after it. In SUBTEMP1.ASM, you have
  10906.              ____________________
  10907.  
  10908.                 1. The reason for having only one set of variables for the
  10909.              style is so that every time you change one of the style
  10910.              variables, the array is updated. If you had two different arrays
  10911.              you could have two different sets of information for
  10912.              set_reg_style.
  10913.  
  10914.  
  10915.              The PC Assembler Tutor                                        140
  10916.              ______________________
  10917.  
  10918.  
  10919.                  END  start
  10920.  
  10921.              so the assembler and linker know that the program begins at the
  10922.              label "start".
  10923.  
  10924.              Let's do the two programs. Here are the data, the entry code and
  10925.              the subroutine code from the first file.
  10926.  
  10927.              ;prog1.asm
  10928.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  10929.              main_message  db   "This is the entry routine.", 0
  10930.              sub2_message  db   "This is subroutine2.", 0
  10931.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  10932.  
  10933.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  10934.              PUBLIC sub2
  10935.              EXTRN  sub1:NEAR, sub3:NEAR
  10936.  
  10937.                     mov   ax, offset main_message
  10938.                     call  print_string
  10939.                     call  sub1
  10940.                     mov   ax, offset main_message
  10941.                     call  print_string
  10942.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  10943.  
  10944.              ; + + + + + + + + + + + + START SUBROUTINES BELOW THIS LINE
  10945.              sub2  proc near
  10946.  
  10947.                     push  ax
  10948.                     mov   ax, offset sub2_message
  10949.                     call  print_string
  10950.                     call  sub3
  10951.                     mov   ax, offset sub2_message
  10952.                     call  print_string
  10953.                     pop   ax
  10954.  
  10955.                     ret
  10956.  
  10957.              sub2   endp
  10958.              ; + + + + + + + + + + + + END SUBROUTINES ABOVE THIS LINE
  10959.  
  10960.              Notice that sub1 and sub3 have been declared EXTRN before they
  10961.              were referenced, and the EXTRN statement tells the assembler that
  10962.              they are both near procedures. sub2 has been declared PUBLIC so
  10963.              the assembler will give the address of sub2 to the linker.
  10964.  
  10965.              Here's the data and code for the other file.
  10966.  
  10967.              ;prog2.asm
  10968.  
  10969.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  10970.              sub1_message  db   "This is subroutine1.", 0
  10971.              sub3_message  db   "This is subroutine 3.", 0
  10972.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  10973.  
  10974.              ; + + + + + + + + + + + + START SUBROUTINES BELOW THIS LINE
  10975.  
  10976.  
  10977.              Chapter 15 - Subroutines                                      141
  10978.              ________________________
  10979.  
  10980.              PUBLIC sub1, sub3
  10981.              EXTRN  sub2:NEAR
  10982.              ;------------
  10983.              sub1  proc near
  10984.  
  10985.                     push  ax
  10986.                     mov   ax, offset sub1_message
  10987.                     call  print_string
  10988.                     call  sub2
  10989.                     mov   ax, offset sub1_message
  10990.                     call  print_string
  10991.                     pop   ax
  10992.  
  10993.                     ret
  10994.  
  10995.              sub1   endp
  10996.              ;------------
  10997.              sub3  proc near
  10998.  
  10999.                     push  ax
  11000.                     mov   ax, offset sub3_message
  11001.                     call  print_string
  11002.                     pop   ax
  11003.  
  11004.                     ret
  11005.  
  11006.              sub3   endp
  11007.              ; ----------
  11008.              ; + + + + + + + + + + + + END SUBROUTINES ABOVE THIS LINE
  11009.  
  11010.              Here sub1 and sub3 have been declared PUBLIC and sub2 has been
  11011.              declared EXTRN.
  11012.  
  11013.              Assemble both programs and then link all three.
  11014.  
  11015.                  link prog1+prog2+\asmhelp.obj
  11016.  
  11017.              assuming that asmhelp is in the root directory. Run it. You
  11018.              should have the same results as before.
  11019.  
  11020.              We are going to do one more thing with the same two files.
  11021.              Without changing any of the code, we are going to put the data
  11022.              for prog1 in prog2 and the data for prog2 in prog1 like this.
  11023.  
  11024.              ;prog1
  11025.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  11026.              sub1_message  db   "This is subroutine1.", 0
  11027.              sub3_message  db   "This is subroutine 3.", 0
  11028.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  11029.  
  11030.              ;prog2
  11031.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  11032.              main_message  db   "This is the entry routine.", 0
  11033.              sub2_message  db   "This is subroutine2.", 0
  11034.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  11035.  
  11036.              So far, so good. Obviously we are going to need some more PUBLIC
  11037.  
  11038.  
  11039.              The PC Assembler Tutor                                        142
  11040.              ______________________
  11041.  
  11042.              statements and some EXTRN statements so the linker can link the
  11043.              four messages, but where do they go and what do they look like?
  11044.              The PUBLIC statements are the easiest. Put them in the segment
  11045.              where the message data appears, either before or after the data
  11046.              declaration.
  11047.  
  11048.              The EXTRN statement is a little more complicated. First, all data
  11049.              is declared EXTRN by giving the variable name followed by a colon
  11050.              followed by its data type. The data types are BYTE, WORD, DWORD
  11051.              (4bytes), QWORD (quadword or 8 bytes), and TBYTE (10 bytes).
  11052.              These are the standard 8086/7 data sizes. Therefore we have:
  11053.  
  11054.                  EXTRN sub1_message:BYTE, sub3_message:BYTE
  11055.  
  11056.              in prog2.asm and:
  11057.  
  11058.                  EXTRN main_message:BYTE, sub2_message:BYTE
  11059.  
  11060.              in prog1.asm. Where do they go? In order to know that, we need to
  11061.              talk about segment overrides again.
  11062.  
  11063.              You will remember from our discussion of the ASSUME statement
  11064.              that every time the assembler writes code with a variable, it
  11065.              checks the ASSUME statements to see which segment register(s)
  11066.              have the address of the segment that that variable is in. If we
  11067.              have:
  11068.  
  11069.                  ASSUME cs:SEG1, ds:SEG2, es:SEG3, ss:SEG4
  11070.  
  11071.              then if variable1 is in SEG2, the assembler will write no
  11072.              override in the code since DS is the 8086 default segment.
  11073.  
  11074.                  MACHINE CODE             ASSEMBLER INSTRUCTION
  11075.                     A1 0000                 mov   ax, variable1
  11076.  
  11077.              If variable1 is in SEG1 or SEG3 or SEG4, the assembler will write
  11078.              the appropriate segment override in the code.
  11079.  
  11080.                     MACHINE CODE               ASSEMBLER INSTRUCTION
  11081.  
  11082.                     2E: A1 0000                  mov   ax, variable1
  11083.                     26: A1 0000                  mov   ax, variable1
  11084.                     36: A1 0000                  mov   ax, variable1
  11085.  
  11086.              (By the way, those zeros just mean that the variable is at 0000
  11087.              offset from the beginning of the segment).
  11088.  
  11089.              The same thing happens when you have an EXTRN statement. The
  11090.              assembler associates the externally declared variable with the
  11091.              segment it is declared in. When the variable is used, it then
  11092.              goes through the same actions as if the variable were actually in
  11093.              that segment. Let's declare variable5 external with:
  11094.  
  11095.                  EXTRN variable5:WORD
  11096.  
  11097.              If we have:
  11098.  
  11099.  
  11100.  
  11101.              Chapter 15 - Subroutines                                      143
  11102.              ________________________
  11103.  
  11104.                  ASSUME cs:SEG1, ds:SEG2, es:SEG3, ss:SEG4
  11105.  
  11106.              then if variable5 is declared external in SEG2, the assembler
  11107.              will write no override in the code since DS is the 8086 default
  11108.              segment.
  11109.  
  11110.                  MACHINE CODE             ASSEMBLER INSTRUCTION
  11111.                     A1 0000 E               mov   ax, variable5
  11112.  
  11113.              If variable5 is declared external in SEG1 or SEG3 or SEG4, the
  11114.              assembler will write the appropriate segment override in the
  11115.              code.
  11116.  
  11117.                     MACHINE CODE               ASSEMBLER INSTRUCTION
  11118.  
  11119.                     2E: A1 0000 E                mov   ax, variable5
  11120.                     26: A1 0000 E                mov   ax, variable5
  11121.                     36: A1 0000 E                mov   ax, variable5
  11122.  
  11123.              The "E" after the machine code means that the assembler knows
  11124.              that the variable is external and it will tell the linker so the
  11125.              linker can put the correct offset address at that point in the
  11126.              machine code.
  11127.  
  11128.              Remember, as always, that it is your responsibility to have the
  11129.              correct segment address in the segment register before using a
  11130.              variable.
  11131.  
  11132.              Now we know where it goes. When you declare a variable external,
  11133.              you must put the EXTRN statement in a segment which uses the same
  11134.              segment register as the EXTRN variable is going to use. If the
  11135.              EXTRN variable will use DS, then the segment where the EXTRN
  11136.              statement is must use DS. If the variable uses ES, then the
  11137.              segment the EXTRN statement is in must use ES. In other words,
  11138.              the ASSUME statement for the segment the variable is in must
  11139.              match EXACTLY the ASSUME statement you would write if the
  11140.              variable were internal, not external.{2}  Normally, this is DS,
  11141.              but in special circumstances you might want something else. Also,
  11142.              if there is no segment that exactly matches what you want, then
  11143.              you need to create a dummy segment:
  11144.  
  11145.                  DUMMY_SEG  SEGMENT
  11146.                       EXTRN  variable7:QWORD
  11147.                  DUMMY_SEG  ENDS
  11148.  
  11149.              and make the assume statement that you want:
  11150.  
  11151.              ____________________
  11152.  
  11153.                 2. This means that if the segment with the EXTRN statement has
  11154.              more than one segment register in the assume statement:
  11155.  
  11156.                  ASSUME  ds:MORESTUFF, es:MORESTUFF
  11157.  
  11158.              then both those registers must be set to the segment of the
  11159.              external variable when using it or your results may be
  11160.              unreliable.
  11161.  
  11162.  
  11163.              The PC Assembler Tutor                                        144
  11164.              ______________________
  11165.  
  11166.                  ASSUME    es:DUMMY_SEG
  11167.  
  11168.  
  11169.  
  11170.              What segment has DS in an ASSUME statement? DATASTUFF in both
  11171.              files, so that is where the EXTRN declaration goes - in the
  11172.              DATASTUFF segment.
  11173.  
  11174.              ;prog1
  11175.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  11176.              PUBLIC sub1_message, sub3_message
  11177.              EXTRN  main_message:BYTE, sub2_message:BYTE
  11178.              sub1_message  db   "This is subroutine1.", 0
  11179.              sub3_message  db   "This is subroutine 3.", 0
  11180.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  11181.  
  11182.              ;prog2
  11183.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  11184.              PUBLIC main_message, sub2_message
  11185.              EXTRN  sub1_message:BYTE, sub3_message:BYTE
  11186.              main_message  db   "This is the entry routine.", 0
  11187.              sub2_message  db   "This is subroutine2.", 0
  11188.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  11189.  
  11190.              Change the data in the two files, assemble them again and link
  11191.              them again:
  11192.  
  11193.                  link prog1+prog2+\asmhelp.obj
  11194.  
  11195.              You should get the same results as before. We are now through
  11196.              with these programs. Make sure you understand how to define
  11197.              PUBLIC and EXTRN procedures and PUBLIC and EXTRN data before
  11198.              going on, since we are not going to cover it again. Everything
  11199.              else in this chapter will be done with a single file in order to
  11200.              make life easier.
  11201.  
  11202.  
  11203.              PASSING DATA
  11204.  
  11205.              When you pass data to the routines in ASMHELP.OBJ, you always
  11206.              pass it through the AX register. The reason for this is that you
  11207.              needed to use these routines before you knew much about 8086
  11208.              assembler language. It is solely for the convenience of beginners
  11209.              and is totally non-standard. In the real world, when you call a
  11210.              subroutine you ALWAYS pass the data on the stack, no matter which
  11211.              language you are using.
  11212.  
  11213.              If you have the C statement:
  11214.  
  11215.                  my_procedure (variable1, variable2, variable3) ;
  11216.  
  11217.              then the C compiler will generate the following code:
  11218.  
  11219.                  push variable3
  11220.                  push variable2
  11221.                  push variable1
  11222.  
  11223.  
  11224.  
  11225.              Chapter 15 - Subroutines                                      145
  11226.              ________________________
  11227.  
  11228.                  call my_procedure{3}
  11229.  
  11230.              The C language pushes these variables in right to left order.
  11231.              Before the call instruction is executed variable1 is on the top
  11232.              of the stack, variable2 is the next down, and variable3 is third
  11233.              on the stack. Is variable1 still on the stack top after the call
  11234.              instruction is executed? No. The call instruction pushes either
  11235.              one or two words on the stack. Before you go any farther with
  11236.              subroutines you need to know how the call and return instruction
  11237.              operate.
  11238.  
  11239.              Every time you have used show_regs, both CS the code segment
  11240.              address and IP the instruction pointer have been displayed. What
  11241.              does IP do? When the 8086 is ready to execute an instruction, it
  11242.              takes IP, adds it to CS to calculate the total address, and gets
  11243.              the instruction at that address. It then immediately figures out
  11244.              how long the instruction is going to be and adds that amount to
  11245.              IP.{4} What this means is that at any time, IP points to the NEXT
  11246.              instruction, not the current instruction. When you execute a
  11247.              call, the 8086 changes IP to point to the first byte of the
  11248.              called subroutine, so the next instruction executed is the first
  11249.              byte of the called subroutine.
  11250.  
  11251.              There are two different types of procedures, near procedures and
  11252.              far procedures. In a near procedure, you keep CS, the code
  11253.              segment register, the same. In a far procedure you change CS. So,
  11254.              when you call a near procedure you change one thing (IP) and in a
  11255.              far procedure you change two things (IP and CS).
  11256.  
  11257.              When you want to get back from the subroutine, you need to have
  11258.              CS with the segment of the calling routine and IP with the
  11259.              address of the instruction after the call. What are the mechanics
  11260.              of all this? Let's take a near procedure first.
  11261.  
  11262.              In a near call, the 8086 first changes the instruction pointer to
  11263.              point to the next instruction. It then pushes IP on the stack,
  11264.              and puts the address of the called subroutine (which is in bytes
  11265.              2 and 3 of the call instruction) in IP. IP now points to the
  11266.              called subroutine. There is one more word (2 bytes) on the stack.
  11267.              At the end of the called subroutine, a NEAR return (ret) pops the
  11268.              top word off the stack into IP. IP then points to the instruction
  11269.              after the call instruction.
  11270.  
  11271.              In a far call, the 8086 first changes the instruction pointer
  11272.              (IP) to point to the next instruction. It then pushes CS on the
  11273.              stack, followed by IP. It then loads the offset address of the
  11274.              called subroutine in IP and the segment address of the called
  11275.              subroutine in CS. This new IP is in bytes 2 and 3 of the call
  11276.              instruction and the new CS is in bytes 4 and 5 of the call
  11277.              ____________________
  11278.  
  11279.                 3. You C fanatics will notice that there are some initial
  11280.              underscores missing. Let's not confuse the issue.
  11281.  
  11282.                 4. Instructions can vary from one byte long to six bytes long,
  11283.              and the 8086 can tell from the first (or first and second)
  11284.              byte(s) how long the total instruction will be.
  11285.  
  11286.  
  11287.              The PC Assembler Tutor                                        146
  11288.              ______________________
  11289.  
  11290.              instruction. IP and CS now have the address of the called
  11291.              subroutine. The stack has two words (4 bytes) more on the stack.
  11292.              The old IP is the stack top and the old CS is next on the stack.
  11293.              At the end of the subroutine, a FAR return (ret) pops the stack
  11294.              top into IP, then pops the next stack item into CS. Now IP and CS
  11295.              point to the instruction after the call instruction.
  11296.  
  11297.              These are two different types of call and they have two different
  11298.              machine codes. These are two different types of returns and they
  11299.              have two different machine codes.
  11300.  
  11301.  
  11302.                  MACHINE CODE                  ASSEMBLER INSTRUCTIONS
  11303.  
  11304.                                                ; a far routine
  11305.                                                ;-----
  11306.                                                far_routine proc far
  11307.                    CB                            ret
  11308.                                                far_routine endp
  11309.                                                ;-----
  11310.  
  11311.                                                ; a near routine
  11312.                                                ;-----
  11313.                                                near_routine proc near
  11314.                    C3                            ret
  11315.                                                near_routine endp
  11316.                                                ;-----
  11317.  
  11318.  
  11319.                                                  ; a near and far call
  11320.                     E8 0A43 R                    call near_routine
  11321.                     9A 015C ---- R               call far_routine
  11322.  
  11323.  
  11324.              The machine code for a near return is C3; for a far return it's
  11325.              CB. The machine code for a near call is E8; for a far call it's
  11326.              9A. The near call has the address of the called routine (0A43h)
  11327.              in the following two bytes. The far call has the address of the
  11328.              the called routine (015Ch) in the next two bytes followed by the
  11329.              segment of the called routine. The segment address isn't there
  11330.              yet. It will be put there by the linker and loader, but the
  11331.              assembler has saved the space for the address. That's why the
  11332.              dashes are there. Remember, the R is there because those
  11333.              addresses might be relocated by the linker or the loader.
  11334.  
  11335.              You tell the assembler whether to code a near return or far
  11336.              return by telling it whether it is a near or a far procedure.
  11337.  
  11338.                  routine1  proc near
  11339.                  routine2  proc far
  11340.  
  11341.              How does the assembler know whether to code a near or far call?
  11342.              If it has already seen the procedure, it knows what type it is.
  11343.  
  11344.  
  11345.              Chapter 15 - Subroutines                                      147
  11346.              ________________________
  11347.  
  11348.              If it hasn't seen it yet, it uses the default type.{5} If it is
  11349.              an external subroutine, the assembler knows because you have
  11350.              written an EXTRN statement.
  11351.  
  11352.                  EXTRN  routine3:NEAR, routine4:FAR
  11353.  
  11354.              This EXTRN statement should appear before the call.
  11355.  
  11356.              What if the routine appears after the call in the source file but
  11357.              it isn't the default type? You can override the default type.
  11358.  
  11359.                  call NEAR PTR routine5
  11360.                  call FAR PTR routine6
  11361.  
  11362.              This is the same cumbersome syntax that we had with pointers to
  11363.              data, but it's the only game in town. Normally, if the subroutine
  11364.              appears after the call, you don't need to do anything if it is a
  11365.              near call but you need to put a FAR PTR override if it is a far
  11366.              call.
  11367.  
  11368.              ____________________
  11369.  
  11370.                 5. The default is near for what we are doing. However,
  11371.              Microsoft has something called "simplified" directives and the
  11372.              default changes in these cases.
  11373.  
  11374.  
  11375.  
  11376.  
  11377.              The PC Assembler Tutor                                        148
  11378.              ______________________
  11379.  
  11380.  
  11381.              THE STACK
  11382.  
  11383.              Up to this time we have used the stack for temporary storage. If
  11384.              you want to temporarily save either a register or a value in
  11385.              memory, you push it:
  11386.  
  11387.                  push ax
  11388.                  push variable1
  11389.  
  11390.              and if you want to get them back you pop them:
  11391.  
  11392.                  pop  variable1
  11393.                  pop  ax
  11394.  
  11395.              This is always a word (2 bytes) at a time. When you pop the
  11396.              stack, the 8086 gives you back the words in reverse order. Thus
  11397.              if you push the following:
  11398.  
  11399.                  push variable1
  11400.                  push variable2
  11401.                  push variable3
  11402.                  push variable4
  11403.                  push variable5
  11404.  
  11405.              then in order to get the data back in the same place, you need to
  11406.              pop in this order:
  11407.  
  11408.                  pop variable5
  11409.                  pop variable4
  11410.                  pop variable3
  11411.                  pop variable2
  11412.                  pop variable1
  11413.  
  11414.              It pops the last thing that was pushed that hasn't been popped
  11415.              yet.
  11416.  
  11417.              Nothing has been said about where the stack is or how it
  11418.              operates. It's time to change that. When the operating system
  11419.              starts a program, it looks for a stack segment. If the stack
  11420.              segment has been properly defined, the operating system puts the
  11421.              stack segment's segment address in SS (the stack segment
  11422.              register) and sets SP (the stack pointer) to point to the first
  11423.              byte AFTER the end of the stack segment. Exactly where this is
  11424.              depends on how large you have defined your stack segment. SS and
  11425.              SP are set, and there is nothing on the stack.
  11426.  
  11427.              When you push something:
  11428.  
  11429.                  push dx
  11430.  
  11431.              the 8086 subtracts 2 from SP (making one word of space) and puts
  11432.              that thing at the new address in SP. SP contains the address of
  11433.  
  11434.              ______________________
  11435.  
  11436.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  11437.  
  11438.  
  11439.              Chapter 15 - Subroutines                                      149
  11440.              ________________________
  11441.  
  11442.              the last thing pushed.
  11443.  
  11444.              This means that SP is decreasing, and the stack segment is
  11445.              filling up from back to front. In the topsy-turvy world of
  11446.              stacks, when you put things on the stack, the stack grows
  11447.              downward. What makes things especially confusing is that many
  11448.              book writers will picture a stack:
  11449.  
  11450.                              variable1
  11451.                              variable2
  11452.                              ax
  11453.                              dx
  11454.  
  11455.              and not bother to tell you whether the stack is growing upwards
  11456.              or downwards or where the stack top is. In this book, the stack
  11457.              TOP will always be visually on the BOTTOM. High addresses will be
  11458.              visually up and low addresses will be visually down. You need to
  11459.              get used to SP decreasing as the stack gets larger, and this is
  11460.              the easiest way to do it. So, if you have the instructions:
  11461.  
  11462.                       push ax
  11463.                       push variable1
  11464.                       push si
  11465.                       push di
  11466.  
  11467.              after these instructions, the stack will look like this:
  11468.  
  11469.                            VALUE               ADDRESS
  11470.  
  11471.                            ax                  sp + 6
  11472.                            variable1           sp + 4
  11473.                            si                  sp + 2
  11474.                  sp ->     di                  sp + 0
  11475.  
  11476.              When you pop a value, the 8086 moves the word (2 bytes) at SP to
  11477.              the appropriate location and INCREMENTS SP by 2.
  11478.  
  11479.                  pop  di
  11480.  
  11481.              You would now have:
  11482.  
  11483.                            VALUE               ADDRESS
  11484.  
  11485.                            ax                  sp + 4
  11486.                            variable1           sp + 2
  11487.                  sp ->     si                  sp + 0
  11488.  
  11489.              As long as you are just using PUSH and POP, this is entirely self
  11490.              regulating. SS is set, and SP is modified by the 8086 without you
  11491.              doing anything. It is now time to get more sophisticated.
  11492.  
  11493.              In our C example:
  11494.  
  11495.                  my_procedure (variable1, variable2, variable3) ;
  11496.  
  11497.              we generated the code:
  11498.  
  11499.  
  11500.  
  11501.              The PC Assembler Tutor                                        150
  11502.              ______________________
  11503.  
  11504.                  push variable3
  11505.                  push variable2
  11506.                  push variable1
  11507.                  call my_procedure
  11508.  
  11509.              What will the stack look like upon entry to my_procedure? That
  11510.              depends on whether my_procedure is a near procedure or a far
  11511.              procedure. If it is a near procedure, you will have:
  11512.  
  11513.  
  11514.                            VALUE               ADDRESS
  11515.  
  11516.                            variable3           sp + 6
  11517.                            variable2           sp + 4
  11518.                            variable1           sp + 2
  11519.                  sp ->     old IP              sp + 0
  11520.  
  11521.              If it is a far procedure, you will have:
  11522.  
  11523.                            VALUE               ADDRESS
  11524.  
  11525.                            variable3           sp + 8
  11526.                            variable2           sp + 6
  11527.                            variable1           sp + 4
  11528.                            old CS              sp + 2
  11529.                  sp ->     old IP              sp + 0
  11530.  
  11531.              Therefore, the variables are in different places relative to SP
  11532.              depending on whether it is a near or a far procedure. All
  11533.              examples will be with near procedures, but they are all valid for
  11534.              far procedures if you adjust for having the old CS on the stack.
  11535.  
  11536.              How do we access these variables? By using a pointer. We could
  11537.              use BX, SI or DI, but they have DS, not SS as their natural
  11538.              segment register. The only pointer with SS as the natural segment
  11539.              register is BP, the base pointer. Since we are going to use BP,
  11540.              we need to push its current value in order to save it:
  11541.  
  11542.                  push bp
  11543.  
  11544.              The stack now looks like this:
  11545.  
  11546.                            VALUE               ADDRESS
  11547.  
  11548.                            variable3           sp + 8
  11549.                            variable2           sp + 6
  11550.                            variable1           sp + 4
  11551.                            old IP              sp + 2
  11552.                  sp ->     old bp              sp + 0
  11553.  
  11554.              This is the standard way to do it and this is what the stack
  11555.              always looks like if you follow the standard method. The standard
  11556.              code for setting up the stack for access is:
  11557.  
  11558.                  push bp
  11559.                  mov  bp, sp
  11560.  
  11561.  
  11562.  
  11563.              Chapter 15 - Subroutines                                      151
  11564.              ________________________
  11565.  
  11566.              We give BP the same value as SP, so BP also points to the top of
  11567.              the stack and we use BP instead of SP. We now have:
  11568.  
  11569.                            VALUE               ADDRESS
  11570.  
  11571.                            variable3           bp + 8
  11572.                            variable2           bp + 6
  11573.                            variable1           bp + 4
  11574.                            old IP              bp + 2
  11575.                  bp ->     old bp              bp + 0
  11576.  
  11577.              Now, if you want to push and pop things, you can do it to your
  11578.              heart's content. BP will always point to the set of data that you
  11579.              want to work with. Let's take the average of the three variables,
  11580.              and print it.
  11581.  
  11582.                  mov  ax, [bp+4]     ; add the three numbers
  11583.                  add  ax, [bp+6]
  11584.                  add  ax, [bp+8]
  11585.                  mov  dx, 0          ; prepare dx for division
  11586.                  mov  bx, 3          ; unsigned divide by 3
  11587.                  div  bx
  11588.                  call print_unsigned      ; result is in ax
  11589.  
  11590.              We are using AX, BX, and DX, so we need to push them before doing
  11591.              this:
  11592.  
  11593.                  push ax
  11594.                  push bx
  11595.                  push dx
  11596.  
  11597.              After we are done we need to (1) pop the registers and (2)
  11598.              restore BP. This is also a pop.
  11599.  
  11600.                  pop  dx
  11601.                  pop  bx
  11602.                  pop  ax
  11603.                  pop  bp
  11604.                  ret
  11605.  
  11606.              The whole subprogram now looks like this
  11607.  
  11608.              ;-----
  11609.              my_procedure proc near
  11610.  
  11611.                  push bp             ; set up base pointer
  11612.                  mov  bp, sp
  11613.                  push ax             ; push registers
  11614.                  push bx
  11615.                  push dx
  11616.                  mov  ax, [bp+4]     ; add the three numbers
  11617.                  add  ax, [bp+6]
  11618.                  add  ax, [bp+8]
  11619.                  mov  dx, 0          ; prepare dx for division
  11620.                  mov  bx, 3          ; unsigned divide by 3
  11621.                  div bx
  11622.                  call print_unsigned      ; result is in ax
  11623.  
  11624.  
  11625.              The PC Assembler Tutor                                        152
  11626.              ______________________
  11627.  
  11628.                  pop  dx             ; pop registers
  11629.                  pop  bx
  11630.                  pop  ax
  11631.                  pop  bp             ; restore old base pointer
  11632.                  ret
  11633.  
  11634.              my_procedure endp
  11635.              ;------
  11636.  
  11637.              There is only one more improvement to make. If you look at the
  11638.              code, it is not clear what [bp+4] refers to. We know where it is,
  11639.              but what is it? Therefore, we will always use EQU statements to
  11640.              give names to our stack variables. It will be clearer, and if you
  11641.              need to change the code, it is much easier to change the EQU
  11642.              definition than to change the stack references in the code. As
  11643.              usual, we follow the C convention and put EQU names in capital
  11644.              letters.
  11645.  
  11646.              ;-----
  11647.              my_procedure proc near
  11648.  
  11649.                  VAR1 EQU [bp+4]
  11650.                  VAR2 EQU [bp+6]
  11651.                  VAR3 EQU [bp+8]
  11652.  
  11653.                  push bp             ; set up base pointer
  11654.                  mov  bp, sp
  11655.                  push ax             ; push registers
  11656.                  push bx
  11657.                  push dx
  11658.                  mov  ax, VAR1       ; add the three numbers
  11659.                  add  ax, VAR2
  11660.                  add  ax, VAR3
  11661.                  mov  dx, 0          ; prepare dx for division
  11662.                  mov  bx, 3          ; divide by 3
  11663.                  div bx
  11664.                  call print_unsigned      ; result is in ax
  11665.                  pop  dx             ; pop registers
  11666.                  pop  bx
  11667.                  pop  ax
  11668.                  pop  bp             ; restore old base pointer
  11669.                  ret
  11670.  
  11671.              my_procedure endp
  11672.              ;------
  11673.  
  11674.              This is a simple example, so it doesn't look that important to
  11675.              use the EQU statements. Just wait till you have more complex
  11676.              subroutines. By the way, this program does no error checking. (If
  11677.              the sum is > 65535 it will give the wrong answer).
  11678.  
  11679.              There is still one thing to do. When we called the subroutine, we
  11680.              pushed the variables on the stack:
  11681.  
  11682.                  push variable3
  11683.                  push variable2
  11684.                  push variable1
  11685.  
  11686.  
  11687.              Chapter 15 - Subroutines                                      153
  11688.              ________________________
  11689.  
  11690.                  call my_procedure
  11691.  
  11692.              We now want to take them off. Do we need to pop them? No, this is
  11693.              trash so they go into the Great Bit Bucket. There are two ways of
  11694.              doing this, and this is language dependent.{1} In C, it is the
  11695.              STANDARD that the calling routine takes them off, and it is done
  11696.              this way:
  11697.  
  11698.                  push variable3
  11699.                  push variable2
  11700.                  push variable1
  11701.                  call my_procedure
  11702.                  add  sp, 6          ; 3 variables = 6 bytes
  11703.  
  11704.              we simply INCREASE sp by the number of bytes that we pushed on
  11705.              the stack. Whoof, they're gone.
  11706.  
  11707.              If you use PASCAL or FORTRAN, then the CALLED routine must take
  11708.              the variables off the stack on return. How does it do that? There
  11709.              is yet another type of return statement:
  11710.  
  11711.                  ret (6)             ; 3 variables = 6 bytes {2}
  11712.  
  11713.              causes the 8086 to increase sp by 6 as the last thing it does
  11714.              before returning from the subroutine. Which method you use is
  11715.              decided by which high-level language you are using.
  11716.  
  11717.  
  11718.                  MACHINE CODE        ASSEMBLER INSTRUCTIONS
  11719.  
  11720.                                 ;-----
  11721.                                      far_routine proc far
  11722.                     CA 001A               ret (26)     ; hex 1A
  11723.                     CB                    ret
  11724.                                      far_routine endp
  11725.                                 ;-----
  11726.  
  11727.                                 ;-----
  11728.                                      near_routine proc near
  11729.                     C2 002C               ret (44)     ; hex 2C
  11730.                     C3                    ret
  11731.                                      near_routine endp
  11732.                                 ;-----
  11733.  
  11734.              Here are the four different types of returns along with the
  11735.              machine code. Notice that the returns which increment the stack
  11736.              have the increment count coded in the machine code.
  11737.  
  11738.  
  11739.              You may have noticed that even in this first subroutine, pushing
  11740.              and popping the registers takes a lot of space. It is fairly
  11741.              ____________________
  11742.  
  11743.              1 And a major reason that is a real pain in keester to have
  11744.              multi-language programs.
  11745.  
  11746.              2 The parentheses are not necessary.
  11747.  
  11748.  
  11749.              The PC Assembler Tutor                                        154
  11750.              ______________________
  11751.  
  11752.              normal to use 6 registers in a subroutine. This means that you
  11753.              will need to write:
  11754.  
  11755.                  push ax
  11756.                  push bx
  11757.                  push cx
  11758.                  push dx
  11759.                  push si
  11760.                  push di
  11761.  
  11762.              at the beginning of the subroutine and:
  11763.  
  11764.                  pop  di
  11765.                  pop  si
  11766.                  pop  dx
  11767.                  pop  cx
  11768.                  pop  bx
  11769.                  pop  ax
  11770.  
  11771.              before returning. This is a lot of space and it gets boring.
  11772.              Also, you have to remember to pop in the exact reverse order or
  11773.              you will screw things up. Fortunately we have two macros to help
  11774.              us. The file  PUSHREGS.MAC has two macros, one called PUSHREGS
  11775.              and the other called POPREGS.
  11776.  
  11777.              A macro is a set of directions for generating additional
  11778.              assembler code before the file is assembled. That's why it is
  11779.              called the Microsoft Macro Assembler. You include \pushregs.mac
  11780.              at the beginning of the file, and then everytime the assembler
  11781.              sees the word PUSHREGS followed by register names it generates
  11782.              push instructions. Every time the assembler sees POPREGS followed
  11783.              by register names, it generates pop instructions. It generates
  11784.              actual text which is assembled later.
  11785.  
  11786.              The form for generating those push instructions above is:
  11787.  
  11788.                  PUSHREGS ax, bx, cx, dx, si, di
  11789.  
  11790.              the word PUSHREGS followed by the registers separated by commas.
  11791.              (Make sure there is no comma after the last register). This must
  11792.              all be on one line. PUSHREGS pushes the registers in left to
  11793.              right order.
  11794.  
  11795.              The form for generating those pop instructions above is
  11796.  
  11797.                  POPREGS ax, bx, cx, dx, si, di
  11798.  
  11799.              The registers will be popped in the REVERSE order to the way they
  11800.              are listed on the line, that is, in RIGHT TO LEFT order.
  11801.  
  11802.              Notice that the order of registers is the same for both PUSHREGS
  11803.              and POPREGS. This is so that you may write the push part:
  11804.  
  11805.                  PUSHREGS ax, bx, cx, dx, si, di
  11806.  
  11807.              and then use your word processor to copy the line to the end of
  11808.              the subroutine, changing PUSHREGS to POPREGS. This insures that
  11809.  
  11810.  
  11811.              Chapter 15 - Subroutines                                      155
  11812.              ________________________
  11813.  
  11814.              the pushes and pops will be in exact reverse order. It saves
  11815.              space and time, and it generates exactly the same code as if you
  11816.              had written all those pushes and pops in the code. Whenever we
  11817.              have subroutines in the future, we will always use it.
  11818.  
  11819.  
  11820.              MOVING A STRING
  11821.  
  11822.              As a final example, we will create a subroutine that moves a
  11823.              Pascal string from one place to another. We'll assume that both
  11824.              strings are in the current DS, so no segments need to be changed.
  11825.  
  11826.                  move_string ( from_string, to_string ) ;
  11827.  
  11828.              where from_string and to_string are the ADDRESSES of the strings.
  11829.              The code generated by the Pascal compiler will be:
  11830.  
  11831.                  mov  ax, offset from_string
  11832.                  push ax
  11833.                  mov  ax, offset to_string
  11834.                  push ax
  11835.                  call move_string
  11836.                  ; this is Pascal, so the CALLED subroutine must
  11837.                  ; get rid of the variables on the stack.
  11838.  
  11839.              Notice that Pascal pushes this data in left to right order,
  11840.              exactly the opposite of how C would handle it. After setting up
  11841.              BP, we have:
  11842.  
  11843.                            from_string offset       bp + 6
  11844.                            to_string offset         bp + 4
  11845.                            old IP                   bp + 2
  11846.                  bp ->     old bp                   bp + 0
  11847.  
  11848.              Before coding this, you need to know the structure of a Pascal
  11849.              text string. The first byte (string[0]) is not text, but the text
  11850.              count. The second byte is the first piece of text. This means two
  11851.              things. First, the maximum string size in Pascal is 255, the
  11852.              largest count that will fit in one byte. Second, you need to move
  11853.              'count + 1' bytes. 'count' is how many text bytes there are, but
  11854.              then you need to move the count itself. If the string is empty
  11855.              (count = 0) you need to move 1 byte, the count byte. Here's the
  11856.              code
  11857.  
  11858.              ; - - - - -
  11859.              move_string   proc near
  11860.  
  11861.                  FROM_ADDRESS  EQU  [bp + 6]
  11862.                  TO_ADDRESS    EQU  [bp + 4]
  11863.  
  11864.                  push bp             ; set up bp
  11865.                  mov  bp, sp
  11866.                  PUSHREGS  ax, cx, si, di
  11867.  
  11868.                  mov  si, FROM_ADDRESS    ; source
  11869.                  mov  di, TO_ADDRESS      ; destination
  11870.                  sub  cx, cx              ; zero cx
  11871.  
  11872.  
  11873.              The PC Assembler Tutor                                        156
  11874.              ______________________
  11875.  
  11876.                  mov  cl, [si]            ; text byte count of source
  11877.                  inc  cl                  ; add 1 for byte count itself
  11878.  
  11879.              move_loop:
  11880.                  mov  al, [si]            ; source to al
  11881.                  mov  [di], al            ; al to destination
  11882.                  inc  si                  ; move pointers to next byte
  11883.                  inc  di
  11884.                  loop move_loop
  11885.  
  11886.                  POPREGS  ax, cx, si, di
  11887.                  pop  bp
  11888.                  ret (4)                  ; Pascal, so pop offsets.
  11889.  
  11890.              move_string  endp
  11891.              ; - - - - - - -
  11892.  
  11893.              We still have some more to do, and we'll do it in part three of
  11894.              the chapter.
  11895.  
  11896.  
  11897.  
  11898.  
  11899.              Chapter 15 - Subroutines                                      157
  11900.              ________________________
  11901.  
  11902.              What if both strings are in memory but we don't know which
  11903.              segments they are in? In that case, the calling subroutine needs
  11904.              to pass both the segment and the offset for both the from_string
  11905.              and the to_string. Let's do this in C.
  11906.  
  11907.                  move_string ( from_string, to_string ) ;
  11908.  
  11909.              In C, this will pass the addresses of the arrays, not the arrays
  11910.              themselves. C, once again, pushes these variables in right to
  11911.              left order. If the compiler is set up to move both the segment
  11912.              and the offset, then the generated C code will be:
  11913.  
  11914.                  mov  ax, seg to_string
  11915.                  push ax
  11916.                  mov  ax, offset to_string
  11917.                  push ax
  11918.                  mov  ax, seg from_string
  11919.                  push ax
  11920.                  mov  ax, offset to_string
  11921.                  push ax
  11922.                  call move_string
  11923.                  add  sp, 8                    ; 4 pushes = 8 bytes
  11924.  
  11925.              On the 8086, the low two bytes are ALWAYS the offset and the high
  11926.              two bytes are ALWAYS the segment. Remember, SEG gets the segment
  11927.              starting address of the named variable.
  11928.  
  11929.              We will do the subroutine as a near routine. After setting up BP,
  11930.              we will have:
  11931.  
  11932.                            to_string segment        bp + 10
  11933.                            to_string offset         bp + 8
  11934.                            from_string segment      bp + 6
  11935.                            from_string offset       bp + 4
  11936.                            old IP                   bp + 2
  11937.                  bp   ->   old bp                   bp + 0
  11938.  
  11939.              In the subroutine we will have to move the segment and the offset
  11940.              for each pointer. Luckily for us, there are two 8086 instructions
  11941.              for doing this:
  11942.  
  11943.                  LDS (load DS) loads the first two bytes into the named
  11944.                  register and the next two bytes into DS.
  11945.  
  11946.                  LES (load ES) loads the first two bytes into the named
  11947.                  register and the next two bytes into ES.
  11948.  
  11949.              If we write:
  11950.  
  11951.                       LES  di, [bp + 8]
  11952.  
  11953.              then the 8086 will load the first two bytes (bp+8 and bp+9) into
  11954.              DI and the next two bytes (bp+10 and bp+11) into ES. This loads
  11955.  
  11956.              ______________________
  11957.  
  11958.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  11959.  
  11960.  
  11961.              The PC Assembler Tutor                                        158
  11962.              ______________________
  11963.  
  11964.              the offset and the segment in the same instruction. If we write:
  11965.  
  11966.                       LDS  si, [bp + 4]
  11967.  
  11968.              the 8086 will load the first two bytes (bp+4 and bp+5) into SI
  11969.              and the next two bytes (bp+6 and bp+7) into ES, loading both the
  11970.              offset and segment in one instruction.
  11971.  
  11972.              LDS and LES allow you to load the offset into any full arithmetic
  11973.              register (AX, BX, CX, DX, SI, DI, BP or SP) but you can't use AX,
  11974.              CX or DX as addressing registers, so it only makes sense to load
  11975.              BX, SI, DI and BP for use as pointers. The two strings will now
  11976.              be addressed by DS:SI and ES:DI. DS is SI's normal segment, so we
  11977.              don't need to do anything, but we need a segment override for
  11978.              ES:DI. Here is the code for a C subroutine:
  11979.  
  11980.              A C string ends with a 0 byte, that is with a byte having the
  11981.              numeric value 0. It can be any length, but we need to test each
  11982.              byte to find out if it is 0.
  11983.  
  11984.              Notice that we are using (and changing) both DS and ES this time,
  11985.              so we have to PUSH and POP them, just like other registers.
  11986.  
  11987.              ; - - - - -
  11988.              move_string   proc near
  11989.  
  11990.                  FROM_POINTER  EQU  [bp+4]
  11991.                  TO_POINTER    EQU  [bp+8]
  11992.  
  11993.                  push bp
  11994.                  mov  bp, sp
  11995.                  PUSHREGS  ds, es, ax, si, di
  11996.  
  11997.                  lds  si, FROM_POINTER
  11998.                  les  di, TO_POINTER
  11999.  
  12000.              move_loop
  12001.                  mov  al, [si]            ; source to al
  12002.                  mov  es:[di], al         ; al to destination
  12003.                  inc  si                  ; pointers to next byte
  12004.                  inc  di
  12005.                  and  al, al              ; is al 0?
  12006.                  jnz  move_loop
  12007.  
  12008.                  POPREGS  ds, es, ax, si, di
  12009.                  pop  bp
  12010.                  ret                      ; calling routine pops variables
  12011.  
  12012.              move_string  endp
  12013.              ; - - - - -
  12014.  
  12015.              Basically, the only difference between this and the Pascal move
  12016.              is that (1) here we check for 0 and there we had an actual count,
  12017.              and (2) in Pascal we used "ret (4)" and here the calling routine
  12018.              does the adjustment.
  12019.  
  12020.  
  12021.  
  12022.  
  12023.              Chapter 15 - Subroutines                                      159
  12024.              ________________________
  12025.  
  12026.              DRAWING THE STACK
  12027.  
  12028.              Each time that we have used the stack we have drawn a picture of
  12029.              where everything is on the stack. In case you think that this is
  12030.              some trivial little learning technique, I'm telling you now that
  12031.              at the assembler level, if you are passing variables on the stack
  12032.              and you don't make a diagram on a piece of paper of where
  12033.              everything is, you are guaranteed to consistently reference
  12034.              things by the wrong address. ALWAYS make a paper diagram that
  12035.              includes BP, ALWAYS use EQU statements, and you'll avoid a lot of
  12036.              mistakes.
  12037.  
  12038.  
  12039.  
  12040.              It is now time to get more complex. Everything that follows is
  12041.              more advanced, so it requires some programming experience.
  12042.              Everything that follows is about C modules and recursion. If this
  12043.              gets too complicated or obscure, just skip to the summary at the
  12044.              end of the chapter.
  12045.  
  12046.  
  12047.              I am going to give you a sample subroutine in C and then show you
  12048.              where all the variables go. The following is a complete C file:
  12049.  
  12050.              /* a complete C file  - - - - - - - - - - - */
  12051.  
  12052.              int            A, B ;
  12053.              static int     C, D ;
  12054.              extern int     E, F ;
  12055.  
  12056.              sample_routine ( G, H )
  12057.              int G, *H ;
  12058.              {
  12059.                  int            I, J ;
  12060.                  static int     K, L ;
  12061.  
  12062.                  A = I ;   /* transfer the words around */
  12063.                  B = G ;
  12064.                  J = E ;
  12065.                  F = C ;
  12066.                  D = K ;
  12067.                  *H = I ;
  12068.  
  12069.                  return ;
  12070.              }
  12071.  
  12072.              /* end of C file   - - - - - - - - - - - - - */
  12073.  
  12074.              The only thing this routine does is tranfer the words around.
  12075.              This is so you can see where things are stored and how they are
  12076.              accessed. If you don't know C, the only thing you really need to
  12077.              know here is that '*H' means that 'H' is the ADDRESS of an
  12078.              integer, not the integer itself, while '*H' is the actual integer
  12079.              that is being addressed.
  12080.  
  12081.              For those of you who DO know C, you need to know exactly what
  12082.              extern, static and static mean. extern means that it is in an
  12083.  
  12084.  
  12085.              The PC Assembler Tutor                                        160
  12086.              ______________________
  12087.  
  12088.              external file. The first 'static' (which is outside of any
  12089.              subroutine) means that the data is INTERNAL to the file; it won't
  12090.              be shared with other files. Variables A and B, which don't have
  12091.              the word 'static' are GLOBAL and will be shared with other files.
  12092.              The other 'static' (inside the subroutine) means that its address
  12093.              is fixed in memory while I and J, which are not 'static' have
  12094.              their addresses generated every time you call the program. Say
  12095.              what? Yes, that's correct, every time you call the program they
  12096.              can be in a different place. That's what allows recursion, and
  12097.              you'll see how that is implemented in a second.
  12098.  
  12099.              Here is the equivalent program in assembler:
  12100.  
  12101.              ; - - - - - START DATA BELOW THIS LINE
  12102.                  PUBLIC A, B
  12103.                  EXTRN  E:WORD, F:WORD
  12104.  
  12105.              A   dw   ?
  12106.              B   dw   ?
  12107.              C   dw   ?
  12108.              D   dw   ?
  12109.              K   dw   ?
  12110.              L   dw   ?
  12111.              ; - - - - - END DATA ABOVE THIS LINE
  12112.  
  12113.              ; - - - - - START SUBROUTINE BELOW THIS LINE
  12114.              sample_routine proc near
  12115.  
  12116.                  ADDRESS_OF_H EQU [bp + 6]
  12117.                  G EQU  [bp + 4]
  12118.  
  12119.                  I EQU  [bp - 2]
  12120.                  J EQU  [bp - 4]
  12121.  
  12122.                  push bp
  12123.                  mov  bp, sp         ; set up bp
  12124.                  sub  sp, 4          ; save space for I and J
  12125.                  PUSHREGS ax, si
  12126.  
  12127.                  mov  ax, I          ; A = I
  12128.                  mov  A, ax
  12129.  
  12130.                  mov  ax, G          ; B = G
  12131.                  mov  B, ax
  12132.  
  12133.                  mov  ax, E          ; J = E
  12134.                  mov  J, ax
  12135.  
  12136.                  mov  ax, C          ; F = C
  12137.                  mov  F, ax
  12138.  
  12139.                  mov  ax, K          ; D = K
  12140.                  mov  D, ax
  12141.  
  12142.                  mov  ax, I          ; *H = I
  12143.                  mov  si, ADDRESS_OF_H
  12144.                  mov  [si], ax
  12145.  
  12146.  
  12147.              Chapter 15 - Subroutines                                      161
  12148.              ________________________
  12149.  
  12150.  
  12151.                  POPREGS  ax, si
  12152.                  mov  sp, bp         ; readjust sp
  12153.                  pop  bp
  12154.                  ret            ; a C routine so calling routine pops
  12155.  
  12156.              sample_routine endp
  12157.              ; - - - - - END SUBROUTINE ABOVE THIS LINE
  12158.  
  12159.              This time the setup is a little longer. It is:
  12160.  
  12161.                  push bp
  12162.                  mov  bp, sp
  12163.                  sub  sp, 4
  12164.                  PUSHREGS ax, si
  12165.  
  12166.              I and J are not in the data segment, so we need to make space for
  12167.              them somewhere, and we do it on the stack. We subtract 4 from sp
  12168.              to provide ourselves with 4 bytes, two for I and two for J. There
  12169.              are two EQU statements that say exactly where I and J will be. We
  12170.              also push AX and SI because we will use them. After this setup,
  12171.              we have:
  12172.  
  12173.                            address of H        bp + 6
  12174.                            G                   bp + 4
  12175.                            old IP              bp + 2
  12176.                  bp ->     old bp              bp + 0
  12177.                            I                   bp - 2
  12178.                            J                   bp - 4
  12179.                            old ax              bp - 6
  12180.                  sp ->     old si              bp - 8
  12181.  
  12182.              We have created space for some variables below bp. This is
  12183.              temporary and will disappear when we leave the subroutine. We do
  12184.              our dummy calculations,{1} and then do the end adjustment. There
  12185.              are two ways to readjust sp before returning:
  12186.  
  12187.                  add  sp, 4
  12188.  
  12189.              just adds the amount that we subtracted, so it winds up in the
  12190.              same place. But the place it winds up is ALWAYS the address of
  12191.              the old BP, and bp is now pointing to that, so
  12192.  
  12193.                  mov  sp, bp
  12194.  
  12195.              does exactly the same thing.
  12196.  
  12197.  
  12198.  
  12199.              ARE YOU REALLY DEMENTED?
  12200.  
  12201.              Yes, for those of you who are truly masochistic, we have -
  12202.              ta-dah! - the Towers of Hanoi in assembler language.
  12203.  
  12204.              ____________________
  12205.  
  12206.                 1. And that's 'dummy' in more ways than one.
  12207.  
  12208.  
  12209.              The PC Assembler Tutor                                        162
  12210.              ______________________
  12211.  
  12212.              The Towers of Hanoi is a game with three posts and a number of
  12213.              disks which have incrementally smaller diameters. At the
  12214.              beginning of the game, all the disks are on post one, ordered by
  12215.              size with the smallest on top and the largest on the bottom. For
  12216.              five disks it looks like this:
  12217.  
  12218.                               (1)              (2)              (3)
  12219.  
  12220.                                 X                X                X
  12221.                                 X                X                X
  12222.                               XXXXX              X                X
  12223.                              XXXXXXX             X                X
  12224.                             XXXXXXXXX            X                X
  12225.                            XXXXXXXXXXX           X                X
  12226.                           XXXXXXXXXXXXX          X                X
  12227.                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  12228.  
  12229.  
  12230.              The object is to wind up with all the disks on post three in the
  12231.              same order. The game has only one rule: You may never put a
  12232.              larger disk on top of a smaller disk.
  12233.  
  12234.              The general solution to this problem is that if you have posts A,
  12235.              B and C with N disks on post A and you want to move them to post
  12236.              C, first move (N-1) disks to post B, move the bottom disk from
  12237.              post A to post C, then move (N-1) disks from post B to post C.
  12238.              This works out to be the optimal recursive solution. Try it out
  12239.              with N = 1, N = 2 and N = 3. N = 4 is already complicated.
  12240.  
  12241.              I won't discuss the game further or how to get the solution. If
  12242.              you don't know about it, you can look it up in either Doug
  12243.              Cooper's "Oh! Pascal" or Robert Kruse's "Data Structures and
  12244.              Program Design".
  12245.  
  12246.              The fact that I need to resort to such an unususl example to
  12247.              illustrate recursion underlines the fundamental rule of
  12248.              recursion, which is:
  12249.  
  12250.                  YOU SELDOM NEED TO USE RECURSION, BUT WHEN YOU NEED IT YOU
  12251.                  REALLY REALLY NEED IT.
  12252.  
  12253.              First, here is the solution in C:
  12254.  
  12255.              /* the C solution - - - - - - - - - - - - - - - */
  12256.  
  12257.              #define MAXIMUM 9
  12258.  
  12259.              main ()
  12260.              {
  12261.                  int  count ;
  12262.  
  12263.                  while (1)
  12264.                  {
  12265.                       printf ("Enter a number less than 10.\n" ) ;
  12266.                       scanf ( "%d", &count ) ;
  12267.                       if ( count > MAXIMUM )
  12268.                            continue ;
  12269.  
  12270.  
  12271.              Chapter 15 - Subroutines                                      163
  12272.              ________________________
  12273.  
  12274.                       towers_of_hanoi ( count, 1, 3, 2 ) ;
  12275.                  }
  12276.              }
  12277.              /* - - - - - - - - - - - - - - - - - - - - - - - - - */
  12278.              towers_of_hanoi ( count, from, to, via )
  12279.              int  count, from, to, via ;
  12280.              {
  12281.                  if (count <= 0)
  12282.                       return ;
  12283.  
  12284.                  count-- ;
  12285.                  towers_of_hanoi ( count, from, via, to ) ;
  12286.                  printf ("Move a disk from %1d to %1d.\n" , from, to ) ;
  12287.                  towers_of_hanoi ( count, via, to, from ) ;
  12288.                  return ;
  12289.              }
  12290.  
  12291.  
  12292.              Notice that by letting a routine call itself, we have reduced it
  12293.              to just a few lines. And this is a problem that looks very
  12294.              complex. Here comes the assembler equivalent of the C code:
  12295.  
  12296.  
  12297.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  12298.  
  12299.                  MAX_COUNT  EQU   9
  12300.  
  12301.              enter_message        db  "Enter a number less than 10", 0
  12302.              make_a_move_message  db  "Move a disk from "
  12303.              from_byte            db  "X to "
  12304.              to_byte              db  "X.", 0
  12305.  
  12306.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  12307.  
  12308.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  12309.              outer_loop:
  12310.                     lea   ax, enter_message        ; count message
  12311.                     call  print_string
  12312.                     call  get_unsigned_byte        ; count in al
  12313.                     sub   ah, ah                   ; zero ah for 'push ax'
  12314.                     cmp   al, MAX_COUNT            ; too big?
  12315.                     ja    outer_loop
  12316.  
  12317.                     ; move from post 1 to post 3 via post 2
  12318.                     mov   bx, 2                    ; post 2 = 'via'
  12319.                     push  bx
  12320.                     mov   bx, 3                    ; post 3 = 'to'
  12321.                     push  bx
  12322.                     mov   bx, 1                    ; post 1 = 'from'
  12323.                     push  bx
  12324.                     push  ax                       ; al = count, ah = 0
  12325.                     call  towers_of_hanoi
  12326.                     add   sp, 8                    ; adjust the stack
  12327.                     jmp   outer_loop
  12328.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  12329.  
  12330.              ; + + + + + + + + + + + + START SUBROUTINES BELOW THIS LINE
  12331.  
  12332.  
  12333.              The PC Assembler Tutor                                        164
  12334.              ______________________
  12335.  
  12336.              towers_of_hanoi proc near
  12337.  
  12338.                     VIA   EQU  [bp + 10]
  12339.                     TO    EQU  [bp + 8]
  12340.                     FROM  EQU  [bp + 6]
  12341.                     COUNT EQU  [bp + 4]
  12342.  
  12343.                     push  bp                  ; set up bp
  12344.                     mov   bp, sp
  12345.                     push  ax
  12346.                     cmp   BYTE PTR COUNT, 0   ; if no disks, we are done
  12347.                     jbe   exit
  12348.  
  12349.                     dec   BYTE PTR COUNT      ; 1 less disk to move
  12350.  
  12351.                     ; first half
  12352.                     push  TO
  12353.                     push  VIA
  12354.                     push  FROM
  12355.                     push  COUNT
  12356.                     call  towers_of_hanoi
  12357.                     add   sp, 8               ; adjust the stack
  12358.  
  12359.                     ; print the message
  12360.                     mov   al, FROM            ; get 'from' number
  12361.                     add   al, '0'             ; convert to ascii
  12362.                     mov   from_byte, al       ; put into message
  12363.                     mov   al, TO              ; get 'to' number
  12364.                     add   al, '0'             ; convert to ascii
  12365.                     mov   to_byte, al         ; put into message
  12366.                     lea   ax, make_a_move_message
  12367.                     call  print_string
  12368.  
  12369.                     ; second half
  12370.                     push  FROM
  12371.                     push  TO
  12372.                     push  VIA
  12373.                     push  COUNT
  12374.                     call  towers_of_hanoi
  12375.                     add   sp, 8               ; adjust the stack
  12376.  
  12377.              exit:
  12378.                     pop   ax
  12379.                     pop   bp
  12380.                     ret
  12381.  
  12382.              towers_of_hanoi endp
  12383.              ; + + + + + + + + + + + + END SUBROUTINES ABOVE THIS LINE
  12384.  
  12385.  
  12386.              The main routine checks that the number you enter is not too big
  12387.              and then calls 'towers_of_hanoi'. After setting up BP, the stack
  12388.              looks like this:
  12389.  
  12390.  
  12391.                            VIA post       bp + 10
  12392.                            TO post        bp + 8
  12393.  
  12394.  
  12395.              Chapter 15 - Subroutines                                      165
  12396.              ________________________
  12397.  
  12398.                            FROM post      bp + 6
  12399.                            count          bp + 4
  12400.                            old IP         bp + 2
  12401.                  bp   ->   old bp         bp + 0
  12402.  
  12403.              We make some EQU statements to define where each variable is and
  12404.              set up BP. We are using only one register, so we have a single
  12405.              PUSH instead of using PUSHREGS. We then check to see if the count
  12406.              is 0. If it is we are done. If not, we decrement the count by 1
  12407.              which gives us 'count - 1' and divide the problem into three
  12408.              parts. Part 1 calls 'towers_of_hanoi' and moves 'count - 1' disks
  12409.              from 'from' to 'via'. Part 2 prints a message of where to move
  12410.              the bottom disk. It converts the post numbers into ascii and
  12411.              inserts them in the string where the 'X's are.{2}  Part 3 calls
  12412.              towers_of_hanoi again, this time moving the 'count - 1' disks
  12413.              from 'via' to 'to'. Assemble and link it with ASMHELP. When you
  12414.              run it, start with just 1 disk, then 2 then 3 etc. If you use the
  12415.              maximum number (9), it will print 511 lines. For N disks, you
  12416.              need (2 ** N) - 1 moves, so this gets very big very fast.
  12417.  
  12418.              If you still feel no need to draw a picture of the stack or to
  12419.              use EQU statements, why don't you try writing this subroutine
  12420.              using the actual pointer values, i.e:
  12421.  
  12422.                  push [bp + 6]
  12423.  
  12424.              and so on. See how long it takes and see how easy it is to read
  12425.              once it's done. Can you get it to work correctly?
  12426.  
  12427.              By the way, how large does the stack get? Well, if you raised the
  12428.              limit to 30 disks and entered 30, It would take your computer
  12429.              about a year, running 24 hours a day, to complete the solution
  12430.              (about 1 billion moves). The maximum stack size would be
  12431.              ((disks+1) * stack_use_per_disk). The extra 1 is for the calling
  12432.              routine. That is 31 * 14 bytes (including pushing IP, BP, and
  12433.              AX), or a mere 434 bytes.
  12434.  
  12435.  
  12436.  
  12437.  
  12438.  
  12439.  
  12440.  
  12441.  
  12442.  
  12443.  
  12444.  
  12445.  
  12446.  
  12447.  
  12448.  
  12449.  
  12450.  
  12451.              ____________________
  12452.  
  12453.                 2 It uses the fact that for a data declaration, a variable
  12454.              name has the address of the first piece of data on that line.
  12455.  
  12456.  
  12457.              The PC Assembler Tutor                                        166
  12458.              ______________________
  12459.  
  12460.                                       SUMMARY
  12461.  
  12462.  
  12463.              To call a procedure you use CALL with the procedure name:
  12464.  
  12465.                  call subroutine1
  12466.  
  12467.              Procedures may be either FAR or NEAR. If there is a mismatch
  12468.              between which type the assembler thinks it is and which type it
  12469.              really is, there will be an error, either at the assembler level
  12470.              for internal subroutines or at the linker level for external
  12471.              subroutines. You may override the default type for procedures
  12472.              with PTR:
  12473.  
  12474.                  call NEAR PTR subroutine5
  12475.                  call FAR PTR subroutine6
  12476.  
  12477.              This is normally needed if the procedure comes after the call in
  12478.              the file and is not the default type.
  12479.  
  12480.              To allow other files to use a procedure you declare it PUBLIC
  12481.              within its own segment.
  12482.  
  12483.                  PUBLIC  subroutine1
  12484.  
  12485.              To use a PUBLIC procedure from another file you declare it EXTRN,
  12486.              stating which type it is:
  12487.  
  12488.                  EXTRN  subroutine1:NEAR, subroutine2:FAR
  12489.  
  12490.              You should make this declaration in the code segment and you
  12491.              should make it before the procedure is referenced.
  12492.  
  12493.              A procedure is defined by giving a name followed by the word
  12494.              'proc' (procedure) followed by either FAR or NEAR
  12495.  
  12496.                  subroutine1 proc near
  12497.                  subroutine2 proc far
  12498.  
  12499.              and a procedure is ended by giving the procedure name followed by
  12500.              'endp' (end of procedure):
  12501.  
  12502.                  subroutine2 endp
  12503.  
  12504.              One procedure must be ended before another is begun.
  12505.  
  12506.  
  12507.              Data is normally passed from one procedure to another on the
  12508.              stack:
  12509.  
  12510.                  push variable1
  12511.                  push variable2
  12512.                  push variable3
  12513.                  call subroutine1
  12514.  
  12515.              If this is done, then the called procedure references this data
  12516.              by using BP, the base pointer. The standard setup code is:
  12517.  
  12518.  
  12519.              Chapter 15 - Subroutines                                      167
  12520.              ________________________
  12521.  
  12522.  
  12523.                  push bp             ; save old bp
  12524.                  mov  bp, sp         ; set bp to current top of stack
  12525.  
  12526.              What the stack looks like at this point depends on whether it is
  12527.              a near or a far procedure. For a near procedure, we have:
  12528.  
  12529.                            variable1           bp + 8
  12530.                            variable2           bp + 6
  12531.                            variable3           bp + 4
  12532.                            old IP              bp + 2
  12533.                  bp   ->   old BP              bp + 0
  12534.  
  12535.              For a far procedure we have:
  12536.  
  12537.                            variable1           bp + 10
  12538.                            variable2           bp + 8
  12539.                            variable3           bp + 6
  12540.                            old CS              bp + 4
  12541.                            old IP              bp + 2
  12542.                  bp   ->   old BP              bp + 0
  12543.  
  12544.              Although it is theoretically possible to access these variables
  12545.              by their pointer definition:
  12546.  
  12547.                       mov  ax, [bp + 10]
  12548.  
  12549.              It is much less error prone and much clearer to use EQU
  12550.              statements:
  12551.  
  12552.                       VAR1 EQU [bp + 10]
  12553.  
  12554.                       mov  ax, VAR1
  12555.  
  12556.              If you are writing a recursive procedure and you need temporary
  12557.              variables, you can allot space on the stack for these variables:
  12558.  
  12559.                       sub  sp, 6     ; room for 6 bytes of temp. variables
  12560.  
  12561.              This should be done before any other pushes are done:
  12562.  
  12563.                       push bp
  12564.                       mov  bp, sp
  12565.                       sub sp, 6
  12566.                       PUSHREGS ax, bx, cx, dx
  12567.  
  12568.  
  12569.              These variables should also be named with EQU statements, and as
  12570.              always, you should draw a picture of what is on the stack:
  12571.  
  12572.                            variable1           bp + 10
  12573.                            variable2           bp + 8
  12574.                            variable3           bp + 6
  12575.                            old CS              bp + 4
  12576.                            old IP              bp + 2
  12577.                  bp   ->   old BP              bp + 0
  12578.                            VAR4                bp - 2
  12579.  
  12580.  
  12581.              The PC Assembler Tutor                                        168
  12582.              ______________________
  12583.  
  12584.                            VAR5                bp - 4
  12585.                            VAR6                bp - 6
  12586.  
  12587.  
  12588.                       VAR4 EQU  [bp - 2]
  12589.                       VAR5 EQU  [bp - 4]
  12590.                       VAR6 EQU  [bp - 6]
  12591.  
  12592.              Data which is passed to the procedure is at a positive offset to
  12593.              BP while data that is created in the procedure is at a negative
  12594.              offset to BP.
  12595.  
  12596.              If you have created a data area for yourself on the stack, then
  12597.              you must eliminate it before leaving the procedure. There are two
  12598.              ways of doing this. One way is to add back what you have
  12599.              subtracted:
  12600.  
  12601.                       POPREGS ax, bx, cx, dx
  12602.                       add  sp, 6
  12603.                       pop  bp
  12604.                       ret
  12605.  
  12606.              The other way is to give SP the value in BP because this is the
  12607.              place where SP will wind up anyway:
  12608.  
  12609.                       POPREGS ax, bx, cx, dx
  12610.                       mov  sp, bp
  12611.                       pop  bp
  12612.                       ret
  12613.  
  12614.              Use whichever one is clearer to you.
  12615.  
  12616.  
  12617.              When you return from a procedure that has had data passed to it,
  12618.              the data must be taken off the stack. There are two ways of doing
  12619.              this. The C standard is that it is the calling program's
  12620.              responsibility to do this:
  12621.  
  12622.                  push variable1
  12623.                  push variable2
  12624.                  push variable3
  12625.                  call subroutine1
  12626.                  add  sp, 6          ; 3 pushes = 6 bytes
  12627.  
  12628.              The Pascal standard is that you take them off the stack on the
  12629.              return. There is a special return instruction for that:
  12630.  
  12631.                  ret (6)             ; 3 pushes = 6 bytes
  12632.  
  12633.  
  12634.              DATA
  12635.  
  12636.              Data can be made available to other files and data can be
  12637.              accessed from other files. To make data available, declare it
  12638.              PUBLIC:
  12639.  
  12640.                  PUBLIC variable1, variable2, variable3
  12641.  
  12642.  
  12643.              Chapter 15 - Subroutines                                      169
  12644.              ________________________
  12645.  
  12646.  
  12647.              To access PUBLIC data from other files, use an EXTRN statement
  12648.              which includes the data type:
  12649.  
  12650.                  EXTRN  variable7:BYTE, variable8:WORD, variable9:DWORD
  12651.                  EXTRN  variable10:QWORD, variable11:TBYTE
  12652.  
  12653.              This EXTRN statement must be in a segment which has the same
  12654.              ASSUME segment register as will be used when accessing the data.
  12655.              Normally this is DS, but it can be something else. For instance,
  12656.              if the above EXTRN statements were in MORESTUFF and you have:
  12657.  
  12658.                  ASSUME es:MORESTUFF
  12659.  
  12660.              then every time you access variable8:
  12661.  
  12662.                  mov  dx, variable8
  12663.  
  12664.              the assembler will code an ES segment override.
  12665.  
  12666.  
  12667.              PUSHREGS and POPREGS
  12668.  
  12669.              When writing a subroutine, you should always save any registers
  12670.              that you use by pushing them.
  12671.  
  12672.                  push ax
  12673.                  push bx
  12674.                  push cx
  12675.  
  12676.              They are then popped before returning
  12677.  
  12678.                  pop  cx
  12679.                  pop  bx
  12680.                  pop  ax
  12681.  
  12682.              In order to save a lot of lines of code, there are two macros,
  12683.              PUSHREGS and POPREGS. They are designed so you may use a word
  12684.              processor to copy them. PUSHREGS pushes in left to right order
  12685.              and POPREGS pops in right to left order:
  12686.  
  12687.                  PUSHREGS ax, bx, cx, dx
  12688.                  POPREGS ax, bx, cx, dx
  12689.  
  12690.              is a matched pair.
  12691.  
  12692.              LES and LDS
  12693.  
  12694.              LDS (load DS) loads the first two bytes into the named register
  12695.              and the next two bytes into DS. LES (load ES) loads the first two
  12696.              bytes into the named register and the next two bytes into ES.
  12697.  
  12698.                       les  si, [bp+6]
  12699.                       lds  di, [bp+10]
  12700. Chapter 16 - Long Signed Multiplication and Division
  12701. ====================================================
  12702.                                                                            170
  12703.  
  12704.  
  12705.  
  12706.              Now that you have some subroutines under your belt, it is time to
  12707.              get back to multiple word arithmetic. This was put on the back
  12708.              burner because we needed to negate long numbers and it is more
  12709.              efficient to do that as a subroutine. First, let's negate a long
  12710.              number and then put the parts together.
  12711.  
  12712.              To negate a number you complement it, then add 1. It looks like
  12713.              this:
  12714.  
  12715.                  NUMBER_LENGTH  EQU 4
  12716.  
  12717.                  variable1 dq   ?
  12718.  
  12719.                       mov  si, offset variable1
  12720.                       mov  cx, NUMBER_LENGTH
  12721.                  not_loop:
  12722.                       not  WORD PTR [si]
  12723.                       add  si, 2
  12724.                       loop not_loop
  12725.  
  12726.                       mov  si, offset variable1
  12727.                       mov  cx, NUMBER_LENGTH
  12728.                       stc                      ; set carry flag
  12729.                  add_loop:
  12730.                       adc  WORD PTR [si], 0
  12731.                       inc  si
  12732.                       inc  si
  12733.                       loop add_loop
  12734.  
  12735.              This is straightforward. First negate, then add 1. The first add
  12736.              will add 1 because the carry flag is set. If there is a carry
  12737.              out, it will be taken care of in the next word with ADC (we add
  12738.              nothing but the carry). We can make this more compact and
  12739.              efficient with:
  12740.  
  12741.                       mov  si, offset variable1
  12742.                       mov  cx, NUMBER_LENGTH
  12743.                       stc                      ; set carry flag
  12744.                  negate_loop:
  12745.                       not  WORD PTR [si]
  12746.                       adc  WORD PTR [si], 0
  12747.                       inc  si
  12748.                       inc  si
  12749.                       loop negate_loop
  12750.  
  12751.              Neither NOT nor INC effect CF, the carry flag, so the correct CF
  12752.              value will be propagated through the whole long number.
  12753.  
  12754.  
  12755.              When we do negation during our multiplication, the multiplicand
  12756.              will be a 4 word negation while the result will be a 5 word
  12757.  
  12758.              ______________________
  12759.  
  12760.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  12761.  
  12762.  
  12763.              Chapter 16 - Multiple Word Arithmetic III                     171
  12764.              _________________________________________
  12765.  
  12766.              negation, so we will pass the length as a parameter. The call in
  12767.              C would look like this:
  12768.  
  12769.                  negate_it ( &number, length ) ; {1}
  12770.  
  12771.              On entry, the stack will look like this:
  12772.  
  12773.                            length    bp + 6
  12774.                            address   bp + 4
  12775.                            old IP    bp + 2
  12776.                  bp   ->   old BP    bp + 0
  12777.  
  12778.              Here is the entire subroutine:
  12779.  
  12780.              ; - - - - - START SUBROUTINES BELOW THIS LINE
  12781.              negate_it proc near
  12782.  
  12783.                  NUMBER_LENGTH  EQU [bp+6]
  12784.                  NUMBER_ADDRESS EQU [bp+4]
  12785.  
  12786.                  push bp
  12787.                  mov  bp, sp
  12788.                  PUSHREGS cx, si
  12789.  
  12790.  
  12791.                  mov  si, NUMBER_ADDRESS
  12792.                  mov  cx, NUMBER_LENGTH
  12793.                  stc                      ; set carry flag
  12794.              negate_loop:
  12795.                  not  WORD PTR [si]
  12796.                  adc  WORD PTR [si], 0
  12797.                  inc  si
  12798.                  inc  si
  12799.                  loop negate_loop
  12800.  
  12801.                  POPREGS cx, si
  12802.                  pop  bp
  12803.                  ret                 ; calling routine adjusts stack
  12804.  
  12805.              negate_it endp
  12806.  
  12807.              ; - - - - - END SUBROUTINES ABOVE THIS LINE
  12808.  
  12809.              Well, so far we have the negation routine and the unsigned
  12810.              multiplication and division routines. What else is necessary?
  12811.              Only the main program, and here it is for multiplication:
  12812.  
  12813.              ; - - - - - START DATA BELOW THIS LINE
  12814.              multiplicand       dq   ?
  12815.              multiplier         dw   ?
  12816.              result             dt   ?
  12817.              result_sign_flag   db   ?
  12818.              ; - - - - - END DATA ABOVE THIS LINE
  12819.  
  12820.              ____________________
  12821.  
  12822.                 1. For you non-C people, the '&' stands for the address.
  12823.  
  12824.  
  12825.              The PC Assembler Tutor                                        172
  12826.              ______________________
  12827.  
  12828.              ; - - - - - START CODE BELOW THIS LINE
  12829.              outer_loop:
  12830.                  mov  result_sign_flag, 0      ; assume positive
  12831.                  mov  ax, offset multiplicand
  12832.                  call get_signed_8byte
  12833.  
  12834.                  test WORD PTR multiplicand + 6, 8000h   ; is it negative ?
  12835.                  jz   get_next_number
  12836.  
  12837.                  mov  ax,4                     ; negate 4 word number
  12838.                  push ax
  12839.                  mov  ax, offset multiplicand
  12840.                  push ax
  12841.                  call negate_it
  12842.                  add  sp, 4                    ; clear 2 pushes off stack
  12843.                  not  result_sign_flag         ; reverse sign of result
  12844.  
  12845.              get_next_number:
  12846.                  call get_signed               ; get signed multiplier
  12847.                  mov  multiplier, ax
  12848.                  test ax, 8000h                ; is it negative
  12849.                  jz   do_the_multiplication
  12850.  
  12851.                  neg  multiplier               ; negate
  12852.                  not  result_sign_flag         ; reverse sign of result
  12853.  
  12854.              do_the_multiplication:
  12855.                  mov  ax, offset result
  12856.                  push ax
  12857.                  mov  ax, multiplier      ; the number, not the address
  12858.                  push ax
  12859.                  mov  ax, offset multiplicand
  12860.                  push ax
  12861.                  call multiply_it
  12862.                  add  sp, 6                    ; clear 3 pushes off stack
  12863.  
  12864.                  ; is the result negative?
  12865.                  test result_sign_flag, 0FFh   ; 1111 1111 mask
  12866.                  jz   print_it
  12867.  
  12868.                  mov  ax, 5                    ; 5 word result
  12869.                  push ax
  12870.                  mov  ax, offset result
  12871.                  push ax
  12872.                  call negate_it
  12873.                  add  sp, 4                    ; clear 2 pushes off stack
  12874.  
  12875.              print_it:
  12876.                  mov  ax, WORD PTR result + 8  ; top two bytes
  12877.                  call print_hex
  12878.                  mov  ax, offset result        ; the rest of result
  12879.                  call print_signed_8byte
  12880.  
  12881.                  jmp  outer_loop
  12882.              ; - - - - - END CODE ABOVE THIS LINE
  12883.  
  12884.              The driver routine gets an 8 byte signed number. If the number is
  12885.  
  12886.  
  12887.              Chapter 16 - Multiple Word Arithmetic III                     173
  12888.              _________________________________________
  12889.  
  12890.              negative it negates the number (to make it positive) and switches
  12891.              the sign of the result_sign_flag. The sign flag will either be
  12892.              00h for positive or FFh for negative. It then gets a two byte
  12893.              signed number. If it is negative, the routine negates it and
  12894.              switches the sign flag. At this point both numbers are positive,
  12895.              so it calls the unsigned multiplication routine. At the end, it
  12896.              checks the result_sign_flag to see if the result should be
  12897.              positive or negative. If it should be negative, the routine calls
  12898.              negate_it one more time. Finally, the routine prints the number.
  12899.              The hex portion will be 0000 for positive or FFFF for negative
  12900.              unless the value is larger than an 8 byte signed number can hold,
  12901.              at which point the value of the 8 byte signed number will be
  12902.              incorrect.
  12903.  
  12904.              Here's the unsigned multiplication routine which has been turned
  12905.              into a subroutine:
  12906.  
  12907.              ; - - - - -
  12908.              multiply_it proc near
  12909.  
  12910.                  RESULT_ADDRESS           EQU  [bp+8]
  12911.                  MULTIPLIER_VALUE         EQU  [bp+6]
  12912.                  MULTIPLICAND_ADDRESS     EQU  [bp+4]
  12913.  
  12914.                  push bp
  12915.                  mov  bp, sp
  12916.                  PUSHREGS  ax, bx, cx, dx, si, di
  12917.  
  12918.                  mov  si, MULTIPLICAND_ADDRESS ; load pointers
  12919.                  mov  bx, RESULT_ADDRESS
  12920.  
  12921.                  mov  cx, 4          ; number of words
  12922.                  sub  di,di          ; clear di
  12923.  
  12924.              mult_loop:
  12925.                  mov  ax, [si]       ; multiplicand to ax
  12926.                  mul  WORD PTR  MULTIPLIER_VALUE
  12927.                  add  ax, di         ; add high word from last multiplication
  12928.                  jnc  store_result
  12929.                  inc  dx
  12930.              store_result:
  12931.                  mov  [bx], ax       ; store 1 word of result.
  12932.                  mov  di, dx         ; save high word for next multiplication
  12933.                  add  si, 2          ; increment pointers
  12934.                  add  bx, 2
  12935.                  loop mult_loop
  12936.  
  12937.                  mov  [bx], di       ; move last word of result
  12938.  
  12939.                  POPREGS   ax, bx, cx, dx, si, di
  12940.                  pop  bp
  12941.                  ret                 ; calling routine adjusts stack
  12942.  
  12943.              multiply_it endp
  12944.              ; - - - - - - - - - - -
  12945.  
  12946.              ______________________
  12947.  
  12948.              correct. The multiplication and the negation subroutines go in
  12949.              the subroutine section of SUBTEMP1.ASM. The driver routine is the
  12950.              main routine. If you don't remember how this multiplication
  12951.              routine works, go back to the chapter on unsigned multiple word
  12952.              multiplication since the code is the same.
  12953.  
  12954.  
  12955.  
  12956.              DIVISION
  12957.  
  12958.              Division is the same situation. We need a driver routine, but the
  12959.              division itself will be the unsigned division. In division, the
  12960.              remainder is the same sign as the dividend, and the sign of the
  12961.              quotient is (dividend_sign XOR divisor_sign). If both signs are
  12962.              the same, the quotient is positive; if the signs are different
  12963.              the quotient is negative. Here's the driver routine:
  12964.  
  12965.              ; - - - - - START DATA BELOW THIS LINE
  12966.              dividend                dq  ?
  12967.              divisor                 dw  ?
  12968.              quotient                dq  ?
  12969.              remainder               dw  ?
  12970.              quotient_sign_flag      db  ?
  12971.              remainder_sign_flag     db  ?
  12972.              ; - - - - - END DATA ABOVE THIS LINE
  12973.  
  12974.              ; - - - - - START CODE BELOW THIS LINE
  12975.              outer_loop:
  12976.                     mov   quotient_sign_flag, 0            ; assume positive
  12977.                     mov   remainder_sign_flag, 0
  12978.  
  12979.                     mov   ax, offset dividend
  12980.                     call  get_signed_8byte
  12981.                     test  WORD PTR (dividend + 6), 8000h   ; is it negative?
  12982.                     jz    get_next_number
  12983.  
  12984.                     mov   ax,4                      ; negate 4 word number
  12985.                     push  ax
  12986.                     mov   ax, offset dividend
  12987.                     push  ax
  12988.                     call  negate_it
  12989.                     add   sp, 4                     ; adjust stack
  12990.                     not   quotient_sign_flag        ; switch sign of quotient
  12991.                     mov   remainder_sign_flag, 0FFh ; remainder is negative
  12992.  
  12993.              get_next_number:
  12994.                     call  get_signed
  12995.                     mov   divisor, ax
  12996.                     test  ax, 8000h                 ; is it negative
  12997.                     jz    do_the_division
  12998.  
  12999.                     neg   divisor
  13000.                     not   quotient_sign_flag       ; switch sign of quotient
  13001.  
  13002.              do_the_division:
  13003.                     mov   ax, offset remainder
  13004.                     push  ax
  13005.  
  13006.  
  13007.              Chapter 16 - Multiple Word Arithmetic III                     175
  13008.              _________________________________________
  13009.  
  13010.                     mov   ax, offset quotient
  13011.                     push  ax
  13012.                     mov   ax, divisor        ; the number, not the address
  13013.                     push  ax
  13014.                     mov   ax, offset dividend
  13015.                     push  ax
  13016.                     call  divide_it
  13017.                     add   sp, 8                    ; clear 4 pushes off stack
  13018.  
  13019.                     ; are the remainder and quotient negative?
  13020.                     test  remainder_sign_flag, 0FFh
  13021.                     jz    test_the_quotient
  13022.                     neg   remainder
  13023.  
  13024.              test_the_quotient:
  13025.                     test  quotient_sign_flag, 0FFh      ; 1111 1111 mask
  13026.                     jz    print_it
  13027.  
  13028.                     mov   ax, 4                         ; 4 word result
  13029.                     push  ax
  13030.                     mov   ax, offset quotient
  13031.                     push  ax
  13032.                     call  negate_it
  13033.                     add   sp, 4                   ; clear 2 pushes off stack
  13034.  
  13035.              print_it:
  13036.                     mov   ax, offset quotient
  13037.                     call  print_signed_8byte
  13038.                     mov   ax, remainder
  13039.                     call  print_signed
  13040.  
  13041.                     jmp  outer_loop
  13042.              ; - - - - - END CODE ABOVE THIS LINE
  13043.  
  13044.              We get the dividend and check the sign. If it is negative, we (1)
  13045.              negate the number, (2) switch the sign of the quotient, and (3)
  13046.              set the remainder sign flag to negative. We get the divisor,
  13047.              check for negative; if it is negative we negate it and switch the
  13048.              sign of the quotient. We now have two unsigned numbers and do
  13049.              unsigned division. After division, both the quotient and
  13050.              remainder are adjusted for sign.
  13051.  
  13052.              The division routine is the same as the unsigned routine before
  13053.              except it is now a subroutine:
  13054.  
  13055.              ; - - - - - - - - ENTER SUBROUTINE BELOW THIS LINE
  13056.              divide_it proc near
  13057.  
  13058.                     REMAINDER_ADDRESS         EQU  [bp+10]
  13059.                     QUOTIENT_ADDRESS          EQU  [bp+8]
  13060.                     DIVISOR_VALUE             EQU  [bp+6]
  13061.                     DIVIDEND_ADDRESS          EQU  [bp+4]
  13062.  
  13063.                     push  bp
  13064.                     mov   bp, sp
  13065.                     PUSHREGS  ax, bx, cx, dx, si, di
  13066.  
  13067.  
  13068.  
  13069.              The PC Assembler Tutor                                        176
  13070.              ______________________
  13071.  
  13072.  
  13073.                     mov   si, DIVIDEND_ADDRESS
  13074.                     mov   bx, QUOTIENT_ADDRESS
  13075.                     add   si, 6                 ; start at the top word
  13076.                     add   bx, 6
  13077.                     mov   di, WORD PTR  DIVISOR_VALUE
  13078.                     mov   cx, 4                    ; number of words
  13079.                     sub   dx, dx          ; clear dx for first division
  13080.  
  13081.              division_loop:
  13082.                     mov   ax, [si]        ; dividend word to ax
  13083.                     div   di
  13084.                     mov   [bx], ax        ; word of result to quotient
  13085.                     sub   si, 2           ; decrement the pointers
  13086.                     sub   bx, 2
  13087.                     loop  division_loop
  13088.  
  13089.                     mov   bx, REMAINDER_ADDRESS    ; store remainder
  13090.                     mov   [bx], dx
  13091.  
  13092.                     POPREGS  ax, bx, cx, dx, si, di
  13093.                     pop   bp
  13094.                     ret                ; calling routine adjusts the stack
  13095.  
  13096.              divide_it  endp
  13097.  
  13098.              ; - - - - - - - - ENTER SUBROUTINE ABOVE THIS LINE
  13099.  
  13100.              Draw a picture of the stack to verify that the EQU statements are
  13101.              correct for a NEAR routine. The division and negation subroutines
  13102.              go in the SUBROUTINES section of SUBTEMP1.ASM. The driver is the
  13103.              main program. If you don't remember how this division works, go
  13104.              back to the division chapter and look it over. Try out a few
  13105.              numbers to make sure that it is working the way it should.
  13106.  
  13107.  
  13108.              DATA INTEGRITY
  13109.  
  13110.              One thing that may have been annoying some of you is that when
  13111.              the programs sent us numbers for multiplication and division we
  13112.              sometimes negated them, effectively changing the data in memory,
  13113.              but never changed them back when we were done. In an operational
  13114.              subroutine, you would have to do it differently. The logic would
  13115.              be:
  13116.  
  13117.                  NUMBER NEGATIVE?    no   everything's o.k.
  13118.  
  13119.                       yes
  13120.  
  13121.                       make copy
  13122.                       negate
  13123.                       reset pointer
  13124.  
  13125.              If the number is positive we won't change it. If the number is
  13126.              negative, we make a copy, negate the copy and use the copy for
  13127.              the operation.
  13128. Chapter 17 - Iinterrupts
  13129. ========================
  13130.                                                                            177
  13131.  
  13132.  
  13133.  
  13134.              Your word processor will work on a Compaq, an IBM, an AST or any
  13135.              other type of PC compatible computer. Every time it wants to read
  13136.              a file from disk or write to a printer, it calls a DOS or BIOS
  13137.              function that takes care of it.{1}  Yet every computer has its
  13138.              own versions of these subroutines, and they not only are
  13139.              different, they are in different places in memory. How does the
  13140.              word processor know where to find them? It uses interrupts.{2}
  13141.  
  13142.              An interrupt is a glorified subprogram call. The first 1024 bytes
  13143.              of your computer's memory (that's from 0000:0000 to 0000:03FF)
  13144.              contain the addresses of all the DOS and BIOS interrupts. Which
  13145.              address contains the address of which subprogram was decided by
  13146.              the triumverate of Intel/IBM/Microsoft. We have two different
  13147.              sets of addresses here, so let's keep them straight. Starting at
  13148.              memory address 0000 there are 4 byte addresses called interrupt
  13149.              vectors. There is a different vector at 0000d, at 0004d, at
  13150.              0008d, at 0012d at 0016d etc.; there are 256 of them in all. Each
  13151.              of these 256 places can hold the address of a subprogram
  13152.              somewhere in memory (although not all of them are used).
  13153.  
  13154.              When you call an interrupt, the 8086 goes to the appropriate
  13155.              place in low memory (from 0000 to 3FFh) and finds the address of
  13156.              the subprogram that you want, loads it into CS and IP, and goes
  13157.              to that subprogram. When that subprogram is done, it goes back to
  13158.              the next instruction in your program after the interrupt.
  13159.  
  13160.              How did those addresses get into the first 1024 bytes? The
  13161.              computer put them there when you started it up. It is one of the
  13162.              first things that the computer did when you turned it on. These
  13163.              subprograms do EVERYTHING, and you can't run the computer without
  13164.              them.
  13165.  
  13166.              If you want to scroll the screen, you put the appropriate
  13167.              information in the 8086 registers and use:
  13168.  
  13169.                  int 10h             ; decimal 16 {3}
  13170.  
  13171.              The 8086 goes to the interrupt 16 address (4*16 = 64), gets the
  13172.              address of the program that contains the video subprograms, and
  13173.              ____________________
  13174.  
  13175.                 1. BIOS stands for basic input/output services.
  13176.  
  13177.                 2. If you haven't gotten it yet, it's time to get either "DOS
  13178.              Programmer's Reference" or "The Peter Norton Programmer's Guide
  13179.              to the IBM PC." I'm serious. They contain the information you
  13180.              need to make use of this chapter.
  13181.  
  13182.                 3. It is normal to use hex numbers for the interrupts, so if
  13183.              you read about an interrupt make sure you know if the numbers are
  13184.              hex or decimal.
  13185.  
  13186.              ______________________
  13187.  
  13188.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  13189.  
  13190.  
  13191.              The PC Assembler Tutor                                        178
  13192.              ______________________
  13193.  
  13194.              goes to it. If you want to write to the printer, you put the
  13195.              appropriate information in the 8086 registers and call:
  13196.  
  13197.                  int 21h             ; decimal 33
  13198.  
  13199.              The 8086 goes to address 132 (4*33 = 132), gets the address of
  13200.              the DOS program, puts it in CS and IP, and starts it. If you want
  13201.              to get input from the keyboard, you put the appropriate
  13202.              information in the 8086 registers and call:
  13203.  
  13204.                  int  21h            ; decimal 33
  13205.  
  13206.              That's right, it is the same program as the one that does the
  13207.              printer. The 8086 goes to 132 (4*33), gets the program address,
  13208.              and goes to it.
  13209.  
  13210.              The lowest interrupt is int 0 (address = 4*0 = 0000). The highest
  13211.              interrupt is int 255 (address = 4*255 = 1020).
  13212.  
  13213.              This is an intelligent way to handle the situation. As long as
  13214.              everyone agrees which interrupt contains the address of which
  13215.              subprogram, our programs will work on any PC compatible. This is
  13216.              one of the things that is meant by PC compatible.
  13217.  
  13218.              On my computer, here is a section of these addresses starting
  13219.              with int 1Eh (30d). High memory is at the top, low memory is at
  13220.              the bottom.
  13221.  
  13222.                  INT #           DATA       LOCATION
  13223.  
  13224.                                 0724h cs       146
  13225.                    36           04A8h ip       144
  13226.                              cs 0724h          142
  13227.                    35        ip 01BDh          140
  13228.                                 0724h cs       138
  13229.                    34           01B0h ip       136
  13230.                              cs 019Fh          134
  13231.                    33        ip 05EBh          132
  13232.                                 019Fh cs       130
  13233.                    32           05E7h ip       128
  13234.                              cs 0000h          126
  13235.                    31        ip 0000h          124
  13236.                                 0070h cs       122
  13237.                    30           0EB8h ip       120
  13238.  
  13239.              If we call int 30d, the 8086 goes to 120 (4*30), puts 0EB8h into
  13240.              the IP, and puts 0070h (from the next higher location) into CS.
  13241.              The next instruction it does will be 0070:0EB8. If we call int
  13242.              35d, the 8086 goes to 140 (4*35), puts 01BDh in IP, puts 0724h
  13243.              (from the next higher location) in CS. The next instruction the
  13244.              8086 does will be 0724:01BD. If we call int 33d, the 8086 goes to
  13245.              132 (4*33), and puts 05EBh in IP, 019Fh  (from the next higher
  13246.              location) in CS. The next instruction executed will be
  13247.              019F:05EBh. The 0000:0000 for int 31d indicates that there is no
  13248.              int 31d (address 0000:0000 contains data, [the vectors for int
  13249.              0], not a program). Make sure that you understand how this is
  13250.              working before you go on.
  13251.  
  13252.  
  13253.              Chapter 17 - Interrupts                                       179
  13254.              _______________________
  13255.  
  13256.  
  13257.              On the PC, information for the interrupts is always passed
  13258.              through registers, not on the stack. Each interrupt type has a
  13259.              specific register for each piece of information it needs. We will
  13260.              do a couple to see how they work.
  13261.  
  13262.  
  13263.              DISPLAYING A CHARACTER
  13264.  
  13265.              We can print a character at a time on the monitor, so we'll input
  13266.              a string, and then print out each character of the string
  13267.              individually. We will stop the printout when we see the 0 at the
  13268.              end of the string.
  13269.  
  13270.              ; - - - - - - - - - - START DATA BELOW THIS LINE
  13271.              buffer  db  80 dup (?)
  13272.              ; - - - - - - - - - - START DATA BELOW THIS LINE
  13273.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  13274.                  call show_regs
  13275.              outer_loop:
  13276.                  mov  ax, offset buffer
  13277.                  call get_string
  13278.  
  13279.                  mov  si, offset buffer
  13280.              inner_loop:
  13281.                  mov  al, [si]
  13282.                  cmp  al, 0
  13283.                  je   next_string
  13284.                  mov  ah, 14              ; ah contains function number
  13285.                  mov  bh, 0               ; where in memory
  13286.                  int  10h                 ; 33d
  13287.                  inc  si
  13288.                  jmp  inner_loop
  13289.  
  13290.              next_string:
  13291.                  call get_continue
  13292.                  jmp  outer_loop
  13293.  
  13294.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  13295.  
  13296.              The program is simple. It gets a string, then checks each
  13297.              character for 0 (end of string), before shipping it off to the
  13298.              screen. I'll explain AH in a second. There are several places
  13299.              this character can be displayed{4}, so use show_regs to force the
  13300.              video screen to a certain place in memory, then put BH = 0 to
  13301.              tell the interrupt that the screen memory is in that place.
  13302.              Finally, with all the information in place, we do the interrupt.
  13303.              You will not print a carriage return, only the data, so we use
  13304.              get_continue to give us a carriage return. It's a little sloppy,
  13305.              but much easier.
  13306.  
  13307.              We have lots and lots of subprograms for the disks, printer,
  13308.              screen, etc. There are only 255 interrupts, so it was decided at
  13309.              the beginning to make most of the interrupts groups of programs
  13310.              ____________________
  13311.  
  13312.                 4. Cf. one of those two books.
  13313.  
  13314.  
  13315.              The PC Assembler Tutor                                        180
  13316.              ______________________
  13317.  
  13318.              instead of a single program. Int 10h (16d) contains about two
  13319.              dozed different video subprograms. Each program is distinguished
  13320.              by a specific number in AH. For int 10h (16d):
  13321.  
  13322.                  ah = 2h        Get cursor position
  13323.                  ah = 6h        Scroll window up
  13324.                  ah = Eh        (14d) Write character to screen
  13325.  
  13326.              For int 21h (33d):
  13327.  
  13328.                  ah = 1h        Keyboard input
  13329.                  ah = 5h        Printer output
  13330.                  ah = 17h       Rename a file
  13331.                  ah = 2Ch       Get the time
  13332.  
  13333.              As you can see, int 21h is a potpourri of subprograms. We'll do
  13334.              the same program as above, but with printer output. Everything is
  13335.              the same except that the inner loop should be changed to look
  13336.              like this:
  13337.  
  13338.              ; - - - - -
  13339.                  mov  si, offset buffer
  13340.              inner_loop:
  13341.                  mov  dl, [si]
  13342.                  cmp  dl, 0
  13343.                  je   next_string
  13344.                  mov  ah, 5               ; ah contains function number
  13345.                  int  21h                 ; 33d
  13346.                  inc  si
  13347.                  jmp  inner_loop
  13348.  
  13349.              ; - - - - -
  13350.  
  13351.              There is practically no change. We use DL instead of AL, have
  13352.              "int 21h" instead of "int 10h", and change the function number in
  13353.              AH to 5. Also, BH is not needed.
  13354.  
  13355.              Leave your printer off at the beginning to see what happens. Turn
  13356.              your printer on and enter a string of 10 or 20 letters. Probably
  13357.              nothing happened. Enter another 20 or 30 letters. Nothing again.
  13358.              Try 50 letters this time. This time it should work. Lots of
  13359.              printers won't print anything unless (1) they get a carriage
  13360.              return (which you haven't sent) or (2) they have a backlog of
  13361.              more than 80 characters. If you ever use the printer interrupt,
  13362.              you'll have to remember that.
  13363.  
  13364.              These interrupts which are in your program are called software
  13365.              interrupts. They are your interface with the peripheral devices
  13366.              on your machine, doing everything from disk i/o to handling
  13367.              memory allocation. They do all your housekeeping for you.{5}  If
  13368.              you are going to work at this level then you should buy one of
  13369.              those two books. They contain all the software interrupts (and
  13370.              there are about a hundred of them), tell how they work and which
  13371.              registers to set for each interrupt. If you don't have access to
  13372.              ____________________
  13373.  
  13374.                 5. But they don't do Windows.
  13375.  
  13376.  
  13377.              Chapter 17 - Interrupts                                       181
  13378.              _______________________
  13379.  
  13380.              these interrupts it's like having your arms cut off - you're
  13381.              unable to do any i/o at all.
  13382.  
  13383.  
  13384.              HARDWARE INTERRUPTS
  13385.  
  13386.              The interrupts that we write in our programs aren't the only
  13387.              interrupts there are. The hardware uses interrupts to take
  13388.              temporary control of the computer. On a Macintosh, if you insert
  13389.              a disk, the program stops and the operating system reads in the
  13390.              disk directory. A modem that is in use will request time for
  13391.              doing i/o. Also, if the 8086 detects a zero divide, it will
  13392.              trigger a special interrupt. You have met int 4 already. It is
  13393.              the INTO instruction, which will trigger an interrupt if the
  13394.              overflow flag is set.
  13395.  
  13396.              Your interrupts are in specific places in your code, but these
  13397.              machine interrupts can happen at any time. There are two lines
  13398.              (wires) into the 8086. One is for serious problems that need to
  13399.              be taken care of NOW, and it has non-maskable interrupts. The
  13400.              other line is for interrupts that need to be taken care of in a
  13401.              timely fashion, and they are maskable interrupts.
  13402.  
  13403.              A non-maskable interrupt (NMI) is when the hardware detects that
  13404.              it is in deep doo doo. It sends a signal on the NMI line that
  13405.              says "Hey! I need an interrupt." The 8086 finishes the
  13406.              instruction it is processing and then IMMEDIATELY gives over
  13407.              control. The NMI uses the same 1024 bytes in low memory for
  13408.              interrupt vectors, but has its own interrupt numbers. Normally
  13409.              this is for very serious errors, so the interrupt program may
  13410.              decide to abort your program and return to the operating system;
  13411.              if it makes sense to, it will return to your program where your
  13412.              program left off.
  13413.  
  13414.              A maskable interrupt is when a piece of hardware has some work to
  13415.              do. It sends a signal to the 8086 (on the INTR line), and the
  13416.              8086 takes care of it when it is ready.
  13417.  
  13418.              When the 8086 is ready depends on you. In the flags register is
  13419.              the IEF, the interrupt enable flag. It should always be set to 1
  13420.              unless you are doing something critical. Basically, the only
  13421.              things that are critical are interrupts themselves and context
  13422.              switches. Context switches are done by the operating system in
  13423.              multitasking environments, so they don't concern you, and you are
  13424.              not writing interrupts, so they don't concern you. Therefore,
  13425.              always keep the IEF set. Just for your information, you set the
  13426.              IEF with:
  13427.  
  13428.                  sti  ; set interrupt flag (interrupts enabled)
  13429.  
  13430.              and clear it with:
  13431.  
  13432.                  cli  ; clear interrupt flag (interrupts disabled)
  13433.  
  13434.              Why do interrupt programs clear the interrupt flag? Because you
  13435.              could have interrupts interrupting other interrupts and wind up
  13436.              with scads of half finished interrupts lying around. This way,
  13437.  
  13438.  
  13439.              The PC Assembler Tutor                                        182
  13440.              ______________________
  13441.  
  13442.              one interrupt finishes before another can take over.
  13443.  
  13444.  
  13445.              Suppose you are in the middle of the following two instructions
  13446.              when an interrupt hits:
  13447.  
  13448.                  cmp  al, 7
  13449.                  jne  some_label
  13450.  
  13451.              If the hardware interrupt takes over after the CMP instruction
  13452.              but before the JNE instruction, the correctness of the result
  13453.              will depend on the zero flag staying the same. Can you trust it?
  13454.              The answer is yes. The first three things an interrupt request
  13455.              does (in the microcode) are:
  13456.  
  13457.                  push flags     ; push the flags
  13458.                  push old CS    ; push the code segment
  13459.                  push old IP    ; push the instruction pointer
  13460.  
  13461.              On return, it pops the flags back in place, so they are exactly
  13462.              the same as just before the interrupt. You will notice that there
  13463.              are 3 things on the stack instead of the usual 2 in a far call.
  13464.              Therefore, there is a special RET instruction called IRET (return
  13465.              from interrupt) which pops not only IP and CS, but the flags as
  13466.              well. You can only use this instruction in an interrupt because
  13467.              it assumes that the flags register is right after CS on the
  13468.              stack.
  13469.  
  13470.  
  13471.              DEBUGGING
  13472.  
  13473.              Finally, if you have a debugger installed, the debugger uses two
  13474.              interrupts, int 1 and int 3. You code int 3 into the program by
  13475.              placing:
  13476.  
  13477.                  int
  13478.  
  13479.              in the program with no number.{6}  The interrupt will call the
  13480.              debugger. The reason for this int with no number is that
  13481.              afterwards, you can replace it with NOP, since they are both 1
  13482.              byte instructions. Int 3 is a 2 byte instruction (they are coded
  13483.              differently, even though they wind up at the same place).
  13484.  
  13485.              Int 1 is the trap instruction. In the flags register is the trap
  13486.              flag (TF). If TF is set, the 8086 will interrupt after the next
  13487.              instruction. That way, the debugger can single step through
  13488.              sections of code. You put Int 3 to set a breakpoint and then set
  13489.              TF to single step from there.
  13490.  
  13491.              The only way to set TF from your own program is:
  13492.  
  13493.              ____________________
  13494.  
  13495.                 6. Though the Microsoft assembler considers this an error. You
  13496.              must code it as:
  13497.  
  13498.                  int  3
  13499.  
  13500.  
  13501.              Chapter 17 - Interrupts                                       183
  13502.              _______________________
  13503.  
  13504.                  pushf
  13505.                  pop  ax
  13506.                  or   ax, 0100h
  13507.                  push ax
  13508.                  popf
  13509.  
  13510.              but you should let the debugger do it.
  13511.  
  13512.              Just so you can get a feel for how the debugger system works,
  13513.              there is a pseudo-debugger called PSEUDDBG.COM in \XTRAFILE. It
  13514.              is not a debugger. It does nothing but tell you whether you have
  13515.              generated a breakpoint interrupt (int 3) or a single_step
  13516.              interrupt (int 1). It is memory resident. That means that once
  13517.              you have loaded it, it will stay in memory until you shut the
  13518.              machine off or reset the machine. You load it by executing:
  13519.  
  13520.                  >pseuddbg
  13521.  
  13522.              It will tell you that it has been loaded and then sit there
  13523.              waiting for interrupts. When you generate a breakpoint interrupt,
  13524.              PSEUDDBG will allow you to set the trap flag or just continue.
  13525.              When you generate a single step interrupt, PSEUDDBG will allow
  13526.              you to clear TF or to continue single stepping. All you need to
  13527.              try it out is a simple program:
  13528.  
  13529.              ; - - - - - ENTER CODE BELOW THIS LINE
  13530.              outer_loop:
  13531.                  call get_continue
  13532.  
  13533.                  mov  cx, 4
  13534.                  int  3
  13535.              inner_loop:
  13536.                  mov  ax, 5
  13537.                  add  ax, 3
  13538.                  add  ax, 7
  13539.                  add  ax, 10
  13540.                  loop inner_loop
  13541.  
  13542.                  loop outer_loop
  13543.              ; - - - - - FINISH CODE ABOVE THIS LINE
  13544.  
  13545.              Each time you start the outer loop, it will generate a breakpoint
  13546.              interrupt. At this point you can set TF to single step or
  13547.              continue. If you single step, watch IP. It will be changing,
  13548.              cycling through the loop till it gets to get_continue. If you
  13549.              start trapping through get_continue, things will get strange
  13550.              because get_continue stores the flags. This means that after you
  13551.              quit trapping, get_continue will POP some flags that have TF set
  13552.              and it will start trapping again. If this happens, just continue
  13553.              hitting 0 until you get out of the single step mode.
  13554.  
  13555.              If you like this method of single stepping through code, there is
  13556.              a memory resident version of ASMHELP called HELPMEM.COM which
  13557.              contains show_regs and allows single stepping. It can be used in
  13558.              conjunction with ASMHELP.OBJ. For details consult APP1.DOC in the
  13559.              appendix.
  13560.  
  13561.  
  13562.  
  13563.              The PC Assembler Tutor                                        184
  13564.              ______________________
  13565.  
  13566.                                           SUMMARY
  13567.  
  13568.              Software interrupts send the 8086 to the first 1024 bytes of
  13569.              memory where it finds the address of the DOS or BIOS subprogram
  13570.              that you want to go to. The correspondence between the interrupt
  13571.              number and the location of the address is:
  13572.  
  13573.                  address location = 4 * interrupt number
  13574.  
  13575.              The address is stored in the next 4 bytes; first IP, then CS.
  13576.  
  13577.  
  13578.  
  13579.              The first 5 interrupts are:
  13580.  
  13581.                  int 0     divide by zero
  13582.                  int 1     trap flag induced single stepping for the debugger
  13583.                  int 2     non_maskable_interrupt (NMI)
  13584.                  int 3     1 byte interrupt for the debugger
  13585.                  int 4     interrupt on overflow
  13586.  
  13587.  
  13588.              There are two types of hardware interrupts. NMI (non maskable
  13589.              interrupt) interrupts are for serious problems that need to be
  13590.              taken care of IMMEDIATELY. They have priority and take over right
  13591.              after the current instruction is finished.
  13592.  
  13593.              Maskable interrupts are hardware interrupts that should be taken
  13594.              care of in a timely fashion. If IEF (the interrupt enable flag)
  13595.              is set the maskable interrupts will go through. If IEF is
  13596.              cleared, the maskable interrupts must wait until it is set again.
  13597.              Set the IEF with:
  13598.  
  13599.                  sti  ; set interrupt flag
  13600.  
  13601.              and clear it with:
  13602.  
  13603.                  cli  ; clear interrupt flag
  13604.  
  13605.              The IEF should always stay set unless there is a specific reason
  13606.              for not allowing interrupts to happen.
  13607.  
  13608.  
  13609.              If you are writing an interrupt routine, then instead of RET you
  13610.              use IRET (return from interrupt) which not only pops off IP and
  13611.              CS, but pops the flags register as well.
  13612. Chapter 18 - Ports
  13613. ==================
  13614.                                                                            185
  13615.  
  13616.  
  13617.  
  13618.              In order to communicate with the outside world, the outside world
  13619.              being your printer, your disk drives, your modem etc., the 8086
  13620.              uses ports. A port is an address distinct from a memory address
  13621.              where the i/o device can be reached.{1}  When IBM set up the
  13622.              first PC, they decided what these port addresses would be and
  13623.              what the function of each bit at each address would be. Any VGA,
  13624.              EGA, CGA or monochrome card has to have it's ports at the same
  13625.              addresses as those set by IBM, and these ports have to do the
  13626.              same thing.
  13627.  
  13628.              Normally, an i/o device has more than one port address. The
  13629.              device not only has to transfer the data, it has to tell the
  13630.              computer whether it is ready, find out if the computer wants to
  13631.              send information, confirm that the transfer was successful etc.
  13632.              COM1 is port addresses 3F8 - 3FF. COM2 is 2F8 - 2FF. The CGA
  13633.              adapter is ports 3D0 - 3DF.
  13634.  
  13635.              In the CGA, port 3D9 sets the different colors. The 8086 writes
  13636.              to it, but can't read it. Port 3DA, it's neighbor, tells certain
  13637.              status information and is read only.
  13638.  
  13639.              All control information for the video cards is passed back and
  13640.              forth through the ports. However, the video card comes into
  13641.              memory 50 times a second to see what it should write to the
  13642.              screen. The characters on the screen don't pass through the
  13643.              ports. The mountain comes to Mohammed.
  13644.  
  13645.              The data does pass through a port on the way to your serial
  13646.              printer. The 8086 sends your printer control information through
  13647.              one port address and sends the data through a different port
  13648.              address.
  13649.  
  13650.              The port instructions can be either with AL or AX. AL and AX are
  13651.              the only registers you can use, AL for a byte transfer and AX for
  13652.              a word transfer. There are two forms to the input instruction:
  13653.  
  13654.                  in   al, port_address
  13655.              or
  13656.                  in   al, dx
  13657.  
  13658.              port_address is a constant and is limited to 0 - 255. It is a one
  13659.              byte constant. DX is the only register than can be used with the
  13660.              second form. DX can contain any number from 0 - 65535. The
  13661.              ____________________
  13662.  
  13663.                 1. There is memory address 0000 and there is port address
  13664.              0000; they are two entirely different things. You can reach
  13665.              memory address 0000 with the DS:SI pair 0000:0000, but DS:SI
  13666.              can't get to port address 0000. The instruction "out 0, al"
  13667.              writes the contents of al to port address 0000,  but the OUT
  13668.              instruction can't get anywhere near memory address 0000.
  13669.  
  13670.              ______________________
  13671.  
  13672.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  13673.  
  13674.  
  13675.              The PC Assembler Tutor                                        186
  13676.              ______________________
  13677.  
  13678.              following two pieces of code do the same thing:
  13679.  
  13680.                  in   al, 155
  13681.  
  13682.                  mov  dx, 155
  13683.                  in   al, dx
  13684.  
  13685.              This code moves one byte of data from port 155 to register AL.
  13686.  
  13687.              The output instruction is similar. We have:
  13688.  
  13689.                  out  port_address, al
  13690.              or
  13691.                  out  dx, al
  13692.  
  13693.              where port_address is a constant 0-255 and DX can contain a
  13694.              number 0 - 65535.
  13695.  
  13696.                  out  97, ax
  13697.  
  13698.              moves a word from AX to port addresses 97-98. Notice that both
  13699.              the IN and the OUT instructions follow the  DESTINATION, SOURCE
  13700.              ordering found in all the other 8086 instructions.
  13701.  
  13702.              The  "in  ax, port_address" form is not all that useful. If you
  13703.              look at the addresses for the video ports, for COM1 and COM2, you
  13704.              will see that they are all larger than 255. Also, most equipment
  13705.              can be at several different addresses. A modem can be at COM1,
  13706.              COM2, COM3, or COM4. If the port address is hard coded into the
  13707.              instructions, you need 4 subprograms to handle the 4 different
  13708.              addresses. It is easier to find out where the modem is, then use:
  13709.  
  13710.              modem_data_address dw   ?
  13711.  
  13712.                  mov  dx, modem_data_address
  13713.                  in   al, dx
  13714.  
  13715.              and
  13716.                  mov  dx, modem_data_address
  13717.                  out  dx, al
  13718.  
  13719.              If you aren't planning on writing device drivers this is for
  13720.              background information only, since all standard i/o is done
  13721.              through DOS or BIOS interrupts.
  13722.  
  13723.              One last thing is the parity flag. It has been sitting on the
  13724.              screen with all the other flags. What is it for?  When you send
  13725.              information over a modem, there is a large possibility of line
  13726.              interference. If it is text, and an occasional screw up is not
  13727.              too bad, using a parity check may be enough.{2}  Parity can be
  13728.              even or odd. Even means that an even number of bits are set to 1;
  13729.              odd means that an odd number of bits are set to 1. This is not
  13730.              whether the number is even or odd, it is whether the number of 1
  13731.              ____________________
  13732.  
  13733.                 2. It is NEVER enough if you are transferring binary data or
  13734.              executable files.
  13735.  
  13736.  
  13737.              Chapter 18 - Ports                                            187
  13738.              __________________
  13739.  
  13740.              BITS is even or odd. Here's a short list.
  13741.  
  13742.                       NUMBER         BINARY         PARITY
  13743.  
  13744.                            6           0110         even
  13745.                            7           0111         odd
  13746.                            8           1000         odd
  13747.                            9           1001         even
  13748.                           10           1010         even
  13749.  
  13750.              8 is an even number but has odd parity, 9 is an odd number but
  13751.              has even parity. 6 has two 1 bits (even), 7 has three 1 bits
  13752.              (odd), 8 has one 1 bit (odd), 9 has two 1 bits (even) and 10 has
  13753.              two 1 bits (even).
  13754.  
  13755.              How does this help us? If there is a chance of 1/100 of screwing
  13756.              up a single bit in a byte, there is only a chance of 1/10,000 of
  13757.              screwing up two bits in a byte. What this means is that of every
  13758.              100 errors, 99 of them will will be detectable because of a
  13759.              change in parity (by changing a bit from 0 to 1 or from 1 to 0)
  13760.              and only 1 of them will go undetected because two changes will
  13761.              keep the same parity. This is a little obscure, so make sure you
  13762.              understand why before continuing.
  13763.  
  13764.              This doesn't help us much yet, because we don't know what the
  13765.              parity was originally. 9 has even parity, 7 has odd parity. What
  13766.              communications programs do is FORCE the parity. They can either
  13767.              force it even or force it odd.
  13768.  
  13769.              The standard ASCII characters end at 127 - that is, 0111 1111
  13770.              (7F) is the highest legal number you can transmit. The left bit
  13771.              is unused, so we can use it to force the parity. We are going to
  13772.              force it even, but forcing it odd uses the same technique.
  13773.  
  13774.              The steps are:
  13775.  
  13776.                  (1) Find the parity of the byte.
  13777.  
  13778.                  (2) If it is even, leave it alone, if it is odd, put a 1 in
  13779.                  the left hand bit. The parity is now even.
  13780.  
  13781.              If both the sending and receiving program have agreed on even
  13782.              parity, then on the receiving end, the program:
  13783.  
  13784.                  (1) Checks for even parity. If the parity is odd, it is an
  13785.                  error.
  13786.  
  13787.                  (2) Puts a 0 back in the left hand bit. The original number
  13788.                  is restored.
  13789.  
  13790.              We are going to make a loop with both parts and use show_regs to
  13791.              watch the parity flag. Here's the program:
  13792.  
  13793.              ; - - - - - - - - - - ENTER DATA BELOW THIS LINE
  13794.  
  13795.              error_banner  db  "Whoa! We have a screw up.", 13, 10, 0
  13796.  
  13797.  
  13798.  
  13799.              The PC Assembler Tutor                                        188
  13800.              ______________________
  13801.  
  13802.              ; - - - - - - - - - - ENTER DATA ABOVE THIS LINE
  13803.  
  13804.              ; - - - - - - - - - - ENTER CODE BELOW THIS LINE
  13805.                  mov  ax_byte, 0A3h            ; al binary
  13806.                  mov  bx_byte, 0A2h            ; unsigned half registers
  13807.                  mov  cx_byte, 0A2h            ; unsigned half registers
  13808.                  mov  dx_byte, 0A3h            ; dl binary
  13809.                  lea  ax, ax_byte
  13810.                  call set_reg_style
  13811.  
  13812.              comm_loop:
  13813.                  mov  ax, 0
  13814.                  call set_count                ; reset count to 0
  13815.                  mov  bx, 0                    ; clear registers
  13816.                  mov  cx, 0
  13817.                  mov  dx, 0
  13818.                  call show_regs                ; (1)
  13819.  
  13820.                  ; the sending program
  13821.  
  13822.                  call get_unsigned_byte
  13823.                  and  al, 7Fh             ; 0111 1111 - make sure al < 128
  13824.                  mov  bl, al                   ; copy to bl
  13825.                  call show_regs_and_wait       ; (2)
  13826.  
  13827.                  and  al, al                   ; check parity
  13828.                  call show_regs_and_wait       ; (3)
  13829.  
  13830.                  jpe  do_nothing
  13831.                  or   al, 80h                  ; if parity odd, set left bit
  13832.  
  13833.              do_nothing:
  13834.                  and  al, al                   ; check parity for show_regs
  13835.                  call show_regs_and_wait       ; (4)
  13836.  
  13837.                  ; the receiving program
  13838.  
  13839.                  mov  dl, al                   ; transmit from al to dl
  13840.                  mov  cl, dl                   ; copy to cl
  13841.                  call show_regs_and_wait       ; (5)
  13842.  
  13843.                  and  dl, dl
  13844.                  jpe  zero_left_bit            ; is parity even?
  13845.                  mov  ax, error_banner
  13846.                  call print_string
  13847.  
  13848.              zero_left_bit:
  13849.                  and  dl, 7Fh             ; 0111 1111  binary, left bit 0
  13850.                  mov  cl, dl                   ; copy to cl
  13851.                  call show_regs_and_wait       ; (6)
  13852.                  jmp  comm_loop
  13853.  
  13854.              ; - - - - - - - - - - ENTER CODE ABOVE THIS LINE
  13855.  
  13856.              First, we set the register style so AL and DL are binary, BL and
  13857.              CL are unsigned. We'll use AL and BL for sending, CL and DL for
  13858.              receiving. Next, we reset the counter to 0 so we can see where we
  13859.  
  13860.  
  13861.              Chapter 18 - Ports                                            189
  13862.              __________________
  13863.  
  13864.              are, and zero AX, BX, CX, and DX. We get a byte and make sure
  13865.              that it is 127 or less,{3} then send a copy to BL. BL stays
  13866.              unchanged for the rest of the loop. We test the parity. and if it
  13867.              is odd, change it. Finally, we send it off to DL.
  13868.  
  13869.              On the receiving end, we test the parity. If it is odd, we print
  13870.              an error message. Then we zero the left hand bit. It is faster to
  13871.              zero the left hand bit than to check to see if it needs to be
  13872.              zeroed, so we do it for all data. The result is put in CL for
  13873.              comparison. AL and DL are shown in binary form so you can count
  13874.              the number of 1 bits in the byte.
  13875.  
  13876.  
  13877.  
  13878.  
  13879.  
  13880.  
  13881.  
  13882.  
  13883.  
  13884.  
  13885.  
  13886.  
  13887.  
  13888.  
  13889.  
  13890.  
  13891.  
  13892.  
  13893.  
  13894.  
  13895.  
  13896.  
  13897.  
  13898.  
  13899.  
  13900.  
  13901.  
  13902.  
  13903.  
  13904.  
  13905.  
  13906.  
  13907.  
  13908.              ____________________
  13909.  
  13910.                 3. The following could happen if we didn't take this step. We
  13911.              get a number > 128 with odd parity. 131 (1000 0011  83h) is an
  13912.              example. The sending program sees that it is odd parity, so it
  13913.              puts a 1 in the left hand bit. But there is already a 1 in the
  13914.              left hand bit, so the parity doesn't change, it is odd. The
  13915.              receiving program gets the byte, tests it, notices that it is
  13916.              odd, and sends back a transmission error. Since the sending
  13917.              program didn't see anything wrong, it just sends the same byte
  13918.              again. It will never get through correctly. In real life, the
  13919.              sending program would probably test the byte to see if it was
  13920.              greater than 127 and print an error if it was.
  13921.  
  13922.  
  13923.              The PC Assembler Tutor                                        190
  13924.              ______________________
  13925.  
  13926.                                           SUMMARY
  13927.  
  13928.  
  13929.              POSSIBLE I/O INSTRUCTIONS
  13930.  
  13931.              in  ax, constant (= port address 0 - 255)
  13932.              in  ax, DX
  13933.  
  13934.              in  al, constant (= port address 0 - 255)
  13935.              in  al, DX
  13936.  
  13937.              out constant, ax  (constant = port address 0 - 255)
  13938.              out DX, ax
  13939.  
  13940.              out constant, al  (constant = port address 0 - 255)
  13941.              out DX, al
  13942.  
  13943.              In these instructions DX holds a port address and
  13944.              can be from  0 - 65535.
  13945.  
  13946.  
  13947.              PARITY
  13948.  
  13949.              Parity is whether the number of 1 bits in a byte (or word) is
  13950.              even or odd. The 8086 sets the parity flag after most arithmetic
  13951.              and all logical operations. If parity is even, the flag is 1, if
  13952.              parity is odd, the flag is 0.
  13953. Chapter 19 - Strings
  13954. ====================
  13955.                                                                          191
  13956.  
  13957.  
  13958.              Sometimes we want to deal with long strings of information. Here
  13959.              long means hundreds or thousands of bytes, not tens of bytes. The
  13960.              8086 provides a group of instructions to move and compare
  13961.              strings. These instructions have a rigid structure, but with a
  13962.              little bit of effort we can get them to work easily for us. We
  13963.              will start with SCAS, since it is simple, yet embodies all the
  13964.              rigid features of these instructions.
  13965.  
  13966.              SCAS (scan string) compares either a byte to AL or a word to AX.
  13967.              The byte or word must be in memory, and the register must be AL
  13968.              or AX. SCAS also increments or decrements the pointer. First, the
  13969.              size:
  13970.  
  13971.                  scasb
  13972.  
  13973.              compares a byte to AL, while:
  13974.  
  13975.                  scasw
  13976.  
  13977.              compares a word to AX. But where's the pointer? You have no
  13978.              choice, it's DI. Not only is it DI, but it MUST be ES:DI. The ES
  13979.              segment is coded into the 8086 microcode; the DI register is
  13980.              coded into the 8086 microcode; there is nothing you can do to
  13981.              change it. What about incrementing or decrementing? In the flags
  13982.              register, there is a flag called the direction flag. It is set
  13983.              manually by the program. If DF = 0, SCAS increments DI; if DF =
  13984.              1, SCAS decrements DI.{1} The equivalent software for the
  13985.              instruction would be:
  13986.  
  13987.                            (scasb)                       (scasw)
  13988.              DF = 0        cmp  al, es:[di]              cmp  ax, es:[di]
  13989.                            pushf                         pushf
  13990.                            add  di, 1                    add  di, 2
  13991.                            popf {2}                      popf
  13992.  
  13993.  
  13994.              DF = 1        cmp  al, es:[di]              cmp  ax, es:[di]
  13995.                            pushf                         pushf
  13996.                            sub  di, 1                    sub  di, 2
  13997.                            popf                          popf
  13998.  
  13999.              ____________________
  14000.  
  14001.                 1. Every time you have called show_regs DF has been there; it
  14002.              doesn't show 0 and 1, it shows + and -  (+ = 0 , - = 1).
  14003.  
  14004.                 2. The microcode doesn't really push and pop the flags. This
  14005.              is only to indicate that the order of operations is (1) get the
  14006.              byte (word) from the string, (2) compare and set the flags, and
  14007.              finally (3) increment (decrement) the pointer without changing
  14008.              any of the flags.
  14009.  
  14010.              ______________________
  14011.  
  14012.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  14013.  
  14014.  
  14015.              The PC Assembler Tutor                                        192
  14016.              ______________________
  14017.  
  14018.              Thus, at the end of the end of the instruction, DI is in a new
  14019.              place and the flags are set according to the compare result. DI
  14020.              is incremented by a byte for the byte instructions; it is
  14021.              incremented by a word for the word instructions. The same pattern
  14022.              holds true for decrementing DI.
  14023.  
  14024.              We set the direction flag with the instruction STD (set direction
  14025.              flag) and we clear the direction flag by using CLD (clear
  14026.              direction flag). It only needs to be set or cleared once, and
  14027.              this should be done before starting the operation. DF is only
  14028.              changed by those specific instructions from the program - it
  14029.              can't be changed by any arithmetical or logical operation on the
  14030.              chip.
  14031.  
  14032.              If you have a string and you are looking for a specific number,
  14033.              (27 for instance), you simply put that number in AL (or AX) and
  14034.              run a loop. If:
  14035.  
  14036.                   long_string  db 5000 dup (?)
  14037.  
  14038.              contains data and we want to look for a 27d then the operation
  14039.              is:
  14040.  
  14041.                  lea  di, long_string{3}
  14042.                  mov  al, 27
  14043.                  cld
  14044.  
  14045.              search_loop:
  14046.                  scasb
  14047.                  jne  search_loop
  14048.  
  14049.              on exiting, DI will point 1 PAST the matching byte (word). You
  14050.              move back one byte (word) to find the match. Why would anyone
  14051.              want this instruction? With a 0 in AL, it will find the end of a
  14052.              C (0d terminated) string quickly. Also, that number 27 is no
  14053.              accident. 27d is the ASCII escape character. For a lot of
  14054.              hardware, 27d indicates that the bytes that follow are not ASCII
  14055.              characters but are technical information. For instance, on my
  14056.              printer the sequence (27d, 65d, 0d, 11d) sets tabs every 11
  14057.              columns. SCAS can find where these substrings are so the program
  14058.              can operate on them.
  14059.  
  14060.              In order to use string instructions, we need strings to work on.
  14061.              The one we will use is called CH1STR.OBJ. It is in \XTRAFILE.  It
  14062.              is an object file that contains one data segment containing a
  14063.              string of lower case characters. The string is several thousand
  14064.              bytes long, it is terminated by 0, and it contains ONLY the lower
  14065.              case letters (a-z). It is the first draft of part of chapter 0
  14066.              with all punctuation, numbers, spaces, carriage returns etc.
  14067.              deleted. All upper case letters have been converted to lower case
  14068.              so we don't have to worry about the difference between A and a, Q
  14069.              and q.
  14070.  
  14071.              The name of the array in CH1STR.OBJ is CH1STR and it is defined:
  14072.              ____________________
  14073.  
  14074.                 3. Assuming that long_string's segment address is in ES.
  14075.  
  14076.  
  14077.              Chapter 19 - Strings                                          193
  14078.              ____________________
  14079.  
  14080.  
  14081.                  PUBLIC  ch1str
  14082.  
  14083.              so that you can access it with the SEG and OFFSET operators.
  14084.              First, let's find out how long the string is.{4}
  14085.  
  14086.              MYPROG1.ASM
  14087.              ; - - - - - - - - - -
  14088.              STRINGSTUFF  SEGMENT  PUBLIC 'DATA'
  14089.              EXTRN  ch1str:BYTE
  14090.              STRINGSTUFF  ENDS
  14091.              ; - - - - - - - - - -
  14092.              ;- - - - - - - - - - PUT CODE BELOW THIS LINE
  14093.  
  14094.                  mov  ax, seg ch1str      ; segment address of ch1str
  14095.                  mov  es, ax
  14096.  
  14097.                  mov  di, offset ch1str   ; offset address of ch1str
  14098.                  mov  al, 0               ; try to match zero
  14099.                  cld                      ; increment (DF = 0)
  14100.  
  14101.              string_end_loop:
  14102.                  scasb
  14103.                  jne  string_end_loop
  14104.  
  14105.                  dec  di                  ; back up one
  14106.                  mov  ax, di
  14107.                  sub  ax, offset ch1str
  14108.                  call print_unsigned
  14109.  
  14110.              ;- - - - - - - - - - PUT CODE ABOVE THIS LINE
  14111.  
  14112.              In all these string operations, we need to be careful about
  14113.              boundary conditions. What if there is no valid data? What if
  14114.              there is one valid item? What if the string is empty?
  14115.  
  14116.              On exiting the loop, DI will point 1 past the first 0d, so we
  14117.              need to back up one to point to the first 0d. Then subtracting
  14118.              the starting position will give us the count.{5} Try it out and
  14119.              find out how long it is. Since we now have 3 object modules, the
  14120.              link instruction must read:
  14121.  
  14122.                  link  myprog+ch1str+asmhelp ;
  14123.  
  14124.              assuming that you name your program myprog.asm. Save the result
  14125.              because we will need to use this number several times.
  14126.  
  14127.              ____________________
  14128.  
  14129.                 4. Just to keep it from being too easy, I have put garbage
  14130.              both in front of the string and behind the string. That means
  14131.              that the string length is shorter than the length of the object
  14132.              file and the string does not start with the first byte of the
  14133.              object file.
  14134.  
  14135.                 5. If the first byte in the string is 0d, we move one, then
  14136.              move back one which gives the length zero.
  14137.  
  14138.  
  14139.              The PC Assembler Tutor                                        194
  14140.              ______________________
  14141.  
  14142.              You will notice that we have gotten the segment address by using
  14143.              the SEG operator. You don't need to know the name of the segment.
  14144.              The segment doesn't even have to be PUBLIC. As long as the
  14145.              VARIABLE is either in the same file or is in another file and
  14146.              PUBLIC, the linker will find the correct segment address and put
  14147.              it there.
  14148.  
  14149.  
  14150.              To make things a little more complicated, we will make another
  14151.              infinite loop. This time you will enter a character, and the
  14152.              program will find the first occurance of that character. We need
  14153.              to add some error checking here. Since you will probably be
  14154.              dreaming about taking your next vacation in Hawaii while you are
  14155.              entering the data, a few characters that don't exist in the
  14156.              string (things like G $ ? ~ ) might creep in. It would be
  14157.              possible to run way past the end of the string before you found
  14158.              that character. We'll put the length of the string (from the last
  14159.              program) in CX, have a regular loop so we can't go too far, and
  14160.              jump out of the loop if we find a match.
  14161.  
  14162.              MYPROG2.ASM
  14163.              ; - - - - - - - - - -
  14164.              STRINGSTUFF  SEGMENT  PUBLIC 'DATA'
  14165.              EXTRN  ch1str:BYTE
  14166.              STRINGSTUFF  ENDS
  14167.              ; - - - - - - - - - -
  14168.              ;- - - - - - - - - - PUT CODE BELOW THIS LINE
  14169.  
  14170.                  mov  ax, seg ch1str
  14171.                  mov  es, ax
  14172.  
  14173.              outer_loop:
  14174.                  call get_ascii_byte           ; returns character in al
  14175.                  mov  cx, $$$$$$$              ; enter string length here
  14176.  
  14177.                  mov  di, offset ch1str
  14178.                  cld                           ; increment (DF = 0)
  14179.  
  14180.              string_end_loop:
  14181.                  scasb
  14182.                  je   after_loop               ; if equal, we found the char
  14183.                  loop string_end_loop
  14184.  
  14185.                  mov  ax, 0                    ; we fell through the loop
  14186.                  call print_unsigned
  14187.                  jmp  outer_loop
  14188.  
  14189.              after_loop:
  14190.                  mov  ax, di                   ; move for printing
  14191.                  sub  ax, offset ch1str        ; number of bytes
  14192.                  call print_unsigned
  14193.  
  14194.                  jmp  outer_loop
  14195.  
  14196.  
  14197.              ;- - - - - - - - - - PUT CODE ABOVE THIS LINE
  14198.  
  14199.  
  14200.  
  14201.              Chapter 19 - Strings                                          195
  14202.              ____________________
  14203.  
  14204.              Those dollar signs are the place to enter the exact length of the
  14205.              string that you got from the last program. This time we jump out
  14206.              of the loop if we find a match; DI will be 1 past the matching
  14207.              character, but this will give us the right count (if we find the
  14208.              character in the first space, we increment once). If we can't
  14209.              find a match we fall through the loop and print a 0. Remember to
  14210.              link all 3 modules when you run the program. Run the program and
  14211.              then we'll move forward.
  14212.  
  14213.              This type of thing is so common with string operations that there
  14214.              is a special prefix for SCAS and all other string operations
  14215.              which makes the coding simpler. It has several forms:
  14216.  
  14217.                  rep       decrement cx ; repeat if cx is not zero
  14218.                  repe      decrement cx ; repeat if cx not zero and zf = 1
  14219.                  repz      decrement cx ; repeat if cx not zero and zf = 1
  14220.                  repne     decrement cx ; repeat if cx not zero and zf = 0
  14221.                  repnz     decrement cx ; repeat if cx not zero and zf = 0
  14222.  
  14223.              REP is for the move instructions which we will see later - it
  14224.              won't work here. For each prefix, if either (or both) of the
  14225.              conditions is not true, the repitition stops. For instance, with
  14226.              REPE, if cx is zero, and/or if the comparison was not equal (so
  14227.              the zero flag was not set), the instruction will stop. For our
  14228.              program, the coding is:
  14229.  
  14230.                  repne scasb
  14231.  
  14232.              That's it. That replaces the whole inner loop. Here is our new
  14233.              coding of the last program.
  14234.  
  14235.              MYPROG3.ASM
  14236.              ; - - - - - - - - - -
  14237.              STRINGSTUFF  SEGMENT  PUBLIC 'DATA'
  14238.              EXTRN  ch1str:BYTE
  14239.              STRINGSTUFF  ENDS
  14240.              ; - - - - - - - - - -
  14241.              ;- - - - - - - - - - PUT CODE BELOW THIS LINE
  14242.  
  14243.                  mov  ax, STRINGSTUFF
  14244.                  mov  es, ax
  14245.                  cld                           ; increment (DF = 0)
  14246.  
  14247.              outer_loop:
  14248.                  call get_ascii_byte           ; returns character in al
  14249.                  mov  cx, $$$$$$$              ; enter string length here
  14250.                  lea  di, ch1str               ; address of string
  14251.  
  14252.                  repne  scasb
  14253.  
  14254.                  je   found_the_char           ; an equal comparison
  14255.                  mov  ax, 0                    ; we didn't find a match
  14256.                  call print_unsigned
  14257.                  jmp  outer_loop
  14258.  
  14259.              found_the_char:
  14260.                  mov  ax, di                   ; move for printing
  14261.  
  14262.  
  14263.              The PC Assembler Tutor                                        196
  14264.              ______________________
  14265.  
  14266.                  sub  ax, offset ch1str        ; number of bytes
  14267.                  call print_unsigned
  14268.                  jmp  outer_loop
  14269.  
  14270.              ;- - - - - - - - - - PUT CODE ABOVE THIS LINE
  14271.  
  14272.              There are two possibilities for exiting the 'repne scasb'
  14273.              instruction. Either we found an equal comparison or we exhausted
  14274.              all the characters in ch1str. If we found an equal comparison, JE
  14275.              will send us to the print routine. Otherwise we print a 0 because
  14276.              we finished the loop without finding anything.
  14277.  
  14278.  
  14279.              STOS
  14280.  
  14281.              We can ask the operating system to allocate memory for us while
  14282.              the program is running.{6} When you get it, however, it will
  14283.              contain trash. The fast way to clear it is to use STOS (store to
  14284.              string). The instruction is:
  14285.  
  14286.                  stosb
  14287.              or:
  14288.                  stosw
  14289.  
  14290.              The equivalent action (not counting changing the value of DI) is:
  14291.  
  14292.                  mov  es:[di], ax    ; or AL for byte moves
  14293.  
  14294.              Once again (1) the pointer is the ES:DI pair, which is mandatory,
  14295.              and (2) DI is incremented or decremented (by 1 for byte, by 2 for
  14296.              word) depending on the status of DF, the direction flag. The
  14297.              instruction moves a byte (a word) from the AL (AX) register to
  14298.              the memory address pointed to by ES:DI. We can use the REP{7}
  14299.              instruction to speed things up a bit. If we have a 11,872 word
  14300.              block of memory, we can clear it with the following instructions:
  14301.  
  14302.              ; - - - - - - -
  14303.              DATASTUFF  SEGMENT
  14304.              my_bufferdw  11872 dup (?)
  14305.              DATASTUFF  ENDS
  14306.              ; - - - - - - -
  14307.  
  14308.                  mov  ax, seg my_buffer
  14309.                  mov  es, ax
  14310.                  cld                           ; increment (DF = 0)
  14311.  
  14312.                  mov  ax, 0               ; clear the buffer with 0s
  14313.                  mov  di, offset my_buffer
  14314.                  mov  cx, 11872
  14315.                  rep  stosw
  14316.              ____________________
  14317.  
  14318.                 6. Cf. You-know-who's Programmer's Guide to You-know-what or
  14319.              "DOS Programmer's Reference."
  14320.  
  14321.                 7. There is no comparison here, so REPE or REPNE doesn't make
  14322.              any sense.
  14323.  
  14324.  
  14325.              Chapter 19 - Strings                                          197
  14326.              ____________________
  14327.  
  14328.  
  14329.              That's as fast as it gets. Why does the STOS instruction use AX?
  14330.              Because that's the register that port i/o uses. If you are
  14331.              writing a communications program, you need speed. You can have
  14332.              the following:
  14333.  
  14334.              ; - - - - - - - - -
  14335.              DATASTUFF  SEGMENT
  14336.              port_address   dw   0F2A8h        ; this address is legal but
  14337.                                                ; there's nothing there.
  14338.              input_buffer  db   4000h     dup (?)
  14339.              output_buffer  db   4000h     dup (?)
  14340.              DATASTUFF  ENDS
  14341.              ; - - - - - - - - -
  14342.  
  14343.                  mov  ax, DATASTUFF
  14344.                  mov  es, ax
  14345.                  cld                           ; increment (DF = 0)
  14346.                  mov  di, offset input_buffer
  14347.                  mov  dx, port_address
  14348.  
  14349.              input_loop:
  14350.                  in   al, dx
  14351.                  stosb
  14352.                  jmp  input_loop
  14353.              ; - - - - - - - - -
  14354.  
  14355.              A real program would be much more complicated because we would
  14356.              have to check to see if data was ready to come in and we might
  14357.              need to check the data for errors. Also we would occasionally
  14358.              have to clear the buffer. The port address F2A8h is just an
  14359.              arbitrary address. It's a legal address but there's nothing
  14360.              there.
  14361.  
  14362.              We should write a program, so let's input a character and have it
  14363.              fill the screen. We'll leave the last line of the screen alone so
  14364.              you can see your input. Move your cursor to the last line before
  14365.              beginning the program.
  14366.  
  14367.              ; - - - - - ENTER CODE BELOW THIS LINE
  14368.  
  14369.                  mov  ax, 0B800h          ; or 0B000h for a monochrome card
  14370.                  mov  es, ax
  14371.                  cld                      ; increment (DF = 0)
  14372.  
  14373.  
  14374.              outer_loop:
  14375.                  call get_ascii_byte      ; AL = fill char from input
  14376.                  mov  ah, 07h             ; black background, white letters
  14377.                  sub  di, di              ; set di to zero
  14378.                  mov  cx, 1920            ; 24 lines X 80 chars
  14379.                  rep  stosw
  14380.                  jmp  outer_loop
  14381.  
  14382.              ; - - - - - ENTER CODE ABOVE THIS LINE
  14383.  
  14384.              If you have a monochrome card, the segment address is 0B000h. If
  14385.  
  14386.  
  14387.              The PC Assembler Tutor                                        198
  14388.              ______________________
  14389.  
  14390.              you have a color card and are in text mode, the segment address
  14391.              should be 0B800h. This fills the first 24 lines with the input
  14392.              character. The STOS instruction has no effect on the cursor.
  14393.  
  14394.  
  14395.              LODS
  14396.  
  14397.              The opposite of STOS is LODS (load string) It moves a byte (word)
  14398.              from the string to the AL (AX) register. This time, for a change,
  14399.              we use the SI register as a pointer, and the default register is
  14400.              DS.{8} As always, SI is incremented or decremented by a byte
  14401.              (word) depending on the setting of DF, the direction flag. The
  14402.              two possibilities are:
  14403.  
  14404.                  lodsb
  14405.              and
  14406.                  lodsw
  14407.  
  14408.              The equivalent action (not counting changing the value of SI) is:
  14409.  
  14410.                  mov  ax, [si]  ; or AL for byte moves
  14411.  
  14412.              This is an instruction for people that write device drivers. You
  14413.              could use it if you are sending a string of characters to the
  14414.              printer, but that's about it. Code for doing that would have the
  14415.              following form:
  14416.  
  14417.              ; - - - - - - -
  14418.              buffer   db   1000 dup (?)
  14419.              ; - - - - - - -
  14420.                  lea  si, buffer   ; the buffer must be in the ds segment
  14421.                  cld                      ; increment
  14422.  
  14423.              out_loop:
  14424.                  lodsb
  14425.                  and  al, al              ; if 0, end of string
  14426.                  jz   quit_loop
  14427.  
  14428.                  mov  dl, al              ; move character to dl {9}
  14429.                  mov  ah, 5               ; int 21h function 5
  14430.                  int  21h                 ; print a character
  14431.                  jmp  out_loop
  14432.  
  14433.              quit_loop:
  14434.                  ; continue with the program
  14435.  
  14436.              If you actually run this program, many printers will not print
  14437.              anything until they get an end of line signal ( 10d, 13d).
  14438.  
  14439.  
  14440.              ____________________
  14441.  
  14442.                 8. Register DS can be overriden. We'll talk about that in the
  14443.              second part of this chapter.
  14444.  
  14445.                 9. Int 21h (AH = 5) prints one character from DL to the
  14446.              printer. Why it's DL and not AL is a mystery.
  14447.  
  14448.  
  14449.  
  14450.  
  14451.                                                                            199
  14452.  
  14453.              By this time you may have become annoyed by the fact that there
  14454.              is no instruction for moving data from one place in memory to
  14455.              another. That is, you can't have:
  14456.  
  14457.                  mov  variable2, variable1
  14458.  
  14459.              Instead you have to have:
  14460.  
  14461.                  mov  ax, variable1
  14462.                  mov  variable2, ax
  14463.  
  14464.              There is one instruction, however, where you can move a BLOCK of
  14465.              data from one place in memory to another. It is called MOVS. As
  14466.              usual with these instructions, there are two forms.
  14467.  
  14468.                  movsb
  14469.  
  14470.              moves a byte from DS:SI to ES:DI and increments or decrements SI
  14471.              and DI by one, depending on the setting of DF, the direction
  14472.              flag. Notice that either both are incremented or both are
  14473.              decremented. You can't have one pointer incrementing while the
  14474.              other one is decrementing.
  14475.  
  14476.                  movsw
  14477.  
  14478.              moves a word from DS:SI to ES:DI and increments or decrements SI
  14479.              and DI by two, depending on the setting of DF, the direction
  14480.              flag. This requires the same amount of setup as all the other
  14481.              routines we have looked at so far, so it is not efficient to use
  14482.              it for just a few bytes. For 30 or so, it is very efficient. This
  14483.              has the equivalent effect (except for changing DI and SI) as:
  14484.  
  14485.                  mov  WORD PTR es:[di], ds:[si]   ; or BYTE PTR for bytes
  14486.  
  14487.              We are going to write some subroutines which copy strings from
  14488.              one place in memory to another.{1} But first we need to review
  14489.              text strings.
  14490.  
  14491.              The text string world is divided into Pascal and C. A Pascal
  14492.              string has its length in the first byte and the first character
  14493.              in the second byte. Since the length is in one byte, the string
  14494.              length may only be 0 to 255. You read the first byte of the
  14495.              string to get the length.
  14496.  
  14497.              A C string can have any length. The end of a C string is marked
  14498.              by a byte with the value 0d. This is not the character '0', it is
  14499.              the number 0 (0hex). In order to find the end of a C string, you
  14500.              need to check each character to see if it is 0h.
  14501.  
  14502.              We'll do the Pascal string first. We are going to pass the
  14503.              ____________________
  14504.  
  14505.                 1. For the technically minded, these routines will be only
  14506.              half of a real life subroutine, since they assume that the two
  14507.              strings do not overlap. In robust subroutines, these routines
  14508.              would be the section for when the destination address is lower
  14509.              than the source address.
  14510.  
  14511.  
  14512.              The PC Assembler Tutor                                        200
  14513.              ______________________
  14514.  
  14515.              addresses of the strings. If you have the Pascal call:
  14516.  
  14517.                  move_pascal_string (from_string, to_string) ;
  14518.  
  14519.              The first thing you need to know is that Pascal pushes things on
  14520.              the stack from left to right. In other words, Pascal will
  14521.              generate the following code:
  14522.  
  14523.                  lea  ax, from_string
  14524.                  push ax
  14525.                  lea  ax, to_string
  14526.                  push ax
  14527.                  call move_pascal_string
  14528.  
  14529.              We will start by assuming both near data (all data is in DS), and
  14530.              near subroutines. After setting up BP, the stack will look like
  14531.              this:{2}
  14532.  
  14533.                       from_string address           bp + 6
  14534.                       to_string address             bp + 4
  14535.                       old IP                        bp + 2
  14536.                bp ->  old BP                        bp + 0
  14537.  
  14538.              Here's the subroutine. Remember, PUSHREGS and POPREGS are macros:
  14539.  
  14540.              ; ----------
  14541.              move_pascal_string  proc  near
  14542.  
  14543.                  FROM_PTR  EQU  [bp+6]
  14544.                  TO_PTR    EQU  [bp+4]
  14545.  
  14546.                  push bp                       ; set up bp
  14547.                  mov  bp, sp
  14548.                  pushf                         ; push the flags
  14549.                  PUSHREGS  cx, si, di, es      ; push the registers
  14550.                  push ds                       ; move ds to es
  14551.                  pop  es
  14552.  
  14553.                  mov  si, FROM_PTR             ; load pointers
  14554.                  mov  di, TO_PTR
  14555.                  cld                           ; clear DF (increment)
  14556.  
  14557.                  sub  cx, cx                   ; zero cx
  14558.                  mov  cl, [si]                 ; length to cl
  14559.                  inc  cx                       ; increment count by one
  14560.  
  14561.                  rep  movsb                    ; the actual move
  14562.  
  14563.                  POPREGS cx, si, di, es
  14564.                  popf                          ; pop the flags
  14565.                  pop  bp
  14566.                  ret (4)                       ; pop pointers and return
  14567.  
  14568.              ____________________
  14569.  
  14570.                 2. If you forgot about BP, go back to the chapter on
  14571.              subroutines.
  14572.  
  14573.  
  14574.              Chapter 19 - Strings                                          201
  14575.              ____________________
  14576.  
  14577.              move_pascal_string endp
  14578.              ; ----------
  14579.  
  14580.              The count is increased by one since we need to move not only the
  14581.              text, but the count itself. If the length is 0, we still need to
  14582.              move one byte - the count byte. The value in DS is moved to ES
  14583.              with a PUSH and a POP. You cannot move directly from one segment
  14584.              register to another. Also, at the return we POP 4 bytes (2 words)
  14585.              to get the pointers off the stack. Remember, in Pascal, it is the
  14586.              subroutine's responsibility to get rid of the arguments from a
  14587.              subroutine call.
  14588.  
  14589.              You will notice that this time we saved the flags register. Why?
  14590.              Because we are clearing DF. When we return from the subroutine,
  14591.              we want DF to be exactly the same as it was on entry to the
  14592.              subroutine. The calling program may have DF set in some special
  14593.              way and we don't want to interfere with that.
  14594.  
  14595.              There are three flags which I will call 'hard' flags. Once they
  14596.              are set they do not change. These are (1) TF, the trap flag, (2)
  14597.              IEF, the interrupt enable flag, and (3) DF, the direction flag.
  14598.              The 'soft' flags are CF, OF, ZF, etc. If you call a subroutine
  14599.              you expect CF, OF, ZF etc. to be unreliable, but you expect these
  14600.              three 'hard' flags to remain the same. TF is the domain of a
  14601.              debugger, so it is none of your business. IEF is only of interest
  14602.              to you if you are writing an interrupt procedure.{3} The third
  14603.              one, DF, is your concern. If you use DF in a subroutine, you MUST
  14604.              save the flags to ensure that the DF flag has the same value at
  14605.              the return that it had on entry.
  14606.  
  14607.  
  14608.              Now for the C subroutine. If we have a C subroutine call:
  14609.  
  14610.                  move_c_string ( from_string, to_string ) ;
  14611.  
  14612.              C pushes things on the stack from right to left (the exact
  14613.              opposite of Pascal). The C complier will generate the following
  14614.              code.
  14615.  
  14616.                  lea  ax, to_string
  14617.                  push ax
  14618.                  lea  ax, from_string
  14619.                  push ax
  14620.                  call move_pascal_string
  14621.                  add  sp, 4
  14622.  
  14623.              After setting up BP, the stack will look like this:
  14624.  
  14625.                       to_string address             bp + 6
  14626.                       from_string address           bp + 4
  14627.                       old IP                        bp + 2
  14628.                bp ->  old BP                        bp + 0
  14629.              ____________________
  14630.  
  14631.                 3. If you do an interrupt procedure you don't have to worry
  14632.              because INT automatically saves the flags while clearing IEF, and
  14633.              IRET restores the flags on exiting.
  14634.  
  14635.  
  14636.              The PC Assembler Tutor                                        202
  14637.              ______________________
  14638.  
  14639.  
  14640.              Here's the C subroutine:
  14641.  
  14642.              ; ----------
  14643.              move_c_string  proc  near
  14644.  
  14645.                  FROM_PTR  EQU  [bp+4]
  14646.                  TO_PTR    EQU  [bp+6]
  14647.  
  14648.                  push bp                       ; set up bp
  14649.                  mov  bp, sp
  14650.                  pushf                         ; push the flags
  14651.                  PUSHREGS  ax, si, di, es      ; push the registers
  14652.                  push ds                       ; move ds to es
  14653.                  pop  es
  14654.  
  14655.                  mov  si, FROM_PTR             ; load pointers
  14656.                  mov  di, TO_PTR
  14657.                  cld                           ; clear DF (increment)
  14658.  
  14659.              move_loop:
  14660.                  lodsb                         ; source to al
  14661.                  stosb                         ; al to destination
  14662.                  and  al, al                   ; check for 0
  14663.                  jnz  move_loop
  14664.  
  14665.  
  14666.                  POPREGS ax, si, di, es
  14667.                  popf                          ; pop the flags
  14668.                  pop  bp
  14669.                  ret
  14670.  
  14671.              move_c_string endp
  14672.              ; ----------
  14673.  
  14674.  
  14675.              We set up the routine the same way, but we cannot use MOVSB. We
  14676.              need to check each individual byte to see if it is 0 hex, so we
  14677.              move it to AL, move it from AL to the destination, and then check
  14678.              AL for 0. Also note that we did not pop the addresses off the
  14679.              stack with the return statement, since in C it is the calling
  14680.              program's responsibility to do that. If you look at the calling
  14681.              code above, you will see:
  14682.  
  14683.                  add  sp, 4
  14684.  
  14685.              which gets rid of the two pointers from the stack. Remember, the
  14686.              stack grows downward, so you ADD to decrease the size of the
  14687.              stack.
  14688.  
  14689.              Let's do the same Pascal program again, but this time use long
  14690.              pointers, that is, give both the segment and offset of the
  14691.              string. This means that we will be able to move from any place in
  14692.              memory to any place in memory. Here is the calling code.
  14693.  
  14694.  
  14695.                  mov  ax, segment from_string
  14696.  
  14697.  
  14698.              Chapter 19 - Strings                                          203
  14699.              ____________________
  14700.  
  14701.                  push ax
  14702.                  mov  ax, offset from_string
  14703.                  push ax
  14704.                  mov  ax, segment to_string
  14705.                  push ax
  14706.                  mov  ax, offest to_string
  14707.                  push ax
  14708.                  call move_pascal_string
  14709.  
  14710.              We will still keep it a near subroutine. After setting up BP, the
  14711.              stack will look like this:
  14712.  
  14713.                       from_string segment           bp + 10
  14714.                       from_string offset            bp + 8
  14715.                       to_string segment             bp + 6
  14716.                       to_string offset              bp + 4
  14717.                       old IP                        bp + 2
  14718.                bp ->  old BP                        bp + 0
  14719.  
  14720.              Here's the subroutine:
  14721.  
  14722.              ; ----------
  14723.              move_pascal_string  proc  near
  14724.  
  14725.                  FROM_PTR  EQU  [bp+8]
  14726.                  TO_PTR    EQU  [bp+4]
  14727.  
  14728.                  push bp                       ; set up bp
  14729.                  mov  bp, sp
  14730.                  pushf                         ; push the flags
  14731.                  PUSHREGS  cx, si, di, ds, es  ; push the registers
  14732.  
  14733.                  lds  si, FROM_PTR             ; load pointers
  14734.                  les  di, TO_PTR
  14735.                  cld                           ; clear DF (increment)
  14736.  
  14737.                  sub  cx, cx                   ; zero cx
  14738.                  mov  cl, [si]                 ; length to cl
  14739.                  inc  cx                       ; increment count by one
  14740.  
  14741.                  rep  movsb                    ; the actual move
  14742.  
  14743.                  POPREGS cx, si, di, ds, es
  14744.                  popf                          ; pop the flags
  14745.                  pop  bp
  14746.                  ret (8)                       ; pop pointers and return
  14747.  
  14748.              move_pascal_string endp
  14749.              ; ----------
  14750.  
  14751.              This takes slightly less code since we load SI and DS at the same
  14752.              time (with LDS -load DS) and we load DI and ES at the same time
  14753.              (with LES - load ES). Remember, 8086 instructions which move an
  14754.              offset:segment pair always have the offset in low memory and the
  14755.              segment in high memory; the offset is the first two bytes and the
  14756.              segment is the next two bytes.
  14757.  
  14758.  
  14759.  
  14760.              The PC Assembler Tutor                                        204
  14761.              ______________________
  14762.  
  14763.              We changed the EQU statements, and the return statement is now:
  14764.  
  14765.                  ret (8)
  14766.  
  14767.              so we take 8 bytes (4 words) off the stack, but the rest is the
  14768.              same.
  14769.  
  14770.  
  14771.              CMPS
  14772.  
  14773.              The final instruction in this group is CMPS, and as usual, it
  14774.              comes in two varieties.
  14775.  
  14776.                  cmpsb
  14777.  
  14778.              compares the byte addressed by DS:SI to the byte addressed by
  14779.              ES:DI. It is the same as the CMP instruction. It moves both bytes
  14780.              into the 8086, subtracts the DI byte from the SI byte and sets
  14781.              the flags. The two bytes in memory remain unchanged. You can look
  14782.              at the flags to see which byte is larger, or if they are equal.
  14783.              As usual, both SI and DI are incremented or decremented by one,
  14784.              depending on the setting of DF, the direction flag.
  14785.  
  14786.                  cmpsw
  14787.  
  14788.              compares the word addressed by DS:SI to the word addressed by
  14789.              ES:DI. It is the same as the CMP instruction. It moves both words
  14790.              into the 8086, subtracts the DI word from the SI word and sets
  14791.              the flags. The two words in memory remain unchanged. You can then
  14792.              look at the flags to see which word is larger, or if they are
  14793.              equal. Both SI and DI are incremented or decremented by two,
  14794.              depending on the setting of DF, the direction flag.  This
  14795.              instruction has the same effect on the flags as:
  14796.  
  14797.                  push ax
  14798.                  mov  ax, ds:[si]    ; or AL for bytes
  14799.                  cmp  ax, es:[di]    ; performs ( DS:[si] - ES:[DI] )
  14800.                  pop  ax
  14801.  
  14802.  
  14803.              What use is this instruction? It is possible to use this for word
  14804.              find, and we will do that later, but it is a little
  14805.              unsophisticated for that. It is great for data verification,
  14806.              however.
  14807.  
  14808.              When you use the DISKCOMP utility in DOS which compares two
  14809.              floppy disks, it reads each of the disks sector by sector, and
  14810.              then compares them. A sector is 512 bytes. The code for this
  14811.              utility looks like this:
  14812.  
  14813.  
  14814.              ; - - - - - DATA - - - - -
  14815.              error_message db   "Sectors are not the same", 0
  14816.              disk1_buffer  db   512 dup (?)
  14817.              disk2_buffer  db   512 dup (?)
  14818.  
  14819.              ; - - - - - CODE - - - - -
  14820.  
  14821.  
  14822.              Chapter 19 - Strings                                          205
  14823.              ____________________
  14824.  
  14825.  
  14826.              get_next_sector:
  14827.  
  14828.              ; the code for reading one sector from each disk goes here.
  14829.              ; then we have the code to compare the two sets of data.
  14830.  
  14831.                  mov  si, offset disk1_buffer
  14832.                  mov  di, offset disk2_buffer
  14833.  
  14834.                  mov  cx, 256                  ; 512 / 2 = 256
  14835.                  repe cmpsw
  14836.                  je   get_next_sector
  14837.  
  14838.                  lea  ax, error_message   ; we had an unequal comparison
  14839.                  call print_string
  14840.                  jmp  get_next_sector
  14841.  
  14842.              ; - - - - - - - - - -
  14843.  
  14844.  
  14845.              We do a word compare since it takes only half as many steps. If
  14846.              there is an unequal comparison at any time, the REPE instruction
  14847.              will terminate the loop. We can test for this inequality with JE
  14848.              or JNE. In this example we assume that DS and ES have the same
  14849.              segment address.
  14850.  
  14851.              Any time you need to verify data, this is the instruction to use.
  14852.  
  14853.              We are going to build a word search program. It is not very
  14854.              valuable since 'a' will not match 'A', but it is a good exercise
  14855.              to look at CMPS. We will use ch1str.obj, the file we used at the
  14856.              beginning of the chapter, as the text file and you can try to
  14857.              find individual words in the file. Remember, the file is
  14858.              continuous characters (no spaces), and all characters are small.
  14859.              If you didn't save the file length, you will have to run that
  14860.              program again to find the length of the file.
  14861.  
  14862.              Here's the word_search program:
  14863.  
  14864.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  14865.              EXTRN  ch1str:BYTE
  14866.              entry_banner     db  13,10, "Enter a word for a word search", 0
  14867.              no_match_banner  db  "There was no match", 0
  14868.              input_buffer     db   80 dup (?)
  14869.              letter_count     dw   ?
  14870.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  14871.  
  14872.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  14873.                     mov   ax, seg ch1str      ; load es register
  14874.                     mov   es, ax
  14875.                     cld                       ; clear DF (increment)
  14876.  
  14877.              big_loop:
  14878.                     ; get a word for the word search
  14879.                     mov   ax, offset entry_banner
  14880.                     call  print_string
  14881.                     mov   ax, offset input_buffer
  14882.  
  14883.  
  14884.              The PC Assembler Tutor                                        206
  14885.              ______________________
  14886.  
  14887.                     call  get_string
  14888.  
  14889.                     ; find the end of string
  14890.                     mov   al, 0               ; compare with 0
  14891.                     mov   bx, offset input_buffer
  14892.                     mov   cx, 0               ; letter count
  14893.              letter_count_loop:
  14894.                     cmp   al, [bx]            ; compare to 0
  14895.                     je    end_of_count_loop
  14896.                     inc   cx                  ; increment count
  14897.                     inc   bx                  ; increment pointer
  14898.                     jmp   letter_count_loop
  14899.              end_of_count_loop:
  14900.                     jcxz  big_loop      ; if cx = 0, string is empty so redo
  14901.                     mov   letter_count, cx   ; store our count
  14902.  
  14903.                     ; look for word match
  14904.                     mov   di, offset ch1str
  14905.                     mov   cx, $$$$            ; $$$$ = length of ch1str
  14906.                     sub   cx, letter_count    ; calculate last possible match
  14907.  
  14908.              word_search_loop:
  14909.                     push  di                  ; start of search
  14910.                     push  cx                  ; count for ch1str
  14911.                     mov   si, offset input_buffer
  14912.                     mov   cx, letter_count
  14913.                     repe  cmpsb               ; the actual comparison
  14914.                     je    found_it           ; if equal, we have a match
  14915.  
  14916.                     ; no match. are we finished?
  14917.                     pop   cx
  14918.                     pop   di
  14919.                     inc   di               ; move to next starting address
  14920.                     loop  word_search_loop
  14921.  
  14922.                     ; we fell through. finished, but no match
  14923.                     mov   ax, offset no_match_banner
  14924.                     call  print_string
  14925.                     jmp   big_loop
  14926.  
  14927.              found_it:
  14928.                     pop   cx                  ; clear cx off the stack
  14929.                     pop   di                  ; start of the match
  14930.                     mov   si, offset input_buffer
  14931.                     mov   cx, 25              ; move 25 characters to buffer
  14932.              transfer_loop:
  14933.                     mov   al, es:[di]
  14934.                     mov   [si], al
  14935.                     inc   si
  14936.                     inc   di
  14937.                     loop  transfer_loop
  14938.                     mov   BYTE PTR [si], 0    ; end of a C string
  14939.  
  14940.                     mov   ax, offset input_buffer
  14941.                     call  print_string
  14942.                     jmp   big_loop
  14943.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  14944.  
  14945.  
  14946.              Chapter 19 - Strings                                          207
  14947.              ____________________
  14948.  
  14949.  
  14950.  
  14951.              The code is so long that the whole assembler file has been put on
  14952.              disk so you don't have to do all the typing. The pathname is
  14953.              \XTRAFILE\COMPARE.ASM. All you need to do is enter the length of
  14954.              ch1str in the MOV instruction where the dollar signs are:
  14955.  
  14956.                     mov   cx, $$$$           ; $$$$ = length of ch1str
  14957.  
  14958.              Link with 'link  compare+ch1str+\asmhelp'.  You enter a text
  14959.              string and the program looks for an exact match in ch1str. Here
  14960.              is how the program is structured.
  14961.  
  14962.              First, the program prompts you to enter a string. The program
  14963.              then counts the number of bytes in the string. It must have a
  14964.              non-zero length or the program will prompt you again for a
  14965.              string. The program then starts at the beginning of the text. It
  14966.              saves a copy of the pointer to the start of the comparison so if
  14967.              we fail we can start over again at the next character. The actual
  14968.              comparison is:
  14969.  
  14970.                  repe cmpsb
  14971.  
  14972.              If that makes it through all the letters in the search string,
  14973.              REPE will quit because CX = 0, not because we have an unequal
  14974.              character. If the comparison failed we pop DI (the text pointer)
  14975.              and start at the next character.
  14976.  
  14977.              If there is a match, we move 25 characters (starting with the
  14978.              matching characters) from the text to the buffer. It is necessary
  14979.              to move these because when you call print_string, the string must
  14980.              be in the DATASTUFF segment, and ch1str isn't. We haven't used
  14981.              MOVSB here because ES and DS are in the wrong place. For 25
  14982.              characters there is only a marginal advantage to setting up for
  14983.              MOVS. Finally, the 25 characters are printed. If there is no
  14984.              match, a message to that effect is printed.
  14985.  
  14986.              The text in ch1str is the first draft of chapter 1, but just for
  14987.              interest, I have hidden eight C keywords and eight of your
  14988.              favorite Middle English words in the text.{4}  See if you can
  14989.              find them.
  14990.  
  14991.  
  14992.              SEGMENT OVERRIDES
  14993.  
  14994.              Here are the string instructions and the override rules for each
  14995.              one.
  14996.  
  14997.                  LODS moves a byte or word from DS:[si] to AL or AX. You may
  14998.                  use CS:[si], SS:[si] or ES:[si].
  14999.  
  15000.                  STOS moves a byte (or a word) from AL (or AX) to ES:[di]. NO
  15001.              ____________________
  15002.  
  15003.                 4. Two hints. You might find four of these Middle English
  15004.              words in the name of a boutique. The other four of the Middle
  15005.              English words are some of your favorite monosyllabic words.
  15006.  
  15007.  
  15008.              The PC Assembler Tutor                                        208
  15009.              ______________________
  15010.  
  15011.                  OVERRIDES ARE ALLOWED.
  15012.  
  15013.                  SCAS compares AL (or AX) to the byte (or word) pointed to by
  15014.                  ES:[di]. NO OVERRIDES ARE ALLOWED.
  15015.  
  15016.                  MOVS moves a byte (or a word) from DS:[si] to ES:[di].  You
  15017.                  may use CS:[si], SS:[si] or ES:[si], but you MAY NOT
  15018.                  OVERRIDE ES:[di].
  15019.  
  15020.                  CMPS compares the byte (or a word) from DS:[si] to ES:[di].
  15021.                  You may use CS:[si], SS:[si] or ES:[si], but you MAY NOT
  15022.                  OVERRIDE ES:[di].
  15023.  
  15024.              Looking at the whole group, you may override DS:[si], but you may
  15025.              not override ES:[di]. The form of the override is strict. We will
  15026.              take MOVS as an example. Till now, the instructions were written:
  15027.  
  15028.                  movsb     ; byte move
  15029.                  movsw     ; word move
  15030.  
  15031.              If you want to do an override, the syntax is:
  15032.  
  15033.                  movs BYTE PTR ES:[di], SS:[si]
  15034.                  movs WORD PTR ES:[di], SS:[si]
  15035.  
  15036.              If you write:
  15037.  
  15038.                  movsb     ES:[di], SS:[di]
  15039.  
  15040.              you will get an assembler error. Here are all the legal forms:
  15041.  
  15042.              LODS
  15043.                  lodsb
  15044.                  lodsw
  15045.                  lods BYTE PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  15046.                  lods WORD PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  15047.  
  15048.              STOS
  15049.                  stosb
  15050.                  stosw
  15051.                  stos BYTE PTR ES:[di]         ; no override allowed
  15052.                  stos WORD PTR ES:[di]         ; no override allowed
  15053.  
  15054.              SCAS
  15055.                  scasb
  15056.                  scasw
  15057.                  scas BYTE PTR ES:[di]         ; no override allowed
  15058.                  scas WORD PTR ES:[di]         ; no override allowed
  15059.  
  15060.              MOVS
  15061.                  movsb
  15062.                  movsw
  15063.                  movs BYTE PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  15064.                  movs WORD PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  15065.  
  15066.              CMPS
  15067.                  cmpsb
  15068.  
  15069.  
  15070.              Chapter 19 - Strings                                          209
  15071.              ____________________
  15072.  
  15073.                  cmpsw
  15074.                  cmps BYTE PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  15075.                  cmps WORD PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  15076.  
  15077.  
  15078.              Just because you can do overrides with these instructions doesn't
  15079.              mean that you should. In fact, there is a problem. If you are
  15080.              using the REP instruction with an override:
  15081.  
  15082.                  rep movs  WORD PTR ES:[di], SS:[si]
  15083.  
  15084.              and the 8086 gets a hardware interrupt,{5} the 8086 forgets the
  15085.              override. What this means is that one moment you are moving data
  15086.              from the SS segment, and the next moment you are moving data from
  15087.              the same offset, but in the DS segment. This just won't do. Thus
  15088.              the rule is:
  15089.  
  15090.                  NEVER USE AN OVERRIDE WITH A REP/REPE/REPNE INSTRUCTION
  15091.  
  15092.              This actually is no hardship. Using the override adds time to the
  15093.              instruction. All you need to do is change the segment addresses
  15094.              for the duration of the string instruction, and the code will run
  15095.              faster. Of course, there is the setup time, but the break even
  15096.              point is say, 20 repeats. Here is what you would do if you needed
  15097.              an SS segment override:
  15098.  
  15099.                  push ds        ; save old DS
  15100.                  push ss        ; move SS to DS
  15101.                  pop  ds        ; the same as an SS:[di] override
  15102.  
  15103.                  rep  movsb
  15104.  
  15105.                  pop  ds        ; get old DS back
  15106.  
  15107.              The other possibility is to use LOOP instead of REP. It is
  15108.              slower, but better slower and reliable than faster and
  15109.              unreliable.
  15110.  
  15111.                  rep  movs BYTE PTR ES:[di], SS:[si]
  15112.  
  15113.              is the same as:
  15114.  
  15115.              repeat_loop:
  15116.                  movs BYTE PTR ES:[di], SS:[si]
  15117.                  loop repeat_loop
  15118.  
  15119.              There are even three forms of the LOOP instruction: LOOP, LOOPE,
  15120.              LOOPNE which are the exact counterparts to REP, REPE, REPNE.
  15121.  
  15122.  
  15123.  
  15124.  
  15125.              ____________________
  15126.  
  15127.                 5. Which can be caused by such rare occurances as your
  15128.              pressing a key on the keyboard or one of the 18 timer interrupts
  15129.              that happen each second.
  15130.  
  15131.  
  15132.              The PC Assembler Tutor                                        210
  15133.              ______________________
  15134.  
  15135.  
  15136.                                         SUMMARY
  15137.  
  15138.  
  15139.  
  15140.              LODS (load from string) moves a byte or word from DS:[si] to AL
  15141.              or AX, and increments (or decrements) SI depending on the setting
  15142.              of DF, the direction flag (by 1 for bytes and by 2 for words).
  15143.              You may use CS:[si], SS:[si] or ES:[si]. This performs the same
  15144.              action (except for changing SI) as:
  15145.  
  15146.                  mov  ax, DS:[SI]              ; or AL for bytes
  15147.  
  15148.              The allowable forms are:
  15149.  
  15150.                  lodsb
  15151.                  lodsw
  15152.                  lods BYTE PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  15153.                  lods WORD PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  15154.  
  15155.  
  15156.              STOS (store to string) moves a byte (or a word) from AL (or AX)
  15157.              to ES:[di], and increments (or decrements) DI depending on the
  15158.              setting of DF, the direction flag (by 1 for bytes and by 2 for
  15159.              words). NO OVERRIDES ARE ALLOWED. This performs the same action
  15160.              (except for changing DI) as:
  15161.  
  15162.                  mov  ES:[DI], ax              ; or AL for bytes
  15163.  
  15164.              The allowable forms are:
  15165.  
  15166.                  stosb
  15167.                  stosw
  15168.                  stos BYTE PTR ES:[di]         ; no override allowed
  15169.                  stos WORD PTR ES:[di]         ; no override allowed
  15170.  
  15171.  
  15172.              SCAS compares AL (or AX) to the byte (or word) pointed to by
  15173.              ES:[di], and increments (or decrements) DI depending on the
  15174.              setting of DF, the direction flag (by 1 for bytes and by 2 for
  15175.              words). NO OVERRIDES ARE ALLOWED. This sets the flags the same
  15176.              way as:
  15177.  
  15178.                  cmp  ax, ES:[DI]              ; or AL for bytes
  15179.  
  15180.              The allowable forms are:
  15181.  
  15182.                  scasb
  15183.                  scasw
  15184.                  scas BYTE PTR ES:[di]         ; no override allowed
  15185.                  scas WORD PTR ES:[di]         ; no override allowed
  15186.  
  15187.  
  15188.              MOVS moves a byte (or a word) from DS:[si] to ES:[di], and
  15189.              increments (or decrements) SI and DI, depending on the setting of
  15190.              DF, the direction flag (by 1 for bytes and by 2 for words).  You
  15191.              may use CS:[si], SS:[si] or ES:[si], but you MAY NOT OVERRIDE
  15192.  
  15193.  
  15194.              Chapter 19 - Strings                                          211
  15195.              ____________________
  15196.  
  15197.              ES:[di]. Though the following is not a legal instruction, it
  15198.              signifies the equivalent action to MOVS (not including changing
  15199.              DI and SI):
  15200.  
  15201.                  mov  WORD PTR ES:[DI], DS:[SI]     ; or BYTE PTR for bytes
  15202.  
  15203.              The allowable forms are:
  15204.  
  15205.                  movsb
  15206.                  movsw
  15207.                  movs BYTE PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  15208.                  movs WORD PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  15209.  
  15210.  
  15211.              CMPS compares the byte (or a word) at DS:[si] to the one at
  15212.              ES:[di], and increments (or decrements) SI and DI, depending on
  15213.              the setting of DF, the direction flag (by 1 for bytes and by 2
  15214.              for words).  You may use CS:[si], SS:[si] or ES:[si], but you MAY
  15215.              NOT OVERRIDE ES:[di]. Although the following is not a legal
  15216.              action, it signifies the equivalent action to CMPS (not including
  15217.              changing DI and SI):
  15218.  
  15219.                  cmp  WORD PTR DS:[SI], ES:[DI]     ; or BYTE PTR for bytes
  15220.  
  15221.              The allowable forms are:
  15222.  
  15223.                  cmpsb
  15224.                  cmpsw
  15225.                  cmps BYTE PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  15226.                  cmps WORD PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  15227.  
  15228.  
  15229.  
  15230.  
  15231.              The string instructions may be prefixed by REP/REPE/REPNE which
  15232.              will repeat the instructions according to the following
  15233.              conditions:
  15234.  
  15235.  
  15236.                  rep       decrement cx ; repeat if cx is not zero
  15237.                  repe      decrement cx ; repeat if cx not zero AND zf = 1
  15238.                  repz      decrement cx ; repeat if cx not zero AND zf = 1
  15239.                  repne     decrement cx ; repeat if cx not zero AND zf = 0
  15240.                  repnz     decrement cx ; repeat if cx not zero AND zf = 0
  15241.  
  15242.              Here, 'e' stands for equal, 'z' is zero and 'n' is not. These
  15243.              repeat instructions should NEVER be used with a segment override,
  15244.              since the 8086 will forget the override if a hardware interrupt
  15245.              occurs in the middle of the REP loop.
  15246.  
  15247.  
  15248.              'HARD' FLAGS
  15249.  
  15250.              IEF, TF and DF are 'hard' flags. Once they are set they remain in
  15251.              the same setting. If you use DF, the direction flag, in a
  15252.              subroutine, you must save the flags upon entry and restore the
  15253.              flags on exiting to make sure that DF has not been altered.
  15254. Chapter 20 - Control Structures
  15255. ===============================
  15256.  
  15257.                                                                            212
  15258.  
  15259.  
  15260.  
  15261.  
  15262.              Control structures are things like 'for', 'do', 'while' and 'if'
  15263.              that you have in high level languages. It is possible to do these
  15264.              things in assembler language if you want. They are included here
  15265.              so if you ever want to use them at this level, you will have a
  15266.              template on how to set them up.
  15267.  
  15268.              All the examples will use integers only. If you have real
  15269.              numbers:
  15270.  
  15271.                  if ( variable < 3.891 )
  15272.                       x = 4 * y ;
  15273.  
  15274.              it is too difficult to implement on the 8086. Of course, if you
  15275.              have an 8087, it's a piece of cake.
  15276.  
  15277.  
  15278.              IF
  15279.  
  15280.              IF is the easiest and it will illustrate how we are going to set
  15281.              up our labels. Let's take that first example. Since it is a waste
  15282.              of space to define variables over and over, we will assume that
  15283.              all variables are words, not bytes. The examples will all be
  15284.              written in C.
  15285.  
  15286.                  if (variable < 7)
  15287.                  {
  15288.                       x = 4 + y ;
  15289.                  }
  15290.  
  15291.              The assembler code looks like this:
  15292.  
  15293.              if1: ;----------------
  15294.                  cmp  variable, 7
  15295.                  jge  end_if1
  15296.  
  15297.                  mov  x, 4
  15298.                  mov  ax, y
  15299.                  add  x, ax
  15300.              end_if1: ;----------------
  15301.  
  15302.              All labels will look like variations of this. The control words
  15303.              will be the labels.{1}  They will have a unique number for each
  15304.              block so you can write as many different blocks as you want; the
  15305.              label that signals the end of the block will be the word 'end_'
  15306.              followed by the control word. The semicolon with the line is a
  15307.              ____________________
  15308.  
  15309.                 1. The words 'if' and 'endif' are Microsoft macro directives,
  15310.              so if you use them without a tag number, you will get some
  15311.              bizarre code indeed.
  15312.  
  15313.              ______________________
  15314.  
  15315.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  15316.  
  15317.  
  15318.              Chapter 20 - Control Structures                               213
  15319.              _______________________________
  15320.  
  15321.              comment, and it is used for visual separation.
  15322.  
  15323.              By using a different number each time, you may nest as deeply as
  15324.              you want, though the code will be pretty unreadable:
  15325.  
  15326.                  if   (variable < 7 )
  15327.                  {
  15328.                       if ( x > 9 )
  15329.                       {
  15330.                            if ( z == 0 )
  15331.                            {
  15332.                                 y = 12 ;
  15333.                            }
  15334.                            q = 5 ;
  15335.                       }
  15336.                       r = 7 ;
  15337.                  }
  15338.  
  15339.              becomes:
  15340.  
  15341.              if1: ;---------------
  15342.                  cmp  variable, 7
  15343.                  jge  end_if1
  15344.  
  15345.              if2: ;---------------
  15346.                  cmp  x, 9
  15347.                  jle  end_if2
  15348.  
  15349.              if3: ;---------------
  15350.                  cmp  z, 0
  15351.                  jne  end_if3
  15352.  
  15353.                  mov  y, 12
  15354.              end_if3: ;-----------
  15355.                  mov  q, 5
  15356.              end_if2: ;-----------
  15357.                  mov  r, 7
  15358.              end_if1: ;-----------
  15359.  
  15360.  
  15361.              With the Microsoft assembler, you don't need to have the labels
  15362.              start at the beginning of the line, but they MUST be the first
  15363.              thing on the line. You can indent them:
  15364.  
  15365.              if1: ; --------------
  15366.                  cmp  variable, 7
  15367.                  jge  end_if1
  15368.  
  15369.                  if2: ;-------------
  15370.                       cmp  x, 9
  15371.                       jle  end_if2
  15372.  
  15373.                       if3: ;------------
  15374.                            cmp  z, 0
  15375.                            jne  end_if3
  15376.  
  15377.                            mov  y, 12
  15378.  
  15379.  
  15380.              The PC Assembler Tutor                                        214
  15381.              ______________________
  15382.  
  15383.                       end_if3: ;--------
  15384.                       mov  q, 5
  15385.                  end_if2: ;---------
  15386.                  mov  r, 7
  15387.              end_if1: ;---------
  15388.  
  15389.              You will notice that they are all compares and jumps using
  15390.              reverse logic. If the condition is NOT true, then we jump. If it
  15391.              is true, we just go on. What about IF ELSE? It looks almost the
  15392.              same.
  15393.  
  15394.                  if ( j < 12 )
  15395.                  {
  15396.                       y = 19 ;
  15397.                  }
  15398.                  else
  15399.                  {
  15400.                       z = 25 ;
  15401.                  }
  15402.  
  15403.              We get:
  15404.  
  15405.              if16: ;----------------
  15406.  
  15407.                  cmp  j, 12
  15408.                  jge  else16
  15409.  
  15410.                  mov  y, 19
  15411.                  jmp  end_if16
  15412.  
  15413.              else16:
  15414.                  mov  z, 25
  15415.                   end_else16:
  15416.              end_if16: ;---------------
  15417.  
  15418.              We jump to another part inside the block, and throw in a JMP
  15419.              after the IF part.
  15420.  
  15421.  
  15422.              WHILE
  15423.  
  15424.              WHILE is the same as IF except that at the end of the block, we
  15425.              jump back to the beginning.
  15426.  
  15427.                  while ( j < 20 )
  15428.                  {
  15429.                       j = j + 1 ;
  15430.                  }
  15431.  
  15432.              while25: ;------------
  15433.                  cmp  j, 20
  15434.                  jge  end_while25
  15435.  
  15436.                  inc  j
  15437.                  jmp  while25
  15438.              end_while25: ;-----------
  15439.  
  15440.  
  15441.  
  15442.              Chapter 20 - Control Structures                               215
  15443.              _______________________________
  15444.  
  15445.  
  15446.              DO WHILE
  15447.  
  15448.              DO WHILE always goes through the code once. The decision process
  15449.              is at the end.
  15450.  
  15451.                  do
  15452.                  {
  15453.                       k = k + 7 ;
  15454.                  } while (k < 0) ;
  15455.  
  15456.  
  15457.              do_while74: ;------------
  15458.                  add  k, 7
  15459.  
  15460.                  cmp  k, 0
  15461.                  jl   do_while74
  15462.              end_do_while74: ;------------
  15463.  
  15464.              Notice that here the jump is a positive decision. If the
  15465.              condition is fulfilled, we jump back to the start, otherwise we
  15466.              fall through. What do you do with 'break' and 'continue'? {2}
  15467.              Let's put them in:
  15468.  
  15469.                  do
  15470.                  {
  15471.                       k = k + 7 ;
  15472.                       if ( y < 9 )
  15473.                       {
  15474.                            continue ;
  15475.                       }
  15476.                       j = j - 6 ;
  15477.                       if ( z == 5 )
  15478.                       {
  15479.                            break ;
  15480.                       }
  15481.                       y = y * 2 ;
  15482.                  } while ( j < 17) ;
  15483.  
  15484.              In assembler language, this becomes:
  15485.  
  15486.              do_while143: ;----------
  15487.                  add  k, 7
  15488.  
  15489.                  if144: ;-----------
  15490.                       cmp  y, 9
  15491.                       jge  end_if144
  15492.  
  15493.                       jmp  continue143
  15494.                  end_if144: ;-----------
  15495.  
  15496.                  sub  j, 6
  15497.              ____________________
  15498.  
  15499.                 2. For those of you who are not C people, BREAK breaks you out
  15500.              of the innermost loop. CONTINUE skips all the code till the end
  15501.              of the loop.
  15502.  
  15503.  
  15504.              The PC Assembler Tutor                                        216
  15505.              ______________________
  15506.  
  15507.  
  15508.                  if145: ;-----------
  15509.                       cmp  z, 5
  15510.                       jne  end_if145
  15511.  
  15512.                       jmp  break143
  15513.                  end_if145: ;-----------
  15514.  
  15515.                  sal  y, 1           ; shift left 1 = multiply by 2
  15516.              continue143:
  15517.                  cmp  j, 17
  15518.                  jl   do_while143
  15519.              end_do_while143:
  15520.              break143:
  15521.  
  15522.              In DO WHILE, CONTINUE is the label just before the decision
  15523.              process. BREAK is always the first label after the block. We can
  15524.              just use the label that marks the end of our block for the break.
  15525.              Instead of:
  15526.  
  15527.                  jmp  break143
  15528.  
  15529.              we can have:
  15530.  
  15531.                  jmp  end_do_while143
  15532.  
  15533.              From now on, we'll put in the continue and break, even if they
  15534.              aren't used. Once you are used to it, you can drop the break
  15535.              label and use the end of block label. Going back to the WHILE
  15536.              statement, we have:
  15537.  
  15538.              while25: ;------------
  15539.                  cmp  j, 20
  15540.                  jge  end_while25
  15541.  
  15542.                  inc  j
  15543.              continue25:
  15544.                  jmp  while25
  15545.              end_while25: ;-----------
  15546.              break25:
  15547.  
  15548.  
  15549.  
  15550.              FOR
  15551.  
  15552.              The FOR statement in C is more sophisticated than in other
  15553.              languages, but we will keep the example simple. It has 3 parts:
  15554.  
  15555.                  for ( i = 11 ; i < 20 ; i = i + 1 )
  15556.  
  15557.              can be looked at as:
  15558.  
  15559.                  for ( initialize ; test ; update )
  15560.  
  15561.              The reason that it is sophisticated is that it can have any
  15562.              number of things in the first and third parts.
  15563.  
  15564.  
  15565.  
  15566.              Chapter 20 - Control Structures                               217
  15567.              _______________________________
  15568.  
  15569.                  for ( i = 11, j = 27  z = 4 ; i < 20 ; k = k + 1, j = j/2)
  15570.  
  15571.              is legitimate. Therefore we need to set aside a block for the
  15572.              initialize part, a block for the test part, and a block for the
  15573.              update part. One question is whether we want the initialization
  15574.              inside the FOR label. I'm not doing it, but it is a possibility.
  15575.              Here's the C code.
  15576.  
  15577.                  for ( i = 11 ; i < 20 ; i = i + 1 )
  15578.                  {
  15579.                       k = k + 9
  15580.                  }
  15581.  
  15582.              And its corresponding assembler code:
  15583.  
  15584.              init217: ;---------------
  15585.  
  15586.                  mov  i, 11
  15587.                  end_init217: ;------
  15588.              for217: ; ---------------
  15589.              test217:
  15590.                  cmp  i, 20
  15591.                  jge  end_for217
  15592.                  end_test217: ;-----
  15593.  
  15594.                  add  k,9
  15595.  
  15596.              continue217:
  15597.              update217:
  15598.                  inc  i
  15599.                  end_update217: ; ----
  15600.                  jmp  test217
  15601.              end_for217: ;-------------
  15602.              break217:
  15603.  
  15604.              My that's a lot of labels. In this example we would get rid of
  15605.              most of them because there is so little code, but if the block
  15606.              contained a lot of code they would be a help, not a hindrance.
  15607.              Remember, a label generates no code, so this makes no difference
  15608.              in the size of the object file. Some of the labels which are not
  15609.              targets of jumps could be made into comments.
  15610.  
  15611.                  end_update217: ;----
  15612.  
  15613.              can become:
  15614.  
  15615.                  ; end_update217 ----
  15616.  
  15617.              Whichever way looks better to you. Using comments instead of
  15618.              labels gives you a slightly faster compile time. Just make sure
  15619.              you don't change one that IS a target of a jump.
  15620.  
  15621.  
  15622.              SWITCH
  15623.  
  15624.              Finally there is the switch statement. If you don't know what it
  15625.              is, skip this section because it will probably be confusing.
  15626.  
  15627.  
  15628.              The PC Assembler Tutor                                        218
  15629.              ______________________
  15630.  
  15631.  
  15632.                  switch ( k )
  15633.                  {
  15634.                            case 'A':      x = x + 9 ;
  15635.                                           break ;
  15636.  
  15637.                            case '&':      y = y * 2 ;
  15638.                                           break ;
  15639.  
  15640.                            case 'j':      z = z - 7 ;
  15641.                                           break ;
  15642.                  }
  15643.  
  15644.              We get the following:
  15645.  
  15646.              switch82: ;-----------------
  15647.  
  15648.                  cmp  k, 'A'
  15649.                  jne  test82_2
  15650.                  jmp  case82_1
  15651.              test82_2
  15652.                  cmp  k, '&'
  15653.                  jne  test82_3
  15654.                  jmp  case82_2
  15655.              test82_3
  15656.                  cmp  k, 'j'
  15657.                  jne  end_test82
  15658.                  jmp  case82_3
  15659.              end_test82:
  15660.                  jmp  deafult82
  15661.                  ; ----------
  15662.  
  15663.              case82_1:
  15664.                  add  x, 9
  15665.                  jmp  break82
  15666.  
  15667.              case82_2:
  15668.                  sal  y, 1           ; multiply by 2
  15669.                  jmp  break82
  15670.  
  15671.              case82_3:
  15672.                  sub  z, 7
  15673.                  jmp  break82
  15674.  
  15675.              default82:
  15676.                  jmp  break82
  15677.  
  15678.              end_switch82: ;------------
  15679.              break82:
  15680.  
  15681.              It may look like there are unnecessary jumps at the beginning,
  15682.              but it is likely that in a real program some of the case
  15683.              statements would be more than +127 bytes away from the
  15684.              conditional jumps, so you need JMP, which can go anywhere in the
  15685.              segment.
  15686. Chapter 21 - .COM Files
  15687. =======================
  15688.                                                                            219
  15689.  
  15690.  
  15691.              All the programs that we have made so far have been .EXE files.
  15692.              That means that the file extension has always been .EXE after
  15693.              linking. When you have and .EXE file, the program loader makes
  15694.              certain adjustments to the machine code at run time. These
  15695.              adjustments are the actual segment addresses of the segments.
  15696.  
  15697.              There is another type of executable file, and that is a .COM
  15698.              file. When the loader puts a .COM file into memory, it makes no
  15699.              adjustments, it simply reads the file directly from disk into
  15700.              memory. Therefore, a .COM file loads faster. However, there is a
  15701.              restriction:
  15702.  
  15703.                  All code, data, and the stack must be in a single segment.
  15704.                  This effectively limits a .COM program to 65536 bytes of
  15705.                  code+data+stack. {1}
  15706.  
  15707.              In general, it is easier for program development to keep code and
  15708.              data separate, but we can mix them together. Let's look at the
  15709.              template for a .COM file. It is called COMTEMP.ASM.
  15710.  
  15711.              ; com file template
  15712.              ; put name here
  15713.              ; * * * * * * * * * * * * * * *
  15714.              INCLUDE \PUSHREGS.MAC
  15715.  
  15716.              COMSEG  SEGMENT  PUBLIC  'CODE'
  15717.  
  15718.                     ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG, ss:COMSEG
  15719.              ; - - - - - - - - - -
  15720.              main   proc  NEAR
  15721.  
  15722.                     ORG 100h
  15723.              start:
  15724.  
  15725.              ; - - - - START CODE BELOW THIS LINE
  15726.  
  15727.              ; - - - - END CODE ABOVE THIS LINE
  15728.  
  15729.                     ret
  15730.  
  15731.              main   endp
  15732.  
  15733.              ; - - - - START SUBROUTINES BELOW THIS LINE
  15734.  
  15735.              ; - - - - END SUBROUTINES ABOVE THIS LINE
  15736.  
  15737.              ____________________
  15738.  
  15739.                 1. Actually, it is possible to get around this restriction
  15740.              with suitable fiddling, but by that time you have made the
  15741.              program much more complicated so you have lost any advantage that
  15742.              you had by not using an .EXE file.
  15743.  
  15744.              ______________________
  15745.  
  15746.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  15747.  
  15748.  
  15749.              The PC Assembler Tutor                                        220
  15750.              ______________________
  15751.  
  15752.  
  15753.              ; - - - - START DATA BELOW THIS LINE
  15754.  
  15755.              ; - - - - END DATA ABOVE THIS LINE
  15756.  
  15757.              Stack_area  dw  500 dup (?)
  15758.  
  15759.              COMSEG ENDS
  15760.              ; * * * * * * * * * * * * * * *
  15761.              END  start
  15762.  
  15763.              We will take the things in order. First, there is the line:
  15764.  
  15765.                  ORG  100h
  15766.  
  15767.              This effectively tells the assembler to put 100h (256d) bytes of
  15768.              zeros at the beginning. Also notice that the setup code that we
  15769.              had in a .EXE file is missing; we start with the code
  15770.              immediately. This all has to do with the PSP (program segment
  15771.              prefix).{2}
  15772.  
  15773.  
  15774.              PSP
  15775.  
  15776.              You will remember from the chapter describing the .EXE template
  15777.              file that we wrote:
  15778.  
  15779.                  push ds
  15780.                  sub  ax, ax
  15781.                  push ax
  15782.  
  15783.              because upon entry to an .EXE file, DS contains the segment
  15784.              address of the PSP, and at offset 0000 (that is, the first byte
  15785.              of the segment) there is a machine instruction for an orderly
  15786.              exit from the program. In an .EXE file, the PSP is somewhere in
  15787.              memory, put there by the loader. In a .COM file, the PSP is the
  15788.              first 100h (256d) bytes of the segment. The loader fills in the
  15789.              PSP and then reads the file directly from disk. That means that
  15790.              the machine instruction for an orderly exit is at 0000 of the
  15791.              current segment. Notice that we have a NEAR procedure. A .COM
  15792.              file has a near return which will stay in the same segment.
  15793.              Normally we would have to write:
  15794.  
  15795.                  sub  ax, ax
  15796.                  push ax
  15797.  
  15798.              to put the return address 0000 on the stack, but for a .COM file
  15799.              the loader pushes 0000 on the stack before giving control to the
  15800.              program. Why the loader provides this service for a .COM file but
  15801.              not for an .EXE file is a mystery. In any case, with a .COM file,
  15802.              you don't need to push the return address on the stack, since
  15803.              it's there already.
  15804.              ____________________
  15805.  
  15806.                 2. If you want to know what the PSP (program segment prefix)
  15807.              is exactly, consult either of the two books on hardware and
  15808.              interrupts. The PSP is always exactly 256 bytes long.
  15809.  
  15810.  
  15811.              Chapter 21 - .COM Files                                       221
  15812.              _______________________
  15813.  
  15814.  
  15815.              We have the assembler directive:
  15816.  
  15817.                     ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG, ss:COMSEG
  15818.  
  15819.              The reason for including all the segments is (1) the loader
  15820.              actually loads the segment address into all four segments and (2)
  15821.              the assembler will use the natural segment for all instructions.
  15822.              The BP instructions will refer to SS, the data instructions will
  15823.              refer to DS, the string move instructions (SCAS, CMPS, MOVS) will
  15824.              refer to ES. That means that the assembler will not use segment
  15825.              overrides. This helps avoid something called phase errors which
  15826.              will be explained at the end.
  15827.  
  15828.  
  15829.              Right after the ORG instruction is start: (the first instruction
  15830.              executed in the program.)
  15831.  
  15832.                       ORG 100h       ; 256d
  15833.                  start:
  15834.  
  15835.              This is inflexible. After reading the program into memory, the
  15836.              loader ALWAYS starts the program at 100h. All .COM files start
  15837.              execution at 100h. Period.
  15838.  
  15839.              There is space for subroutines, space for data, and finally space
  15840.              for the stack. The order of subroutines and data can be changed.
  15841.              The stack space is technically not necessary, but it is a good
  15842.              reminder to leave it there. All .COM files take up a whole 65536
  15843.              byte segment in memory, no matter how short they are.{3} The
  15844.              stack is at the very end of the segment, so if your program is
  15845.              200 bytes long, you have 65336 bytes of stack space.
  15846.  
  15847.              Let's make a simple program. It is the famous "Hello,World"
  15848.              program.
  15849.  
  15850.              ; - - - - START CODE BELOW THIS LINE
  15851.                  mov  dx, offset mr_happy_face      ; int 21h, function 9
  15852.                  mov  ah, 9
  15853.                  int  21h
  15854.              ; - - - - END CODE ABOVE THIS LINE
  15855.  
  15856.              ; - - - - START DATA BELOW THIS LINE
  15857.              mr_happy_face db  "Hello, world!", 13, 10, "$"
  15858.              ; - - - - END DATA ABOVE THIS LINE
  15859.  
  15860.              This program prints 'Hello World!' and a new line. The dollar
  15861.              sign signifies the end of the string for this interrupt. It is
  15862.              simple enough, and it gives us the chance to look at the extra
  15863.              step needed to make a .COM file. Assemble it, and link it. When
  15864.              you link it, you will get a warning that there is no stack
  15865.              segment. For .COM files, this warning is unimportant. We now have
  15866.              an .EXE file (which, by the way, won't run correctly). How do we
  15867.              ____________________
  15868.  
  15869.                 3. This is a slightly abridged explaination. For the real
  15870.              details, consult Microsoft's "The MS-DOS Encyclopedia".
  15871.  
  15872.  
  15873.              The PC Assembler Tutor                                        222
  15874.              ______________________
  15875.  
  15876.              make it a .COM file?
  15877.  
  15878.              Among the programs that you got with DOS is one called EXE2BIN.
  15879.              It takes an .EXE file, and if possible, converts it into a .COM
  15880.              file. You simply write the name of the file you want converted
  15881.              and the name of the converted file. Both of these names must have
  15882.              the full file extension:
  15883.  
  15884.                  exe2bin  programA.exe  programA.com
  15885.  
  15886.              You will now have programA.com as a .COM file. If we now write:
  15887.  
  15888.                  C> programA
  15889.  
  15890.              Will the loader load the .COM file or the .EXE file? The DOS
  15891.              order for execution is .COM files first, then .EXE files, then
  15892.              .BAT files. DOS will execute the .COM file. Try it.
  15893.  
  15894.              That was pretty easy. Now, as a technical exercise, we  are going
  15895.              to link together three different files. Here's the first file:
  15896.  
  15897.                  ; prog1.asm
  15898.                  ; * * * * * * * * * * * * * * *
  15899.                  INCLUDE \PUSHREGS.MAC
  15900.                  COMSEG  SEGMENT  PUBLIC  'CODE'
  15901.  
  15902.                         ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG, ss:COMSEG
  15903.  
  15904.                  PUBLIC message1
  15905.                  EXTRN  subroutine_a:NEAR, message3:BYTE
  15906.                  ; - - - - - - - - - -
  15907.                  main   proc  NEAR
  15908.  
  15909.                         ORG 100h
  15910.                  start:
  15911.                         mov   ah, 9                ; int 21h, ah = 9
  15912.                         mov   dx, offset message1
  15913.                         int   21h
  15914.  
  15915.                         mov   ah, 9                ; int 21h, ah = 9
  15916.                         mov   dx, offset message3
  15917.                         int   21h
  15918.  
  15919.                         call  subroutine_a
  15920.                         ret
  15921.  
  15922.                  main   endp
  15923.  
  15924.                  message1 db  "This is from the main program.", 13, 10, "$"
  15925.  
  15926.                  COMSEG ENDS
  15927.                  ; * * * * * * * * * * * * * * *
  15928.                  END  start
  15929.                  ; ----------
  15930.  
  15931.              Here is the second file:
  15932.                  ; prog2.asm
  15933.  
  15934.  
  15935.              Chapter 21 - .COM Files                                       223
  15936.              _______________________
  15937.  
  15938.                  ; * * * * * * * * * * * * * * *
  15939.                  INCLUDE \PUSHREGS.MAC
  15940.                  COMSEG  SEGMENT  PUBLIC  'CODE'
  15941.  
  15942.                         ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG, ss:COMSEG
  15943.  
  15944.                  PUBLIC message2, subroutine_a
  15945.                  EXTRN  subroutine_b:NEAR, message3:BYTE
  15946.                  ; - - - - - - - - - -
  15947.                  subroutine_a   proc  NEAR
  15948.  
  15949.                         PUSHREGS ax, dx
  15950.                         mov   ah, 9                ; int 21h, ah = 9
  15951.                         mov   dx, offset message2
  15952.                         int   21h
  15953.  
  15954.                         mov   ah, 9                ; int 21h, ah = 9
  15955.                         mov   dx, offset message3
  15956.                         int   21h
  15957.  
  15958.                         call  subroutine_b
  15959.                         POPREGS ax, dx
  15960.                         ret
  15961.  
  15962.                  subroutine_a   endp
  15963.  
  15964.                  message2 db  "This is from subroutine A.", 13, 10, "$"
  15965.  
  15966.                  COMSEG ENDS
  15967.                  ; * * * * * * * * * * * * * * *
  15968.                  END
  15969.                  ; ----------
  15970.  
  15971.              And here is the third file:
  15972.  
  15973.                  ; prog3.asm
  15974.                  ; * * * * * * * * * * * * * * *
  15975.                  INCLUDE \PUSHREGS.MAC
  15976.                  COMSEG  SEGMENT  PUBLIC  'CODE'
  15977.  
  15978.                         ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG, ss:COMSEG
  15979.  
  15980.                  PUBLIC message3, subroutine_b
  15981.                  EXTRN  message1:BYTE, message2:BYTE
  15982.                  ; - - - - - - - - - -
  15983.                  subroutine_b   proc  NEAR
  15984.  
  15985.                         PUSHREGS ax, dx
  15986.                         mov   ah, 9                ; int 21h, ah = 9
  15987.                         mov   dx, offset message1
  15988.                         int   21h
  15989.  
  15990.                         mov   ah, 9                ; int 21h, ah = 9
  15991.                         mov   dx, offset message2
  15992.                         int   21h
  15993.  
  15994.                         POPREGS ax, dx
  15995.  
  15996.  
  15997.              The PC Assembler Tutor                                        224
  15998.              ______________________
  15999.  
  16000.                         ret
  16001.  
  16002.                  subroutine_b   endp
  16003.  
  16004.                  message3 db  "This is from subroutine B.", 13, 10, "$"
  16005.  
  16006.                  COMSEG ENDS
  16007.                  ; * * * * * * * * * * * * * * *
  16008.                  END
  16009.                  ; ----------
  16010.  
  16011.              If you look at them, you will see that all they do is print
  16012.              messages; sometimes from their own file, sometimes from external
  16013.              files. The subprograms all have different names since they call
  16014.              each other. Of course, they have both PUBLIC and EXTRN
  16015.              statements. Only prog1 has:
  16016.  
  16017.                  start:
  16018.  
  16019.              since that is where the program execution will start, and only
  16020.              prog1 has:
  16021.  
  16022.                   ORG 100h
  16023.  
  16024.              since having it in the other files would leave unnecessary blank
  16025.              spaces in the other programs. Assemble the programs. When you
  16026.              link the programs, prog1 MUST be the first on the line:
  16027.  
  16028.                  link prog1+prog2+prog3
  16029.                  link prog1+prog3+prog2
  16030.  
  16031.              are both ok, but:
  16032.  
  16033.                  link prog2+prog1+prog3
  16034.  
  16035.              will not work since the linker, exe2bin, and the loader are
  16036.              counting on the starting instruction being at 100h. If you change
  16037.              the order, you will either get complaints from EXE2BIN or the
  16038.              program won't run correctly. Now with:
  16039.  
  16040.                  exe2bin  prog1.exe  prog1.com
  16041.  
  16042.              you have a .COM file. Try it out.
  16043.  
  16044.  
  16045.              Any .COM file can also be made into an .EXE file. We will make a
  16046.              simple program in .COM format, and then add the necessary things
  16047.              to make it an .EXE format. First, here's the .COM format.
  16048.  
  16049.  
  16050.              ; commode.asm
  16051.              ; * * * * * * * * * * * * * * *
  16052.              COMSEG  SEGMENT  PUBLIC  'CODE'
  16053.  
  16054.                     ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG, ss:COMSEG
  16055.  
  16056.              main   proc  NEAR
  16057.  
  16058.  
  16059.              Chapter 21 - .COM Files                                       225
  16060.              _______________________
  16061.  
  16062.  
  16063.                     ORG 100h
  16064.              start:
  16065.                     ; get video mode int 10h, function 0Fh
  16066.                     mov   ah, 0Fh
  16067.                     int   10h
  16068.  
  16069.                     ; al = display mode
  16070.                     mov   bl, 10              ; divide by 10
  16071.                     mov   si, offset ones     ; right hand digit
  16072.                     mov   cx, 2               ; 2 digit answer
  16073.              division_loop:
  16074.                     mov   ah, 0               ; clear ah
  16075.                     div   bl                  ; al/bl, remainder in ah
  16076.                     add   ah, '0'             ; change to ascii
  16077.                     mov   [si], ah
  16078.                     dec   si                  ; one byte to the left
  16079.                     loop  division_loop
  16080.  
  16081.                     ; display string
  16082.                     mov   dx, offset message  ; int 21h, service 9
  16083.                     mov   ah, 9
  16084.                     int   21h
  16085.  
  16086.                     ret
  16087.  
  16088.              main   endp
  16089.  
  16090.              ; - - - - START DATA BELOW THIS LINE
  16091.              message      db  "The  current video mode is  "
  16092.              ones         db  ?
  16093.                           db  ".",  13, 10, "$"
  16094.              ; - - - - END DATA ABOVE THIS LINE
  16095.  
  16096.              COMSEG ENDS
  16097.              ; * * * * * * * * * * * * * * *
  16098.              END  start
  16099.              ; - - - - - - - - - -
  16100.  
  16101.              This program gets the video mode which is a number which tells
  16102.              you what mode the monitor is operating in. To find out what the
  16103.              number means, consult either of those two hardware books. It gets
  16104.              the mode through an interrupt. The mode is returned in AL. It
  16105.              then puts the number in a string and prints the string. We cannot
  16106.              link with asmhelp.obj, so it takes all this work is simply to
  16107.              output a number.
  16108.  
  16109.              We'll call this commode.asm. Assemble, link, use exe2bin, and run
  16110.              it. Now let's make the .EXE counterpart. Here it is.
  16111.  
  16112.              ; exemode.asm
  16113.              ; * * * * * * * * * * * * * * *
  16114.              STACKSEG     SEGMENT  STACK  'STACK'
  16115.  
  16116.                     dw    20 dup (?)
  16117.  
  16118.              STACKSEG  ENDS
  16119.  
  16120.  
  16121.              The PC Assembler Tutor                                        226
  16122.              ______________________
  16123.  
  16124.              ; - - - - - - - - - -
  16125.              COMSEG  SEGMENT  PUBLIC  'CODE'
  16126.  
  16127.                     ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG
  16128.  
  16129.              main   proc  FAR
  16130.  
  16131.              start:
  16132.                     push  ds                  ; same as before
  16133.                     sub   ax, ax
  16134.                     push  ax
  16135.  
  16136.                     push  cs                  ; ds = cs
  16137.                     pop   ds
  16138.  
  16139.                     ; get video mode int 10h, service 15
  16140.                     mov   ah, 15
  16141.                     int 10h
  16142.  
  16143.                     ; al = display mode
  16144.                     mov   bl, 10              ; divide by 10
  16145.                     mov   si, offset ones     ; right hand digit
  16146.                     mov   cx, 2               ; 2 digit answer
  16147.              division_loop:
  16148.                     mov   ah, 0               ; clear ah
  16149.                     div   bl                  ; al/bl, remainder in ah
  16150.                     add   ah, '0'             ; change to ascii
  16151.                     mov   [si], ah
  16152.                     dec   si                  ; one byte to the left
  16153.                     loop  division_loop
  16154.  
  16155.                     ; display string
  16156.                     mov   dx, offset message  ; int 21h, service 9
  16157.                     mov   ah, 9
  16158.                     int   21h
  16159.  
  16160.                     ret
  16161.  
  16162.              main   endp
  16163.  
  16164.              ; - - - - START DATA BELOW THIS LINE
  16165.              message      db  "The  current video mode is  "
  16166.              ones         db  ?
  16167.                           db  ".",  13, 10, "$"
  16168.              ; - - - - END DATA ABOVE THIS LINE
  16169.  
  16170.              COMSEG ENDS
  16171.              ; * * * * * * * * * * * * * * *
  16172.              END  start
  16173.  
  16174.              This is almost the same. We have put in a small stack segment.
  16175.              Here's the different part:
  16176.  
  16177.              ;----------
  16178.                     ASSUME  cs:COMSEG, ds:COMSEG, es:COMSEG
  16179.  
  16180.              main   proc  FAR
  16181.  
  16182.  
  16183.              Chapter 21 - .COM Files                                       227
  16184.              _______________________
  16185.  
  16186.  
  16187.              start:
  16188.                     push  ds                  ; same as before
  16189.                     sub   ax, ax
  16190.                     push  ax
  16191.  
  16192.                     push  cs                  ; ds = cs
  16193.                     pop   ds
  16194.  
  16195.              ;----------
  16196.  
  16197.              SS is no longer in the ASSUME statement, since it now refers to
  16198.              the stack segment. CS, DS, and ES still refer to COMSEG. The
  16199.              procedure is now a FAR procedure. We have taken the ORG out,
  16200.              since that would simply waste 100h (256) bytes of space. Then we
  16201.              have the normal .EXE startup except the data is now in COMSEG, so
  16202.              we move CS to DS. That's it. We'll call this one exemode.asm.
  16203.              Assemble, link and run it. It should give you the same result.
  16204.  
  16205.              Here is the listing for both executable files:
  16206.  
  16207.                  COMMODE  COM       65  10-21-89  11:11p
  16208.                  EXEMODE  EXE      631  10-21-89  11:10p
  16209.  
  16210.              Notice how much bigger the .EXE file is. That is because the .EXE
  16211.              file has a bunch of information for the loader. Also, if you run
  16212.              both programs, the .COM file will start a little quicker. Those
  16213.              are the only advantages.
  16214.  
  16215.  
  16216.              PHASE ERRORS
  16217.  
  16218.              The assembler generates code in two steps. On the first pass, it
  16219.              calculates the address of each variable and machine instruction
  16220.              without actually writing code. For instance, if there is the
  16221.              instruction:
  16222.  
  16223.                  mov  ax, variable1
  16224.  
  16225.              The assembler will allocate 4 bytes for the instruction. If
  16226.              variable1 has already been defined, but is in ES, then the
  16227.              assembler will allocate 5 bytes; 1 for the segment override and 4
  16228.              for the instruction itself. If, however, variable1 has not been
  16229.              defined yet (it appears later in the code), then the assembler
  16230.              will assume that it is in DS and allocate 4 bytes. If it turns
  16231.              out that it is later defined to be in ES, then when the assembler
  16232.              generates code, it will write 5 bytes, 1 for the override and 4
  16233.              for the instruction. But this means that EVERYTHING after this
  16234.              instruction will have been shifted one byte, so EVERYTHING after
  16235.              the instruction will be at the wrong address. The assembler will
  16236.              detect this and print out a PHASE ERROR. This means that the
  16237.              machine code is garbage.
  16238.  
  16239.              By having all four segment registers in the ASSUME statement of a
  16240.              .COM file, you guarantee that the assembler will not generate
  16241.              segment overrides.
  16242.  
  16243.  
  16244.  
  16245.              The PC Assembler Tutor                                        228
  16246.              ______________________
  16247.  
  16248.                  add  dx, [bp]
  16249.  
  16250.              will have BP relative to SS.
  16251.  
  16252.                  sub  variable1, si
  16253.  
  16254.              will have variable1 relative to DS. This will go a long way
  16255.              towards eliminating errors in a .COM file.
  16256. Chapter 22 - BCD Numbers
  16257. ========================
  16258.                                                                            229
  16259.  
  16260.  
  16261.  
  16262.              This chapter covers numbers which are in BCD format, both packed
  16263.              and unpacked. You will probably never need to write any programs
  16264.              on the 8086 that need these instructions, so you can either do
  16265.              this chapter because it is the only time that you will run into
  16266.              them, or you can skip the chapter because you will never see them
  16267.              again. My advice would be to go through it anyways so you know
  16268.              what the capabilities of the 8086 are. The programs in this
  16269.              chapter are more advanced so will be more of a challenge to your
  16270.              understanding.
  16271.  
  16272.              BCD stands for binary coded decimals. In their unpacked form,
  16273.              each byte stands for a single decimal digit. If we take a number
  16274.              like 831974 and put it in memory, the bytes will look like this:
  16275.  
  16276.                  08h
  16277.                  03h
  16278.                  01h
  16279.                  09h
  16280.                  07h
  16281.                  04h
  16282.  
  16283.              With high memory at the top and low memory at the bottom. Notice
  16284.              that the top number is not ASCII '8' (hex 38), but the number 8
  16285.              (hex 08). The same holds true for all the bytes; they are not
  16286.              ASCII characters, they are numbers.
  16287.  
  16288.              Why would we want to have numbers like this? They use up more
  16289.              space (about 2 1/2 times as much), and the arithmetic operations
  16290.              are slower (a multiplication on the 8086 can be several HUNDRED
  16291.              times slower). They are not used for scientific operations. They
  16292.              are used in business for billing. In a typical billing operation,
  16293.              the number is entered from a terminal and stored. At the end of
  16294.              the month, the number is printed on one line of the bill, and
  16295.              then added to the total. If it were converted to an integer, it
  16296.              would be necessary to do one conversion during data entry, then
  16297.              another conversion during printing. This way, the only time it
  16298.              will be converted is if it is used in some arithmetic.
  16299.  
  16300.              One excuse for using BCD numbers is that they are more accurate.
  16301.              It is true that they have no rounding errors, but neither do
  16302.              integers. Integers and BCD numbers can have the same accuracy.
  16303.  
  16304.              The typical form for BCD numbers is 18 digits. If you want to
  16305.              convert an 18 digit number to a standard integer, it takes about
  16306.              25 multiplications. That is a lot of multiplications to convert
  16307.              one number. Similarly, it takes about 25 divisions to get a
  16308.              number out of integer form back into a BCD number. This is a big
  16309.              waste of time for a business situation. We keep the numbers in
  16310.              decimal form and use them occasionally for arithmetic.
  16311.  
  16312.              Actually, you would have to be crazy to use an 8086 for BCD
  16313.  
  16314.              ______________________
  16315.  
  16316.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  16317.  
  16318.  
  16319.              The PC Assembler Tutor                                        230
  16320.              ______________________
  16321.  
  16322.              numbers. If you have a PC and use BCD numbers habitually, an 8087
  16323.              coprocessor will work on BCD numbers at lightning speed. An
  16324.              investment of $175 will save you countless hours of grief. Also,
  16325.              it is next to impossible to do BCD division on the 8086, but
  16326.              takes no longer than normal to do BCD division on the 8087.
  16327.  
  16328.              That being said, just consider this chapter an academic exercise.
  16329.              It will give you the information so if the question should arise
  16330.              at a party, you will be able to say how BCD arithmetic works on
  16331.              the 8086.
  16332.  
  16333.              All 8086 numbers have the least significant digit in low memory
  16334.              and the most significant digit in high memory. This includes
  16335.              packed and unpacked BCD numbers. In the unpacked form, each byte
  16336.              represents a digit, and has a value from 0 to 9. Here are some
  16337.              numbers and their unpacked BCD encoding (in hex):
  16338.  
  16339.                  3986149        27        961728          74610
  16340.  
  16341.                    03h          02h         09h            07h
  16342.                    09h          07h         06h            04h
  16343.                    08h                      01h            06h
  16344.                    06h                      07h            01h
  16345.                    01h                      02h            00h
  16346.                    04h                      08h
  16347.                    09h
  16348.  
  16349.              This wastes a lot of space. Someone early on noticed that the
  16350.              upper half byte is completely unused. You can cut the space
  16351.              consumption in half if you put two digits in each byte - one in
  16352.              the lower half byte, and the other in the upper half byte. This
  16353.              packing always starts from the least significant digit and goes
  16354.              to the most significant digit. Here are the same numbers in
  16355.              packed form.
  16356.  
  16357.                  3986149        27        961728          74610
  16358.  
  16359.                    03h          27h         96h            07h
  16360.                    98h                      17h            46h
  16361.                    61h                      28h            10h
  16362.                    49h
  16363.  
  16364.              This is a considerable saving in space. The 8087 (not 8086)
  16365.              standard for these numbers is 10 byte long numbers. The low order
  16366.              9 bytes contain 18 digits. The last byte is 00h if the number is
  16367.              positive and 80h if the number is negative. Here are an 18 digit
  16368.              positive number and an 18 digit negative number, along with their
  16369.              10 byte BCD encoding.
  16370.  
  16371.  
  16372.  
  16373.  
  16374.  
  16375.  
  16376.  
  16377.  
  16378.  
  16379.  
  16380.  
  16381.              Chapter 22 - BCD Numbers                                      231
  16382.              ________________________
  16383.  
  16384.                       +137486298374691552           -581726405829645298
  16385.  
  16386.                            00                            80
  16387.                            13                            58
  16388.                            74                            17
  16389.                            86                            26
  16390.                            29                            40
  16391.                            83                            58
  16392.                            74                            29
  16393.                            69                            64
  16394.                            15                            52
  16395.                            52                            98
  16396.  
  16397.  
  16398.              Let's work with the packed BCD numbers first.
  16399.  
  16400.  
  16401.              PACKED BCD NUMBERS
  16402.  
  16403.              The 8086 can manipulate packed BCD numbers. There is a subprogram
  16404.              in asmhelp.obj that gets a BCD number from the keyboard and one
  16405.              that prints a BCD number on the screen. Let's do some input and
  16406.              output first.
  16407.  
  16408.              ; - - - - - - - - - - START DATA BELOW THIS LINE
  16409.              variable1dw   ?    ; first four bcd digits
  16410.              variable2dw   ?    ; second four bcd digits
  16411.              variable3dw   ?    ; third four bcd digits
  16412.              variable4dw   ?    ; fourth four bcd digits
  16413.              variable5dw   ?    ; last two bcd digits and sign
  16414.              ; - - - - - - - - - - END DATA ABOVE THIS LINE
  16415.  
  16416.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  16417.  
  16418.              outer_loop:
  16419.                  lea  ax, variable1
  16420.                  call get_bcd
  16421.  
  16422.                  mov  ax, variable5
  16423.                  call print_hex
  16424.                  mov  ax, variable4
  16425.                  call print_hex
  16426.                  mov  ax, variable3
  16427.                  call print_hex
  16428.                  mov  ax, variable2
  16429.                  call print_hex
  16430.                  mov  ax, variable1
  16431.                  call print_hex
  16432.  
  16433.                  lea  ax, variable1
  16434.                  call print_bcd
  16435.                  loop outer_loop
  16436.  
  16437.              ; - - - - - - - - - - END CODE ABOVE THIS LINE
  16438.  
  16439.              This gets a 10 byte BCD number, prints it out in hex (with high
  16440.              memory on top), and then prints the BCD number out again. Commas
  16441.  
  16442.  
  16443.              The PC Assembler Tutor                                        232
  16444.              ______________________
  16445.  
  16446.              are allowed. Printing the number in hex form allows you to see
  16447.              what the numbers look like internally.
  16448.  
  16449.              There are two instructions for BCD numbers - DAA (decimal adjust
  16450.              for addition), and DAS (decimal adjust for subtraction). We'll
  16451.              use each one on individual bytes to see how they work. Let's try
  16452.              DAA.
  16453.  
  16454.  
  16455.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  16456.                  mov  ax_byte, 0C4h  ; half registers, hex
  16457.                  mov  bx_byte, 094h  ; half regs signed, hex
  16458.                  mov  dx_byte, 91h   ; half registers, signed
  16459.                  lea  ax, ax_byte
  16460.                  call set_reg_style
  16461.  
  16462.                  sub  cx, cx         ; clear cx for clarity
  16463.              outer_loop:
  16464.                  mov  ax, 0               ; clear the registers
  16465.                  mov  bx, 0
  16466.                  mov  dx, 0
  16467.                  call set_count
  16468.                  call show_regs
  16469.  
  16470.                  call get_hex_byte        ; byte for al
  16471.                  mov  dx, ax              ; copy to dx
  16472.                  push ax                  ; save al
  16473.                  call get_hex_byte        ; byte for bl
  16474.                  mov  bl, al
  16475.                  mov  bh, bl              ; copy to bh
  16476.                  pop  ax                  ; restore al
  16477.                  call show_regs_and_wait
  16478.                  add  al, bl              ; normal add
  16479.                  mov  dx, ax              ; copy to dx
  16480.                  call show_regs_and_wait
  16481.                  daa                      ; make adjustment
  16482.                  mov  dx, ax              ; copy to dx
  16483.                  call show_regs_and_wait
  16484.                  jmp  outer_loop
  16485.  
  16486.              ; - - - - - - - - - - END CODE ABOVE THIS LINE
  16487.  
  16488.              Since the program works with both BCD adjustments and integer
  16489.              arithmetic, DX shows a signed integer copy of AX and BH shows a
  16490.              signed integer copy of BL. A copy of AX is moved to DX every time
  16491.              an operation is performed on AL.
  16492.  
  16493.              The idea of this subprogram is to enter hex numbers that look
  16494.              like decimal numbers - e.g. 65h, 78h, 08h, 29h. You can enter
  16495.              numbers that contain A-F if you want to see what happens with bad
  16496.              data. Each half byte of a BCD number should look like a decimal
  16497.              number. You will notice that the actual addition is done by ADD.
  16498.              The alteration is done by DAA, which assumes that you have just
  16499.              added two legitimate BCD numbers, and the result is in AL. The
  16500.              register MUST be AL. What DAA does will be discussed in a moment.
  16501.  
  16502.  
  16503.  
  16504.  
  16505.              Chapter 22 - BCD Numbers                                      233
  16506.              ________________________
  16507.  
  16508.              The DAA instruction looks at AL as being two half bytes. If the
  16509.              result from the lower half byte addition is 10 or over, the 8086
  16510.              subtracts 10 and then adds 1 to the upper half byte.{1}  For
  16511.              instance, if the result is 17, it subtracts 10, to leave 7 in the
  16512.              lower half byte, and adds 1 to the upper half byte. After it has
  16513.              done this, it looks at the upper half byte. If the upper half
  16514.              byte is now 10 or over, it subtracts 10 and sets the carry flag
  16515.              for future additions.{2}
  16516.  
  16517.              While the whole thing sounds a little confusing, if you look at
  16518.              the bytes in hex, they will act in the same way as if you were
  16519.              doing normal decimal addition with pencil and paper except all
  16520.              the carries are done at one time. CF will be set if the hundreds
  16521.              digit is 1 and will be cleared if the hundreds digit is 0. Use
  16522.              the previous program a few times to get the hang of what is going
  16523.              on. When you feel confident, we'll move on to subtraction.
  16524.  
  16525.  
  16526.              SUBTRACTION
  16527.  
  16528.              DAS (decimal adjust for subtraction) is similar to DAA. It makes
  16529.              an adjustment after the subtraction itself. We'll use the same
  16530.              program as before, making two alterations. Where you have ADD,
  16531.              change it to SUB ; where you have DAA, change it to DAS. That's
  16532.              all.
  16533.  
  16534.                  add  ->   sub
  16535.                  daa  ->   das
  16536.  
  16537.              Run the program, and put in a number of examples. If the lower
  16538.              number is larger than the top number, there will be a borrow.
  16539.  
  16540.              DAS works the opposite of DAA. If there has been a borrow into
  16541.              the low half byte, it adds 10 to the low half byte and subtracts
  16542.              1 from the high half byte. It then looks at the high half byte.
  16543.              ____________________
  16544.  
  16545.                 1. The technical description is a little confusing, so if you
  16546.              get muddled up, just forget it. Here it goes. (AF is the
  16547.              auxilillary flag). The 8086 checks for either (1) the low half
  16548.              byte > 9 (that is, between 10 and 15) or (2) AF = 1. AF is set on
  16549.              a byte addition when there is a carry out of the low half byte,
  16550.              that is, the result is greater than 16 for the low half byte. If
  16551.              either of these events has occured, the 8086 ADDS 6 to the low
  16552.              half byte. Why?  Let 10 + x represent the result of the addition,
  16553.              where x < 10. We then have:
  16554.  
  16555.                  10 + x + 6 = 10 + 6 + x = 16 + x
  16556.  
  16557.              but 16 is 0001 0000 (10h), the first bit in the high byte, so
  16558.              this has the effect of leaving the part less than 10 in the low
  16559.              half byte and adding 1 (the carry) to the high half byte.
  16560.  
  16561.                 2. This is exactly the same logic as in the last footnote,
  16562.              except that the 8086 adds 60h to the high byte. This has the
  16563.              effect of shifting the excess out of AL altogether. The 8086 then
  16564.              sets the carry flag.
  16565.  
  16566.  
  16567.              The PC Assembler Tutor                                        234
  16568.              ______________________
  16569.  
  16570.              If there has been a borrow into it, the 8086 adds 10 to the high
  16571.              half byte and sets the carry flag.
  16572.  
  16573.              Do a few more examples with the program to make sure you
  16574.              understand what's happening. It will look just like what you do
  16575.              with pencil and paper, except that with pencil and paper you do
  16576.              the borrow before you do the subtraction and here the borrow is
  16577.              done afterwards if it is needed.
  16578.  
  16579.  
  16580.              ADDITION AND SUBTRACTION
  16581.  
  16582.              We have a number of possibilities with addition and subtraction.
  16583.              Here they are. The sign of the numbers is in parentheses and the
  16584.              operation is in between.
  16585.  
  16586.                  (+) + (+)      (+) + (-)      (+) - (+)      (+) - (-)
  16587.                  (-) + (+)      (-) + (-)      (-) - (+)      (-) - (-)
  16588.  
  16589.              The subroutines we use are going to assume that (1) both numbers
  16590.              are the same sign, and (2) for subtraction, the larger number is
  16591.              on top and the smaller number is on the bottom. There should be a
  16592.              section of the program that decides whether addition or
  16593.              subtraction should be used, and which number is on top. Then
  16594.              comes the addition or subtraction. We will write the first part
  16595.              later. For now, when you input numbers, both numbers must have
  16596.              the same sign, and for subtraction, the larger number must be
  16597.              first. Here's the addition subroutine.
  16598.  
  16599.              ; - - - - - START SUBROUTINE BELOW THIS LINE
  16600.              _bcd_addition proc near
  16601.  
  16602.                  RESULT_ADDRESS      EQU  [bp + 8]
  16603.                  BOTTOM_ADDRESS      EQU  [bp + 6]
  16604.                  TOP_ADDRESS         EQU  [bp + 4]
  16605.  
  16606.                  push bp
  16607.                  mov  bp, sp
  16608.                  PUSHREGS  ax, bx, cx, si, di
  16609.  
  16610.                  mov  si, TOP_ADDRESS
  16611.                  mov  bx, BOTTOM_ADDRESS
  16612.                  mov  di, RESULT_ADDRESS
  16613.                  mov  cx, 9          ; 9 bytes of BCD numbers
  16614.                  clc                 ; clear the carry flag
  16615.  
  16616.              add_loop:
  16617.                  mov  al, [si]       ; move and add bytes
  16618.                  adc  al, [bx]       ; add two numbers and the carry
  16619.                  daa                 ; bcd adjust
  16620.                  mov  [di], al       ; store result
  16621.                  inc  si             ; increment pointers
  16622.                  inc  bx
  16623.                  inc  di
  16624.                  loop add_loop
  16625.  
  16626.                  jnc  continue_addition   ; BCD overflow if CF = 1
  16627.  
  16628.  
  16629.              Chapter 22 - BCD Numbers                                      235
  16630.              ________________________
  16631.  
  16632.                  lea  ax, overflow_message
  16633.                  call print_string
  16634.  
  16635.              continue_addition:
  16636.                  mov  al, [si]       ; sign of top addend to result
  16637.                  mov  [di], al
  16638.  
  16639.                  POPREGS   ax, bx, cx, si, di
  16640.                  pop  bp
  16641.                  ret
  16642.  
  16643.              _bcd_addition endp
  16644.              ; - - - - END SUBROUTINE ABOVE THIS LINE
  16645.  
  16646.              AL contains the first number.  We use the carry flag (ADC) for
  16647.              carrying from byte to byte. The carry flag is cleared before the
  16648.              first addition since we don't want a carry there. The INC
  16649.              instruction was designed by Intel so that it would not alter the
  16650.              carry flag just so we could use it in situations like this.
  16651.  
  16652.              If CF = 1 upon exiting the loop, the result was too large and we
  16653.              had overflow. In this case we print a message to that effect.
  16654.  
  16655.              The result [di] can be stored in the same place as one of the
  16656.              numbers. The addition of each byte is completed before the result
  16657.              for that byte is stored, so this won't interfere with the
  16658.              addition. We assume that the sign of the result is the sign of
  16659.              the top addend. (If this goes into a working program, it will
  16660.              only be used if both numbers have the same sign).
  16661.  
  16662.              This is designed as a C subroutine. The calling program pushes
  16663.              things on the stack and it has the responsibility of popping them
  16664.              off the stack on return from the call.
  16665.  
  16666.              Here's the calling routine:
  16667.  
  16668.              ; - - - - - ENTER DATA BELOW THIS LINE
  16669.              bcd_num1 dt   ?
  16670.              bcd_num2 dt   ?
  16671.              bcd_num3 dt   ?
  16672.              overflow_message   db  "We had overflow.", 0
  16673.              ; - - - - - ENTER DATA ABOVE THIS LINE
  16674.              ; - - - - - ENTER CODE BELOW THIS LINE
  16675.  
  16676.              outer_loop:
  16677.                  lea  ax, bcd_num1        ; get 2 numbers for addition
  16678.                  call get_bcd
  16679.                  lea  ax, bcd_num2
  16680.                  call get_bcd
  16681.  
  16682.                  lea  ax, bcd_num3        ; push addresses for subroutine
  16683.                  push ax
  16684.                  lea  ax, bcd_num2
  16685.                  push ax
  16686.                  lea  ax, bcd_num1
  16687.                  push ax
  16688.  
  16689.  
  16690.  
  16691.              The PC Assembler Tutor                                        236
  16692.              ______________________
  16693.  
  16694.                  call _bcd_addition
  16695.                  add  sp, 6                   ; 3 pushes = 6 bytes
  16696.  
  16697.                  lea  ax, bcd_num1        ; print both numbers and result
  16698.                  call print_bcd
  16699.                  lea  ax, bcd_num2
  16700.                  call print_bcd
  16701.                  lea  ax, bcd_num3
  16702.                  call print_bcd
  16703.                  loop outer_loop
  16704.  
  16705.              ; - - - - - ENTER CODE ABOVE THIS LINE
  16706.  
  16707.              This is the main program. The other one should be in the
  16708.              subroutine section. This program is straightforward. We get two
  16709.              numbers, call the subroutine, and print the numbers and the
  16710.              result. Try some numbers. The results you get will always have
  16711.              the sign of the top number and will have the same numerical
  16712.              result as adding two unsigned numbers.
  16713.  
  16714.              The subtraction routine is the same as the addition but we need
  16715.              to make some changes because it is subtraction:
  16716.  
  16717.                  ADC  ->   SBB
  16718.                  DAA  ->   DAS  (decimal adjust for subtraction)
  16719.  
  16720.                  add_loop:      ->   subtract_loop
  16721.                  loop add_loop  ->   loop subtract_loop
  16722.  
  16723.                  jmp  continue_addition   ->   jmp  continue_subtraction
  16724.                  continue_addition:       ->   continue_subtraction:
  16725.  
  16726.              Also, we want to change the subroutine name and call
  16727.  
  16728.                  _bcd_addition       ->   _bcd_subtraction
  16729.                  call _bcd_addition  ->   call _bcd_subtraction
  16730.  
  16731.              Do all these changes, run the program, but make sure the top
  16732.              number is larger or you will get strange results.
  16733.  
  16734.              We now have an addition routine that only works with the right
  16735.              numbers and a subtraction routine that is very touchy about the
  16736.              numbers that are put in. How can these things help us? In fact
  16737.              they are almost all we need. The only thing else we need is a
  16738.              preliminary subroutine to organize what we do. To see why we have
  16739.              some orginizational problems, take out a pencil and paper and do
  16740.              the following additions and subtractions. Do these with a pencil
  16741.              and paper, not a pocket calculator:
  16742.  
  16743.                  (+15)     +    (+27)               (+15)     +    (-27)
  16744.                  (+15)     -    (+27)               (+15)     -    (-27)
  16745.                  (-15)     +    (+27)               (-15)     +    (-27)
  16746.                  (-15)     -    (+27)               (-15)     -    (-27)
  16747.                  (+27)     +    (+15)               (+27)     +    (-15)
  16748.                  (+27)     -    (+15)               (+27)     -    (-15)
  16749.                  (-27)     +    (+15)               (-27)     +    (-15)
  16750.                  (-27)     -    (+15)               (-27)     -    (-15)
  16751.  
  16752.  
  16753.              Chapter 22 - BCD Numbers                                      237
  16754.              ________________________
  16755.  
  16756.  
  16757.              There are only four possible answers:  +12, -12, +42 and -42. We
  16758.              had 16 different additions and subtractions, yet we got only 4
  16759.              possible answers and only 2 possible absolute values. We could
  16760.              have a different subroutine for each one, but 16 subprograms is a
  16761.              LOT of code, so it's easier to do it with 2 subprograms. We
  16762.              merely need to order the numbers and pick the correct subroutine.
  16763.  
  16764.              Here is the BCD driving routine. We'll get a number and then
  16765.              we'll get an operation, either addition or subtraction. If it is
  16766.              subtraction, when we get the second number, we REVERSE the sign.
  16767.              We don't even check what it is; plus becomes minus and minus
  16768.              becomes plus. What we have now is the ADDITION of two signed
  16769.              numbers. We XOR the two signs. If the result is 0, they both are
  16770.              the same sign and we can go to the addition subroutine. If the
  16771.              signs are different we need to subtract the smaller from the
  16772.              larger. We find out which one is larger and then call the
  16773.              subtraction routine. It is going to take you a little time to
  16774.              read this code.
  16775.  
  16776.              ; - - - - - START DATA BELOW THIS LINE
  16777.              top_number    dt   ?
  16778.              bottom_number dt   ?
  16779.              result        dt   ?
  16780.              sign_mask     db   ?
  16781.              sign_message       db   "Enter either + or -.", 0
  16782.              overflow_message   db   "We had overflow.", 0
  16783.              ; - - - - - END DATA ABOVE THIS LINE
  16784.  
  16785.              ; - - - - - START CODE BELOW THIS LINE
  16786.              outer_loop:
  16787.                  lea  ax, top_number
  16788.                  call get_bcd
  16789.  
  16790.              sign_loop:
  16791.                  ; get either a '+' or a '-'
  16792.                  lea  ax, sign_message    ; prompt for operation
  16793.                  call print_string
  16794.                  call get_ascii_byte      ; operation type in al
  16795.                  cmp  al, '+'
  16796.                  jne  check_for_minus
  16797.                  mov  sign_mask, 00h      ; for XOR of bottom number sign
  16798.                  jmp  get_second_number
  16799.              check_for_minus:
  16800.                  cmp  al, '-'
  16801.                  jne  sign_loop           ; if not a minus then redo
  16802.                  mov  sign_mask, 80h      ; for XOR of bottom sign
  16803.  
  16804.              get_second_number:
  16805.                  lea  ax, bottom_number
  16806.                  call get_bcd
  16807.                  ; XOR bottom sign with sign mask
  16808.                  mov  al, sign_mask
  16809.                  xor  BYTE PTR (bottom_number + 9), al   ; sign byte
  16810.  
  16811.                  ; same sign or different signs?
  16812.                  mov  ah, BYTE PTR (top_number + 9)      ; sign byte
  16813.  
  16814.  
  16815.              The PC Assembler Tutor                                        238
  16816.              ______________________
  16817.  
  16818.                  xor  ah, BYTE PTR (bottom_number + 9)   ; different?
  16819.                  jnz  which_is_larger          ; if different, subtract
  16820.  
  16821.                  ; same sign, so add
  16822.                  lea  ax, result               ; push parameters and add
  16823.                  push ax
  16824.                  lea  ax, bottom_number
  16825.                  push ax
  16826.                  lea  ax, top_number
  16827.                  push ax
  16828.                  call _bcd_addition
  16829.                  add  sp, 6                    ; adjust the stack
  16830.                  jmp  print_the_numbers
  16831.  
  16832.              which_is_larger:
  16833.                  lea  si, top_number + 8       ; top digits
  16834.                  lea  di, bottom_number + 8
  16835.                  mov  cx, 9                    ; 9 bytes of digits
  16836.              check_for_greater_loop:
  16837.                  mov  al, [si]                 ; top number
  16838.                  cmp  al, [di]                 ; bottom number
  16839.                  ja   top_is_more
  16840.                  jb   bottom_is_more
  16841.                  dec  si                       ; equal, so continue
  16842.                  dec  di
  16843.                  loop check_for_greater_loop
  16844.  
  16845.                  ; we fell through so they are the same
  16846.                  ; leave the top number on top
  16847.              top_is_more:
  16848.                  lea  ax, result
  16849.                  push ax
  16850.                  lea  ax, bottom_number
  16851.                  push ax
  16852.                  lea  ax, top_number
  16853.                  push ax
  16854.                  call _bcd_subtraction
  16855.                  add  sp, 6                    ; adjust the stack
  16856.                  jmp  print_the_numbers
  16857.  
  16858.              bottom_is_more:
  16859.                  lea  ax, result
  16860.                  push ax
  16861.                  lea  ax, top_number
  16862.                  push ax
  16863.                  lea  ax, bottom_number
  16864.                  push ax
  16865.                  call _bcd_subtraction
  16866.                  add  sp, 6                    ; adjust the stack
  16867.  
  16868.              print_the_numbers:
  16869.                  lea  ax, top_number
  16870.                  call print_bcd
  16871.                  lea  ax, bottom_number
  16872.                  call print_bcd
  16873.                  lea  ax, result
  16874.                  call print_bcd
  16875.  
  16876.  
  16877.              Chapter 22 - BCD Numbers                                      239
  16878.              ________________________
  16879.  
  16880.                  jmp  outer_loop
  16881.              ; - - - - - END CODE ABOVE THIS LINE
  16882.  
  16883.              The sign_mask is either 00h if it is addition or 80h if it is
  16884.              subtraction. 00h XOR sign_byte will leave the sign byte
  16885.              unchanged. 80h XOR sign_byte will reverse the sign of the sign
  16886.              byte.
  16887.  
  16888.              Thus, as we did in the earlier multiplication and division
  16889.              routines, we sometimes change the sign of a number
  16890.              (bottom_number). In a real routine we would either have to make a
  16891.              copy of it when we change the sign or change the sign back at the
  16892.              end. Here we leave it with the sign change (if any) so you can
  16893.              see what is happening internally. If you want to restore the
  16894.              number, you can alter the code a little:
  16895.  
  16896.                  print_the_numbers:
  16897.                       mov  al, sign_mask                      ; add this
  16898.                       xor  BYTE PTR (bottom_number + 9), al   ; add this
  16899.                       lea  ax, top_number
  16900.  
  16901.              This will restore the bottom number to its original form ASSUMING
  16902.              THAT THE RESULT HAS NOT BEEN STORED THERE.
  16903.  
  16904.              It is possible to get -0 as a result. This is a legally defined
  16905.              number: +0 = -0.
  16906.  
  16907.              There are three places where we have 'BYTE PTR'. This is because
  16908.              the variables are defined as 10 byte long objects and the
  16909.              assembler will complain if we don't put in the 'BYTE PTR'.
  16910.  
  16911.              Finally, if we want to use the typical 'destination, source'
  16912.              style for the 8086, it is built in. Here it is for addition:
  16913.  
  16914.                  lea  ax, top_number
  16915.                  push ax
  16916.                  lea  ax, bottom_number
  16917.                  push ax
  16918.                  lea  ax, top_number
  16919.                  push ax
  16920.  
  16921.              We put the address of 'top_number' where the result address goes.
  16922.              The routines store a byte only after all calculations are done on
  16923.              that particular byte, so there is no interference from doing
  16924.              this.
  16925.  
  16926.  
  16927.  
  16928.  
  16929.                                                                            240
  16930.  
  16931.              The first thing to talk about is the 8086 mnemonics. The four
  16932.              instructions for unpacked BCD numbers are:
  16933.  
  16934.                  AAA       ASCII adjust for addition
  16935.                  AAD       ASCII adjust for division
  16936.                  AAM       ASCII adjust for multiplication
  16937.                  AAS       ASCII adjust for subtraction.
  16938.  
  16939.  
  16940.              Even though all four instructions have ASCII as part of their
  16941.              mnemonic, they have NOTHING to do with ASCII numbers. These
  16942.              instructions operate on unpacked BCD numbers. They always give
  16943.              results which are unpacked BCD numbers. Because of side effects
  16944.              of the 8086 microcode, the add and subtract instructions do
  16945.              something unusual, but this will be covered a little later.
  16946.  
  16947.              Just like the packed BCD instructions, these four unpacked
  16948.              instructions use the normal arithmetic operations and adjust the
  16949.              results to compensate for their being unpacked BCD numbers. Let's
  16950.              start with addition. The following program is like the BCD
  16951.              program except that we use AAA (ascii adjust for addition)
  16952.              instead of DAA (decimal adjust for addition).
  16953.  
  16954.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  16955.                     mov   ax_byte, 0C4h       ; half registers, hex, hex
  16956.                     mov   bx_byte, 94h        ; half regs, signed, hex
  16957.                     mov   dx_byte, 91h        ; half regs, signed, signed
  16958.                     lea   ax, ax_byte
  16959.                     call  set_reg_style
  16960.  
  16961.                     mov   cx, 0                    ; clear cx for clarity
  16962.              outer_loop:
  16963.                     mov   ax, 0                    ; clear the registers
  16964.                     mov   bx, 0
  16965.                     mov   dx, 0
  16966.                     call  set_count
  16967.                     call  show_regs
  16968.  
  16969.                     call  get_hex_byte             ; byte for al
  16970.                     mov   dx, ax                   ; copy for dx
  16971.                     push  ax                       ; save al
  16972.                     call  get_hex_byte             ; byte for bl
  16973.                     mov   bl, al
  16974.                     mov   bh, bl                   ; copy to bh
  16975.                     pop   ax                       ; restore al
  16976.                     call  show_regs_and_wait
  16977.                     add   al, bl                   ; normal add
  16978.                     mov   dx, ax                   ; copy to dx
  16979.                     call  show_regs_and_wait
  16980.                     aaa                            ; make adjustment
  16981.                     mov   dx, ax                   ; copy to dx
  16982.                     call  show_regs_and_wait
  16983.                     jmp   outer_loop
  16984.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  16985.  
  16986.              ______________________
  16987.  
  16988.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  16989.  
  16990.  
  16991.              Chapter 22 - BCD Numbers                                      241
  16992.              ________________________
  16993.  
  16994.  
  16995.              Since this is a mixture of unpacked BCD instructions and normal
  16996.              integer instructions, a copy of AX (which is showing hex) is
  16997.              moved to DX (which is showing signed) each time AX is changed.
  16998.              Also, a copy of BL is put in BH.
  16999.  
  17000.              For the rest of this chapter, I will refer to the one's digit,
  17001.              the ten's digit and the hundred's digit. In the number 346, 3 is
  17002.              the hundred's digit, 4 is the ten's digit, and 6 is the one's
  17003.              digit. In the number 928, 9 is the hundred's digit, 2 is the
  17004.              ten's digit, and 8 is the one's digit.
  17005.  
  17006.              If two valid unpacked BCD numbers are added, the result will be
  17007.              between 0 and 18. This result MUST be in AL. AAA sets CF if this
  17008.              result is greater than 9 ( i.e. there was a carry). AAA leaves
  17009.              the one's digit in AL and adds the ten's digit to AH. (The ten's
  17010.              digit can only be 0 for 0 - 9 or 1 for 10 - 18).
  17011.  
  17012.              Run this. The standard input should be hex numbers between 00h
  17013.              and 09h. When you feel comfortable with this, stop for a minute
  17014.              and read on.
  17015.  
  17016.              We now come to the peculiarity of this instruction. It isn't
  17017.              operating on the whole byte, only the lower half byte.
  17018.              Technically, if the LOWER HALF BYTE of each of the two numbers
  17019.              which are added is a legitimate BCD digit, then at the end of AAA
  17020.              the above results will be true and the UPPER HALF BYTE of AL will
  17021.              be set to 0. If you put anything in the upper half byte, it will
  17022.              be added by ADD, but will be blanked out (zeroed) by AAA.{1}
  17023.  
  17024.  
  17025.              As a side effect of blanking out (zeroing) the upper half byte,
  17026.              it is possible to use the ASCII numbers '0' (30h) to '9' (39h) as
  17027.              addends. The result (after AAA) will still be a number between
  17028.              00h and 09h with the carry flag either set or cleared, and AH
  17029.              incremented if appropriate. Don't use it this way. The
  17030.              multiplication and division instructions REQUIRE that the numbers
  17031.              be between 00h and 09h, so all numbers should be changed into
  17032.              unpacked BCD on data entry. Here is a partial list of things you
  17033.              can add to get the result 07h:
  17034.  
  17035.                  03h + 04h           'c' + 'd'           '+' + 't'
  17036.                  '3' + '4'           's' + 't'           'c' + '4'
  17037.                  'C' + 'D'           '#' + '$'           'S' + '$'
  17038.  
  17039.              As you can see, the addition instruction is pretty indiscriminate
  17040.              about what kind of data you can put in and still get a legitimate
  17041.              unpacked number out. It is your job to make sure that you have
  17042.              ____________________
  17043.  
  17044.                 1. They used the same microcode as for the beginning of DAA.
  17045.              If the low half byte of AL is greater than 9, or if there was a
  17046.              carry out of the low half byte (if AF, the auxillary flag was
  17047.              set), then the 8086 adds 6 to AL, which shifts the excess out of
  17048.              the low half byte. It then zeros the high half byte, sets the
  17049.              carry flag, and increments AH. If you don't understand this, go
  17050.              back and give the footnote on DAA a try.
  17051.  
  17052.  
  17053.              The PC Assembler Tutor                                        242
  17054.              ______________________
  17055.  
  17056.              legitimate unpacked data upon data entry.
  17057.  
  17058.              If this is just a side effect of blanking the upper half byte,
  17059.              why did they name the instruction "ascii adjust for addition"?
  17060.              It's just that impish sense of humor of those Intel engineers. If
  17061.              you think of these instructions as having anything to do with
  17062.              ASCII numbers, you will only confuse yourself. These are
  17063.              instructions on unpacked BCD numbers, period.
  17064.  
  17065.              The other thing to notice is that this instruction increments the
  17066.              AH register if there is a carry, so whenever you use AAA, it is
  17067.              going to trash the AH register. Count on it. If AH contains
  17068.              important data, store it somewhere else.
  17069.  
  17070.  
  17071.              SUBTRACTION
  17072.  
  17073.              When you have gotten used to how this works, look at subtraction.
  17074.              The subtraction routine is the same as the addition routine
  17075.              except that (1) ADD is replaced by SUB and (2) AAA is replaced by
  17076.              AAS (ascii adjust for subtraction). If you generate a borrow, the
  17077.              carry flag will be set and AH will be decremented by 1.
  17078.  
  17079.              If you have two legitimate unpacked BCD numbers, then after
  17080.              subtraction the result will be from +9 to -9. This result must be
  17081.              in AL. After AAS (ascii adjust for subtraction), (1) if AL was
  17082.              from 0 to +9, it will stay the same and CF will be cleared (CF=0)
  17083.              or (2) if AL was -1 to -9, AAF will borrow 1 from AH (considering
  17084.              it a ten's digit), and add 10 to AL. This will give a number from
  17085.              +1 to +9. It will also set the carry flag (CF=1) to signal a
  17086.              borrow. This is what you do with pencil and paper except that you
  17087.              always do the borrow BEFORE the subtraction and AAS always does
  17088.              the borrow AFTER the subtraction. Once again, this operates on
  17089.              the LOW HALF BYTE, so what is in the upper half byte of the
  17090.              numbers is irrelevant. It will be zeroed by AAS.
  17091.  
  17092.              There is all sorts of data which will generate a legitimate
  17093.              unpacked result; some of it is actually legitimate input. It too
  17094.              trashes the AH register, so beware. Run this program till you see
  17095.              what is going on with individual numbers.
  17096.  
  17097.  
  17098.              MULTIPLICATION
  17099.  
  17100.              There is also an instruction for multiplication. It assumes that
  17101.              AL contains the result of the multiplication of two unpacked BCD
  17102.              numbers. That is, 0 <= AL <= 81. After AAM (ascii adjust for
  17103.              multiplication), AH will contain the 10's digit and AL will
  17104.              contain the 1's digit. If AL is 75, then after AAM, AH will be 7
  17105.              and AL will be 5. If AL is 48, then after AAM, AH will be 4 and
  17106.              AL will be 8. (If AL is 183, an illegal result, then after AAM,
  17107.              AH will be 18 and AL will be 3).
  17108.  
  17109.              We are going to use a similar program for multiplication, but AX
  17110.              and BL will be half byte unsigned; BH and DX will be half byte
  17111.              hex. Every time we change AX, we will copy it to DX. Here is the
  17112.              program:
  17113.  
  17114.  
  17115.              Chapter 22 - BCD Numbers                                      243
  17116.              ________________________
  17117.  
  17118.  
  17119.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  17120.                     mov   ax_byte, 0A2h       ; half registers, unsigned
  17121.                     mov   bx_byte, 0C2h       ; half regs, hex, unsigned
  17122.                     mov   dx_byte, 0C4h       ; half regs, hex
  17123.                     lea   ax, ax_byte
  17124.                     call  set_reg_style
  17125.  
  17126.                     mov   cx, 0               ; clear cx for clarity
  17127.              outer_loop:
  17128.                     mov   ax, 0               ; clear the registers
  17129.                     mov   bx, 0
  17130.                     mov   dx, 0
  17131.                     call  set_count
  17132.                     call  show_regs
  17133.  
  17134.                     call  get_hex_byte        ; byte for al
  17135.                     mov   dx, ax              ; copy to dx
  17136.                     push  ax                  ; save al
  17137.                     call  get_hex_byte        ; byte for bl
  17138.                     mov   bl, al
  17139.                     mov   bh, bl              ; copy to bh
  17140.                     pop   ax                  ; restore al
  17141.                     call  show_regs_and_wait
  17142.                     mul   bl                  ; unsigned multiplication
  17143.                     mov   dx, ax              ; copy to dx
  17144.                     call  show_regs_and_wait
  17145.                     aam                       ; make adjustment
  17146.                     mov   dx, ax              ; copy to dx
  17147.                     call  show_regs_and_wait
  17148.                     jmp   outer_loop
  17149.               ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  17150.  
  17151.              All you need to change from the addition program is the register
  17152.              style, ADD -> MUL and AAA -> AAM.
  17153.  
  17154.              Try this out. Notice that after MUL, what we get in DX is
  17155.              garbage. This number is a pure unsigned number, not a BCD number.
  17156.              Just to underline how important it is to have legitimate unpacked
  17157.              BCD data, try a few multiplications where the upper half byte is
  17158.              non-zero and see what happens.
  17159.  
  17160.  
  17161.              DIVISION
  17162.  
  17163.              AAD (ascii adjust for division) is slightly different. What we
  17164.              want to do is divide a number from 0 - 99 by a number from 1 to 9
  17165.              (0 gives a zero divide interrupt). Therefore, AAD multiplies what
  17166.              is in AH by 10 and adds it to what is in AL. It clears AH for the
  17167.              coming unsigned division.{2}  If AH contains 7 and AL contains 2,
  17168.              then after AAD, AL will be 72 and AH will be 0. If AH is 4 and AL
  17169.              is 6, then after AAD, AL will be 46 and AH will be 0. (If AH is
  17170.              14 and AL is 7 - an illegal situation - then after AAD, AL will
  17171.              ____________________
  17172.  
  17173.                 2. Remember that for unsigned byte division, we set AH to 0.
  17174.              This was back in the first chapter on division.
  17175.  
  17176.  
  17177.              The PC Assembler Tutor                                        244
  17178.              ______________________
  17179.  
  17180.              be 147 and AH will be 0)
  17181.  
  17182.              After you have done AAD, you are ready for regular unsigned
  17183.              division. After division, the quotient will be in AL and the
  17184.              remainder will be in AH. Here's the program:
  17185.  
  17186.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  17187.                     mov   ax_byte, 0A2h      ; half registers, unsigned
  17188.                     mov   bx_byte, 0C2h      ; half regs, hex, unsigned
  17189.                     mov   dx_byte, 0C4h      ; half regs, hex
  17190.                     lea   ax, ax_byte
  17191.                     call  set_reg_style
  17192.  
  17193.                     mov   cx, 0              ; clear cx for clarity
  17194.              outer_loop:
  17195.                     mov   ax, 0               ; clear the registers
  17196.                     mov   bx, 0
  17197.                     mov   dx, 0
  17198.                     call  set_count
  17199.                     call  show_regs
  17200.  
  17201.                     call  get_hex             ; word
  17202.                     mov   dx, ax              ; copy to dx
  17203.                     push  ax                  ; save al for later
  17204.                     call  get_hex_byte        ; byte for bl
  17205.                     mov   bl, al
  17206.                     mov   bh, bl              ; copy to bh
  17207.                     pop   ax                  ; get back al
  17208.                     call  show_regs_and_wait
  17209.                     aad                       ; adjust for division
  17210.                     mov   dx, ax              ; copy to dx
  17211.                     call  show_regs_and_wait
  17212.                     div   bl                  ; unsigned division
  17213.                     mov   dx, ax              ; copy to dx
  17214.                     call  show_regs_and_wait
  17215.                     jmp   outer_loop
  17216.  
  17217.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  17218.  
  17219.              The differences from the multiplication routine are (1) we get a
  17220.              TWO byte hex number for the dividend and (2) AAD comes first,
  17221.              then DIV. That's all.
  17222.  
  17223.              We need to enter a TWO byte unpacked BCD number in order to get
  17224.              grist for the mill. 87 is 0807h, 93 is 0903h, 42 is 0402h. This
  17225.              will give us two unpacked digits so we have something to put in
  17226.              AH.
  17227.  
  17228.              Run this program and enter legitimate (and if you want,
  17229.              illegitimate) data. Notice that even with legitimate data, it is
  17230.              possible to get a quotient that is larger than 9. (47 / 3 is 15,
  17231.              remainder 2). Don't worry about this. We will avoid this problem
  17232.              in the real program.
  17233.  
  17234.  
  17235.              PACKING AND UNPACKING
  17236.  
  17237.  
  17238.  
  17239.              Chapter 22 - BCD Numbers                                      245
  17240.              ________________________
  17241.  
  17242.              Making an i/o routine is such a bother that we will enter data
  17243.              with 'get_bcd' and output data with 'print_bcd'. To do this, we
  17244.              need to pack and unpack the data. The calls in C would be:
  17245.  
  17246.                  unpack_bcd (&packed_number, &unpacked_number) ;
  17247.                  pack_bcd (&unpacked_number, &packed_number) ;{3}
  17248.  
  17249.              The thing to be operated on is the first variable and the result
  17250.              is the second variable. First, here's the unpacking routine:
  17251.  
  17252.              ; - - - - - - - - - -
  17253.              unpack_bcd proc near
  17254.  
  17255.                     UNPACK_UNPACKED_ADDRESS  EQU    [bp + 6]
  17256.                     UNPACK_BCD_ADDRESS       EQU    [bp + 4]
  17257.  
  17258.  
  17259.                     push  bp
  17260.                     mov   bp, sp
  17261.                     PUSHREGS  ax, bx, cx, si, di
  17262.  
  17263.                     mov   si, UNPACK_BCD_ADDRESS
  17264.                     mov   di, UNPACK_UNPACKED_ADDRESS
  17265.  
  17266.                     ; start at top to unpack
  17267.                     add   si, 9               ; bcd sign
  17268.                     add   di, 18              ; unpacked sign
  17269.                     mov   al, [si]            ; move sign to unpacked
  17270.                     mov   [di], al
  17271.                     dec   si                  ; move down to next byte
  17272.                     dec   di
  17273.  
  17274.                     mov   cx, 9               ; 9 bcd data bytes
  17275.                     ; unpack high byte first, then low byte
  17276.              unpack_loop:
  17277.                     push  cx                  ; save counter
  17278.                     mov   al, [si]            ; bcd byte to al
  17279.                     mov   bl, al              ; copy to bl
  17280.                     mov   cl, 4
  17281.                     ror   al, cl              ; high half byte to low half
  17282.                     and   al, 0Fh             ; blank upper half byte
  17283.                     mov   [di], al            ; al is high half of bcd byte
  17284.                     dec   di                  ; result pointer
  17285.                     and   bl, 0Fh             ; blank upper half byte
  17286.                     mov   [di], bl            ; bl is low half bcd byte
  17287.                     dec   di                  ; result pointer
  17288.                     dec   si                  ; source pointer
  17289.                     pop   cx                  ; restore counter
  17290.                     loop  unpack_loop
  17291.  
  17292.                     POPREGS  ax, bx, cx, si, di
  17293.                     pop   bp
  17294.                     ret
  17295.              ____________________
  17296.  
  17297.                 3. That '&' means that we are passing the addresses, not the
  17298.              values.
  17299.  
  17300.  
  17301.              The PC Assembler Tutor                                        246
  17302.              ______________________
  17303.  
  17304.  
  17305.              unpack_bcd  endp
  17306.              ; - - - - - - - - - - - - - - - -
  17307.  
  17308.              This is pretty straightforward. First it moves the sign. Then,
  17309.              taking a BCD byte, the routine divides it in two parts, rotating
  17310.              the high half byte to the proper position, storing it,
  17311.              DECREMENTING the pointer and storing the low half byte. The upper
  17312.              half byte of each unpacked byte is zeroed. Notice that by
  17313.              starting at the top, it is possible to unpack a number in place.
  17314.              Diagram what is happening to make sure you believe that.
  17315.  
  17316.              Here's the packing routine:
  17317.  
  17318.              ; - - - - - - - - - - - - - - - -
  17319.              pack_bcd     proc near
  17320.  
  17321.                     PACK_BCD_ADDRESS       EQU     [bp + 6]
  17322.                     PACK_UNPACKED_ADDRESS  EQU     [bp + 4]
  17323.  
  17324.                     push  bp
  17325.                     mov   bp, sp
  17326.                     PUSHREGS  ax, bx, cx, si, di
  17327.  
  17328.                     mov   si, PACK_UNPACKED_ADDRESS
  17329.                     mov   di, PACK_BCD_ADDRESS
  17330.  
  17331.                     ; start at bottom to pack
  17332.                     mov   cx, 9               ; 9 bytes of bcd data
  17333.              pack_loop:
  17334.                     push  cx                  ; save counter
  17335.                     mov   al, [si + 1]        ; high unpacked byte
  17336.                     mov   cl, 4
  17337.                     ror   al, cl              ; move to high half byte
  17338.                     or    al, [si]            ; OR low half with high half
  17339.                     mov   [di], al            ; move to BCD
  17340.                     inc   di                  ; adjust pointers
  17341.                     add   si, 2
  17342.                     pop   cx                  ; restore counter
  17343.                     loop  pack_loop
  17344.  
  17345.                     ; si and di are now in the right place for the sign
  17346.                     mov   al, [si]
  17347.                     mov   [di], al
  17348.  
  17349.                     POPREGS  ax, bx, cx, si, di
  17350.                     pop   bp
  17351.                     ret
  17352.  
  17353.              pack_bcd endp
  17354.              ; - - - - - - - -
  17355.  
  17356.              This does the reverse process, rotating the high byte to the
  17357.              proper place, then ORing the two together to form a packed BCD
  17358.              byte. Notice that by starting at the BOTTOM it is possible to
  17359.              pack a number in place. Diagram the action to make sure you
  17360.              believe this.
  17361.  
  17362.  
  17363.              Chapter 22 - BCD Numbers                                      247
  17364.              ________________________
  17365.  
  17366.  
  17367.              These two routines allow us to use 19 byte unpacked BCD numbers.
  17368.              Here's the multiplication routine for a 19 byte (18 data bytes
  17369.              and 1 sign byte) unpacked number by a 1 byte unpacked number:
  17370.  
  17371.              ; - - - - -
  17372.              unpacked_multiply  proc near
  17373.  
  17374.                     PUSHREGS  ax, bx, cx, dx, si, di
  17375.                     mov   si, offset multiplicand
  17376.                     mov   di, offset result
  17377.                     mov   dh, 0                    ; clear dh for carry
  17378.                     mov   bl, multiplier_copy
  17379.                     mov   cx, 18                   ; 18 data bytes
  17380.  
  17381.              mult_loop:
  17382.                     mov   al, [si]                 ; multiplicand to al
  17383.                     mul   bl                       ; multiply
  17384.                     add   al, dh                   ; add old carry
  17385.                     aam                            ; adjust al
  17386.                     mov   [di], al                 ; partial result
  17387.                     mov   dh, ah                   ; save carry
  17388.                     inc   si                       ; adjust pointers
  17389.                     inc   di
  17390.                     loop  mult_loop
  17391.  
  17392.                     mov   extra_byte, ah           ; extra byte
  17393.                     POPREGS  ax, bx, cx, dx, si, di
  17394.                     ret
  17395.  
  17396.              unpacked_multiply  endp
  17397.              ; - - - - -
  17398.  
  17399.              This is a clone of what we had in the chapter on multiple word
  17400.              multiplication but is byte multiplication instead of word
  17401.              multiplication. Refer back to that chapter to understand the
  17402.              mechanics of the process. We store the partial result and save
  17403.              the high byte for addition with the next multiplication. At the
  17404.              end we save the 19th byte for printout.
  17405.  
  17406.              We are cheating a little. we have:
  17407.  
  17408.                     mul   bl                       ; multiply
  17409.                     add   al, dh                   ; add old carry
  17410.                     aam                            ; adjust al
  17411.  
  17412.              when we should have:
  17413.  
  17414.                     mul   bl                       ; multiply
  17415.                     aam                            ; adjust al
  17416.                     add   al, dh                   ; add old carry
  17417.                     aaa                            ; adjust al
  17418.  
  17419.              The reason this works is that the maximum multiplication is
  17420.              9X9=81. The maximum addition is 81+9 = 90. AAM will work
  17421.              correctly with any number 99 or less, so we save a step; we do
  17422.              one adjustment instead of two.
  17423.  
  17424.  
  17425.              The PC Assembler Tutor                                        248
  17426.              ______________________
  17427.  
  17428.  
  17429.              Now, let's look at the driver for the program:
  17430.  
  17431.              ; + + + + + START DATA BELOW THIS LINE
  17432.              multiplier_message  db    "Enter a number from -9 to +9", 0
  17433.              bcd_in              dt    ?
  17434.              bcd_out             dt    ?
  17435.              multiplicand        db    19 dup (?)
  17436.              multiplier          db    ?
  17437.              multiplier_copy     db    ?
  17438.              result              db    19 dup (?)
  17439.              extra_byte          db    ?
  17440.              result_sign         db    ?
  17441.              ; + + + + + END DATA ABOVE THIS LINE
  17442.  
  17443.  
  17444.              ; + + + + + START CODE BELOW THIS LINE
  17445.              outer_loop:
  17446.                     mov   ax, offset bcd_in
  17447.                     call  get_bcd
  17448.                     call  print_bcd           ; reprint for clarity
  17449.                     lea   cx, multiplicand
  17450.                     push  cx                  ; unpacked address
  17451.                     push  ax                  ; bcd_in address
  17452.                     call  unpack_bcd
  17453.                     add   sp, 4               ; adjust the stack
  17454.                     mov   al, multiplicand + 18      ; sign byte
  17455.                     mov   result_sign, al     ; either 00h or 80h
  17456.  
  17457.              enter_multiplier:
  17458.                     lea   ax, multiplier_message
  17459.                     call  print_string
  17460.                     call  get_signed_byte
  17461.                     ; check for valid multiplier
  17462.                     cmp   al, 9               ; > +9 ?
  17463.                     jg    enter_multiplier
  17464.                     cmp   al, -9              ; < -9?
  17465.                     jl    enter_multiplier
  17466.  
  17467.                     ; adjust multiplier sign
  17468.                     mov   multiplier, al
  17469.                     mov   multiplier_copy, al
  17470.                     and   al, 80h             ; sign bit set?
  17471.                     jz    do_the_multiplication
  17472.                     ; negative multiplier, so adjust
  17473.                     neg   multiplier_copy     ; make positive
  17474.                     xor   result_sign, 80h    ; reverse sign of result
  17475.  
  17476.              do_the_multiplication:
  17477.                     call  unpacked_multiply
  17478.  
  17479.                     mov   al, result_sign   ; transfer sign to result
  17480.                     mov   result + 18, al
  17481.  
  17482.                     ; pack the result
  17483.                     mov   ax, offset bcd_out  ; bcd number
  17484.                     push  ax
  17485.  
  17486.  
  17487.              Chapter 22 - BCD Numbers                                      249
  17488.              ________________________
  17489.  
  17490.                     mov   ax, offset result   ; unpacked number
  17491.                     push  ax
  17492.                     call  pack_bcd
  17493.                     add   sp, 4               ; adjust the stack
  17494.  
  17495.                     ; print multiplicand, multiplier, extra byte and result
  17496.                     mov   ax, offset bcd_in
  17497.                     call  print_bcd
  17498.                     mov   al, multiplier
  17499.                     call  print_signed_byte
  17500.                     mov   al, extra_byte
  17501.                     call  print_hex_byte
  17502.                     mov   ax, offset bcd_out
  17503.                     call  print_bcd
  17504.  
  17505.                     jmp   outer_loop
  17506.              ; - - - - - END CODE ABOVE THIS LINE
  17507.  
  17508.              We enter a multiplicand, unpack it, enter a number from -9 to +9
  17509.              and save a copy of its absolute value as well as the sign the
  17510.              result will be. We adjust the sign of the result after the
  17511.              multiplication. No matter what you enter, the sign will be
  17512.              correct, and if you put the 19th byte in front of the BCD number
  17513.              which you have printed, the absolute value will be correct. We
  17514.              are multiplying with a COPY of the multiplier, so we can reprint
  17515.              the actual multiplier; it hasn't changed. At the end we pack the
  17516.              result and print everything. The order of printout is
  17517.              multiplicand, multiplier, extra byte and result.
  17518.  
  17519.              Notice that we are not passing the parameters for
  17520.              unpacked_multiply on the stack. This is pure whim. A rule for
  17521.              when to pass on the stack is (1) If the subroutine doesn't know
  17522.              where the parameters will be located in memory, you MUST pass
  17523.              them on the stack but (2) if the subroutine knows for certain
  17524.              where the parameters will be, it can fetch them itself.
  17525.  
  17526.              DIVISION
  17527.  
  17528.              Here is the division routine for 19 byte unpacked numbers:
  17529.  
  17530.              ; - - - - - - - -
  17531.              unpacked_divide  proc near
  17532.  
  17533.                     PUSHREGS  ax, bx, cx, si, di
  17534.  
  17535.                     mov   bl, divisor_copy
  17536.                     mov   si, offset dividend + 17  ; start at the top
  17537.                     mov   di, offset quotient + 17
  17538.                     mov   cx, 18              ; 18 numeric bytes
  17539.                     mov   ah, 0               ; clear ah for division
  17540.  
  17541.              div_loop:
  17542.                     mov   al, [si]            ; dividend byte to al
  17543.                     aad                       ; adjust for unpacked number
  17544.                     div   bl                  ; bl is divisor
  17545.                     mov   [di], al            ; move partial quotient
  17546.                     dec   si                  ; decrement pointers
  17547.  
  17548.  
  17549.              The PC Assembler Tutor                                        250
  17550.              ______________________
  17551.  
  17552.                     dec   di
  17553.                     loop  div_loop
  17554.  
  17555.                     mov   remainder, ah       ; final remainder
  17556.  
  17557.                     POPREGS  ax, bx, cx, si, di
  17558.                     ret
  17559.  
  17560.              unpacked_divide  endp
  17561.              ; - - - - -
  17562.  
  17563.              It is pretty simple; it too is a clone of the multiple word
  17564.              division process. Go back to multiple word division if you don't
  17565.              understand it. We keep the previous remainder for the next
  17566.              division. Here is the driver:
  17567.  
  17568.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  17569.              divisor_message db "Enter a number from -9 to +9 (but not 0).",0
  17570.              bcd_in          dt     ?
  17571.              bcd_out         dt     ?
  17572.              quotient_sign   db     ?
  17573.              remainder_sign  db     ?
  17574.              divisor         db     ?
  17575.              divisor_copy    db     ?
  17576.              dividend        db     19 dup (?)
  17577.              quotient        db     19 dup (?)
  17578.              remainder       db     ?
  17579.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  17580.  
  17581.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  17582.              outer_loop:
  17583.                     mov   ax, offset bcd_in
  17584.                     call  get_bcd
  17585.                     call  print_bcd           ; reprint for clarity
  17586.                     lea   cx, dividend
  17587.                     push  cx                  ; unpacked address
  17588.                     push  ax                  ; bcd_in address
  17589.                     call  unpack_bcd
  17590.                     add   sp, 4               ; adjust the stack
  17591.                     mov   al, dividend + 18   ; sign byte
  17592.                     mov   quotient_sign, al   ; either 00h or 80h
  17593.                     mov   remainder_sign, al  ; ditto
  17594.  
  17595.              enter_divisor:
  17596.                     lea   ax, divisor_message
  17597.                     call  print_string
  17598.                     call  get_signed_byte
  17599.                     ; check for valid divisor
  17600.                     cmp   al, 0               ; 0?
  17601.                     je    enter_divisor
  17602.                     cmp   al, 9               ; > +9 ?
  17603.                     jg    enter_divisor
  17604.                     cmp   al, -9              ; < -9?
  17605.                     jl    enter_divisor
  17606.  
  17607.                     ; adjust divisor, quotient sign
  17608.                     mov   divisor, al
  17609.  
  17610.  
  17611.              Chapter 22 - BCD Numbers                                      251
  17612.              ________________________
  17613.  
  17614.                     mov   divisor_copy, al
  17615.                     and   al, 80h             ; sign bit set?
  17616.                     jz    do_the_division
  17617.                     ; negative divisor, so adjust
  17618.                     neg   divisor_copy        ; make positive
  17619.                     xor   quotient_sign, 80h  ; reverse sign of quotient
  17620.  
  17621.              do_the_division:
  17622.                     call  unpacked_divide
  17623.  
  17624.                     mov   al, quotient_sign   ; transfer sign to quotient
  17625.                     mov   quotient + 18, al
  17626.                     test  remainder_sign, 0FFh  ; positive or negative?
  17627.                     jz    pack_the_quotient
  17628.                     neg   remainder             ; make the remainder negative
  17629.  
  17630.              pack_the_quotient:
  17631.                     mov   ax, offset bcd_out  ; bcd number
  17632.                     push  ax
  17633.                     mov   ax, offset quotient ; unpacked number
  17634.                     push  ax
  17635.                     call  pack_bcd
  17636.                     add   sp, 4               ; adjust the stack
  17637.  
  17638.                     ; print dividend, divisor, quotient and remainder
  17639.                     mov   ax, offset bcd_in
  17640.                     call  print_bcd
  17641.                     mov   al, divisor
  17642.                     call  print_signed_byte
  17643.                     mov   ax, offset bcd_out
  17644.                     call  print_bcd
  17645.                     mov   al, remainder
  17646.                     call  print_signed_byte
  17647.  
  17648.                     jmp   outer_loop
  17649.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  17650.  
  17651.              Enter a BCD number, unpack it. Enter a signed number, do range
  17652.              checking, and if it is ok, store it (along with a copy, which we
  17653.              make sure is positive). Calculate the sign of the quotient and
  17654.              remainder. Do the division, then print the dividend, divisor,
  17655.              quotient and remainder. (which have been sign adjusted). The
  17656.              remainder is a signed byte.
  17657.  
  17658.              Like unpacked_multiply, unpacked_divide fetches the parameters
  17659.              directly from memory rather than from the stack.
  17660.  
  17661.  
  17662.              The PC Assembler Tutor                                        252
  17663.              ______________________
  17664.  
  17665.                                     SUMMARY
  17666.  
  17667.  
  17668.  
  17669.              PACKED BCD INSTRUCTIONS
  17670.  
  17671.              DAA (decimal adjust for addition) adjusts AL, assuming that it
  17672.              contains the result of a legitimate packed BCD addition. It
  17673.              treats AL as two independent half-bytes. If the result of the
  17674.              lower half-byte is 10 or over, it subtracts 10 from the lower
  17675.              half-byte and adds the carry to the upper half byte. It then
  17676.              looks at the upper half byte. If its result is 10 or over, DAA
  17677.              subtracts 10 from the upper half byte and sets the carry flag.
  17678.              Otherwise the carry flag is cleared.
  17679.  
  17680.              DAS (decimal adjust for subtraction) adjusts AL, assuming that it
  17681.              contains the result of a legitimate packed BCD subtraction.It
  17682.              treats AL as two independent half-bytes. If the result of the
  17683.              lower half-byte is -1 or less, it adds 10 to the lower half-byte
  17684.              and borrows 1 from the upper half byte. It then looks at the
  17685.              upper half byte. If its result is -1 or less, DAS adds 10 to the
  17686.              upper half byte and sets the carry flag to indicate a borrow.
  17687.              Otherwise the carry flag is cleared.
  17688.  
  17689.  
  17690.              UNPACKED BCD INSTRUCTIONS
  17691.  
  17692.              AAA  (ascii adjust for addition) adjusts AL, assuming that it
  17693.              contains the result of a legitimate unpacked BCD addition. If the
  17694.              lower half-byte has generated a result 10 or over, it subtracts
  17695.              10, carries 1 to AH, and sets the carry flag. If the result is 9
  17696.              or less, it clears CF. In either case it zeroes the upper
  17697.              half-byte of AL.
  17698.  
  17699.              AAD (ascii adjust for division) PREPARES AL and AH for division.
  17700.              It assumes that AH contains the 10's digit and AL contains the
  17701.              1's digit of a two byte unpacked BCD number. It multiplies AH by
  17702.              10 and adds it to AL, thus making a single integer between 0 and
  17703.              99. It zeroes AH in preparation for division.
  17704.  
  17705.              AAM (ascii adjust for multiplication) adjusts AL, assuming that
  17706.              it contains the result of a legitimate BCD multiplication. It
  17707.              divides the result by 10, putting the quotient in AH and the
  17708.              remainder in AL.
  17709.  
  17710.              AAS  (ascii adjust for subtraction) adjusts AL, assuming that it
  17711.              contains the result of a legitimate unpacked BCD subtraction. If
  17712.              the lower half-byte has generated a result -1 or less, it borrows
  17713.              1 from AH, adds 10 to AL, and sets the carry flag. If the result
  17714.              is 0 or more, it clears CF. In either case it zeroes the upper
  17715.              half-byte of AL
  17716. Chapter 23 - XLAT
  17717. =================
  17718.                                                                            253
  17719.  
  17720.  
  17721.  
  17722.              The 800 pound gorilla in the computer field is, of course, IBM.
  17723.              It can go its own way and other companies have to adjust to keep
  17724.              themselves in line with what IBM is doing.
  17725.  
  17726.              You have been using ASCII characters since the first time you
  17727.              used BASIC (or whatever your first high-level language was).
  17728.              Every character has a unique number which represents it.
  17729.  
  17730.                    character    ASCII encoding
  17731.  
  17732.                       A              65d
  17733.                       a              97d
  17734.                       ?              63d
  17735.                       0              48d
  17736.  
  17737.              IBM has its own encoding for mainframe computers. It is called
  17738.              EBCDIC (pronounced ebb'-sih-dick).{1} It is a spinoff of the
  17739.              coding on punch cards. You remember punch cards? This coding is
  17740.              entirely different from ASCII. Here are some examples.
  17741.  
  17742.                    character    ASCII code      EBCDIC code
  17743.  
  17744.                       a               97d           129d
  17745.                       ?               63d           111d
  17746.                       0               48d           240d
  17747.                       H               72d           200d
  17748.                       I               73d           201d
  17749.                       J               74d           209d
  17750.                       K               75d           210d
  17751.  
  17752.              You can see that there is no relationship between the two
  17753.              encodings. Also, notice that while the alphabet is a continuous
  17754.              section of ASCII coding, there are breaks in the EBCDIC code
  17755.              (I=201, J=209).
  17756.  
  17757.              All PCs use ASCII, so if we want to transfer text from a PC to an
  17758.              IBM mainframe computer, we need to change  ASCII -> EBCDIC going
  17759.              to the mainframe and change EBCDIC -> ASCII coming from the
  17760.              mainframe. This is the responsibility of the communications
  17761.              program that runs the modem, so you will never have to do it
  17762.              yourself. Intel has provided an instruction to help the
  17763.              communications program do this translation. It is called XLAT.
  17764.  
  17765.              In order to use XLAT, you need a translation table. This is a 256
  17766.              byte array where each element of the array contains the result
  17767.              you want. Looking at the data above:
  17768.  
  17769.              ____________________
  17770.  
  17771.                 1. Which stands for Extended Binary Coded Decimal Interchange
  17772.              Code.
  17773.  
  17774.              ______________________
  17775.  
  17776.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  17777.  
  17778.  
  17779.              The PC Assembler Tutor                                        254
  17780.              ______________________
  17781.  
  17782.  
  17783.              CHARACTER   ASCII TO EBCDIC TABLE      EBCDIC TO ASCII TABLE
  17784.  
  17785.                  a         array1 [97] = 129          array2 [129] = 97
  17786.                  ?         array1 [63] = 111          array2 [111] = 63
  17787.                  0         array1 [48] = 240          array2 [240] = 48
  17788.                  H         array1 [72] = 200          array2 [200] = 72
  17789.                  I         array1 [73] = 201          array2 [201] = 73
  17790.                  J         array1 [74] = 209          array2 [209] = 74
  17791.                  K         array1 [75] = 210          array2 [210] = 75
  17792.  
  17793.              We have two different tables here. Array1 takes the ASCII
  17794.              encoding and gives back the EBCDIC encoding. Array2 takes the
  17795.              EBCDIC encoding and gives back the ASCII encoding. For each
  17796.              character, the appropriate table gives the correct translation
  17797.              from one encoding to another. All we need now is the translation
  17798.              instruction. Put the address of the translation table in BX. This
  17799.              table should be in the DS segment, but DS may be overriden:
  17800.  
  17801.                  mov  bx, offset ascii_to_ebcdic_table
  17802.  
  17803.              Put the character you want translated in al:
  17804.  
  17805.                  mov  al, character
  17806.  
  17807.              translate:
  17808.  
  17809.                  xlat
  17810.  
  17811.              To translate a 20 byte string of ASCII data into EBCDIC, you
  17812.              might have the following code:
  17813.  
  17814.              ;----------
  17815.                  mov  di, offset ebcdic_string
  17816.                  mov  ax, seg ebcdic_string
  17817.                  mov  es, ax
  17818.  
  17819.                  mov  si, offset ascii_string
  17820.  
  17821.                  mov  bx, offset ascii_to_ebcdic_table
  17822.                  mov  cx, 20                   ; translate 20 bytes
  17823.                  cld                           ; clear DF (increment)
  17824.  
  17825.              translation_loop:
  17826.                  lodsb                         ; ascii to al
  17827.                  xlat                          ; translate
  17828.                  stosb                         ; al to ebcdic
  17829.                  loop translation_loop
  17830.              ; ----------
  17831.  
  17832.              Since this is ASCII to EBCDIC, if AL contained 63 before XLAT,
  17833.              then after XLAT AL would contain 111. If AL contained 73 before
  17834.              XLAT, then after XLAT it would contain 201. If AL contained 97
  17835.              before XLAT, after XLAT it would contain 129.
  17836.  
  17837.              If we wanted to go the other direction we would have to make the
  17838.              EBCDIC string the source string, make the ASCII string the
  17839.  
  17840.  
  17841.              Chapter 23 - Xlat                                             255
  17842.              _________________
  17843.  
  17844.              destination string, and use the other table:
  17845.  
  17846.                  mov  bx, offset ebcdic_to_ascii_table
  17847.  
  17848.              The rest of the code would be the same.
  17849.  
  17850.  
  17851.              Since this is done by the communications program, we won't
  17852.              concern ourselves with ASCII <-> EBCDIC any more, but we will use
  17853.              XLAT in two slightly different ways.
  17854.  
  17855.  
  17856.              First, let's categorize characters. Some things are Whitespace
  17857.              (that is, tabs, newlines, spaces, form feeds, etc.) Some
  17858.              characters are octal, decimal, punctuation, hex, etc. There is a
  17859.              pre-existing table called translation_table in the subdirectory
  17860.              XTRAFILE. Its pathname is \xtrafile\transtbl.obj. It has all 256
  17861.              ascii characters coded in the following way:
  17862.  
  17863.                     WHITESPACE    EQU  80h     ; 1000 0000
  17864.                     PUNCTUATION   EQU  40h     ; 0100 0000
  17865.                     ALPHABETIC    EQU  20h     ; 0010 0000
  17866.                     OCTAL         EQU  10h     ; 0001 0000
  17867.                     DECIMAL       EQU  08h     ; 0000 1000
  17868.                     HEX           EQU  04h     ; 0000 0100
  17869.                     BOX_CHAR      EQU  02h     ; 0000 0010
  17870.                     GREEK_CHAR    EQU  01h     ; 0000 0001
  17871.  
  17872.              If the character is whitespace, then the leftmost bit is set. If
  17873.              it is a greek character (ascii 224 - 239 on the PC) then the
  17874.              rightmost bit is set. If it is more than one thing, then the
  17875.              appropriate bits are set. For instance, '6' is octal, decimal and
  17876.              hex, so it's encoding is:
  17877.  
  17878.                  '6'  0001 1100
  17879.  
  17880.              'a' is both alphabetic and hex, so it's encoding is:
  17881.  
  17882.                  'a'  0010 0100
  17883.  
  17884.              The following program inputs a character, and finds out whether
  17885.              it is punctuation, a letter, etc. If it is none of the eight
  17886.              things, then the program prints that nothing was found. It is the
  17887.              same block of code over and over, so you might want to do only
  17888.              part, or you might want to cut it out with a word processor and
  17889.              insert it in the template file (don't forget to delete the page
  17890.              headers and page numbers).
  17891.  
  17892.  
  17893.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  17894.              EXTRN  translation_table:BYTE  ;\xtrafile\transtbl.obj
  17895.  
  17896.              whitespace_banner   db "It is whitespace." , 0
  17897.              punctuation_banner  db "It is punctuation." , 0
  17898.              alphabet_banner     db "It is alphabetic." , 0
  17899.              octal_banner        db "It is octal." , 0
  17900.              decimal_banner      db "It is decimal." , 0
  17901.  
  17902.  
  17903.              The PC Assembler Tutor                                        256
  17904.              ______________________
  17905.  
  17906.              hex_banner          db "It is hex." , 0
  17907.              drawing_banner      db "It is a box drawing character." , 0
  17908.              greek_banner        db "It is a Greek character." , 0
  17909.              nothing_banner      db "No match was found." , 0
  17910.  
  17911.              dirty_flag          db ?
  17912.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  17913.  
  17914.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  17915.                     WHITESPACE    EQU  80h
  17916.                     PUNCTUATION   EQU  40h
  17917.                     ALPHABETIC    EQU  20h
  17918.                     OCTAL         EQU  10h
  17919.                     DECIMAL       EQU  08h
  17920.                     HEX           EQU  04h
  17921.                     BOX_CHAR      EQU  02h
  17922.                     GREEK_CHAR    EQU  01h
  17923.  
  17924.                     ; set up the xlat table
  17925.                     mov   ax, seg translation_table
  17926.                     mov   es, ax
  17927.                     mov   bx, offset translation_table
  17928.  
  17929.              outer_loop:
  17930.                     mov   dirty_flag, 0       ; marker for success
  17931.                     call get_ascii_byte       ; input a byte to al
  17932.                     xlat es:[bx]              ; do the translation
  17933.  
  17934.                     test  al, WHITESPACE
  17935.                     jz    punct_check
  17936.                     push  ax                  ; save translation in al
  17937.                     mov   ax, offset whitespace_banner
  17938.                     call  print_string
  17939.                     pop   ax
  17940.                     mov   dirty_flag, 1       ; set the dirty flag
  17941.  
  17942.              punct_check:
  17943.                     test  al, PUNCTUATION
  17944.                     jz    alpha_check
  17945.                     push  ax                  ; save translation in al
  17946.                     mov   ax, offset punctuation_banner
  17947.                     call  print_string
  17948.                     pop   ax
  17949.                     mov   dirty_flag, 1       ; set the dirty flag
  17950.  
  17951.              alpha_check:
  17952.                     test  al, ALPHABETIC
  17953.                     jz    octal_check
  17954.                     push  ax                  ; save translation in al
  17955.                     mov   ax, offset alphabet_banner
  17956.                     call  print_string
  17957.                     pop   ax
  17958.                     mov   dirty_flag, 1       ; set the dirty flag
  17959.  
  17960.              octal_check:
  17961.                     test  al, OCTAL
  17962.                     jz    decimal_check
  17963.  
  17964.  
  17965.              Chapter 23 - Xlat                                             257
  17966.              _________________
  17967.  
  17968.                     push  ax                  ; save translation in al
  17969.                     mov   ax, offset octal_banner
  17970.                     call  print_string
  17971.                     pop   ax
  17972.                     mov   dirty_flag, 1       ; set the dirty flag
  17973.  
  17974.              decimal_check:
  17975.                     test  al, DECIMAL
  17976.                     jz    hex_check
  17977.                     push  ax                  ; save translation in al
  17978.                     mov   ax, offset decimal_banner
  17979.                     call  print_string
  17980.                     pop   ax
  17981.                     mov   dirty_flag, 1       ; set the dirty flag
  17982.  
  17983.              hex_check:
  17984.                     test  al, HEX
  17985.                     jz    drawing_check
  17986.                     push  ax                  ; save translation in al
  17987.                     mov   ax, offset hex_banner
  17988.                     call  print_string
  17989.                     pop   ax
  17990.                     mov   dirty_flag, 1       ; set the dirty flag
  17991.  
  17992.              drawing_check:
  17993.                     test  al, BOX_CHAR
  17994.                     jz    greek_check
  17995.                     push  ax                  ; save translation in al
  17996.                     mov   ax, offset drawing_banner
  17997.                     call  print_string
  17998.                     pop   ax
  17999.                     mov   dirty_flag, 1       ; set the dirty flag
  18000.  
  18001.              greek_check:
  18002.                     test  al, GREEK_CHAR
  18003.                     jz    nothing_check
  18004.                     push  ax                  ; save translation in al
  18005.                     mov   ax, offset greek_banner
  18006.                     call  print_string
  18007.                     pop   ax
  18008.                     mov   dirty_flag, 1       ; set the dirty flag
  18009.  
  18010.              nothing_check:
  18011.                     cmp   dirty_flag, 0       ; was anything found?
  18012.                     je    print_nothing_banner
  18013.                     jmp   outer_loop
  18014.              print_nothing_banner:
  18015.                     mov   ax, offset nothing_banner
  18016.                     call  print_string
  18017.                     jmp   outer_loop
  18018.  
  18019.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  18020.  
  18021.              you need to:
  18022.  
  18023.                  link prog1+transtbl+\asmhelp ;
  18024.  
  18025.  
  18026.  
  18027.              The PC Assembler Tutor                                        258
  18028.              ______________________
  18029.  
  18030.              The program is long, but straightforward. Input a character and
  18031.              get its encoding. Test for each characteristic. If it is found,
  18032.              print the appropriate message and set the dirty_flag to indicate
  18033.              something was printed. At the end, if nothing was printed, print
  18034.              the failure message.
  18035.  
  18036.              Notice that the translation table is in ES and we are using a
  18037.              segment override for it. If you look at the EXTRN statement for
  18038.              'translation_table', you will see that even though we are using
  18039.              ES, it is declared EXTRN in a segment with an:
  18040.  
  18041.                  ASSUME ds:DATASTUFF
  18042.  
  18043.              statement. How can we get away with this? The assembler never
  18044.              deals with 'translation table' directly. The only thing it does
  18045.              is put the offset in BX. We put the segment override in ourselves
  18046.              with:
  18047.  
  18048.                  xlat es:[bx]
  18049.  
  18050.              so the assembler never has to decide whether a segment override
  18051.              is necessary or which segment override to use.
  18052.  
  18053.  
  18054.              WORD SEARCH
  18055.  
  18056.              When doing the mock word search program in the chapter on string
  18057.              instructions, I mentioned that it really wouldn't cut the mustard
  18058.              when it comes to real word searches. Why?  If we are looking for
  18059.              "when" we also want to find "When". If we are looking for
  18060.              " searches ", we also want to find " searches,", that is,
  18061.              punctuation should not interefere unless we want it to, and
  18062.              capitals should not interefere unless we want them to. With the
  18063.              aid of a translation table, we will make a word search program
  18064.              which uses the following rules. In the SEARCH string (the string
  18065.              that defines what you are looking for):
  18066.  
  18067.                  (1) Any small letter will match either a small or large
  18068.                      letter.
  18069.                  (2) A capital letter will match only a capital letter.
  18070.                  (3) A blank will match any whitespace or punctuation.
  18071.                  (4) A punctuation mark will only match itself.
  18072.  
  18073.              With these rules "Why" must start with a capital 'W' to be a
  18074.              match, but 'h' and 'y' may be either capital or small. " some,"
  18075.              may have any whitespace (including a carriage return) in front,
  18076.              but must hava a comma ',' at the end.
  18077.  
  18078.              This program has two data files. \XTRAFILE\SRCHTBL.OBJ contains
  18079.              the translation table. It is called "wordsearch_table" and is in
  18080.              DATASTUFF, so will be in our normal DS segment. In order to have
  18081.              text to search I have included an object file that is the text of
  18082.              a chapter from a book. (The object file text includes carriage
  18083.              returns). The text is a C string - it is terminated by a 0.
  18084.  
  18085.              The book was written by C.D. Huffam, and is the autobiographical
  18086.              account of his dual life as a writer and lecturer. The book is
  18087.  
  18088.  
  18089.              Chapter 23 - Xlat                                             259
  18090.              _________________
  18091.  
  18092.              called "A Tale of Two C.D.s". The object file with the text is
  18093.              \XTRAFILE\TWOTALE.OBJ. It is in a private segment and will use ES
  18094.              as a segment register. There is also a straight text file which
  18095.              you can print out so you can see what is in the object file. It
  18096.              is \XTRAFILE\TWOTALE.DOC.
  18097.  
  18098.              Here's the program. The explaination is at the end.
  18099.  
  18100.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
  18101.              EXTRN  tale_text:BYTE, wordsearch_table:BYTE
  18102.  
  18103.              entry_message    db   13,10, "Enter a word for a word search", 0
  18104.              no_match_message db   "There was no match", 0
  18105.              input_buffer     db   80 dup (?)
  18106.              text_file_length dw   ?
  18107.              letter_count     dw   ?
  18108.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE
  18109.  
  18110.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
  18111.  
  18112.                     ; find the length of the text file
  18113.                     mov   ax, seg tale_text      ; load es register
  18114.                     mov   es, ax
  18115.  
  18116.                     mov   di, offset tale_text   ; offset to di
  18117.                     mov   bx, di                 ; copy to bx
  18118.                     mov   al, 0                  ; try to match zero
  18119.                     cld                          ; clear DF (increment)
  18120.  
  18121.              string_end_loop:
  18122.                     scasb                        ; search for zero
  18123.                     jne   string_end_loop
  18124.  
  18125.                     dec   di                  ; one too many , so decrement
  18126.                     sub   di, bx                   ; finish - start = length
  18127.                     mov   text_file_length, di     ; length of text_file
  18128.  
  18129.  
  18130.              big_loop:
  18131.                     ; get a word for the word search
  18132.                     mov   ax, offset entry_message
  18133.                     call  print_string
  18134.                     mov   ax, offset input_buffer
  18135.                     call  get_string
  18136.  
  18137.                     ; find the end of string
  18138.                     mov   al, 0               ; compare with 0
  18139.                     mov   bx, offset input_buffer
  18140.                     mov   cx, 0               ; letter count
  18141.              letter_count_loop:
  18142.                     cmp   al, [bx]            ; compare to 0
  18143.                     je    end_of_count_loop
  18144.                     inc   cx                  ; increment count
  18145.                     inc   bx                  ; increment pointer
  18146.                     jmp   letter_count_loop
  18147.              end_of_count_loop:
  18148.                     cmp   cx, 0               ; if 0, string is empty
  18149.  
  18150.  
  18151.              The PC Assembler Tutor                                        260
  18152.              ______________________
  18153.  
  18154.                     je    big_loop            ; so start again
  18155.                     mov   letter_count, cx
  18156.  
  18157.                     ; look for word match. In this program, the text string
  18158.                     ; is referenced by si and the search string is referenced
  18159.                     ; by di.
  18160.  
  18161.                     mov   si, offset tale_text
  18162.                     mov   cx, text_file_length    ; length of file
  18163.                     sub   cx, letter_count        ; last possible match
  18164.                     inc   cx                      ; +1 for boundary condition
  18165.  
  18166.                     ; set up translation table ( it is in DATASTUFF )
  18167.                     mov   bx, offset wordsearch_table
  18168.  
  18169.  
  18170.              word_search_loop:
  18171.                     push  si                  ; save a copy
  18172.                     push  cx                  ; save a copy
  18173.                     mov   di, offset input_buffer
  18174.                     mov   cx, letter_count
  18175.  
  18176.  
  18177.              letter_loop:
  18178.                     mov   al, es:[si]         ; text to al
  18179.                     cmp   al, [di]            ; same as search string?
  18180.                     je    next_letter
  18181.                     xlat                      ; if not, translate
  18182.                     cmp   al, [di]            ; allowable substitute?
  18183.                     jne   new_start           ; if not, start at new place
  18184.              next_letter:
  18185.                     inc   di                  ; move to next letter
  18186.                     inc   si
  18187.                     loop  letter_loop
  18188.  
  18189.                     ; we fell through, so we found a complete match
  18190.                     jmp    found_it
  18191.  
  18192.                     ; no match. are we finished?
  18193.              new_start:
  18194.                     pop   cx
  18195.                     pop   si
  18196.                     inc   si                  ; move to next character
  18197.                     loop  word_search_loop
  18198.  
  18199.                     ; we fell through. finished, but no match
  18200.                     mov   ax, offset no_match_message
  18201.                     call  print_string
  18202.                     jmp   big_loop
  18203.  
  18204.              found_it:
  18205.                     pop   cx                  ; take cx off the stack
  18206.                     pop   si                  ; start of the match
  18207.  
  18208.                     ; move 25 characters to buffer for printing
  18209.                     mov   di, offset input_buffer
  18210.                     mov   cx, 25
  18211.  
  18212.  
  18213.              Chapter 23 - Xlat                                             261
  18214.              _________________
  18215.  
  18216.              character_move:
  18217.                     mov   al, es:[si]
  18218.                     mov   [di], al
  18219.                     inc   si                  ; increment pointers
  18220.                     inc   di
  18221.                     loop  character_move
  18222.  
  18223.                     mov   BYTE PTR [di], 0    ; end of string
  18224.                     mov   ax, offset input_buffer
  18225.                     call  print_string
  18226.                     jmp   big_loop
  18227.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  18228.  
  18229.              You need to:
  18230.  
  18231.                  link prog2+twotale+srchtbl+\asmhelp ;
  18232.  
  18233.              to get asmhelp and the two data files in the program.
  18234.  
  18235.              This program is very similar to the search program in the chapter
  18236.              on strings. However, because of where the files are, the pointers
  18237.              have been changed around. Therefore, it is safer if you simply
  18238.              cut out the program with a word processor and paste it into the
  18239.              template file rather than try to modify the prevoius search
  18240.              program.{2}
  18241.  
  18242.              It is assumed that you did the string match program. The logic is
  18243.              the same and will not be covered again. First we input a search
  18244.              string. Then starting at the beginning of the text to be search
  18245.              we check till we find the first match. If we find a match, we
  18246.              print out 25 characters starting with the first character of the
  18247.              match. If no match is found, a message to that effect is printed.
  18248.  
  18249.              The character match is a two step process. The character from the
  18250.              text is put in AL. It is compared with the search character for
  18251.              an EXACT match. If they match, we are done. If not, we use XLAT
  18252.              on AL (the character from the text) which will translate to its
  18253.              allowable substitute. In fact, all this is just: (1) all capital
  18254.              letters become small, (2) all punctuation becomes spaces, and (3)
  18255.              all whitespace becomes spaces. Once again, we compare AL with the
  18256.              search character. If we have a match, ok. If not, we start over.
  18257.  
  18258.              The text is in ES, the translation table is in DS, so it is
  18259.              inconvenient to use the string instructions in this program.
  18260.  
  18261.              Try to match a word at the beginning of the line, end of the
  18262.              line, with and without punctuation and with and without capitals.
  18263.              If you go across a line break, you need to substitute two blanks
  18264.              in the search string for CRLF (13,10).
  18265.  
  18266.  
  18267.              ____________________
  18268.  
  18269.                 2. You should understand what is going on in the code before
  18270.              you run these programs. I didn't write the code for myself, I
  18271.              wrote it for you. If you run it but don't understand it, it won't
  18272.              help you a bit.
  18273.  
  18274.  
  18275.              The PC Assembler Tutor                                        262
  18276.              ______________________
  18277.  
  18278.              Suppose you are not interested in all 256 values of the
  18279.              translation table. Let's say that you only want to have a
  18280.              translation table for the numbers from 0 to 99. Can you still use
  18281.              this? Yes, but you need to put in some range checking to make
  18282.              sure that you have valid data.
  18283.  
  18284.                  MAX_VALUE EQU 99
  18285.  
  18286.                  mov  al, data_byte       ; byte to al
  18287.                  cmp  al, MAX_VALUE       ; too large?
  18288.                  ja   data_error          ; report error
  18289.                  xlat
  18290.  
  18291.              This insures that any data that is out of range is not
  18292.              translated. Therefore the translation table only needs to be 100
  18293.              bytes long (0 - 99).
  18294.  
  18295.              If you want more than 256 elements in the translation table you
  18296.              need to use words, not bytes, and you cannot use XLAT. You can
  18297.              make your own code to do the same thing.
  18298.  
  18299.                  MAX_VALUE EQU 999
  18300.                  my_translation_table     dw   1000 dup (?)
  18301.  
  18302.              if you put the translation data into the table, you can then have
  18303.              the following code:
  18304.  
  18305.                  mov  bx, offset my_translation_table
  18306.  
  18307.                  ; - - - - - translation block
  18308.                  mov  si, data_word       ; word to si
  18309.                  cmp  si, MAX_VALUE       ; too large?
  18310.                  ja   data_error
  18311.                  shl  si, 1         ; SI x 2 = number of BYTES into table
  18312.                  mov  ax, [bx+si]         ; base + offset
  18313.                  ; - - - - - end of translation block
  18314.  
  18315.              XLAT is about twice as fast as this last code, so when you have a
  18316.              choice always use XLAT.
  18317.  
  18318.  
  18319.              Chapter 23 - Xlat                                             263
  18320.              _________________
  18321.  
  18322.                                           SUMMARY
  18323.  
  18324.  
  18325.              XLAT
  18326.  
  18327.                  BX holds the address of a 256 byte array called a
  18328.                  translation table. AL holds the character to be translated.
  18329.                  If x is the value in AL before XLAT, then after XLAT,
  18330.                  AL=array[x].
  18331. Chapter 24 - Miscellaneous Instructions
  18332. =======================================
  18333.                                                                            264
  18334.  
  18335.  
  18336.  
  18337.              There are a few more assembler instructions which have not been
  18338.              covered. Some are seldom used and some are used in special
  18339.              circumstances. This chapter gives a brief explanation of them.
  18340.  
  18341.  
  18342.              XCHG
  18343.  
  18344.              XCHG, the exchange instruction, switches the contents of two
  18345.              registers or of a register and a memory location.
  18346.  
  18347.                  xchg ax, bx
  18348.                  xchg al, dl
  18349.                  xchg variable1, si
  18350.                  xchg ch, variable2
  18351.                  xchg [si], ax
  18352.                  xchg bh, [di]
  18353.  
  18354.              It operates on either a word or a byte. It cannot switch two
  18355.              memory locations, and it does not operate on the segment
  18356.              registers, only on the 8 arithmetic registers.
  18357.  
  18358.  
  18359.              ESC
  18360.  
  18361.              The escape instruction is not a complete instruction, it is the
  18362.              beginning of an instruction. It signals the 8086 that the first
  18363.              two bytes of the instruction contain a co-processor instruction.
  18364.              The 8087 is a mathematics co-processor. It reads the instructions
  18365.              at the same time as the 8086, and when it sees an escape
  18366.              instruction meant for it, it performs that operation. The 8086
  18367.              does nothing unless there is a memory address involved. In that
  18368.              case the 8086 calculates the address and gives the address to the
  18369.              8087.
  18370.  
  18371.                  fld  DWORD PTR [si]
  18372.                  fmul st, st(3)
  18373.                  fisub WORD PTR [di]
  18374.  
  18375.              are all 8087 instructions that the assembler codes with the
  18376.              escape code. You will never code the escape instruction yourself.
  18377.  
  18378.  
  18379.              WAIT (FWAIT)
  18380.  
  18381.              The 8087 operates independently of the 8086. They can both
  18382.              perform operations at the same time, but it is possible for them
  18383.              to interfere with each other. If both the 8086 and 8087 are
  18384.              reading from or writing to memory, or if one is reading from
  18385.              while the other is writing to memory, the read/write will be
  18386.              corrupted. In order to stop this, whenever you access memory with
  18387.              the 8087, you need to put in a WAIT instruction. WAIT (or FWAIT
  18388.  
  18389.              ______________________
  18390.  
  18391.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  18392.  
  18393.  
  18394.              Chapter 24 - Miscellaneous Instructions                       265
  18395.              _______________________________________
  18396.  
  18397.              which is the same thing), suspends operation of the 8086 until
  18398.              the co-processor is finished. It would look like this:
  18399.  
  18400.                  fstp DWORD PTR [bx]
  18401.                  fwait
  18402.  
  18403.              The 8086 will wait until the 8087 is finished storing into
  18404.              memory.
  18405.  
  18406.              There must also be a wait between 8087 instructions. This is to
  18407.              keep the 8087 from starting a second instruction before it is
  18408.              finished with the first one. The instruction execution is done by
  18409.              the 8087, but the timing is done by the 8086. If you had the
  18410.              folllowing code:
  18411.  
  18412.                  fmul st, st(3)
  18413.                  fadd st, st(2)
  18414.                  fsub st, st(4)
  18415.  
  18416.              the 8086 would be past the third instruction before the 8087 had
  18417.              time to finish doing the first instruction. The proper coding is:
  18418.  
  18419.                  fmul st, st(3)
  18420.                  fwait
  18421.                  fadd st, st(2)
  18422.                  fwait
  18423.                  fsub st, st(4)
  18424.  
  18425.              This should concern you only if you program the 8087. It is
  18426.              outside the realm of this book. Remember, WAIT and FWAIT are the
  18427.              same instruction.
  18428.  
  18429.  
  18430.              LOCK
  18431.  
  18432.              LOCK is not really of concern to a PC programmer. On some systems
  18433.              it is possible to have more than one 8086 that has access to the
  18434.              same memory. The problem then arises as it did with the 8087 that
  18435.              there is the possibility of two 8086s doing read/write operations
  18436.              to memory at the same time. This will result in corrupted data.
  18437.              LOCK allows an 8086 to lock out other 8086s. It will be the only
  18438.              one allowed to read to or write to memory during the next
  18439.              instruction. This is mostly of concern to people who write code
  18440.              for peripheral devices which have DMA (direct memory access).
  18441.  
  18442.  
  18443.              LOOPE/LOOPZ  LOOPNE/LOOPNZ
  18444.  
  18445.              We have used the loop instruction, but these are special cases.
  18446.              Remember, the general loop instruction decrements CX by 1, and if
  18447.              the result is not zero, it jumps to the top of the loop. In
  18448.              special circumstances, you not only want to check the counter in
  18449.              CX, but you also want to check the result of some other
  18450.              operation.
  18451.  
  18452.                  test  ax, 5
  18453.                  loope outer_loop
  18454.  
  18455.  
  18456.              The PC Assembler Tutor                                        266
  18457.              ______________________
  18458.  
  18459.  
  18460.              will loop if cx is not zero AND ax = 5
  18461.  
  18462.                  test   ax, 5
  18463.                  loopne outer_loop
  18464.  
  18465.              will loop if cx is not zero AND ax is not 5.
  18466.  
  18467.  
  18468.              HALT
  18469.  
  18470.              HALT actually halts the operation of the 8086. It can only be
  18471.              started again by a reset or an interrupt. If you write:
  18472.  
  18473.                  cli       ; clear interrupt flag
  18474.                  halt
  18475.  
  18476.              Then normal interrupts can't happen and you have effectively shut
  18477.              down the system. One place this might be of use is if you have a
  18478.              copy protection scheme and you detect that someone has violated
  18479.              it. It halts the system and you need to reset to start again.
  18480.              Another place might be if you are writing a game -  someone
  18481.              inadvertently enters the "Dungeon of Darkness" and you shut the
  18482.              system down.
  18483.  
  18484.  
  18485.              CMC
  18486.  
  18487.              CMC (complement the carry flag) toggles the carry flag. If it is
  18488.              off this turns it on, and if it is on, this turns it off. Why
  18489.              this instruction exists is a complete mystery to me. Why would
  18490.              you want it as part of the instruction set?
  18491.  
  18492.  
  18493.              LAHF
  18494.  
  18495.              LAHF (load AH from flags) stores half of the flags in AH. The
  18496.              register looks like this:
  18497.  
  18498.                  7 6 5 4 3 2 1 0
  18499.                  S Z   A   P   C
  18500.  
  18501.              Where these are the Sign, Zero, Auxillary, Parity and Carry
  18502.              flags. This is a leftover from earlier Intel chips. All these
  18503.              flags are testable so you dont need to look at them, and if you
  18504.              want to save them, then:
  18505.  
  18506.                  pushf
  18507.  
  18508.              does the trick. Notice that AH does not contain DF, the direction
  18509.              flag or IEF, the interrupt enable flag, which are things you
  18510.              CAN'T test with a jump instruction. To test for them you need to:
  18511.  
  18512.                  pushf
  18513.                  pop  ax
  18514.  
  18515.              Then AX contains all the flags.
  18516.  
  18517.  
  18518.              Chapter 24 - Miscellaneous Instructions                       267
  18519.              _______________________________________
  18520.  
  18521.  
  18522.  
  18523.              SAHF
  18524.  
  18525.              SAHF (store AH to flags) is the counterpart to the above
  18526.              instruction. It puts AH into the low half of the flags register.
  18527.              The comments about LAHF apply to this instruction also.
  18528.  
  18529.  
  18530.  
  18531.              NOP
  18532.  
  18533.              And finally, NOP does absolutely nothing. It is there in case you
  18534.              need to fill space, either because you have taken out an
  18535.              instruction or for reasons of alignment. It also allows a
  18536.              debugger to put the single byte INT (int 3) in the code and then
  18537.              replace it with NOP when the breakpoint is no longer desired.
  18538. Chapter 25 - What Does it all mean?
  18539. ===================================
  18540.                                                                            268
  18541.  
  18542.  
  18543.  
  18544.              What does it all mean? Not a whole lot, actually. We can now save
  18545.              memory space and we can save a lot of time. But with the
  18546.              incessant march of technology these things mean less and less. A
  18547.              few years ago, when 64k or 128k was a lot of memory and memory
  18548.              was expensive, having a 20k program instead of a 40k program was
  18549.              a significant advantage. Now it means almost nothing unless it is
  18550.              a memory resident program. What about disk space? Just a while
  18551.              back we were operating with two 360k floppy disks and a hard disk
  18552.              was too expensive. Nowdays everyone has a 20meg hard disk.{1} And
  18553.              speed? Those programs that were slow on an 8088 now seem o.k. on
  18554.              an 80386. Those programs that were unbearably slow on the 8088
  18555.              died a quick death and are no longer around.
  18556.  
  18557.              Compilers are better and they have more subroutines available.
  18558.              They are also easier to program than going to the assembler
  18559.              level. What this chapter is about is when NOT to use the standard
  18560.              compiler functions and subroutines.
  18561.  
  18562.              First, you should understand that all compiler subroutines are
  18563.              general purpose subroutines. They need to be all things to all
  18564.              people. Imagine what a vehicle would be like if we gave the
  18565.              designer the following specification:
  18566.  
  18567.                  We want to be able to drive to the store for groceries. It
  18568.                  should be fuel efficient. In case we want to go into the
  18569.                  mountains it should be an all terrain vehicle. We also want
  18570.                  to be able to haul a roomful of furniture from coast to
  18571.                  coast. Oh yes, and we want to be able to race it at Le Mans.
  18572.  
  18573.              Being universal requires a lot of code and it slows things down.
  18574.              Whether this extra code and time is too much is a question you
  18575.              need to decide for yourself. First, here are some examples of
  18576.              size. This is a C program that does almost nothing:
  18577.  
  18578.                  #include <stdio.h>
  18579.                  main()
  18580.                  {
  18581.                          int     x ;
  18582.                          x = 27 ;                        /* line 1 */
  18583.                          scanf  ( "%d", &x ) ;           /* line 2 */
  18584.                          printf ( "%d\n", x ) ;          /* line 3 */
  18585.                  }
  18586.              ____________________
  18587.  
  18588.                 1. Which has led to one of my pet peeves. All installation
  18589.              programs for compilers and word processors dump EVERYTHING on the
  18590.              hard disk. This gives us subdirectories that have 50 files in
  18591.              them, and we don't have the foggiest notion of what any of the
  18592.              files are for. If these installation programs would only prompt
  18593.              us by type of file to find out what we want to install and want
  18594.              to leave off the hard disk, we would all be better off.
  18595.  
  18596.              ______________________
  18597.  
  18598.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  18599.  
  18600.  
  18601.              Chapter 25 - What Does It All Mean?                           269
  18602.              ___________________________________
  18603.  
  18604.  
  18605.              I have made 3 programs from this. LINE1.C has line1 only. LINE2.C
  18606.              has lines 1 and 2. LINE3.C has lines 1, 2 and 3. For those non C
  18607.              people, scanf is an input function, printf is an output function.
  18608.              Guess how big each program is. Here's the directory listing.
  18609.  
  18610.                  LINE1    EXE     3176   6-22-90   8:48a
  18611.                  LINE2    EXE     7170   6-22-90   8:49a
  18612.                  LINE3    EXE     9134   6-22-90   8:49a
  18613.  
  18614.              It takes 3000 bytes to start a C program (this is the startup
  18615.              module) 4000 bytes more to enter something and 2000 bytes extra
  18616.              to print something. That first 3000 bytes is unavoidable if you
  18617.              are writing in a high level language. If you are doing a lot of
  18618.              general purpose i/o, these extra amounts aren't too bad. There
  18619.              are two cases where you might want to use your own i/o routines.
  18620.              First, if you have something simple or secondly, if you have
  18621.              something special, you want to do your own i/o.
  18622.  
  18623.              If you don't need all that flexibility, you are better off doing
  18624.              your own i/o. Here are two files that write a text screen to a
  18625.              disk file.
  18626.  
  18627.                  COPYSCRN EXE    10454   6-10-90   9:30a
  18628.                  INTSCRN  COM      445   6-12-90   7:40p
  18629.  
  18630.              They both do the same thing except that the .COM file is a little
  18631.              more sophisticated. Notice the difference in size. Speed really
  18632.              doesn't play a part here because what they do is so simple that
  18633.              it takes just a second in any case. The program was so simple
  18634.              that it only took an hour or two to write, so I didn't lose any
  18635.              time by writing it in assembler.
  18636.  
  18637.              The other case is when you have a specific idea of what the
  18638.              screen should look like. You want control of the whole screen all
  18639.              the time. This includes all word processors, databases,
  18640.              programming environments, etc. They all take charge of the screen
  18641.              because some DOS functions are too slow. If you remember from the
  18642.              ZOOM chapter, there is a radical difference between what you can
  18643.              do and what DOS can do. Even though these large programs are
  18644.              written in C, they all bypass the C i/o functions. That does not
  18645.              mean that they go down to the assembler level, however.
  18646.  
  18647.  
  18648.              INTERRUPTS
  18649.  
  18650.              You have done a few interrupts. They call the standard DOS or
  18651.              BIOS functions. Remember, they do this by going into low memory
  18652.              (the first 1k of memory) and getting the address of the
  18653.              subprogram that handles that particular interrupt. However, you
  18654.              do not need to call these interrupts from the assembler level.
  18655.              All modern compilers support interrupt calls in the language. If
  18656.              yours doesn't, you need a more recent compiler. Before going on
  18657.              with this chapter you need to read your compiler documentation
  18658.              about interrupts. TURBO Pascal has INTR, QuickBASIC and QuickC
  18659.              have INT86.  Read the documentation now.
  18660.  
  18661.  
  18662.  
  18663.              The PC Assembler Tutor                                        270
  18664.              ______________________
  18665.  
  18666.              Have you read it? No cheating is allowed, because you won't
  18667.              understand the rest of this if you haven't read it.
  18668.  
  18669.              Though technically C is a structure and Pascal is a record, they
  18670.              are actually arrays where each array element has a specific name.
  18671.              The interrupt routine reads all these values into the
  18672.              corresponding register, calls the interrupt, then reads the
  18673.              register values back into the array. Some languages have one
  18674.              array for the input and another for the output. Int 21h is
  18675.              special so QuickC has a special function called INTDOS. It is the
  18676.              same as using Int 21h.
  18677.  
  18678.              The order of registers in the array is arbitrary and language
  18679.              dependent. For TurboPascal it is (AX, BX, CX, DX, BP, SI, DI, DS,
  18680.              ES, FLAGS). You enter values in the registers specified by the
  18681.              interrupt, and then call the interrupt. The routine does the
  18682.              rest.
  18683.  
  18684.                  INTR ( int_no: byte, var the_regs: Registers)
  18685.  
  18686.              This will push the interrupt number, then the array address. On
  18687.              entry to the interrupt call and after initializing BP, we will
  18688.              have:
  18689.                       int_no              bp + 6
  18690.                       array_address       bp + 4
  18691.                       old IP              bp + 2
  18692.                 bp -> old BP              bp
  18693.  
  18694.              What follows is not the exact code, but is similar to what the
  18695.              Pascal routine does:
  18696.  
  18697.                  ; - - - - - - - - - -
  18698.                  intr proc near
  18699.  
  18700.                       push bp
  18701.                       mov  bp, sp
  18702.                       push ax   ; save all registers except SP, DS, SS, CS
  18703.                       push bx
  18704.                       push cx
  18705.                       push dx
  18706.                       push si
  18707.                       push di
  18708.                       push bp   ; this is OUR bp
  18709.                       push es
  18710.  
  18711.                       ; insert the interrupt number in the interrupt
  18712.                       mov  al, [bp+6]  ; AL now contains the interrupt number
  18713.                       lea  si, interrupt_spot  ; where the interrupt is
  18714.                       mov  cs:[si+1], al       ; insert it in the interrupt
  18715.  
  18716.                       ; change all the registers
  18717.                       mov  si, [bp+4]     ; array address is DS:SI
  18718.                       mov  ax, [si]
  18719.                       mov  bx, [si+2]
  18720.                       mov  cx, [si+4]
  18721.                       mov  dx, [si+6]
  18722.                       mov  bp, [si+8]
  18723.  
  18724.  
  18725.              Chapter 25 - What Does It All Mean?                           271
  18726.              ___________________________________
  18727.  
  18728.                       mov  di, [si+12]
  18729.                       mov  es, [si+16]
  18730.  
  18731.                       ; special manipulation for DS and SI
  18732.                       push ds             ; save ds
  18733.                       push si             ; save si
  18734.                       push ax             ; temp save of ax from array
  18735.                       mov  ax, [si+14]         ; ds from array to ax
  18736.                       mov  si, [si+10]         ; si from array to si
  18737.                       mov  ds, ax              ; now move ax to ds
  18738.                       pop  ax                  ; restore ax
  18739.  
  18740.                       ; call the interrupt
  18741.              interupt_spot:
  18742.                       int  0         ; dummy number for the interrupt
  18743.  
  18744.                       ; special needs for SI and DS
  18745.                       ; our SI and DS are at the top of the stack
  18746.                       ; save values of flags, si and ds from interrupt
  18747.                       pushf          ; value from interrupt
  18748.                       push si        ; value from interrupt
  18749.                       push ds        ; value from interrupt
  18750.                       add  sp, 6     ; get to our si and ds
  18751.                       pop  si        ; our si
  18752.                       pop  ds        ; our ds
  18753.                       sub  sp, 10    ; sp is where it was a moment ago.
  18754.                       mov  [si], ax  ; DS:SI points to array
  18755.                       mov  [si+2], bx
  18756.                       mov  [si+4], cx
  18757.                       mov  [si+6], dx
  18758.                       mov  [si+8], bp
  18759.                       mov  [si+12], di
  18760.                       mov  [si+16], es
  18761.                       pop  [si+14]   ; ds from the interrupt
  18762.                       pop  [si+10]   ; si from the interrupt
  18763.                       pop  [si+18]   ; flags from the interrupt
  18764.                       add  sp, 4     ; skip our DS and SI (already in regs)
  18765.  
  18766.                       pop  es
  18767.                       pop  bp        ; this is OUR bp
  18768.                       pop  di
  18769.                       pop  si
  18770.                       pop  dx
  18771.                       pop  cx
  18772.                       pop  bx
  18773.                       pop  ax
  18774.  
  18775.                       mov  sp, bp
  18776.                       pop  bp
  18777.  
  18778.                       ret (4)        ; clear arguments off the stack
  18779.                       ; - - - - - - - - - - - - - - - - - - - -
  18780.  
  18781.              This should test your insight into using code. DS and SI are
  18782.              needed for moving data, so we use some kludges to get it to work.
  18783.  
  18784.              There are two things here that you shouldn't normally do. First,
  18785.  
  18786.  
  18787.              The PC Assembler Tutor                                        272
  18788.              ______________________
  18789.  
  18790.              we are inserting the interrupt number directly in the machine
  18791.              code. Secondly, we are playing around with the value of SP. These
  18792.              are rare exceptions and shouldn't occur in your own code unless
  18793.              absolutely necessary.
  18794.  
  18795.              The first thing you are going to say is, "Gosh, that's a lot of
  18796.              code for one interrupt." True, especially when the interrupt is
  18797.              interrupt 12h. Here's int 12h inside of our template file:
  18798.  
  18799.                  ; - - - - - START CODE HERE
  18800.                       int  12h       ; machine memory  (return in ax)
  18801.                       call print unsigned
  18802.                  ; - - - - - END CODE HERE
  18803.  
  18804.              It finds out how much memory your computer has and returns the
  18805.              number of kbytes in AX. But how much extra time does using this
  18806.              Pascal interrupt routine take? About 700 clocks or about .0002
  18807.              seconds (that's right, 2 ten thousandths) on the slowest machine.
  18808.              How many times will you call it during a program? Only one time.
  18809.              There is no point in going down to the assembler level to write a
  18810.              program that saves you .0002 seconds. In Pascal, you would write:
  18811.  
  18812.                  INTR ( $12 , the_regs) ;
  18813.  
  18814.              and be done with it. No big loss of time and no trouble at all.
  18815.  
  18816.  
  18817.              In fact, as far as I can see, there is no reason for doing any
  18818.              interrupts from the assembler level. You may want to do a whole
  18819.              subprogram that contains interrupts, but if you just need one or
  18820.              two interrupts, it is easier to work from inside the high-level
  18821.              language.
  18822.  
  18823.              This includes the i/o we were talking about a minute ago. Yoy can
  18824.              write a screen program inside a high level language using arrays.
  18825.              Just think of a screen as a 80X25 array. If a two dimensional
  18826.              array is too slow you need to go to a one dimensional array. All
  18827.              interrupts that tell what kind of video card is in the computer,
  18828.              what mode the screen is in, etc. can be done from the high-level
  18829.              language. The most you need assembler for (depending on the
  18830.              language) is moving the text array into video memory. You want a
  18831.              bunch of help screens? Put all the help screens in a single file
  18832.              and use the interrupt for random access file read to read a
  18833.              screen when you need it.{2}
  18834.  
  18835.              Anything else?  Yes, we still have the need for speed. There are
  18836.              certain types of operations like block moves of data, word
  18837.              searches and sorting of arrays that are characterized by large
  18838.              amounts of data and/or large amounts of computation. If you think
  18839.              you see a way to use registers effectively for one of these
  18840.              ____________________
  18841.  
  18842.                 2. What you actually want to do is have the first block of
  18843.              data in the file tell you where each screen is and how long its
  18844.              data is. Then the first 2 bytes or words of the screen data
  18845.              should say the dimensions of the screen data ( 12 X 25, 17 X 3,
  18846.              etc.). This will allow you to store and use screens of any size.
  18847.  
  18848.  
  18849.              Chapter 25 - What Does It All Mean?                           273
  18850.              ___________________________________
  18851.  
  18852.              things, you probably can beat a compiled version of the
  18853.              subprogram. Then the only question is whether or not it is worth
  18854.              the trouble.
  18855.  
  18856.              We have used the words "fast" and "slow" ambiguously so far, but
  18857.              now it is time to quantify them. Before you get the numbers, you
  18858.              need to know one thing about memory. People always talk about the
  18859.              "data bus". What is it? It is a group of wires connecting the
  18860.              80x86 chip to memory. The 8088 has 8 wires, the 8086, 80286 and
  18861.              80386/SX have 16 wires, and the 80386 has 32 wires. That means
  18862.              that the 8088 can transfer 8 bits of information at one time, the
  18863.              8086 et. al. can transfer 16 bits at a time and the 80386 can
  18864.              transfer 32 bits at a time. This means one byte, two byte and
  18865.              four byte transfers respectively. This also means that the memory
  18866.              bytes are ordered a little differently. You will never notice it
  18867.              externally, but here is the different internal ordering.
  18868.  
  18869.              The 8088 has all bytes one after the other. All memory
  18870.              read/writes are done with the same 8 wires:
  18871.  
  18872.                            8088
  18873.                      MEMORY ADDRESSES
  18874.  
  18875.                            00005
  18876.                            00004
  18877.                            00003
  18878.                            00002
  18879.                            00001
  18880.                            00000
  18881.              data lines   ||||||||   (8 bits)
  18882.  
  18883.              (All our examples will use absolute memory locations starting at
  18884.              00000). The chips with a 16 bit data bus have all the even
  18885.              locations on the first 8 wires and the odd locations on the other
  18886.              8 wires. They come in pairs - first even then odd:
  18887.  
  18888.                                 8086
  18889.                           MEMORY ADDRESSES
  18890.  
  18891.                            00006     00007
  18892.                            00004     00005
  18893.                            00002     00003
  18894.                            00000     00001
  18895.              data lines   ||||||||  ||||||||   (16 bits)
  18896.  
  18897.              When one of these chips reads or writes, it can read/write either
  18898.              the left or the right byte or the whole word. What it cannot do
  18899.              is read the right byte from one pair along with the left byte
  18900.              from another pair. If you want to read the word at 00005:00006,
  18901.              the 8086 must:
  18902.  
  18903.                  1) read the 00005 byte.
  18904.                  2) read the 00006 byte.
  18905.                  3) join them together.
  18906.  
  18907.              This takes longer than just a single word read.
  18908.  
  18909.  
  18910.  
  18911.              The PC Assembler Tutor                                        274
  18912.              ______________________
  18913.  
  18914.              The true 80386 has a 32 bit data bus. This allows it to read 4
  18915.              bytes at a time, and its physical memory structure looks like
  18916.              this:
  18917.  
  18918.                                           80386
  18919.                                      MEMORY ADDRESSES
  18920.  
  18921.                            00010     00011     00012     00013
  18922.                            0000C     0000D     0000E     0000F
  18923.                            00008     00009     0000A     0000B
  18924.                            00004     00005     00006     00007
  18925.                            00000     00001     00002     00003
  18926.              data lines   ||||||||  ||||||||  ||||||||  ||||||||  (32 bits)
  18927.  
  18928.              Instead of memory pairs, we now have memory quadruplets. As long
  18929.              as a word is totally inside of one quadruplet, the read/write
  18930.              time will be unaffected. If the read/write crosses the boundary
  18931.              (as we did above), the read/write time will be affected in the
  18932.              same way. The 80386 can also read 4 byte data quickly as long as
  18933.              the total data is inside of one memory quadruplet.
  18934.  
  18935.              In the 8086 family, data can always be read across these
  18936.              boundaries but it takes more time. (On the IBM 370, on the other
  18937.              hand, there are instructions that REQUIRE that data be aligned
  18938.              along 32 bit boundaries).
  18939.  
  18940.              This means you should order your data in the following way in the
  18941.              data segment:
  18942.  
  18943.                  QWORD DATA
  18944.                  DWORD DATA
  18945.                  TBYTE DATA     ; this is for the 8087
  18946.                  WORD DATA
  18947.                  BYTE DATA      ; all strings, etc.
  18948.  
  18949.              This insures that any read/write for that type of data will
  18950.              always be as fast as possible. If the segment  definition has no
  18951.              alignment type, it will start on a paragraph boundary - i.e.
  18952.              every 16 bytes, and will work with anything. {3}
  18953.  
  18954.              In addition, if you ever subtract a number from SP to provide for
  18955.              a temporary data area, it should always be an even number. If SP
  18956.              is at an odd address instead of an even address, it takes longer
  18957.              for PUSHes and POPs. Also, when you define the size of the stack
  18958.              segment, it should be an even number of bytes.
  18959.  
  18960.              Having said that, it is now time for you to see the speeds of
  18961.              ____________________
  18962.  
  18963.                 3. The alignment type is a word after the word SEGMENT which
  18964.              says how the segment should be aligned. The following:
  18965.  
  18966.                       DATASTUFF SEGMENT BYTE PUBLIC 'DATA'
  18967.  
  18968.              says the segment can be aligned at any byte. The allowable forms
  18969.              are BYTE, WORD, DWORD, PARA, PAGE (256 bytes). If there is no
  18970.              explicit type, the default is PARA.
  18971.  
  18972.  
  18973.              Chapter 25 - What Does It All Mean?                           275
  18974.              ___________________________________
  18975.  
  18976.              instructions. Read the introduction to APPENDIX III, then glance
  18977.              at the times to get the general idea of how fast times are. Come
  18978.              back to this chapter when you are comfortable with what the times
  18979.              look like.
  18980.  
  18981.              Have you read APPENDIX III? If not, do it before going on.
  18982.  
  18983.              The compiled languages all have one thing in common. They tell
  18984.              you that if you are writing a subroutine, you need to return from
  18985.              the subroutine with DS, BP, SS, and SP unchanged. They don't say
  18986.              a thing about any of the other registers. One thing this tells us
  18987.              is that they are doing everything from memory locations, not
  18988.              register locations. If you have taken a good look at the
  18989.              execution times, you will have noticed the phenomenal difference
  18990.              in time between a "memory, register" addition and a "register,
  18991.              register" addition.
  18992.  
  18993.              Now, if all you are going to do is move a number to a register,
  18994.              add it, and move it out again, a compiler can do it as fast as
  18995.              you can. But, if you run into a situation where you can use three
  18996.              or four registers at the same time, you can cut the execution
  18997.              time drastically. Compilers really can't use registers as
  18998.              efficiently as we can (yet). This is an ideal spot for using
  18999.              assembly language.
  19000.  
  19001.              The old adage that 10% of the code uses 90% of the computer time
  19002.              is appropriate here. You now know about assembler language, and
  19003.              you know what you want to do with it, so go out and enjoy. But
  19004.              before you do, try to slog your way through the next chapter on
  19005.              "simplified" segment definitions and linking to high level
  19006.              languages.
  19007. Chapter 26 - Simplifying the Template
  19008. =====================================
  19009.                                                                            276
  19010.  
  19011.  
  19012.  
  19013.              By the time you have finished this chapter your assembler files
  19014.              will look cleaner. Unfortunately there is some heavy sledding
  19015.              before we get there.
  19016.  
  19017.  
  19018.              EXITING A PROGRAM
  19019.  
  19020.              Till now, we have exited most programs with CTRL-C; otherwise the
  19021.              program has done a return. A return to what? It has been
  19022.              returning to a section of code that does INT 20h, one of the ways
  19023.              of quitting a program when everything is in order. Notice the
  19024.              "everything is in order" in the last sentence. What happens if
  19025.              you have 2 files open, you are off in some subroutine, and you
  19026.              have things so hopelessly confused that you might as well give
  19027.              up? Can you call INT 20h? The answer is no for two reasons.
  19028.              First, you need CS to point to the PSP (program segment prefix).
  19029.              and you don't know where the PSP is. Secondly, you need to close
  19030.              files. Now, it is possible to make some code to do this, but why
  19031.              bother. We have a special interrupt for this:
  19032.  
  19033.                  INT 21h function 4Ch
  19034.                  AH = 4Ch
  19035.                  AL = return code
  19036.  
  19037.              This will close all files, get you out of the program, and give a
  19038.              return code that is usable by the calling program. Here's a small
  19039.              program. Use template.asm and call this TEST4CH.ASM.
  19040.  
  19041.                  ; - - - - - - - - - - - - - - - - - - - - - - - - -
  19042.                  CODESTUFF    SEGMENT   PUBLIC  'CODE'
  19043.  
  19044.                     ASSUME cs:CODESTUFF, ds:DATASTUFF
  19045.                     EXTRN   get_unsigned_byte:NEAR
  19046.  
  19047.                  main   proc far
  19048.  
  19049.                  start:
  19050.                          mov  ax, DATASTUFF    ; load ds
  19051.                          mov  ds,ax
  19052.  
  19053.                          call get_unsigned_byte    ; value is in al
  19054.                          mov  ah, 4Ch              ; int 21h, function  4Ch
  19055.                          int  21h
  19056.  
  19057.                  main   endp
  19058.                  CODESTUFF    ENDS
  19059.                  ; - - - - - - - - - - - - - - - - - - - - - - - - - -
  19060.  
  19061.              We have revised the CODESTUFF segment so there is only one EXTRN
  19062.              statement and the beginning code:
  19063.  
  19064.  
  19065.              ______________________
  19066.  
  19067.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  19068.  
  19069.  
  19070.              Chapter 26 - Simplifying The Template                         277
  19071.              _____________________________________
  19072.  
  19073.                  push ds
  19074.                  sub  ax, ax
  19075.                  push ax
  19076.  
  19077.              is gone. Since we will never again do a return to the PSP, there
  19078.              is no need for this code anymore.
  19079.  
  19080.              The program gets a single byte to use as the exit code and then
  19081.              exits using int 21h function 4Ch. Get this assembled and linked.
  19082.              It should ask for a number and then exit. But where is that
  19083.              number? It is available through the operating system. Make the
  19084.              following batch file. It runs TEST4CH.EXE and then looks at the
  19085.              error code. Unfortunately ERRORLEVEL is not available as an exact
  19086.              number to a batch file, so we are checking to see if the return
  19087.              code was above a certain level.
  19088.  
  19089.                  -----------------  DO4CH.BAT -----------------------
  19090.                  test4ch
  19091.                  ECHO OFF
  19092.                  IF ERRORLEVEL   1  ECHO The return code was over 0
  19093.                  IF ERRORLEVEL  51  ECHO The return code was over 50
  19094.                  IF ERRORLEVEL 101  ECHO The return code was over 100
  19095.                  IF ERRORLEVEL 151  ECHO The return code was over 150
  19096.                  IF ERRORLEVEL 201  ECHO The return code was over 200
  19097.                  ECHO ON
  19098.                  ----------------------------------------------------
  19099.  
  19100.              Here's one run of the batch file:
  19101.  
  19102.                  >do4ch
  19103.                  >int4ch
  19104.  
  19105.                  The PC Assembler Helper   Version 1.01
  19106.                  Copyright (C) 1989  Chuck Nelson   All rights reserved.
  19107.                  Enter a number from 0 to 255  172
  19108.  
  19109.                  >ECHO OFF
  19110.                  The return code was over 0
  19111.                  The return code was over 50
  19112.                  The return code was over 100
  19113.                  The return code was over 150
  19114.  
  19115.              This is what happens to DO4CH.BAT with a return code of 172.
  19116.  
  19117.              From now on, always use INT 21h function 4Ch to exit.
  19118.  
  19119.  
  19120.              SEGMENTS
  19121.  
  19122.              Our major simplification has to do with segment names. Before we
  19123.              go on with segment simplification, here are the rules the linker
  19124.              uses. If you don't remember them, you should review Chapter 10
  19125.              before going on.
  19126.  
  19127.              During the link process, the linker will combine any segments
  19128.              which:
  19129.  
  19130.  
  19131.  
  19132.              The PC Assembler Tutor                                        278
  19133.              ______________________
  19134.  
  19135.                  1) have the same name.
  19136.                  2) are declared PUBLIC.
  19137.                  3) have the same class name (type).
  19138.  
  19139.              The linker processes object modules from left to right on the
  19140.              command line. The classes will be ordered in the ordering in
  19141.              which they were encountered (including the empty class type).
  19142.              Within each class, the segments will be ordered in the ordering
  19143.              in which they were encountered.
  19144.  
  19145.              If we have all these rules, how do high-level languages manage to
  19146.              combine their data and code correctly? The answer is that they
  19147.              use standardized segment definitions. Here are the basic ones for
  19148.              our data:
  19149.  
  19150.                  ;---------------------------------------------------
  19151.                  _DATA  SEGMENT WORD PUBLIC 'DATA'
  19152.                  _DATA  ENDS
  19153.                  ;---------------------------------------------------
  19154.                  ;---------------------------------------------------
  19155.                  CONST  SEGMENT WORD PUBLIC 'CONST'
  19156.                  CONST  ENDS
  19157.                  ;---------------------------------------------------
  19158.                  ;---------------------------------------------------
  19159.                  _BSS  SEGMENT WORD PUBLIC 'BSS'
  19160.                  _BSS  ENDS
  19161.                  ;---------------------------------------------------
  19162.                  ;---------------------------------------------------
  19163.                  STACK  SEGMENT PARA STACK 'STACK'
  19164.                  STACK  ENDS
  19165.                  ;---------------------------------------------------
  19166.  
  19167.              If all the code will fit in one segment we can use a single
  19168.              segment name:
  19169.  
  19170.                  ;---------------------------------------------------
  19171.                  _TEXT  SEGMENT WORD PUBLIC 'CODE'
  19172.                  _TEXT  ENDS
  19173.                  ;---------------------------------------------------
  19174.  
  19175.              otherwise we can make independent segments, each with an
  19176.              independant name:
  19177.  
  19178.                  ;---------------------------------------------------
  19179.                  name_TEXT  SEGMENT WORD PUBLIC 'CODE'
  19180.                  name_TEXT  ENDS
  19181.                  ;---------------------------------------------------
  19182.  
  19183.              where the "name" can be anything, but the "_TEXT" remains
  19184.              invariable. Any subroutine calls within the segment can be NEAR,
  19185.              while any calls to a different segment should be FAR.
  19186.  
  19187.              The "WORD" in these definitions says that when the linker
  19188.              combines segments into a larger segment, each subsegment must
  19189.              start at an even address (a word boundary). This has to do with
  19190.              the speed of word fetches from memory that we discussed in the
  19191.              last chapter. "WORD" is fine for 16 bit data busses, but for a
  19192.  
  19193.  
  19194.              Chapter 26 - Simplifying The Template                         279
  19195.              _____________________________________
  19196.  
  19197.              80386 you actually want "DWORD" so things are correctly aligned
  19198.              with a 32 bit data bus. "PARA" means paragraph and that means
  19199.              aligned with a segment starting address (every 16 bytes).
  19200.              Everything will work with "PARA".
  19201.  
  19202.              For reasons of convenience, compilers put different types of data
  19203.              in different segments. For you, there is no reason to use more
  19204.              than one segment, and that is:
  19205.  
  19206.                  ;--------------------------------
  19207.                  _DATA  SEGMENT WORD PUBLIC 'DATA'
  19208.                  _DATA  ENDS
  19209.                  ;--------------------------------
  19210.  
  19211.              Compilers use these different segments because they can. If they
  19212.              were constrained to use only one segment name, they could do it
  19213.              with no problem. What is in these different segments?
  19214.  
  19215.                  _DATA     standard initialized data
  19216.                  CONST     data constants
  19217.                  _BSS      uninitialized static data
  19218.                  STACK     room for the SS:SP stack
  19219.  
  19220.              So what do these things mean?
  19221.  
  19222.              _DATA
  19223.  
  19224.              The _DATA segment stores all initialized data which exists from
  19225.              the time the program starts till the time that the program ends.
  19226.  
  19227.              In C:
  19228.                  static int     x = 5;
  19229.  
  19230.              In Pascal:
  19231.                  const
  19232.                       my_salary  : real = 52.77
  19233.  
  19234.              These variables have a specific value at the start of the
  19235.              program, even before the first instruction is executed. This
  19236.              value may change during the program. The variable exists during
  19237.              the whole program.
  19238.  
  19239.  
  19240.              _BSS
  19241.  
  19242.              The _BSS segment stores all uninitialized data which exists from
  19243.              the time the program starts till the time that the program ends.
  19244.  
  19245.              In C:
  19246.                  static int     x ;
  19247.  
  19248.              In Pascal, any variable declared outside a procedure but without
  19249.              an initial value will be in _BSS. In compiled BASIC, everything
  19250.              except dynamic arrays is in the _BSS. These variables have an
  19251.              indeterminate value at the start of the program and exist during
  19252.              the whole program.
  19253.  
  19254.  
  19255.  
  19256.              The PC Assembler Tutor                                        280
  19257.              ______________________
  19258.  
  19259.  
  19260.              CONST
  19261.  
  19262.              CONST takes all constants which are longer than 2 bytes. If the
  19263.              compiler is on its toes, anything one or two bytes long will be
  19264.              coded into the machine instructions since this is much faster.
  19265.              What is a constant? It is anything that has a value but doesn't
  19266.              have a variable name:
  19267.  
  19268.                  value = 275.29 ;
  19269.                  printf ( "Mr. Yellow: 'Read my lips - no new taxis!'\n");
  19270.                  result =  value / 27.619 ;
  19271.                  file_ptr = fopen ( "stuff.doc", "r+") ;
  19272.  
  19273.              All the numbers and all the text strings need to be stored
  19274.              somewhere. They are stored in the CONST segment and given an
  19275.              internal name by the compiler so they can be used at the
  19276.              appropriate location. They are not available in other parts of
  19277.              the program.{1} These constants are sometimes called literals.
  19278.  
  19279.  
  19280.              STACK
  19281.  
  19282.              BASIC does not use the stack in the same way as Pascal and C. In
  19283.              BASIC it is used only for passing variables between subroutines.
  19284.              In C and Pascal, most variables are temporary. They come into
  19285.              existance at the beginning of the subroutine and they disappear
  19286.              upon leaving the subroutine. When you call the subroutine again,
  19287.              the values these variables have are indeterminate. These
  19288.              variables all exist on the stack relative to BP, the base
  19289.              pointer. This is why you can have recursion in C and Pascal but
  19290.              not in BASIC.
  19291.  
  19292.              As I said, you don't need to put your different types of data in
  19293.              different segments. It can all go into _DATA.
  19294.  
  19295.  
  19296.  
  19297.              GROUPS
  19298.  
  19299.              We now come to the bizarre. You will notice that when the linker
  19300.              links all these object modules together, it will have four
  19301.              distinct segments with each segment having a distinct class name.
  19302.              We will get:
  19303.  
  19304.                  _DATA     'DATA'
  19305.                  CONST     'CONST'
  19306.                  _BSS      'BSS'
  19307.                  STACK     'STACK'
  19308.  
  19309.              The problem here is that we want to set DS at the beginning of
  19310.              ____________________
  19311.  
  19312.                 1. There is an exception. Some compilers check to make sure
  19313.              that there are no duplicates of the constant. These compilers
  19314.              give all duplicates the same address so there is only one copy of
  19315.              any one constant such as 0, 1, etc.
  19316.  
  19317.  
  19318.              Chapter 26 - Simplifying The Template                         281
  19319.              _____________________________________
  19320.  
  19321.              the program so that it will reference all the data. How are we
  19322.              going to do this? The warped minds of electrical engineers and
  19323.              computer scientists spent hours and hours trying to find the most
  19324.              obscure way possible to unify data addressing and they came up
  19325.              with GROUPS.
  19326.  
  19327.              You can tell the linker that you want data from distinct segments
  19328.              to be referenced by the offset from the beginning of the lowest
  19329.              segment in memory that belongs to the group. Read this about five
  19330.              or ten times to get the hang of it. You tell the linker that a
  19331.              bunch of different segments belong to a group. It will find the
  19332.              segment which is lowest in memory and then whenever you ask for
  19333.              the GROUP offset, the linker will calculate the offset from the
  19334.              beginning of this first segment.
  19335.  
  19336.              The way you define a group is with a name, the word "GROUP", and
  19337.              then a list of those segments in the file which belong to the
  19338.              group:
  19339.  
  19340.                  DGROUP  GROUP _DATA, CONST, _BSS, STACK
  19341.  
  19342.              Note that it is the segment names, not the class names. DGROUP is
  19343.              the standard name for the data group. If the assembler gives the
  19344.              linker the correct information, the linker will adjust all
  19345.              offsets relative to the beginning of the group. The only limit on
  19346.              a group is that the distance from the first byte of the group to
  19347.              the last byte of the group must be 65535 bytes or less. This is
  19348.              because all the group segments must reside in one physical
  19349.              segment in memory.
  19350.  
  19351.              It is not even necessary for all the segments in a block of
  19352.              memory to belong to the group. Consider the following ordering of
  19353.              segments in memory.
  19354.  
  19355.                       _DATA
  19356.                       DATASTUFF
  19357.                       CONST
  19358.                       CODESTUFF
  19359.                       _BSS
  19360.                       EVENMORESTUFF
  19361.                       STACK
  19362.  
  19363.              As long as the distance from one end of _DATA to the other end of
  19364.              STACK is 65535 bytes or less, the linker will adjust the offsets
  19365.              in _DATA, CONST, _BSS and STACK relative to the start of DGROUP
  19366.              and the linker will adjust the offsets of DATASTUFF, CODESTUFF
  19367.              and EVENMORESTUFF relative to their respective segment starting
  19368.              addresses. I didn't say that this was good programming, I only
  19369.              said that it was possible.
  19370.  
  19371.              Thoroughly confused? You're not alone. Just remember, in all
  19372.              compiled languages, we are going to combine these four types of
  19373.              segments into a single group where offsets are relative to the
  19374.              very beginning of the data.
  19375.  
  19376.              Before getting you even more confused, let's take a look at what
  19377.              we have so far. Make sure you actually do all of the following
  19378.  
  19379.  
  19380.              The PC Assembler Tutor                                        282
  19381.              ______________________
  19382.  
  19383.              examples. Use template.asm and at the very top, put in the
  19384.              following segments:
  19385.  
  19386.                  ; - - - - - - - - -
  19387.                  SEG1  SEGMENT 'STUFF'
  19388.                            db  100  dup (?)
  19389.                  seg1_data db  ?
  19390.                            db  899 dup (?)
  19391.                  SEG1 ENDS
  19392.                  ; - - - - - - - - -
  19393.                  SEG3  SEGMENT 'STUFF'
  19394.                            db  300  dup (?)
  19395.                  seg3_data db  ?
  19396.                            db  699 dup (?)
  19397.                  SEG3 ENDS
  19398.                  ; - - - - - - - - -
  19399.                  SEG5  SEGMENT 'STUFF'
  19400.                            db  500  dup (?)
  19401.                  seg5_data db  ?
  19402.                            db  499 dup (?)
  19403.                  SEG5 ENDS
  19404.                  ; - - - - - - - - -
  19405.  
  19406.              Call this program QGROUP1.ASM. These segments are 1000 bytes
  19407.              long, and the data names are 100, 300 and 500 bytes into their
  19408.              respective segments. Because these segments will be paragraph
  19409.              aligned, the second and third segments will start 1008 bytes (16
  19410.              X 63) after the proceeding one. You need to tell the assembler
  19411.              that these are in a group and give the proper ASSUME statement.
  19412.              We'll call this QGROUP:
  19413.  
  19414.                     QGROUP  GROUP  SEG1, SEG3, SEG5
  19415.                     ASSUME cs:CODESTUFF, ds:DATASTUFF, ds:QGROUP
  19416.  
  19417.              Here's some code:
  19418.  
  19419.                  ; + + + + + + + + + + + + START CODE BELOW THIS LINE
  19420.                      lea     ax, seg1_data
  19421.                      call    print_unsigned
  19422.                      lea     ax, seg3_data
  19423.                      call    print_unsigned
  19424.                      lea     ax, seg5_data
  19425.                      call    print_unsigned
  19426.                  ; + + + + + + + + + + + + END CODE ABOVE THIS LINE
  19427.  
  19428.              As you can see, all we are doing is putting the addresses into AX
  19429.              and then printing them as unsigned numbers. Here's the output:
  19430.  
  19431.                  00100
  19432.                  01308
  19433.                  02516
  19434.  
  19435.              Remember, each segment is starting 1008 bytes after the start of
  19436.              the previous one. Here's the same program with a few extra
  19437.              segments thrown in. Call it QGROUP2.ASM.:
  19438.  
  19439.                  ; - - - - - - - - -
  19440.  
  19441.  
  19442.              Chapter 26 - Simplifying The Template                         283
  19443.              _____________________________________
  19444.  
  19445.                  SEG1  SEGMENT  'STUFF'
  19446.                            db  100  dup (?)
  19447.                  seg1_data db  ?
  19448.                            db  899 dup (?)
  19449.                  SEG1 ENDS
  19450.                  ; - - - - - - - - -
  19451.                  SEG2  SEGMENT  'STUFF'
  19452.                            db  200  dup (?)
  19453.                  seg2_data db  ?
  19454.                            db  799 dup (?)
  19455.                  SEG2 ENDS
  19456.                  ; - - - - - - - - -
  19457.                  SEG3  SEGMENT  'STUFF'
  19458.                            db  300  dup (?)
  19459.                  seg3_data db  ?
  19460.                            db  699 dup (?)
  19461.                  SEG3 ENDS
  19462.                  ; - - - - - - - - -
  19463.                  SEG4  SEGMENT  'STUFF'
  19464.                            db  400  dup (?)
  19465.                  seg4_data db  ?
  19466.                            db  599 dup (?)
  19467.                  SEG4 ENDS
  19468.                  ; - - - - - - - - -
  19469.                  SEG5  SEGMENT  'STUFF'
  19470.                            db  500  dup (?)
  19471.                  seg5_data db  ?
  19472.                            db  499 dup (?)
  19473.                  SEG5 ENDS
  19474.                  ; - - - - - - - - -
  19475.  
  19476.              This is almost the same thing but we have added two more
  19477.              segments. We are NOT going to join these two segments into the
  19478.              group. Here's the GROUP and ASSUME statements:
  19479.  
  19480.                     QGROUP  GROUP  SEG1, SEG3, SEG5
  19481.                     ASSUME  ds:SEG2, ds:SEG4
  19482.                     ASSUME cs:CODESTUFF, ds:DATASTUFF, ds:QGROUP
  19483.  
  19484.              Make sure the ASSUME statements are in that order or things may
  19485.              get confused. We also add some code:
  19486.  
  19487.                  ; + + + + + + + + + + + + START CODE BELOW THIS LINE
  19488.                      lea     ax, seg1_data
  19489.                      call    print_unsigned
  19490.                      lea     ax, seg2_data
  19491.                      call    print_unsigned
  19492.                      lea     ax, seg3_data
  19493.                      call    print_unsigned
  19494.                      lea     ax, seg4_data
  19495.                      call    print_unsigned
  19496.                      lea     ax, seg5_data
  19497.                      call    print_unsigned
  19498.                  ; + + + + + + + + + + + + END CODE ABOVE THIS LINE
  19499.  
  19500.              This shows the addresses of all five variables. Here's the new
  19501.              output:
  19502.  
  19503.  
  19504.              The PC Assembler Tutor                                        284
  19505.              ______________________
  19506.  
  19507.  
  19508.                  00100
  19509.                  00200
  19510.                  02316
  19511.                  00400
  19512.                  04532
  19513.  
  19514.              As you can see, the GROUPed segments have their offsets relative
  19515.              to the beginning of the group while the others have their offsets
  19516.              relative to the beginning of the segment.
  19517.  
  19518.              Make a copy of QGROUP1.ASM and call it QGROUP?.ASM. Leave the
  19519.              segment definitions, group definitions, ASSUME statements and
  19520.              code the same, but add six more lines of code at the end:
  19521.  
  19522.                      ; compare the offsets
  19523.                      mov     ax, offset seg1_data
  19524.                      call    print_unsigned
  19525.                      mov     ax, offset seg3_data
  19526.                      call    print_unsigned
  19527.                      mov     ax, offset seg5_data
  19528.                      call    print_unsigned
  19529.                  ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  19530.  
  19531.              After the three LEAs we now do 3 OFFSETS. Assemble and link this.
  19532.              Here's the output:
  19533.  
  19534.                  00100
  19535.                  01308
  19536.                  02516
  19537.                  00100
  19538.                  00300
  19539.                  00500
  19540.  
  19541.              Wait a minute! Those last three numbers should be the same as the
  19542.              first three numbers. That's right, folks. This is a known error
  19543.              in the MASM assembler. In fact the Turbo Assembler copies this
  19544.              mistake when it is in "MASM" mode but does it right when it is in
  19545.              "IDEAL" mode. A86 does it right all the time. Here is the output
  19546.              from the same source file when assembled by A86:
  19547.  
  19548.                  00100
  19549.                  01308
  19550.                  02516
  19551.                  00100
  19552.                  01308
  19553.                  02516
  19554.  
  19555.              You have told the assembler to calculate all offsets relative to
  19556.              the beginning of the group and MASM is ignoring you every time
  19557.              you use the OFFSET operator. The code fix for this is to use an
  19558.              override when you use OFFSET:
  19559.  
  19560.                       ; compare the offsets
  19561.                      mov     ax, offset QGROUP:seg1_data
  19562.                      call    print_unsigned
  19563.                      mov     ax, offset QGROUP:seg3_data
  19564.  
  19565.  
  19566.              Chapter 26 - Simplifying The Template                         285
  19567.              _____________________________________
  19568.  
  19569.                      call    print_unsigned
  19570.                      mov     ax, offset QGROUP:seg5_data
  19571.                      call    print_unsigned
  19572.                  ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  19573.  
  19574.              Better yet, use LEA whenever possible. If you do use OFFSET with
  19575.              groups, you need to go through the text file with a word search
  19576.              to make sure that all OFFSETs have a group override. This is a
  19577.              subtle error and it is very hard to find if you are not looking
  19578.              for it.
  19579.  
  19580.  
  19581.              This system is designed so that we can have 64k of data and
  19582.              stack, all of which is addressable with DS without changing DS's
  19583.              value. What happens if you have more data than that? One thing
  19584.              for sure is that you don't have more than 64k of individually
  19585.              named variables. Either that or you have some huge calluses on
  19586.              your typing fingers.
  19587.  
  19588.              What you do have is arrays. If you run into space problems, you
  19589.              move the least used or the biggest arrays into their own
  19590.              segments. You can have one segment per array if you want. The
  19591.              standardized high-level language names for these segments is:
  19592.  
  19593.                  ; - - - - - - - - - - - - - - - - - - - - -
  19594.                  FAR_DATA  SEGMENT  PARA  'FAR_DATA'
  19595.                  FAR_DATA  ENDP
  19596.                  ; - - - - - - - - - - - - - - - - - - - - -
  19597.                  FAR_BSS   SEGMENT  PARA  'FAR_BSS'
  19598.                  FAR_BSS   ENDP
  19599.                  ; - - - - - - - - - - - - - - - - - - - - -
  19600.  
  19601.              Once again, the '_DATA' is for initialized data while the '_BSS'
  19602.              is for uninitialized data. Use only the 'FAR_DATA' kind.{2} You
  19603.              will notice that these segments are NOT PUBLIC. Although an
  19604.              assembler will unify all segments with the same definition that
  19605.              are in the same file, the linker will not unify segments from
  19606.              different files which are not PUBLIC. If we create 4 different
  19607.              .ASM files, each with one segment:
  19608.  
  19609.                  ; FARDATA1.ASM
  19610.                  PUBLIC data1
  19611.                  ; - - - - - - - - - - - - - - - - - - - - -
  19612.                  FAR_DATA  SEGMENT  PARA  'FAR_DATA'
  19613.                  data1     db   1A67h dup (0)
  19614.                  FAR_DATA  ENDS
  19615.                  ; - - - - - - - - - - - - - - - - - - - - -
  19616.  
  19617.                  ; FARDATA2.ASM
  19618.                  PUBLIC data2
  19619.              ____________________
  19620.  
  19621.                 2. A high-level language has the right to set all the data of
  19622.              a 'BSS' segment to zero as part of its startup routine. Whether
  19623.              it does so or not depends on what it has told the linker. If you
  19624.              put initialized data into either a '_BSS' or a 'FAR_BSS' segment,
  19625.              it might easily wind up zero after startup.
  19626.  
  19627.  
  19628.              The PC Assembler Tutor                                        286
  19629.              ______________________
  19630.  
  19631.                  ; - - - - - - - - - - - - - - - - - - - - -
  19632.                  FAR_DATA  SEGMENT  PARA  'FAR_DATA'
  19633.                  data2     db   0D4A8h dup (0)
  19634.                  FAR_DATA  ENDS
  19635.                  ; - - - - - - - - - - - - - - - - - - - - -
  19636.  
  19637.                  FARDATA3.ASM
  19638.                  PUBLIC data3
  19639.                  ; - - - - - - - - - - - - - - - - - - - - -
  19640.                  FAR_DATA  SEGMENT  PARA  'FAR_DATA'
  19641.                  data3     db   200h dup (0)
  19642.                  FAR_DATA  ENDS
  19643.                  ; - - - - - - - - - - - - - - - - - - - - -
  19644.  
  19645.                  FARDATA4.ASM
  19646.                  PUBLIC  data4
  19647.                  ; - - - - - - - - - - - - - - - - - - - - -
  19648.                  FAR_DATA  SEGMENT PARA  'FAR_DATA'
  19649.                  data4     db   8716h dup (0)
  19650.                  FAR_DATA  ENDS
  19651.                  ; - - - - - - - - - - - - - - - - - - - - -
  19652.  
  19653.              and link these with TEMPLATE.OBJ and ASMHELP, we will get the
  19654.              following .MAP file:
  19655.  
  19656.                   Start  Stop   Length Name                   Class
  19657.                   00000H 01A66H 01A67H FAR_DATA               FAR_DATA
  19658.                   01A70H 0EF17H 0D4A8H FAR_DATA               FAR_DATA
  19659.                   0EF20H 0F11FH 00200H FAR_DATA               FAR_DATA
  19660.                   0F120H 17835H 08716H FAR_DATA               FAR_DATA
  19661.                   17840H 1823FH 00A00H STACKSEG               STACK
  19662.                   18240H 1875DH 0051EH DATASTUFF              DATA
  19663.                   18760H 1A02FH 018D0H CODESTUFF              CODE
  19664.  
  19665.                  Program entry point at 1876:0000
  19666.  
  19667.              The numbers in the segment definitions were in hex so you could
  19668.              read the .MAP file more easily. We have created four different
  19669.              FAR_DATAs - one for each variable.
  19670.  
  19671.              The idea here is to leave DS alone if possible and use ES:SI or
  19672.              ES:DI for your manipulation of the array.
  19673.  
  19674.                  mov  ax, seg data1
  19675.                  mov  es, ax
  19676.                  mov  si, offset data1
  19677.  
  19678.              Of course, if you using two different FAR_DATA arrays from two
  19679.              different segments at the same time, you will probably need to
  19680.              use DS temporarily. This is the kind of thing you need to plan
  19681.              before you start a program which contains large arrays.
  19682.  
  19683.  
  19684.  
  19685.  
  19686.  
  19687.  
  19688.  
  19689.  
  19690.              Chapter 26 - Simplifying The Template                         287
  19691.              _____________________________________
  19692.  
  19693.              You have now seen all possible segments for any Microsoft
  19694.              language and for Turbo C.{3} These are:
  19695.  
  19696.  
  19697.                  _DATA  SEGMENT WORD PUBLIC 'DATA'
  19698.                  CONST  SEGMENT WORD PUBLIC 'CONST'
  19699.                  _BSS   SEGMENT WORD PUBLIC 'BSS'
  19700.                  STACK  SEGMENT PARA STACK 'STACK'
  19701.                  _TEXT  SEGMENT WORD PUBLIC 'CODE'
  19702.  
  19703.                  name_TEXT SEGMENT WORD PUBLIC 'CODE'
  19704.                  FAR_DATA  SEGMENT PARA  'FAR_DATA'
  19705.                  FAR_BSS   SEGMENT PARA  'FAR_BSS'
  19706.  
  19707.                  DGROUP  GROUP  _DATA, CONST, _BSS, STACK
  19708.  
  19709.  
  19710.              We have another problem on our road to simplification. We want DS
  19711.              to have the address of the start of DGROUP. How do we do it?
  19712.              Well, before we had:
  19713.  
  19714.                  mov  ax, DATASTUFF
  19715.                  mov  ds, ax
  19716.  
  19717.              DATASTUFF was a segment. We do the same thing for groups:
  19718.  
  19719.                  mov  ax, DGROUP
  19720.                  mov  ds, ax
  19721.  
  19722.              We use a group name instead of a segment name. This means that
  19723.              our ultimate code segment will look like this
  19724.  
  19725.                  ; - - - - - - - - - -
  19726.                  _TEXT  SEGMENT WORD PUBLIC 'CODE'
  19727.  
  19728.                       DGROUP  GROUP  _DATA, CONST, _BSS, STACK
  19729.                       ASSUME cs:_TEXT, ds:DGROUP
  19730.  
  19731.                  start:
  19732.                       mov  ax, DGROUP
  19733.                       mov  ds, ax
  19734.  
  19735.                       ; - - - - - - - - - -
  19736.                       ; the program goes here
  19737.                       ; - - - - - - - - - -
  19738.  
  19739.                       mov  ah, 4Ch
  19740.              ____________________
  19741.  
  19742.                 3. If you are using Turbo PASCAL, then there are only two
  19743.              segments possible. They are:
  19744.  
  19745.                  DATA  SEGMENT WORD PUBLIC
  19746.                  CODE  SEGMENT BYTE PUBLIC
  19747.  
  19748.              There is no class name. You can substitute DSEG for DATA and CSEG
  19749.              for CODE if you want. Turbo Pascal has no DGROUP.
  19750.  
  19751.  
  19752.              The PC Assembler Tutor                                        288
  19753.              ______________________
  19754.  
  19755.                       mov  al, ?          ; replace ? with error code
  19756.                       int  21h
  19757.  
  19758.                  _TEXT  ENDS
  19759.                  ; - - - - - - - - - -
  19760.  
  19761.              Say, if all this stuff is standardized text, why are we forced to
  19762.              type all this drivel over and over again. The answer is that we
  19763.              aren't. All the segment information has a shorthand. Here's how
  19764.              it works. Every shorthand symbol starts with a dot. The assembler
  19765.              will then generate the desired text.{4} This is from MASM 5.0 on,
  19766.              so if you have an earlier assembler you'll have to write the full
  19767.              text.
  19768.  
  19769.              To start out, use the two starting directives DOSSEG (with no
  19770.              dot) and .MODEL. MODEL will be explained later.{5}
  19771.  
  19772.                       DOSSEG
  19773.                       .MODEL Medium
  19774.  
  19775.              For now, 'medium' is what we want.
  19776.  
  19777.              From that point, if you want a data segment, you just write
  19778.              .DATA, if you want code, you write .CODE. Every time that the
  19779.              assembler sees a segment directive it will close any segment that
  19780.              is open and start the segment indicated by the directive. (You
  19781.              can always reopen a segment). Here is what replaces the
  19782.              directives:
  19783.  
  19784.                  DIRECTIVE                REPLACEMENT TEXT
  19785.  
  19786.                  .DATA               _DATA  SEGMENT WORD PUBLIC 'DATA'
  19787.                  .CONST              CONST  SEGMENT WORD PUBLIC 'CONST'
  19788.                  .DATA?              _BSS   SEGMENT WORD PUBLIC 'BSS'
  19789.                  .STACK [size]       STACK  SEGMENT PARA STACK 'STACK'
  19790.                  .CODE               _TEXT  SEGMENT WORD PUBLIC 'CODE'
  19791.  
  19792.                  .CODE [name]        name_TEXT SEGMENT WORD PUBLIC 'CODE'
  19793.                  .FARDATA [name]     FAR_DATA  SEGMENT PARA  'FAR_DATA'
  19794.                  .FARDATA? [name]    FAR_BSS   SEGMENT PARA  'FAR_BSS'
  19795.  
  19796.              The [name] in brackets will be explained in a minute. The [size]
  19797.              after the stack declaration allows you to customize the size of
  19798.              the stack. Without any size, the declaration
  19799.  
  19800.                  .STACK
  19801.  
  19802.              will allocate 1k of memory for the stack. A size allocates a
  19803.              ____________________
  19804.  
  19805.                 4. It really generates no text. It is just that the assembler
  19806.              will generate the same machine code as if that text had been
  19807.              generated.
  19808.  
  19809.                 5. DOSSEG tells the assembler to tell the linker that the .EXE
  19810.              file should have the standard segment  order. It is not necessary
  19811.              but it doesn't hurt.
  19812.  
  19813.  
  19814.              Chapter 26 - Simplifying The Template                         289
  19815.              _____________________________________
  19816.  
  19817.              specific number of bytes:
  19818.  
  19819.                  .STACK 2000h
  19820.  
  19821.              You can make it anything you want, but make sure it is an even
  19822.              number and remember that the limit for all four parts of DGROUP
  19823.              is 64k.
  19824.  
  19825.              To see how the names work, we need some text files. Here is a
  19826.              complete main file:
  19827.  
  19828.                  ; FARDATA.ASM   - driver module
  19829.                  DOSSEG
  19830.                  .MODEL  medium
  19831.                  EXTRN  data2_routine:FAR, data3_routine:FAR
  19832.                  .STACK  200h
  19833.                  .FARDATA
  19834.                       data1     db   0100h dup (0)
  19835.                  .CODE
  19836.                  main:
  19837.                       mov  ax, DGROUP
  19838.                       mov  ds, ax
  19839.                       call data2_routine
  19840.                       call data3_routine
  19841.                       mov  ax, 4C00h
  19842.                       int 21h
  19843.                  END  main
  19844.  
  19845.              It has some data and some code though it doesn't really do
  19846.              anything. We will use this along with two other files for the
  19847.              examples. Here is FARDATA2:
  19848.  
  19849.                  ; FARDATA2.ASM
  19850.                  DOSSEG
  19851.                  .MODEL  medium
  19852.                  PUBLIC data2_routine
  19853.                  .FARDATA
  19854.                       data2     db   0200h dup (0)
  19855.                  .CODE
  19856.                  data2_routine  proc
  19857.                       ret
  19858.                  data2_routine endp
  19859.                  END
  19860.  
  19861.              Notice that data2_routine doesn't have a FAR or NEAR. That's
  19862.              being taken care of by the memory model. Data2_routine's type
  19863.              does need to be declared EXTRN in the main module. The third
  19864.              routine has similar code. Here is the .MAP file when they are
  19865.              combined:
  19866.  
  19867.  
  19868.                   Start  Stop   Length Name                   Class
  19869.                   00000H 00013H 00014H FARDATA_TEXT           CODE
  19870.                   00014H 00014H 00001H FARDATA2_TEXT          CODE
  19871.                   00016H 00016H 00001H FARDATA3_TEXT          CODE
  19872.                   00020H 0011FH 00100H FAR_DATA               FAR_DATA
  19873.                   00120H 0031FH 00200H FAR_DATA               FAR_DATA
  19874.  
  19875.  
  19876.              The PC Assembler Tutor                                        290
  19877.              ______________________
  19878.  
  19879.                   00320H 0061FH 00300H FAR_DATA               FAR_DATA
  19880.                   00620H 00620H 00000H _DATA                  DATA
  19881.                   00620H 0081FH 00200H STACK                  STACK
  19882.  
  19883.  
  19884.              You can see the FAR_DATAs there, but where did the FARDATA3_TXT
  19885.              come from? The assembler decided that we wanted independent code
  19886.              segments and gave each one the name of the assembler file it came
  19887.              from. Since all the object files in a program must have unique
  19888.              names, these segment names should also be unique. If we change
  19889.              the .MODEL from MEDIUM to COMPACT without touching anything else,
  19890.              then we get:
  19891.  
  19892.  
  19893.                   Start  Stop   Length Name                   Class
  19894.                   00000H 00022H 00023H _TEXT                  CODE
  19895.                   00030H 0012FH 00100H FAR_DATA               FAR_DATA
  19896.                   00130H 0032FH 00200H FAR_DATA               FAR_DATA
  19897.                   00330H 0062FH 00300H FAR_DATA               FAR_DATA
  19898.                   00630H 00630H 00000H _DATA                  DATA
  19899.                   00630H 0082FH 00200H STACK                  STACK
  19900.  
  19901.              If we now put a name after the .FARDATA directive, it will give
  19902.              the segment a unique name. Putting:
  19903.  
  19904.                  .FARDATA  jake_the_snake
  19905.  
  19906.              in  FARDATA2.ASM, along with name changes in the other modules
  19907.              results in the following .MAP file:
  19908.  
  19909.  
  19910.                   Start  Stop   Length Name                   Class
  19911.                   00000H 00022H 00023H _TEXT                  CODE
  19912.                   00030H 0012FH 00100H HACKSAW                FAR_DATA
  19913.                   00130H 0032FH 00200H JAKE_THE_SNAKE         FAR_DATA
  19914.                   00330H 0062FH 00300H HULKSTER               FAR_DATA
  19915.                   00630H 00630H 00000H _DATA                  DATA
  19916.                   00630H 0082FH 00200H STACK                  STACK
  19917.  
  19918.  
  19919.  
  19920.              We are doing a number of interrelated things here, so let's try
  19921.              to unify what is going on. You have seen both NEAR and FAR
  19922.              routines in the Tutor. A NEAR routine alters IP and restores IP
  19923.              on the return. A FAR routine alters both CS and IP and restores
  19924.              them on the return.
  19925.  
  19926.              When we passed addresses of data, we have almost always passed
  19927.              just the offset of the data. That is because the data has almost
  19928.              always been in the DATASTUFF SEGMENT, and the value of DS has
  19929.              been known. In Chapter 19 we did "move_pascal_string" which was a
  19930.              subroutine where we passed both the segment and offset of the
  19931.              data. These are our two choices for passing addresses:
  19932.  
  19933.                       OFFSET           1 word
  19934.                       SEGMENT:OFFSET   2 words
  19935.  
  19936.  
  19937.  
  19938.              Chapter 26 - Simplifying The Template                         291
  19939.              _____________________________________
  19940.  
  19941.              This gives us four basic possiblilities for program structure:
  19942.  
  19943.                  SUBROUTINE CALL     DATA ADDRESSES PASSED AS
  19944.  
  19945.                       NEAR               OFFSET
  19946.                       FAR                OFFSET
  19947.                       NEAR               SEGMENT:OFFSET
  19948.                       FAR                SEGMENT:OFFSET
  19949.  
  19950.              Each of these structural possibilities has a name called a MODEL
  19951.              name. They are:
  19952.  
  19953.                  SUBROUTINE CALL      ADDRESSES PASSED AS        MODEL NAME
  19954.  
  19955.                       NEAR               OFFSET                   SMALL
  19956.                       FAR                OFFSET                   MEDIUM
  19957.                       NEAR               SEGMENT:OFFSET           COMPACT
  19958.                       FAR                SEGMENT:OFFSET           LARGE
  19959.  
  19960.  
  19961.              You tell the assembler which model you are working with by using
  19962.              the .MODEL directive:
  19963.  
  19964.                  .MODEL  medium
  19965.  
  19966.              The assembler will then make either NEAR or FAR the default type.
  19967.              This can be overridden if you have explicitly given a NEAR or
  19968.              FAR:
  19969.  
  19970.                  my_proc  procedure
  19971.  
  19972.              will generate the correct subroutine calls and returns for that
  19973.              model, while:
  19974.  
  19975.                  my_proc1  procedure  near
  19976.                  my_proc2  procedure  far
  19977.  
  19978.              will remain unaltered.
  19979.  
  19980.              At the assembler level, you need to code the address passing
  19981.              yourself, but if you have a MODEL and you are connected to a
  19982.              high-level language (with the same .MODEL type), the high-level
  19983.              language will pass all addresses as stated above.
  19984.  
  19985.              The advantage of this system is that using the .MODEL directive
  19986.              and appropriate EQU statements and MACROS (which we have not
  19987.              covered), it is possible to write a single subroutine which can
  19988.              then be assembled in all four model configurations. Coding this
  19989.              is non-trivial, but when you have done more programming you will
  19990.              see how to deal with the stack using EQUs and MACROs.
  19991.  
  19992.              For now, you want to stay with data addresses which are passed by
  19993.              offset only. This is much easier. These are the SMALL and MEDIUM
  19994.              models. Whether you choose NEAR or FAR procedures doesn't affect
  19995.              much except where parameters are on the stack (because of that
  19996.              extra CS).
  19997.  
  19998.  
  19999.  
  20000.              The PC Assembler Tutor                                        292
  20001.              ______________________
  20002.  
  20003.              Are these .MODELS important? They are nice, but not particularly
  20004.              vital. What happens is that in a manual you see a sample program
  20005.              like this:
  20006.  
  20007.                  DOSSEG
  20008.                  .MODEL medium
  20009.                  .STACK
  20010.                  .DATA
  20011.                  variable1 dw 25
  20012.                  .CODE
  20013.                  sample proc
  20014.                  mov  ax, variable1
  20015.                  ret
  20016.                   sample  endp
  20017.                  ENDS
  20018.                  END
  20019.  
  20020.              and you start comparing the size of this to the size it would be
  20021.              if you used the standard segment definitions. I have news for
  20022.              you. This is not a legitimate program. A legitimate program is a
  20023.              page or two long.{6} Also, at least to my way of thinking, you
  20024.              want visual separation between segments. The above is a
  20025.              disordered presentation of segments. We want order in our
  20026.              programs and the segment headers provide a visual structure. In
  20027.              the text file for ASMHELP, (which is about 3600 lines long), the
  20028.              SEGMENT declarations occupy about 20 lines. This is about 0.5% of
  20029.              the total length of the file.
  20030.  
  20031.              If you are going to assemble a file in multiple models, then it
  20032.              is worthwhile to use the .MODEL directives, otherwise it is
  20033.              optional depending more on your concept of what looks clear than
  20034.              any major difference.
  20035.  
  20036.  
  20037.              ____________________
  20038.  
  20039.                 6. Perhaps you want to scan \COMMENTS\MISHMASH.DOC which
  20040.              contains some real subroutines. They are all long.
  20041.  
  20042.  
  20043.              Chapter 26 - Simplifying The Template                         293
  20044.              _____________________________________
  20045.  
  20046.                                       SUMMARY
  20047.  
  20048.              To exit a program, use INT 21h Function 4Ch
  20049.  
  20050.                  mov   ah, 4Ch         ; exit program
  20051.                  mov   al, ?           ; replace ? with error code
  20052.                  int   21h
  20053.  
  20054.  
  20055.              A GROUP is a group of segments whose data will be referenced by
  20056.              the offset from the beginning of the group. You declare a group
  20057.              with:
  20058.  
  20059.                  DGROUP  GROUP  _DATA, CONST, _BSS, STACK
  20060.  
  20061.              MASM calculates OFFSETS incorectly with groups, so you should
  20062.              either use LEA or the DGROUP override:
  20063.  
  20064.                  lea  ax, variable1
  20065.                  mov  ax, offset DGROUP:variable1
  20066.  
  20067.              To get the address of DGROUP in DS you need:
  20068.  
  20069.                  ASSUME ds:DGROUP
  20070.  
  20071.              and:
  20072.  
  20073.                  mov  ax, DGROUP
  20074.                  mov  ds, ax
  20075.  
  20076.  
  20077.  
  20078.              The standardized segment definitions, along with their simplified
  20079.              directives are:
  20080.  
  20081.                  DIRECTIVE                REPLACEMENT TEXT
  20082.  
  20083.                  .DATA               _DATA  SEGMENT WORD PUBLIC 'DATA'
  20084.                  .CONST              CONST  SEGMENT WORD PUBLIC 'CONST'
  20085.                  .DATA?              _BSS   SEGMENT WORD PUBLIC 'BSS'
  20086.                  .STACK [size]       STACK  SEGMENT PARA STACK 'STACK'
  20087.                  .CODE               _TEXT  SEGMENT WORD PUBLIC 'CODE'
  20088.  
  20089.                  .CODE [name]        name_TEXT SEGMENT WORD PUBLIC 'CODE'
  20090.                  .FARDATA [name]     FAR_DATA  SEGMENT PARA  'FAR_DATA'
  20091.                  .FARDATA? [name]    FAR_BSS   SEGMENT PARA  'FAR_BSS'
  20092.  
  20093.  
  20094.              In addition you have the different model names:
  20095.  
  20096.                  SUBROUTINE CALL      ADDRESSES PASSED AS        .MODEL NAME
  20097.  
  20098.                       NEAR               OFFSET                   SMALL
  20099.                       FAR                OFFSET                   MEDIUM
  20100.                       NEAR               SEGMENT:OFFSET           COMPACT
  20101.                       FAR                SEGMENT:OFFSET           LARGE
  20102. Using BASIC - 1
  20103. ===============
  20104.                                                                            mmi
  20105.  
  20106.  
  20107.  
  20108.              So you like using BASIC. I can't blame you. BASIC has several
  20109.              very nice features.
  20110.  
  20111.                  (1) Its graphics interface is easy to use and is very
  20112.                  powerful. If you want to draw lines, circles, patterns or
  20113.                  other graphics images, you can do it. Your only limitations
  20114.                  are the aspect ratio of the screen and the possible screen
  20115.                  colors. These limitations have to do with hardware, not with
  20116.                  BASIC.
  20117.  
  20118.                  (2) Its string handling capabilities are unparalleled. If
  20119.                  you want to have an array of strings like the following:
  20120.  
  20121.                       DIM  STRING.ARRAY$ (50, 25)  ' 1250 elements
  20122.  
  20123.                  and some of the elements are only a couple of characters
  20124.                  long but some of the strings are several hundred characters
  20125.                  long, BASIC is the ONLY language that can handle it
  20126.                  correctly. In fact, if you wanted one of the strings to be
  20127.                  1000 characters (it's allowed in the most recent BASIC),
  20128.                  then you would need 1.25 megabytes for this array in C or in
  20129.                  PASCAL. This would probably be too big for the computer.
  20130.                  BASIC, on the other hand, implements this without wasting
  20131.                  any space in memory.
  20132.  
  20133.  
  20134.              BASIC has a few things that require a little more work than with
  20135.              other languages. It has strings, integers, and floating point
  20136.              numbers but it is missing characters and unsigned numbers - these
  20137.              can be implemented by using integers (if you are careful). This
  20138.              is not all that limiting.
  20139.  
  20140.  
  20141.              If BASIC is so nice, why do I think that you should have some
  20142.              experience with a structured language before doing assembler? It
  20143.              all has to do with data INTEGRITY. The question is, are you sure
  20144.              that you haven't inadvertantly screwed up your data? There are
  20145.              two ways that this might happen. You need to know how BASIC works
  20146.              to understand the problem.
  20147.  
  20148.              A variable name in BASIC consists of a name followed by a type
  20149.              identifier. The identifiers are '%', '!', '$', '#' (double
  20150.              precision) and possibly '&' (long integer). The name:
  20151.  
  20152.                  CURRENT.TEMPERATURE
  20153.  
  20154.              is NOT a variable inside of BASIC while:
  20155.  
  20156.                  CURRENT.TEMPERATURE%
  20157.                  CURRENT.TEMPERATURE!
  20158.                  CURRENT.TEMPERATURE$
  20159.  
  20160.              ______________________
  20161.  
  20162.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  20163.  
  20164.  
  20165.              The PC Assembler Tutor                                       mmii
  20166.              ______________________
  20167.  
  20168.              are all valid variables. In fact, all three can be in the same
  20169.              program, as we shall see shortly. I just said that that first
  20170.              example is not a valid variable inside of BASIC, but that is
  20171.              exactly what you have been writing since the first time that you
  20172.              wrote a BASIC program. What's going on here? Well, when the BASIC
  20173.              interpreter or compiler reads the text file, it reads the name
  20174.              and the identifier. If the type identifier is missing, it tacks
  20175.              the default identifier on to the end of the name.
  20176.  
  20177.              What's the default type identifier? The answer is '!'. BASIC
  20178.              maintains a default list. The legal first characters in a name
  20179.              are A-Z. For each legal first letter, there is an entry in the
  20180.              list which says what the default type for that letter is. When
  20181.              you start the program, the type list looks like this:
  20182.  
  20183.                  !!!!!!!!!!!!!!!!!!!!!!!!!!
  20184.  
  20185.              When the interpreter sees a name without an identifier, it takes
  20186.              the first character of the name and goes to the default-type
  20187.              list, gets the appropraiate type identifier for that first
  20188.              character, and adds the identifier on to the end of the name.
  20189.              Externally (in your text file) there may be names without type
  20190.              identifiers, but internally (inside the interpreter/compiler) ALL
  20191.              variables have the type identifier after the name.
  20192.  
  20193.              Though the list starts out with all '!'s, you can change this
  20194.              list with a DEF statement. Here are some DEF statements and how
  20195.              they change the list. I am assuming that we are always starting
  20196.              at the beginning of the program (with all '!'s).
  20197.  
  20198.                  (1)
  20199.                  DEFINT A-Z     ' This is probably your favorite
  20200.                  %%%%%%%%%%%%%%%%%%%%%%%%%%
  20201.  
  20202.                  (2)
  20203.                  DEFINT A-E
  20204.                  %%%%%!!!!!!!!!!!!!!!!!!!!!
  20205.  
  20206.                  (3)
  20207.                  DEFINT A-E
  20208.                  DEFSTR F-K
  20209.                  DEFDBL L-R
  20210.                  %%%%%$$$$$$#######!!!!!!!!
  20211.  
  20212.              This last one is a recipe for disaster, but it's legal.
  20213.  
  20214.              Have you understood everything so far? Then let's go on. This
  20215.              ability to NOT use the type identifier leads to laziness. The
  20216.              usual thing is to always identify strings with a '$' but to be
  20217.              rather lax about the distinction between integers and floating
  20218.              point numbers.
  20219.  
  20220.              Now we get to the problem. Look at the following code:
  20221.  
  20222.  
  20223.  
  20224.  
  20225.  
  20226.  
  20227.              BASIC I - Using Basic                                       mmiii
  20228.              _____________________
  20229.  
  20230.  
  20231.              10       DEFINT A-Z
  20232.              20       FOR I = 1 to 5
  20233.              30            LARGE.NUMBER! = 300.0 * I
  20234.              40            SMALL.NUMBER! = LARGE.NUMBER / 7
  20235.              50            PRINT  LARGE.NUMBER!, SMALL.NUMBER!
  20236.              60            NEXT I
  20237.  
  20238.              If you can't see the error, I forgot to put the '!' after
  20239.              LARGE.NUMBER in line 40. The problem here is that BASIC is not
  20240.              going to complain. When it sees LARGE.NUMBER without a type
  20241.              identifier, it will go to the default list (because of the DEFINT
  20242.              statement, all defaults are integer) and put a % after it. BASIC
  20243.              has created a SECOND variable with the same initial name, but a
  20244.              different type. This is not because you wanted it to, but rather
  20245.              because you forgot a '!'. Lets do a BASIC program. Here's some
  20246.              code:
  20247.  
  20248.                  *******************  NINJA.BAS *******************
  20249.  
  20250.                  10                       ' place for defint statement
  20251.                  20      '
  20252.                  30      ' enter all the data
  20253.                  40      NINJA% = 20
  20254.                  50      NINJA! = 4.7134E+13
  20255.                  60      NINJA$ = "Hey dude!"
  20256.                  70      '
  20257.                  80      NINJA$ (0) = "howdy! "
  20258.                  90      FOR I% = 1 TO 5
  20259.                  100         NINJA$ (I%) = NINJA$ ( I% - 1 ) + "doody! "
  20260.                  110         NINJA! (I%) = I% * 25.31
  20261.                  120         NINJA% (I%) = I% * 3
  20262.                  130     NEXT I%
  20263.                  140     '
  20264.                  150     ' print all the data
  20265.                  160     PRINT   NINJA%, NINJA!, NINJA$
  20266.                  170     FOR I% = 1 TO 5
  20267.                  180         PRINT NINJA% (I%), NINJA! (I%), NINJA$ (I%)
  20268.                  190     NEXT I%
  20269.                  200     PRINT  NINJA , NINJA (3)
  20270.                  210     '
  20271.                  220     END
  20272.  
  20273.                  *********************************************************
  20274.  
  20275.              I actually want you to run this through your BASIC. I have
  20276.              defined SIX different NINJAs. They are:
  20277.  
  20278.                  1)   NINJA%
  20279.                  2)   NINJA% ( )
  20280.                  3)   NINJA!
  20281.                  4)   NINJA! ( )
  20282.                  5)   NINJA$
  20283.                  6)   NINJA$ ( )
  20284.  
  20285.              where the parentheses indicate an array. If I had gotten carried
  20286.              away, I could have included 4 more NINJAs for double precision,
  20287.  
  20288.  
  20289.              The PC Assembler Tutor                                       mmiv
  20290.              ______________________
  20291.  
  20292.              double precision array, long integer and long integer array.
  20293.  
  20294.              Running this through my computer I get:
  20295.  
  20296.              20          4.7134E+13  Hey dude!
  20297.              3           25.31       howdy! doody!
  20298.              6           50.62       howdy! doody! doody!
  20299.              9           75.93       howdy! doody! doody! doody!
  20300.              12          101.24      howdy! doody! doody! doody! doody!
  20301.              15          126.55      howdy! doody! doody! doody! doody! doody!
  20302.              4.7134E+13  75.93
  20303.  
  20304.              Notice that I have 3 different array types and 3 different
  20305.              variable types. As you can see from the bottom line (which prints
  20306.              the defaults), the default here is '!'. If we now put DEFINT A-Z
  20307.              on line 10:
  20308.  
  20309.                  10   DEFINT A-Z
  20310.  
  20311.              everything will stay the same except the bottom line which now
  20312.              is:
  20313.  
  20314.                  20        9
  20315.  
  20316.              which are the integers. Finally, if we have DEFSTR A-Z
  20317.  
  20318.                  10   DEFSTR A-Z
  20319.  
  20320.              we get:
  20321.  
  20322.                  Hey dude!    howdy! doody! doody! doody!
  20323.  
  20324.              as the bottom line of output. If you didn't know all of this
  20325.              before, make sure you have assimilated this before going on.
  20326.  
  20327.  
  20328.              PASCAL and C
  20329.  
  20330.              Both PASCAL and C require you to account for every variable. You
  20331.              need to specifically state what kind of variable it is and you
  20332.              need to enter it in a list of variables used in that block of
  20333.              code:
  20334.  
  20335.                  int    ch, variable1, variable2, variable3[60] ;
  20336.                  char   long_array[4027] ;
  20337.  
  20338.              A name can be used in only one way in any block of code. If you
  20339.              try to use it differently, the compiler will generate an error.
  20340.              Though people moan and groan about having to specifically list
  20341.              all the variables used in a block of code, over time they get
  20342.              used to it. It offers two advantages.
  20343.  
  20344.                  (1) No variable can be inadvertently used in an incorrect
  20345.                  way (used as a string when you wanted an integer, used as an
  20346.                  integer when you wanted a floating point number, etc.).
  20347.  
  20348.                  (2) There can be no variables which are misspelled, are left
  20349.  
  20350.  
  20351.              BASIC I - Using Basic                                         mmv
  20352.              _____________________
  20353.  
  20354.                  over from a previous version of the code or appear by
  20355.                  mistake, without both the compiler and you knowing about
  20356.                  them.
  20357.  
  20358.              Slightly modifying names is one of my major problems. I might use
  20359.              'long_array' and 'longarray' in different places in the C code,
  20360.              though I want the same thing. C will catch this, BASIC won't.
  20361.              Every time BASIC encounters a slightly different name it simply
  20362.              creates a new variable (and normally sets it to 0). In programs
  20363.              larger than a few pages, this situation is almost impossible to
  20364.              detect or control.
  20365.  
  20366.  
  20367.  
  20368.              THE RANGE OF A VARIABLE
  20369.  
  20370.              When talking about PASCAL and C I kept using the word BLOCK. Both
  20371.              languages have a modular (or block) design. The philosophy behind
  20372.              this is that when you write a program you want to divide a task
  20373.              into a number of distinct subtasks, and you don't want these
  20374.              subtasks to interfere with each other. You put each of these in a
  20375.              subprogram. In BASIC you can put sections of a program in
  20376.              subprograms too. This is good programming practice. There is a
  20377.              difference, however. In C and PASCAL, a variable is valid ONLY in
  20378.              the block in which it is declared (unless you take specific steps
  20379.              to make it universally valid). If you try to use this variable in
  20380.              a different block of code the compiler will generate an error -
  20381.              it literally has no information about where this variable is. In
  20382.              BASIC, all names are valid everywhere in a file. If on page 1 you
  20383.              have:
  20384.  
  20385.                  210 CURRENT.PRICE! = 427.11
  20386.  
  20387.              you may want to use this as a constant number throughout the
  20388.              program. But if in a subprogram on page 23 you have:
  20389.  
  20390.                  12060     CURRENT.PRICE! = 0
  20391.  
  20392.              you have changed the value. You're saying to yourself "But why
  20393.              would I change a variable like "CURRENT.PRICE!"? You probably
  20394.              wouldn't. But you will have dozens and dozens of variables around
  20395.              with names like I, J, K, COUNT, START, STARTING.NUMBER,
  20396.              STARTING.VALUE, TOTAL, RESULT and LAST.VALUE (whatever their
  20397.              type). It is exactly these that will be corrupted in BASIC but
  20398.              will be reliable in C or PASCAL.
  20399.  
  20400.                  **********************************************
  20401.                  1920      MINIMUM! = RATE! * 5.0
  20402.                  1930      MAXIMUM! = RATE! * 7.32
  20403.                  1940      COUNT% = (MAXIMUM! - MINIMUM!) / 3.0
  20404.                  1950      CALL CHECK.ANSWER
  20405.                  1960      PRINT COUNT%
  20406.                  **********************************************
  20407.  
  20408.              In this section of code, did 'CALL CHECK.ANSWER' change any of
  20409.              the four variables MINIMUM!, MAXIMUM!, RATE! or COUNT%? In BASIC,
  20410.              we hope so, but we can't be sure. In PASCAL or C we KNOW that it
  20411.  
  20412.  
  20413.              The PC Assembler Tutor                                       mmvi
  20414.              ______________________
  20415.  
  20416.              didn't change any code. This is the difference between a
  20417.              structural engineer HOPING that a bridge won't fall down and
  20418.              KNOWING that a bridge won't fall down.
  20419.  
  20420.              In fact, the PASCAL and C programmers will probably be annoyed
  20421.              when they start using assembler because in assembler, ALL names
  20422.              are valid everywhere. More importantly, however, these
  20423.              programmers will make great efforts to insure that all constants
  20424.              stay constants and that each variable is used for one job only.
  20425.  
  20426.  
  20427.              Finally, some BASIC programmers still do most of their
  20428.              programming using GOTO statements instead of CALLs. This is the
  20429.              antethesis of modular programming and is called spaghetti
  20430.              programming. The code winds up as an intertwined mess. If you are
  20431.              one of these people, once you start writing assembler code it
  20432.              will be impossible to figure out what the code is doing once it
  20433.              is longer than a page or two.
  20434.  
  20435.              All that being said, if you feel that your programming style is
  20436.              clear and orderly, have a fun time learning assembler. When you
  20437.              are done with all the chapters, read the second BASIC appendix on
  20438.              the problems associated with linking a non-BASIC subprogram to a
  20439.              BASIC program.
  20440. BASIC II - Interfacing BASIC with ASSEMBLER
  20441. ===========================================
  20442.                                                                          mmvii
  20443.  
  20444.  
  20445.  
  20446.              Have you finished reading all the chapters? If not, go back and
  20447.              do them, then come back to this when you are done. This chapter
  20448.              assumes you know about segments, subroutines, and the general
  20449.              information about linking subroutines to high-level languages.
  20450.  
  20451.              In order to do this appendix I had to dust off my old QuickBASIC
  20452.              3.0. If you have QuickBASIC 4.x, some things will have been
  20453.              updated. If you have TurboBASIC, the subroutine conventions are
  20454.              different. However, the structure will be the same. You will have
  20455.              to consult your manual for exact details. If you are trying to
  20456.              this with the interpreter that came with DOS, I have a simple
  20457.              comment -> Forget it! I won't go into the details, but the BASIC
  20458.              interpreter is so much slower (about 10 times slower) and so much
  20459.              more difficult to use with assembler that I won't even cover it.
  20460.              There is no reason not to use one of the compiled BASICs if BASIC
  20461.              is your language of choice. This material only covers how to deal
  20462.              with COMPILED BASIC.
  20463.  
  20464.  
  20465.              In BASIC, all individual numeric data, strings, "static" arrays
  20466.              and the stack must fit into one 64k segment. The word 'segment'
  20467.              here has the same meaning as in assembler. Both the DS register
  20468.              and the SS register are set to this segment, and must stay set to
  20469.              this segment whenever BASIC has control of the program. "Dynamic"
  20470.              arrays can be located somewhere else in memory.
  20471.  
  20472.              You allocate a "static" array with a constant number as a
  20473.              dimension:
  20474.  
  20475.                  DIM  array1! (277), array2% (346), array3$ (500)
  20476.  
  20477.              and you allocate a "dynamic" array by using a variable to
  20478.              dimension the array:
  20479.  
  20480.                  length1% = 277
  20481.                  length2% = 346
  20482.                  length3$ = 500
  20483.  
  20484.                  DIM  array1!(length1%), array2%(length2%), array3$(length3%)
  20485.  
  20486.              Even though the first and second dimension statements produce the
  20487.              same size and type arrays, the first ones must be located inside
  20488.              DS and the second ones can be located outside of DS.
  20489.  
  20490.              "Static" means that once the array is defined, its length and
  20491.              number of dimensions cannot be changed for the rest of the
  20492.              program. It will occupy a specific amount of space for the rest
  20493.              of the program. "Dynamic" means that you can change the length of
  20494.              the array whenever you want to. You do this with:
  20495.  
  20496.                  REDIM  array1! (495)
  20497.  
  20498.              ______________________
  20499.  
  20500.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  20501.  
  20502.  
  20503.              The PC Assembler Tutor                                     mmviii
  20504.              ______________________
  20505.  
  20506.  
  20507.              BASIC does this by deallocating space for the old array and then
  20508.              reallocating space for the new array. All the old information is
  20509.              lost. There are certain restrictions. You cannot change the
  20510.              number of dimensions in an array (if it starts out with 2
  20511.              dimensions like DIM A!(47,63), it must always have 2 dimensions).
  20512.  
  20513.              In order to understand BASIC's memory strategy, we need to look
  20514.              at strings, the reason for it all.{1} The limit for a single
  20515.              string is 32,767 bytes. If the total amount of data you can have
  20516.              in the DS segment is only 65536 bytes, how does BASIC allocate
  20517.              memory so you can have long strings without runnung out of space?
  20518.              It uses only as much space as it needs. Let's define 3 strings
  20519.              (the dots will indicate a space):
  20520.  
  20521.                  mystring$ = "You.say.either"
  20522.                  yourstring$ = "And.I.say.either"
  20523.                  ourstring$ = "Let's.call.it.off"
  20524.  
  20525.              After defining these three strings one after the other, memory
  20526.              will look like this:
  20527.  
  20528.                  17150
  20529.                  |You.say.eitherAnd.I.say.eitherLet's.call.it.off|
  20530.  
  20531.              (For clarity, the memory image will be between the '|'s and each
  20532.              row will be 50 bytes long. The next row down would start at
  20533.              17200). For our example we will assume that this data starts at
  20534.              memory location 17150.
  20535.  
  20536.              There is no empty space. How does BASIC know where and how long
  20537.              mystring$ is? It has something called a string descriptor. This
  20538.              is a two word (4 byte) block, also in DS, which says exactly
  20539.              where and how long the string is. The first word is the length
  20540.              and the second word is the location (offset) in DS.
  20541.  
  20542.              From BASIC's view, we have:
  20543.  
  20544.                  STRING         DESCRIPTOR
  20545.                               length:location
  20546.  
  20547.                  mystring$      14:17150       ->        |You.say.either|
  20548.                  yourstring$    16:17164       ->        |And.I.say.either|
  20549.                  ourstring$     17:17180       ->        |Let's.call.it.off|
  20550.  
  20551.              Now let's change one of the strings:
  20552.  
  20553.                  yourstring$ = "But.oh!,.If.we.call.the.whole.thing.off"
  20554.  
  20555.              We now have a problem. The current "yourstring$" is only 16 bytes
  20556.              long, but the new one is 39 bytes long. What does BASIC do? It
  20557.              (1) deallocates the space for the old "yourstring", (2) allocates
  20558.              new space for the new string and (3) updates the string
  20559.              ____________________
  20560.  
  20561.                 1. This is an outline of what BASIC does, but it will not
  20562.              include the parts of memory management that you will never see.
  20563.  
  20564.  
  20565.              BASIC II - Interfacing BASIC With Assembler                  mmix
  20566.              ___________________________________________
  20567.  
  20568.              descriptor. Memory will now look like this:
  20569.  
  20570.                  17150
  20571.                  |You.say.either                Let's.call.it.offBut|
  20572.                  |.oh!,.If.we.call.the.whole.thing.off|
  20573.  
  20574.              and the descriptors will now look like this:
  20575.  
  20576.  
  20577.                  STRING         DESCRIPTOR
  20578.                               length:location
  20579.  
  20580.                  mystring$      14:17150       ->        |You.say.either|
  20581.                  yourstring$    39:17197       ->        |But.oh!,.if.we...
  20582.                  ourstring$     17:17180       ->        |Let's.call.it.off|
  20583.  
  20584.              BASIC is aware that there is an empty block of space and has a
  20585.              strategy for dealing with empty spaces, though each BASIC has its
  20586.              own strategy. We don't know exactly WHEN it will take action, but
  20587.              we do know WHAT action it will take. At some point BASIC will
  20588.              decide that it has too many empty spaces in memory and will
  20589.              REORGANIZE the segment. This is known as GARBAGE COLLECTION.
  20590.              Exactly how this is done is up to the person who wrote the BASIC
  20591.              compiler/interpreter.
  20592.  
  20593.              After reorganization, the addresses of ALMOST ALL strings and
  20594.              MANY dynamic arrays will have changed. The string locations
  20595.              themselves will have changed, but the string descriptors will
  20596.              still be in the same place in DS, and they will have been
  20597.              updated. Here is the new memory:
  20598.  
  20599.  
  20600.                                          12724
  20601.                                          |You.say.eitherLet's.call.i|
  20602.                   |t.offBut.oh!,.If.we.call.the.whole.thing.off|
  20603.  
  20604.              and here are the updated descriptors:
  20605.  
  20606.                  STRING         DESCRIPTOR
  20607.                               length:location
  20608.  
  20609.                  mystring$      14:12724       ->        |You.say.either|
  20610.                  yourstring$    39:12755       ->        |But.oh!,.if.we...
  20611.                  ourstring$     17:12738       ->        |Let's.call.it.off|
  20612.  
  20613.              The strings have been moved several thousand bytes from where
  20614.              they were just a second ago. The information that was in the
  20615.              string descriptors a second ago is no longer valid. Old
  20616.              information about dynamic arrays is also unreliable. This means
  20617.              that if you have a subroutine written in assembler, you must get
  20618.              any address information at the time the subroutine is called.
  20619.              We'll come back to this later.
  20620.  
  20621.  
  20622.              Let's go on to data input and output. When you first started
  20623.  
  20624.  
  20625.  
  20626.              The PC Assembler Tutor                                        mmx
  20627.              ______________________
  20628.  
  20629.              doing BASIC, you did i/o using only:
  20630.  
  20631.                  WRITE #1, my.data!
  20632.  
  20633.              Perhaps you you do it differently now, perhaps not. In any case,
  20634.              you need to know about i/o speed and how different file i/o
  20635.              works. Here's the simplest file output:
  20636.  
  20637.                  ***********************************
  20638.                  DIM   large.array! (10000)
  20639.  
  20640.                  FOR i% = 1 to 10000
  20641.                       large.array! (i%) = 2.1
  20642.                  NEXT i%
  20643.  
  20644.                  OPEN "2-1.doc" for output as # 1
  20645.                  PRINT time$
  20646.                  FOR i% = 1 to 10000
  20647.                       WRITE #1, large.array! (i%)
  20648.                  NEXT i%
  20649.                  PRINT time$
  20650.                  CLOSE #1
  20651.                  ***********************************
  20652.  
  20653.              Of course, to make it a challenge we are going to write an array
  20654.              of 10,000 numbers. How long does it take?{2} For this output it
  20655.              took 38 seconds. The same program, inputting the same data with:
  20656.  
  20657.                  INPUT #1, large.array(i%)
  20658.  
  20659.              took 49 seconds. These are fairly large amounts of time. But
  20660.              wait, it gets worse. Let's change one line of the above program:
  20661.  
  20662.                  large.array! (i%) = 2.1678319E+19
  20663.  
  20664.              This is a different constant which is put into each element of
  20665.              the array. How long does output take now? 59 seconds. And input?
  20666.              a whopping 79 seconds! What's going on here?
  20667.  
  20668.              When you do i/o with INPUT #, WRITE # or PRINT #, it is exactly
  20669.              like doing i/o to the screen. For output, BASIC converts the
  20670.              binary numbers into TEXT and then writes the TEXT to the disk.
  20671.              When it does input, it reads the TEXT from the disk and converts
  20672.              the TEXT into a binary number. Here is the beginning of the
  20673.              output file from the first example:
  20674.  
  20675.                  2.1
  20676.                  2.1
  20677.                  2.1
  20678.                  2.1
  20679.              ____________________
  20680.  
  20681.                 2. All times from now on are with a slower PC with a slower
  20682.              hard disk, but an 8087. Since these are floating point numbers,
  20683.              your results should be slower if you don't have an 80x87, while
  20684.              if you have an 80386 with an 80387 and a fast hard disk, your
  20685.              times will be much faster.
  20686.  
  20687.  
  20688.              BASIC II - Interfacing BASIC With Assembler                  mmxi
  20689.              ___________________________________________
  20690.  
  20691.                  2.1
  20692.  
  20693.              Each data item has been converted into "2.1" + CHR$(13) +
  20694.              CHR$(10). These last two things are a carriage return on the IBM
  20695.              PC. That's (5 bytes X 10000 items) plus 1 byte for the end of
  20696.              file marker, or 50001 bytes:
  20697.  
  20698.                  2-1      DOC    50001   6-29-90  12:39p
  20699.  
  20700.              Here's the beginning of the output file from the second example:
  20701.  
  20702.                  2.167832E+19
  20703.                  2.167832E+19
  20704.                  2.167832E+19
  20705.                  2.167832E+19
  20706.                  2.167832E+19
  20707.  
  20708.              Each data item has been converted into "2.167832E+19" + CHR$(13)
  20709.              + CHR$(10). That's (14 bytes * 10000 items) plus 1 byte for the
  20710.              end of file marker, or 140001 bytes:
  20711.  
  20712.                  2-1E19   DOC   140001   6-29-90  12:47p
  20713.  
  20714.              These files are unnecessarily large, and i/o is slow: if you
  20715.              don't have an 8087 and you are doing floating-point i/o, it can
  20716.              be slower still.
  20717.  
  20718.              Can we do it faster? Yes. Using GET and PUT, we get a certain
  20719.              number of bytes from the disk, then transfer them to the array.
  20720.              Some of you have never used random access i/o, so this is a brief
  20721.              summary.
  20722.  
  20723.              When you open a file as text (as we did in the above examples),
  20724.              BASIC divides the text by looking for carriage returns. When you
  20725.              open a file as a random access file, you are telling BASIC that
  20726.              you want to divide the file into distinct blocks of information.
  20727.              It may be text or it may be something else - BASIC doesn't care.
  20728.              If you say nothing, BASIC assumes that you want the blocks to be
  20729.              128 bytes long, but the length can be anything.
  20730.  
  20731.              In the example that we will do, we will use 1024 byte blocks
  20732.              because that is exactly 2 disk sectors long, so the disk can read
  20733.              information easily and efficiently. If we had a block length of 4
  20734.              bytes, the disk would have to do 10000 disk writes; that would be
  20735.              very slow and be hard on the disk. Here's how we open the file:
  20736.  
  20737.                  OPEN "packed.doc" for RANDOM as #1 LEN = 1024
  20738.  
  20739.              This will be a random access file and the block length will be
  20740.              1024 bytes. When you tell it to read or write, it will do it 1024
  20741.              bytes at a time. That is getting faster.
  20742.  
  20743.              Where is the block of data that it is going to write to disk?
  20744.              Here life starts getting complicated, so I hope you have
  20745.              understood everything that we have done so far. When you open a
  20746.              file, BASIC assigns it a BUFFER. The buffer has a fixed length
  20747.              (either 128 bytes or the length you have designated), and is
  20748.  
  20749.  
  20750.              The PC Assembler Tutor                                      mmxii
  20751.              ______________________
  20752.  
  20753.              located somewhere in the DS data segment along with the numbers
  20754.              and strings. Like a string, it is relocatable. We need a way to
  20755.              pin it down. The easy and nice way would be if it were an array
  20756.              and we cound address it like an array:
  20757.  
  20758.                  buffer#1 (45) = 20
  20759.  
  20760.              We are not that lucky. The only thing you can do is overlay a
  20761.              template on the buffer, and work from the template. This template
  20762.              MUST be made up of strings. We make up the template with a FIELD
  20763.              statement.
  20764.  
  20765.                  FIELD #1, 1024 AS out.string$
  20766.  
  20767.              The FIELD statement starts out with the file # followed by a list
  20768.              of strings and the length of each string.
  20769.  
  20770.                  FIELD #1, 100 AS string1$, 200 AS string2$, 300 AS string3$
  20771.  
  20772.              The total length of the strings may be shorter than the buffer,
  20773.              but may not be longer than the buffer. What does the FIELD
  20774.              statement do?  The first thing that it does is set the string
  20775.              descriptor for all of these strings. Let's say that at the moment
  20776.              file #1 buffer is at 46217:
  20777.  
  20778.                  STRING         DESCRIPTOR
  20779.                               length:location
  20780.  
  20781.                  string1$       100:46217
  20782.                  string2$       200:46317
  20783.                  string3$       300:46517
  20784.  
  20785.              The first string starts at the first byte of the buffer. The
  20786.              second string starts right where the first string ends and the
  20787.              third string starts right where the second string ends. This is
  20788.              true for any FIELD statement, no matter how many strings are
  20789.              defined. Because of the way BASIC does memory management, if it
  20790.              moves the buffer, it will also update these string descriptors to
  20791.              point to the same relative places in the buffer. These string
  20792.              descriptors are on auto pilot.
  20793.  
  20794.              Suppose now that we have the following string:
  20795.  
  20796.                  "Let's get physical"
  20797.  
  20798.              and we want to write it to disk as string1$. All we need to do
  20799.              is:
  20800.  
  20801.                  string1$ = "Let's get physical"
  20802.  
  20803.              Right? No, that's very, very, very wrong. What you have just done
  20804.              is alter the string descriptor of string1$ to point to an
  20805.              entirely different place in memory. The string descriptors are
  20806.              now:
  20807.  
  20808.                  string1$        18:58902
  20809.                  string2$       200:46317
  20810.  
  20811.  
  20812.              BASIC II - Interfacing BASIC With Assembler                mmxiii
  20813.              ___________________________________________
  20814.  
  20815.                  string3$       300:46517
  20816.  
  20817.              BASIC deallocated the space for string1, reallocated it somewhere
  20818.              else in memory, and changed the file descriptor. Not only is
  20819.              string1 in a different place in memory, but BASIC may think that
  20820.              part of the file #1 buffer is actually empty space, and the next
  20821.              time it reorganizes memory, who knows what is going to happen.
  20822.              From the moment you define strings in a FIELD statement until the
  20823.              time you close the corresponding file, you can NEVER have them on
  20824.              the left side of an equal sign. Having them on the left side is
  20825.              sure to change the file descriptor.
  20826.  
  20827.              How are we going to transfer data to these strings? There are
  20828.              three special operators in BASIC - LSET, MID$ and RSET. Their job
  20829.              is to put something into a string without altering the string
  20830.              length or location (i.e. without altering the string descriptor).
  20831.  
  20832.                  LSET string1$ = "Let's get physical"
  20833.                  MID$ (string1$,17) = "Let's get physical"
  20834.                  RSET string1$ = "Let's get physical"
  20835.  
  20836.              LSET will insert the string at the very left of string1, RSET
  20837.              will insert the string at the very right of string1, and MID$
  20838.              will insert the string starting at the 17th byte of string1.
  20839.  
  20840.              This is the strategy for all random access i/o in BASIC. We:
  20841.  
  20842.                  1) open a file as RANDOM and declare a block size.
  20843.                  2) define some "fixed length" strings inside the buffer with
  20844.                     a FIELD statement.
  20845.                  3) insert data in the strings using LSET, RSET or MID$. This
  20846.                     is true whether the data is strings or numbers.
  20847.  
  20848.              There's only one problem left. For LSET, RSET and MID$, the thing
  20849.              on the RIGHT side of the equal sign must be a string. You can't
  20850.              have:
  20851.  
  20852.                  LSET string1$ = number!
  20853.  
  20854.              It's illegal. To counter this, BASIC has some pseudo-functions.
  20855.              Let's take integers as an example:
  20856.  
  20857.                  a.string$ = MKI$ (number%)
  20858.                  number% = CVI (a.string$)
  20859.  
  20860.              MKI$ doesn't actually DO anything. It just tells BASIC that it is
  20861.              o.k. to move two bytes from "number%" to "a.string$". The bytes
  20862.              are binary data and are moved unaltered. Similarly, CVI tells
  20863.              BASIC that it is alright to move two bytes of binary data from
  20864.              "a.string$" to "number%". We are tricking BASIC into moving
  20865.              binary data from one data type to another. This is simply data
  20866.              movement, and there is no data conversion. The forms are:
  20867.  
  20868.                  NUMERIC DATA MOVE          TO STRING      FROM STRING
  20869.  
  20870.                  integer <-> string            MKI$           CVI
  20871.                  long integer <->string        MKL$           CVL
  20872.  
  20873.  
  20874.              The PC Assembler Tutor                                      mmxiv
  20875.              ______________________
  20876.  
  20877.                  single precision <-> string   MKS$           CVS
  20878.                  double precision <-> string   MKD$           CVD
  20879.  
  20880.              In contrast, the functions STR$ and VAL convert text
  20881.              representations to binary representations and binary
  20882.              representations to text representations. This is the same as what
  20883.              happens with PRINT and INPUT. Here's a program:
  20884.  
  20885.                      **********************************************
  20886.                      number! = 2.1678319E+19
  20887.                      binary.string$ = MKS$ (number!)
  20888.                      text.string$ = STR$ (number!)
  20889.                      PRINT  LEN(text.string$), LEN(binary.string$)
  20890.                      PRINT  text.string$, binary.string$
  20891.                      **********************************************
  20892.  
  20893.              and here's the output:
  20894.  
  20895.                   13            4
  20896.                   2.167832E+19 nl _
  20897.  
  20898.              You probably won't be able to see all of that last output on your
  20899.              printer because it is four bytes long and the number is:
  20900.  
  20901.                  6E6C965F hex or   110, 108, 150, 95 decimal
  20902.  
  20903.              The third byte is outside of ASCII 33-127, the standard ASCII
  20904.              characters.
  20905.  
  20906.              STR$ gives us the text representation of the number, while MKS$
  20907.              stuffs the binary representation of a number into a string. In
  20908.              the opposite direction, VAL gives us the numeric value of a text
  20909.              string (if it has a numeric representation), while CVS stuffs 4
  20910.              binary bytes from a string into a single precision number.
  20911.  
  20912.                  STR$      from binary value to text representation
  20913.                  VAL       from text representation to binary value
  20914.  
  20915.              Note that STR$ can convert ANY type of number to a text string
  20916.              and VAL can convert a text string to ANY type of number, while
  20917.              CVI, CVL, CVS, CVD, MKI$, MKL$, MKS$, and MKD$ can only stuff a
  20918.              specific type of number into a string or a string into a specific
  20919.              type of number.
  20920.  
  20921.              We want our output program to stuff the binary value from a
  20922.              single precision number to selected bytes of a string. To stuff a
  20923.              floating-point number into string1$ above, all we need to do is:
  20924.  
  20925.                  LSET string1 = MKS$ ( number!)
  20926.  
  20927.              The following program has a single string which is the size of
  20928.              the entire buffer, and we are going to stuff the single precision
  20929.              numbers in one at a time with MID$.
  20930.  
  20931.                  ************************************************************
  20932.                  number% = 10240
  20933.                  DIM   large.array! (number%)
  20934.  
  20935.  
  20936.              BASIC II - Interfacing BASIC With Assembler                  mmxv
  20937.              ___________________________________________
  20938.  
  20939.  
  20940.                  FOR i% = 1 to 10240
  20941.                       large.array! (i%) = 2.1678319e+19
  20942.                  NEXT i%
  20943.  
  20944.                  OPEN "packed.doc" for RANDOM as #1 LEN = 1024
  20945.                  FIELD #1 , 1024 AS out.string$
  20946.  
  20947.                  PRINT time$
  20948.                  k% = 0
  20949.                  record.count% = 0
  20950.                  FOR i% = 1 to 40
  20951.                       record.count% = record.count% + 1
  20952.                       spot% = 1
  20953.                       FOR j% = 1 to 256
  20954.                          k% = k% + 1
  20955.                          MID$ (out.string$,spot%,4) = MKS$ (large.array!(k%))
  20956.                          spot% = spot% + 4
  20957.                       NEXT j%
  20958.                       PUT #1, record.count%
  20959.                  NEXT i%
  20960.                  PRINT time$
  20961.                  CLOSE #1
  20962.                  ***********************************************************
  20963.  
  20964.  
  20965.              The array length has been increased slightly so that we have an
  20966.              exact number of blocks. We use MID$ to make sure that the string
  20967.              descriptor for out.string$ does not get changed. Each file write
  20968.              will be (256 numbers * 4 bytes/number) 1024 bytes long. We start
  20969.              with the first record and increase the record number by 1 each
  20970.              time we write. Does this increase the speed any? Well, this takes
  20971.              11 seconds.
  20972.  
  20973.                  TYPE                     OUTPUT              INPUT
  20974.  
  20975.                  num <-> text             38 - 59 sec         49 - 79 secs
  20976.                  num <-> bin. string      11 sec              11 sec
  20977.  
  20978.              I didn't show you the equivalent input routines but here are the
  20979.              times they took. Note that the complexity of the single precision
  20980.              number has no effect on the last (the binary) routine. Also, the
  20981.              last routine does not suffer if there is no 8087. If you are
  20982.              running an 80286 with a fast hard disk, this last routine should
  20983.              only take a second or two. Here are the file sizes:
  20984.  
  20985.                  2-1      DOC    50001   6-29-90  12:39p
  20986.                  2-1E19   DOC   140001   6-29-90  12:47p
  20987.                  PACKED   DOC    40960   6-29-90   1:08p
  20988.  
  20989.              The first two are the different sizes depending on whether the
  20990.              constant was 2.1 or 2.1678319E+19. The last one is for our last
  20991.              routine. Notice that it is more compact.
  20992.  
  20993.              Can we do any better than 11 seconds? Yes, but we need to take
  20994.              over disk i/o and we need to know a few more things before we do
  20995.              that.
  20996.  
  20997.  
  20998.              The PC Assembler Tutor                                      mmxvi
  20999.              ______________________
  21000.  
  21001.  
  21002.  
  21003.              LOCATION OF DATA
  21004.  
  21005.              BASIC is designed to pass subroutines the location of the data,
  21006.              not the data itself. This is called passing by reference. Though
  21007.              it is possible to pass the data itself, there are certain
  21008.              problems with the stack if you do.{3} We will always pass the
  21009.              addresses.
  21010.  
  21011.              All single numeric variables are in the DS segment. BASIC passes
  21012.              the offset address of these variables in DS (1 word).
  21013.  
  21014.              All strings are in the DS segment. Their string descriptors are
  21015.              also in the DS segment. BASIC always passes the offset address of
  21016.              the STRING DESCRIPTOR. This, in fact, is what we want. We need to
  21017.              know both where the string is and how long it is. If we write
  21018.              past the end of the string we may destroy BASIC's memory
  21019.              management system.
  21020.  
  21021.              Static arrays are in the DS segment but dynamic arrays can be
  21022.              anywhere. If we want to write a general purpose routine with
  21023.              arrays, we need to handle them no matter where they are.
  21024.  
  21025.              BASIC has a special function called VARPTR that tells you where a
  21026.              variable is in memory. Here's a program that uses it for a couple
  21027.              of variables:
  21028.  
  21029.                  ***********************************************************
  21030.                  ' check out the use of varptr
  21031.                  n% = 5000
  21032.                  p% = 50
  21033.                  DIM  b!(800),a!(900)
  21034.                  DIM  d!(n%), c!(p%)
  21035.  
  21036.                  mystring$ = "What's up, doc?"
  21037.                  addressA! = varptr (n%)
  21038.                  addressB! = varptr (p%)
  21039.                  address1! = varptr (a!(0))
  21040.                  address2! = varptr (b!(0))
  21041.                  address3! = varptr (c!(0))
  21042.                  address4! = varptr (d!(0))
  21043.                  address5! = varptr (mystring$)
  21044.                  PRINT addressA!, addressB!
  21045.                  PRINT address1!, address2!, address3!, address4!, address5!
  21046.                  ***********************************************************
  21047.  
  21048.              It gives us the addresses of all sorts of things. a!() and b!()
  21049.              are static arrays, so they should be in the DS segment. c!() and
  21050.              d!() are dynamic arrays, so they might be anywhere. Remember, the
  21051.              DS segment is from offset 0 to offset 65535. Let's see where they
  21052.              ____________________
  21053.  
  21054.                 3. If you make a mistake and pass a single precision number
  21055.              instead of an integer, you will pass 4 bytes instead of 2. From
  21056.              that moment on the stack will have 2 extra bytes on it and you
  21057.              won't know where they came from.
  21058.  
  21059.  
  21060.              BASIC II - Interfacing BASIC With Assembler                mmxvii
  21061.              ___________________________________________
  21062.  
  21063.              are:
  21064.  
  21065.                   6230         6232
  21066.                   9438         6234         87616        67584        13062
  21067.  
  21068.              The individual numbers are in DS, and the two static arrays are
  21069.              in DS, but c!() and d!() are outside of DS. These numbers tell us
  21070.              the address relative to the start of DS, but we don't know where
  21071.              DS is at the moment. Where exactly are these arrays? It would be
  21072.              tedious to pass the subroutine these numbers because they are
  21073.              floating-point numbers and would be very difficult to deal with.
  21074.  
  21075.              QuickBASIC has a function called PTR86. It is in an external
  21076.              object file called INT86.OBJ.{4} This object file has the
  21077.              routines that you need if you want to do interrupts from BASIC
  21078.              itself. We'll come back to that. PTR86's job is to take the
  21079.              floating-point number which we got from VARPTR, add the starting
  21080.              address of the DS segment to get an absolute address in memory,
  21081.              and then calculate both a segment and an offset for that address
  21082.              in memory. The segment will always be the highest segment that
  21083.              contains the first byte of the variable or array and the offset
  21084.              will always be a number from 0 to 15.
  21085.  
  21086.              In order to use an object file from inside of QuickBASIC you need
  21087.              to put it in a library file and then load the library file when
  21088.              starting QuickBASIC.
  21089.  
  21090.              Building the library file is quite easy. QuickBASIC comes with a
  21091.              program called BUILDLIB.EXE which builds the library for you. For
  21092.              now, you need only INT86.OBJ and PREFIX.OBJ in your library.{5}
  21093.              Put these two things in every library that you build from now on.
  21094.              PREFIX.OBJ insures proper segment ordering in the executable
  21095.              file.
  21096.  
  21097.                  >buildlib  int86+prefix
  21098.  
  21099.              This will create a library with the default name USERLIB.EXE. To
  21100.              load a library with this default file name, just put '/l' on the
  21101.              command line:
  21102.  
  21103.                  >qb  /l
  21104.  
  21105.              If you have given the library a different name like XQRTYF.EXE,
  21106.              then put that name after the '/l':
  21107.  
  21108.                  >qb /lXQRTYF.EXE
  21109.  
  21110.              These object files will now be loaded and their subroutines will
  21111.              be usable from inside BASIC.
  21112.  
  21113.  
  21114.  
  21115.              ____________________
  21116.  
  21117.                 4. PTR86 has been replaced by VARSEG in QuickBASIC 4.0.
  21118.  
  21119.                 5. Both of these object files come with your QuickBASIC.
  21120.  
  21121.  
  21122.  
  21123.  
  21124.              The PC Assembler Tutor                                    mmxviii
  21125.              ______________________
  21126.  
  21127.              If you now load the user library along with BASIC, you can look
  21128.              at the segments and offsets of the arrays. Here is a program with
  21129.              a couple of arrays:
  21130.  
  21131.                      **********************************************
  21132.                      k% = 12000
  21133.                      DIM   array1!(k%), array2!(k%), array3!(12000)
  21134.  
  21135.                      value1! = VARPTR (array1!(0))
  21136.                      value2! = VARPTR (array2!(0))
  21137.                      value3! = VARPTR (array3!(0))
  21138.                      PRINT value1!, value2!, value3!
  21139.                      **********************************************
  21140.  
  21141.              Each array is 12001 * 4 (bytes) or 48004 bytes long. The first
  21142.              two have been defined as dynamic, so they can be anywhere in
  21143.              memory as long as they're after the start of DS. The third one is
  21144.              static, so it must be entirely in DS. Here's the output:
  21145.  
  21146.                     68032         116064        6252
  21147.  
  21148.              Remember, these offsets are not relative to the start of memory,
  21149.              they are relative to the start of the DS segment. The DS segment
  21150.              only goes up to offset 65535 so the first two are outside of DS
  21151.              while the third one is totally inside (6252 + 48004 = 54006 which
  21152.              is less than 65536). PTR86 is going to give us a SEGMENT:OFFSET
  21153.              pair relative to the start of memory.{1} The form for the call
  21154.              is:
  21155.  
  21156.                  CALL  PTR86 (segment%, offset%, value!)
  21157.  
  21158.              where value! is the number returned by VARPTR.
  21159.  
  21160.              We'll add the following code to the bottom of the above program:
  21161.  
  21162.                      ***************************************
  21163.                      CALL  PTR86 (  seg1% , off1%, value1! )
  21164.                      CALL  PTR86 (  seg2% , off2%, value2! )
  21165.                      CALL  PTR86 (  seg3% , off3%, value3! )
  21166.  
  21167.                      PRINT  seg1%, off1%
  21168.                      PRINT  seg2%, off2%
  21169.                      PRINT  seg3%, off3%
  21170.                      ***************************************
  21171.  
  21172.              What does the program print now?
  21173.  
  21174.                   68032         116064        6252
  21175.              ____________________
  21176.  
  21177.                 1. If you are using QuickBasic 4.0 or later this has become
  21178.              simplified. You can just use VARSEG and VARPTR. These will give
  21179.              you a segment offset pair which is usable in a subroutine call.
  21180.  
  21181.              ______________________
  21182.  
  21183.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  21184.  
  21185.  
  21186.              BASIC II - Interfacing BASIC With Assembler                 mmxix
  21187.              ___________________________________________
  21188.  
  21189.                   19125         0
  21190.                   22127         0
  21191.                   15263         12
  21192.  
  21193.              PTR86 has calculated the segments. The first two offsets are 0
  21194.              and the third one is 12. Even though the third array is in the DS
  21195.              segment, PTR86 has recalculated the segment to find the highest
  21196.              segment which contains the first byte of the data. If you want to
  21197.              do a little calculation, you can figure out that while this
  21198.              program was running, DS was set to segment 14873.
  21199.  
  21200.              VARPTR and PTR86 can be used to do this calculation for any array
  21201.              element, not just array(0). Here's VARPTR:
  21202.  
  21203.                      value1! = VARPTR (array1!(0))
  21204.                      value2! = VARPTR (array1!(196))
  21205.                      value3! = VARPTR (array1!(2781))
  21206.                      PRINT value1!, value2!, value3!
  21207.  
  21208.  
  21209.              And here's the output:
  21210.  
  21211.                     68032         68816         79156
  21212.  
  21213.              From now on, we will have VARPTR inside of the PTR86 call:
  21214.  
  21215.                         CALL  PTR86 (  seg1% , off1%, VARPTR (array1!(0)) )
  21216.                         CALL  PTR86 (  seg2% , off2%, VARPTR (array2!(0)) )
  21217.                         CALL  PTR86 (  seg3% , off3%, VARPTR (array3!(0)) )
  21218.  
  21219.  
  21220.              It is clearer and uses less space. Of course, we can use this for
  21221.              any element in the array, not just the beginning of the array:
  21222.  
  21223.                         CALL  PTR86 (  seg1% , off1%, VARPTR (array1!(0)) )
  21224.                         CALL  PTR86 (  seg2% , off2%, VARPTR (array1!(5076)) )
  21225.                         CALL  PTR86 (  seg3% , off3%, VARPTR (array1!(1983)) )
  21226.  
  21227.  
  21228.  
  21229.              We now have all the ammunition we need to do a quicker disk
  21230.              write. We can pass the offset address of any numeric data in the
  21231.              DS segment, we can pass the string descriptor address of any
  21232.              string, and we can pass the SEGMENT:OFFSET pair of any array
  21233.              anywhere.
  21234.  
  21235.              Before doing our disk write program, I need to say something
  21236.              about subroutine calls. You notice that when I show a subroutine
  21237.              call I am always showing what numeric type I am passing. There is
  21238.              a reason for this. Let's step back from assembler for a minute
  21239.              and do a BASIC program with a subroutine. Here's the program:
  21240.  
  21241.                  **********************************************************
  21242.                  floatA! = 27.925
  21243.                  floatB! = 16.96
  21244.                  integerA% = 300
  21245.                  integerB% = 140
  21246.  
  21247.  
  21248.              The PC Assembler Tutor                                       mmxx
  21249.              ______________________
  21250.  
  21251.                  CALL CheckTheNumbers (integerA%, integerB%, floatA!, floatB!)
  21252.                  PRINT floatA!, floatB!, integerA%, integerB%
  21253.                  END
  21254.  
  21255.                  SUB  CheckTheNumbers ( int1%, int2%, flt1!, flt2!) STATIC
  21256.                          int1% = int1% + 7
  21257.                          int2% = int2% * 45
  21258.                          flt1! = flt1! + 19.0
  21259.                          flt2! = flt2! * 43.0
  21260.                  END SUB
  21261.                  ***********************************************************
  21262.  
  21263.              This gives the following output:
  21264.  
  21265.                  46.925        729.28        307           6300
  21266.  
  21267.              This is nothing earthshaking. Now let's change one line in the
  21268.              program:
  21269.  
  21270.                  CALL CheckTheNumbers (floatA!, floatB!, integerA%, integerB%)
  21271.  
  21272.              In the call statement I have put single precision numbers where
  21273.              the integers were and integers where the single precision numbers
  21274.              were. Now let's look at the output:
  21275.  
  21276.                  Overflow.
  21277.                  ENTER to debug, SPACEBAR to edit
  21278.  
  21279.              We had an overflow. Here's where the debugger said the error was:
  21280.  
  21281.                            int2% = int2% * 45
  21282.  
  21283.              But int2% received the floating point number for floatB!, and
  21284.              that is 16.96. Since 16.96 * 45 = 763.2, where was the overflow?
  21285.              What the subroutine saw was not 16.96 but the first two binary
  21286.              bytes of floatB! (since we passed the address of floatB!). It
  21287.              thought these were an integer, performed a multiplication and got
  21288.              an error because the result was too big for an integer. Now,
  21289.              change the value in floatB! to:
  21290.  
  21291.                  floatB! = 1.696E-29
  21292.  
  21293.              and run the program again. Your results should be:
  21294.  
  21295.                   27.92501      1.693756E-29  0             16792
  21296.  
  21297.              These numbers have no relation to either what we started out with
  21298.              or what we did. Why? Because the subroutine mixed binary
  21299.              information from single precision numbers with binary information
  21300.              from integers and came up with unmitigated garbage. It was not
  21301.              only doing that, it was also writing 4 bytes of information into
  21302.              a 2 byte integer. Both flt1! and flt2! were writing past the end
  21303.              of the data and overwriting something else.
  21304.  
  21305.              There is NEVER any checking between a subroutine and the calling
  21306.              program to see that the correct numeric types are being passed
  21307.              (integers, long integers, single precision, double precision). In
  21308.  
  21309.  
  21310.              BASIC II - Interfacing BASIC With Assembler                 mmxxi
  21311.              ___________________________________________
  21312.  
  21313.              C and Pascal this checking is done by the compiler at compile
  21314.              time and the compiler will howl if you try to do something like
  21315.              this. In BASIC (my BASIC at least), this checking is not being
  21316.              done. Therefore, it is IMPERATIVE that you make sure that you
  21317.              pass the correct data types.
  21318.  
  21319.              Having given that warning, we are going to build an assembler
  21320.              subprogram that opens a file for writing, then writes a block of
  21321.              data from memory to disk. The form of the call will be:
  21322.  
  21323.              CALL BlockToDisk ("filename$"+CHR$(0), seg%, offset%, # of bytes)
  21324.  
  21325.              Notice that there MUST be a 0 after the filename. When you use
  21326.              this function, you must always have:
  21327.  
  21328.                  filename$ = "my.file.name" + CHR$(0)
  21329.  
  21330.              The disk interrupt that is used in this subroutine expects a 'C'
  21331.              string (terminated by a number 0, not an ASCII character '0'). If
  21332.              you don't do it, you will almost certainly get an error.
  21333.  
  21334.              This is followed by the segment of the first byte of data, the
  21335.              offset of the first byte of data, and the number of BYTES (not
  21336.              array elements) to write.
  21337.  
  21338.              Which way does BASIC load the arguments to a subroutine? From
  21339.              left to right, just like PASCAL. Also, in BASIC, ALL subroutine
  21340.              calls are far calls. Therefore, BASIC will do the following when
  21341.              it calls BlockToDisk:
  21342.  
  21343.                       PUSH address of file_descriptor
  21344.                       PUSH address of block segment
  21345.                       PUSH address of block offset
  21346.                       PUSH address of length
  21347.                       CALL FAR PTR BlockToDisk
  21348.  
  21349.              There are two things to notice here. First, these are all
  21350.              ADDRESSES of the data, not the data items themselves. Upon entry
  21351.              to the subroutine and initialization of BP, the stack will look
  21352.              like this:
  21353.  
  21354.                       address of file_descriptor    bp+12
  21355.                       address of block segment      bp+10
  21356.                       address of block offset       bp+8
  21357.                       address of length             bp+6
  21358.                       old CS                        bp+4
  21359.                       old IP                        bp+2
  21360.                  BP-> old BP                        bp
  21361.  
  21362.              Secondly, the name of the subroutine does not have any periods.
  21363.              BASIC allows periods '.' but does not allow underscores '_' while
  21364.              assembler allows underscores but doesn't allow periods. (Periods
  21365.              have a special meaning in assembler; they are used in
  21366.              structures).
  21367.  
  21368.              In order to go on from here, you need a book about interrupts.
  21369.              This information is from "DOS Programmer's Reference" by Terry
  21370.  
  21371.  
  21372.              The PC Assembler Tutor                                     mmxxii
  21373.              ______________________
  21374.  
  21375.              Dettmann, but if you have "The Peter Norton Programmer's Guide to
  21376.              The IBM PC", that's fine too. I'm going to give only partial
  21377.              information about these interrupts and you should have complete
  21378.              information.
  21379.  
  21380.              Here's the program. The explaination will come afterwards.
  21381.  
  21382.              *******************************************************
  21383.              ; BASOUT.ASM
  21384.              include \pushregs.mac
  21385.              PUBLIC  BLOCKTODISK
  21386.              DGROUP  GROUP _DATA
  21387.              ; - - - - - - - - - - - - - - - - - - - - -
  21388.              _DATA  SEGMENT PUBLIC 'DATA'
  21389.              file_handle     dw      ?
  21390.              error_message   db      "Disk i/o error #"
  21391.              error_byte      db      "   ", 13, 10, "$"
  21392.              _DATA  ENDS
  21393.              ; - - - - - - - - - - - - - - - - - - - - -
  21394.              _TEXT  SEGMENT 'CODE'
  21395.                      ASSUME cs:_TEXT, ds:DGROUP
  21396.              ; - - - - - - - - - - - - - - - - - - - - -
  21397.              print_error  proc far
  21398.                      mov     ah, 9           ; print error message
  21399.                      mov     dx, offset DGROUP:error_message
  21400.                      int     21h
  21401.                      ret
  21402.              print_error  endp
  21403.              ; - - - - - - - - - - - - - - - - - - - - - - - - -
  21404.              ; BlockToDisk ( filename , array SEG, array OFF, # of bytes)
  21405.              ; this is for BASIC
  21406.  
  21407.                      DESCRIPTOR_LOCATION  EQU    [bp + 12]
  21408.                      SEGMENT_LOCATION     EQU    [bp + 10]
  21409.                      OFFSET_LOCATION      EQU    [bp + 8]
  21410.                      LENGTH_LOCATION      EQU    [bp + 6]
  21411.  
  21412.              BLOCKTODISK proc far
  21413.                      push    bp
  21414.                      mov     bp, sp
  21415.                      PUSHREGS    ax, bx, cx, dx, si, ds
  21416.  
  21417.                      ; open a new file or truncate an old one
  21418.                      mov     si, DESCRIPTOR_LOCATION
  21419.                      mov     ah, 3Ch         ; open new or truncate old
  21420.                      mov     cx, 0           ; normal file attribute
  21421.                      mov     dx, [si+2]      ; [si] =length, [si+2] =location
  21422.                      int     21h
  21423.                      jnc     write_the_file  ; ok if CF=0, error if CF=1
  21424.  
  21425.                      mov     error_byte, '1' ; cannot open
  21426.                      call    print_error
  21427.                      jmp     exit
  21428.  
  21429.              write_the_file:
  21430.                      mov     file_handle, ax ; store handle for later use
  21431.                      mov     bx, ax          ; file_handle to bx
  21432.  
  21433.  
  21434.              BASIC II - Interfacing BASIC With Assembler               mmxxiii
  21435.              ___________________________________________
  21436.  
  21437.                      mov     ah, 40h         ; int 21h ah = 40h, write block
  21438.                      mov     si, LENGTH_LOCATION
  21439.                      mov     cx, [si]        ; # of bytes into CX
  21440.                      push    ds              ; save BASIC's DS
  21441.                      mov     si, OFFSET_LOCATION
  21442.                      mov     dx, [si]        ; offset to DX
  21443.                      mov     si, SEGMENT_LOCATION
  21444.                      mov     ds, [si]        ; segment to DS
  21445.                      int     21h
  21446.                      pop     ds              ; restore BASIC's DS
  21447.                      jnc     normal_exit     ; ok if CF=0, error if CF=1
  21448.                      mov     error_byte, '2' ; bad file write
  21449.                      call    print_error
  21450.  
  21451.              normal_exit:
  21452.                      mov     ah, 3Eh         ; close the file
  21453.                      mov     bx, file_handle
  21454.                      int     21h
  21455.  
  21456.              exit:
  21457.                      POPREGS    ax, bx, cx, dx, si, ds
  21458.                      mov     sp, bp
  21459.                      pop     bp
  21460.                      ret     (8)      ; pop 4 words (8 bytes off the stack)
  21461.  
  21462.              BLOCKTODISK  endp
  21463.              ; - - - - - - - - - - - - - - - - - - - - - - - - -
  21464.              _TEXT  ENDS
  21465.              END
  21466.              **************************************************************
  21467.  
  21468.              This is using the standardized segment names so the data will be
  21469.              in the DS segment. Notice the DGROUP GROUP declaration. Also
  21470.              notice that in the 'print_error' subroutine, we have done an
  21471.              offset override with 'offset DGROUP:error_message'. You need to
  21472.              do this every time to avoid errors with the offset addressing
  21473.              whenever you are using the DGROUP GROUP directive. Go back to the
  21474.              discussion of simplified segment directives if you don't remember
  21475.              this.
  21476.  
  21477.              The main program has 3 interrupts. The first one opens a new file
  21478.              or truncates an old one to zero length. The file will be usable
  21479.              for reading and/or writing:
  21480.  
  21481.                  Int  21h   function 3Ch
  21482.                  AH = 3Ch
  21483.                  CX = 0     0 indicates a normal file
  21484.                  DS:DX      address of ASCII filename (terminated by 0)
  21485.  
  21486.                  Returns:
  21487.                            AX = file handle if CF = 0
  21488.                       or:  AX = error code if CF = 1
  21489.  
  21490.              This filename can be any legitimate pathname specification, and
  21491.              must be terminated by a zero. Like all the disk interrupts we
  21492.              will see in this chapter, if there is an error, the interrupt
  21493.              will set CF = 1. Otherwise it will clear CF = 0. If CF = 0 you
  21494.  
  21495.  
  21496.              The PC Assembler Tutor                                     mmxxiv
  21497.              ______________________
  21498.  
  21499.              can go on; if CF = 1, there was an error and you need to
  21500.              terminate the subroutine and do some error reporting. The file
  21501.              handle is a number from 0 to 65535 which the operating system
  21502.              gives your program to uniquely identify that open file. There is
  21503.              no other open file in the system which has that number. Guard it
  21504.              carefully because it is your ONLY access to the file.
  21505.  
  21506.              The second interrupt writes a block of data to disk.
  21507.  
  21508.                  Int 21h   function 40h
  21509.                  AH = 40h
  21510.                  BX = file handle
  21511.                  CX = number of bytes to write
  21512.                  DS:DX = address of first byte of data
  21513.  
  21514.                  Returns:
  21515.                       AX = actual number of bytes written if CF = 0
  21516.                       AX = error code if CF = 1
  21517.  
  21518.              This too sets the carry flag if there was an error and clears it
  21519.              if there wasn't. It is limited to writing 65535 bytes at a time,
  21520.              but the largest array we can have is 65535 bytes (actually 65534
  21521.              since all data types have an even number of bytes), so this is no
  21522.              problem.
  21523.  
  21524.              The third interrupt closes the file.
  21525.  
  21526.                  Int 21h   function 3Eh
  21527.                  AH = 3Eh
  21528.                  BX = File handle
  21529.  
  21530.              Also, the print-error subroutine has an interrupt
  21531.  
  21532.                  Int 21h   function 09h
  21533.                  AH = 9
  21534.                  DS:DX = first byte of string.
  21535.  
  21536.              This string must be terminated by a dollar sign '$' (of all
  21537.              things). The message is on two lines so we can insert an error
  21538.              number into the middle of the message. This is a quick and dirty
  21539.              interrupt for string printing.
  21540.  
  21541.              All interrupt numbers and function numbers are hex. This is
  21542.              standard for interrupts. If things go wierd, always check first
  21543.              to make sure that you have a hex number and not a decimal number.
  21544.  
  21545.              The data has an 'ASSUME ds:DGROUP' statement.
  21546.  
  21547.              Like Pascal, BASIC requires that the CALLED subroutine pop the
  21548.              arguments off the stack, so we pop 4 extra words (8 extra bytes)
  21549.              with:
  21550.  
  21551.                  ret  (8)
  21552.  
  21553.              Assemble this program and put the object file in a library with
  21554.              the other object files by using BUILDLIB.EXE. Now all we need is
  21555.              a BASIC program to use this. Here it is:
  21556.  
  21557.  
  21558.              BASIC II - Interfacing BASIC With Assembler                 mmxxv
  21559.              ___________________________________________
  21560.  
  21561.  
  21562.                  ************************************************************
  21563.                  DIM   large.array! (10000)
  21564.  
  21565.                  FOR i% = 1 to 10000
  21566.                        large.array! (i%) = 2.167832E+19
  21567.                  NEXT i%
  21568.  
  21569.                  filename$ = "blocktxt.doc" + CHR$ (0)
  21570.                  length% = 40000 - 65536
  21571.                  PRINT time$
  21572.                  CALL PTR86 (segment%, offset%, VARPTR (large.array!(1)) )
  21573.                  CALL BlockToDisk ( filename$ , segment% , offset%, length% )
  21574.                  PRINT
  21575.                  PRINT time$
  21576.                  ************************************************************
  21577.  
  21578.              There is an extra PRINT statement there which I will explain
  21579.              later. We are starting at large.array!(1) because that is where
  21580.              we started with the other programs. why are we subtracting 65536?
  21581.              Because BASIC has a limit of -32768 to +32767, so we store 40000
  21582.              as its modular equivalent (mod 65536).
  21583.  
  21584.              How long does the disk write take? From 2 to 3 seconds, and much
  21585.              of that time was spent opening and truncating the file. This is
  21586.              significantly better than the other ways of doing i/o. In fact,
  21587.              the limits of this routine are the limits of your system. It is
  21588.              literally impossible to do disk i/o any faster than this.
  21589.  
  21590.              Try using a filename that doesn't have a CHR$(0) at the end. You
  21591.              should get an error message. In my BASIC, here is the output:
  21592.  
  21593.                  19:50:23
  21594.                  Disk i/o error #1
  21595.                  19:50:23
  21596.  
  21597.              Now remove that lone PRINT statement (the next to the last line).
  21598.              Here's my output:
  21599.  
  21600.                  19:50:00
  21601.                  D9:50:00 error #1
  21602.                  1
  21603.  
  21604.              For the QuickBASIC 3.0 environment, BASIC thinks that it has
  21605.              complete control of screen i/o, so it is not doing its i/o in a
  21606.              standard way and is overwriting the error message. If you are
  21607.              going to do any screen i/o from assembler, you will have to think
  21608.              of a way to live in harmony with BASIC.{2} We simply trick BASIC
  21609.              into writing an empty line where the message was. This may not
  21610.              always work correctly, especially if the window is scrolling up.
  21611.  
  21612.              ____________________
  21613.  
  21614.                 2. The easiest way to do this is to save the whole screen
  21615.              image and cursor location, do what you want using the whole
  21616.              screen, and then restore the screen and the cursor before
  21617.              returning.
  21618.  
  21619.  
  21620.              The PC Assembler Tutor                                     mmxxvi
  21621.              ______________________
  21622.  
  21623.              This whole program only involved interrupts. There is nothing
  21624.              intrinsically assembler-like in its capabilities. In fact, we'll
  21625.              do its disk read counterpart entirely in BASIC.
  21626.  
  21627.              Let's do something that requires assembler language. The BASIC
  21628.              program:
  21629.                  *******************************************
  21630.                  FOR i% = 1 to 10000
  21631.                       to.array!(i%) = from.array!(i%)
  21632.                  NEXT i%
  21633.                  *******************************************
  21634.  
  21635.              get's the job done, but is isn't all that fast. It requires about
  21636.              5.5 seconds. This is a natural for assembler. Dive down into the
  21637.              assembler level, move the string, and come back up for air. Our
  21638.              BASIC program will be:
  21639.  
  21640.                  *******************************************
  21641.                  n% = 10000
  21642.                  DIM from.array! (n%), to.array! (n%)
  21643.  
  21644.                  PRINT time$
  21645.                  FOR i% = 1 to 10000
  21646.                       to.array!(i%) = from.array!(i%)
  21647.                  NEXT i%
  21648.                  PRINT time$
  21649.                 FOR j% = 1 to 50
  21650.                  cnt% = 40000 - 65536     'count
  21651.                  CALL PTR86 (from.seg%, from.off%, VARPTR( from.array!(1)) )
  21652.                  CALL PTR86 (to.seg%, to.off%, VARPTR ( to.array!(1)) )
  21653.                  CALL BlockMove(from.seg%, from.off%, to.seg%, to.off%, cnt%)
  21654.                 NEXT j%
  21655.                  PRINT time$
  21656.                  ********************************************
  21657.  
  21658.              We are doing 50 repeats of the bottom section of code so you will
  21659.              be able to average the time. Here's the assembler program:
  21660.  
  21661.              ; - - - - - - - - - -
  21662.              include /pushregs.mac
  21663.              _TEXT SEGMENT PUBLIC 'CODE'
  21664.                      ASSUME cs:_TEXT
  21665.                      PUBLIC BlockMove
  21666.              ; - - - - - - - - - -
  21667.              ; BlockMove ( from.seg, from.off, to.seg, to.off, byte.count)
  21668.              ; for BASIC
  21669.              ; MOVSW is from DS:[SI] to ES:[DI]
  21670.  
  21671.                      FROM_SEG_ADDRESS        EQU     [bp+14]
  21672.                      FROM_OFFSET_ADDRESS     EQU     [bp+12]
  21673.                      TO_SEG_ADDRESS          EQU     [bp+10]
  21674.                      TO_OFFSET_ADDRESS       EQU     [bp+8]
  21675.                      BYTE_COUNT_ADDRESS      EQU     [bp+6]
  21676.              ; - - - - - - - - - -
  21677.              BlockMove proc far
  21678.                      push    bp
  21679.                      mov     bp, sp
  21680.  
  21681.  
  21682.              BASIC II - Interfacing BASIC With Assembler               mmxxvii
  21683.              ___________________________________________
  21684.  
  21685.                      PUSHREGS  ax, bx, cx, dx, si, di, es, ds
  21686.  
  21687.                      mov     si, TO_SEG_ADDRESS
  21688.                      mov     es, [si]        ; to_seg to ES
  21689.                      mov     si, TO_OFFSET_ADDRESS
  21690.                      mov     di, [si]        ; to_offset to DI
  21691.                      mov     si, BYTE_COUNT_ADDRESS
  21692.                      mov     cx, [si]        ; byte count to CX
  21693.                      mov     si, FROM_SEG_ADDRESS
  21694.                      mov     ax, [si]        ; temporary storage for new DS
  21695.                      mov     si, FROM_OFFSET_ADDRESS
  21696.                      mov     si, [si]        ; from_offset to SI
  21697.                      mov     ds, ax          ; now move from_seg to DS
  21698.                      sub     bx, bx          ; clear BX
  21699.                      shr     cx, 1           ; divide by 2, remainder in CF
  21700.                      rcl     bx, 1           ; move CF to low bit of BX
  21701.                      cld                     ; clear DF (go up)
  21702.                      rep     movsw           ; the block move (count in CX)
  21703.                      and     bx, bx          ; one extra byte?
  21704.                      jz      exit
  21705.                      movsb                   ; move one last byte
  21706.  
  21707.              exit:
  21708.                      POPREGS  ax, bx, cx, dx, si, di, es, ds
  21709.                      mov     sp, bp
  21710.                      pop     bp
  21711.                      ret     (10)
  21712.  
  21713.              BlockMove endp
  21714.              ; - - - - - - - - - -
  21715.              _TEXT ENDS
  21716.              END
  21717.              ; - - - - - - - - - -
  21718.  
  21719.              This is a string block move using MOVSW. The count is the number
  21720.              of BYTES, not the number of array elements. CX contains the byte
  21721.              count. It is divided by 2 so we can move words, and if there is a
  21722.              remainder (i.e. if the number was odd), BX is set to 1. We move
  21723.              words instead of bytes, and afterwards we check BX to see if we
  21724.              need to move 1 byte more. This routine takes about 1/8 second
  21725.              instead of 5.5 seconds. This is a considerable savings in time.
  21726.              There is a small problem, however. If the FROM block and the TO
  21727.              block overlap (e.g. move 400 bytes from array!(11) to
  21728.              array!(26)), then the data may be compromised. To be exact, if
  21729.              the start of the FROM data is below the start of the TO data, the
  21730.              data will be screwed up. The general solution of this for BASIC
  21731.              is in BLKMOVE.ASM, which is in a file called MISHMASH.DOC which
  21732.              is in \XTRAFILE.
  21733.  
  21734.              Finally, here's the disk read done entirely in BASIC. Once again
  21735.              you need your DOS interrupt book.
  21736.  
  21737.                  ********************************************************
  21738.                  ' READBLK.BAS
  21739.                  ' reads a block from the disk into memory
  21740.  
  21741.                  DIM in.regs%(9), out.regs%(9)
  21742.  
  21743.  
  21744.              The PC Assembler Tutor                                   mmxxviii
  21745.              ______________________
  21746.  
  21747.                  DIM big.array! (10000)
  21748.  
  21749.                  AX% = 0
  21750.                  BX% = 1
  21751.                  CX% = 2
  21752.                  DX% = 3
  21753.                  BP% = 4
  21754.                  SI% = 5
  21755.                  DI% = 6
  21756.                  FLGS% = 7
  21757.                  DS% = 8
  21758.                  ES% = 9
  21759.  
  21760.                  filename$ = "blocktxt.doc" + CHR$(0)
  21761.  
  21762.                  PRINT time$
  21763.                  ' open an existing file for reading
  21764.                  in.regs%(AX%) = &H3D00
  21765.                  in.regs%(DX%) = SADD (filename$)
  21766.                  CALL INT86 (&H21,VARPTR (in.regs%(0)),VARPTR (out.regs%(0)))
  21767.  
  21768.                  IF  (out.regs%(FLGS%) AND &H0001)  <> 0  THEN
  21769.                       PRINT  "Can't open the file."
  21770.                       GOTO ExitProgram
  21771.                  END IF
  21772.  
  21773.                  ' set the i/o pointer to 0
  21774.                  file.handle% = out.regs%(AX%)
  21775.                  in.regs%(AX%) = &H4200
  21776.                  in.regs%(BX%) = file.handle%
  21777.                  in.regs%(CX%) = 0
  21778.                  in.regs%(DX%) = 0
  21779.                  CALL INT86 (&H21, VARPTR(in.regs%(0)), VARPTR(out.regs%(0)))
  21780.  
  21781.                  IF  (out.regs%(FLGS%) AND &H0001)  <> 0   THEN
  21782.                       PRINT  "File pointer error"
  21783.                       GOTO  CloseFile
  21784.                  END IF
  21785.  
  21786.                  in.regs%(AX%) = &H3F00
  21787.                  in.regs%(BX%) = file.handle%
  21788.                  in.regs%(CX%) = 40000 - 65536
  21789.                  CALL PTR86 ( segment%, offset%, VARPTR (big.array!(1) ))
  21790.                  in.regs%(DX%) = offset%
  21791.                  in.regs%(DS%) = segment%
  21792.                  CALL INT86X (&H21,VARPTR(in.regs%(0)),VARPTR(out.regs%(0)))
  21793.                  IF  (out.regs%(FLGS%) AND &H0001)  <> 0  THEN
  21794.                       PRINT  "Disk read error"
  21795.                  END IF
  21796.  
  21797.  
  21798.              CloseFile:
  21799.                  in.regs%(AX%) = &H3E00
  21800.                  in.regs%(BX%) = file.handle%
  21801.                  CALL INT86 (&H21, VARPTR(in.regs%(0)), VARPTR(out.regs%(0)))
  21802.  
  21803.                  PRINT time$
  21804.  
  21805.  
  21806.              BASIC II - Interfacing BASIC With Assembler                mmxxix
  21807.              ___________________________________________
  21808.  
  21809.  
  21810.              ExitProgram:
  21811.                  END
  21812.                  ******************************************************
  21813.  
  21814.              This shows the use of INT86 in BASIC. We have two 10 element
  21815.              integer arrays (0 - 9). One is used for putting the data into the
  21816.              registers before the interrupt call and the other is used for
  21817.              getting the data out of the registers after the interrupt. At the
  21818.              top we have substituted variable names for the array elements
  21819.              they represent. This is the only way to make sense of things in
  21820.              BASIC. What is the difference between INT86 and INT86X? INT86X
  21821.              also changes ES and DS.
  21822.  
  21823.              We need a different file opening call because the last one
  21824.              TRUNCATED the file. This one just opens a pre-existing file and
  21825.              we put 00 in AL to signal a file read:
  21826.  
  21827.                  INT 21h   Function 3Dh
  21828.                  AH = 3dh  ; open a pre-existing file
  21829.                  AL = 0    ; file read
  21830.                  DS:DX     ; pointer to a 00h terminated string
  21831.  
  21832.                  Returns
  21833.                       AX = file handle if CF = 0
  21834.                       AX = error code if CF = 1
  21835.  
  21836.              We use SADD to get the filename offset in DS because this is
  21837.              exactly what DOS wants. SADD is a function that gives the offset
  21838.              of a string (the string itself) relative to the DS segment. It
  21839.              gives no length information.
  21840.  
  21841.              At every step along the way we check CF to make sure it is 0 and
  21842.              not 1. We need to make sure that the file pointer is at the
  21843.              beginning of the file. This is:
  21844.  
  21845.                  INT 21h   Function 42h
  21846.                  AH = 42h  ; move file pointer
  21847.                  AL = 0    ; count from beginning of the file
  21848.                  CX:DX = 0 ; 4 byte offset from beginning of file
  21849.  
  21850.                  Returns
  21851.                       DX:AX = new file-pointer location if CF = 0
  21852.                       AX = error code if CF = 1
  21853.  
  21854.              Then we do the block read:
  21855.  
  21856.                  INT 21h   Function 3Fh
  21857.                  AH = 3Fh  ; read a block from disk
  21858.                  BX = file handle
  21859.                  CX = byte count
  21860.                  DS:DX = pointer to first byte of block
  21861.  
  21862.                  Returns
  21863.                       AX = # of bytes read (can be less than CX) if CF = 0
  21864.                       AX = error code if CF = 1
  21865.  
  21866.  
  21867.  
  21868.              The PC Assembler Tutor                                      mmxxx
  21869.              ______________________
  21870.  
  21871.              Finally, we close the file:
  21872.  
  21873.                  INT 21h   Function 3Eh
  21874.                  AH = 3Eh  ; close a file
  21875.                  BX = file handle
  21876.  
  21877.                  Returns
  21878.                       nothing if CF = 0 (close was successful)
  21879.                       AX = error code if CF = 1
  21880.  
  21881.              All you need to know about CF is that when the flags are
  21882.              represented as a word (2 bytes) CF = &H0001.
  21883.  
  21884.              Is this faster than our other access methods? Our worst case
  21885.              before took 79 seconds and this one takes 1 second. This is
  21886.              certainly worth using for large disk reads. We don't need to go
  21887.              down to the assembler level, either.
  21888.  
  21889.              What's the difference between this and bload? Bload requires that
  21890.              the file has already been stored from memory. When you use BSAVE,
  21891.              the binary information is written to disk, but the first seven
  21892.              bytes is BLOAD information. The first byte (0FDh) is a signature
  21893.              byte. The next six bytes are three words. (1) the segment where
  21894.              the data came from, (2) the offset where the data came from, and
  21895.              (3) the length of the data. Of course, having these seven bytes
  21896.              in the front makes the file incompatible with everything else in
  21897.              the world. It even makes it difficult to load the information
  21898.              into BASIC the first time, since this seven byte header is
  21899.              missing in the original data unless the data came from a BASIC
  21900.              file.
  21901.  
  21902.  
  21903.  
  21904.              So what sorts of things are candidates for assembler subroutines?
  21905.              Things that are cumbersome in BASIC. If you want the top byte of
  21906.              a number, that's difficult. If you want to rotate the bits of a
  21907.              number, that's extremely hard. Shifting bits is hard. Practically
  21908.              everything involving unsigned numbers is problematic. For every
  21909.              assembler instruction, if you can't do it easily in BASIC you
  21910.              should make a subroutine that does it in assembler. How about one
  21911.              that does unsigned division? Another subroutine that you might
  21912.              want to make is one that returns both the quotient and the
  21913.              remainder from signed division. This cuts the work in half if you
  21914.              need both of them.
  21915.  
  21916.              Well, use BASIC any way you want, but most of all, have fun!
  21917.  
  21918.  
  21919.  
  21920.  
  21921.  
  21922.  
  21923.  
  21924.  
  21925.  
  21926.  
  21927.  
  21928.  
  21929.  
  21930.              BASIC II - Interfacing BASIC With Assembler                mmxxxi
  21931.              ___________________________________________
  21932.  
  21933.  
  21934.                                      SUMMARY
  21935.  
  21936.              BASIC strings are defined by STRING DESCRIPTORS. A string
  21937.              descriptor is a 4 byte block that contains the LENGTH of the
  21938.              string and its LOCATION in the DS segment. Though you may  modify
  21939.              individual bytes of a string from the assembler level, you may
  21940.              not alter the length without interfering with BASIC's memory
  21941.              management system.
  21942.  
  21943.              BASIC passes all arguments by reference. That is it sends the
  21944.              offset address of the data instead of the data itself. The ruiles
  21945.              are:
  21946.  
  21947.                  1) If it is a single piece of numeric data, the offset is
  21948.                  relative to the DS segment.
  21949.  
  21950.                  2) If it is a string, the address is the address of the
  21951.                  STRING DESCRIPTOR which contains both the length and
  21952.                  location of the string.
  21953.  
  21954.                  3) To reference an array, use VARPTR (array(0)) to get the
  21955.                  offset and then PTR86 to convert this to a SEGMENT:OFFSET
  21956.                  pair which is usable by the assembler subroutine.
  21957.  
  21958.                  4) If you pass a single array element array(x) instead of
  21959.                  using VARPTR, BASIC will pass the location of that element,
  21960.                  but the element might be separated from the rest of the
  21961.                  array, so only pass an individual element if you want the
  21962.                  element itself and not the array.{3}
  21963.  
  21964.              SADD (stringname$)
  21965.                  SADD [Microsoft's string address function] is used to pass
  21966.                  the offset address of stringname$ relative to BASIC's DS
  21967.                  segment. It should only be used with 00h terminated strings
  21968.                  since this gives no length information. It can, however, be
  21969.                  used in conjunction with LEN (stringname$).
  21970.  
  21971.              number! = VARPTR (variable)
  21972.                  In older BASICs, VARPTR gives the offset address of
  21973.                  "variable" relative to the first byte of the DS segment.
  21974.                  This variable can be anywhere in memory from DS:0000 to the
  21975.                  end of memory, and the number returned will be a single
  21976.                  precision number in the range 0 to 1,048,576.
  21977.  
  21978.              VARSEG and VARPTR
  21979.                  In more recent BASICs, using a combination of VARSEG and
  21980.              ____________________
  21981.  
  21982.                 3. The rule here is that if the array itself is outside of the
  21983.              DS segment (if it is a dynamic array), BASIC will make a copy of
  21984.              the element inside of DS before the CALL, give you the address of
  21985.              the COPY, and return the copy to its appropriate place in the
  21986.              array after the CALL. This copy can be hundreds of thousands of
  21987.              bytes away from the actual array. If you want the element itself
  21988.              this works properly, but if you want the array, the address will
  21989.              be the wrong address.
  21990.  
  21991.  
  21992.              The PC Assembler Tutor                                    mmxxxii
  21993.              ______________________
  21994.  
  21995.                  VARPTR has supplanted the use of PTR86.
  21996.  
  21997.              PTR86 ( segment%, offset%, VARPTR (variable) )
  21998.                  PTR86 [Microsoft's segmentation scheme] takes the result
  21999.                  provided by VARPTR and adds it to DS to come up with a total
  22000.                  address. It then converts this absolute address into a
  22001.                  SEGMENT:OFFSET pair where the segment is the highest segment
  22002.                  that contains the first byte of the variable from VARPTR and
  22003.                  the offset is a number from 0 to 15 which is the offset of
  22004.                  this variable in this segment.
  22005.  
  22006.              RET (x)
  22007.                  When executing a return, all called subroutines must pop the
  22008.                  arguments passed to them by BASIC. The number of BYTES
  22009.                  popped is twice the number of arguments (as long as you are
  22010.                  passing addresses and not actual data).
  22011.  
  22012.              CALL MY_ROUTINE (arg1, arg2, arg3, etc)
  22013.                  Arguments are always PUSHed on the stack from left to right,
  22014.                  so this call will:
  22015.  
  22016.                       PUSH address of arg1
  22017.                       PUSH address of arg2
  22018.                       PUSH address of arg3
  22019.                       etc.
  22020.  
  22021.                  in that order.
  22022.  
  22023.              INT86 ( interrupt.number%, in.reg.array%(9), out.reg.array%(9) )
  22024.                  INT86 executes a DOS interrupt (interrupt.number%). The
  22025.                  integers in in.reg.array% are put into the arithmetic
  22026.                  registers before the call and the arithmetic registers are
  22027.                  put into out.reg.array after the call. INT86X does the same
  22028.                  thing but also changes the DS and ES segment registers.
  22029.                  Consult your BASIC manual for the proper ordering of the
  22030.                  registers in the array. Neither of these changes CS, SS or
  22031.                  SP.
  22032.  
  22033. The PC ASSEMBLER Helper
  22034. =======================
  22035.  
  22036.                                                                              i
  22037.  
  22038.  
  22039.  
  22040.              "The PC Assembler Helper" is an object module (ASMHELP.OBJ) which
  22041.              is designed as a companion for "The PC Assembler Tutor". It can
  22042.              also be used as an aid in the development of assembler programs
  22043.              and subprograms. It allows for input to and output from the
  22044.              assembler level, as well as displaying the data in all the 8086
  22045.              registers. Part 1 will give a discription of all callable
  22046.              subroutines and part 2 will explain how to correctly link a
  22047.              program to ASMHELP.OBJ.
  22048.  
  22049.  
  22050.              THE SUBROUTINES
  22051.  
  22052.              There are a number of routines for displaying data and for
  22053.              inputting data. They all follow a standard format.
  22054.  
  22055.                  (1) All subroutines which display output on the monitor at
  22056.                  the current cursor position start with the word "print_". On
  22057.                  the screen, all hex output is followed by an 'H'. All signed
  22058.                  output has a + or a -. Unsigned output has no sign. All
  22059.                  binary output is distinguishable because it is either 8
  22060.                  digits or 16 digits long. ASCII output is followed by a
  22061.                  single asterisk '*' under normal conditions. If one or more
  22062.                  of the ASCII characters cannot be printed (i.e. if it is
  22063.                  less than 33d or it is 127d or 255d){1} then it will be
  22064.                  displayed as a two digit hex number instead of a single
  22065.                  character. A double asterisk '**' is then used to signal the
  22066.                  presence of a hex number in an ASCII format. The only time
  22067.                  there might be confusion is if one of the characters is also
  22068.                  an asterisk.
  22069.  
  22070.                  (2) All subroutines which get input from the keyboard start
  22071.                  with the word "get_". They all display prompts to tell you
  22072.                  what kind of input is desired.
  22073.  
  22074.                  (3) One byte input or output is passed through register AL.
  22075.                  One word (two byte) input or output is passed through
  22076.                  register AX. Any input or output that is longer than two
  22077.                  bytes is passed by reference. The offset address of the data
  22078.                  is put into AX before calling the subroutine.
  22079.  
  22080.              Each subroutine returns with all 8086 registers (including the
  22081.              flags register) unchanged from when the subroutine was called.
  22082.              As an example, if "variable3" is the name of a variable (in
  22083.              memory) which we want to print as a signed number, the proper way
  22084.              to set up for the calls is:
  22085.  
  22086.  
  22087.              ____________________
  22088.  
  22089.                 1 In this chapter, as in all others, 'd' stands for decimal,
  22090.              'h' for hex.
  22091.  
  22092.              ______________________
  22093.  
  22094.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  22095.  
  22096.  
  22097.              The PC Assembler Tutor                                         ii
  22098.              ______________________
  22099.  
  22100.              ONE BYTE:
  22101.                  mov  al, variable3
  22102.                  call print_signed_byte
  22103.  
  22104.              ONE WORD:
  22105.                  mov  ax, variable3
  22106.                  call print_signed
  22107.  
  22108.              FOUR BYTES:
  22109.                  lea  ax, variable3
  22110.                  call print_signed_4byte
  22111.  
  22112.              EIGHT BYTES:
  22113.                  lea  ax, variable3
  22114.                  call print_signed_8byte
  22115.  
  22116.              For a byte or a word we use the data itself, while for data
  22117.              larger than a word we pass the data by reference. The same
  22118.              applies for getting input. Here are the subroutines:
  22119.  
  22120.              get_num
  22121.                  Enters a number 5 digits or less, either signed or unsigned.
  22122.                  It does no checking to see if the number is too positive or
  22123.                  too negative. Returns a two byte value in AX.
  22124.  
  22125.              print_num
  22126.                  Prints a word value (from AX) in its signed, unsigned, hex,
  22127.                  ASCII, and binary representation.
  22128.  
  22129.  
  22130.              All the other input routines do strict error checking. If an
  22131.              illegal character is entered or if the input is out of range, the
  22132.              subroutine will ask for new input until it receives valid input.
  22133.  
  22134.  
  22135.  
  22136.              get_string
  22137.                  Enters a string of 79 characters or less, and puts a 00h at
  22138.                  the end of the string (a C string with a maximum total
  22139.                  length of 80 bytes). The address of the string must be in AX
  22140.                  before making the call.
  22141.  
  22142.              print_string
  22143.                  Displays a 00h terminated string (a C string) on the
  22144.                  monitor. The address of the first byte of the string must be
  22145.                  in AX before making the call.
  22146.  
  22147.  
  22148.  
  22149.              get_ascii_byte
  22150.                  Enters a single ascii character and returns it in AL.
  22151.  
  22152.              print_ascii_byte
  22153.                  Displays one byte (from AL) as an ascii character.
  22154.  
  22155.              get_ascii
  22156.                  Enters one or two characters and returns it (them) in AX.
  22157.  
  22158.  
  22159.              Appendix I - The PC Assembler Helper                          iii
  22160.              ____________________________________
  22161.  
  22162.  
  22163.              print_ascii
  22164.                  Displays the value in AX as two characters.
  22165.  
  22166.  
  22167.  
  22168.              get_bcd
  22169.                  Enters a one to eighteen digit signed number as a 10 byte
  22170.                  bcd number. Commas are allowed. The address of the bcd
  22171.                  variable must be in AX before calling the routine.
  22172.  
  22173.              print_bcd
  22174.                  Displays a 10 byte bcd number as a one to eighteen digit
  22175.                  signed number with commas. The address of the bcd number
  22176.                  must be in AX before calling the subroutine.
  22177.  
  22178.  
  22179.  
  22180.              get_binary_byte
  22181.                  Enters a one to eight digit binary number and returns it in
  22182.                  AL.
  22183.  
  22184.              print_binary_byte
  22185.                  Displays a one byte number (from AL) as an eight digit
  22186.                  binary number.
  22187.  
  22188.              get_binary
  22189.                  Enters a one to sixteen digit binary number and returns it
  22190.                  in AX.
  22191.  
  22192.              print_binary
  22193.                  Displays a one word number (from AX) as a sixteen digit
  22194.                  binary number.
  22195.  
  22196.  
  22197.  
  22198.              get_hex_byte
  22199.                  Enters a one or two digit hex number and returns it in AL.
  22200.  
  22201.              print_hex_byte
  22202.                  Displays a one byte number (in AL) as two hex digits.
  22203.  
  22204.              get_hex
  22205.                  Enters a one to four digit hex number and returns it in AX.
  22206.  
  22207.              print_hex
  22208.                  Displays a one word number (from AX) as a four digit hex
  22209.                  number.
  22210.  
  22211.  
  22212.  
  22213.              get_signed_byte
  22214.                  Enters a one byte signed number (-128 to +127) and returns
  22215.                  it in AL.
  22216.  
  22217.              print signed_byte
  22218.                  Displays a one byte number (from AL) as a signed number
  22219.  
  22220.  
  22221.              The PC Assembler Tutor                                         iv
  22222.              ______________________
  22223.  
  22224.                  (-128 to +127).
  22225.  
  22226.              get_signed
  22227.                  Enters a one word signed number (-32768 to +32767) and
  22228.                  returns it in AX.
  22229.  
  22230.              print_signed
  22231.                  Displays a one word number (from AX) as a signed number
  22232.                  (-32768 to +32767).
  22233.  
  22234.              get_signed_4byte
  22235.                  Enters a 4 byte signed number (-2,147,483,648 to
  22236.                  +2,147,483,647). Commas are allowed. The address of the 4
  22237.                  byte number must be in AX before calling the subroutine.
  22238.  
  22239.              print_signed_4byte
  22240.                  Displays a 4 byte signed number (-2,147,483,648 to
  22241.                  +2,147,483,647) with commas. The address of the 4 byte
  22242.                  number must be in AX before calling the subroutine.
  22243.  
  22244.              get_signed_8byte
  22245.                  Enters an 8 byte signed number (-9,223,372,036,854,775,808
  22246.                  to +9,223,372,036,854,775,807). Commas are allowed. The
  22247.                  address of the 8 byte number must be in AX before calling
  22248.                  the subroutine. The screen prompt will show the last 3
  22249.                  negative digits as -807 instead of -808, but this is because
  22250.                  of lack of screen space.
  22251.  
  22252.              print_signed_8byte
  22253.                  Displays an 8 byte signed number (-9,223,372,036,854,775,808
  22254.                  to +9,223,372,036,854,775,807) with commas. The address of
  22255.                  the 8 byte number must be in AX before calling the
  22256.                  subroutine.
  22257.  
  22258.  
  22259.  
  22260.              get_unsigned_byte
  22261.                  Enters a one byte unsigned number (0 to 255) and returns it
  22262.                  in AL.
  22263.  
  22264.              print_unsigned_byte
  22265.                  Displays a one byte number (from AL) as an unsigned number
  22266.                  (0 to 255).
  22267.  
  22268.              get_unsigned
  22269.                  Enters a one word unsigned number (0 to 65535) and returns
  22270.                  it in AX.
  22271.  
  22272.              print_unsigned
  22273.                  Displays a one word number (from AX) as an unsigned number
  22274.                  (0 to 65535).
  22275.  
  22276.              get_unsigned_4byte
  22277.                  Enters a 4 byte unsigned number (0 to 4,294,967,295). Commas
  22278.                  are allowed. The address of the 4 byte number must be in AX
  22279.                  before calling the subroutine.
  22280.  
  22281.  
  22282.  
  22283.              Appendix I - The PC Assembler Helper                            v
  22284.              ____________________________________
  22285.  
  22286.              print_unsigned_4byte
  22287.                  Displays a 4 byte unsigned number (0 to 4,294,967,295) with
  22288.                  commas. The address of the 4 byte number must be in AX
  22289.                  before calling the subroutine.
  22290.  
  22291.              get_unsigned_8byte
  22292.                  Enters an 8 byte unsigned number (0 to
  22293.                  18,446,744,073,709,551,615). Commas are allowed. The address
  22294.                  of the 8 byte number must be in AX before calling the
  22295.                  subroutine.
  22296.  
  22297.              print_unsigned_8byte
  22298.                  Displays an 8 byte unsigned number (0 to
  22299.                  18,446,744,073,709,551,615) with commas. The address of the
  22300.                  8 byte number must be in AX before calling the subroutine.
  22301.  
  22302.  
  22303.              Some of the above routines allow commas to be used for data
  22304.              entry. These routines strip the commas before looking at the
  22305.              number, so the following numbers all give the equivalent input:
  22306.  
  22307.                       2134875
  22308.                       2,134,875
  22309.                       21,,,34875
  22310.                       2,1,3,4,8,7,5
  22311.                       21,34,87,5
  22312.  
  22313.              show_regs
  22314.                  Displays the 8086 registers on the top of the screen. Each
  22315.                  call to show_regs increments a resettable counter so you can
  22316.                  know where you are in the program. The counter starts with
  22317.                  an initial value of 0. It also scrolls the screen up if the
  22318.                  cursor is past line 19.
  22319.  
  22320.              show_regs_and_wait
  22321.                  The same as show_regs except that it waits for you to press
  22322.                  ENTER before continuing.
  22323.  
  22324.              set_count
  22325.                  Sets the counter in show_regs to the value in AX. The new
  22326.                  counter value (incremented by 1) will appear the next time
  22327.                  show_regs is called.
  22328.  
  22329.              set_blue
  22330.                  If you have a color monitor which is displaying color text,
  22331.                  it sets the background to blue.
  22332.  
  22333.              get_continue
  22334.                  Waits for you to press ENTER before continuing the program.
  22335.                  It enters no data. See also set_timer below.
  22336.  
  22337.              set_timer
  22338.                  In order to allow use with a debugger, it is possible to set
  22339.                  a timer so the print functions keep control of the screen
  22340.                  for a specific amount of time. To use set_timer, you put a
  22341.                  number from 1 to 5 in AL, and call set_timer. This will
  22342.                  cause a 1 to 5 second delay every time a print function or
  22343.  
  22344.  
  22345.              The PC Assembler Tutor                                         vi
  22346.              ______________________
  22347.  
  22348.                  show_regs is called. A 0 in AL will reset the timer to 0. A
  22349.                  number larger than 5 in AL will cause ASMHELP to wait for
  22350.                  you to press the ENTER key every time a print function or
  22351.                  show_regs is called.
  22352.  
  22353.              kill_timer
  22354.                  Resets the timer to 0.
  22355.  
  22356.              set_reg_style
  22357.                  Sets the display style of the individual registers. The
  22358.                  correct order of the definition is:
  22359.  
  22360.                       AX, BX, CX, DX, SI, DI, BP, SP
  22361.  
  22362.                  The style values are:
  22363.  
  22364.                            FULL REGISTER OR RIGHT HALF REGISTER
  22365.  
  22366.                            signed    = 1d           1h
  22367.                            unsigned  = 2d           2h
  22368.                            binary    = 3d           3h
  22369.                            hex       = 4d           4h
  22370.                            ascii     = 5d           5h
  22371.  
  22372.                  plus (if applicable)
  22373.  
  22374.                            LEFT HALF REGISTER AND HALF REG. BIT
  22375.  
  22376.                            signed    = 144d         90h
  22377.                            unsigned  = 160d         A0h
  22378.                            binary    = 176d         B0h
  22379.                            hex       = 192d         C0h
  22380.                            ascii     = 208d         D0h
  22381.  
  22382.  
  22383.                  set_reg_style makes a COPY of the information, which is used
  22384.                  the next (and subsequent) times that show_regs is called.
  22385.                  The next several paragraphs explain how this works. AX must
  22386.                  contain the address of the 8 byte style definition array
  22387.                  before calling set_reg_style.
  22388.  
  22389.  
  22390.              REGISTER DISPLAY STYLE
  22391.  
  22392.              The 8086 contains a number of different registers. Some of these
  22393.              always contain addresses and are always displayed in hex. These
  22394.              are CS, DS, ES, SS and IP. They are always in hex and cannot be
  22395.              changed. Also, each flag has an unchangeable style which will be
  22396.              explained later.
  22397.  
  22398.              This still leaves us with AX, BX, CX, DX, SI, DI, BP and SP. Any
  22399.              of these registers can be displayed in any style desired. In
  22400.              addition, AX, BX, CX and DX can each be broken into two half
  22401.              registers, and each half register has an independent style. The
  22402.              default style is full register, unsigned. If you call show_regs
  22403.              without setting a style, the eight abovementioned registers will
  22404.              all display full, unsigned numbers.
  22405.  
  22406.  
  22407.              Appendix I - The PC Assembler Helper                          vii
  22408.              ____________________________________
  22409.  
  22410.  
  22411.              In order to change the style, you need to set up an 8 byte style
  22412.              definition array in your data segment. The correct definition for
  22413.              this is the following:
  22414.  
  22415.              ax_byte  db   2
  22416.              bx_byte  db   2
  22417.              cx_byte  db   2
  22418.              dx_byte  db   2
  22419.              si_byte  db   2
  22420.              di_byte  db   2
  22421.              bp_byte  db   2
  22422.              sp_byte  db   2
  22423.  
  22424.              This is the order that ASMHELP.OBJ expects. It is also the order
  22425.              that the registers appear on the screen. The number 2 is the
  22426.              default value for each byte. Once you have defined the 8 bytes,
  22427.              you may alter any of them that you want to.
  22428.  
  22429.              The possible styles are signed, unsigned, binary, hex, and ascii.
  22430.              If you look at a byte:
  22431.  
  22432.                  76543210
  22433.                  HLLL0RRR
  22434.  
  22435.              the leftmost bit (80h or 128d) signals a half or a full register.
  22436.              If it is 1, you get a half register, if it is 0, you get a full
  22437.              register. bits 4,5 and 6 represent the left half register (if
  22438.              appropriate), while bits 0,1 and 2 represent either the full
  22439.              register or the right half register. For SI, DI, BP and SP, only
  22440.              bits 0, 1 and 2 are significant. For AX, BX, CX and DX, all bits
  22441.              except bit 3 are significant.
  22442.  
  22443.              The code for bits 0, 1 and 2 is the following:
  22444.  
  22445.                  signed    = 1d           1h
  22446.                  unsigned  = 2d           2h
  22447.                  binary    = 3d           3h
  22448.                  hex       = 4d           4h
  22449.                  ascii     = 5d           5h
  22450.  
  22451.              This is the correct code for either a full register or the right
  22452.              half register.
  22453.  
  22454.              If you want half registers, you must add 80h (128d) and the code
  22455.              for the left half register. We have for the left half register:
  22456.  
  22457.                                DECIMAL                 HEX
  22458.  
  22459.                  signed    = 16d  + 128d            10h + 80h
  22460.                  unsigned  = 32d  + 128d            20h + 80h
  22461.                  binary    = 48d  + 128d            30h + 80h
  22462.                  hex       = 64d  + 128d            40h + 80h
  22463.                  ascii     = 80d  + 128d            50h + 80h
  22464.  
  22465.              which is the same as the above codes but shifted left 4 bits.
  22466.              Since the 128d and the left half register code always appear in
  22467.  
  22468.  
  22469.              The PC Assembler Tutor                                       viii
  22470.              ______________________
  22471.  
  22472.              tandem, we may simply add them together. This gives us:
  22473.  
  22474.                            DECIMAL        HEX
  22475.  
  22476.                  signed    = 144d         90h
  22477.                  unsigned  = 160d         A0h
  22478.                  binary    = 176d         B0h
  22479.                  hex       = 192d         C0h
  22480.                  ascii     = 208d         D0h
  22481.  
  22482.              Here are some examples:
  22483.  
  22484.                  signed left + hex right            = 144 + 4 = 148
  22485.                  ascii left  + unsigned right       = 208 + 2 = 210
  22486.                  binary left + ascii right          = 176 + 5 = 181
  22487.  
  22488.              Simply move the constant to the appropriate byte:
  22489.  
  22490.                  mov  cx_byte, 210        ; ascii left, unsigned right
  22491.                  mov  ax_byte, 5          ; full register, ascii
  22492.                  mov  si_byte, 1          ; full register signed
  22493.                  mov  dx_byte, 148        ; signed left, hex right
  22494.                  mov  bx_byte, 4          ; full register, hex
  22495.  
  22496.              After you have changed all the styles you want to, put the
  22497.              address of ax_byte (the first byte of the array) in ax and call
  22498.              set_reg_style:
  22499.  
  22500.                  lea  ax, ax_byte
  22501.                  call set_reg_style
  22502.  
  22503.              ASMHELP.OBJ uses the address in AX and stores a COPY of those 8
  22504.              bytes. The next time you call show_regs, it will use this copy to
  22505.              define the styles. If you make a mistake, it will default to
  22506.              unsigned.
  22507.  
  22508.              Registers are displayed the same way as with the 'print_'
  22509.              subroutines. Unsigned numbers have no sign. Signed numbers have a
  22510.              + or -, hex has an 'H', binary is either 8 or 16 digits, and
  22511.              ascii has a single asterisk '*' unless one of the characters is
  22512.              not printable ( 00d to 32d, 127d and 255d) in which case the non-
  22513.              printable character will be written as a two digit hex number,
  22514.              and a double asterisk will signal the event.
  22515.  
  22516.  
  22517.  
  22518.              Finally, the flags are displayed as follows:
  22519.  
  22520.              OF, IEF, TF, ZF, AF, and CF are blank if they are not set and
  22521.              have an X if they are set.
  22522.  
  22523.              DF has a + or a - to indicate increment or decrement.
  22524.  
  22525.              SF has a + or a - to indicate the sign.
  22526.  
  22527.              PF has E (for even) or O (for odd).
  22528.  
  22529.  
  22530.  
  22531.              Appendix I - The PC Assembler Helper                           ix
  22532.              ____________________________________
  22533.  
  22534.  
  22535.  
  22536.              LINKING
  22537.  
  22538.              In order to use ASMHELP.OBJ you must have some standard segments
  22539.              in your program. The standard code segment is defined as:
  22540.  
  22541.                  CODESTUFF SEGMENT PUBLIC 'CODE'
  22542.  
  22543.              and the standard data segment is:
  22544.  
  22545.                  DATASTUFF SEGMENT PUBLIC 'DATA'
  22546.  
  22547.              In addition you need a stack segment:
  22548.  
  22549.                  STACKSEG SEGMENT STACK 'STACK'
  22550.  
  22551.              which should have at least 100 bytes for ASMHELP to use.
  22552.  
  22553.              ASMHELP.OBJ expects that when you call one of its routines the
  22554.              following conditions are met:
  22555.  
  22556.                  (1) It is a near call, and CS is set to the CODESTUFF
  22557.                  segment.
  22558.  
  22559.                  (2) DS is set to the DATASTUFF segment. Any data which is
  22560.                  longer than one word long and is being transferred must be
  22561.                  in the DATASTUFF segment and its offset address must be in
  22562.                  AX. For one byte or one word data, the data itself is in
  22563.                  AX/AL.
  22564.  
  22565.                  (3) There is available stack space.
  22566.  
  22567.              In order to link properly, any subroutine which is called must
  22568.              have an EXTRN statement.
  22569.  
  22570.                  EXTRN  show_regs:NEAR
  22571.  
  22572.              If the above conditions hold, then simply link your object file
  22573.              with asmhelp.obj:
  22574.  
  22575.                  C> link myfile.obj+asmhelp.obj
  22576.  
  22577.              and the subroutines will be usable.
  22578.  
  22579.  
  22580.  
  22581.              ALIASES
  22582.  
  22583.              The subroutine names were chosen so their functions would be
  22584.              completely clear. However, they tend to be long so if you use
  22585.              them with some frequency it would be easier to use aliases. Make
  22586.              a redefinition macro file and then include it at the beginning of
  22587.              the program. For instance, if you have:
  22588.  
  22589.                  call print_unsigned_8byte
  22590.  
  22591.  
  22592.  
  22593.              The PC Assembler Tutor                                          x
  22594.              ______________________
  22595.  
  22596.              you might want the EQU statement
  22597.  
  22598.                  prt_u8  EQU  print_unsigned_8byte
  22599.  
  22600.              and then rewrite the call:
  22601.  
  22602.                  call prt_u8
  22603.  
  22604.              Include redefinitions which are reasonable to you and put them in
  22605.              a file REDEF.MAC.
  22606.  
  22607.                  prt_s8    EQU  print_signed_8byte
  22608.                  get_u4    EQU  get_unsigned_4byte
  22609.                  show_rw   EQU  show_regs_and_wait
  22610.  
  22611.              Then all you need on the first line of your program is:
  22612.  
  22613.                  include REDEF.MAC
  22614.  
  22615.              and the assembler will do the work for you.
  22616.  
  22617.  
  22618.  
  22619.              MEMORY RESIDENT SHOW_REGS
  22620.  
  22621.              If you are not using a debugger, it is possible to have the
  22622.              show_regs portion of The Assembler Helper resident in memory.
  22623.              This means that once it is installed, it will stay there till you
  22624.              turn the machine off or reset the machine. The name of the
  22625.              program is HELPMEM.COM and it is in \XTRAFILE. It operates
  22626.              exactly the same way as show_regs except you get to it by using
  22627.              INT 3. At that point you can set TF to do single stepping.
  22628.  
  22629.              You load it into memory by typing:
  22630.  
  22631.                  >helpmem
  22632.  
  22633.              and it will wait for you to send interrupts.
  22634.  
  22635.              The first time you do INT 3, you will see the normal show_regs
  22636.              screen. The count is reset to 0 each time you have an INT 3.
  22637.              There will also be two menu lines:
  22638.  
  22639.              ----------
  22640.              0=clear TF ; X=set TF ; A=regs from ax ; B=set count ; C=continue
  22641.              1=ax ; 2=bx ; 3=cx ; 4=dx ; 5=si ; 6=di ; 7=bp ; 8=sp
  22642.              ----------
  22643.  
  22644.              This is pretty clear. 0 clears TF and X sets TF. If you press B
  22645.              you will be prompted for a new count number. C lets you continue.
  22646.              You can set the registers individually. Each register has a
  22647.              number; ax is 1, bx is 2, etc. When you press the number, you
  22648.              will be prompted for a HEX style code. This is the same style
  22649.              code you have been using all the time.
  22650.  
  22651.              Finally, if you want to transfer the style information from your
  22652.              program, do the following:
  22653.  
  22654.  
  22655.              Appendix I - The PC Assembler Helper                           xi
  22656.              ____________________________________
  22657.  
  22658.  
  22659.                  mov  ax, offset ax_byte
  22660.                  int  3
  22661.  
  22662.              Load the address of ax_byte in AX before the interrupt, and then
  22663.              press selection A. The 8 bytes located at the address in AX will
  22664.              be transfered to HELPMEM. The advantage of this is that you can
  22665.              run ASMHELP concurrently, and they will both show the same
  22666.              register styles.
  22667.  
  22668.  
  22669.              When you move into single step mode, you get a different prompt
  22670.              line:
  22671.  
  22672.              ----------
  22673.              0 = clear TF and continue  ;  2 = menu  ;  other keys = continue
  22674.              ----------
  22675.  
  22676.              0 clears TF, 2 sends you to the above menu, and any other key
  22677.              will continue the program with TF set.
  22678.  
  22679.  
  22680.              As is true with all debuggers, you should not single step through
  22681.              subroutine calls since the subroutine might have hundreds or
  22682.              thousands of steps. In addition, the subroutines in ASMHELP store
  22683.              the flags. This means that they may store the flags with the trap
  22684.              flag set. If you clear the trap flag while inside ASMHELP, when
  22685.              the code exits ASMHELP it will POP the flags with the trap flag
  22686.              set. If this happens, keep pressing 0 until you get out of single
  22687.              step mode.
  22688.  
  22689.              The memory resident version works fine for single stepping as
  22690.              long as there are no interrupts or subroutine calls. The
  22691.              show_regs in ASMHELP works fine inbetween subroutine calls but
  22692.              requires a lot of coding for single stepping. Therefore, a
  22693.              strategy for using these two programs in conjunction is:
  22694.  
  22695.                  1) use the same register style definition array for both
  22696.                  programs.
  22697.  
  22698.                  2) when there is only 8086 code with no interrupts or
  22699.                  subroutine calls, use INT 3 and single step.
  22700.  
  22701.                  3) When there are interrupts and subroutines interspersed
  22702.                  with the 8086 instructions, switch to ASMHELP.
  22703.  
  22704.              The screens should look the same except the count will be
  22705.              different.
  22706.  
  22707.  
  22708.  
  22709.              I/O
  22710.  
  22711.              All data i/o is done to the current screen at the current cursor
  22712.              position. As long as you are in a text mode, ASMHELP should write
  22713.              to whatever is current.
  22714.  
  22715.  
  22716.  
  22717.              The PC Assembler Tutor                                        xii
  22718.              ______________________
  22719.  
  22720.              SHOW_REGS is a different matter. The only allowed modes are 2, 3
  22721.              and 7. If you enter in modes 2, 3 or 7, it will keep the same
  22722.              mode. If it is in any other mode, the video card will be forced
  22723.              to mode 3.
  22724.  
  22725.              The memory resident version does almost the same thing. Every
  22726.              time it is called, it checks for modes 2, 3 or 7, and if the mode
  22727.              is not one of these, changes the mode to mode 3. If the mode is
  22728.              3, then HELPMEM looks at the first character on page 0 and uses
  22729.              the attribute of that character for all its display operations.
  22730.              Whatever display attribute is in the upper left hand corner of
  22731.              page 0 will be the display attribute for everything.
  22732.  
  22733.              The register display occupies the first 10 lines of page 0 of
  22734.              whatever mode you are in. This is written directly to memory and
  22735.              cannot be changed. Both versions change the page to page 0 upon
  22736.              entry. If you are on another page, after a call to show_regs you
  22737.              will be on page 0 at its current cursor position.
  22738.  
  22739.              If you use ASMHELP in a programming environment or with a
  22740.              debugger there may be conflicts as to who controls the screen
  22741.              display. HELPMEM.COM should not be used in a programming
  22742.              environment and cannot be used with a debugger, since both
  22743.              HELPMEM and the debugger try to use the same interrupts.
  22744. The 8086 Instruction Set
  22745. ========================
  22746.                                                                           xiii
  22747.  
  22748.  
  22749.  
  22750.              Before I go through the instructions I need to say something
  22751.              about the structure of the instructions. For the majority of
  22752.              instructions, two bits in the first instruction byte and the
  22753.              whole second byte determine whether it is a byte or word
  22754.              instruction and whether it is:
  22755.  
  22756.                  1. register, register
  22757.                  2. register, memory
  22758.                  3. memory, register
  22759.  
  22760.              These refer to the 8 arithmetic registers, not the segment
  22761.              registers. "Memory" is any allowable addressing mode. Therefore,
  22762.              it is simpler to abbreviate this as:
  22763.  
  22764.                  reg/mem, reg/mem
  22765.  
  22766.              This will always mean:
  22767.  
  22768.                  1. either a byte or word operation is allowed
  22769.                  2. any of the above 3 possibilities is allowed
  22770.  
  22771.              unless specifically stated otherwise. Another possibility is the
  22772.              operations with constants:
  22773.  
  22774.                  add  ax, 7
  22775.                  xor  sign_flag, 80h
  22776.  
  22777.              These follow the same form. There are two possibilities:
  22778.  
  22779.                  1. register, constant
  22780.                  2. memory, constant
  22781.  
  22782.              where this may be a byte or a word, "register" is any arithmetic
  22783.              register, and "memory" is any allowable addressing mode. These
  22784.              will be abbreviated:
  22785.  
  22786.                  reg/mem, constant
  22787.  
  22788.              If we are taliking about a segment register, they will be
  22789.              abbreviated:
  22790.  
  22791.                  segreg
  22792.  
  22793.              If anything does not follow this pattern, it will be explicitly
  22794.              stated.
  22795.  
  22796.  
  22797.              DESTINATION,SOURCE
  22798.  
  22799.              The standard way of writing instructions is with the destination
  22800.              on the left and the source on the right:
  22801.  
  22802.              ______________________
  22803.  
  22804.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  22805.  
  22806.  
  22807.              The PC Assembler Tutor                                        xiv
  22808.              ______________________
  22809.  
  22810.  
  22811.                  add  destination, source
  22812.  
  22813.              The register or memory location on the left ALWAYS gets the
  22814.              result of the operation. The thing on the right is the second
  22815.              operand (if any).
  22816.  
  22817.              ADDRESSING MODES
  22818.  
  22819.              These are the natural (default) segments of all addressing modes:
  22820.  
  22821.                  (1) DS
  22822.  
  22823.                       variable + (constant)
  22824.                       [bx] + (constant)
  22825.                       [si] + (constant)
  22826.                       [di] + (constant)
  22827.                       [bx+si] + (constant)
  22828.                       [bx+di] + (constant)
  22829.  
  22830.  
  22831.                  (2) SS
  22832.  
  22833.                       [bp] + (constant)
  22834.                       [bp+si] + (constant)
  22835.                       [bp+di] + (constant)
  22836.  
  22837.              Where the constant is optional. Segment overrides may be used.
  22838.              The segment overrides are:
  22839.  
  22840.                  SEGMENT OVERRIDE         MACHINE CODE (hex)
  22841.                       CS:                      2E
  22842.                       DS:                      3E
  22843.                       ES:                      26
  22844.                       SS:                      36
  22845.  
  22846.  
  22847.              These default segments apply to all instructions except the
  22848.              string instructions, which will be explained individually.
  22849.  
  22850.  
  22851.  
  22852.              THE INSTRUCTION SET
  22853.  
  22854.              AAA  (ascii adjust for addition) adjusts AL, assuming that it
  22855.              contains the result of a legitimate unpacked BCD addition. If the
  22856.              lower half-byte has generated a result 10 or over, it subtracts
  22857.              10, carries 1 to AH, and sets the carry flag. If the result is 9
  22858.              or less, it clears CF. In either case it zeroes the upper
  22859.              half-byte of AL.
  22860.  
  22861.  
  22862.              AAD (ascii adjust for division) PREPARES AL and AH for division.
  22863.              It assumes that AH contains the 10's digit and AL contains the
  22864.              1's digit of a two byte unpacked BCD number. It multiplies AH by
  22865.              10 and adds it to AL, thus making a single integer between 0 and
  22866.              99. It zeroes AH in preparation for unsigned division.
  22867.  
  22868.  
  22869.              Appendix II - The 8086 Instruction Set                         xv
  22870.              ______________________________________
  22871.  
  22872.  
  22873.  
  22874.              AAM (ascii adjust for multiplication) adjusts AL, assuming that
  22875.              it contains the result of a legitimate BCD multiplication. It
  22876.              divides the result by 10, putting the quotient in AH and the
  22877.              remainder in AL. If AL contains 73 before AAM, then afterwards AH
  22878.              will contain 7 and AL will contain 3
  22879.  
  22880.  
  22881.              AAS  (ascii adjust for subtraction) adjusts AL, assuming that it
  22882.              contains the result of a legitimate unpacked BCD subtraction. If
  22883.              the lower half-byte has generated a result -1 or less, it borrows
  22884.              1 from AH, adds 10 to AL, and sets the carry flag. If the result
  22885.              is 0 or more, it clears CF. In either case it zeroes the upper
  22886.              half-byte of AL.
  22887.  
  22888.  
  22889.              ADC (add with carry) adds two integers, either signed or
  22890.              unsigned, and adds in the carry from the previous arithmetic
  22891.              operation. It is used for multiple word (byte) addition.
  22892.  
  22893.                  adc  reg/mem, reg/mem
  22894.                  adc  reg/mem, constant
  22895.  
  22896.  
  22897.              ADD adds two integers, either signed or unsigned.
  22898.  
  22899.                  add  reg/mem, reg/mem
  22900.                  add  reg/mem, constant
  22901.  
  22902.  
  22903.              AND ands two bytes or words. A bit remains set only if both its
  22904.              respective source and destination bits were set.
  22905.  
  22906.                  and  reg/mem, reg/mem
  22907.                  and  reg/mem, constant
  22908.  
  22909.  
  22910.  
  22911.              CALL calls a procedure. The two forms we have used are NEAR and
  22912.              FAR calls.
  22913.  
  22914.                  call set_regs
  22915.                  call FAR PTR calculate_array
  22916.  
  22917.              There are two other forms where the addresses of the subprograms
  22918.              are in memory and changable. You should only use these forms if
  22919.              you are writing an operating system or a compiler, since they are
  22920.              an invitation to disaster.
  22921.  
  22922.  
  22923.  
  22924.              CBW sign extends the signed byte in AL to AH:AL in preparation
  22925.              for signed division. It is used alone without any register
  22926.              specification.
  22927.  
  22928.                  cbw
  22929.  
  22930.  
  22931.              The PC Assembler Tutor                                        xvi
  22932.              ______________________
  22933.  
  22934.  
  22935.  
  22936.  
  22937.              CLC clears the carry flag (CF = 0).
  22938.  
  22939.  
  22940.              CLD clears the direction flag (increments string pointers).
  22941.  
  22942.  
  22943.              CLI clears the interrupt flag (disables interrupts). All maskable
  22944.              interrupts must wait till the flag is set again before they will
  22945.              be processed.
  22946.  
  22947.  
  22948.              CMC changes the value of the carry flag. If CF =1, then CF = 0;
  22949.              if CF = 0, then CF = 1.
  22950.  
  22951.  
  22952.              CMP compares two values. It is the same as SUB except it does not
  22953.              alter the "destination" variable. Its job is to set the flags as
  22954.              if a subtraction had been performed, in preparation for a
  22955.              conditional jump instruction.
  22956.  
  22957.                  cmp  reg/mem, reg/mem
  22958.                  cmp  reg/mem, constant
  22959.  
  22960.  
  22961.  
  22962.              CMPS compares the byte (or word) at DS:[si] with the one at
  22963.              ES:[di], (calculating [si] - [di]) and sets the flags
  22964.              accordingly. It increments (or decrements) SI and DI depending on
  22965.              the setting of DF, the direction flag (by 1 for bytes and by 2
  22966.              for words). You may use CS:[si], SS:[si] or ES:[si], but you MAY
  22967.              NOT OVERRIDE ES:[di]. Notice that SI and DI are reversed from
  22968.              their position in the other string instructions.
  22969.  
  22970.                  cmpsb
  22971.                  cmpsw
  22972.                  cmps BYTE PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  22973.                  cmps WORD PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  22974.  
  22975.  
  22976.  
  22977.              CWD sign extends the signed integer in AX to DX:AX in preparation
  22978.              for signed word division. No register specification is used.
  22979.  
  22980.                  cwd
  22981.  
  22982.  
  22983.  
  22984.              DAA (decimal adjust for addition) adjusts AL, assuming that it
  22985.              contains the result of a legitimate packed BCD addition. It
  22986.              treats AL as two independent half-bytes. If the result of the
  22987.              lower half-byte is 10 or over, it subtracts 10 from the lower
  22988.              half-byte and adds the carry to the upper half byte. It then
  22989.              looks at the upper half byte. If its result is 10 or over, DAA
  22990.              subtracts 10 from the upper half byte and sets the carry flag.
  22991.  
  22992.  
  22993.              Appendix II - The 8086 Instruction Set                       xvii
  22994.              ______________________________________
  22995.  
  22996.              Otherwise the carry flag is cleared.
  22997.  
  22998.  
  22999.  
  23000.              DAS (decimal adjust for subtraction) adjusts AL, assuming that it
  23001.              contains the result of a legitimate packed BCD subtraction.It
  23002.              treats AL as two independent half-bytes. If the result of the
  23003.              lower half-byte is -1 or less, it adds 10 to the lower half-byte
  23004.              and borrows 1 from the upper half byte. It then looks at the
  23005.              upper half byte. If its result is -1 or less, DAA adds 10 to the
  23006.              upper half byte and sets the carry flag to indicate a borrow.
  23007.              Otherwise the carry flag is cleared.
  23008.  
  23009.  
  23010.  
  23011.              DEC decrements the byte or word by 1. It does not alter CF.
  23012.  
  23013.                  dec  reg/mem
  23014.  
  23015.  
  23016.  
  23017.              DIV performs unsigned division. If byte division, the unsigned
  23018.              double byte must be in AH:AL. Afterwards, AL has the quotient and
  23019.              AH has the remainder. If word division, the unsigned word must be
  23020.              in DX:AX. Afterwards, AX has the quotient, and DX the remainder.
  23021.              Division by a constant is not allowed. The DIV instruction does
  23022.              not mention DX:AX or AH:AL. They are understood.
  23023.  
  23024.                  div  reg/mem
  23025.  
  23026.  
  23027.  
  23028.              ESC (escape) signals to the 8086 that the bytes starting with the
  23029.              ESC byte are a coprocessor instruction. The 8086 will calculate a
  23030.              memory address (if appropriate) and give it to the coprocessor.
  23031.              The 8086 will then go on to the next instruction.
  23032.  
  23033.  
  23034.  
  23035.              HLT (halt) halts the machine. It can be restarted with a reset, a
  23036.              non-maskable interrupt, or (if IEF is set) with a maskable
  23037.              interrupt.
  23038.  
  23039.  
  23040.  
  23041.              IDIV performs signed integer division. For byte division, the
  23042.              value in AL must be sign extended to AH (with CBW), giving the
  23043.              double signed byte AH:AL, after division, AL contains the
  23044.              quotient and AH contains the remainder. For word division, the
  23045.              value in AX must be sign extended to DX (with CWD), giving the
  23046.              double signed word DX:AX, after division, AX contains the
  23047.              quotient and DX contains the remainder. Division by a constant is
  23048.              not allowed. IDIV cannot perform multiple word division. To do
  23049.              that, you need to make the numbers positive and use DIV,
  23050.              adjusting the signs afterwards. IDIV does not mention the AL or
  23051.              AX register, it is understood.
  23052.  
  23053.  
  23054.  
  23055.              The PC Assembler Tutor                                      xviii
  23056.              ______________________
  23057.  
  23058.                  idiv reg/mem
  23059.  
  23060.  
  23061.  
  23062.              IMUL performs signed integer multiplication. The multiplicand
  23063.              must be in AX (or AL for bytes). After the multiplication, the
  23064.              result is in DX:AX for words and AH:AL for bytes. If DX (or AH
  23065.              for bytes) contains significant information, then CF is set (=1).
  23066.              If all the information is in AX (or AL for bytes) then CF = 0.
  23067.              Multiplication by a constant is not allowed. The IMUL instruction
  23068.              does not mention AX (or AL); it is understood.
  23069.  
  23070.                  imul mem/reg
  23071.  
  23072.  
  23073.  
  23074.              IN allows you to move data from a port address to AX (for a word)
  23075.              or AL (for a byte). There are two forms:
  23076.  
  23077.                  IN   al, port_number
  23078.                  IN   al, dx
  23079.  
  23080.              Where 'port_number' is a CONSTANT that is hard coded into the
  23081.              machine instruction and is from 0 - 255. The port address in DX
  23082.              may be from 0-65535. It must be the DX register.
  23083.  
  23084.  
  23085.  
  23086.              INC increments a register or a variable by 1. It does not effect
  23087.              CF.
  23088.                  inc  reg/mem
  23089.  
  23090.  
  23091.  
  23092.              INT (interrupt) sends the program to a subprogram whose address
  23093.              is located in the interrupt vector table in low memory. For any
  23094.              interrupt number I, the 8086 goes to segment 0000, offset
  23095.              (I X 4). It loads IP with the first two bytes, then loads CS from
  23096.              the next two bytes. The next instruction executed will be at the
  23097.              new CS:IP. Before loading the new CS and IP, it pushes (1) the
  23098.              flags, (2) the old CS and (3) the old IP in that order.
  23099.  
  23100.                  int  3
  23101.                  int  21h
  23102.  
  23103.  
  23104.  
  23105.              INTO checks OF. If OF = 1, INTO generates interrupt 4. Otherwise
  23106.              it does nothing. It is used for error handling of signed numbers.
  23107.  
  23108.                  into
  23109.  
  23110.  
  23111.  
  23112.              IRET (interrupt return) is a special return instruction for
  23113.              interrupt routines. It pops (1) the old IP, (2) the old CS, and
  23114.              (3) the old flags in that order.
  23115.  
  23116.  
  23117.              Appendix II - The 8086 Instruction Set                        xix
  23118.              ______________________________________
  23119.  
  23120.  
  23121.                  iret
  23122.  
  23123.  
  23124.  
  23125.              JUMPS ---------------------------------------------------------
  23126.  
  23127.              There are two kinds of jumps. JMP is unconditional and can jump
  23128.              anywhere in the segment. All other jumps are conditional and are
  23129.              limited jumps from -128 to +127 bytes from the END of the jump
  23130.              instruction.{1}
  23131.  
  23132.  
  23133.              THESE ARE THE JUMP INSTRUCTIONS FOR SIGNED NUMBERS
  23134.  
  23135.                  jg        ; jump if greater
  23136.                  jnle      ; jump if not (less or equal)
  23137.  
  23138.                  jl        ; jump if less
  23139.                  jnge      ; jump if not (greater or equal)
  23140.  
  23141.                  je        ; jump if equal
  23142.                  jz        ; jump if zero
  23143.  
  23144.                  jge       ; jump if greater or equal
  23145.                  jnl       ; jump if not less
  23146.  
  23147.                  jle       ; jump if less or equal
  23148.                  jng       ; jump if not greater
  23149.  
  23150.                  jne       ; jump if not equal
  23151.                  jnz       ; jump if not zero
  23152.  
  23153.  
  23154.  
  23155.              THESE ARE THE JUMP INSTRUCTIONS FOR UNSIGNED NUMBERS
  23156.  
  23157.                  ja        ; jump if above
  23158.                  jnbe      ; jump if not (below or equal)
  23159.  
  23160.                  jb        ; jump if below
  23161.                  jnae      ; jump if not (above or equal)
  23162.  
  23163.                  je        ; jump if equal
  23164.                  jz        ; jump if zero
  23165.  
  23166.                  jae       ; jump if above or equal
  23167.                  jnb       ; jump if not below
  23168.  
  23169.                  jbe       ; jump if below or equal
  23170.                  jna       ; jump if not above
  23171.              ____________________
  23172.  
  23173.                 1 There is also a long jump which can jump anywhere in memory
  23174.              (from 00000 to FFFFF). Except under special circumstances, using
  23175.              this is truly lousy programming practice. The long jump is for
  23176.              the Olympics, not for quality programming.
  23177.  
  23178.  
  23179.              The PC Assembler Tutor                                         xx
  23180.              ______________________
  23181.  
  23182.  
  23183.                  jne       ; jump if not equal
  23184.                  jnz       ; jump if not zero
  23185.  
  23186.  
  23187.              THESE JUMPS CHECK A SINGLE FLAG
  23188.  
  23189.              These come in opposite pairs
  23190.  
  23191.                  jc        ; jump if the carry flag is set
  23192.                  jnc       ; jump if the carry flag is not set
  23193.  
  23194.                  jo        ; jump if the overflow flag is set
  23195.                  jno       ; jump if the overflow flag is not set
  23196.  
  23197.                  jp or jpe ; jump if parity flag is set (parity is even)
  23198.                  jnp or jpo  ;jump if parity flag is not set (parity is odd)
  23199.  
  23200.  
  23201.                  js        ; jump if the sign flag is set (negative )
  23202.                  jns       ; jump if the sign flag is not set (positive or 0)
  23203.  
  23204.  
  23205.              THIS CHECKS THE CX REGISTER
  23206.  
  23207.                  jcxz      ; jump if cx is zero
  23208.  
  23209.              This is used before entry to a loop to make sure the loop counter
  23210.              is not set to 0.
  23211.  
  23212.  
  23213.              INDIRECT JUMPS are jumps where the information for the jump is
  23214.              stored in memory (or in a register for an in-segment jump). These
  23215.              forms are dangerous and should be used only when writing things
  23216.              like multi-tasking operating systems. Have you written one
  23217.              lately?
  23218.  
  23219.              END OF JUMPS  - - - - - - - - - - - - - - - - - - - - - - -
  23220.  
  23221.  
  23222.              LAHF (load AH from flags) loads the lower half of the flags
  23223.              register into AH.
  23224.  
  23225.  
  23226.  
  23227.              LDS (load DS) loads the first two bytes of the memory address
  23228.              into the named arithmetic register and the next two bytes into
  23229.              DS. The register may be any arithmetic register, but this is
  23230.              designed for the 4 addressing registers: BX, SI, DI and BP.
  23231.  
  23232.                       lds  reg, memory_address
  23233.  
  23234.  
  23235.  
  23236.              LEA calculates an offset address and puts it in the named
  23237.              register.
  23238.  
  23239.  
  23240.  
  23241.              Appendix II - The 8086 Instruction Set                        xxi
  23242.              ______________________________________
  23243.  
  23244.                       lea  ax, [bp+si]+145
  23245.  
  23246.  
  23247.  
  23248.              LES (load ES) loads the first two bytes into the named arithmetic
  23249.              register and the next two bytes into ES. The register may be any
  23250.              arithmetic register, but this is designed for the 4 addressing
  23251.              registers: BX, SI, DI and BP.
  23252.  
  23253.                       les  reg, memory_address
  23254.  
  23255.  
  23256.  
  23257.              LOCK is for use if there is more than one 8086 operating in a
  23258.              computer. LOCK allows one 8086 to be the only one which can read
  23259.              from or write to memory during the following instruction.
  23260.  
  23261.  
  23262.  
  23263.              LODS moves a byte or word from DS:[si] to AL or AX, and
  23264.              increments (or decrements) SI depending on the setting of DF, the
  23265.              direction flag (by 1 for bytes and by 2 for words). You may use
  23266.              CS:[si], SS:[si] or ES:[si].
  23267.  
  23268.                  lodsb
  23269.                  lodsw
  23270.                  lods BYTE PTR SS:[si]         ; or CS, DS, ES:[si]
  23271.                  lods WORD PTR SS:[si]         ; or CS, DS, ES:[si]
  23272.  
  23273.  
  23274.  
  23275.              LOOP/LOOPE/LOOPNE are conditional jumps (-128 to + 127 bytes).
  23276.              They will jump back to the start of the loop (which is identified
  23277.              by a label), under the following conditions:
  23278.  
  23279.                  loop      decrement cx ; jump back if cx is not zero
  23280.                  loope     decrement cx ; jump back if cx not zero AND zf = 1
  23281.                  loopz     decrement cx ; jump back if cx not zero AND zf = 1
  23282.                  loopne    decrement cx ; jump back if cx not zero AND zf = 0
  23283.                  loopnz    decrement cx ; jump back if cx not zero AND zf = 0
  23284.  
  23285.              Here, 'e' stands for equal, 'z' is zero and 'n' is not.
  23286.  
  23287.                  loop some_label
  23288.  
  23289.  
  23290.  
  23291.              MOV is the general instruction for moving bytes or words on the
  23292.              8086. The forms are:
  23293.  
  23294.                  reg/mem, reg/mem
  23295.                  reg/mem, constant
  23296.                  segreg, reg/mem
  23297.                  reg/mem, segreg
  23298.  
  23299.              Notice that you may not (1) move a constant to a segment
  23300.              register, (2) move a segment register to another segment
  23301.  
  23302.  
  23303.              The PC Assembler Tutor                                       xxii
  23304.              ______________________
  23305.  
  23306.              register, or (3) move from memory to memory.
  23307.  
  23308.  
  23309.  
  23310.              MOVS moves a byte (or a word) from DS:[si] to ES:[di], and
  23311.              increments (or decrements) SI and DI depending on the setting of
  23312.              DF, the direction flag (by 1 for bytes and by 2 for words). You
  23313.              may use CS:[si], SS:[si] or ES:[si], but you MAY NOT OVERRIDE
  23314.              ES:[di].
  23315.  
  23316.                  movsb
  23317.                  movsw
  23318.                  movs BYTE PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  23319.                  movs WORD PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  23320.  
  23321.  
  23322.  
  23323.              MUL performs unsigned integer multiplication. The multiplicand
  23324.              must be in AX (or AL for bytes). After the multiplication, the
  23325.              result is in DX:AX (or AH:AX for bytes). If DX (or AH for bytes)
  23326.              contains significant information, then CF = 1. If all the
  23327.              significant information is in AX (or AL for bytes) then CF = 0.
  23328.              Multiplication by a constant is not allowed. The MUL instruction
  23329.              does not mention AX (or AL). It is understood.
  23330.  
  23331.                  mul  reg/mem
  23332.  
  23333.  
  23334.  
  23335.              NEG performs '0 - number' on a number and sets the flags
  23336.              accordingly.
  23337.  
  23338.                  neg  reg/mem
  23339.  
  23340.  
  23341.  
  23342.              NOP (no operation) does absolutely nothing. It can fill up space
  23343.              which was previously occupied by a different instruction.
  23344.  
  23345.  
  23346.  
  23347.              NOT performs bitwise logical NOT on a word or a byte.
  23348.  
  23349.                  not  reg/mem
  23350.  
  23351.  
  23352.  
  23353.              OR performs bitwise logical OR on a byte or word
  23354.  
  23355.                  or   reg/mem, reg/mem
  23356.                  or   reg/mem, constant
  23357.  
  23358.  
  23359.  
  23360.              OUT moves a byte (or word) from AL (or AX) to the named port.
  23361.              There are two forms:
  23362.  
  23363.  
  23364.  
  23365.              Appendix II - The 8086 Instruction Set                      xxiii
  23366.              ______________________________________
  23367.  
  23368.                  out  port_number, ax
  23369.                  out  dx, ax
  23370.  
  23371.              where 'port_number' is a CONSTANT which is coded into the machine
  23372.              code. The constant may be from 0-255. DX (and it must be DX) may
  23373.              hold a port number from 0-65535.
  23374.  
  23375.  
  23376.  
  23377.              POP pops a WORD off the stack. Technically, POP takes the word at
  23378.              SS:SP and puts it into the named register or memory location,
  23379.              then ADDS 2 to SP.
  23380.  
  23381.                  pop  mem/reg
  23382.                  pop  segreg
  23383.  
  23384.  
  23385.  
  23386.              POPF pops a WORD off the top of the stack into the flags
  23387.              register. Its technical operation is the same as for POP.
  23388.  
  23389.  
  23390.  
  23391.              PUSH pushes a WORD on the stack from the named register or memory
  23392.              location. Technically, PUSH subtracts 2 from SP, then moves the
  23393.              word to SS:SP.
  23394.  
  23395.                  push reg/mem
  23396.                  push segreg
  23397.  
  23398.  
  23399.  
  23400.              PUSHF pushes the flags register on the stack. Its technical
  23401.              operation is the same as for PUSH.
  23402.  
  23403.  
  23404.  
  23405.              RCR (rotate through carry right) and RCL (rotate through carry
  23406.              left) rotate the bits of a register or of memory data right and
  23407.              left respectively. The bit which is shoved off the register (or
  23408.              data) is placed in CF and CF is placed on the other side of the
  23409.              register (or data). There are two fixed forms. (1) rotate 1 bit
  23410.              and (2) rotate by the number in CL.
  23411.  
  23412.                  rcr  reg/mem, 1
  23413.                  rcl  reg/mem, cl
  23414.  
  23415.  
  23416.  
  23417.              REP. The string instructions may be prefixed by REP/REPE/REPNE
  23418.              which will repeat the instructions according to the following
  23419.              conditions:
  23420.  
  23421.                  rep       decrement cx ; repeat if cx is not zero
  23422.                  repe      decrement cx ; repeat if cx not zero AND zf = 1
  23423.                  repz      decrement cx ; repeat if cx not zero AND zf = 1
  23424.                  repne     decrement cx ; repeat if cx not zero AND zf = 0
  23425.  
  23426.  
  23427.              The PC Assembler Tutor                                       xxiv
  23428.              ______________________
  23429.  
  23430.                  repnz     decrement cx ; repeat if cx not zero AND zf = 0
  23431.  
  23432.              Here, 'e' stands for equal, 'z' is zero and 'n' is not. These
  23433.              repeat instructions should NEVER be used with a segment override,
  23434.              since the 8086 will forget the override if a hardware interrupt
  23435.              occurs in the middle of the REP loop.
  23436.  
  23437.                  rep    movsb
  23438.                  repe   scasb
  23439.                  repne  cmpsw
  23440.  
  23441.  
  23442.  
  23443.              RET returns from the subroutine to the calling program. The
  23444.              return will be either NEAR or FAR depending on the type of
  23445.              procedure it is in. It may take the parameters off the stack (for
  23446.              Pascal) or leave them on the stack (for C).
  23447.  
  23448.                  ret (6)        ; Pascal - take 6 bytes off the stack
  23449.                  ret            ; C - do nothing
  23450.  
  23451.  
  23452.  
  23453.              ROR (rotate right) and ROL (rotate left) rotate the bits of a
  23454.              register or memory data right and left respectively. The bit
  23455.              which is shoved off one end is moved to the other end. CF
  23456.              indicates whether the last bit moved from one end to the other
  23457.              was a 1 or a 0. There are two fixed forms. (1) rotate 1 bit and
  23458.              (2) rotate by the number in CL.
  23459.  
  23460.                  ror  reg/mem, 1
  23461.                  rol  reg/mem, cl
  23462.  
  23463.  
  23464.  
  23465.              SAHF (store AH into flags) puts AH into the low byte of the flags
  23466.              register.
  23467.  
  23468.  
  23469.  
  23470.              SAL (shift arithmetic left) and SHL (shift logical left) are
  23471.              exactly the same instruction. They move bits left. 0s are placed
  23472.              in the low bit. Bits are shoved off the register or memory data
  23473.              on the left side, and CF indicates whether the last bit shoved
  23474.              off was a 1 or a 0. It is used for multiplying an unsigned number
  23475.              by powers of 2. There are two fixed forms. (1) shift 1 bit and
  23476.              (2) shift by the number in CL.
  23477.  
  23478.                  shl  reg/mem, 1
  23479.                  sal  reg/mem, cl
  23480.  
  23481.  
  23482.  
  23483.              SAR (shift arithmetic right) shifts bits right. The high (sign)
  23484.              bit stays the same throughout the operation. Bits are shoved off
  23485.              the register or memory location on the right side. CF indicates
  23486.              whether the last bit shoved off was a 1 or a 0. It is used (with
  23487.  
  23488.  
  23489.              Appendix II - The 8086 Instruction Set                        xxv
  23490.              ______________________________________
  23491.  
  23492.              difficulty) for dividing a signed number by powers of 2. There
  23493.              are two fixed forms. (1) shift 1 bit and (2) shift by the number
  23494.              in CL.
  23495.  
  23496.                  sar  reg/mem, 1
  23497.                  sar  reg/mem, cl
  23498.  
  23499.  
  23500.  
  23501.              SBB (subtract with borrow) subtracts one integer from another and
  23502.              then subtracts 1 more if CF is set. It works with both signed and
  23503.              unsigned numbers and is used for multiple word arithmetic.
  23504.  
  23505.                  sbb  reg/mem, reg/mem
  23506.                  sbb  reg/mem, constant
  23507.  
  23508.  
  23509.  
  23510.              SCAS compares AL (or AX) to the byte (or word) pointed to by
  23511.              ES:[di], and increments (or decrements) DI depending on the
  23512.              setting of DF, the direction flag (by 1 for bytes and by 2 for
  23513.              words).  NO OVERRIDES ARE ALLOWED.
  23514.  
  23515.                  scasb
  23516.                  scasw
  23517.                  scas BYTE PTR ES:[di]    ; no override allowed
  23518.                  scas WORD PTR ES:[di]    ; no override allowed
  23519.  
  23520.  
  23521.  
  23522.              SHR (shift logical right) does the same thing as SHL but in the
  23523.              opposite direction. Bits are shifted right. 0s are placed in the
  23524.              high bit. Bits are shoved off the register or memory location on
  23525.              the right side and CF indicates whether the last bit shoved off
  23526.              was a 0 or a 1. It is used for dividing an unsigned number by
  23527.              powers of 2. There are two fixed forms. (1) shift 1 bit and (2)
  23528.              shift by the number in CL.
  23529.  
  23530.                  shr  reg/mem, 1
  23531.                  shr  reg/mem, cl
  23532.  
  23533.  
  23534.  
  23535.              STC sets the carry flag (CF = 1).
  23536.  
  23537.              STD sets the direction flag (DF = 1, which decrements).
  23538.  
  23539.              STI sets the interrupt flag (IEF = 1; interrupts enabled).
  23540.  
  23541.  
  23542.  
  23543.              STOS (store to string) moves a byte (or a word) from AL (or AX)
  23544.              to ES:[di], and increments (or decrements) DI depending on the
  23545.              setting of DF, the direction flag (by 1 for bytes and by 2 for
  23546.              words). NO SEGMENT OVERRIDES ARE ALLOWED.
  23547.  
  23548.                  stosb
  23549.  
  23550.  
  23551.              The PC Assembler Tutor                                       xxvi
  23552.              ______________________
  23553.  
  23554.                  stosw
  23555.                  stos BYTE PTR ES:[di]    ; no override allowed
  23556.                  stos WORD PTR ES:[di]    ; no override allowed
  23557.  
  23558.  
  23559.  
  23560.  
  23561.              SUB subtracts one integer from another. It works for both signed
  23562.              and unsigned numbers.
  23563.  
  23564.                  sub  reg/mem, reg/mem
  23565.                  sub  reg/mem, constant
  23566.  
  23567.  
  23568.  
  23569.              TEST performs logical AND, but does not alter the value of the
  23570.              "destination" variable. Its purpose is to set the flags for
  23571.              conditional jumps.
  23572.  
  23573.                  test reg/mem, reg/mem
  23574.                  test reg/mem, constant
  23575.  
  23576.  
  23577.  
  23578.              WAIT forces the 8086 to wait until the coprocessor is finished
  23579.              with its instruction before the 8086 can go on to the next
  23580.              instruction.
  23581.  
  23582.  
  23583.  
  23584.              XCHG exchanges the values in two registers or memory locations.
  23585.  
  23586.                  xchg reg/mem, reg/mem
  23587.  
  23588.  
  23589.  
  23590.              XLAT. If BX contains the address of a 256 byte translation table,
  23591.              then XLAT goes to (BX + AL), takes the byte there and puts it
  23592.              into AL. DS is the normal segment, but segment overrides are
  23593.              allowed.
  23594.  
  23595.  
  23596.  
  23597.              XOR performs logical exclusive OR on two numbers.
  23598.  
  23599.                  xor  reg/mem, reg/mem
  23600.                  xor  reg/mem, constant
  23601. APPENDIX III - Instruction Speed and Flags
  23602. ==========================================
  23603.  
  23604.                                                                          xxvii
  23605.  
  23606.  
  23607.  
  23608.  
  23609.              This appendix contains a list of all the 8086 instructions along
  23610.              with their relatives speed and the flags they affect. These speed
  23611.              numbers are from INTEL and are in clock ticks.{1} If you have a
  23612.              25 mhz clock, then it is doing 25 million ticks per second. If
  23613.              you have a 4.77 mhz clock, then it is doing 4.77 million ticks
  23614.              per second. Instead of calling them ticks, I'll be calling them
  23615.              clocks.
  23616.  
  23617.              In order to understand these numbers, you need a little extra
  23618.              information. In order to do an instruction, ANY microprocessor
  23619.              must:
  23620.  
  23621.                  (1) calculate any memory addresses.
  23622.                  (2) fetch the two operands (or the single operand if it is
  23623.                      an instruction like NOT).
  23624.                  (3) process the instruction and
  23625.                  (4) put the result in the destination.
  23626.  
  23627.              While (3) will require the same amount of time whether the
  23628.              operands are in memory or in registers, (1), (2) and (4) are all
  23629.              different depending on where the operands are. Some machines
  23630.              allow both operands to be in memory, but the 8086 does not.
  23631.              Therefore, (1) is either NO memory addresses (if everything is in
  23632.              registers) or ONE memory address.
  23633.  
  23634.              Calculating a memory address involves (a) calculating the offset
  23635.              from the beginning of the segment and (b) adding it to the
  23636.              segment starting address. Part (a) is different depending on the
  23637.              addressing mode. In terms of the speed of calculating (a) and
  23638.              (b), the order is:
  23639.  
  23640.                  (i)     one pointer
  23641.                  (ii)    named variable (+ constant)
  23642.                  (iii)   two pointers
  23643.                  (iv)    one pointer + constant
  23644.                  (v)     two pointers + constant
  23645.  
  23646.              The reason that (ii) contains both "variable" and "variable +
  23647.              constant" is that the addition (variable + constant) is done by
  23648.              the assembler, not the 8086. By the time the 8086 sees it, it is
  23649.              a single number. The constants in both (iv) and (v) need to be
  23650.              added by the 8086, and this takes extra time. According to INTEL,
  23651.              here is the time required for calculating all possible memory
  23652.              addresses. Note that some pointers are marginally faster than
  23653.              other pointers (this is trivial - don't worry about it).
  23654.  
  23655.              ____________________
  23656.  
  23657.                 1 All the speed numbers are from "Programmer's Pocket
  23658.              Reference Guide", (c)1980, 1982 Intel Corporation.
  23659.  
  23660.              ______________________
  23661.  
  23662.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  23663.  
  23664.  
  23665.              The PC Assembler Tutor                                     xxviii
  23666.              ______________________
  23667.  
  23668.  
  23669.                         ADDRESSING MODE                          CLOCKS (EA)
  23670.                  (i)   [bx], [si], [di], [bp]                           5
  23671.                  (ii)  variable (+ constant)                            6
  23672.                  (iii) [bp+di] or [bx+si]                               7
  23673.                        [bp+si] or [bx+di]                               8
  23674.                  (iv)  ([bx] or [si] or [di] or [bp]) + constant        9
  23675.                  (v)   ([bp+di] or [bx+si]) + constant                  11
  23676.                        ([bp+si] or [bx+di]) + constant                  12
  23677.  
  23678.              The most complicated memory address takes 2.4 times longer to
  23679.              calculate than the simplest address. These calculation times will
  23680.              be noted by EA (calculate Effective Address). Remember, if both
  23681.              operands are in registers, this calculation does not have to be
  23682.              done.
  23683.  
  23684.              In order for you to see how all of this works, we'll use ADD as
  23685.              an example. Don't start using the table till you understand this
  23686.              example.
  23687.  
  23688.              On the 8086, we normaly have the following possibilities for
  23689.              source and destination:
  23690.  
  23691.                  register, register
  23692.                  register, memory
  23693.                  memory, register
  23694.                  register, constant
  23695.                  memory, constant
  23696.  
  23697.              In Appendix II, we simply combined them as:
  23698.  
  23699.                  reg/mem, reg/mem
  23700.                  reg/mem, constant
  23701.  
  23702.              We can't do this here because they all have different times. For
  23703.              ADD they are:
  23704.  
  23705.                  ADD                      CLOCKS
  23706.                  register, register       3
  23707.                  register, memory         9  + EA
  23708.                  memory, register         16 + EA
  23709.                  register, constant       4
  23710.                  memory, constant         17 + EA
  23711.  
  23712.              Notice how much faster using a register is. The EA stands for
  23713.              "calculate Effective Address" and is the number from the list
  23714.              above. If you have:
  23715.  
  23716.                  add  ax, bx
  23717.  
  23718.              that is "register, register", and it will take 3 clocks to
  23719.              execute. If you have:
  23720.  
  23721.                  add [bx+di+9], 17
  23722.  
  23723.              that is "memory, constant" and will take 17 + EA. What is EA
  23724.              here? According to the above list, [BX+DI+CONSTANT] takes 12
  23725.  
  23726.  
  23727.              Appendix III - Speeds and Flag Settings                      xxix
  23728.              _______________________________________
  23729.  
  23730.              cycles, so 17 + EA is 17 + 12 is 29, so this will take 29 clocks.
  23731.              That's right. The one instruction is almost 10 times slower than
  23732.              the other. If you can move things into some registers, do a
  23733.              number of calculations, and then move them back to memory, you
  23734.              can save a lot of time. Let's do a few examples to make sure that
  23735.              you see all of them:
  23736.  
  23737.  
  23738.                  INSTRUCTION              TYPE                TIME
  23739.  
  23740.                  add  variable1, bl       memory, register    16 + EA = 22
  23741.                  add  bl, variable1       register, memory    9  + EA = 15
  23742.                  add  [si], di            memory, register    16 + EA = 21
  23743.                  add  di, [si]            register, memory    9  + EA = 14
  23744.  
  23745.              Examples 1 and 2 are the same except that source and destination
  23746.              have been switched. The same applies to examples 3 and 4. Notice
  23747.              that when the 8086 has to fetch the variable and then put the
  23748.              result back in memory, it is significantly slower than when it
  23749.              just gets the variable from memory and puts the result in a
  23750.              register.
  23751.  
  23752.  
  23753.                  INSTRUCTION              TYPE                TIME
  23754.  
  23755.                  add  ax, cx              register, register  3
  23756.                  add  di, 1876            register, constant  4
  23757.                  add  variable1, 199      memory, constant    17 + EA = 23
  23758.  
  23759.              To show you how the different types of EA effect the time, let's
  23760.              do all 3 types of "source, destination" that involve memory.
  23761.              First, "memory, register":
  23762.  
  23763.  
  23764.                  INSTRUCTION              TIME
  23765.  
  23766.                  add  [bx], ax            16 + EA = 21
  23767.                  add  variable1, ax       16 + EA = 22
  23768.                  add  [bp+di], ax         16 + EA = 23
  23769.                  add  [bp+si], ax         16 + EA = 24
  23770.                  add  [bx+9], ax          16 + EA = 25
  23771.                  add  [bp+di+294], ax     16 + EA = 27
  23772.                  add  [bx+di+294], ax     16 + EA = 28
  23773.  
  23774.              Now let's do the same things but go "register, memory":
  23775.  
  23776.                  INSTRUCTION              TIME
  23777.  
  23778.                  add  ax, [bx]            9 + EA = 14
  23779.                  add  ax, variable1       9 + EA = 15
  23780.                  add  ax, [bp+di]         9 + EA = 16
  23781.                  add  ax, [bp+si]         9 + EA = 17
  23782.                  add  ax, [bx+9]          9 + EA = 18
  23783.                  add  ax, [bp+di+294]     9 + EA = 20
  23784.                  add  ax, [bx+di+294]     9 + EA = 21
  23785.  
  23786.  
  23787.  
  23788.  
  23789.              The PC Assembler Tutor                                        xxx
  23790.              ______________________
  23791.  
  23792.              And finally we have "memory, constant":
  23793.  
  23794.                  INSTRUCTION              TIME
  23795.  
  23796.                  add  [bx], 177           17 + EA = 22
  23797.                  add  variable1, 177      17 + EA = 23
  23798.                  add  [bp+di], 177        17 + EA = 24
  23799.                  add  [bp+si], 177        17 + EA = 25
  23800.                  add  [bx+9], 177         17 + EA = 26
  23801.                  add  [bp+di+294], 177    17 + EA = 28
  23802.                  add  [bx+di+294], 177    17 + EA = 29
  23803.  
  23804.  
  23805.              Is this everything you need to know before looking at the list?
  23806.              Not quite. Most of the 8086 family has a 16 bit data bus. That
  23807.              means that there are 16 wires connecting the processor to memory,
  23808.              and the processor reads 1 word (2 bytes) at a time. These memory
  23809.              reads ALWAYS start at an even location 1472d, 88026d, 198752d,
  23810.              etc. If you are reading one byte, it makes no difference whether
  23811.              it is at an even or odd location. If you are reading a word at an
  23812.              even location, then everything is normal. If you are reading a
  23813.              word at an ODD location, however, the processor must:
  23814.  
  23815.                  1) start reading at the first even location that contains
  23816.                  the variable.
  23817.                  2) read the next even location (which contains the last part
  23818.                  of the variable).
  23819.                  3) join the parts together.
  23820.  
  23821.              As an example, let's take a word at address 21957 (i.e. 21957-
  23822.              21958). The processor will:
  23823.  
  23824.                  1) read the high byte from the word at 21956
  23825.                  2) read the low byte from the word at 21958
  23826.                  3) join them together. It now has 21957-21958.
  23827.  
  23828.              The processor can do this, but it takes extra time (4 extra clock
  23829.              ticks), so our speed listing will also contain the following
  23830.              notice:
  23831.  
  23832.                  WORDS WHICH ARE AT ODD ADDRESSES NEED 4 EXTRA CLOCKS
  23833.  
  23834.              Thus for:
  23835.  
  23836.                  add  ax, variable1        (9 + EA = 15)
  23837.  
  23838.              if the address is an even location, the instruction will require
  23839.              15 clocks. If it is an odd location, the instruction will require
  23840.              19 clocks (4 extra ticks). It is worth your while to keep words
  23841.              at even locations if at all possible.
  23842.  
  23843.  
  23844.  
  23845.  
  23846.  
  23847.  
  23848.  
  23849.  
  23850.  
  23851.              Appendix III - Speeds and Flag Settings                      xxxi
  23852.              _______________________________________
  23853.  
  23854.  
  23855.                                 INSTRUCTION SPEEDS AND FLAGS
  23856.  
  23857.  
  23858.              REGISTER:
  23859.                  One of the arithmetic registers - either word (AX, BX, CX,
  23860.                  DX, SI, DI, BP, SP for word operations) or byte (AH, AL, BH,
  23861.                  BL, CH, CL, DH or DL for byte operations).
  23862.  
  23863.              AX or AL:
  23864.                  AX and AL are considered special on the 8086. Sometimes
  23865.                  there is a special AX/AL form of the instruction, (such as
  23866.                  for ADD). This form will be shorter. It will only be noted
  23867.                  if it is FASTER (such as for MOV) or if it is the only form
  23868.                  allowed. It will either say (AX/AL) or (AX only) depending
  23869.                  on whether both words and bytes are allowed or only words
  23870.                  are allowed. Though both multiplication and division require
  23871.                  the use of the (AX/AL) register, the AX/AL is understood,
  23872.                  and is not mentioned in the instruction.
  23873.  
  23874.              SEGREG:
  23875.                  One of the 4 segment registers - CS, DS, ES or SS.
  23876.  
  23877.              MEMORY:
  23878.                  Either a byte or word in memory. It may be addressed with
  23879.                  any possible addressing mode, and the extra time needed to
  23880.                  calculate the address in memory (EA - calculate Effective
  23881.                  Address) is the following:
  23882.  
  23883.  
  23884.                         ADDRESSING MODE                          CLOCKS (EA)
  23885.  
  23886.                  (i)   [bx], [si], [di], [bp]                           5
  23887.                  (ii)  variable (+ constant)                            6
  23888.                  (iii) [bp+di] or [bx+si]                               7
  23889.                        [bp+si] or [bx+di]                               8
  23890.                  (iv)  ([bx] or [si] or [di] or [bp]) + constant        9
  23891.                  (v)   ([bp+di] or [bx+si]) + constant                  11
  23892.                        ([bp+si] or [bx+di]) + constant                  12
  23893.  
  23894.              EXTRA TIME:
  23895.                  1) WORDS WHICH ARE AT ODD ADDRESSES NEED 4 EXTRA CLOCKS.
  23896.                  2) A segment override adds 2 clocks to the instruction.
  23897.  
  23898.  
  23899.  
  23900.              FLAGS
  23901.  
  23902.              Following the instruction mnemonic is information in square
  23903.              brackets that indicates how the instruction effects the flags
  23904.              register. There are three possibilities
  23905.  
  23906.                  1) The instruction may alter the value of a flag in a
  23907.                  particular way depending on the result. For AND, the sign,
  23908.                  zero and parity flags [SZP] will be set according to the
  23909.                  result.
  23910.  
  23911.  
  23912.  
  23913.              The PC Assembler Tutor                                      xxxii
  23914.              ______________________
  23915.  
  23916.                  2) The instruction may set a flag to a specific number
  23917.                  (either 1 or 0). For AND, the overflow flag and the carry
  23918.                  flag are cleared [(OC=0)].
  23919.  
  23920.                  3) The instruction may unreliably alter a flag. That means
  23921.                  that the instruction might change the flag, but that it
  23922.                  gives you no information. This kind of flag cannot be
  23923.                  trusted after this operation. For AND, the auxillary flag is
  23924.                  unreliable [?A?].
  23925.  
  23926.              The information will always be displayed in this order. The flags
  23927.              which are reliably set according to the result will be listed
  23928.              first [SZP]. Next, any flags which are either set or cleared will
  23929.              be put inside parentheses followed by an equal sign followed by
  23930.              the value of the flag (all inside of the parentheses)
  23931.              [SZP,(OC=0)]. Finally, any unrelaible flags will be put between
  23932.              question marks [SZP,(OC=0),?A?]. Each part will be separated by
  23933.              commas. If no flags are changed by the instruction, the brackets
  23934.              will have [none] written between them.
  23935.  
  23936.              Each flags will be indicated by a single letter. The letters are:
  23937.  
  23938.                  O    overflow flag       0 = no overflow, 1 = overflow
  23939.  
  23940.                  D    direction flag      direction of movement
  23941.                                           for string instructions
  23942.                                           0 = upwards, 1 = downwards
  23943.  
  23944.                  I    interrupt enable    0 = no ints, 1 = ints o.k.
  23945.  
  23946.                  T    trap flag           trap next instruction?
  23947.                                           0 = no trap, 1 = trap
  23948.  
  23949.                  S    sign flag           0 = positive, 1 = negative
  23950.  
  23951.                  Z    zero flag           0 = non-zero, 1 = zero
  23952.  
  23953.                  A    auxillary flag      carry out of bottom half
  23954.                                           register?  0 = no, 1 = yes
  23955.  
  23956.                  P    parity flag         0 = odd, 1 = even
  23957.  
  23958.                  C    carry flag          0 = no carry, 1 = carry
  23959.  
  23960.  
  23961.  
  23962.              ***************** THE INSTRUCTIONS *********************
  23963.  
  23964.              INSTRUCTION                TIMING
  23965.  
  23966.              AAA [AC,?OSZP?]         4 clocks
  23967.  
  23968.              AAD [SZP,?OAC?]         60 clocks
  23969.  
  23970.              AAM [SZP,?OAC?]         83 clocks
  23971.  
  23972.              AAS [AC,?OSZP?]         4 clocks
  23973.  
  23974.  
  23975.              Appendix III - Speeds and Flag Settings                    xxxiii
  23976.              _______________________________________
  23977.  
  23978.  
  23979.              ADC [OSZAPC]            see ADD
  23980.  
  23981.              ADD [OSZAPC]            register, register    3
  23982.                                      register, memory      9  + EA
  23983.                                      memory, register      16 + EA
  23984.                                      register, constant    4
  23985.                                      memory, constant      17 + EA
  23986.  
  23987.              AND [SZP,(OC=0),?A?]    see ADD
  23988.  
  23989.              CALL [none]             near call                19
  23990.                                      far call                 28
  23991.                                      near call (reg-ind)      16  {2}
  23992.                                      near call (mem-ind)      21 + EA
  23993.                                      far call (mem-ind)       37 + EA
  23994.  
  23995.              CBW [none]              2 clocks
  23996.  
  23997.              CLC [(C=0)]             2 clocks
  23998.  
  23999.              CLD [(D=0)]             2 clocks
  24000.  
  24001.              CLI [(I=0)]             2 clocks
  24002.  
  24003.              CMC [C]                 2 clocks
  24004.  
  24005.              CMP [OSZAPC]            register, register    3
  24006.                                      register, memory      9  + EA
  24007.                                      memory, register      9  + EA
  24008.                                      register, constant    4
  24009.                                      memory, constant      10 + EA
  24010.  
  24011.              CMPS [OSZAPC]           22 clocks
  24012.  
  24013.              CWD [none]              5 clocks
  24014.  
  24015.              DAA [SZAPC,?O?]         4 clocks
  24016.  
  24017.              DAS [SZAPC,?O?]         4 clocks
  24018.  
  24019.              DEC [OSZAP]             word register       2
  24020.                                      byte register       3
  24021.                                      word/byte memory    15 + EA
  24022.  
  24023.              DIV [?OSZAPC?]          byte register       80 - 90  {3}
  24024.                                      word register       144 - 162
  24025.              ____________________
  24026.  
  24027.                 2 These three last ones are indirect calls. They get the
  24028.              address of the subroutine from a register (reg-ind) or from
  24029.              memory (mem-ind).
  24030.  
  24031.                 3 The smaller the numbers, the faster this operation can be
  24032.              accomplished. This applies for signed and unsigned multiplication
  24033.              and division. They all show a range of values rather than a
  24034.              specific value.
  24035.  
  24036.  
  24037.              The PC Assembler Tutor                                      xxxiv
  24038.              ______________________
  24039.  
  24040.                                      byte memory         (86 - 96) + EA
  24041.                                      word memory         (150 - 168) + EA
  24042.  
  24043.              ESC [none]              memory              8 + EA
  24044.                                      coproc. register    2
  24045.                                      This only makes sense if you know
  24046.                                      about coprocessors.
  24047.  
  24048.              HLT [none]              2 clocks
  24049.  
  24050.              IDIV [none]             byte register       101 - 112
  24051.                                      word register       165 - 184
  24052.                                      byte memory         (107 - 118) + EA
  24053.                                      word memory         (171 - 190) + EA
  24054.  
  24055.              IMUL [OC,?SZAP?]        byte register       80 - 98
  24056.                                      word register       128 - 154
  24057.                                      byte memory         (86 - 104) + EA
  24058.                                      word memory         (134 - 160) + EA
  24059.  
  24060.              IN [none]               (AX/AL), port#      10
  24061.                                      (AX/AL), dx         8
  24062.  
  24063.              INC [OSCAP]             word register       2
  24064.                                      byte register       3
  24065.                                      word/byte memory    15 + EA
  24066.  
  24067.              INT [(IT=0) {4} ]       51 clocks  {5}
  24068.  
  24069.              INTO [ {6} ]            overflow            54
  24070.                                      no overflow         4
  24071.  
  24072.              IRET [ {7} ]            24 clocks
  24073.  
  24074.  
  24075.              J(condition) [none]     This includes all conditional jumps
  24076.                                      (JAE, JZ, JNO, JLE, JP, etc.) with the
  24077.                                      exception of JCXZ.
  24078.                                      jump                16
  24079.                                      no jump             4
  24080.              ____________________
  24081.  
  24082.                 4 Although this sets the trap flag and interrupt flag to 0, it
  24083.              doesn't do it to YOUR flags, it does it to the flags that the
  24084.              interrupt will see. Your flags are safely stored on the stack and
  24085.              will return unaltered at the end of the interrupt.
  24086.  
  24087.                 5 There is one exception. INT 3, when coded as the single byte
  24088.              trap interrupt, is 52 clocks.
  24089.  
  24090.                 6 If there is overflow, it pushes your flags and sets (IT=0)
  24091.              for the interrupt. If there was no overflow, it does nothing. In
  24092.              either case your own flags will remain unaffected.
  24093.  
  24094.                 7 This puts the copy of your old flags back in the flags
  24095.              register. The flags will be the same as they were when you called
  24096.              the interrupt.
  24097.  
  24098.  
  24099.              Appendix III - Speeds and Flag Settings                      xxxv
  24100.              _______________________________________
  24101.  
  24102.  
  24103.              JCXZ [none]             jump                18
  24104.                                      no jump             6
  24105.  
  24106.              JMP [none]              same segment        15
  24107.                                      different segment   15
  24108.                                      near (reg-ind)      11  {8}
  24109.                                      near (mem-ind)      18 + EA
  24110.                                      far  (mem-ind)      24 + EA
  24111.  
  24112.              LAHF [none]             4 clocks
  24113.  
  24114.              LDS [none]              16 + EA
  24115.  
  24116.              LES [none]              16 + EA
  24117.  
  24118.              LEA [none]              2 + EA
  24119.  
  24120.              LOCK [none]             2 clocks
  24121.  
  24122.              LODS [none]             12 clocks
  24123.  
  24124.              LOOP [none]             jump                17
  24125.                                      no jump             5
  24126.  
  24127.              LOOPE/LOOPZ [none]      jump                18
  24128.                                      no jump             6
  24129.  
  24130.              LOOPNE/LOOPNZ [none]    jump                19
  24131.                                      no jump             5
  24132.  
  24133.              MOV [none]              register, register    2
  24134.                                      register, memory      8  + EA
  24135.                                      memory, register      9  + EA
  24136.                                      register, constant    4
  24137.                                      memory, constant      10 + EA
  24138.                                      (AX/AL) <-> memory    10 {9}
  24139.  
  24140.  
  24141.  
  24142.              ____________________
  24143.  
  24144.                 8 These last three are indirect jumps. The information about
  24145.              where to jump to is coming from a register (reg-ind) or from
  24146.              memory (mem-ind).
  24147.  
  24148.                 9 This is a special instruction which moves a directly
  24149.              addressed variable to or from AX (or AL for bytes). Pointers are
  24150.              not allowed, only the forms:
  24151.  
  24152.                  mov  ax, variable1
  24153.                  mov  variable1, ax
  24154.  
  24155.              This takes 10 clocks instead of 14 or 15 for the other form.
  24156.              Whether this form gets used is up to the assembler, not you.
  24157.              Fortunately, MASM, TurboAssembler and A86 all use this form when
  24158.              appropriate.
  24159.  
  24160.  
  24161.              The PC Assembler Tutor                                      xxxvi
  24162.              ______________________
  24163.  
  24164.                                      segreg <-> register   2  {10}
  24165.                                      segreg, memory        8 + EA
  24166.                                      memory, segreg        9 + EA
  24167.  
  24168.              MOVS [none]             11 clocks
  24169.  
  24170.              MUL [OC,?SZAP?]         byte register       70 - 77
  24171.                                      word register       118 - 133
  24172.                                      byte memory         (76 - 83) + EA
  24173.                                      word memory         (124 - 139) + EA
  24174.  
  24175.              NEG [OSZAPC] {11}       register            3
  24176.                                      memory              16 + EA
  24177.  
  24178.              NOP [none]              3 clocks
  24179.  
  24180.              NOT [none]              register            3
  24181.                                      memory              16 + EA
  24182.  
  24183.              OR [SZP,(OC=0),?A?]     see ADD
  24184.  
  24185.              OUT [none]              (AX/AL), port#      10
  24186.                                      (AX/AL), dx         8
  24187.  
  24188.              POP [none]              register            8
  24189.                                      segreg              8
  24190.                                      memory              17 + EA
  24191.  
  24192.              POPF [ {12} ]           8 clocks
  24193.  
  24194.              PUSH [none]             register            11
  24195.                                      segreg              10
  24196.                                      memory              16 + EA
  24197.  
  24198.              PUSHF [none]            10 clocks
  24199.  
  24200.              RCL [OC]                register by 1 bit     2
  24201.                                      memory by 1 bit       15 + EA
  24202.                                      register by # in CL   8 + (4 * #) {13}
  24203.                                      memory by # in CL     20