home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_asm / alprog / alprog2.tut < prev   
Encoding:
Text File  |  1986-11-03  |  37.6 KB  |  1,020 lines

  1. ..       90 lines per page 
  2. ..       leave 6/12 lines for article headline
  3. ..    put cursor on ruler line & hit Ctrl-OF
  4. .. !------!----!----!----!----!----!----!----!-----
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  IBM Personal Computer Assembly Language Tutorial
  11.          Joshua Auerbach, Yale University
  12.  
  13.                Yale Computer Center
  14.                 175 Whitney Avenue
  15.                   P. O. Box 2112
  16.            New Haven, Connecticut 06520
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28. Learning the assembler
  29. _____________________
  30.  
  31. It is my feeling that many people can teach them-
  32. selves to use the assembler by reading the MACRO 
  33. Assembler manual if
  34.  
  35. 1.  You have read and understood a book like Morse 
  36.     and thus have a feeling for the instruction set
  37.  
  38. 2.  You know something about DOS services and so 
  39.     can communicate with the keyboard and screen 
  40.     and do something marginally useful with files.  
  41.     In the absence of this kind of knowledge, you 
  42.     can't write meaningful practice programs and so 
  43.     will not progress.
  44.  
  45. 3.  You have access to some good examples (the ones 
  46.     supplied with the assembler are not good, in my 
  47.     opinion.  I will try to supply you with some 
  48.     more relevant ones.
  49.  
  50. 4.  You ignore the things which are most confusing 
  51.     and least useful.  Some of the most confusing 
  52.     aspects of the assembler include the facilities 
  53.     combining segments.  But, you can avoid using 
  54.     all but the simplest of these facilities in 
  55.     many cases, even while writing quite 
  56.     substantial applications.
  57.  
  58. 5.  The easiest kind of assembler program to write 
  59.     is a COM program.  They might seem harder, at 
  60.     first, than EXE programs because there is an 
  61.     extra step involved in creating the executable 
  62.     file, but COM programs are structurally very 
  63.     much simpler.
  64.  
  65. At this point, it is necessary to talk about COM 
  66. programs and EXE programs.  As you probably know, 
  67. DOS supports two kinds of executable files.  EXE 
  68. programs are much more general, can contain many 
  69. segments, and are generally built by compilers and 
  70. sometimes by the assembler.  If you follow the lead 
  71. given by the samples distributed with the assembler,
  72. you will end up with EXE programs.  A COM program, 
  73. in contrast, always contains just one segment, and 
  74. receives control with all four segment registers 
  75. containing the same value.  A COM program, thus, 
  76. executes in a simplified environment, a 64K address 
  77. space.  You can go outside this address space 
  78. simply by temporarily changing one segment 
  79. register, but you don't have to, and that is the 
  80. thing which makes COM programs nice and simple.  
  81. Let's look at a very simple one.
  82.  
  83. The classic text on writing programs for the C 
  84. language says that the first thing you should write 
  85. is a program which says
  86.  
  87.       HELLO, WORLD.
  88.  
  89. when invoked.  What's sauce for C is sauce for 
  90. assembler, so let's start with a HELLO program of 
  91. our own.  My first presentation of this will be 
  92. bare bones, not stylistically complete, but just an 
  93. illustration of what an assembler program 
  94. absolutely has to have:
  95.  
  96.  
  97.  
  98. HELLO  SEGMENT                  ;Set up HELLO code 
  99.                                 ; and data section
  100.        ASSUME CS:HELLO,DS:HELLO ;Tell assembler 
  101.                                 ; about conditions 
  102.                                 ; at entry
  103.        ORG  100H                ;A .COM program 
  104.                                 ; begins with 100H 
  105.                                 ; byte prefix
  106. MAIN:  JMP  BEGIN               ;Control must start 
  107.                                 ; here
  108. MSG    DB   'Hello, world.$'    ;But it is 
  109.                                 ; generally useful 
  110.                                 ; to put data first
  111. BEGIN: MOV  DX,OFFSET MSG       ;Let DX --> message.
  112.        MOV  AH,9                ;Set DOS function 
  113.                                 ; code for printing 
  114.                                 ; a message
  115.        INT  21H                 ;Invoke DOS
  116.        RET                      ; Return to system
  117. HELLO  ENDS                     ;End of code and 
  118.                                 ; data section
  119.        END  MAIN                ;Terminate assem-
  120.                                 ; bler and specify 
  121.                                 ; entry point
  122.  
  123. First, let's attend to some obvious points.  The 
  124. macro assembler uses the general form
  125.  
  126.    name    opcode    operands
  127.  
  128. Unlike the 370 assembler, though, comments are NOT 
  129. set off from operands by blanks.  The syntax uses 
  130. blanks as delimiters within the operand field (see 
  131. line 6 of the example) and so all comments must be 
  132. set off by semi-colons.
  133.  
  134. Line comments are frequently set off with a semi-
  135. colon in column 1.  I use this approach for block 
  136. comments too, although there is a COMMENT statement 
  137. which can be used to introduce a block comment.
  138.  
  139. Being an old 370 type, I like to see assembler code 
  140. in upper case, although my comments are mixed case.  
  141. Actually, the assembler is quite happy with mixed 
  142. case anywhere.
  143.  
  144. As with any assembler, the core of the opcode set 
  145. consists of opcodes which generate machine instruc-
  146. tions but there are also opcodes which generate 
  147. data and ones which function as instructions to the 
  148. assembler itself, sometimes called pseudo-ops.  In 
  149. the example, there are five lines which generate 
  150. machine code (JMP, MOV, MOV, INT, RET), one line 
  151. which generates data (DB) and five pseudo-ops 
  152. (SEGMENT, ASSUME, ORG, ENDS, and END).
  153.  
  154. We will discuss all of them.
  155.  
  156. Now, about labels.  You will see that some labels 
  157. in the example end in a colon and some don't.  This 
  158. is just a bit confusing at first, but no real 
  159. mystery.  If a label is attached to a piece of code 
  160. (as opposed to data), then the assembler needs to 
  161. know what to do when you JMP to or CALL that label.  
  162. By convention, if the label ends in a colon, the 
  163. assembler will use the NEAR form of JMP or CALL.  
  164. If the label does not end in a colon, it will use 
  165. the FAR form.  In practice, you will always use the 
  166. colon on any label you are jumping to inside your 
  167. program because such jumps are always NEAR; there 
  168. is no reason to use a FAR jump within a single code 
  169. section.  I mention this, though, because leaving 
  170. off the colon isn't usually trapped as a syntax 
  171. error, it will generally cause something more 
  172. abstruse to go wrong.
  173.  
  174. On the other hand, a label attached to a piece of 
  175. data or a pseudo-op never ends in a colon.
  176.  
  177. Machine instructions will generally take zero, one 
  178. or two operands.  Where there are two operands, the 
  179. one which receives the result goes on the left as 
  180. in 370 assembler.
  181.  
  182. I tried to explain this before, now maybe it will 
  183. be even clearer:  there are many more 8086 machine 
  184. opcodes then there are assembler opcodes to repre-
  185. sent them.  For example, there are five kinds of 
  186. JMP, four kinds of CALL, two kinds of RET, and at 
  187. least five kinds of MOV depending on how you count 
  188.  
  189.  
  190. them.  The macro assembler makes a lot of decisions 
  191. for you based on the form taken by the operands or 
  192. on attributes assigned to symbols elsewhere in your 
  193. program.  In the example above, the assembler will 
  194. generate the NEAR DIRECT form of JMP because the 
  195. target label BEGIN labels a piece of code instead 
  196. of a piece of data (this makes the JMP DIRECT) and 
  197. ends in a colon (this makes the JMP NEAR).  The 
  198. assembler will generate the immediate forms of MOV 
  199. because the form OFFSET MSG refers to immediate 
  200. data and because 9 is a constant.  The assembler 
  201. will generate the NEAR form of RET because that is 
  202. the default and you have not told it otherwise.
  203.  
  204. The DB (define byte) pseudo-op is an easy one:  it 
  205. is used to put one or more bytes of data into 
  206. storage.  There is also a DW (define word) pseudo-
  207. op and a DD (define doubleword) pseudo-op;  in the 
  208. PC MACRO assembler, the fact that a label refers to 
  209. a byte of storage, a word of storage, or a double-
  210. word of storage can be very significant in ways 
  211. which we will see presently.
  212.  
  213. About that OFFSET operator, I guess this is the 
  214. best way to make the point about how the assembler 
  215. decides what instruction to assemble:  an analogy 
  216. with 370 assembler:
  217.  
  218.   PLACE    DC   ......
  219.            ...
  220.            LA   R1,PLACE
  221.            L    R1,PLACE
  222.  
  223. In 370 assembler, the first instruction puts the 
  224. address of label PLACE in register 1, the second 
  225. instruction puts the contents of storage at label 
  226. PLACE in register 1.  Notice that two different 
  227. opcodes are used.  In the PC assembler, the 
  228. analogous instructions would be
  229.  
  230.   PLACE    DW   ......
  231.            ...
  232.            MOV  DX,OFFSET PLACE
  233.            MOV  DX,PLACE
  234.  
  235. If PLACE is the label of a word of storage, then 
  236. the second instruction will be understood as a 
  237. desire to fetch that data into DX.  If X is a 
  238. label, then "OFFSET X" means "the ordinary number 
  239. which represents X's offset from the start of the 
  240. segment."  And, if the assembler sees an ordinary 
  241. number, as opposed to a label, it uses the 
  242. instruction which is equivalent to LA.
  243.  
  244. If PLACE were the label of a DB pseudo-op, instead 
  245. of a DW, then
  246.  
  247.            MOV  DX,PLACE
  248.  
  249. would be illegal.  The assembler worries about 
  250. length attributes of its operands.
  251.  
  252. Next, numbers and constants in general.  The 
  253. assembler's default radix is decimal.  You can 
  254. change this, but I don't recommend it.  If you want 
  255. to represent numbers in other forms of notation 
  256. such as hex or bit, you generally use a trailing 
  257. letter.  For example,
  258.  
  259.       21H is hexidecimal 21,
  260. 00010000B is the eight bit binary number pictured.
  261.  
  262. The next elements we should point to are the 
  263. SEGMENT...ENDS pair and the END instruction.  Every 
  264. assembler program has to have these elements.
  265.  
  266. SEGMENT tells the assembler you are starting a 
  267. section of contiguous material (code and/or data).  
  268. The symmetrically named ENDS statement tells the 
  269. assembler you are finished with a section of conti-
  270. guous material.  I wish they didn't use the word 
  271. SEGMENT in this context.  To me, a "segment" is a 
  272. hardware construct:  it is the 64K of real storage 
  273. which becomes addressable by virtue of having a 
  274. particular value in a segment register.  Now, it is 
  275. true that the "segments" you make with the assem-
  276. bler often correspond to real hardware "segments" 
  277. at execution time.  But, if you look at things like 
  278. the GROUP and CLASS options supported by the linker,
  279. you will discover that this correspondence is by no 
  280.  
  281.  
  282. means exact.  So, at risk of maybe confusing you 
  283. even more, I am going to use the more informal term 
  284. "section" to refer to the area set off by means of 
  285. the SEGMENT and ENDS instructions.
  286.  
  287. The sections delimited by SEGMENT...ENDS pairs are 
  288. really a lot like CSECTs and DSECTs in the 370 world.
  289.  
  290. I strongly recommend that you be selective in your 
  291. study of the SEGMENT pseudo-op as described in the 
  292. manual.  Let me just touch on it here.
  293.  
  294.   name     SEGMENT
  295.   name     SEGMENT  PUBLIC
  296.   name     SEGMENT  AT  nnn
  297.  
  298. Basically, you can get away with just the three 
  299. forms given above.  The first form is what you use 
  300. when you are writing a single section of assembler 
  301. code which will not be combined with other pieces 
  302. of code at link time.  The second form says that 
  303. this assembly only contains part of the section;  
  304. other parts might be assembled separately and 
  305. combined later by the linker.
  306.  
  307. I have found that one can construct reasonably 
  308. large modular applications in assembler by simply 
  309. making every assembly use the same segment name and 
  310. declaring the name to be PUBLIC each time.  If you 
  311. read the assembler and linker documentation, you 
  312. will also be bombarded by information about more 
  313. complex options such as the GROUP statement and the 
  314. use of other "combine types" and "classes."  I 
  315. don't recommend getting into any of that.  I will 
  316. talk more about the linker and modular construction 
  317. of programs a little later.  The assembler manual 
  318. also implies that a STACK segment is required.  
  319. This is not really true.  There are numerous ways 
  320. to assure that you have a valid stack at execution 
  321. time.
  322.  
  323. Of course, if you plan to write applications in 
  324. assembler which are more than 64K in size, you will 
  325. need more than what I have told you; but who is 
  326. really going to do that?  Any application that 
  327. large is likely to be coded in a higher level 
  328. language.
  329.  
  330. The third form of the SEGMENT statement makes the 
  331. delineated section into something like a "DSECT;" 
  332. that is, it doesn't generate any code, it just 
  333. describes what is present somewhere already in the 
  334. computer's memory.  Sometimes the AT value you give 
  335. is meaningful.  For example, the BIOS work area is 
  336. located at location 40 hex.  So, you might see
  337.  
  338. BIOSAREA  SEGMENT AT 40H     ;Map BIOS work area
  339.           ORG  BIOSAREA+10H
  340. EQUIP     DB   ?             ;Location of equipment 
  341.                              ;flags, first byte
  342. BIOSAREA  ENDS
  343.  
  344. in a program which was interested in mucking around 
  345. in the BIOS work area.
  346.  
  347. At other times, the AT value you give may be arbi-
  348. trary, as when you are mapping a repeated control 
  349. block:
  350.  
  351. PROGPREF SEGMENT   AT 0     ;Really a DSECT mapping 
  352.                             ;the program prefix
  353.          ORG   PROGPREF+6
  354. MEMSIZE  DW   ?             ;Size of available 
  355.                             ;memory
  356. PROGPREF ENDS
  357.  
  358. Really, no matter whether the AT value represents 
  359. truth or fiction, it is your responsibility, not 
  360. the assembler's, to set up a segment register so 
  361. that you can really reach the storage in question.   
  362. So, you can't say
  363.  
  364.          MOV  AL,EQUIP
  365.  
  366. unless you first say something like
  367.  
  368.          MOV  AX,BIOSAREA    ;BIOSAREA becomes a 
  369.                              ;symbol with value 40H
  370.          MOV  ES,AX
  371.          ASSUME ES:BIOSAREA
  372.  
  373.  
  374. Enough about SEGMENT.  The END statement is simple.  
  375. It goes at the end of every assembly.  When you are 
  376. assembling a subroutine, you just say
  377.  
  378.          END
  379.  
  380. but when you are assembling the main routine of a 
  381. program you say
  382.  
  383.         END label
  384.  
  385. where 'label' is the place where execution is to 
  386. begin.
  387.  
  388. Another pseudo-op illustrated in the program is 
  389. ASSUME.  ASSUME is like the USING statement in 370 
  390. assembler.  However, ASSUME can ONLY refer to 
  391. segment registers.  The assembler uses ASSUME 
  392. information to decide whether to assemble segment 
  393. override prefixes and to check that the data you 
  394. are trying to access is really accessible.  In this 
  395. case, we can reassure the assembler that both the 
  396. CS and DS registers will address the section called 
  397. HELLO at execution time.  Actually, the SS and ES 
  398. registers will too, but the assembler never needs 
  399. to make use of this information.
  400.  
  401. I guess I have explained everything in the program 
  402. except that ORG pseudo-op.  ORG means the same 
  403. thing as it does in many assembly languages.  It 
  404. tells the assembler to move its location counter to 
  405. some particular address.  In this case, we have 
  406. asked the assembler to start assembling code hex 
  407. 100 bytes from the start of the section called 
  408. HELLO instead of at the very beginning.  This 
  409. simply reflects the way COM programs are loaded.  
  410. When a COM program is loaded by the system, the 
  411. system sets up all four segment registers to 
  412. address the same 64K of storage.  The first 100 hex 
  413. bytes of that storage contains what is called the 
  414. program prefix; this area is described in appendix 
  415. E of the DOS manual.  Your COM program physically 
  416. begins after this.  Execution begins with the first 
  417. physical byte of your program; that is why the JMP 
  418. instruction is there.
  419.  
  420. Wait a minute, you say, why the JMP instruction at 
  421. all?  Why not put the data at the end?  Well, in a 
  422. simple program like this I probably could have 
  423. gotten away with that.  However, I have the habit 
  424. of putting data first and would encourage you to do 
  425. the same because of the way the assembler has of 
  426. assembling different instructions depending on the 
  427. nature of the operand.
  428.  
  429. Unfortunately, sometimes the different choices of 
  430. instruction which can assemble from a single opcode 
  431. have different lengths.  If the assembler has 
  432. already seen the data when it gets to the instruc-
  433. tions it has a good chance of reserving the right 
  434. number of bytes on the first pass.  If the data is 
  435. at the end, the assembler may not have enough 
  436. information on the first pass to reserve the right 
  437. number of bytes for the instruction.  Sometimes the 
  438. assembler will complain about this, something like 
  439. "Forward reference is illegal" but at other times, 
  440. it will make some default assumption.  On the 
  441. second pass, if the assumption turned out to be 
  442. wrong, it will report what is called a "Phase 
  443. error," a very nasty error to track down.  So get 
  444. in the habit of putting data and equated symbols 
  445. ahead of code.
  446.  
  447. OK.  Maybe you understand the program now.  Let's 
  448. walk through the steps involved in making it into a 
  449. real COM file.
  450.  
  451. 1.  The file should be created with the name 
  452.     HELLO.ASM (actually the name is arbitrary but 
  453.     the extension .ASM is conventional and useful)
  454.  
  455. 2.  
  456.           ASM   HELLO,,;
  457.  
  458.    (this is just one example of invoking the 
  459.     assembler; it uses the small assembler ASM, it 
  460.     produces an object file and a listing file with 
  461.     the same name as the source file.  I am not 
  462.     going exhaustively into how to invoke the 
  463.     assembler, which the manual goes into pretty 
  464.  
  465.  
  466.     well.  I guess this is the first time I 
  467.     mentioned that there are really two assemblers; 
  468.     the small assembler ASM will run in a 64K 
  469.     machine and doesn't support macros.  I used to 
  470.     use it all the time; now that I have a bigger 
  471.     machine and a lot of macro libraries I use the 
  472.     full function assembler MASM.  You get both 
  473.     when you buy the package).
  474.  
  475. 3.  If you issue DIR at this point, you will 
  476.     discover that you have acquired HELLO.OBJ (the 
  477.     object code resulting from the assembly) and 
  478.     HELLO.LST (a listing file).  I guess I can 
  479.     digress for a second here concerning the 
  480.     listing file.  It contains TAB characters.  I 
  481.     have found there are two good ways to get it 
  482.     printed and one bad way.  The bad way is to use 
  483.     LPT1: as the direct target of the listing file 
  484.     or to try copying the LST file to LPT1 without 
  485.     first setting the tabs on the printer.  The two 
  486.     good ways are to either
  487.  
  488.     a.  direct it to the console and activate the 
  489.     printer with CTRL-PRTSC.  In this case, DOS 
  490.     will expand the tabs for you.
  491.  
  492.     b.  direct to LPT1: but first send the right 
  493.     escape sequence to LPT1 to set the tabs every 
  494.     eight columns.  I have found that on some early 
  495.     serial numbers of the IBM PC printer, tabs 
  496.     don't work quite right, which forces you to the 
  497.     first option.
  498.  
  499. 4.
  500.           LINK  HELLO;
  501.  
  502.    (again, there are lots of linker options but 
  503.     this is the simplest.  It takes HELLO.OBJ and 
  504.     makes HELLO.EXE).  HELLO.EXE?  I thought we 
  505.     were making a COM program, not an EXE program.  
  506.     Right.  HELLO.EXE isn't really executable; its 
  507.     just that the linker doesn't know about COM 
  508.     programs.  That requires another utility.  You 
  509.     don't have this utility if you are using DOS 
  510.     1.0; you have it if you are using DOS 1.1 or 
  511.     DOS 2.0.  Oh, by the way, the linker will warn 
  512.     you that you have no stack segment.  Don't 
  513.     worry about it.
  514.  
  515. 5.
  516.           EXE2BIN  HELLO HELLO.COM
  517.  
  518.     This is the final step.  It produces the actual 
  519.     program you will execute.  Note that you have 
  520.     to spell out HELLO.COM; for a nominally 
  521.     rational but actually perverse reason, EXE2BIN 
  522.     uses the default extension BIN instead of COM 
  523.     for its output file.  At this point, you might 
  524.     want to erase HELLO.EXE; it looks a lot more 
  525.     useful than it is.  Chances are you won't need 
  526.     to recreate HELLO.COM unless you change the 
  527.     source and then you are going to have to redo 
  528.     the whole thing.
  529.  
  530. 6.
  531.           HELLO
  532.  
  533.     You type hello, that invokes the program, it 
  534.     says
  535.  
  536.           HELLO YOURSELF!!!
  537.  
  538.     (oops, what did I do wrong....?)
  539.  
  540. What about subroutines?
  541. ______________________
  542.  
  543. I started with a simple COM program because I 
  544. actually think they are easier to create than 
  545. subroutines to be called from high level languages, 
  546. but maybe its really the latter you are interested 
  547. in.  Here, I think you should get comfortable with 
  548. the assembler FIRST with little exercises like the 
  549. one above and also another one which I will finish 
  550. up with.
  551.  
  552. Next you are ready to look at the interface 
  553. information for your particular language.  You 
  554. usually find this in some sort of an appendix.  For 
  555. example, the BASIC manual has Appendix C on Machine 
  556. Language Subroutines.  The PASCAL manual buries the 
  557.  
  558.  
  559. information a little more deeply:  the interface to 
  560. a separately compiled routine can be found in the 
  561. Chapter on Procedures and Functions, in a 
  562. subsection called Internal Calling Conventions.
  563.  
  564. Each language is slightly different, but here are 
  565. what I think are some common issues in subroutine 
  566. construction.
  567.  
  568. 1.  NEAR versus FAR?  Most of the time, your 
  569.     language will probably call your assembler 
  570.     routine as a FAR routine.  In this case, you 
  571.     need to make sure the assembler will generate 
  572.     the right kind of return.  You do this with a 
  573.     PROC...ENDP statement pair.  The PROC statement 
  574.     is probably a good idea for a NEAR routine too 
  575.     even though it is not strictly required:
  576.  
  577.               FAR linkage:
  578.  
  579.     ARBITRARY SEGMENT
  580.               PUBLIC THENAME
  581.               ASSUME CS:ARBITRARY
  582.     THENAME   PROC FAR
  583.               ..... code and data
  584.     THENAME   ENDP
  585.     ARBITRARY ENDS
  586.               END
  587.  
  588.  
  589.               NEAR linkage:
  590.  
  591.     SPECIFIC  SEGMENT  PUBLIC
  592.               PUBLIC THENAME
  593.               ASSUME CS:SPECIFIC,DS:SPECIFIC
  594.               ASSUME ES:SPECIFIC,SS:SPECIFIC
  595.     THENAME   PROC NEAR
  596.               ..... code and data ....
  597.     THENAME   ENDP
  598.     SPECIFIC  ENDS
  599.               END
  600.  
  601.     With FAR linkage, it doesn't really matter what 
  602.     you call the segment.  you must declare the 
  603.     name by which you will be called in a PUBLIC 
  604.     pseudo-op and also show that it is a FAR 
  605.     procedure.  Only CS will be initialized to your 
  606.     segment when you are called.  Generally, the 
  607.     other segment registers will continue to point 
  608.     to the caller's segments.
  609.  
  610.     With NEAR linkage, you are executing in the 
  611.     same segment as the caller.  Therefore, you 
  612.     must give the segment a specific name as 
  613.     instructed by the language manual.  However, 
  614.     you may be able to count on all segment 
  615.     registers pointing to your own segment 
  616.     (sometimes the situation can be more 
  617.     complicated but I cannot really go into all of 
  618.     the details).  You should be aware that the 
  619.     code you write will not be the only thing in 
  620.     the segment and will be physically relocated 
  621.     within the segment by the linker.  However, all 
  622.     OFFSET references will be relocated and will be 
  623.     correct at execution time.
  624.  
  625. 2.  Parameters passed on the stack.  Usually, high 
  626.     level languages pass parameters to subroutines 
  627.     by pushing words onto the stack prior to 
  628.     calling you.  What may differ from language to 
  629.     language is the nature of what is pushed 
  630.     (OFFSET only or OFFSET and SEGMENT) and the 
  631.     order in which it is pushed (left to right, 
  632.     right to left within the CALL statement).  
  633.     However, you will need to study the examples to 
  634.     figure out how to retrieve the parameters from 
  635.     the stack.  A useful fact to exploit is the 
  636.     fact that a reference involving the BP register 
  637.     defaults to a reference to the stack segment.  
  638.     So, the following strategy can work:
  639.  
  640.     ARGS     STRUC
  641.              DW   3 DUP(?)  ;Saved BP and return 
  642.                             ; address
  643.     ARG3     DW   ?
  644.     ARG2     DW   ?
  645.     ARG1     DW   ?
  646.     ARGS     ENDS
  647.          ...........
  648.          (continued at top of next column)
  649.  
  650.  
  651.              PUSH BP             ;save BP 
  652.                                  ; register
  653.              MOV  BP,SP          ;Use BP to 
  654.                                  ; address stack
  655.              MOV   ...,[BP].ARG2 ;retrieve second 
  656.                                  ; argument
  657.              (etc.)
  658.  
  659.     This example uses something called a structure, 
  660.     which is only available in the large assembler; 
  661.     furthermore, it uses it without allocating it, 
  662.     which is not a well-documented option.  
  663.     However, I find the above approach generally 
  664.     pleasing.  The STRUC is like a DSECT in that it 
  665.     establishes labels as being offset a certain 
  666.     distance from an arbitrary point; these labels 
  667.     are then used in the body of code by beginning 
  668.     them with a period; the construction ".ARG2" 
  669.     means, basically, " + (ARG2-ARGS)."
  670.  
  671.     What you are doing here is using BP to address 
  672.     the stack, accounting for the word where you 
  673.     saved the caller's BP and also for the two 
  674.     words which were pushed by the CALL 
  675.     instruction.
  676.  
  677. 3.  How big is the stack?  BASIC only gives you an 
  678.     eight word stack to play with.  On the other 
  679.     hand, it doesn't require you to save any 
  680.     registers except the segment registers.  Other 
  681.     languages give you a liberal stack, which makes 
  682.     things a lot easier.  If you have to create a 
  683.     new stack segment for yourself, the easiest 
  684.     thing is to place the stack at the end of your 
  685.     program and:
  686.  
  687.          CLI                ;suppress interrupts 
  688.                             ; while changing the 
  689.                             ; stack
  690.          MOV  SSAVE,SS      ;save old SS in local 
  691.                             ; storage (old SP
  692.                             ; already saved in BP)
  693.          MOV  SP,CS               ;switch
  694.          MOV  SS,SP               ;the
  695.          MOV  SP,OFFSET STACKTOP  ;stack
  696.          STI                      ;(maybe)
  697.  
  698.     Later, you can reverse these steps before 
  699.     returning to the caller.  At the end of your 
  700.     program, you place the stack itself:
  701.  
  702.              DW   128 DUP(?)   ;stack of 128 words 
  703.                                ; (liberal)
  704.     STACKTOP LABEL WORD
  705.  
  706. 4.  Make sure you save and restore those registers 
  707.     required by the caller.
  708.  
  709. 5.  Be sure to get the right kind of addressibili-
  710.     ty.  In the FAR call example, only CS addresses 
  711.     your segment.  If you are careful with your 
  712.     ASSUME statements the assembler will keep track 
  713.     of this fact and generate CS prefixes when you 
  714.     make data references; however, you might want 
  715.     to do something like
  716.  
  717.         MOV AX,CS      ;get current segment address
  718.         MOV DS,AX      ;To DS
  719.         ASSUME DS:THISSEG
  720.  
  721.     Be sure you keep your ASSUMEs in synch with 
  722.     reality.
  723.  
  724. Learning about BIOS and the hardware
  725. ___________________________________
  726.  
  727. You can't do everything with DOS calls.  You may 
  728. need to learn something about the BIOS and about 
  729. the hardware itself.  In this, the Technical 
  730. Reference is a very good thing to look at.
  731.  
  732. The first thing you look at in the Technical 
  733. Reference, unless you are really determined to 
  734. master the whole ball of wax, is the BIOS listings 
  735. presented in Appendix A. Glory be:  here is the 
  736. whole 8K of ROM which deals with low level hardware 
  737. support layed out with comments and everything.
  738.  
  739. In fact, if you are just interested in learning 
  740. what BIOS can do for you, you just need to read the 
  741. header comments at the beginning of each section of 
  742. the listing.
  743.  
  744. BIOS services are invoked by means of the INT 
  745. instruction; the BIOS occupies interrupts 10H 
  746. through 1FH and also interrupt 5H; actually, of 
  747. these seventeen interrupts, five are used for user 
  748. exit points or data pointers, leaving twelve actual 
  749. services.
  750.  
  751. In most cases, a service deals with a particular 
  752. hardware interface; for example, BIOS interrupt 10H 
  753. deals with the screen.  As with DOS function calls, 
  754. many BIOS services can be passed a function code in 
  755. the AH register and possible other arguments.
  756.  
  757. I am not going to summarize the most useful BIOS 
  758. features here; you will see some examples in the 
  759. next sample program we will look at.
  760.  
  761. The other thing you might want to get into with the 
  762. Tech reference is the description of some hardware 
  763. options, particularly the asynch adapter, which are 
  764. not well supported in the BIOS.  The writeup on the 
  765. asynch adapter is pretty complete.
  766.  
  767. Actually, the Tech reference itself is pretty 
  768. complete and very nice as far as it goes.  One 
  769. thing which is missing from the Tech reference is 
  770. information on the programmable peripheral chips on 
  771. the system board.  These include
  772.  
  773.       the 8259 interrupt controller
  774.       the 8253 timer
  775.       the 8237 DMA controller and
  776.       the 8255 peripheral interface
  777.  
  778. To make your library absolutely complete, you 
  779. should order the INTEL data sheets for these 
  780. beasts.
  781.  
  782. I should say, though, that the only one I ever 
  783. found I needed to know about was the interrupt 
  784. controller.  If you happen to have the 8086 Family 
  785. User's Manual, the big book put out by INTEL, which 
  786. is one of the things people sometimes buy to learn 
  787. about 8086 architecture, there is an appendix there 
  788. which gives an adequate description of the 8259.
  789.  
  790. A final example
  791. ______________
  792.  
  793. I leave you with a more substantial example of code 
  794. which illustrates some good elementary techniques; 
  795. I won't claim its style is perfect, but I think it 
  796. is adequate.  I think this is a much more useful 
  797. example than what you will get with the assembler:
  798.  
  799.  PAGE 61,132
  800.  TITLE SETSCRN -- Establish correct monitor use at 
  801.                   boot time
  802. ;
  803. ;This program is a variation on many which toggle 
  804. ;the equipment flags to support the use of either 
  805. ;video option (monochrome or color).  The thing 
  806. ;about this one is it prompts the user in such a 
  807. ;way that he can select the use of the monitor he 
  808. ;is currently looking at (or which is currently 
  809. ;connected or turned on) without really having to 
  810. ;know which is which.  SETSCRN is a good program to 
  811. ;put first in an AUTOEXEC.BAT file.
  812. ;
  813. ;This program is highly dependent on the hardware 
  814. ;and BIOS of the IBMPC and is hardly portable, 
  815. ;except to very exact clones.  For this reason, 
  816. ;BIOS calls are used in lieu of DOS function calls 
  817. ;where both provide equal function.
  818. ;
  819.  
  820.  
  821. OK.  That's the first page of the program.  Notice 
  822. the PAGE statement, which you can use to tell the 
  823. assembler how to format the listing.  You give it 
  824. lines per page and characters per line.  I have 
  825. mine setup to print on the host lineprinter; I 
  826. routinely upload my listings at 9600 baud and print 
  827. them on the host; it is faster than using the PC 
  828. printer.
  829.  
  830. There is also a TITLE statement.  This simply 
  831. provides a nice title for each page of your 
  832. listing.  Now for the second page:
  833.  
  834.  
  835.  
  836.  
  837.           SUBTTL -- Provide .COM type environment 
  838.                       and Data
  839.           PAGE
  840. ;
  841. ;         First, describe the one BIOS byte we are 
  842. ;           interested in
  843. ;
  844. BIOSDATA  SEGMENT   AT 40H    ;Describe where BIOS 
  845. ;                               keeps his data
  846.           ORG       10H       ;Skip parts we are 
  847. ;                               not interested in
  848. EQUIP     DB        ?         ;Equipment flag 
  849. ;                               location
  850. MONO      EQU       00110000B ;These bits on if 
  851. ;                               monochrome
  852. COLOR     EQU       11101111B ;Mask to make BIOS 
  853. ;                               think of the color 
  854. ;                               board
  855. BIOSDATA  ENDS                ;End of interesting 
  856. ;                               part
  857. ;
  858. ;         Next, describe some values for interrupts 
  859. ;           and functions
  860. ;
  861. DOS       EQU       21H      ;DOS Function Handler 
  862. ;                              INT code
  863. PRTMSG    EQU       09H      ;Function code to 
  864. ;                              print a message
  865. KBD       EQU       16H      ;BIOS keyboard 
  866. ;                              services INT code
  867. GETKEY    EQU       00H      ;Function code to 
  868. ;                              read a character
  869. SCREEN    EQU       10H      ;BIOS Screen services 
  870. ;                              INT code
  871. MONOINIT  EQU       02H      ;Value to initialize 
  872. ;                              monochrome screen
  873. ;COLORINIT EQU       03H     ;Value to initialize
  874. ;                              color screen (80x25)
  875. COLORINIT EQU      01H       ;Value to initialize 
  876. ;                              color screen (40X25)
  877. ;
  878. ;         Now, describe our own segment
  879. ;
  880. SETSCRN   SEGMENT            ;Set operating segment 
  881. ;                              for CODE and DATA
  882. ;
  883.           ASSUME CS:SETSCRN,DS:SETSCRN
  884.           ASSUME ES:SETSCRN,SS:SETSCRN 
  885. ;                             All segments
  886. ;
  887.           ORG       100H     ;Begin assembly at 
  888. ;                              standard .COM offset
  889. ;
  890. MAIN      PROC      NEAR     ;COM files use NEAR 
  891. ;                              linkage
  892.           JMP       BEGIN    ;And, it is helpful to 
  893. ;                              put the data first, 
  894. ;                              but then you must 
  895. ;                              branch around it.
  896. ;
  897. ;         Data used in SETSCRN
  898. ;
  899. CHANGELOC   DD      EQUIP    ;Location of the 
  900. ;                              EQUIP, recorded as 
  901. ;                              far pointer
  902. MONOPROMPT  DB 'Please press the plus ( + ) key.$'    
  903. ;                                 User sees on mono
  904. COLORPROMPT DB 'Please press the minus ( - ) key.$'   
  905. ;                                User sees on color
  906.  
  907. Several things are illustrated on this page.  
  908. First, in addition to titles, the assembler 
  909. supports subtitles:  hence the SUBTTL pseudo-op.  
  910. Second, the PAGE pseudo-op can be used to go to a 
  911. new page in the listing.  You see an example here 
  912. of the DSECT-style segment in the "SEGMENT AT 40H".  
  913. Here, our interest is in correctly describing the 
  914. location of some data in the BIOS work area which 
  915. really is located at segment 40H.
  916.  
  917. You will also see illustrated the EQU instruction, 
  918. which just gives a symbolic name to a number.  I 
  919. don't make a fetish of giving a name to every 
  920. single number in a program.  I do feel strongly, 
  921. though, that interrupts and function codes, where 
  922. the number is arbitrary and the function being 
  923. performed is the thing of interest, should always 
  924. be given symbolic names.
  925.  
  926.  
  927.  
  928.  
  929. One last new element in this section is the define 
  930. doubleword (DD) instruction.  A doubleword constant 
  931. can refer, as in this case, to a location in 
  932. another segment.  The assembler will be happy to 
  933. use information at its disposal to properly 
  934. assemble it.  In this case, the assembler knows 
  935. that EQUIP is offset 10 in the segment BIOSDATA 
  936. which is at 40H.
  937.  
  938.         SUBTTL -- Perform function
  939.         PAGE
  940. BEGIN:  CALL   MONOON                  ;Turn on 
  941. ;                                      mono display
  942.         MOV    DX,OFFSET MONOPROMPT    ;GET MONO 
  943. ;                                        PROMPT
  944.         MOV    AH,PRTMSG               ;ISSUE
  945.         INT    DOS                     ;IT
  946.         CALL   COLORON                 ;Turn on 
  947. ;                                     color display
  948.         MOV    DX,OFFSET COLORPROMPT   ;GET COLOR 
  949. ;                                        PROMPT
  950.         MOV    AH,PRTMSG               ;ISSUE
  951.         INT    DOS                     ;IT
  952.         MOV    AH,GETKEY               ;Obtain user 
  953. ;                                        response
  954.         INT    KBD
  955.         CMP    AL,'+'                  ;Does he 
  956. ;                                        want MONO?
  957.         JNZ    NOMONO
  958.         CALL   MONOON                  ;yes.  give 
  959. ;                                        it to him
  960. NOMONO: RET
  961. MAIN    ENDP
  962.  
  963.  
  964. The main code section makes use of subroutines to 
  965. keep the basic flow simple.  About all that's new 
  966. to you in this section is the use of the BIOS 
  967. interrupt KBD to read a character from the 
  968. keyboard.
  969.  
  970. Now for the subroutines, MONOON and COLORON:
  971.  
  972.         SUBTTL -- Routines to turn monitors on
  973.         PAGE
  974. MONOON  PROC   NEAR           ;Turn mono on
  975.         LES    DI,CHANGELOC   ;Get location to 
  976. ;                               change
  977.         ASSUME ES:BIOSDATA    ;TELL ASSEMBLER ABOUT 
  978. ;                               CHANGE TO ES
  979.         OR     EQUIP,MONO
  980.         MOV    AX,MONOINIT    ;Get screen 
  981. ;                              initialization value
  982.         INT    SCREEN         ;Initialize screen
  983.         RET
  984. MONOON  ENDP
  985. COLORON PROC   NEAR           ;Turn color on
  986.         LES    DI,CHANGELOC   ;Get location to 
  987. ;                               change
  988.         ASSUME ES:BIOSDATA    ;TELL ASSEMBLER ABOUT 
  989. ;                               CHANGE TO ES
  990.         AND    EQUIP,COLOR
  991.         MOV    AX,COLORINIT   ;Get screen 
  992. ;                              initialization value
  993.         INT    SCREEN         ;Initialize screen
  994.         RET
  995. COLORON ENDP
  996. SETSCRN ENDS                  ;End of segment
  997.         END    MAIN           ;End of assembly; 
  998. ;                               execution at MAIN
  999.  
  1000.  
  1001. The instructions LES and LDS are useful ones for 
  1002. dealing with doubleword addresses.  The offset is 
  1003. loaded into the operand register and the segment 
  1004. into ES (for LES) or DS (for LDS).  By telling the 
  1005. assembler, with an ASSUME, that ES now addresses 
  1006. the BIOSDATA segment, it is able to correctly 
  1007. assemble the OR and AND instructions which refer to 
  1008. the EQUIP byte.  An ES segment prefix is added.
  1009.  
  1010. To understand the action here, you simply need to 
  1011. know that flags in that particular byte control how 
  1012. the BIOS screen service initializes the adapters.  
  1013. BIOS will only work with one adapter at a time; by 
  1014. setting the equipment flags to show one or the 
  1015. other as installed and calling BIOS screen 
  1016. initialization, we achieve the desired effect.
  1017.  
  1018. The rest is up to you.
  1019. r the 
  1020. other as installed and calling BIOS scr