home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / books / 68k_book / arp_doc / chap_10.doc < prev    next >
Text File  |  1985-11-20  |  115KB  |  2,536 lines

  1.    Atari ST Machine Specific Programming In Assembly
  2.  
  3.    
  4. Chapter 10: AES Initialization Algorithms
  5.  
  6.  
  7.       Tired of typing file names on TTP input parameter lines?  I 
  8. assumed as much.  Fortunately, the designers of the ST's 
  9. operating system have provided an easier way to deal with such 
  10. trivia.  And the system that alleviates file selections also 
  11. contains many other tools with which a programmer can design 
  12. sophisticated interfacing algorithms.  The user-interface 
  13. features are provided by the Application Environment Services 
  14. (AES) segment of the ST's operating system.  Although the AES 
  15. appears to be unreasonably complex during a first study of its 
  16. repertoire, I intend to prove to you that using its routines is 
  17. one of the easier operating system manipulations.  But I'm not 
  18. going to try to do the job alone; I sincerely recommend that you 
  19. obtain at least one of two books and read it carefully, several 
  20. times, if necessary.  Because it is not possible for me to 
  21. anticipate your every need and desire.  Therefore, I want to show 
  22. you methods, not canned routines; you'll find lots of those in 
  23. other books.  Magazine articles are another valuable source of 
  24. AES information.  The more you read about the ST's operating 
  25. system, the less intimidated you will feel by its structure.
  26.      Of the two books, Atari ST GEM Programmer's Reference, by 
  27. Norbert Szczepanowski and Bernd Gunther, published by Abacus 
  28. Software; and COMPUTE!'s Technical Reference Guide--Atari ST, 
  29. Volume Two: GEM AES, by Sheldon Leemon; I feel that I must 
  30. recommend Leemon's.  The Abacus book omits too many AES 
  31. functions, and it hardly mentions assembly language; however, it 
  32. is just better than no book at all.  I should have more 
  33. enthusiastically recommended the Leemon book in chapter one.  My 
  34. enthusiasm had been dampened because Leemon did not include 
  35. assembly setups in the function reference section, Appendix A of 
  36. his book; but, then, neither does the Abacus book.  Nevertheless, 
  37. the assembly setups can more readily be inferred from the data 
  38. given in the Leemon book.  Furthermore, the Leemon book's 
  39. assembly language examples are more comprehensive than are those 
  40. in the Abacus book.
  41.  
  42. Initializing Programs With AES Subroutines
  43.  
  44.      The initialization algorithms that have been used in the 
  45. programs presented up to this point have been those that invoked 
  46. GEMDOS functions.  Incorporated into the design of those programs 
  47. were some BIOS and XBIOS function invocations also.  The AES 
  48. subroutines are invoked with a trap #2 exception generating 
  49. instruction.  When the GEMDOS, BIOS and XBIOS functions are 
  50. invoked, parameters are passed to the functions via the stack; 
  51. and returned parameters are passed in one or more registers.  
  52. Parameters are passed between invoking programs and AES 
  53. subroutines via arrays.  The arrays must be declared in the data 
  54. section of an assembly language program.
  55.      It is the descriptions concerning these arrays which 
  56. probably cause much of the confusion associated with the AES.  
  57. For example, on page 275 of the Abacus GEM book, the discussion 
  58. of AES functions and the associated arrays begins, just at the 
  59. conclusion of a VDI discussion.  The array descriptions begin on 
  60. the following page.  The first problem in the Abacus book is that 
  61. the authors omitted the int_out array in the list at the top of 
  62. page 276.  The second problem,    although it is not immediately 
  63. obvious that the information there is less accurate than that 
  64. presented elsewhere, is that the functions assigned to the 
  65. elements of the control array listed at the bottom of that page 
  66. do not agree with other references.
  67.      The Abacus book states that the parameters passed to contrl 
  68. (sometimes called control) elements 1 and 2 are the size of the 
  69. intin (sometimes called int_in or sintin) and intout (sometimes 
  70. called int_out or sintout) arrays, respectively, in bytes.  Atari 
  71. documentation declares the information to be the same as does the 
  72. Abacus book, however, that documentation reports that the size of 
  73. the data must be specified in words.  The COMPUTE! book insists 
  74. that the items of information stored in those control array 
  75. elements are the number of integer inputs passed in int_in and 
  76. the number of integer results returned in int_out (see page 11).  
  77. The Peel book (page 3-59) states the parameter values to be the 
  78. length of i/p coor table and length of o/p coor table, where 
  79. table length is in words; then, in tables under the AES function 
  80. headings, indicates that the values are the number of particular 
  81. parameter items passed.  It seems prudent to suspect that the 
  82. information given in the COMPUTE! book is the most consistent, 
  83. even though the parameter specifications are also given there in 
  84. both sets of terminology; because the information given on pages 
  85. 10 through 13 leads one to conclude that, if the size of each 
  86. parameter is one word long, and if it is the number of parameters 
  87. that is specified via the pertinent element of array control, 
  88. then the number of parameters, times the size of each parameter, 
  89. equals the length of the array portion, in words, that is being 
  90. passed.  Notice that I said array portion, not the entire size of 
  91. the declared array, as the references indicate.  That is, if the 
  92. number of int_in parameters is 2, then the size of the portion of 
  93. the int_in array being passed is 2 words.
  94.      There is a neat list of the applicable arrays on page 12 of 
  95. the COMPUTE! book; unfortunately, the ordering of the list is not 
  96. as it should be for storage of the addresses of the listed items 
  97. in the application parameter block (apb) structure.  The apb 
  98. structure is a pointer array in which the addresses to other 
  99. major AES structures are stored.  That ordering is shown on page 
  100. 13.  I hope to be able to prove to you that it is more the 
  101. inconsistency of data presentation in the references with which 
  102. we must contend than it is with AES function intricacy.  As you 
  103. will see if you look at page 46 of the COMPUTE! book, that 
  104. inconsistency is carried over into programming examples.  There 
  105. you see that not only have the names initially given for the 
  106. arrays been changed, but also the values used in the array 
  107. declarations: ds.w 8 for int_out (aintout), not ds.w 7 as 
  108. indicated on page 12; ds.w 18 for int_in (aintin), not ds.w 16 as 
  109. indicated on page 12; ds.l 3 for addr_in (addrin), not ds.l 2 as 
  110. indicated on page 12; and ds.l 2 for addr_out (addrout), not ds.l 
  111. 1 as indicated on page 12.  I shall begin the unraveling with 
  112. figure 10.1, in which I present the structure of the major AES 
  113. variables under discussion.
  114.    
  115. Figure 10.1. Major AES structures.  The apb structure is a 
  116. pointer array containing the addresses of the other structures; 
  117. each address requires one longword of storage, therefore, the 
  118. space occupied by the pointer array is 6 longwords.  Within each 
  119. structure, the digits indicate the element of the structure.  The 
  120. order in which the other 6 major AES structures are shown below 
  121. is the order in which the addresses of these structures must be 
  122. stored in the apb structure.
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149.  
  150.  
  151.   
  152.      Within the figure, I have indicated a specific length for 
  153. each structure; previously, I have spoken of array portions; 
  154. congruency is provided by the following statement: the size 
  155. declared for the int_in, int_out, addr_in and addr_out arrays 
  156. must be commensurable with the number of parameters required by 
  157. the specific AES functions invoked within a program.  For 
  158. example, when invoking the appl_init function (AES opcode $A), no 
  159. parameters are passed, and only one parameter is returned--via 
  160. the int_out array; therefore, the size declared for the int_out 
  161. array must be at least one word; the size declared for each of 
  162. the int_in, addr_in and addr_out arrays is irrelevant.  On the 
  163. other hand, when invoking the form_dial function (AES opcode $33 
  164. dec 51), 9 parameters are passed to the function via the int_in 
  165. array, and one parameter is returned via the int_out array.  That 
  166. function requires a declared size of at least 18 words for the 
  167. int_in array and at least 1 word for the int_out array.  In the 
  168. figure, you can see that the int_in array is illustrated as a 16 
  169. word length structure.  That's because I used the structure 
  170. lengths indicated on page 12 of the COMPUTE! book.
  171.      What all of this means is that, in order to declare arrays 
  172. of sufficient size for the functions to be invoked within a 
  173. program, you must look under the function headings in your 
  174. reference book for each of the functions to be used; determine 
  175. which function requires the most parameters to be passed and 
  176. which requires the most to be returned (these values may not 
  177. coincide for a particular function); then declare sizes that meet 
  178. the needs of the functions with the greatest number of 
  179. parameters.  If the form_dial function is not to be invoked 
  180. within a program, then the size of the int_in array need not be 
  181. sufficient to contain 9 word size integers.
  182.      The sizes declared for the apb, control and global 
  183. structures are constant for every application.  Notice that the 
  184. structure shown for global is constructed of both word size and 
  185. longword size elements.  I should also mention that, since the 
  186. addresses of the four arrays for which the sizes need not be 
  187. constant must still be stored in the apb array, even if no 
  188. parameter exchange takes place, I would consider it prudent to 
  189. declare a size of at least 1 element for each of those four.  One 
  190. other point; when declaring space for variables, a declaration of 
  191. ds.w 8 reserves 16 bytes, as does a declaration of ds.b 16, or a 
  192. declaration of ds.l 4, or a declaration of ds.w 4 followed by 
  193. ds.l 2.  We use one method or another simply to satisfy our human 
  194. sense of structure--the machine does not give a damn.  The values 
  195. that are to be stored in each of the array elements are specified 
  196. under the function headings in each reference book.  I find the 
  197. presentations in the Leemon book to be the easiest to decipher.
  198.      It is time for an example program.  But before that, let me 
  199. stress a point.  The structure element digits shown in figure 
  200. 10.1 are not the numbers by which we reference those elements in 
  201. assembly language programming.  That's because the addressing 
  202. modes we use require byte size offsets.  Suppose that the 
  203. appl_init function is being invoked.  Preparatory to the trap #2 
  204. invocation, 5 values must be stored in the control array.  One 
  205. method of accomplishing the required storage is to load the 
  206. address of the array in a register, say A4.  The function opcode 
  207. would then be stored in control array element 0 with the 
  208. instruction move.w #$A, (A4), where the digit 0 can be assumed to 
  209. exist in front of (A4).  But this 0 does not reference element 0 
  210. of the array; it references a memory location that is at offset 0 
  211. from the base address stored in register A4.  If we know that the 
  212. other control array elements contain the value 0, then we would 
  213. not have to store that value in control array elements 1, 3 and 
  214. 4; but we would have to store the value 1 in element 2.  That 
  215. would be accomplished with the instruction move.w #1, 4(A4) 
  216. because element 2 of the control array is located in memory at an 
  217. offset of 4 bytes from the base address of the array.  When the 
  218. length of each element of a structure is identical, an element's 
  219. offset, in bytes, is calculated by multiplying the element number 
  220. by the element length; hence, 2 (for element 2) times 2 bytes 
  221. (the element length) equals 4 bytes offset.
  222.      There is another method of passing parameters in the control 
  223. array, which some programmers find attractive.  For each function 
  224. to be invoked within a program, an array bearing the function's 
  225. name and containing all required parameters is declared within 
  226. the data section.  Then, before the trap #2 call, the address of 
  227. the function being invoked is stored within the apb at the slot 
  228. reserved for array control.  You may see that method used in 
  229. magazine articles.  You can try each method and choose the one 
  230. that suits you best.
  231.  
  232. AES Structure Volatility
  233.   
  234.      Although this chapter is the official introduction to 
  235. programs which utilize AES functions, I included program 57 
  236. (REG_TST5.S) in chapter eight because it seemed to fit there more 
  237. naturally.  Using that program, I showed that the ST's operating 
  238. system protects the registers which are being used by a desk 
  239. accessory while it is waiting for a message event.  In this 
  240. chapter I will concentrate on programs that investigate the 
  241. volatility of AES arrays during AES function invocation.  Because 
  242. the primary, investigatory, algorithm does not vary much from 
  243. example to example within the chapter, I will introduce ancillary 
  244. functions selected from the other segments of the operating 
  245. system at relevant locations, so that you may become familiar 
  246. with their usage.
  247.  
  248.   
  249. Program 76. A program that investigates AES array corruption 
  250. during AES function invocation and saves the results in a disk 
  251. file.
  252.  
  253.  ; Program Name: PRG_8AR.S
  254.  ;      Version: 1.005
  255.  
  256.  ; Assembly Instructions:
  257.  
  258.  ;      Assemble in Relocatable mode and save with a PRG extension.  From
  259.  ; the desktop, change the extension to ACC.  Programs that are to function
  260.  ; as desk accessories MUST be assembled in Relocatable mode.  If you design
  261.  ; a desk accessory so that it can be assembled in PC-relative mode, and if
  262.  ; you attempt to load that accessory via MultiDesk, you will receive an
  263.  ; error pertaining to the attempt to read in the accessory.  If you place
  264.  ; that accessory in the boot directory, the system will reset every time
  265.  ; it attempts to load the accessory.  Sei gewarnt! 
  266.  
  267.  ; Function:
  268.  
  269.  ;      This program is used to observe system corruption of AES arrays
  270.  ; during the execution of AES functions.  Data is output into a file which
  271.  ; assumes the name declared at the variable "filename".  You may wish to
  272.  ; alter the file's path to something like "A:\PRG_8DR.DAT" if you decide to
  273.  ; assemble and execute the program.
  274.  
  275.  ;      The program's output data is written with GEMDOS function $9, the write
  276.  ; string to screen function.  But the data is redirected to the file with
  277.  ; GEMDOS function $46, the f_force function.
  278.  
  279.  ; Execution Instructions:
  280.  
  281.  ;      Place PRG_8AR.ACC in the root directory of your boot disk.  During
  282.  ; the next power-up cycle, the program will be installed in memory as a desk
  283.  ; accessory; assuming that it is not blocked by a program that permits desk
  284.  ; accessory loading selections.  Of course, if you use MultiDesk, you need
  285.  ; not power up to use PRG_8AR.ACC immediately.
  286.  
  287.  ;      The desk accessory is identified as a menu selection by the name
  288.  ; Accessory Arrays.  Choose the desk accessory twice so that the contents
  289.  ; of the arrays will be printed twice from within the message handler.  The
  290.  ; message handler is disabled after it has been invoked twice.
  291.  
  292.  ; MAJOR NOTE:
  293.  
  294.  ;      Although accessories such as MultiDesk add significant power to a ST
  295.  ; system; and although initial testing of a desk accessory program can be
  296.  ; accomplished via MultiDesk loading of the program, thereby alleviating the
  297.  ; the testing process, the program under test cannot be pronounced correct
  298.  ; until it has been permitted to load normally from the boot disk.
  299.  
  300.  ;      For example, if the first statement of this program, which loads the
  301.  ; stack's address into A7, is moved to a location following any instruction
  302.  ; that requires stack usage, the accessory will function perfectly when it is
  303.  ; loaded and executed via MultiDesk; but it will bomb during boot because, at
  304.  ; that time, no default stack has yet been assigned by the system.
  305.  
  306.  ; REMEMBER:  Programs executed during boot MUST provide their own stacks.
  307.  
  308.  ; NOTE: Within the program, registers are used as variables as much as
  309.  ;       possible to speed things up.
  310.  
  311.  lea        stack(pc), a7        ; This must be the first instruction.
  312.  
  313. create_output_file:              ; COMPUTE! TOS book page 270.
  314.  move.w     #0, -(sp)            ; File attribute = read/write.
  315.  pea        filename(pc)
  316.  move.w     #$3C, -(sp)          ; Function = f_create = GEMDOS $3C.
  317.  trap       #1                   ; File handle is returned in D0.
  318.  addq.l     #8, sp
  319.  move.w     d0, handle
  320.  
  321. redirect_output_bound_for_screen:
  322.  
  323.  ; NOTE: If the output file's handle is exchanged with the video screen's
  324.  ;       handle, then the printline function = GEMDOS $9 can be used to 
  325.  ;       write to the file.
  326.  
  327. redirect_output:                 ; Exchange file handle with screen's handle.
  328.  move.w     handle(pc), -(sp)    ; This is the disk file's handle.
  329.  move.w     #1, -(sp)            ; This is the video screen's handle.
  330.  move.w     #$46, -(sp)          ; Function = f_force = GEMDOS $46.
  331.  trap       #1
  332.  addq.l     #6, sp
  333.  
  334. initialize_register_variables:
  335.  move.w     #$C8, d3             ; *** D3 is variable for AES call number.
  336.  lea        aes_pb(pc), a5       ; A5 is pointer to array address block.
  337.  lea        control(pc), a4      ; A4 is pointer for array 'control'.
  338.  lea        hex_table(pc), a3    ; A3 points to hexadecimal ASCII digits.
  339.  
  340.  ; For each test point, the contents of each AES array are printed.
  341.  
  342.  ;                           
  343.  ;                 TEST POINT 0: Before appl_init     
  344.  ;                           
  345.  bsr        print_arrays
  346.  
  347. initialize_application:          ; COMPUTE! AES book page 223.
  348.  
  349.  ; Application identification = apid returned in int_out[0] and global[2].
  350.  
  351.  move.w     #$A, (a4)            ; Function = appl_init = AES $A.
  352.  move.w     #0, 2(a4)            ; Input no 16-bit integer parameters.
  353.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.
  354.  move.w     #0, 6(a4)            ; Input no 32-bit pointer parameters.
  355.  move.w     #0, 8(a4)            ; Return no 32-bit pointer parameters.
  356.  bsr        aes                  ; Invoke trap #2 AES exception.
  357.  
  358.  ;
  359.  ;          TEST POINT 1: After appl_init, before menu_register
  360.  ;
  361.  bsr        print_arrays
  362.  
  363. menu_installation:               ; COMPUTE! AES book page 248.
  364.  
  365.  ; Menu identification number returned in int_out[0].
  366.              
  367.  move.w     #$23, (a4)           ; Function = menu_register = AES $23.
  368.  move.w     #1, 2(a4)            ; Input one 16-bit integer parameter.
  369.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.    
  370.  move.w     #1, 6(a4)            ; Input one 32-bit pointer parameter.
  371.  move.w     #0, 8(a4)            ; Return no 32-bit pointer parameters.
  372.  lea        global(pc), a0       ; Fetch address of global array.
  373.  move.w     4(a0), int_in        ; Application identification to int_in[0].
  374.  move.l     #menu_text, addr_in  ; Menu text address to addr_in[0].
  375.  bsr        aes                 
  376.  move.w     int_out(pc), menu_id ; Store menu identification number.
  377.  
  378.  ; MAIN ACCESSORY LOOP
  379.  
  380.  ;
  381.  ;          TEST POINT 2: After menu_register, before evnt_mesag
  382.  ;
  383.  bsr        print_arrays
  384.  
  385.  move.l     #message, addr_in    ; Address of message array to addr_in. 
  386. wait_for_message:                ; COMPUTE! AES book page 235.
  387.  move.w     #$17, (a4)           ; Function = evnt_mesag = AES $17.
  388.  move.w     #0, 2(a4)            ; Input one 16-bit integer parameter.
  389.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.    
  390.  move.w     #1, 6(a4)            ; Input one 32-bit pointer parameter.
  391.  move.w     #0, 8(a4)            ; Return no 32-bit pointer parameters.
  392.  bsr        aes
  393.  
  394.  ; When a message is received it is placed in array 'message'.
  395.  
  396.  ; ****************************************************************************
  397.  ; ****************************************************************************
  398.  
  399. message_handler:                 ; Entrance point when message is received.
  400.  lea        message(pc), a0      ; Fetch address of array 'message'.
  401.  cmpi.w     #$28, (a0)           ; Compare ACCESSORY OPEN code with message[0].
  402.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  403.  move.w     8(a0), d0            ; The menu item selected is stored in element
  404.                                  ; four (message[4]) of array 'message'.  This
  405.                                  ; application's id # is in menu_id.
  406.  cmp.w      menu_id(pc), d0      ; Was this application selected.
  407.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  408.  
  409.  ; ****************************************************************************
  410.  ; ****************************************************************************
  411.  
  412.  ; Execution proceeds past this point only when this application has been
  413.  ; selected from the menu.
  414.  
  415.  cmpi.w     #5, test             ; Have five array groups been printed?
  416.  beq        wait_for_message     ; Disable message handler if true.
  417.  ;
  418.  ;          TEST POINT 3: In message handler, before evnt_mesag
  419.  ;
  420.  bsr        print_arrays
  421.  cmpi.w     #5, test             ; Branch after 2nd entrance in message handler.
  422.  beq.s      close_file         
  423.  bra        wait_for_message     ; Execute the evnt_mesag function.
  424.  
  425. close_file:
  426.  move.w     handle(pc), -(sp) 
  427.  move.w     #$3E, -(sp)          ; Function = GEMDOS $3E = f_close.
  428.  trap       #1
  429.  addq.l     #4, sp
  430. redirect_screen_output:
  431.  move.w     #1, -(sp)            ; This is the screen's handle.
  432.  move.w     handle(pc), -(sp)    ; This is the file's handle.
  433.  move.w     #$46, -(sp)          ; Function = f_force = GEMDOS $46.
  434.  trap       #1
  435.  addq.l     #6, sp
  436.  bra        wait_for_message
  437.  
  438.  ;
  439.  ; SUBROUTINES
  440.  ;
  441.  
  442. print_arrays:
  443.  lea        newline(pc), a0       
  444.  bsr        print_line        
  445.  lea        test_header(pc), a0  ; Setup to fetch test point header.
  446.  move.w     test(pc), d0         ; Load test point number into D0.
  447.  lsl.w      #2, d0               ; Multiply by 4 to reach next pointer slot.     
  448.  movea.l    0(a0,d0.w), a0       ; Print test point header.
  449.  bsr        print_line
  450.  lea        pre_spaces(pc), a0   ; Print spaces before column headers.
  451.  bsr        print_line
  452.  lea        aes_names(pc), a0    ; Print AES array column headers.
  453.  bsr        print_line
  454.  lea        pre_spaces(pc), a0   ; Print spaces before underline.
  455.  bsr        print_line
  456.  lea        aes_underline(pc), a0; Print AES underline.
  457.  bsr        print_line
  458.  moveq.l    #0, d7               ; D7 is up counter to print 5 rows.
  459.  moveq.l    #4, d6               ; D6 is down counter to print 5 elements.
  460. put_row:
  461.  lea        buffer(pc), a0       ; Buffer is an 80 byte line buffer.
  462.  movea.l    a5, a6               ; Copy aes parameter block address.
  463.  move.w     #5, d5               ; D5 is array counter for 6 arrays.
  464.  move.w     #11, d0              ; Print beginning spaces to line up columns.
  465. put_space:
  466.  move.b     #$20, (a0)+
  467.  dbra       d0, put_space
  468. put_element:                     ; Print contents of array element.
  469.  move.w     d7, d0               ; Print array element number.
  470.  andi.b     #$F, d0              ; Mask most significant nibble.
  471.  move.b     0(a3,d0.w), d0       ; Store appropriate hex character in D0.
  472.  move.b     d0, (a0)+
  473.  move.b     #$3A, (a0)+          ; A colon.
  474.  move.b     #$20, (a0)+          ; A space.
  475.  move.w     d7, d0               ; Multiply contents of D7 by 2 in D0 to
  476.  lsl.w      #1, d0               ; obtain offset for next array element.
  477.  movea.l    (a6)+, a1            ; Copy array address into A1 and increment
  478.                                  ; A6 to point to next array address.
  479.  move.w     0(a1,d0.w), d0       ; Fetch contents of array element.
  480.  moveq      #3, d2               ; D2 is loop counter for ASCII conversion.
  481. convert_digit:                   ; Convert a nibble, then print it.
  482.  rol.w      #4, d0               ; Rotate most significant nibble to the
  483.                                  ; least significant nibble position.
  484.  move.b     d0, d1               ; Copy least significant byte of D0 to D1.
  485.  andi.b     #$F, d1              ; Mask out most significant nibble of D1.
  486.  ext.w      d1                   ; Extend to word length.
  487.  move.b     0(a3,d1.w), d1       ; Fetch ASCII hex digit to D1.
  488. put_digit:
  489.  move.b     d1, (a0)+
  490.  dbra       d2, convert_digit    ; Loop until D2 = -1.
  491. _put_spaces:
  492.  move.b     #$20, (a0)+
  493.  move.b     #$20, (a0)+
  494.  dbra       d5, put_element
  495.  move.b     #0, (a0)             ; Store NULL at end of string.
  496.  lea        buffer(pc), a0
  497.  bsr        print_line
  498.  lea        newline(pc), a0      ; Print a newline.
  499.  bsr        print_line
  500.  addi.w     #1, d7               ; Increment up counter.
  501.  dbra       d6, put_row
  502.  add.w      #1, test             ; Increment test for next test point.
  503.  rts
  504.  
  505. aes:                             ; COMPUTE! AES book page 13,
  506.  move.l     a5, d1               ; Address of aes_pb.
  507.  move.w     d3, d0               ; AES identifier = $C8.
  508.  trap       #2
  509.  rts
  510.  
  511. print_line:
  512.  move.l     a0, -(sp)            ; Push string onto stack.
  513.  move.w     #9, -(sp)            ; Function = c_conws.
  514.  trap       #1
  515.  addq.l     #6, sp
  516.  rts
  517.  
  518.  data
  519. aes_pb:       dc.l  control,global,int_in,int_out,addr_in,addr_out
  520. test_header:  dc.l  zero,one,two,three,four
  521. zero:
  522.  dc.b $D,$A,'TEST POINT 0: Before appl_init',$D,$A,$D,$A,0
  523. one:
  524.  dc.b $D,$A,'TEST POINT 1: After appl_init, before menu_register',$D,$A,$D,$A,0
  525. two:
  526.  dc.b $D,$A,'TEST POINT 2: After menu_register, before evnt_mesag',$D,$A,$D,$A,0
  527. three:
  528.  dc.b $D,$A,'TEST POINT 3: In message handler, before evnt_mesag',$D,$A,$D,$A,0
  529. four:
  530.  dc.b $D,$A,'TEST POINT 4: In message handler second time',$D,$A,$D,$A,0
  531.  
  532. hex_table:    dc.b   '0123456789ABCDEF'
  533. newline:      dc.b   $D,$A,0
  534. aes_header:   dc.b   '                                AES ARRAYS',$D,$A,0
  535. aes_names:    dc.b   'CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  '
  536.               dc.b   'ADDR_OUT',$D,$A,0
  537. aes_underline:dc.b   '-------  -------  -------  -------  -------  '
  538.               dc.b   '--------',$D,$A,0
  539. pre_spaces:   dc.b   '            ',0
  540. spaces:       dc.b   '  ',0
  541. menu_text:    dc.b   '  Accessory Arrays ',0
  542. filename:     dc.b   'E:\PRG_8\PRG_8AR.DAT',0
  543.  
  544.  bss
  545.  align
  546.  
  547.  ;
  548.  ; AES ARRAYS:
  549.  ;
  550.  
  551.  ;     The addresses of the arrays declared below will be stored in the pointer
  552.  ; array 'aes_pb'.  That happens because the program is assembled in Relocatable
  553.  ; mode.  The sizes declared for the arrays are identical simply because I am
  554.  ; making it easy to line up the program's output.
  555.  
  556. control:      ds.w     5  ; Control parameters.
  557. global:       ds.w     5  ; Global parameters.
  558. int_in:       ds.w     5  ; Input parameters.
  559. int_out:      ds.w     5  ; Output parameters.
  560. addr_in:      ds.w     5  ; Input addresses.
  561. addr_out:     ds.w     5  ; Output addresses.
  562.  
  563.  ;
  564.  ; APPLICATION VARIABLES
  565.  ;
  566.  
  567. handle:       ds.w     1
  568. test:         ds.w     1
  569. message:      ds.l     4  ; 16 byte array.
  570. menu_id:      ds.w     1
  571. buffer:       ds.b    80
  572.               ds.l   300  ; Program stack.
  573. stack:        ds.l     0  ; Address of program stack.
  574.  end
  575.  
  576.  
  577. Execution Results
  578.  
  579. TEST POINT 0: Before appl_init
  580.  
  581.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  582.             -------  -------  -------  -------  -------  --------
  583.             0: 0000  0: 0000  0: 0000  0: 0000  0: 0000  0: 0000  
  584.             1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  
  585.             2: 0000  2: 0000  2: 0000  2: 0000  2: 0000  2: 0000  
  586.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  587.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  588.  
  589. TEST POINT 1: After appl_init, before menu_register
  590.  
  591.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  592.             -------  -------  -------  -------  -------  --------
  593.             0: 000A  0: 0120  0: 0000  0: 0003  0: 0000  0: 0000  
  594.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0000  1: 0000  
  595.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  596.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  597.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  598.  
  599. TEST POINT 2: After menu_register, before evnt_mesag
  600.  
  601.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  602.             -------  -------  -------  -------  -------  --------
  603.             0: 0023  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  604.             1: 0001  1: 0001  1: 0000  1: 0000  1: D341  1: 0000  
  605.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  606.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  607.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  608.  
  609. TEST POINT 3: In message handler, before evnt_mesag
  610.  
  611.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  612.             -------  -------  -------  -------  -------  --------
  613.             0: 0017  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  614.             1: 0000  1: 0001  1: 0000  1: 0000  1: D3AA  1: 0000  
  615.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  616.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  617.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  618.  
  619. TEST POINT 4: In message handler second time
  620.  
  621.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  622.             -------  -------  -------  -------  -------  --------
  623.             0: 0017  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  624.             1: 0000  1: 0001  1: 0000  1: 0000  1: D3AA  1: 0000  
  625.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  626.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  627.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  628.  
  629. Execution Results Discussion
  630.   
  631.      Looking at the contents of the AES structures displayed in 
  632. the test point tables produced by program 76, you should be able 
  633. to spot relevant values passed to the AES system and returned 
  634. therefrom.  At test point 1, the second element of global 
  635. (global[2]) contains the application identification number, 3.  
  636. Identical data can be found in the first element of int_out 
  637. (int_out[0]).  At test point 2, the menu identification number, 
  638. 1, is evident in int_out[0].  Of particular interest is the fact 
  639. that there is no structure content corruption beyond that 
  640. purposely imposed by the program involved.  We will be able to 
  641. use this information to simplify function invocation in future 
  642. programs.  Note especially that the contents of addr_in[0] remain 
  643. stable between evnt_mesag invocations; that is the address of the 
  644. message buffer.  Remember that this stability is constant only as 
  645. long as no other AES function invocation disturbs the contents of 
  646. addr_in[0].
  647.  
  648. Storing Data in the File With GEMDOS $40
  649.   
  650.      It is always advantageous to understand several methods of 
  651. accomplishing a task, especially when the system is as complex as 
  652. is the ST, therefore, I want to present a method of storing data 
  653. in the file which does not rely on GEMDOS function $9 and output 
  654. redirection.  Program 77 stores data in a buffer of sufficient 
  655. size, then it writes the contents of that buffer to disk using 
  656. GEMDOS function $40.  I have chosen a particular directory path 
  657. for the data file; if you decide to experiment with the program, 
  658. you may want to alter that path.
  659.      Since we have learned that we can rely on the stability of 
  660. AES structures during AES function invocation, several short cuts 
  661. have been taken in program 77's 'control' array setups.  Program 
  662. 78 accomplishes the same task as does program 77, but it permits 
  663. the data file's directory path to be chosen via the AES file 
  664. selector function.  In addition, program 78 uses a group of 
  665. predeclared, function named arrays that are substituted for 
  666. 'control' in the aes parameter block array, as each AES function 
  667. is invoked.
  668.    
  669. Program 77. Writing data to the desk accessory's data file using 
  670. a buffer and GEMDOS function $40 .
  671.  
  672.  ; Program Name: PRG_8BR.S
  673.  ;      Version: 1.002
  674.  
  675.  ; Assembly Instructions:
  676.  
  677.  ;      Assemble in Relocatable mode and save with a PRG extension.  From
  678.  ; the desktop, change the extension to ACC.  Programs that are to function
  679.  ; as desk accessories MUST be assembled in Relocatable mode.  If you design
  680.  ; a desk accessory so that it can be assembled in PC-relative mode, and if
  681.  ; you attempt to load that accessory via MultiDesk, you will receive an
  682.  ; error pertaining to the attempt to read in the accessory.  If you place
  683.  ; that accessory in the boot directory, the system will reset every time
  684.  ; it attempts to load the accessory.  Sei gewarnt! 
  685.  
  686.  ; Function:
  687.  
  688.  ;      This program is used to observe system corruption of AES arrays
  689.  ; during the execution of AES functions, as does PRG_8AR.S, and this program
  690.  ; also creates a file in which to store its output data, but this program
  691.  ; does not write its data via GEMDOS function $9 and redirection; instead it
  692.  ; stores the data in a buffer, then writes the contents of the buffer to the
  693.  ; file using GEMDOS function $40.
  694.  
  695.  ;      Also note some of the short cuts taken to prepare the control array
  696.  ; for each AES function.
  697.  
  698.  ; Execution Instructions:
  699.  
  700.  ;      Place PRG_8BR.ACC in the root directory of your boot disk.  During the
  701.  ; next power-up cycle, the desk accessory will be installed.  From the desktop
  702.  ; select 'Accessory Arrays' two times in order to store the pertinent data in
  703.  ; the file buffer.  After the second selection, the file will be created and
  704.  ; the buffer's contents will be written thereto.  You can execute the desk
  705.  ; accessory from within MultiDesk if you desire.  You may want to change the
  706.  ; path for the file so that it is created on another disk or partition.
  707.  
  708.  lea        stack, a7            ; This must be the first instruction.
  709.  
  710. initialize_register_variables:
  711.  lea        buffer(pc), a5       ; A5 is pointer to buffer.
  712.  lea        control(pc), a4      ; A4 is pointer for array 'control'.
  713.  lea        hex_table(pc), a3    ; A3 points to hexadecimal ASCII digits.
  714.  
  715.  ; For each test point, the contents of each AES array are printed.
  716.  
  717.  ;                           
  718.  ;                 TEST POINT 0: Before appl_init     
  719.  ;                           
  720.  bsr        print_arrays
  721.  
  722. initialize_application:          ; COMPUTE! AES book page 223.
  723.  
  724.  ; Application identification = apid returned in int_out[0] and global[2].
  725.  
  726.  move.w     #$A, (a4)            ; Function = appl_init = AES $A.
  727.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.
  728.  bsr        aes                  ; Invoke trap #2 AES exception.
  729.  
  730.  ;
  731.  ;          TEST POINT 1: After appl_init, before menu_register
  732.  ;
  733.  bsr        print_arrays
  734.  
  735. menu_installation:               ; COMPUTE! AES book page 248.
  736.  
  737.  ; Menu identification number returned in int_out[0].
  738.              
  739.  move.w     #$23, (a4)           ; Function = menu_register = AES $23.
  740.  move.w     #1, 2(a4)            ; Input one 16-bit integer parameter.
  741.  move.w     #1, 6(a4)            ; Input one 32-bit pointer parameter.
  742.  lea        global(pc), a0       ; Fetch address of global array.
  743.  move.w     4(a0), int_in        ; Application identification to int_in[0].
  744.  move.l     #menu_text, addr_in  ; Menu text address to addr_in[0].
  745.  bsr        aes                 
  746.  move.w     int_out(pc), menu_id ; Store menu identification number.
  747.  
  748.  ; MAIN ACCESSORY LOOP
  749.  
  750.  ;
  751.  ;          TEST POINT 2: After menu_register, before evnt_mesag
  752.  ;
  753.  bsr        print_arrays
  754.  
  755.  move.l     #message, addr_in    ; Address of message array to addr_in. 
  756.  move.w     #$17, (a4)           ; Function = evnt_mesag = AES $17.
  757.  move.w     #0, 2(a4)            ; Input one 16-bit integer parameter.
  758.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.    
  759.  move.w     #1, 6(a4)            ; Input one 32-bit pointer parameter.
  760. wait_for_message:                ; This simplification is not always
  761.  bsr        aes                  ; appropriate.
  762.  
  763.  ; When a message is received it is placed in array 'message'.
  764.  
  765.  ; ****************************************************************************
  766.  ; ****************************************************************************
  767.  
  768. message_handler:                 ; Entrance point when message is received.
  769.  lea        message(pc), a0      ; Fetch address of array 'message'.
  770.  cmpi.w     #$28, (a0)           ; Compare ACCESSORY OPEN code with message[0].
  771.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  772.  move.w     8(a0), d0            ; The menu item selected is stored in element
  773.                                  ; four (message[4]) of array 'message'.  This
  774.                                  ; application's id # is in menu_id.
  775.  cmp.w      menu_id(pc), d0      ; Was this application selected.
  776.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  777.  
  778.  ; ****************************************************************************
  779.  ; ****************************************************************************
  780.  
  781.  ; Execution proceeds past this point only when this application has been
  782.  ; selected from the menu.
  783.  
  784.  ;
  785.  ;          TEST POINT 3: In message handler, before evnt_mesag
  786.  ;
  787.  cmpi.w     #5, test             ; Have five array groups been printed?
  788.  beq        wait_for_message     ; This effectively disables the handler.
  789.  bsr        print_arrays
  790.  cmpi.w     #5, test             ; Branch after 2nd entrance in message handler.
  791.  beq.s      store_in_file        ; Create file and store buffer contents.
  792.  bra        wait_for_message     ; Execute the evnt_mesag function.
  793.  
  794. store_in_file:
  795.  lea        buffer(pc), a6       ; Fetch start address.
  796.  suba.l     a6, a5               ; Calculate number of bytes stored in buffer.
  797.  
  798. create_file:                     ; COMPUTE! TOS book page 270.
  799.  move.w     #0, -(sp)            ; File attribute = read/write.
  800.  pea        filename(pc)
  801.  move.w     #$3C, -(sp)          ; Function = f_create = GEMDOS $3C.
  802.  trap       #1                   ; File handle is returned in D0.
  803.  addq.l     #8, sp
  804.  move.w     d0, d4               ; Save file handle in D4.
  805.  
  806. write_buffer_to_file:            ; Function = f_write.  COMPUTE! TOS p.274.
  807.  pea        (a6)                 ; Push buffer's address.
  808.  move.l     a5, -(sp)            ; Push byte count length.
  809.  move.w     d4, -(sp)            ; COMPUTE!'s TOS book incorrectly specifies
  810.  move.w     #$40, -(sp)          ; a longword operation here; see page 274.
  811.  trap       #1
  812.  lea        $C(sp), sp
  813.  
  814. close_output_file:               ; COMPUTE! TOS book page 272.
  815.  move.w     d4, -(sp)            ; Push file handle. 
  816.  move.w     #$3E, -(sp)          ; Function = GEMDOS $3E = f_close.
  817.  trap       #1
  818.  addq.l     #4, sp
  819.  bra        wait_for_message     ; Execute the evnt_mesag function.
  820.  
  821.  ;
  822.  ; SUBROUTINES
  823.  ;
  824.  
  825. print_arrays:
  826.  lea        newline(pc), a0       
  827.  bsr        store_line        
  828.  lea        test_header(pc), a0  ; Setup to fetch test point header.
  829.  move.w     test(pc), d0         ; Load test point number into D0.
  830.  lsl.w      #2, d0               ; Multiply by 4 to reach next pointer slot.     
  831.  movea.l    0(a0,d0.w), a0       ; Print test point header.
  832.  bsr        store_line
  833.  lea        pre_spaces(pc), a0   ; Print spaces before column headers.
  834.  bsr        store_line
  835.  lea        aes_names(pc), a0    ; Print AES array column headers.
  836.  bsr        store_line
  837.  lea        pre_spaces(pc), a0   ; Print spaces before underline.
  838.  bsr        store_line
  839.  lea        aes_underline(pc), a0; Print AES underline.
  840.  bsr        store_line
  841.  moveq.l    #0, d7               ; D7 is up counter to print 5 rows.
  842.  moveq.l    #4, d6               ; D6 is down counter to print 5 elements.
  843. put_row:
  844.  lea        aes_pb(pc), a6       ; Fetch parameter block address.
  845.  move.w     #5, d5               ; D5 is array counter for 6 arrays.
  846.  move.w     #11, d0              ; Print beginning spaces to line up columns.
  847. put_space:
  848.  move.b     #$20, (a5)+
  849.  dbra       d0, put_space
  850. put_element:                     ; Print contents of array element.
  851.  move.w     d7, d0               ; Print array element number.
  852.  andi.b     #$F, d0              ; Mask most significant nibble.
  853.  move.b     0(a3,d0.w), d0       ; Store appropriate hex character in D0.
  854.  move.b     d0, (a5)+
  855.  move.b     #$3A, (a5)+          ; A colon.
  856.  move.b     #$20, (a5)+          ; A space.
  857.  move.w     d7, d0               ; Multiply contents of D7 by 2 in D0 to
  858.  lsl.w      #1, d0               ; obtain offset for next array element.
  859.  movea.l    (a6)+, a1            ; Copy array address into A1 and increment
  860.                                  ; A6 to point to next array address.
  861.  move.w     0(a1,d0.w), d0       ; Fetch contents of array element.
  862.  moveq      #3, d2               ; D2 is loop counter for ASCII conversion.
  863. convert_digit:                   ; Convert a nibble, then print it.
  864.  rol.w      #4, d0               ; Rotate most significant nibble to the
  865.                                  ; least significant nibble position.
  866.  move.b     d0, d1               ; Copy least significant byte of D0 to D1.
  867.  andi.b     #$F, d1              ; Mask out most significant nibble of D1.
  868.  ext.w      d1                   ; Extend to word length.
  869.  move.b     0(a3,d1.w), d1       ; Fetch ASCII hex digit to D1.
  870. put_digit:
  871.  move.b     d1, (a5)+
  872.  dbra       d2, convert_digit    ; Loop until D2 = -1.
  873. _put_spaces:
  874.  move.b     #$20, (a5)+
  875.  move.b     #$20, (a5)+
  876.  dbra       d5, put_element
  877.  lea        newline(pc), a0      ; Print a newline.
  878.  bsr        store_line
  879.  addi.w     #1, d7               ; Increment up counter.
  880.  dbra       d6, put_row
  881.  add.w      #1, test             ; Increment test for next test point.
  882.  rts
  883.  
  884. aes:                             ; COMPUTE! AES book page 13,
  885.  move.l     #aes_pb, d1          ; Address of aes_pb.
  886.  move.w     #$C8, d0             ; AES identifier = $C8.
  887.  trap       #2
  888.  rts
  889.  
  890. store_line:
  891.  move.b     (a0)+, d0
  892.  beq.s      end_of_string
  893.  move.b     d0, (a5)+            ; Store byte in buffer.
  894.  bra.s      store_line
  895. end_of_string:
  896.  rts
  897.  
  898.  data
  899. aes_pb:          dc.l control,global,int_in,int_out,addr_in,addr_out
  900. test_header:     dc.l zero,one,two,three,four
  901. zero:
  902.  dc.b $D,$A,'TEST POINT 0: Before appl_init',$D,$A,$D,$A,0
  903. one:
  904.  dc.b $D,$A,'TEST POINT 1: After appl_init, before menu_register',$D,$A,$D,$A,0
  905. two:
  906.  dc.b $D,$A,'TEST POINT 2: After menu_register, before evnt_mesag',$D,$A,$D,$A,0
  907. three:
  908.  dc.b $D,$A,'TEST POINT 3: In message handler, before evnt_mesag',$D,$A,$D,$A,0
  909. four:
  910.  dc.b $D,$A,'TEST POINT 4: In message handler second time',$D,$A,$D,$A,0
  911.  
  912. hex_table:      dc.b  '0123456789ABCDEF'
  913. newline:        dc.b  $D,$A,0
  914. aes_header:     dc.b  '                                AES ARRAYS',$D,$A,0
  915. aes_names:      dc.b  'CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  '
  916.                 dc.b  'ADDR_OUT',$D,$A,0
  917. aes_underline:  dc.b  '-------  -------  -------  -------  -------  '
  918.                 dc.b  '--------',$D,$A,0
  919. pre_spaces:     dc.b  '            ',0
  920. spaces:         dc.b  '  ',0
  921. menu_text:      dc.b  '  Accessory Arrays ',0
  922. filename:       dc.b  'E:\PRG_8\PRG_8BR.DAT',0
  923.  
  924.  bss
  925.  align
  926.  ;
  927.  ; AES ARRAYS:
  928.  ;
  929.  
  930.  ;     The addresses of the arrays declared below will be stored in the pointer
  931.  ; array 'aes_pb'.  That happens because the program is assembled in Relocatable
  932.  ; mode.  The sizes declared for the arrays are identical simply because I am
  933.  ; making it easy to line up the program's output.
  934.  
  935. control:      ds.w     5  ; Control parameters.
  936. global:       ds.w     5  ; Global parameters.
  937. int_in:       ds.w     5  ; Input parameters.
  938. int_out:      ds.w     5  ; Output parameters.
  939. addr_in:      ds.w     5  ; Input addresses.
  940. addr_out:     ds.w     5  ; Output addresses.
  941.  
  942.  ;
  943.  ; APPLICATION VARIABLES
  944.  ;
  945.  
  946. file_handle:  ds.w     1
  947. test:         ds.w     1
  948. message:      ds.l     4  ; 16 byte array.
  949. menu_id:      ds.w     1
  950. buffer:       ds.l $2000
  951.               ds.l   300  ; Program stack.
  952. stack:        ds.l     0  ; Address of program stack.
  953.  end
  954.  
  955.  
  956. Execution Results
  957.  
  958. TEST POINT 0: Before appl_init
  959.  
  960.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  961.             -------  -------  -------  -------  -------  --------
  962.             0: 0000  0: 0000  0: 0000  0: 0000  0: 0000  0: 0000  
  963.             1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  
  964.             2: 0000  2: 0000  2: 0000  2: 0000  2: 0000  2: 0000  
  965.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  966.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  967.  
  968. TEST POINT 1: After appl_init, before menu_register
  969.  
  970.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  971.             -------  -------  -------  -------  -------  --------
  972.             0: 000A  0: 0120  0: 0000  0: 0005  0: 0000  0: 0000  
  973.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0000  1: 0000  
  974.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  975.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  976.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  977.  
  978. TEST POINT 2: After menu_register, before evnt_mesag
  979.  
  980.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  981.             -------  -------  -------  -------  -------  --------
  982.             0: 0023  0: 0120  0: 0005  0: 0001  0: 0003  0: 0000  
  983.             1: 0001  1: 0001  1: 0000  1: 0000  1: 0DCD  1: 0000  
  984.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  985.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  986.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  987.  
  988. TEST POINT 3: In message handler, before evnt_mesag
  989.  
  990.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  991.             -------  -------  -------  -------  -------  --------
  992.             0: 0017  0: 0120  0: 0005  0: 0000  0: 0003  0: 0000  
  993.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0E36  1: 0000  
  994.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  995.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  996.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  997.  
  998. TEST POINT 4: In message handler second time
  999.  
  1000.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1001.             -------  -------  -------  -------  -------  --------
  1002.             0: 0017  0: 0120  0: 0005  0: 0000  0: 0003  0: 0000  
  1003.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0E36  1: 0000  
  1004.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  1005.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1006.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1007.  
  1008. Predeclaring AES Control Structures
  1009.   
  1010.      In program 78, no space is reserved for the control 
  1011. structure, and control array values are not stored just before 
  1012. each AES function invocation.  Instead, four function structures 
  1013. are declared in the data section of the program.  Each of these 
  1014. function structures serves as array control when the particular 
  1015. function is invoked.  Initially, the address of the first 
  1016. function structure is stored in the aes_pb slot reserved for 
  1017. array control.  Then, when it is time to invoke the other AES 
  1018. functions, the address of the relevant function is inserted in 
  1019. that slot.
  1020.  
  1021.   
  1022. Program 78. Illustrates the use of predefined AES control 
  1023. structures and the AES file selector function.
  1024.  
  1025.  ; Program Name: PRG_8CR.S
  1026.  ;      Version: 1.003
  1027.  
  1028.  ; Assembly Instructions:
  1029.  
  1030.  ;      Assemble in Relocatable mode and save with a PRG extension.  From
  1031.  ; the desktop, change the extension to ACC.
  1032.  
  1033.  ; Function:
  1034.  
  1035.  ;      Identical to that of PRG_8BR.S, but this program uses a group of
  1036.  ; predeclared, function named arrays that are substituted for 'control' in
  1037.  ; the aes_pb, as each AES function is invoked.  In addition, a path for the
  1038.  ; data file is chosen via the AES file selector function.  An additional
  1039.  ; array set is provided after the fsel_input function is invoked, then a
  1040.  ; final set is printed when the message handler is entered the second time.
  1041.  
  1042.  ; Execution Instructions:
  1043.  
  1044.  ;      Place PRG_8CR.ACC in the root directory of your boot disk.  During the
  1045.  ; next power-up cycle, the desk accessory will be installed.  From the desktop
  1046.  ; select 'Accessory Arrays' two times in order to store the pertinent data in
  1047.  ; the file buffer.  At the first selection, the file selector will appear so
  1048.  ; that a path and filename can be chosen for the data file; the buffer contents
  1049.  ; are written to the file when the accessory is chosen the second time.
  1050.  
  1051.  lea        stack, a7            ; This must be the first instruction.
  1052.  
  1053. initialize_register_variables:
  1054.  move.w     #$C8, d3             ; *** D3 is variable for AES call number.
  1055.  lea        buffer(pc), a5       ; A5 is pointer to buffer.
  1056.  lea        aes_pb(pc), a4       ; A4 is pointer for aes pointer array.
  1057.  lea        hex_table(pc), a3    ; A3 points to hexadecimal ASCII digits.
  1058.  
  1059.  ; For each test point, the contents of each AES array are printed.
  1060.  
  1061.  ;                           
  1062.  ;                 TEST POINT 0: Before appl_init     
  1063.  ;                           
  1064.  bsr        print_arrays
  1065.  
  1066. initialize_application:          ; COMPUTE! AES book page 223.
  1067.  
  1068.  ; Application identification = apid returned in int_out[0] and global[2].
  1069.  
  1070.  ; Since the address of the predeclared appl_init array is already stored in
  1071.  ; aes_pb, only the trap 2 invocation need be done here.
  1072.  
  1073.  bsr        aes                  ; Invoke trap #2 AES exception.
  1074.  ;
  1075.  ;          TEST POINT 1: After appl_init, before menu_register
  1076.  ;
  1077.  bsr        print_arrays
  1078.  
  1079. menu_installation:               ; COMPUTE! AES book page 248.
  1080.  
  1081.  ; Menu identification number returned in int_out[0].
  1082.  
  1083.  move.l     #menu_register, (a4) ; Store address of next 'control' array.             
  1084.  lea        global(pc), a0       ; Fetch address of global array.
  1085.  move.w     4(a0), int_in        ; Application identification to int_in[0].
  1086.  move.l     #menu_text, addr_in  ; Menu text address to addr_in[0].
  1087.  bsr        aes                 
  1088.  move.w     int_out(pc), menu_id ; Store menu identification number.
  1089.  
  1090.  ; MAIN ACCESSORY LOOP
  1091.  
  1092.  ;
  1093.  ;          TEST POINT 2: After menu_register, before evnt_mesag
  1094.  ;
  1095.  bsr        print_arrays
  1096.  
  1097.  
  1098. wait_for_message:                ; Relinquish processor control.
  1099.  move.l     #evnt_mesag, (a4)    ; Store address of next 'control' array.
  1100.  move.l     #message, addr_in    ; Address of message array to addr_in.
  1101.  
  1102.  ; The above instruction must be executed each time evnt_mesag is invoked
  1103.  ; because the fsel_input function must store addresses in addr_in also.
  1104.  ; Furthermore, the evnt_mesag "control" array must be restored in aes_pb
  1105.  ; because it is replaced by the fsel_input "control" array whenever that
  1106.  ; function is invoked.  If these alterations did not take place, then the
  1107.  ; evnt_mesag setup could be simplified as was done in PRG_8BR.S.
  1108.  
  1109.  bsr        aes
  1110.  
  1111.  ; When a message is received it is placed in array 'message'.
  1112.  
  1113.  ; ****************************************************************************
  1114.  ; ****************************************************************************
  1115.  
  1116. message_handler:                 ; Entrance point when message is received.
  1117.  lea        message(pc), a0      ; Fetch address of array 'message'.
  1118.  cmpi.w     #$28, (a0)           ; Compare ACCESSORY OPEN code with message[0].
  1119.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  1120.  move.w     8(a0), d0            ; The menu item selected is stored in element
  1121.                                  ; four (message[4]) of array 'message'.  This
  1122.                                  ; application's id # is in menu_id.
  1123.  cmp.w      menu_id(pc), d0      ; Was this application selected.
  1124.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  1125.  
  1126.  ; ****************************************************************************
  1127.  ; ****************************************************************************
  1128.  
  1129.  ; Execution proceeds past this point only when this application has been
  1130.  ; selected from the menu.
  1131.  
  1132.  ;
  1133.  ;          TEST POINT 3: In message handler, before fsel_input
  1134.  ;
  1135.  cmpi.w     #5, test             ; Have six array groups been printed?
  1136.  beq        wait_for_message     ; This effectively disables the handler.
  1137.  bsr        print_arrays
  1138.  
  1139.  cmpi.w     #5, test             ; Is this 2nd entrance into message handler?
  1140.  bne.s      get_current_drive
  1141.  
  1142. store_in_file:
  1143.  lea        buffer(pc), a6       ; Fetch start address.
  1144.  suba.l     a6, a5               ; Calculate number of bytes stored in buffer.
  1145.  
  1146. create_file:                     ; COMPUTE! TOS book page 270.
  1147.  move.w     #0, -(sp)            ; File attribute = read/write.
  1148.  pea        path(pc)             ; Push address of path.
  1149.  move.w     #$3C, -(sp)          ; Function = f_create = GEMDOS $3C.
  1150.  trap       #1                   ; Path handle is returned in D0.
  1151.  addq.l     #8, sp
  1152.  move.w     d0, d4               ; Save path handle in D4.
  1153.  
  1154. write_buffer_to_file:            ; Function = f_write.  COMPUTE! TOS p.274.
  1155.  pea        (a6)                 ; Push buffer's address.
  1156.  move.l     a5, -(sp)            ; Push byte count length.
  1157.  move.w     d4, -(sp)            ; COMPUTE!'s TOS book incorrectly specifies
  1158.  move.w     #$40, -(sp)          ; a longword operation here; see page 274.
  1159.  trap       #1
  1160.  lea        $C(sp), sp
  1161.  
  1162. close_output_file:               ; Actually, close the path.
  1163.  move.w     d4, -(sp)            ; Push path handle. 
  1164.  move.w     #$3E, -(sp)          ; Function = GEMDOS $3E = f_close.
  1165.  trap       #1
  1166.  addq.l     #4, sp
  1167.  bra        wait_for_message     ; Execute the evnt_mesag function.
  1168.  
  1169. get_current_drive:               ; Current drive = last drive accessed.
  1170.  move.w     #$19, -(sp)          ; Function = c_getdrv.
  1171.  trap       #1                   ; Current drive number returned in D0.
  1172.  addq.l     #2, sp
  1173.  
  1174.  ; Here we begin to construct the path that will be displayed when the
  1175.  ; file selector function is invoked.  Below, I indicate the data that
  1176.  ; is actually being extracted from the system.  Most references do not
  1177.  ; accurately describe what is being done with these functions.
  1178.  
  1179.  add.w      #$41, d0             ; Convert drive number to drive letter.
  1180.  lea        path(pc), a6         ; Fetch address of array 'path'.
  1181.  move.b     d0, (a6)+            ; Store drive letter in 'path' array.
  1182.  move.b     #$3A, (a6)+          ; Store a colon in 'path' array.
  1183.  
  1184. get_sub_directories:             ; The drive is the main directory.
  1185.  move.w     #0, -(sp)            ; See Internals page 135 for other values.
  1186.  pea        (a6)                 ; Push address of next 'path' array element.
  1187.  move.w     #$47, -(sp)          ; Function = d_getpath = get subdirectories.
  1188.  trap       #1                   ; Subdirectories are returned in the array
  1189.  addq.l     #8, sp               ; 'path'.
  1190.  
  1191. add_extension:
  1192.  
  1193.  ; The extension '\*.*' is added to complete the path.  This string must be
  1194.  ; NULL terminated.  For example, assume that the get_current_drive routine
  1195.  ; returns #6; adding 41 would convert this to ASCII F.  After the drive
  1196.  ; letter and the colon are stored in the path array, the elements of the
  1197.  ; array would contain:
  1198.  
  1199.  ;             F:____________________________________,etc.
  1200.  
  1201.  ; Whatever the get_sub_directories routine returns will be placed in the path
  1202.  ; array; the first character will be stored in element 3.  Assume that the
  1203.  ; routine returns \FILE_SEL as a subdirectory.  The path array would contain:
  1204.  
  1205.  ;             F:\FILE_SEL___________________________,etc.
  1206.  
  1207.  ; The extension is then added to the array contents to yield:
  1208.  
  1209.  ;             F:\FILE_SEL\*.*O______________________,etc.
  1210.  
  1211.  ; where the O represents the NULL character.  This is the path.  At this
  1212.  ; time there is no file name in the path.
  1213.  
  1214.  ; When the file selector is displayed, the contents of array 'path' will
  1215.  ; appear on the path = directory line.  The selection line will be blank
  1216.  ; because the program does not write anything to that line.
  1217.  
  1218. find_path_end:                   ; Find NULL at end of returned string.
  1219.  tst.b      (a6)+
  1220.  bne        find_path_end        ; Branch back till NULL is found.
  1221.  subq.l     #1, a6               ; Back up to overwrite the NULL.
  1222.  move.b     #$5C, (a6)+          ; Add '\'.
  1223.  move.b     #$2A, (a6)+          ; Add '*'.
  1224.  move.b     #$2E, (a6)+          ; Add '.'.
  1225.  move.b     #$2A, (a6)+          ; Add '*'.
  1226.  move.b     #0, (a6)             ; Add NULL.
  1227.  
  1228. display_file_selector:           ; COMPUTE!'s AES book page 282.
  1229.  
  1230.  ; Returns the drive name and subdirectories selected by the user in the
  1231.  ; array 'path', and, if a file was selected, it is returned in the array
  1232.  ; 'file'.
  1233.  
  1234.  move.l     #fsel_input, (a4)    ; Store address of next 'control' array.  
  1235. initialize_address_in_array:
  1236.  lea        addr_in(pc), a1      ; Fetch address of array 'addr_in'.
  1237.  lea        path(pc), a6         ; Fetch address of array 'path'.
  1238.  move.l     a6, (a1)             ; Store address of 'path' in 'addr_in'.
  1239.  move.l     #file, 4(a1)         ; Store address of 'file' in 'addr_in'.
  1240.  bsr        aes
  1241.  
  1242.  ;
  1243.  ;          TEST POINT 4: In message handler, after fsel_input
  1244.  ;
  1245.  bsr        print_arrays
  1246.  
  1247. analyze_returns:                 ; Look at returns for File Selector box.
  1248.  tst.w      int_out + 2          ; CANCEL button => 0.
  1249.  beq        wait_for_message     ; Go there if CANCEL was selected.
  1250.  tst.b      file                 ; NO FILE CHOSEN => 0.
  1251.  beq        wait_for_message     ; Go there if no file was chosen.
  1252.  
  1253. find_selected_path_end:          ; Search for first asterisk.
  1254.  
  1255.  ; At this point, the selected path does not include the file name, so
  1256.  ; it is only a "partial" path.
  1257.  
  1258.  cmp.b      #$2A, (a6)+         
  1259.  bne        find_selected_path_end
  1260.  subq.l     #1, a6               ; Back up to overwrite the asterisk.
  1261.  
  1262.  ; Here we add the file name to the path by appending it to the "partial"
  1263.  ; path.  Then we will have constructed the complete path.
  1264.  
  1265.  lea        file(pc), a0
  1266. add_filename_to_path:
  1267.  move.b     (a0)+, (a6)+
  1268.  bne.s      add_filename_to_path  ; Now we have a true path.
  1269.  bra        wait_for_message     ; Execute the evnt_mesag function.       
  1270.  
  1271.  ;
  1272.  ; SUBROUTINES
  1273.  ;
  1274.  
  1275. print_arrays:
  1276.  lea        newline(pc), a0       
  1277.  bsr        store_line        
  1278.  lea        test_header(pc), a0  ; Setup to fetch test point header.
  1279.  move.w     test(pc), d0         ; Load test point number into D0.
  1280.  lsl.w      #2, d0               ; Multiply by 4 to reach next pointer slot.     
  1281.  movea.l    0(a0,d0.w), a0       ; Print test point header.
  1282.  bsr        store_line
  1283.  lea        pre_spaces(pc), a0   ; Print spaces before column headers.
  1284.  bsr        store_line
  1285.  lea        aes_names(pc), a0    ; Print AES array column headers.
  1286.  bsr        store_line
  1287.  lea        pre_spaces(pc), a0   ; Print spaces before underline.
  1288.  bsr        store_line
  1289.  lea        aes_underline(pc), a0; Print AES underline.
  1290.  bsr        store_line
  1291.  moveq.l    #0, d7               ; D7 is up counter to print 5 rows.
  1292.  moveq.l    #4, d6               ; D6 is down counter to print 5 elements.
  1293. put_row:
  1294.  lea        aes_pb(pc), a6       ; Fetch parameter block address.
  1295.  move.w     #5, d5               ; D5 is array counter for 6 arrays.
  1296.  move.w     #11, d0              ; Print beginning spaces to line up columns.
  1297. put_space:
  1298.  move.b     #$20, (a5)+
  1299.  dbra       d0, put_space
  1300. put_element:                     ; Print contents of array element.
  1301.  move.w     d7, d0               ; Print array element number.
  1302.  andi.b     #$F, d0              ; Mask most significant nibble.
  1303.  move.b     0(a3,d0.w), d0       ; Store appropriate hex character in D0.
  1304.  move.b     d0, (a5)+
  1305.  move.b     #$3A, (a5)+          ; A colon.
  1306.  move.b     #$20, (a5)+          ; A space.
  1307.  move.w     d7, d0               ; Multiply contents of D7 by 2 in D0 to
  1308.  lsl.w      #1, d0               ; obtain offset for next array element.
  1309.  movea.l    (a6)+, a1            ; Copy array address into A1 and increment
  1310.                                  ; A6 to point to next array address.
  1311.  move.w     0(a1,d0.w), d0       ; Fetch contents of array element.
  1312.  moveq      #3, d2               ; D2 is loop counter for ASCII conversion.
  1313. convert_digit:                   ; Convert a nibble, then print it.
  1314.  rol.w      #4, d0               ; Rotate most significant nibble to the
  1315.                                  ; least significant nibble position.
  1316.  move.b     d0, d1               ; Copy least significant byte of D0 to D1.
  1317.  andi.b     #$F, d1              ; Mask out most significant nibble of D1.
  1318.  ext.w      d1                   ; Extend to word length.
  1319.  move.b     0(a3,d1.w), d1       ; Fetch ASCII hex digit to D1.
  1320. put_digit:
  1321.  move.b     d1, (a5)+
  1322.  dbra       d2, convert_digit    ; Loop until D2 = -1.
  1323. _put_spaces:
  1324.  move.b     #$20, (a5)+
  1325.  move.b     #$20, (a5)+
  1326.  dbra       d5, put_element
  1327.  lea        newline(pc), a0      ; Print a newline.
  1328.  bsr        store_line
  1329.  addi.w     #1, d7               ; Increment up counter.
  1330.  dbra       d6, put_row
  1331.  add.w      #1, test             ; Increment test for next test point.
  1332.  rts
  1333.  
  1334. aes:                             ; COMPUTE! AES book page 13,
  1335.  move.l     a4, d1               ; Address of aes_pb.
  1336.  move.w     d3, d0               ; AES identifier = $C8.
  1337.  trap       #2
  1338.  rts
  1339.  
  1340. store_line:
  1341.  move.b     (a0)+, d0
  1342.  beq.s      end_of_string
  1343.  move.b     d0, (a5)+            ; Store byte in buffer.
  1344.  bra.s      store_line
  1345. end_of_string:
  1346.  rts
  1347.  
  1348.  data
  1349. aes_pb:          dc.l appl_init,global,int_in,int_out,addr_in,addr_out
  1350. test_header:     dc.l zero,one,two,three,four,five
  1351. zero:
  1352.  dc.b $D,$A,'TEST POINT 0: Before appl_init',$D,$A,$D,$A,0
  1353. one:
  1354.  dc.b $D,$A,'TEST POINT 1: After appl_init, before menu_register',$D,$A,$D,$A,0
  1355. two:
  1356.  dc.b $D,$A,'TEST POINT 2: After menu_register, before evnt_mesag',$D,$A,$D,$A,0
  1357. three:
  1358.  dc.b $D,$A,'TEST POINT 3: In message handler, before fsel_input',$D,$A,$D,$A,0
  1359. four:
  1360.  dc.b $D,$A,'TEST POINT 4: In message handler, after fsel_input',$D,$A,$D,$A,0
  1361. five:
  1362.  dc.b $D,$A,'TEST POINT 5: In message handler second time',$D,$A,$D,$A,0
  1363.  
  1364. hex_table:      dc.b  '0123456789ABCDEF'
  1365. newline:        dc.b  $D,$A,0
  1366. aes_header:     dc.b  '                                AES ARRAYS',$D,$A,0
  1367. aes_names:      dc.b  'CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  '
  1368.                 dc.b  'ADDR_OUT',$D,$A,0
  1369. aes_underline:  dc.b  '-------  -------  -------  -------  -------  '
  1370.                 dc.b  '--------',$D,$A,0
  1371. pre_spaces:     dc.b  '            ',0
  1372. spaces:         dc.b  '  ',0
  1373. menu_text:      dc.b  '  Accessory Arrays ',0
  1374.  
  1375.  align
  1376.  
  1377.  ;
  1378.  ; AES ARRAYS:
  1379.  ;
  1380.  
  1381.  ; PREDEFINED 'CONTROL' STRUCTURES
  1382.  
  1383. appl_init:     ; This one is prestored in aes_pb.
  1384.  dc.w 10
  1385.  dc.w  0
  1386.  dc.w  1
  1387.  dc.w  0
  1388.  dc.w  0
  1389.  
  1390. menu_register:
  1391.  dc.w 35
  1392.  dc.w  1
  1393.  dc.w  1
  1394.  dc.w  1
  1395.  dc.w  0
  1396.  
  1397. evnt_mesag:
  1398.  dc.w 23
  1399.  dc.w  0
  1400.  dc.w  1
  1401.  dc.w  1
  1402.  dc.w  0
  1403.  
  1404. fsel_input:
  1405.  dc.w 90
  1406.  dc.w  0
  1407.  dc.w  2
  1408.  dc.w  2
  1409.  dc.w  0
  1410.  
  1411.  ;     The addresses of the arrays declared below will be stored in the pointer
  1412.  ; array 'aes_pb'.  That happens because the program is assembled in Relocatable
  1413.  ; mode.  The sizes declared for the arrays are identical simply because I am
  1414.  ; making it easy to line up the program's output.
  1415.  
  1416.  ;     Note that array 'control' is not declared below,.
  1417.  
  1418.  bss   ; The location of this assembler directive is critical.  It must
  1419.        ; not be placed before the predeclared 'control' structures.
  1420.  
  1421. global:       ds.w     5  ; Global parameters.
  1422. int_in:       ds.w     5  ; Input parameters.
  1423. int_out:      ds.w     5  ; Output parameters.
  1424. addr_in:      ds.w     5  ; Input addresses.
  1425. addr_out:     ds.w     5  ; Output addresses.
  1426.  
  1427.  
  1428.  ;
  1429.  ; FSEL_INPUT ARRAYS
  1430.  ;
  1431.  
  1432. path:         ds.b   120          ; Array for drive name and subdirectories.
  1433. file:         ds.b    14          ; Array for selected file.
  1434.  
  1435.  ;
  1436.  ; APPLICATION VARIABLES
  1437.  ;
  1438.  
  1439. file_handle:  ds.w     1
  1440. test:         ds.w     1
  1441. message:      ds.l     4  ; 16 byte array.
  1442. menu_id:      ds.w     1
  1443. buffer:       ds.l $2000
  1444.               ds.l   300  ; Program stack.
  1445. stack:        ds.l     0  ; Address of program stack.
  1446.  end
  1447.  
  1448.   
  1449. Execution Results
  1450.  
  1451. TEST POINT 0: Before appl_init
  1452.  
  1453.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1454.             -------  -------  -------  -------  -------  --------
  1455.             0: 000A  0: 0000  0: 0000  0: 0000  0: 0000  0: 0000  
  1456.             1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  
  1457.             2: 0001  2: 0000  2: 0000  2: 0000  2: 0000  2: 0000  
  1458.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1459.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1460.  
  1461. TEST POINT 1: After appl_init, before menu_register
  1462.  
  1463.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1464.             -------  -------  -------  -------  -------  --------
  1465.             0: 000A  0: 0120  0: 0000  0: 0003  0: 0000  0: 0000  
  1466.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0000  1: 0000  
  1467.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  1468.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1469.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1470.  
  1471. TEST POINT 2: After menu_register, before evnt_mesag
  1472.  
  1473.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1474.             -------  -------  -------  -------  -------  --------
  1475.             0: 0023  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  1476.             1: 0001  1: 0001  1: 0000  1: 0000  1: D3D8  1: 0000  
  1477.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  1478.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1479.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1480.  
  1481. TEST POINT 3: In message handler, before fsel_input
  1482.  
  1483.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1484.             -------  -------  -------  -------  -------  --------
  1485.             0: 0017  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  1486.             1: 0000  1: 0001  1: 0000  1: 0000  1: D4D0  1: 0000  
  1487.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0000  2: 0000  
  1488.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1489.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1490.  
  1491. TEST POINT 4: In message handler, after fsel_input
  1492.  
  1493.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1494.             -------  -------  -------  -------  -------  --------
  1495.             0: 005A  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  1496.             1: 0000  1: 0001  1: 0000  1: 0001  1: D446  1: 0000  
  1497.             2: 0002  2: 0003  2: 0000  2: 9C58  2: 0003  2: 0000  
  1498.             3: 0002  3: 0000  3: 0000  3: 0000  3: D4BE  3: 0000  
  1499.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1500.  
  1501. TEST POINT 5: In message handler second time
  1502.  
  1503.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1504.             -------  -------  -------  -------  -------  --------
  1505.             0: 0017  0: 0120  0: 0003  0: 0001  0: 0003  0: 0000  
  1506.             1: 0000  1: 0001  1: 0000  1: 0001  1: D4D0  1: 0000  
  1507.             2: 0001  2: 0003  2: 0000  2: 9C58  2: 0003  2: 0000  
  1508.             3: 0001  3: 0000  3: 0000  3: 0000  3: D4BE  3: 0000  
  1509.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1510.  
  1511. Execution Results Discussion
  1512.   
  1513.      There is nothing unexpected in the test point tables 
  1514. produced by the program.  Note that the first table does contain 
  1515. the values stored in the appl_init array since it was predeclared 
  1516. and its address was prestored in aes_pb.  In the test point 5 
  1517. table, note that addr_in[0] contains the address of the message 
  1518. array, as it should, but that the fsel_input required address 
  1519. that was stored in addr_in[1] during that function's invocation 
  1520. has not been disturbed.  The stability of that array element 
  1521. could be used to advantage if it was necessary to invoke 
  1522. fsel_input again.
  1523.  
  1524. Spawning a Program to Create a Desk Accessory's File
  1525.   
  1526.      Each ST programmer tends to concentrate productivity in 
  1527. areas of personal interest and commitment; therefore, it is not 
  1528. unusual to find that information passed on to one programmer by 
  1529. another is not quite applicable to the second programmer's 
  1530. particular programming applications.  For example, I am 
  1531. completely neglecting the concept of color monitor programming 
  1532. requirements in this book.  In order to offset this neglect, I am 
  1533. inclined to illustrate several methods of accomplishing a task.  
  1534. In some cases, I am prompted to do so because of something I have 
  1535. heard or read; in other cases, I feel that readers may notice 
  1536. something of assistance within the additional material.  It is 
  1537. with from this viewpoint that I include programs 79 and 80.
  1538.      This pair of programs also create a file and store the 
  1539. contents of the AES structures therein, but the desk accessory, 
  1540. program 79, does not create the file; instead, it stores its data 
  1541. in a buffer and spawns program 80 so that it can create the file, 
  1542. write the buffer contents therein and properly close the file.  I 
  1543. have chosen particular directory paths for the programs and the 
  1544. data files; if you decide to experiment with the programs, you 
  1545. may want to alter those paths.
  1546.   
  1547. Program 79. Spawning a program from within a desk accessory so 
  1548. that the spawned program can create a file in which to store data 
  1549. generated by the desk accessory.
  1550.  
  1551.  ; Program Name: PRG_8DR.S
  1552.  ;      Version: 1.002
  1553.  
  1554.  ; Assembly Instructions:
  1555.  
  1556.  ;      Assemble in Relocatable mode and save with a PRG extension.  From
  1557.  ; the desktop, change the extension to ACC.  Programs that are to function
  1558.  ; as desk accessories MUST be assembled in Relocatable mode.  If you design
  1559.  ; a desk accessory so that it can be assembled in PC-relative mode, and if
  1560.  ; you attempt to load that accessory via MultiDesk, you will receive an
  1561.  ; error pertaining to the attempt to read in the accessory.  If you place
  1562.  ; that accessory in the boot directory, the system will reset every time
  1563.  ; it attempts to load the accessory.  Sei gewarnt! 
  1564.  
  1565.  ; Function:
  1566.  
  1567.  ;      Identical to that of PRG_8CR.S, but this program does not create a
  1568.  ; file and place data directly therein.  Instead, another program is spawned
  1569.  ; to create the file and transfer the data from the buffer to the file.  The
  1570.  ; name of the disk file to be created by PRG_8EP.TOS is passed as a parameter
  1571.  ; in an environmental string.
  1572.  
  1573.  ; Execution Instructions:
  1574.  
  1575.  ;      Place PRG_8DR.ACC and PRG_8EP.TOS in the root directory of your boot
  1576.  ; disk. During the next power-up cycle, the desk accessory will be installed.
  1577.  ; From the desktop select 'Accessory Arrays' two times in order to store the
  1578.  ; pertinent data in the file buffer.  After the second selection, PRG_8EP.TOS
  1579.  ; will be executed.  PRG_8EP.TOS will create file PRG_8DR.DAT, write the
  1580.  ; contents of the buffer to the file and close the file properly.  You can
  1581.  ; execute the desk accessory from within MultiDesk, but don't forget that
  1582.  ; PRG_8EP.TOS must be in the same directory as is PRG_8DR.ACC.  You may want
  1583.  ; to change the path for the file so that it is created on another disk or disk
  1584.  ; partition.
  1585.  
  1586.  lea        stack, a7            ; This must be the first instruction.
  1587.  
  1588.  ; Note that when the buffer is declared before the stack, pc-relative
  1589.  ; addressing can't be used in the instruction above, because of the distance
  1590.  ; between the instruction and the address of the stack.
  1591.  
  1592. initialize_register_variables:
  1593.  move.w    #$C8, d3              ; *** D3 is variable for AES call number.
  1594.  lea       buffer(pc), a5        ; A5 is pointer to buffer.
  1595.  lea       aes_pb(pc), a4        ; A4 is pointer for aes pointer array.
  1596.  lea       hex_table(pc), a3     ; A3 points to hexadecimal ASCII digits.
  1597.  
  1598.  ; The file buffer's address must be converted to an ASCII hexadecimal string
  1599.  ; and stored in the variable "command_string" so that it can be passed in that
  1600.  ; form to the spawned process, PRG_8EP.TOS.  Note that the 8 digit string can be
  1601.  ; passed in "pure" form when it is done in this manner; that is, no quotes nor
  1602.  ; NULL character is needed.  Refer to COMPUTE!'s TOS book pages 100 - 102 for
  1603.  ; other important information concerning the spawning process.
  1604.  
  1605. store_buffer_address_in_command_line:
  1606.  move.l     a5, d0
  1607.  lea        command_string, a0   ; Fetch address of command line string.
  1608.  moveq      #7, d2               ; D2 is loop counter for ASCII conversion.
  1609. _convert_digit:                  ; Convert a nibble, then store it.
  1610.  rol.l      #4, d0               ; Rotate most significant nibble to the
  1611.                                  ; least significant nibble position.
  1612.  move.b     d0, d1               ; Copy least significant byte of D0 to D1.
  1613.  andi.b     #$F, d1              ; Mask out most significant nibble of D1.
  1614.  ext.w      d1                   ; Extend to word length.
  1615.  move.b     0(a3,d1.w), d1       ; Fetch ASCII hex digit to D1.
  1616. _put_digit:
  1617.  move.b     d1, (a0)+
  1618.  dbra       d2, _convert_digit   ; Loop until D2 = -1.
  1619.  
  1620.  ; For each test point, the contents of each AES array are printed.
  1621.  
  1622.  ;                           
  1623.  ;          TEST POINT 0: Before appl_init     
  1624.  ;                           
  1625.  bsr        print_arrays
  1626.  
  1627. initialize_application:          ; COMPUTE! AES book page 223.
  1628.  
  1629.  ; Application identification = apid returned in int_out[0] and global[2].
  1630.  
  1631.  ; Since the address of the predeclared appl_init array is already stored in
  1632.  ; aes_pb, only the trap 2 invocation need be done here.
  1633.  
  1634.  bsr        aes                  ; Invoke trap #2 AES exception.
  1635.  ;
  1636.  ;          TEST POINT 1: After appl_init, before menu_register
  1637.  ;
  1638.  bsr        print_arrays
  1639.  
  1640. menu_installation:               ; COMPUTE! AES book page 248.
  1641.  
  1642.  ; Menu identification number returned in int_out[0].
  1643.        
  1644.  move.l     #menu_register, (a4) ; Store address of next "control" array.      
  1645.  lea        global(pc), a0       ; Fetch address of global array.
  1646.  move.w     4(a0), int_in        ; Application identification to int_in[0].
  1647.  move.l     #menu_text, addr_in  ; Menu text address to addr_in[0].
  1648.  bsr        aes                 
  1649.  move.w     int_out(pc), menu_id ; Store menu identification number.
  1650.  
  1651.  ; MAIN ACCESSORY LOOP
  1652.  
  1653.  ;
  1654.  ;          TEST POINT 2: After menu_register, before evnt_mesag
  1655.  ;
  1656.  bsr        print_arrays
  1657.  
  1658.  move.l     #message, addr_in    ; Address of message array to addr_in. 
  1659.  move.l     #evnt_mesag, (a4)    ; Store address of next "control" array.
  1660. wait_for_message:                ; COMPUTE! AES book page 235.
  1661.  bsr        aes
  1662.  
  1663.  ; When a message is received it is placed in array 'message'.  Note the
  1664.  ; short cuts taken above.  Since the aes_pb and the addr_in structures remain
  1665.  ; uncorrupted after they have been initialized for the evnt_mesag function,
  1666.  ; only the trap 2 exception need be invoked each time that function is to be
  1667.  ; executed.
  1668.  
  1669.  ; ****************************************************************************
  1670.  ; ****************************************************************************
  1671.  
  1672. message_handler:                 ; Entrance point when message is received.
  1673.  lea        message(pc), a0      ; Fetch address of array 'message'.
  1674.  cmpi.w     #$28, (a0)           ; Compare ACCESSORY OPEN code with message[0].
  1675.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  1676.  move.w     8(a0), d0            ; The menu item selected is stored in element
  1677.                                  ; four (message[4]) of array 'message'.  This
  1678.                                  ; application's id # is in menu_id.
  1679.  cmp.w      menu_id(pc), d0      ; Was this application selected.
  1680.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  1681.  
  1682.  ; ****************************************************************************
  1683.  ; ****************************************************************************
  1684.  
  1685.  ; Execution proceeds past this point only when this application has been
  1686.  ; selected from the menu.
  1687.  
  1688.  ;
  1689.  ;          TEST POINT 3: In message handler, before evnt_mesag
  1690.  ;
  1691.  cmpi.w     #5, test             ; Have five array groups been printed?
  1692.  beq        wait_for_message
  1693.  bsr        print_arrays
  1694.  cmpi.w     #5, test             ; Branch after 2nd entrance in message handler.
  1695.  beq.s      store_in_file        ; Spawn process to create file.
  1696.  bra        wait_for_message     ; Execute the evnt_mesag function.
  1697.  
  1698. store_in_file:
  1699.  move.b     #0, (a5)             ; Store NULL at end of file buffer.
  1700.  pea        environ_string
  1701.  pea        command_string       ; Contains address of file buffer.
  1702.  pea        program              ; Push address of program name string.
  1703.  move.w     #0, -(sp)
  1704.  move.w     #$4B, -(sp)          ; Function = GEMDOS $4B = p_exec.
  1705.  trap       #1
  1706.  lea        $10(sp), sp
  1707.  bra        wait_for_message     ; Execute the evnt_mesag function.
  1708.  
  1709.  ;
  1710.  ; SUBROUTINES
  1711.  ;
  1712.  
  1713. print_arrays:
  1714.  lea        newline(pc), a0       
  1715.  bsr        store_line        
  1716.  lea        test_header(pc), a0  ; Setup to fetch test point header.
  1717.  move.w     test(pc), d0         ; Load test point number into D0.
  1718.  lsl.w      #2, d0               ; Multiply by 4 to reach next pointer slot.     
  1719.  movea.l    0(a0,d0.w), a0       ; Print test point header.
  1720.  bsr        store_line
  1721.  lea        pre_spaces(pc), a0   ; Print spaces before column headers.
  1722.  bsr        store_line
  1723.  lea        aes_names(pc), a0    ; Print AES array column headers.
  1724.  bsr        store_line
  1725.  lea        pre_spaces(pc), a0   ; Print spaces before underline.
  1726.  bsr        store_line
  1727.  lea        aes_underline(pc), a0; Print AES underline.
  1728.  bsr        store_line
  1729.  moveq.l    #0, d7               ; D7 is up counter to print 5 rows.
  1730.  moveq.l    #4, d6               ; D6 is down counter to print 5 elements.
  1731. put_row:
  1732.  lea        aes_pb(pc), a6       ; Fetch parameter block address.
  1733.  move.w     #5, d5               ; D5 is array counter for 6 arrays.
  1734.  move.w     #11, d0              ; Print beginning spaces to line up columns.
  1735. put_space:
  1736.  move.b     #$20, (a5)+
  1737.  dbra       d0, put_space
  1738. put_element:                     ; Print contents of array element.
  1739.  move.w     d7, d0               ; Print array element number.
  1740.  andi.b     #$F, d0              ; Mask most significant nibble.
  1741.  move.b     0(a3,d0.w), d0       ; Store appropriate hex character in D0.
  1742.  move.b     d0, (a5)+
  1743.  move.b     #$3A, (a5)+          ; A colon.
  1744.  move.b     #$20, (a5)+          ; A space.
  1745.  move.w     d7, d0               ; Multiply contents of D7 by 2 in D0 to
  1746.  lsl.w      #1, d0               ; obtain offset for next array element.
  1747.  movea.l    (a6)+, a1            ; Copy array address into A1 and increment
  1748.                                  ; A6 to point to next array address.
  1749.  move.w     0(a1,d0.w), d0       ; Fetch contents of array element.
  1750.  moveq      #3, d2               ; D2 is loop counter for ASCII conversion.
  1751. convert_digit:                   ; Convert a nibble, then print it.
  1752.  rol.w      #4, d0               ; Rotate most significant nibble to the
  1753.                                  ; least significant nibble position.
  1754.  move.b     d0, d1               ; Copy least significant byte of D0 to D1.
  1755.  andi.b     #$F, d1              ; Mask out most significant nibble of D1.
  1756.  ext.w      d1                   ; Extend to word length.
  1757.  move.b     0(a3,d1.w), d1       ; Fetch ASCII hex digit to D1.
  1758. put_digit:
  1759.  move.b     d1, (a5)+
  1760.  dbra       d2, convert_digit    ; Loop until D2 = -1.
  1761. _put_spaces:
  1762.  move.b     #$20, (a5)+
  1763.  move.b     #$20, (a5)+
  1764.  dbra       d5, put_element
  1765.  lea        newline(pc), a0      ; Print a newline.
  1766.  bsr        store_line
  1767.  addi.w     #1, d7               ; Increment up counter.
  1768.  dbra       d6, put_row
  1769.  add.w      #1, test             ; Increment test for next test point.
  1770.  rts
  1771.  
  1772. aes:                             ; COMPUTE! AES book page 13,
  1773.  move.l     a4, d1               ; Address of aes_pb.
  1774.  move.w     d3, d0               ; AES identifier = $C8.
  1775.  trap       #2
  1776.  rts
  1777.  
  1778. store_line:
  1779.  move.b     (a0)+, d0
  1780.  beq.s      end_of_string
  1781.  move.b     d0, (a5)+            ; Store byte in buffer.
  1782.  bra.s      store_line
  1783. end_of_string:
  1784.  rts
  1785.  
  1786.  data
  1787. aes_pb:          dc.l appl_init,global,int_in,int_out,addr_in,addr_out
  1788.  
  1789.  ; PREDEFINED 'CONTROL' STRUCTURES
  1790.  
  1791. appl_init:       ; This one is prestored in aes_pb.
  1792.  dc.w 10
  1793.  dc.w  0
  1794.  dc.w  1
  1795.  dc.w  0
  1796.  dc.w  0
  1797.  
  1798. menu_register:
  1799.  dc.w 35
  1800.  dc.w  1
  1801.  dc.w  1
  1802.  dc.w  1
  1803.  dc.w  0
  1804.  
  1805. evnt_mesag:
  1806.  dc.w 23
  1807.  dc.w  0
  1808.  dc.w  1
  1809.  dc.w  1
  1810.  dc.w  0
  1811.    
  1812. test_header:     dc.l zero,one,two,three,four
  1813. zero:
  1814.  dc.b $D,$A,'TEST POINT 0: Before appl_init',$D,$A,$D,$A,0
  1815. one:
  1816.  dc.b $D,$A,'TEST POINT 1: After appl_init, before menu_register',$D,$A,$D,$A,0
  1817. two:
  1818.  dc.b $D,$A,'TEST POINT 2: After menu_register, before evnt_mesag',$D,$A,$D,$A,0
  1819. three:
  1820.  dc.b $D,$A,'TEST POINT 3: In message handler, before evnt_mesag',$D,$A,$D,$A,0
  1821. four:
  1822.  dc.b $D,$A,'TEST POINT 4: In message handler second time',$D,$A,$D,$A,0
  1823.  
  1824. hex_table:      dc.b  '0123456789ABCDEF'
  1825. newline:        dc.b  $D,$A,0
  1826. aes_header:     dc.b  '                                AES ARRAYS',$D,$A,0
  1827. aes_names:      dc.b  'CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  '
  1828.                 dc.b  'ADDR_OUT',$D,$A,0
  1829. aes_underline:  dc.b  '-------  -------  -------  -------  -------  '
  1830.                 dc.b  '--------',$D,$A,0
  1831. pre_spaces:     dc.b  '            ',0
  1832. spaces:         dc.b  '  ',0
  1833. menu_text:      dc.b  '  Accessory Arrays ',0
  1834. program:        dc.b  'PRG_8EP.TOS',0
  1835. environ_string: dc.b  'E:\PRG_8\PRG_8DR.DAT',0
  1836.  
  1837.  bss
  1838. command_string: ds.b 30   ; Must contain address of buffer.
  1839.  
  1840.  align
  1841.  ;
  1842.  ; AES ARRAYS:
  1843.  ;
  1844.  
  1845.  ;     The addresses of the arrays declared below will be stored in the pointer
  1846.  ; array 'aes_pb'.  That happens because the program is assembled in Relocatable
  1847.  ; mode.  The sizes declared for the arrays are identical simply because I am
  1848.  ; making it easy to line up the program's output.
  1849.  
  1850.  ; Note that array "control" is not declared below.
  1851.  
  1852. global:       ds.w     5  ; Global parameters.
  1853. int_in:       ds.w     5  ; Input parameters.
  1854. int_out:      ds.w     5  ; Output parameters.
  1855. addr_in:      ds.w     5  ; Input addresses.
  1856. addr_out:     ds.w     5  ; Output addresses.
  1857.  
  1858.  ;
  1859.  ; APPLICATION VARIABLES
  1860.  ;
  1861.  
  1862. test:         ds.w     1
  1863. message:      ds.l     4  ; 16 byte array.
  1864. menu_id:      ds.w     1
  1865. buffer:       ds.l $2000
  1866.               ds.l   300  ; Program stack.
  1867. stack:        ds.l     0  ; Address of program stack.
  1868.  end
  1869.  
  1870.     
  1871. Program 80.  The program that creates the file for program 79.  
  1872. This program accepts a file name as a passed parameter in an 
  1873. environmental string.  Note that the data passed to this program 
  1874. via its command line is processed with an ASCII hexadecimal 
  1875. string to binary number conversion algorithm.
  1876.  
  1877.  ; Program Name: PRG_8EP.S
  1878.  ;      Version: 1.001
  1879.  
  1880.  ; Assembly Instructions:
  1881.  
  1882.  ;    Assemble in PC-relative mode and save with a TOS suffix.
  1883.  
  1884.  ; Execution Instructions:
  1885.  
  1886.  ;    This program is spawned by PRG_8DR.ACC.  Both programs must reside in
  1887.  ; the same directory.
  1888.  
  1889.  ; Function:
  1890.  
  1891.  ;    This program creates disk file specified in the first environmental string
  1892.  ; and stores therein the data contained in PRG_8DR.ACC's buffer.  The purpose
  1893.  ; of this exercise is to illustrate an alternate method of creating a disk file
  1894.  ; for a desk accessory, writing data to the file and invoking proper file
  1895.  ; closure.
  1896.  
  1897. fetch_command_line_address:
  1898.  lea        -$82(pc), a3
  1899.  
  1900. fetch_environmental_string_address:
  1901.  lea        -$54(a3), a4         ; Fetch address of string pointer.
  1902.  movea.l    (a4), a4             ; Fetch address of string.
  1903.  
  1904.  ; The data in the command line is a string of eight ASCII hexadecimal digits
  1905.  ; passed from PRG_8DR.ACC when that program spawned this one.  There are no
  1906.  ; characters before that string and none following.  The string is the address
  1907.  ; of PRG_8DR.ACC's buffer, in which data that is to be written to a disk file
  1908.  ; has been stored.  The address must be converted from an ASCII hexadecimal
  1909.  ; string to a binary longword.
  1910.  
  1911.  ; The data in the environmental string can be used as it exist.
  1912.  
  1913. process_command_line:            ; Convert ASCII hexadecimal string to binary.
  1914.  moveq.l    #0, d0               ; Accumulator.
  1915.  moveq.l    #7, d2               ; Loop counter.
  1916. convert_string:
  1917.  move.b     (a3)+, d1            ; D1 is used for each character conversion.
  1918.  cmpi.b     #$39, d1             ; Character greater than 9 test.
  1919.  bgt        not_decimal
  1920.  cmpi.b     #$30, d1             ; Character less than 0 test.
  1921.  blt        not_valid
  1922.  subi.b     #$30, d1             ; Convert ASCII hex character to binary.
  1923.  bra.s      accumulate
  1924. not_decimal:
  1925.  cmpi.b     #$41, d1             ; Character less than A test.
  1926.  blt        not_valid
  1927.  cmpi.b     #$46, d1             ; Character greater than F test.
  1928.  bgt        not_valid
  1929.  subi.b     #$37, d1             ; Convert ASCII hex character to binary.
  1930. accumulate:
  1931.  lsl.l      #4, d0               ; Shift one nibble = one hex character.
  1932.  add.b      d1, d0
  1933.  dbra       d2, convert_string
  1934.  move.l     d0, a3               ; Address of buffer is in D0.
  1935.  movea.l    a3, a0               ; Copy for buffer byte count.
  1936.  
  1937. count_bytes_in_buffer:
  1938.  moveq.l    #0, d0               ; NULL byte test register.
  1939.  moveq.l    #0, d3               ; Byte count accumulator.
  1940. count:
  1941.  move.b     (a0)+, d0            ; Look at each character in buffer and
  1942.  beq        create_file          ; accumulate count until NULL is found at
  1943.  addq.l     #1, d3               ; end of buffer.
  1944.  bra.s      count
  1945.  
  1946. create_file:                     ; COMPUTE! TOS book page 270.
  1947.  move.w     #0, -(sp)            ; File attribute = read/write.
  1948.  pea        (a4)                 ; Environmental string has file's path.
  1949.  move.w     #$3C, -(sp)          ; Function = f_create = GEMDOS $3C.
  1950.  trap       #1                   ; File handle is returned in D0.
  1951.  addq.l     #8, sp
  1952.  move.w     d0, d4               ; Save file handle in D4.
  1953.  
  1954. write_buffer_to_file:            ; Function = f_write.  COMPUTE! TOS p.274.
  1955.  pea        (a3)                 ; Push buffer's address.
  1956.  move.l     d3, -(sp)            ; Push byte count length.
  1957.  move.w     d4, -(sp)            ; COMPUTE!'s TOS book incorrectly specifies
  1958.  move.w     #$40, -(sp)          ; a longword operation here; see page 274.
  1959.  trap       #1
  1960.  lea        $C(sp), sp
  1961.  
  1962. close_output_file:               ; COMPUTE! TOS book page 272.
  1963.  move.w     d4, -(sp)            ; Push file handle. 
  1964.  move.w     #$3E, -(sp)          ; Function = GEMDOS $3E = f_close.
  1965.  trap       #1
  1966.  addq.l     #4, sp
  1967.  
  1968. not_valid:
  1969. terminate:
  1970.  move.w     #0, -(sp)
  1971.  trap       #1
  1972.  
  1973.  data
  1974.  align
  1975.  bss
  1976. program_end:  ds.l  0
  1977.  end
  1978.  
  1979. Execution Results
  1980.  
  1981. TEST POINT 0: Before appl_init
  1982.  
  1983.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1984.             -------  -------  -------  -------  -------  --------
  1985.             0: 000A  0: 0000  0: 0000  0: 0000  0: 0000  0: 0000  
  1986.             1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  1: 0000  
  1987.             2: 0001  2: 0000  2: 0000  2: 0000  2: 0000  2: 0000  
  1988.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1989.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  1990.  
  1991. TEST POINT 1: After appl_init, before menu_register
  1992.  
  1993.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  1994.             -------  -------  -------  -------  -------  --------
  1995.             0: 000A  0: 0120  0: 0000  0: 0005  0: 0000  0: 0000  
  1996.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0000  1: 0000  
  1997.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  1998.             3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  1999.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  2000.  
  2001. TEST POINT 2: After menu_register, before evnt_mesag
  2002.  
  2003.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  2004.             -------  -------  -------  -------  -------  --------
  2005.             0: 0023  0: 0120  0: 0005  0: 0000  0: 0003  0: 0000  
  2006.             1: 0001  1: 0001  1: 0000  1: 0000  1: 0DD5  1: 0000  
  2007.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  2008.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  2009.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  2010.  
  2011. TEST POINT 3: In message handler, before evnt_mesag
  2012.  
  2013.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  2014.             -------  -------  -------  -------  -------  --------
  2015.             0: 0017  0: 0120  0: 0005  0: 0000  0: 0003  0: 0000  
  2016.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0E5C  1: 0000  
  2017.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  2018.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  2019.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  2020.  
  2021. TEST POINT 4: In message handler second time
  2022.  
  2023.             CONTROL  GLOBAL   INT_IN   INT_OUT  ADDR_IN  ADDR_OUT
  2024.             -------  -------  -------  -------  -------  --------
  2025.             0: 0017  0: 0120  0: 0005  0: 0000  0: 0003  0: 0000  
  2026.             1: 0000  1: 0001  1: 0000  1: 0000  1: 0E5C  1: 0000  
  2027.             2: 0001  2: 0005  2: 0000  2: 9C58  2: 0000  2: 0000  
  2028.             3: 0001  3: 0000  3: 0000  3: 0000  3: 0000  3: 0000  
  2029.             4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  4: 0000  
  2030.  
  2031. A Practical Example
  2032.   
  2033.      As a practical desk accessory example, I have chosen a 
  2034. program which represents a continuation of the linear print 
  2035. buffer discussion of the last chapter.  You will be able to 
  2036. invoke this program from within any application permitting access 
  2037. to desk accessories.  Furthermore, since you should be somewhat 
  2038. familiar with the program's purpose, you will be able to 
  2039. concentrate on the AES functions introduced to invoke the alert 
  2040. form.  I suggest that you change the name of this program and put 
  2041. it to good use as a desk accessory.  I call the source file 
  2042. PRTASCII.S and the object file PRTASCII.ACC in my system.
  2043.   
  2044. Program 81. A desk accessory linear print buffer.  The AES file 
  2045. selector function is invoked to obtain the path for the file to 
  2046. be printed.  If the desk accessory is selected while it is busy 
  2047. with a file, the form_alert function is invoked.
  2048.  
  2049.  ; Program Name: PRG_8FR.S
  2050.  ;      Version: 1.002
  2051.  
  2052.  ; Assembly Instructions:
  2053.  
  2054.  ;      Assemble in Relocatable mode and save with a PRG extension.  From
  2055.  ; the desktop, change the extension to ACC.  Programs that are to function
  2056.  ; as desk accessories MUST be assembled in Relocatable mode. 
  2057.  
  2058.  ; Function:
  2059.  
  2060.  ;      A desk accessory that prints the file selector chosen ASCII file.
  2061.  ; The print buffer which is used by this program is linear, not circular.
  2062.  ; This program also installs a parallel interface interrupt handler.  The
  2063.  ; handler is invoked each time that the logic level of pin 11 of the parallel
  2064.  ; port drops from high to low.
  2065.  
  2066.  ;      This program initiates the interrupt controlled printing process by
  2067.  ; sending a NULL character to the printer (A BELL character could also be
  2068.  ; used, if that is desirable.).
  2069.  
  2070.  ;      The default size of the linear print buffer is 32,768 bytes.  You can
  2071.  ; alter the buffer size and assemble the program with the larger size if you
  2072.  ; desire.
  2073.  
  2074.  ;      The program reads the file to be printed and stores it in the buffer
  2075.  ; very rapidly, but the speed with which the buffer is cleared depends on
  2076.  ; the particular system configuration: if there is an external hardware buffer
  2077.  ; between the computer and the printer, or if the printer has an internal
  2078.  ; buffer of significant size, then the linear buffer will transfer its
  2079.  ; information to the external buffers very rapidly also.
  2080.  
  2081.  ;      If there are no such buffers to speed up the transfer rate from the
  2082.  ; buffer to the printer, then it is possible that you may interrupt a session
  2083.  ; in progress by selecting the desk accessory before the buffer is clear.  If
  2084.  ; that event occurs, an alert box will be displayed indicating that the buffer
  2085.  ; is busy.  You will be able to select OK to continue the printing session
  2086.  ; (a Return key press is sufficient), or you may select CANCEL to stop the
  2087.  ; session.  If you do choose to cancel the session, the buffer will be cleared
  2088.  ; immediately; but remember that the printer will continue to print until its
  2089.  ; internal buffer, however small, is also cleared.
  2090.  
  2091.  ; Execution Instructions:
  2092.  
  2093.  ;      Place PRG_8FR.ACC in the root directory of your boot disk.  During
  2094.  ; the next power-up cycle, the program will be installed in memory as a desk
  2095.  ; accessory; assuming that it is not blocked by a program that permits desk
  2096.  ; accessory loading selections.  Of course, if you use MultiDesk, you need
  2097.  ; not power up to use PRG_8FR.ACC immediately.
  2098.  
  2099.  ;      The desk accessory is identified as a menu selection by the name
  2100.  ; PRINT ASCII.
  2101.  
  2102.  ; MAJOR NOTE:
  2103.  
  2104.  ;      Although accessories such as MultiDesk add significant power to a ST
  2105.  ; system; and although initial testing of a desk accessory program can be
  2106.  ; accomplished via MultiDesk loading of the program, thereby alleviating the
  2107.  ; the testing process, the program under test cannot be pronounced correct
  2108.  ; until it has been permitted to load normally from the boot disk.
  2109.  
  2110.  ;      For example, if the first statement of this program, which loads the
  2111.  ; stack's address into A7, is moved to a location following the routine that
  2112.  ; installs the interrupt handler, the accessory will function perfectly when
  2113.  ; it is loaded and executed via MultiDesk; but it will bomb during boot
  2114.  ; because, at that time, no default stack has yet been assigned by the system.
  2115.  
  2116.  ; REMEMBER:  Programs executed during boot MUST provide their own stacks.
  2117.  
  2118.  ; NOTE: Within the program, registers are used as variables as much as
  2119.  ;       possible to speed things up.
  2120.  
  2121.  lea        stack(pc), a7        ; This must be the first instruction.
  2122.  
  2123. install_interrupt_handler_vector:; Parallel port pin 11 interrupt handler.
  2124.  pea        interrupt_handler(pc); Push interrupt handler address.
  2125.  move.w     #0, -(sp)            ; Interrupt level for MFP GPIP bit 0.
  2126.  move.w     #$D, -(sp)           ; Functon = XBIOS #13 decimal = mfpint.
  2127.  trap       #14
  2128.  addq.l     #8, sp
  2129.  
  2130. install_desk_accessory:
  2131.  lea        aes_pb(pc), a3       ; aes_pb = AES parameter block.
  2132.  move.l     a3, d5               ; Use D5 as a variable for aes_pb address.
  2133.  lea        control(pc), a4      ; A4 is pointer for array 'control'.
  2134.  move.w     #$C8, d3             ; *** D3 is variable for AES call number.
  2135.  
  2136. initialize_application:
  2137.  move.w     #$A, (a4)            ; Function = appl_init = AES $A.
  2138.  move.w     #1, 4(a4)            ; Return one int_out parameter.
  2139.  move.l     d5, d1               ; D5 contains address of aes parameter block.
  2140.  move.w     d3, d0               ; D3 contains AES call number.
  2141.  trap       #2                   ; apid returned in int_out[0] and global[2].
  2142.  
  2143. menu_installation:             
  2144.  move.w     #$23, (a4)           ; Function = menu_register = AES $23.
  2145.  move.w     #1, 2(a4)            ; Pass one int_in parameter.         
  2146.  move.w     #1, 6(a4)            ; Pass one addr_in parameter.
  2147.  lea        int_out(pc), a6      ; Fetch address of int_out array.
  2148.  move.w     (a6), int_in         ; Application identification to int_in[0].
  2149.  lea        addr_in(pc), a5      ; Fetch address of addr_in array.
  2150.  move.l     #menu_text, (a5)     ; Menu text address to addr_in[0].
  2151.  move.l     d5, d1               ; Address of aes parameter block to D1.
  2152.  move.w     d3, d0               ; D3 contains AES call number.
  2153.  trap       #2                   ; Menu identification number returned
  2154.                                  ; in int_out[0].
  2155.  move.w     (a6), d4             ; Store menu identification number in D4.
  2156.  
  2157.  ; MAIN ACCESSORY LOOP
  2158.  
  2159.  lea        message(pc), a3      ; A3 is variable for message array address.
  2160. wait_for_message:                ; Relinquish processor control.
  2161.  move.w     #$17, (a4)           ; Function = evnt_mesag = AES $17.
  2162.  move.w     #0, 2(a4)            ; Input one 16-bit integer parameter.
  2163.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.    
  2164.  move.w     #1, 6(a4)            ; Input one 32-bit pointer parameter.
  2165.  move.w     #0, 8(a4)            ; Return no 32-bit pointer parameters.
  2166.  move.l     a3, (a5)             ; Address of message array to addr_in.
  2167.  
  2168.  ; The above instruction must be executed each time evnt_mesag is invoked
  2169.  ; because the form_alert and fsel_input functions must store addresses in
  2170.  ; addr_in also.  If that were not so, then the evnt_mesag setup could be
  2171.  ; simplified; that is, the address of the message array could be stored in
  2172.  ; addr_in once, before the wait_for_message label.
  2173.  
  2174.  ; In addition, I have been successful with the following abbreviated
  2175.  ; implementation of the evnt_mesag function, even though the fsel_input
  2176.  ; function intervenes between evnt_mesag invocations:
  2177.  
  2178.  ;                  lea        message(pc), a3 
  2179.  ;                 wait_for_message:             
  2180.  ;                  move.w     #$17, (a4)        
  2181.  ;                  move.w     #0, 2(a4)         
  2182.  ;                  move.l     a3, (a5)          
  2183.  ;                  move.l     d5, d1            
  2184.  ;                  move.w     d3, d0            
  2185.  ;                  trap       #2
  2186.  
  2187.  ; Therefore, it does not seem to matter that the fsel_input function 
  2188.  ; requires that the value 2 in 4(a4) and 6(a4), while the evnt_mesag
  2189.  ; function requires the value 1 in those control array elements.  I have
  2190.  ; not used the abbreviated version in this program because I want to avoid
  2191.  ; any unexplained phenomenona in your implementation of the program.  But
  2192.  ; you should be aware of this type of simplification technique because it
  2193.  ; permits shorter, faster programs.
  2194.   
  2195.  move.l     d5, d1               ; Address of aes parameter block to D1.
  2196.  move.w     d3, d0               ; AES call number to D0.
  2197.  trap       #2
  2198.  
  2199.  ; When a message is received it is placed in array 'message'.
  2200.  
  2201.  ; ****************************************************************************
  2202.  ; ****************************************************************************
  2203.  
  2204.  ; Execution proceeds past this point only when a message is received.
  2205.  ; Preserve contents of registers A3, A4, A5, D3, D4, and D5 during the rest of
  2206.  ; the program.
  2207.  
  2208. message_handler:                 ; Entrance point when message is received.
  2209.  lea        message(pc), a0      ; Fetch address of array 'message'.
  2210.  cmpi.w     #$28, (a0)           ; Compare ACCESSORY OPEN code with message[0].
  2211.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  2212.  move.w     8(a0), d0            ; The menu item selected is stored in element
  2213.                                  ; four (message[4]) of array 'message'.  This
  2214.                                  ; application's id # is in D4.
  2215.  cmp.w      d4, d0               ; Was this application selected.
  2216.  bne.s      wait_for_message     ; Execute the evnt_mesag function.
  2217.  
  2218.  ; ****************************************************************************
  2219.  ; ****************************************************************************
  2220.  
  2221.  ; Execution proceeds past this point only when this application has been
  2222.  ; selected from the menu.
  2223.  
  2224.  ; Preserve contents of registers A3, A4, A5, D3, D4, and D5 during the rest of
  2225.  ; the program.
  2226.  
  2227. busy_test:                       ; Printing session in progress test.
  2228.  move.l     filesize, d0         ; Fetch file size.
  2229.  beq.s      get_current_drive    ; Display alert if buffer is busy.
  2230. form_alert:
  2231.  move.w     #$34, (a4)           ; Function = form_alert = AES $34.
  2232.  move.w     #1, 2(a4)            ; Input one 16-bit integer parameter.
  2233.  move.w     #1, 4(a4)            ; Return one 16-bit integer parameter.    
  2234.  move.w     #1, 6(a4)            ; Input one 32-bit pointer parameter.
  2235.  move.w     #0, 8(a4)            ; Return no 32-bit pointer parameters.
  2236.  move.w     #1, int_in           ; Default exit button to int_in[0].
  2237.  move.l     #alert_text, addr_in ; Alert text address to addr_in[0].
  2238.  move.l     d5, d1               ; Address of aes parameter block to D1.
  2239.  move.w     d3, d0               ; AES call number to D0.
  2240.  trap       #2
  2241.  cmp.w      #1, int_out          ; Button test.
  2242.  beq        wait_for_message     ; Continue printing.
  2243.  move.l     #0, filesize         ; Cancel printing session.
  2244.  move.l     #buffer, extract_pointer
  2245.  bra        wait_for_message 
  2246.  
  2247. get_current_drive:               ; Current drive = last drive accessed.
  2248.  move.w     #$19, -(sp)          ; Function = c_getdrv.
  2249.  trap       #1                   ; Current drive number returned in D0.
  2250.  addq.l     #2, sp
  2251.  
  2252.  ; Here we begin to construct the path that will be displayed when the
  2253.  ; file selector function is invoked.  Below, I indicate the data that
  2254.  ; is actually being extracted from the system.  Most references do not
  2255.  ; accurately describe what is being done with these functions.
  2256.  
  2257.  add.w      #$41, d0             ; Convert drive number to drive letter.
  2258.  lea        path(pc), a6         ; Fetch address of array 'path'.
  2259.  move.b     d0, (a6)+            ; Store drive letter in 'path' array.
  2260.  move.b     #$3A, (a6)+          ; Store a colon in 'path' array.
  2261.  
  2262. get_sub_directories:             ; The drive is the main directory.
  2263.  move.w     #0, -(sp)            ; See Internals page 135 for other values.
  2264.  pea        (a6)                 ; Push address of next 'path' array element.
  2265.  move.w     #$47, -(sp)          ; Function = d_getpath = get subdirectories.
  2266.  trap       #1                   ; Subdirectories are returned in the array
  2267.  addq.l     #8, sp               ; 'path'.
  2268.  
  2269. add_extension:
  2270.  
  2271.  ; The extension '\*.*' is added to complete the path.  This string must be
  2272.  ; NULL terminated.  For example, assume that the get_current_drive routine
  2273.  ; returns #6; adding 41 would convert this to ASCII F.  After the drive
  2274.  ; letter and the colon are stored in the path array, the elements of the
  2275.  ; array would contain:
  2276.  
  2277.  ;             F:____________________________________,etc.
  2278.  
  2279.  ; Whatever the get_sub_directories routine returns will be placed in the path
  2280.  ; array; the first character will be stored in element 3.  Assume that the
  2281.  ; routine returns \FILE_SEL as a subdirectory.  The path array would contain:
  2282.  
  2283.  ;             F:\FILE_SEL___________________________,etc.
  2284.  
  2285.  ; The extension is then added to the array contents to yield:
  2286.  
  2287.  ;             F:\FILE_SEL\*.*O______________________,etc.
  2288.  
  2289.  ; where the O represents the NULL character.  This is the path.  At this
  2290.  ; time there is no file name in the path.
  2291.  
  2292.  ; When the file selector is displayed, the contents of array 'path' will
  2293.  ; appear on the path = directory line.  The selection line will be blank
  2294.  ; because the program does not write anything to that line.
  2295.  
  2296. find_path_end:                   ; Find NULL at end of returned string.
  2297.  tst.b      (a6)+
  2298.  bne        find_path_end        ; Branch back till NULL is found.
  2299.  subq.l     #1, a6               ; Back up to overwrite the NULL.
  2300.  move.b     #$5C, (a6)+          ; Add '\'.
  2301.  move.b     #$2A, (a6)+          ; Add '*'.
  2302.  move.b     #$2E, (a6)+          ; Add '.'.
  2303.  move.b     #$2A, (a6)+          ; Add '*'.
  2304.  move.b     #0, (a6)             ; Add NULL.
  2305.  
  2306. display_file_selector:           ; COMPUTE!'s AES book page 282.  
  2307.  move.w     #$5A, (a4)           ; Function = fsel_input.
  2308.  move.w     #0, 2(a4)            ; Returns the drive name and subdirectories
  2309.  move.w     #2, 4(a4)            ; selected by the user in the array 'path'
  2310.  move.w     #2, 6(a4)            ; and, if a file was selected, it is returned
  2311.  move.w     #0, 8(a4)            ; in the array 'file'.
  2312. initialize_address_in_array:
  2313.  lea        addr_in(pc), a1      ; Fetch address of array 'addr_in'.
  2314.  lea        path(pc), a6         ; Fetch address of array 'path'.
  2315.  move.l     a6, (a1)             ; Store address of 'path' in 'addr_in'.
  2316.  move.l     #file, 4(a1)         ; Store address of 'file' in 'addr_in'.
  2317.  move.l     d5, d1               ; Address of aes parameter block to D1.
  2318.  move.w     d3, d0               ; D3 contains AES call number.
  2319.  trap       #2
  2320.  
  2321. analyze_returns:                 ; Look at returns for File Selector box.
  2322.  tst.w      int_out + 2          ; CANCEL button => 0.
  2323.  beq        wait_for_message     ; Go there if CANCEL was selected.
  2324.  tst.b      file                 ; NO FILE CHOSEN => 0.
  2325.  beq        wait_for_message     ; Go there if no file was chosen.
  2326.  
  2327. find_selected_path_end:          ; Search for first asterisk.
  2328.  
  2329.  ; At this point, the selected path does not include the file name, so
  2330.  ; it is only a "partial" path.
  2331.  
  2332.  cmp.b      #$2A, (a6)+         
  2333.  bne        find_selected_path_end
  2334.  subq.l     #1, a6               ; Back up to overwrite the asterisk.
  2335.  
  2336.  ; Here we add the file name to the path by appending it to the "partial"
  2337.  ; path.  Then we will have constructed the complete path.
  2338.  
  2339.  lea        file(pc), a0
  2340. add_filename_to_path:
  2341.  move.b     (a0)+, (a6)+
  2342.  bne.s      add_filename_to_path  ; Now we have a true path.
  2343.  
  2344. set_dta:
  2345.  pea        dta(pc)              ; dta = address of 44 byte buffer.
  2346.  move.w     #$1A, -(sp)          ; GEMDOS function = set dta.
  2347.  trap       #1
  2348.  addq.l     #6, sp
  2349.  
  2350. search_for_file: 
  2351.  move.w     #0, -(sp)            ; Attribute = normal access.
  2352.  pea        path(pc)             ; Push address of path.
  2353.  move.w     #$4E, -(sp)          ; GEMDOS function = search first.
  2354.  trap       #1
  2355.  addq.l     #8, sp
  2356.  tst        d0
  2357.  bne        wait_for_message     ; Do nothing if file not found.
  2358.  
  2359. open_file:                       ; Function returns file handle in D0.
  2360.  move.w     #0, -(a7)            ; Open as read only.
  2361.  pea        path(pc)             ; Push address of path.
  2362.  move.w     #$3D, -(a7)          ; See Internals page 127.
  2363.  trap       #1
  2364.  addq.l     #8, a7
  2365.  move.w     d0, d6               ; Store file handle in D6.
  2366.  
  2367. read_file:
  2368.  pea        buffer(pc)           ; Push buffer address
  2369.  lea        dta(pc), a0
  2370.  move.l     $1A(a0), filesize    ; Store file size in handler variable.
  2371.  move.l     $1A(a0), -(sp)       ; Number of bytes to read.
  2372.  move.w     d6, -(sp)            ; File's handle number.
  2373.  
  2374.  ; NOTE:
  2375.  
  2376.  ;     While I use the terminology found in the references when I say that
  2377.  ; D6 contains the file's handle, the truth is that we are not dealing with
  2378.  ; "just" a file; we are dealing with a path, therefore, D6 actually contains
  2379.  ; a path handle, not a file handle.
  2380.  
  2381.  move.w     #$3F, -(sp)          ; GEMDOS function = read.
  2382.  trap       #1
  2383.  lea        $C(sp), sp           ; Reposition stack pointer.
  2384.  
  2385. close_file:                      ; Actually, close the path.
  2386.  move.w     d6, -(sp)            ; Push file (path) handle.
  2387.  move.w     #$3E, -(sp)          ; See Internals page 128.
  2388.  trap       #1
  2389.  addq       #4, sp
  2390.  
  2391. initiate_interrupt_process:      ; Execute in supervisor mode.
  2392.  pea        send_null(pc)
  2393.  move.w     #$26, -(sp)
  2394.  trap       #14               
  2395.  addq.l     #6, sp
  2396.  
  2397.  bra        wait_for_message     ; Execute the evnt_mesag function.
  2398.  
  2399. send_null:                       ; The printer will drop the logic level of
  2400.  moveq.l    #0, d0               ; pin 11 from high to low after receiving this
  2401.                                  ; character, even though nothing will be
  2402.                                  ; printed.
  2403.  lea        $FF8800, a1          ; Address of Programmable Sound Generator.
  2404.  move.b     #$F, (a1)            ; Select port B (register 17) of PSG.
  2405.  move.b     d0, 2(a1)            ; Write character to PSG register 17.
  2406.  move.b     #$E, (a1)            ; Select port A (register 16) of PSG.
  2407.  move.b     (a1), d0             ; Get current register 16 value.
  2408.  andi.b     #$DF, d0             ; Bit 5 to zero.          
  2409.  move.b     d0, 2(a1)            ; Bit 5 of port A to zero.
  2410.  ori.b      #$20, d0             ; Bit 5 to one.
  2411.  move.b     d0, 2(a1)            ; Bit 5 of port A to one.
  2412.  rts
  2413.  
  2414. interrupt_handler:               ; Parallel interface interrupt handler.
  2415.  movem.l    d0/a0-a1, -(sp)      ; Save content of registers used by handler.
  2416.  move.l     filesize, d0         ; Fetch file size.
  2417.  beq.s      clear_in_service_bit ; Exit if buffer is empty (filesize = 0).
  2418.  lea        io_buffer, a0        ; Fetch address of io_buffer pointer.
  2419.  movea.l    4(a0), a1            ; Fetch content of extract pointer.
  2420.  subq.l     #1, d0               ; Decrement filesize.
  2421.  move.l     d0, 8(a0)            ; Store new filesize value.
  2422.  bne.s      print_character      ; Stop printing if filesize is 0.
  2423.  move.l     (a0), 4(a0)          ; Put buffer start address in extract pointer.
  2424.  bra.s      clear_in_service_bit
  2425.  
  2426. print_character:
  2427.  move.b     (a1)+, d0            ; Fetch character.  Increment extract pointer.
  2428.  move.l     a1, 4(a0)            ; Store location of next character.
  2429.  lea        $FF8800, a1          ; Address of Programmable Sound Generator.
  2430.  move.b     #$F, (a1)            ; Select port B (register 17) of PSG.
  2431.  move.b     d0, 2(a1)            ; Write character to PSG register 17.
  2432.  move.b     #$E, (a1)            ; Select port A (register 16) of PSG.
  2433.  move.b     (a1), d0             ; Get current register 16 value.
  2434.  
  2435. strobe_low:                   
  2436.  andi.b     #$DF, d0             ; Bit 5 to zero.          
  2437.  move.b     d0, 2(a1)            ; Bit 5 of port A to zero.
  2438.                               
  2439. strobe_high:                  
  2440.  ori.b      #$20, d0             ; Bit 5 to one.
  2441.  move.b     d0, 2(a1)            ; Bit 5 of port A to one.
  2442.  
  2443. clear_in_service_bit:
  2444.  movem.l    (sp)+, d0/a0-a1      ; Restore registers used by handler.
  2445.  bclr       #0, $FFFA11          ; Clear MFP in service bit.
  2446.  rte
  2447.  
  2448.  data
  2449. menu_text:       dc.b '  PRINT ASCII ',0
  2450. alert_text:      dc.b '[3][ | | | Print buffer is busy.][OK|CANCEL]',0
  2451.  
  2452.  ; In the above declaration, each blank space followed by a vertical bar
  2453.  ; inserts a blank line in the alert box.  I have used this method of
  2454.  ; formatting the line of text so that it is where I want it in the box.
  2455.  
  2456.  align
  2457. io_buffer:       dc.l  buffer     ; Pointer to buffer's starting address.
  2458. extract_pointer: dc.l  buffer     ; Buffer character output pointer.
  2459. filesize:        dc.l       0     ; filesize = 0 when buffer is empty.
  2460. aes_pb:          dc.l  control,global,int_in,int_out,addr_in,addr_out
  2461.  
  2462.  bss
  2463.  
  2464.  ;
  2465.  ; AES ARRAYS: The addresses of the arrays declared below will be stored in
  2466.  ;             the pointer array 'aes_pb'.  That happens because the program
  2467.  ;             is assembled in Relocatable mode.  Note that minimum space
  2468.  ;             has been reserved for the int_in, int_out, addr_in and
  2469.  ;             addr_out structures.  It is the fsel_input function which
  2470.  ;             requires the 2 words for int_out and the 2 longwords for
  2471.  ;             addr_in.
  2472.  ;
  2473.  
  2474. control:      ds.w     5          ; Control parameters.
  2475. global:       ds.w    15          ; Global parameters.
  2476. int_in:       ds.w     1          ; Input parameters.
  2477. int_out:      ds.w     2          ; Output parameters.
  2478. addr_in:      ds.l     2          ; Input addresses.
  2479. addr_out:     ds.l     1          ; Output addresses.
  2480.  
  2481.  ;
  2482.  ; FSEL_INPUT ARRAYS
  2483.  ;
  2484.  
  2485. path:         ds.b   120          ; Array for drive name and subdirectories.
  2486. file:         ds.b    14          ; Array for selected file.
  2487.  
  2488.  ;
  2489.  ; APPLICATION VARIABLES
  2490.  ;
  2491.  
  2492. message:      ds.l     4          ; 16 byte array.
  2493. dta:          ds.l    11          ; 44 byte array.
  2494.               ds.l   300          ; Program stack.
  2495. stack:        ds.l     0          ; Address of program stack.
  2496. buffer:       ds.l $2000          ; Linear print buffer.
  2497.  end
  2498.  
  2499.  
  2500. Other AES Functions
  2501.   
  2502.      Of the 11 AES libraries discussed on pages 8 and 9 of 
  2503. COMPUTE!'s AES book, I have briefly explored the Application 
  2504. Library, the Event Library, the Menu Library, the Form Library 
  2505. and the File Selector Library.  I am going to delay discussions 
  2506. concerning the other libraries until I have introduced VDI 
  2507. Initialization Algorithms and Resource Construction.  Once again, 
  2508. I want to mention that many of these other functions are 
  2509. discussion in magazines, such as ST Log and Atari Explorer.  
  2510. Also, these subjects are discussed in COMPUTE!'s AES book.  If 
  2511. the xdef and xref assembler directives seen in programs designed 
  2512. for assemblers other than AssemPro seem foreign to you, then you 
  2513. should consult those subjects in references such as the Ford and 
  2514. Topp or Stan Kelly-Bootle books.
  2515.  
  2516. Conclusion
  2517.   
  2518.      I have kept this chapter short and uncomplicated; it 
  2519. contains enough information about the AES to permit further 
  2520. explorations using the COMPUTE! AES book if you are impatient, 
  2521. but it is not cluttered with so much information that it bogs you 
  2522. down.  In addition, I have introduced some of the more 
  2523. interesting TOS functions.  I suggest that you explore these 
  2524. further using the COMPUTE! TOS book.  Sheldon Leemon has written 
  2525. three excellent books for the ST; each seems better than the 
  2526. last; and I hope he continues his superior work.  His references 
  2527. are valuable resources for both C language and assembly language 
  2528. programmers.
  2529.      I will continue the discussion of desk accessories in the 
  2530. next chapter, but it will be from a shifted viewpoint.  Enough 
  2531. material has been covered to permit excursions into the subject 
  2532. of alterations to software packages produced by others, which you 
  2533. purchase or obtain via public domain, but which do not perform 
  2534. entirely to your personal satisfaction.
  2535.  
  2536.