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

  1.      Atari ST Machine Specific Programming In Assembly
  2.  
  3. Chapter 8: Seizing Control of the System
  4.   
  5. The If Onlies
  6.   
  7.      I think it would be very interesting to read a report 
  8. of experiments conducted to determine the mean time between 
  9. computer system purchase and the first utterance of if 
  10. only... in connection with product design, construction or 
  11. operating system.  Some of my favorite ST if onlies are:
  12.  
  13.      1. If only the operating system were written in 
  14.         assembly language instead of C.
  15.  
  16.      2. If only the cartridge port data lines were 
  17.         bidirectional.
  18.  
  19.      3. If only GDOS were more like G+PLUS.
  20.  
  21.      4. If only the file selector function were more like 
  22.         Universal II.
  23.  
  24.      5. If only we were not limited to six desk accessories.
  25.  
  26.      If only some manufacturer would design the perfect 
  27. computer system and sell it to me at a price that I could 
  28. afford.  Well, that's not likely to happen any time soon, 
  29. say not before hell freezes over; neither are the if onlies 
  30. associated with any computer system likely to ever be 
  31. eliminated.  However, to the extent that manufacturers and 
  32. programmers find it profitable to satisfy a majority of 
  33. users, a continuous stream of improvements and corrections, 
  34. in one form or another, is guaranteed.  For ST users, 
  35. Universal II (a better file selector), G+PLUS (an improved 
  36. GDOS), MultiDesk (a desk accessory which eliminates the 
  37. operating system limitation of six) and a method of getting 
  38. data out of the cartridge port (See Cartridge Port 
  39. Interface, by Randy Constan, ST LOG, January 1989.) are 
  40. already available.
  41.      In this chapter I shall begin to address the subject of 
  42. if onlies elimination with user designed software.  I am not 
  43. going to dwell extensively on aspects of ST system design 
  44. and construction which, in my opinion, or in the opinion of 
  45. others, constitute errors or limitations, because, however 
  46. much I might wish that certain details of that design and 
  47. construction would conform to my own ideas of perfection, 
  48. the fact is that I could not have done a better job than was 
  49. done by the manufacturer.  Yet it is possible for me and for 
  50. you to redirect certain operating system activities, and/or 
  51. to design surrogate functions that effectively tailor the 
  52. ST's system controlling software so that it more accurately 
  53. responds to our individual needs or tastes.  Therefore, I 
  54. shall offer suggestions that I believe are indicative of the 
  55. methodology involved in altering some aspects of the 
  56. operating system in order to effect subjective improvements.
  57.  
  58. The Second MC68000 Flaw
  59.   
  60.      I discussed one of what I consider to be two serious 
  61. flaws in the design of the MC68000 microprocessor in chapter 
  62. 3.  I was then, as I am now, forced to remind you that the 
  63. qualification of these design characteristics as flaws is my 
  64. response to the limitations imposed upon my programming 
  65. efforts by those characteristics.  In chapter 3, I spoke of 
  66. the obstacle to storing data imposed by the addressing 
  67. scheme that rejects pc-relative destination operands.  Here 
  68. I am concerned with the limitations imposed by the existence 
  69. of two privilege states.  I am not amused by the necessitity 
  70. of having to go through the trouble of forcing the processor 
  71. into the supervisor state in order to access system 
  72. variables.  I could speak of the limitations imposed by this 
  73. attribute of the processor for hours; but I have promised 
  74. not to dwell on such unpleasant topics.  Instead, I shall 
  75. propose methods of performing the transition between states 
  76. as painlessly as possible.  But I want to stress that it is 
  77. an attribute that must be overcome in order to write 
  78. efficient software.  Furthermore, in order to illustrate 
  79. that I know that I am not infallible, and that I realize 
  80. that I could be completely wrong about everything that I 
  81. have said concerning flaws in the design of the MC68000, I 
  82. remind you that it is my opinion that these flaws exist.
  83.  
  84. Normal Processor State Toggling
  85.   
  86.      Without interference from an outside agent, whenever a 
  87. program is executed, the operating system relinquishes 
  88. processor control to that program.  During the program's 
  89. execution cycle, it will probably return processor control 
  90. to the operating system many times by invoking system 
  91. functions.  During these invocation cycles, through 
  92. operating system directed activities, the functions perform 
  93. services for the program.  After each such invocation, 
  94. processor control is returned to the program.  In this 
  95. chapter I shall discuss methods of seizing control of system 
  96. functions so that their activities may be redirected by the 
  97. invoking program.  In addition, I shall discuss user defined 
  98. functions that act as surrogates for system functions.  
  99. Usually, the surrogate functions will access system 
  100. variables.  Those accesses must be performed while the 
  101. processor is in the supervisor state.
  102.  
  103. Absolute Power Corrupts Absolutely
  104.   
  105.      The first lesson to be learned about seizing control of 
  106. anything is that control is inherently tenuous.  Ask any 
  107. deposed dictator.  When you seize control of ST functions so 
  108. that you can redirect their activities, or when you seize 
  109. control of the processor state, you can remain in control as 
  110. long as you prevent vital portions of the system from 
  111. becoming corrupted.  Here, the word system includes all 
  112. hardware and resident software.  I will try to remember to 
  113. tell you as much as possible about maintaining compatibility 
  114. between your applications and others, between your 
  115. applications and the operating system and between your 
  116. applications and hardware.  However, I feel it prudent to 
  117. cover my ass now, by saying that, even if I do manage to 
  118. tell you all that I know, it will probably not be enough.  
  119. All that I can really hope to do is to help you develop a 
  120. certain wariness about potential compatibility problems 
  121. between certain types of software/software and 
  122. software/hardware combinations.
  123.      Compounding the complexity of the task we face in 
  124. understanding each other in this chapter is the 
  125. intractability of the terminology required for the 
  126. discussions to follow.  This terminology can be perplexing, 
  127. even to the most seasoned programmers.  One of the reasons 
  128. that this is so is that discussants can rarely be certain 
  129. that everyone is familiar with the vocabulary involved, and 
  130. even when terminological familiarity is not a problem, the 
  131. congruency of definitions remains questionable.  Some of the 
  132. terms and definitions with which we must contend are to be 
  133. found in the references under headings such as exceptions, 
  134. exception vectors, exception handlers/handling, vectors, 
  135. interrupts, interrupt structure, traps and, perhaps, some 
  136. others, depending on the particular vernacular of the 
  137. writers involved.
  138.      My task is to describe required algorithms with 
  139. lubricious phraseology so that you will understand (or at 
  140. least be able to fake an understanding of) what you are 
  141. doing, whether or not your references are comprehensible.  I 
  142. suggest that you read as much as you can concerning the 
  143. topics mentioned above, but with particular concentration on 
  144. exceptions caused by the Trap instruction and those caused 
  145. by external hardware.  I shall not be discussing exceptions 
  146. involving resets, bus errors, address errors, and tracing.
  147.  
  148. Accessing System Variables
  149.   
  150.      The ST's operating system variables are accessible only 
  151. when the processor is in the supervisor mode.  I pointed 
  152. this out when I introduced the get_time subroutine in 
  153. program 11.  You can see a partial listing of system 
  154. variables on pages 236-237 and 250-257 of the Internals 
  155. book.  The Peel book also contains a partial listing on 
  156. pages A-2 through A-6.  Each book contains some variables 
  157. not listed by the other.
  158.      A dissonance greater than disagreement between 
  159. information contained in books exists, however.  Atari does 
  160. not guarantee that system variables located at addresses 
  161. below $400 will not change in future versions of the 
  162. operating system.  In chapter 1, I pointed out that 
  163. consumers must realize that the product they receive at the 
  164. time of purchase is the product they should plan to possess, 
  165. until a new purchase is made.  There is no reason to expect 
  166. that any alterations to future models of the product will 
  167. have any effect on the model purchased.  Certainly, there is 
  168. no reason to expect that any effects will be beneficial.  
  169. Therefore, as long as you are willing to learn to control 
  170. the product you have, instead of anticipating enhancements 
  171. by the manufacturer, you reduce the manufacturer's control 
  172. over you by alleviating the impact of future modifications 
  173. to the product.  In retaliation, then, we adopt a pugnacious 
  174. attitude and thumb our noses at malicious attempts to thwart 
  175. our progress via the alteration of variables.  We shall work 
  176. with what we have.
  177.      So, I continue with the mutual understanding that there 
  178. is some finite chance, however slim, that operating system 
  179. variables used in this book might not match those of your 
  180. system, and, if such circumstances should occur, you will be 
  181. forced to seek out knowledge about such differences from 
  182. other references or by searching through system memory 
  183. yourself.  We do begin with at least one constant.  The 
  184. behavior of the MC68000's supervisor mode is not likely to 
  185. change.
  186.      In addition to permitting access to system variables, 
  187. the supervisor mode permits the execution of certain 
  188. instructions which cannot be executed in the user mode.  
  189. Although it may not be immediately obvious, there need be no 
  190. compelling reason not to execute ordinary instructions in 
  191. the supervisor mode.  (Instructions which must be executed 
  192. in supervisor mode are called privileged instructions.)  
  193. However, assuring that one is in user mode before exiting a 
  194. program is prudent, to say the least.
  195.  
  196. Forcing the Processor Into Supervisor Mode
  197.   
  198.      As you will learn from reading the references, there is 
  199. only one method by which the MC68000 can be forced into the 
  200. supervisor state.  An exception generating instruction must 
  201. be executed.  Exception handlers usually return processor 
  202. control to the exception generating source with the 
  203. processor in the user state.  The Atari operating system 
  204. provides GEMDOS function $20 as an orderly way of locking 
  205. the processor state in supervisor mode until the invoking 
  206. program forces the processor back into user mode.  Getting 
  207. back into user mode can be accomplished in several ways 
  208. (Refer to Privilege State Changes in the Motorola manual.), 
  209. however, if the supervisor mode has been entered via GEMDOS 
  210. function $20, then the return to user mode is usually 
  211. accomplished via the same function.
  212.      I remind you that the easiest way to force the 
  213. processor into the supervisor state is by executing a trap 
  214. instruction.  You have seen this done in the program which 
  215. installs custom traps.  Once you are in a trap routine, the 
  216. processor is in supervisor mode and it will remain in that 
  217. state for the duration of the trap invocation unless bit 13 
  218. of the status register is reset to zero.  Furthermore, if 
  219. bit 13 of the stack word that is the content of the status 
  220. register, as it was before the trap invocation, is set to 
  221. one, then the invoking program will be executing in 
  222. supervisor mode when the return from exception instruction 
  223. is executed by the trap handler.
  224.  
  225. Stack Pointer Control
  226.   
  227.      When the processor is toggled between modes, one of the 
  228. most compelling concerns of a programmer is stack pointer 
  229. control.  Without external interference, the processor will 
  230. use the supervisor stack when it is in supervisor mode, and 
  231. it will use the user stack when it is in user mode.  The 
  232. processor accomplishes this by placing the address of the 
  233. supervisor stack in the supervisor stack pointer (SSP) and 
  234. by placing the address of the user stack in the user stack 
  235. pointer (USP).  This is the default stack pointer control.  
  236. Neglecting the issue of desirability, for the moment, it is 
  237. important to realize that GEMDOS $20 transfers the address 
  238. of the user stack to the supervisor stack pointer (SSP) when 
  239. it is used to toggle the processor from the user state to 
  240. the supervisor state.  GEMDOS $20 expects that the address 
  241. of the supervisor stack will be saved and returned to it 
  242. when it is invoked to toggle back to user mode.  At that 
  243. time the function will reinstate the address from whence it 
  244. came.  During all of this, the content of the USP remains 
  245. constant.
  246.      If a programmer decides to force the processor into the 
  247. supervisor state by some other method, perhaps by using a 
  248. custom trap similar to trap #0, which is installed by 
  249. CUSTOM.PRG, then the programmer assumes responsibility for 
  250. stack control.  The programmer can chose to continue 
  251. execution with the SSP pointing to the supervisor stack, or, 
  252. as is accomplished via GEMDOS $20, the address of the user 
  253. stack may be loaded into the SSP.  In any case, in addition 
  254. to fulfilling the program's stack needs, the active stack 
  255. must be large enough to accommodate all supervisor mode 
  256. activity, which includes, but is not limited to, the storage 
  257. of the program counter and status register during exception 
  258. handling.
  259.      I have designed three programs to assist me during this 
  260. discussion.  Program 42 installs a custom trap that 
  261. immediately prints the content of A7 = the address of the 
  262. supervisor stack, then sets bit 13 of the invoking program's 
  263. status register so that the processor will be in the 
  264. supervisor state when processor control is returned to the 
  265. trap invoking program.  During the course of its execution, 
  266. program 43 invokes the custom trap installed by program 42.  
  267. In addition, it also prints the content of A7 during other 
  268. stages of its execution.  Program 43 is divided into two 
  269. parts.  Part one is executed before a user stack is assigned 
  270. within the program.  This means that the USP contains the 
  271. address of the system assigned default user stack.  The 
  272. content of A7 is printed three times during the execution of 
  273. part one, as follows:
  274.  
  275.      1. Before the custom trap is invoked.
  276.   
  277.      2. During trap invocation, by the custom trap.
  278.   
  279.      3. After trap invocation.
  280.   
  281. Part two is executed after a user stack has been assigned.  
  282. From that point on, the USP contains the address of the 
  283. program assigned stack.  The content of A7 is printed four 
  284. times during the execution of part two, as follows:
  285.  
  286.      1. After the processor has been toggled to user mode 
  287.         and the user stack has been assigned.
  288.  
  289.      2. During trap invocation, by the custom trap.
  290.  
  291.      3. After trap invocation.
  292.  
  293.      4. After the processor has been toggled to user mode.
  294.  
  295.      The structure of program 44 is similar to that of 
  296. program 43, but it does not use the custom trap installed by 
  297. program 42 to force the processor into the supervisor state.  
  298. Instead, it does this by invoking GEMDOS $20.  Seven 
  299. addresses are printed by program 44 also, but since I have 
  300. no way to force the printing of A7's content during the 
  301. execution of function GEMDOS $20, the value returned by the 
  302. function in register D0 = address of supervisor stack is 
  303. printed instead.  This is not quite equivalent to that which 
  304. is done in program 43 because GEMDOS $20 returns the top 
  305. address of the supervisor stack, whereas the address printed 
  306. during the custom trap invocation is the address to which 
  307. the SSP has been decremented as a result of the generated 
  308. exception.  That address is 6 bytes lower than that of the 
  309. top of the supervisor stack, because a return address (4 
  310. bytes) and status register contents (2 bytes) has been 
  311. pushed onto the stack.  This means that the custom trap 
  312. prints the current content of the SSP.
  313.  
  314.  
  315. Program 42. A LSR program that installs custom trap #11.  
  316. This program must be executed before program 43 can be 
  317. executed.
  318.  
  319.  ; Program Name: PRG_6AP.S
  320.  ;      Version: 1.002
  321.  
  322.  ; Assembly Instructions:
  323.  
  324.  ;     Assemble in PC-relative mode and save with a TOS extension.
  325.  
  326.  ; Program Function:
  327.  
  328.  ;    This is a LSR program that installs custom trap #11, which is invoked
  329.  ; by PRG_6BP.TOS.  This program uses traps installed by CUSTOM.PRG.
  330.  
  331. program_start:                  ; Calculate program size and retain result.
  332.  lea        program_end, a3     ; Fetch program end address.
  333.  suba.l     4(a7), a3           ; Subtract basepage address.
  334.  
  335. enter_supervisor_mode:
  336.  trap       #0                  ; Sets bit 13 of status register to 1.
  337.  
  338. install_trap_11_routine:        ; Note: pointer = vector = pointer.
  339.  lea        trap_11_routine, a0 ; Fetch address of trap #11 routine.
  340.  move.l     a0, $AC             ; Store trap address at pointer address.
  341.  
  342. enter_user_mode:
  343.  andi.w     #$DFFF, SR          ; Sets bit 13 of status register to 0.
  344.  
  345. relinquish_processor_control:   ; Maintain memory residency.
  346.  move.w     #0, -(sp)           ; Exit code.
  347.  move.l     a3, -(sp)           ; Program size.
  348.  move.w     #$31, -(sp)         ; Function = ptermres = GEMDOS $31.
  349.  trap       #1
  350.  
  351. trap_11_routine:
  352.  
  353.  ; When trap #11 is invoked, the CPU pushes the calling program's return
  354.  ; address onto the supervisor stack, then it pushes the calling program's
  355.  ; status register contents onto the supervisor stack.
  356.  
  357.  ; This custom trap #11 handler sets bit #13 of the calling program's status
  358.  ; register.  The bset instruction can be used to set a bit in the first
  359.  ; byte of the word on the top of the stack.  Bit #13 of the status register
  360.  ; is bit #5 of that byte.  Note: to set a bit means to make it equal to 1.
  361.  
  362.  ; When the rte instruction is executed, the CPU will return to the calling
  363.  ; program with the altered copy of its status register, and the calling
  364.  ; program will now be executing in supervisor mode.
  365.  
  366.  ; Register A7 will contain the address of the supervisor stack.  The calling
  367.  ; program now has complete control of the system.  But a decision about
  368.  ; which stack is to be used must be made. 
  369.  
  370.  ; The calling program can choose to continue with A7 pointing to the 
  371.  ; supervisor stack, or it can save the contents of A7 = SSP and load the
  372.  ; address of a user stack into A7.  Processing can continue in that manner
  373.  ; until it is necessary to return to user mode.  The return to user mode
  374.  ; can be accomplished by reloading A7 with the SSP, then by resetting bit
  375.  ; #13 of the status register.  Note: resetting a bit means to make it equal
  376.  ; to 0.
  377.  
  378. print_current_SSP:              ; SSP = address of supervisor stack.
  379.  lea        header_1, a0
  380.  bsr.s      print_string
  381.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  382.  trap       #5
  383.  bsr.s      print_string
  384.  bsr.s      print_newline
  385.  bset       #5, (sp)            ; The invoking program will be executing in 
  386.  rte                            ; supervisor mode upon return.
  387.  
  388.  ;
  389.  ; SUBROUTINES
  390.  ;
  391.  
  392. print_string:         
  393.  pea        (a0)
  394.  move.w     #9, -(sp) 
  395.  trap       #1        
  396.  addq.l     #6, sp    
  397.  rts
  398.  
  399. print_newline:
  400.  lea        newline, a0
  401.  bsr.s      print_string
  402.  rts
  403.  
  404.  data
  405. newline:     dc.b $D,$A,0
  406. header_1:    dc.b '    During trap 11 invocation:  ',0
  407.  bss
  408.  align
  409. program_end: ds.l 0
  410.  end
  411.  
  412.  
  413. Program 43. This program prints the content of register A7 
  414. during various stages of execution.  Program 42 must be 
  415. executed prior to the execution of this program.
  416.  
  417.  ; Program Name: PRG_6BP.S
  418.  ;      Version: 1.002
  419.  
  420.  ; Assembly Instructions:
  421.  
  422.  ;     Assemble in PC-relative mode and save with a TOS extension.
  423.  
  424.  ; Execution Instructions:
  425.  
  426.  ;     Before this program is executed, trap 11 must be installed via the
  427.  ; execution of program PRG_6AP.TOS.  In addition, this program uses traps
  428.  ; installed by CUSTOM.PRG.  Execute from the desktop or via SPAWN.TTP.
  429.  
  430.  ; Program Function:
  431.  
  432.  ;     Prints the content of register A7 during various stages of execution
  433.  ; while the state of the processor is varied from user mode to supervisor
  434.  ; mode, from supervisor mode back to user mode, from user mode to supervisor
  435.  ; mode a second time and from supervisor mode back to user mode a second time.
  436.  
  437.  ;     In this program, custom trap #11 is used to toggle the state of the
  438.  ; processor.
  439.  
  440. calculate_program_size:
  441.  lea        -$102(pc), a1       ; Fetch basepage start address.
  442.  lea        program_end, a0     ; Fetch program end address.
  443.  trap       #6                  ; Return unused memory to op system.
  444.  
  445. print_heading:
  446.  lea        heading, a0
  447.  bsr.s      print_string
  448.  
  449.  ; NOTE: During this section of the program, no user stack has yet been
  450.  ;       assigned, therefore, the user stack address is that of the default
  451.  ;       user stack assigned by the system when execution commences.
  452.  
  453. print_address_of_default_user_stack:
  454.  lea        header_1, a0
  455.  bsr.s      print_string
  456.  move.l     a7, d1              ; Convert address to ASCII hexadecimal.
  457.  trap       #5
  458.  bsr.s      print_string
  459.  bsr.s      print_newline
  460.  
  461. invoke_custom_super_mode_trap:
  462.  trap       #11                 ; Sets bit 13 of SR to 1 and prints SSP.
  463.  
  464. print_current_SSP:              ; SSP = address of supervisor stack.
  465.  lea        header_2, a0        ; Did invocation of trap #11 alter the address
  466.  bsr.s      print_string        ; in the supervisor stack pointer?
  467.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  468.  trap       #5
  469.  bsr.s      print_string
  470.  bsr.s      print_newline
  471.  bsr.s      print_newline
  472.  
  473. enter_user_mode:
  474.  andi.w     #$DFFF, SR          ; Sets bit 13 of status register to zero.
  475.  
  476.  ; NOTE: During this section of the program, a user stack is assigned.  The
  477.  ;       user stack address is that of the label "stack".
  478.  
  479. print_address_of_assigned_user_stack:
  480.  lea        header_3, a0
  481.  bsr.s      print_string
  482.  lea        stack, a7
  483.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  484.  trap       #5
  485.  bsr.s      print_string
  486.  bsr.s      print_newline
  487.  
  488. invoke_custom_super_mode_trap_again:
  489.  trap       #11                 ; Sets bit 13 of SR to 1 and prints SSP.
  490.  
  491. print_current_SSP_again:        ; Did invocation of trap #11 alter the address
  492.  lea        header_4, a0        ; in the supervisor stack pointer?
  493.  bsr.s      print_string
  494.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  495.  trap       #5
  496.  bsr.s      print_string
  497.  bsr.s      print_newline
  498.  
  499. enter_user_mode_again:
  500.  andi.w     #$DFFF, SR          ; Sets bit 13 of status register to zero.
  501.  
  502. print_address_of_assigned_user_stack_again:
  503.  lea        header_5, a0
  504.  bsr.s      print_string
  505.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  506.  trap       #5
  507.  bsr.s      print_string
  508.  bsr.s      print_newline
  509.  
  510. terminate:
  511.  trap       #8
  512.  
  513.  ;
  514.  ; SUBROUTINES
  515.  ;
  516.  
  517. print_string:         
  518.  pea        (a0)
  519.  move.w     #9, -(sp) 
  520.  trap       #1        
  521.  addq.l     #6, sp    
  522.  rts
  523.  
  524. print_newline:
  525.  lea        newline, a0
  526.  bsr.s      print_string
  527.  rts
  528.  
  529.  data
  530. newline:   dc.b $D,$A,0
  531. heading:   dc.b 'PRG_6BP Execution Results => Content of A7:',$D,$A,$D,$A,0
  532. header_1:  dc.b   '  NO USER STACK ASSIGNED',$D,$A
  533.            dc.b   '    Start of program:          ',0
  534. header_2:  dc.b   '    After trap 11 invocation:   ',0
  535. header_3:  dc.b   '  USER STACK ASSIGNED',$D,$A
  536.            dc.b   '    After force to user mode:  ',0
  537. header_4:  dc.b   '    After trap 11 invocation:   ',0
  538. header_5:  dc.b   '    After force to user mode:  ',0
  539.  bss
  540.  align 
  541.                ds.l   96
  542. stack:         ds.l    0
  543. program_end:   ds.l    0
  544.  end
  545.  
  546.  
  547. PRG_6BP Execution Results => Content of A7:
  548.  
  549.   NO USER STACK ASSIGNED
  550.     Start of program:          F7FF8 = address of system assigned user stack 
  551.     During trap 11 invocation:  4DB2 = current content of SSP
  552.     After trap 11 invocation:   4DB8 = address of supervisor stack
  553.  
  554.   USER STACK ASSIGNED
  555.     After force to user mode:  1E254 = address of program assigned user stack
  556.     During trap 11 invocation:  4DB2 = current content of SSP
  557.     After trap 11 invocation:   4DB8 = address of supervisor stack
  558.     After force to user mode:  1E254 = address of user stack
  559.  
  560.  
  561.      Of special interest, in reviewing the data generated by 
  562. program PRG_6BP is the content of A7 after the first and 
  563. second trap 11 invocations.  In both cases, the invoking 
  564. program is executing in supervisor mode, therefore, the 
  565. supervisor stack pointer is in use.  Note, further, that the 
  566. supervisor stack is active.  Any stack activity generated by 
  567. the program will affect that stack, as will any stack 
  568. activity generated by the operating system, therefore, the 
  569. stack must be large enough to accommodate both.  If it is 
  570. not, then a system crash is virtually assured.  If extensive 
  571. program stack activity is to take place during this period, 
  572. the only way to insure that the active stack is large enough 
  573. is to load the address of a program assigned stack into 
  574. register A7.  On the other hand, if the program is to 
  575. generate little or no stack activity, then execution can 
  576. continue with the active supervisor stack.
  577.  
  578.   
  579. Program 44. This program also prints the content of register 
  580. A7 during various stages of execution, but it does not use 
  581. the custom trap installed by program 43.
  582.  
  583.  ; Program Name: PRG_6CP.S
  584.  ;      Version: 1.002
  585.  
  586.  ; Assembly Instructions:
  587.  
  588.  ;     Assemble in PC-relative mode and save with a TOS extension.
  589.  
  590.  ; Execution Instructions:
  591.  
  592.  ;     Execute from the desktop or from SPAWN.TTP.  This program uses traps
  593.  ; installed by CUSTOM.PRG.
  594.  
  595.  ; Program Function:
  596.  
  597.  ;     Prints the content of register A7 during various stages of execution
  598.  ; while the state of the processor is varied from user mode to supervisor
  599.  ; mode, from supervisor mode back to user mode, from user mode to supervisor
  600.  ; mode a second time and from supervisor mode back to user mode a second time.
  601.  
  602.  ;     In this program, GEMDOS function $20 is used to toggle the state of the
  603.  ; processor.  Refer to pages 117 and 118 of the Internals book, under the
  604.  ; $20 SUPER heading.  Aside from the fundamental methodology of function
  605.  ; invocation, two important items of information are discussed there.
  606.  
  607.  ;     1. GEMDOS $20 transfers the address of the user stack to the supervisor
  608.  ; stack pointer (SSP).  This means that while the processor is in the
  609.  ; supervisor state, if that state was forced by invocation of GEMDOS $20,
  610.  ; then the user stack is active and the supervisor stack is inactive.  This
  611.  ; means that the user stack must be large enough to accomodate supervisor
  612.  ; state activity.  Among other things, this activity includes the storage of
  613.  ; the program counter and status register during exception processing.
  614.  
  615.  ;     2. Invocation of GEMDOS $20 can corrupt, and usually does as far as I can
  616.  ; determine, registers A1 and D1.  Information in that section of the Internals
  617.  ; book indicates that only this GEMDOS function can alter the values in those
  618.  ; registers.
  619.  
  620. calculate_program_size:
  621.  lea        -$102(pc), a1       ; Fetch basepage start address.
  622.  lea        program_end, a0     ; Fetch program end address.
  623.  trap       #6                  ; Return unused memory to op system.
  624.  
  625. print_heading:
  626.  lea        heading, a0
  627.  bsr        print_string
  628.  
  629.  ; NOTE: During this section of the program, no user stack has yet been
  630.  ;       assigned, therefore, the user stack address is that of the default
  631.  ;       user stack assigned by the system when execution commences.
  632.  
  633. print_address_of_default_user_stack:
  634.  lea        header_1, a0
  635.  bsr        print_string
  636.  move.l     a7, d1              ; Convert address to ASCII hexadecimal.         
  637.  trap       #5
  638.  bsr        print_string
  639.  bsr        print_newline
  640.  
  641. invoke_gemdos_super_mode_function:
  642.  move.l     #0, -(sp)           ; The zero turns on supervisor mode.
  643.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  644.  trap       #1                  ; Supervisor stack pointer (SSP) returned in D0.
  645.  addq.l     #6, sp              ; SSP = address of supervisor stack.
  646.  movea.l    d0, a5              ; Save SSP in scratch register.
  647.  
  648. print_SSP_returned_by_GEMDOS_20:
  649.  lea        header_6, a0
  650.  bsr        print_string
  651.  move.l     a5, d1              ; Convert to ASCII hexadecimal.
  652.  trap       #5
  653.  bsr        print_string
  654.  bsr        print_newline
  655.  
  656. print_current_SSP:              ; Did GEMDOS $20 alter the address in the
  657.  lea        header_2, a0        ; supervisor stack pointer?
  658.  bsr        print_string
  659.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  660.  trap       #5
  661.  bsr.s      print_string
  662.  bsr        print_newline
  663.  bsr        print_newline
  664.  
  665. enter_user_mode:
  666.  pea        (a5)                ; Restore supervisor stack pointer.
  667.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  668.  trap       #1
  669.  addq.l     #6, sp
  670.  
  671.  ; NOTE: During this section of the program, a user stack is assigned.  The
  672.  ;       user stack address is that of the label "stack".
  673.  
  674. print_address_of_assigned_user_stack:
  675.  lea        header_3, a0
  676.  bsr.s      print_string
  677.  lea        stack, a7
  678.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  679.  trap       #5
  680.  bsr.s      print_string
  681.  bsr.s      print_newline
  682.  
  683. invoke_gemdos_function_again:
  684.  move.l     #0, -(sp)           ; The zero turns on supervisor mode.
  685.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  686.  trap       #1                  ; Supervisor stack pointer (SSP) returned in D0.
  687.  addq.l     #6, sp
  688.  movea.l    d0, a5              ; Save SSP in scratch register.
  689.  
  690. print_SSP_returned_by_GEMDOS_20_again:
  691.  lea        header_7, a0
  692.  bsr        print_string
  693.  move.l     a5, d1
  694.  trap       #5
  695.  bsr        print_string
  696.  bsr        print_newline
  697.  
  698. print_current_SSP_again:
  699.  lea        header_4, a0
  700.  bsr.s      print_string
  701.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  702.  trap       #5
  703.  bsr.s      print_string
  704.  bsr.s      print_newline
  705.  
  706. enter_user_mode_again:
  707.  pea        (a5)                ; Restore supervisor stack pointer.
  708.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  709.  trap       #1
  710.  addq.l     #6, sp
  711.  
  712. print_address_of_assigned_user_stack_again:
  713.  lea        header_5, a0
  714.  bsr.s      print_string
  715.  move.l     a7, d1              ; Convert to ASCII hexadecimal.
  716.  trap       #5
  717.  bsr.s      print_string
  718.  bsr.s      print_newline
  719.  
  720. terminate:
  721.  trap       #8
  722.  
  723.  ;
  724.  ; SUBROUTINES
  725.  ;
  726.  
  727. print_string:         
  728.  pea        (a0)
  729.  move.w     #9, -(sp) 
  730.  trap       #1        
  731.  addq.l     #6, sp    
  732.  rts
  733.  
  734. print_newline:
  735.  lea        newline, a0
  736.  bsr.s      print_string
  737.  rts
  738.  
  739.  data
  740. newline:   dc.b $D,$A,0
  741. heading:   dc.b 'PRG_6CP Execution Results => Content of A7:',$D,$A,$D,$A,0
  742. header_1:  dc.b   '  NO USER STACK ASSIGNED',$D,$A
  743.            dc.b   '    Start of program:             ',0
  744. header_2:  dc.b   '    After GEMDOS $20 invocation:  ',0
  745. header_6:  dc.b   '    SSP returned by GEMDOS $20:    ',0
  746. header_3:  dc.b   '  USER STACK ASSIGNED',$D,$A
  747.            dc.b   '    After force to user mode:     ',0
  748. header_4:  dc.b   '    After GEMDOS $20 invocation:  ',0
  749. header_7:  dc.b   '    SSP returned by GEMDOS $20:    ',0
  750. header_5:  dc.b   '    After force to user mode:     ',0
  751.  bss
  752.  align
  753.                ds.l   96
  754. stack:         ds.l    0
  755. program_end:   ds.l    0
  756.  end
  757.  
  758.  
  759. PRG_6CP Execution Results => Content of A7:
  760.  
  761.   NO USER STACK ASSIGNED
  762.     Start of program:             F7FF8 = address of system assigned user stack
  763.     SSP returned by GEMDOS $20:    4DB8 = address of supervisor stack
  764.     After GEMDOS $20 invocation:  F7FF8 = address of user stack
  765.  
  766.   USER STACK ASSIGNED
  767.     After force to user mode:     1E308 = address of program assigned user stack
  768.     SSP returned by GEMDOS $20:    4DB8 = address of supervisor stack
  769.     After GEMDOS $20 invocation:  1E308 = address of user stack
  770.     After force to user mode:     1E308 = address of user stack
  771.  
  772.  
  773.      The data generated by PRG_6CP illustrates the stack 
  774. address transfer effected by GEMDOS $20.  After the function 
  775. invocation, the program is executing in supervisor mode, but 
  776. the supervisor stack pointer does not contain the address of 
  777. the supervisor stack; it contains the address of the user 
  778. stack.  During this period, the supervisor stack is inactive 
  779. and the user stack is active.  Because the SSP no longer has 
  780. access to the supervisor stack, the address of that stack 
  781. must be restored to register A7 before the processor is 
  782. forced back into user mode, therefore, the value returned in 
  783. D0 by GEMDOS $20, which is that address, must be stored by 
  784. the invoking program.  The invoking program can force the 
  785. processor into user mode with GEMDOS $20, passing the 
  786. supervisor stack address as a parameter, or it may simple 
  787. store the address in register A7 and reset bit 13 of the 
  788. status register to zero.
  789.      Since the operating system provides a way to force the 
  790. processor into the supervisor mode, the question of why a 
  791. user defined function to accomplish the same task is 
  792. desirable is certainly relevant.  Well, a programmer may 
  793. decide to dispense with the overhead involved when the 
  794. GEMDOS $20 function is invoked.  Also, it is not actually 
  795. necessary to design a specific function to place the 
  796. processor in the supervisor state.  It enters that state 
  797. whenever an exception occurs.  One might choose to redirect 
  798. an exception handler so that, in addition to its normal 
  799. duties, it also returns processor control to the invoking 
  800. program in supervisor mode rather than in user mode.
  801.  
  802. Altering a System Variable
  803.   
  804.      The next program illustrates the manner in which a 
  805. system variable may be altered while the processor is in 
  806. supervisor mode.  The byte length variable to be altered is 
  807. composed of a group of flag bits, each of which controls one 
  808. function.  Therefore, the byte is composed of eight 
  809. variables, each of which is one bit long, compressed into 
  810. one byte of data.  The address of this byte is $484.  Only 
  811. the first four bits of the byte are assigned.  The bit 
  812. assignments given in the Internals book is correct; those 
  813. given in the Peel book are not.
  814.      The particular bit to be altered is bit #0, that which 
  815. controls the emission of a clicking sound when a key is 
  816. pressed on the keyboard.  I happen to find this noise 
  817. extremely disturbing, therefore, I was very upset when I 
  818. learned that the clicking sound emission was a default 
  819. attribute after power up.  I was even more distraught when I 
  820. discovered that the only way I could disable this function 
  821. was by altering a button in the window available via 
  822. CONTROL.ACC, a desk accessory, my version of which consumes 
  823. 20,080 bytes (decimal).
  824.      Program 45 is one of several that accomplish the same 
  825. goal.  Each of the programs may be executed from the 
  826. desktop, or they may be executed from the AUTO folder on a 
  827. boot disk.  The ability to execute programs of this type 
  828. from the AUTO folder is a powerful convenience, however, 
  829. there is a payment extracted in the form of the 
  830. inconvenience associated with the debugging stage of 
  831. programs that are being prepared for this type of service.  
  832. You should, if at all possible, confirm the bug-free 
  833. operation of programs before placing them in the AUTO 
  834. folder, just as you should verify the validity of desk 
  835. accessories before placing them into service.  We have very 
  836. little control over programs that are executed during the 
  837. initial stages of power up, therefore, if a malfunction 
  838. exists, the system may not be able to complete the boot 
  839. cycle because it will be stuck in a mode from which it can't 
  840. escape until the defective program (which can't be executed) 
  841. is executed.
  842.      Fortunately, there are programs available on public 
  843. domain and commercial disks that permit us to deselect desk 
  844. accessories and programs in AUTO folders during the very 
  845. first stages of the boot cycle.  From ST Informer, there is 
  846. DESKMNG2.ARC on their PDM disk 688, discussed in the June 
  847. issue of the newspaper.  From MichTron, there is STSELECT, 
  848. one of the programs on their STuff disk; this is the 
  849. selector that I use.  Using one of these utilities, when you 
  850. discover that your new program is obstructing the boot 
  851. cycle, you can deselect the offending program so that the 
  852. system will stop trying to execute it.
  853.   
  854. Program 45. Altering a System Variable.
  855.  
  856.  ; Program Name: PRG_6DP.S
  857.  ;      Version: 1.001
  858.  
  859.  ; Assembly Instructions:
  860.  
  861.  ;     Assemble in PC-relative mode and save with a PRG extension.
  862.  
  863.  ; Execution Instructions:
  864.  
  865.  ;     Execute from the desktop.
  866.  
  867.  ; Program Function:
  868.  
  869.  ; Turns off keyclick sound.  Refer to page 254 of the Internals book.  The
  870.  ; system variable at address $484 is a byte length variable.  The bits of
  871.  ; this variable have the meanings as indicated in the Internals book.  The
  872.  ; bit of interest is #0.  When this bit is a one, the computer emits a
  873.  ; click each time a key is pressed.  When the bit is a zero, these clicks
  874.  ; are not emitted.  A zero is placed in this bit by replacing the content
  875.  ; of the byte at $484 (which is 7 before the replacement, if key click is
  876.  ; enabled) with $6.
  877.  
  878.  ; If the object code for this program has a PRG extension, it may be placed
  879.  ; in the AUTO folder of a boot disk or hard disk partition so that it will
  880.  ; be executed during power up.  The reason for doing this would be to 
  881.  ; disable the key click without the presence of the desk accessory CONTROL,
  882.  ; thereby conserving the memory required by CONTROL.ACC.
  883.  
  884.  ; Of course, the program need not be executed at power up.  It may be
  885.  ; executed from the desktop whenever you wish to do so.
  886.  
  887.  ; Program Purpose:
  888.  
  889.  ; 1. Illustrate the use of GEMDOS function $20.
  890.  
  891.  ; 2. Illustrate the manner in which a system variable may be altered.
  892.  
  893.  ; 3. Show how the key click may be disabled without the presence of
  894.  ;    CONTROL.ACC as a desk accessory.
  895.  
  896.  ; 4. Show how to save a few lines of code by not repositioning the stack
  897.  ;    pointer after the first GEMDOS $20 call, then by using address register
  898.  ;    indirect with displacement addressing to store the SSP within the stack.
  899.  
  900. mainline:
  901.  lea        stack, a7           ; Point A7 to this program's stack.
  902.  
  903.  ; NOTE: For many programs, an extensive initialization routine is not
  904.  ;       necessary nor desirable.  Here, for instance, since this program
  905.  ;       will simply execute, remaining in memory for a very brief period,
  906.  ;       and since no other program will be executed in the mean time, there
  907.  ;       is no reason to calculate the program's size and return excess
  908.  ;       memory to the operating system.
  909.  
  910. enter_supervisor_mode:
  911.  move.l     #0, -(sp)           ; The zero turns on supervisor mode.
  912.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  913.  trap       #1                  ; Content of SSP returned in D0.
  914.  move.l     d0, 2(sp)           ; Store returned value in stack.
  915.                                 ; Do not reposition stack pointer.
  916.  
  917. disable_key_click:
  918.  move.b     #6, $484            ; Refer to page 254 of the Internals book.
  919.  
  920. return_to_user_mode:            ; Stack is already setup.
  921.  trap       #1                 
  922.  addq.l     #6, sp              ; Now reposition stack pointer to top.
  923.  
  924. terminate:
  925.  move.w     #0, -(sp)           ; Function = p_term_old = GEMDOS $0.
  926.  trap       #1                  ; GEMDOS call.
  927.  
  928.              ds.l    24         ; Stack.
  929. stack:       ds.l     0         ; Address of stack.
  930. program_end: ds.l     0
  931.  end
  932.  
  933.  
  934. Altering Variables With XBIOS Function $26
  935.   
  936.      Actually, as if anticipating the ST user's 
  937. unwillingness to be content to operate in the user mode, 
  938. Atari has provided us with a method of executing a 
  939. subroutine in supervisor mode without having to bother with 
  940. the transference thereto.  The extended bios function 
  941. (XBIOS) $26 (dec 38), called superx or superexec by name, 
  942. accepts the address of an executable block of code that is 
  943. terminated by the rts instruction, and the operating system 
  944. executes that block of code in the supervisor mode.  Program 
  945. 46 is an example which illustrates the correct use of the 
  946. superexec function.  The example given in the Internals book 
  947. is flawed (See page 203.).  The block of code used in the 
  948. example is not executable, and the single instruction 
  949. algorithm is not followed by the rts instruction.
  950.   
  951. Program 46. Execution in supervisor mode via XBIOS function 
  952. $26.  As always, the SSP uses the program's stack after the 
  953. switch to supervisor mode has been accomplished.
  954.  
  955.  ; Program Name: PRG_6EP.S
  956.  ;      Version: 1.001
  957.  
  958.  ; Assembly Instructions:
  959.  
  960.  ;     Assemble in PC-relative mode and save with a PRG extension.
  961.  
  962.  ; Execution Instructions:
  963.  
  964.  ;     Execute from the desktop.
  965.  
  966.  ; Program Function:
  967.  
  968.  ;     Turns off keyclick sound.  Refer to PRG_6DP.S for documentation.
  969.  
  970.  ; Program Purpose:
  971.  
  972.  ;   1. Illustrate the use of XBIOS function $26.  This function forces a
  973.  ;      subroutine to be executed in the supervisor mode.  The subroutine
  974.  ;      may be located at any address in memory.  Function $26 expects the
  975.  ;      address of the subroutine to be on the stack.
  976.  
  977.  ;   2. Point out error in Internals book (The example in the Internals
  978.  ;      book is garbage).  The "address" which must be pushed on the stack 
  979.  ;      is the address of a subroutine that is terminated with an RTS
  980.  ;      instruction.  See page 3-13 of the Peel book.
  981.  
  982.  ;   3. Illustrate the confusion created by writers of reference books when
  983.  ;      they are not consistent in their usage of number bases.  To wit:
  984.  ;      in the Internals book, GEMDOS function numbers are presented in
  985.  ;      hexadecimal.  The index of these functions are immediately followed
  986.  ;      by the BIOS and XBIOS functions, which are numbered in decimal.  I
  987.  ;      will try remember to use the following notation to express the BIOS
  988.  ;      and XBIOS in both number bases so that it will be easier for you to
  989.  ;      look them up in the Internals book:
  990.  
  991.  ;      Function = superexec = XBIOS $26 (dec 38).
  992.  
  993.  ;      Where (dec 38) indicates that the digits 38 are the decimal 
  994.  ;      equivalent of the hexadecimal number 26.
  995.  
  996. mainline:
  997.  lea        stack, a7           ; Point A7 to this program's stack.
  998.  
  999. execute_subroutine_in_supervisor_mode:
  1000.  pea        turn_key_click_off  ; Push address of subroutine onto stack.
  1001.  move.w     #$26, -(sp)         ; Function = superexec = XBIOS $26 (dec 38).
  1002.  trap       #14                 ; XBIOS call.
  1003.  addq.l     #6, sp
  1004.  
  1005. terminate:
  1006.  move.w     #0, -(sp)           ; Function = p_term_old = GEMDOS $0.
  1007.  trap       #1                  ; GEMDOS call.
  1008.  
  1009. turn_key_click_off:             ; Subroutine to be executed in supervisor
  1010.  move.b     #6, $484            ; mode.  
  1011.  rts
  1012.      
  1013.              ds.l    24         ; Stack.
  1014. stack:       ds.l     0         ; Address of stack.
  1015. program_end: ds.l     0
  1016.  end
  1017.  
  1018.  
  1019.      Of course, you need a program to toggle the keyclick 
  1020. function on so that you can verify the correctness of those 
  1021. that turn it off.  Program 47 will do that for you.  Just 
  1022. execute PRG_6FP.PRG from the desktop each time you want to 
  1023. active the keyclick function.
  1024.   
  1025. Program 47. This program turns on the keyclick sound.
  1026.  
  1027.  ; Program Name: PRG_6FP.S
  1028.  ;      Version: 1.001
  1029.  
  1030.  ; Assembly Instructions:
  1031.  
  1032.  ;     Assemble in PC-relative mode and save with a PRG extension.
  1033.  
  1034.  ; Execution Instructions:
  1035.  
  1036.  ;     Execute from the desktop.
  1037.  
  1038.  ; Program Function:
  1039.  
  1040.  ; Turns on keyclick sound so that the programs that turn it off can be
  1041.  ; tested.  See programs PRG_6DP and PRG_6EP for further documentation.
  1042.  
  1043. mainline:
  1044.  lea        stack, a7           ; Point A7 to this program's stack.
  1045.  
  1046. execute_subroutine_in_supervisor_mode:
  1047.  pea        turn_key_click_on   ; Push address of subroutine onto stack.
  1048.  move.w     #$26, -(sp)         ; Function = superexec = XBIOS $26 (dec 38).
  1049.  trap       #14                 ; XBIOS call.
  1050.  addq.l     #6, sp
  1051.  
  1052. terminate:
  1053.  move.w     #0, -(sp)           ; Function = p_term_old = GEMDOS $0.
  1054.  trap       #1                  ; GEMDOS call.
  1055.  
  1056. turn_key_click_on:              ; Subroutine to be executed in supervisor
  1057.  move.b     #7, $484            ; mode.
  1058.  rts
  1059.      
  1060.              ds.l    24         ; Stack.
  1061. stack:       ds.l     0         ; Address of stack.
  1062. program_end: ds.l     0
  1063.  end
  1064.  
  1065.   
  1066.      The error in the Internals book, concerning the 
  1067. construction of the subroutine, is not the only source of 
  1068. false information pertaining to the XBIOS function $26 
  1069. usage.  In another reference, the Peel book (page 3-13), a 
  1070. note appended to functions parameter data declares that the 
  1071. subroutine must not contain BIOS nor GEMDOS calls.  
  1072. Strangely enough, the same note is included in Atari's 
  1073. description of the function.  Program 48, the function of 
  1074. which is to dispel those rumors, is included, for your 
  1075. perusal, below.
  1076.  
  1077. Program 48. The impossible made trivial.
  1078.  
  1079.  ; Program Name: PRG_6GP.S
  1080.  ;      Version: 1.001
  1081.  
  1082.  ; Assembly Instructions:
  1083.  
  1084.  ;     Assemble in PC-relative mode and save with a TOS extension.
  1085.  
  1086.  ; Execution Instructions:
  1087.  
  1088.  ;     Execute from the desktop.
  1089.  
  1090.  ; Program Function:
  1091.  
  1092.  ; Within a subroutine addressed by XBIOS function $26, does the following:
  1093.  
  1094.  ;   1. Turns off keyclick sound.
  1095.  ;   2. Prints a string with GEMDOS function $9.
  1096.  ;   3. Prints a string with BIOS function $3.
  1097.  
  1098.  ; See programs PRG_6DP and PRG_6EP for further documentation.
  1099.  
  1100.  ; Program Purpose:
  1101.  
  1102.  ;   Illustrate the use of XBIOS function $26 to execute a subroutine in
  1103.  ;   the supervisor mode, even though the subroutine contains GEMDOS and
  1104.  ;   BIOS function calls.  This example shows that these function calls
  1105.  ;   can be executed within the subroutine, a contradiction to information
  1106.  ;   contained in some of the references (See page 3-13 of the Peel book,
  1107.  ;   for example.).
  1108.  
  1109. mainline:
  1110.  lea        stack, a7           ; Point A7 to this program's stack.
  1111.  
  1112. execute_subroutine_in_supervisor_mode:
  1113.  pea        subroutine          ; Push address of subroutine onto stack.
  1114.  move.w     #$26, -(sp)         ; Function = superexec = XBIOS $26 (dec 38).
  1115.  trap       #14                 ; XBIOS call.
  1116.  addq.l     #6, sp
  1117.  
  1118. terminate:
  1119.  move.w     #0, -(sp)           ; Function = p_term_old = GEMDOS $0.
  1120.  trap       #1                  ; GEMDOS call.
  1121.  
  1122. subroutine:         
  1123.  move.b     #6, $484
  1124.  pea        message             ; Push address of first string.
  1125.  move.w     #$9, -(sp)          ; GEMDOS function $9 = c_conws.
  1126.  trap       #1                  ; Print first string.
  1127.  addq.l     #6, sp
  1128.  lea        message_2, a3       ; Load address of second string.
  1129. print_string:
  1130.  move.b     (a3)+, d3
  1131.  beq.s      wait_for_keypress
  1132.  move.w     d3, -(sp)
  1133.  move.w     #2, -(sp)
  1134.  move.w     #3, -(sp)           ; BIOS function $3 = bconout.
  1135.  trap       #13
  1136.  addq.l     #6, sp
  1137.  bra.s      print_string        ; Branch until NULL detected.
  1138. wait_for_keypress: 
  1139.  move.w     #8, -(sp)           ; Function = c_necin = GEMDOS $8.
  1140.  trap       #1                  ; GEMDOS call.
  1141.  addq.l     #2, sp              ; Reposition stack pointer at top of stack.
  1142.  rts
  1143.  
  1144.  data
  1145. message:     dc.b 'This string printed with GEMDOS function $9.',$D,$A,0
  1146. message_2:   dc.b 'This string printed with BIOS function $3.',$D,$A,0
  1147.  align
  1148.  bss
  1149.              ds.l    48         ; Stack.  Must be large enough for system
  1150.                                 ; use when the switch to supervisor mode
  1151.                                 ; is accomplished by GEMDOS $26.
  1152. stack:       ds.l     0         
  1153. program_end: ds.l     0
  1154.  end
  1155.  
  1156.  
  1157. Execution Results:
  1158.  
  1159. This string printed with GEMDOS function $9.
  1160. This string printed with BIOS function $3.
  1161.  
  1162.  
  1163. An Unfortunate Operating System Bug
  1164.  
  1165.      While I was trying to prepare a shorter version of 
  1166. program 48, I discovered that the operating system corrupts 
  1167. the stack when it executes the bconout function (BIOS 
  1168. function #3).  Were it not for this, the three stack pushes 
  1169. required to print the first character of a string could be 
  1170. reduced to one for each subsequent invocation to print each 
  1171. of the other characters in the string.  Program 49 
  1172. illustrates the attempt to gain the advantage, and figure 
  1173. 8.1 shows how the bconout function corrupts the stack.
  1174.   
  1175. Program 49. Illustrates the presence of an unfortunate 
  1176. operating system bug.
  1177.  
  1178.  ; Program Name: PRG_6HP.S
  1179.  ;      Version: 1.001
  1180.  
  1181.  ; Assembly Instructions:
  1182.  
  1183.  ;     Assemble in PC-relative mode and save with a TOS extension.
  1184.  
  1185.  ; Execution Instructions:
  1186.  
  1187.  ;     Execute from the desktop.
  1188.  
  1189.  ; Program Function:
  1190.  
  1191.  ; A shorter version of PRG_6GP.S.  In addition this program provides a way
  1192.  ; to verify that the trap #13 system function corrupts the stack.
  1193.  
  1194.  ; Just before the trap #13 function is invoked to print a character, three
  1195.  ; words of data must be pushed onto the stack.  This having been done once,
  1196.  ; it should thenceforth be sufficient to simply move character data into the
  1197.  ; appropriate location within the stack to continue printing characters, as
  1198.  ; long as the stack pointer is not altered by the program.
  1199.  
  1200.  ; Unfortunately, the location at which the trap #13 function number is
  1201.  ; pushed or stored is corrupted by the trap when it is invoked, therefore,
  1202.  ; it is necessary to restore the function number each time, just before the
  1203.  ; trap is invoked again.  So we lose an advantage.
  1204.  
  1205.  ; But the device to which the character is printed is not changed, so we
  1206.  ; can take advantage of that to store it once, before we start printing 
  1207.  ; characters.
  1208.  
  1209.  ; Only after all characters are printed is the stack repositioned to the
  1210.  ; top of the stack.  In fact, in this program, I wait until after the
  1211.  ; wait_for_keypress algorithm has been executed before repositioning the
  1212.  ; stack pointer.
  1213.  
  1214. mainline:
  1215.  lea        stack, a7           ; Point A7 to this program's stack.
  1216.  
  1217. execute_subroutine_in_supervisor_mode:
  1218.  pea        subroutine          ; Push address of subroutine onto stack.
  1219.  move.w     #$26, -(sp)         ; Function = superexec = XBIOS $26 (dec 38).
  1220.  trap       #14                 ; XBIOS call.
  1221.  addq.l     #6, sp
  1222.  
  1223. terminate:
  1224.  move.w     #0, -(sp)           ; Function = p_term_old = GEMDOS $0.
  1225.  trap       #1                  ; GEMDOS call.
  1226.  
  1227. subroutine:         
  1228.  move.b     #6, $484            ; Turn keyclick off.
  1229.  pea        message             ; Push address of first string.
  1230.  move.w     #$9, -(sp)          ; GEMDOS function $9 = c_conws.
  1231.  trap       #1                  ; Print first string.
  1232.  addq.l     #6, sp
  1233.  lea        message_2, a3       ; Load address of second string.
  1234. SET_UP_STACK:                   ; DO THIS BEFORE CHARACTER PRINTING COMMENCES.
  1235.  MOVE.W     #0, -(SP)           ; MAKE ROOM FOR CHARACTER TO BE PRINTED.
  1236.  MOVE.W     #2, -(SP)           ; OUTPUT DEVICE = SCREEN.
  1237.  MOVE.W     #3, -(SP)           ; FUNCTION = BIOS #3.
  1238.  MOVE.W     #3, D4              ; BECAUSE TRAP #13 CORRUPTS STACK.
  1239. print_string:
  1240.  move.b     (a3)+, d3
  1241.  beq.s      wait_for_keypress
  1242.  move.w     d4, (sp)            ; Restore BIOS function number because
  1243.  move.w     d3, 4(sp)           ; Trap #13 corrupts stack.
  1244.  trap       #13
  1245.  bra.s      print_string        ; Branch until NULL detected.
  1246. wait_for_keypress: 
  1247.  move.w     #8, -(sp)           ; Function = c_necin = GEMDOS $8.
  1248.  trap       #1                  ; GEMDOS call.
  1249.  addq.l     #8, sp              ; Reposition stack pointer at top of stack.
  1250.  rts
  1251.  
  1252.  data
  1253. message:     dc.b 'This string printed with GEMDOS function $9.',$D,$A,0
  1254. message_2:   dc.b 'This string printed with BIOS function $3.',$D,$A,0
  1255.  align
  1256.  bss
  1257.              ds.l    48         ; Stack.  Must be large enough for system
  1258.                                 ; use when the switch to supervisor mode
  1259.                                 ; is accomplished by GEMDOS $26.
  1260. stack:       ds.l     0         
  1261. program_end: ds.l     0
  1262.  end
  1263.  
  1264.  
  1265. Figure 8.1. Stack corruption by the system's BCONOUT 
  1266. function.
  1267.  
  1268.  PRG_6HP stack before trap #13 invoked.
  1269.  
  1270. 06D68C 00030002         ORI.B #2,D3
  1271. 06D690 005400FC         ORI.W #$FC,(A4)
  1272.  
  1273.  PRG_6HP stack after trap #13 invoked.
  1274.  
  1275. 06D68C 0786             BCLR D3,D6     ; This word has been corrupted.
  1276. 06D68E 00020054         ORI.B #$54,D2
  1277.  
  1278.  
  1279. Exception Handler Vectors
  1280.   
  1281.      Exceptions are interrupts.  Exception handlers are 
  1282. nothing more than subroutines.  Vectors are pointers.  
  1283. Pointers are memory locations that contain addresses.  
  1284. Therefore, exception handler vectors are memory locations 
  1285. that contain addresses to subroutines.  The word pointer is 
  1286. used to describe these memory locations because it connotes 
  1287. the act of pointing to something.  Thence to the word vector 
  1288. is not as straightforward.
  1289.      As an engineer, I can understand why the originator of 
  1290. the association between vectors and memory locations that 
  1291. contain addresses decided to form the association.  When an 
  1292. engineer hears the word vector it connotes a straight line 
  1293. terminated with an arrowhead, similar to ->.  In ST 
  1294. vernacular, however, the association is not pure.  It is 
  1295. corrupted because some references apply the term vector to 
  1296. simple data.  For example, on page 254 of the Internals 
  1297. book, the data stored in memory location $484 is called an 
  1298. attribute vector.
  1299.      Although they may not always be thought of as such, the 
  1300. 68000 registers are also locations in memory.  When a 
  1301. register contains an address, it can be called a pointer or 
  1302. a vector.  If the address that is stored in a register is a 
  1303. memory location that also contains an address, then the 
  1304. content of the register is a pointer or a vector also.  Thus 
  1305. we have pointers to pointers, vectors to vectors, pointers 
  1306. to vectors, and etc.  When a system variable contains data, 
  1307. that variable is not a vector unless the data is an address.
  1308.      I have reduced the scope of our interest in exceptions 
  1309. and interrupts to the extent that I can adjust Motorola's 
  1310. use of the words in the Programmer's Reference Manual to 
  1311. this: exceptions are generated by the trap instruction; 
  1312. interrupts are generated by hardware that is external to the 
  1313. 68000.  Note, however, that because you and I both realize 
  1314. that the words exception and interrupt have identical 
  1315. meanings, I shall not have to worry about using the words 
  1316. interchangeably.  The only reason we need be concerned, at 
  1317. all, by interpretations in the meanings of the two words is 
  1318. this: trap instructions are constituents of an exception 
  1319. group which has a lower execution priority than does the 
  1320. exception group of which hardware interrupts are 
  1321. constituents.  In spite of this difference, however, both 
  1322. exception groups affect the supervisor stack in an identical 
  1323. manner--that's what we need to know.
  1324.  
  1325. The Details of Storing Data in Memory
  1326.   
  1327.      At this point I must confirm that your knowledge of the 
  1328. manner in which data is stored in a block of memory 
  1329. locations to which we refer as a stack matches ST reality.  
  1330. Consider this: computer memory is constructed from single 
  1331. element constituents called bits.  Now, the 68000 
  1332. microprocessor permits bit operations to be performed on 
  1333. data, however, when we store or retrieve data in ram, the 
  1334. smallest section of ram with which we may deal is eight 
  1335. bits, a grouping of bits for which we have a special name.  
  1336. We call this conglomerate a byte.
  1337.      The 68000 deals with two other conglomerates.  These 
  1338. are called words and longwords.  A word is composed of 16 
  1339. bits (two bytes), and a longword is composed of 32 bits 
  1340. (four bytes).  But, even though a grouping of 32 bits is 
  1341. recognized as a legitimate conglomerate by the 
  1342. microprocessor, as far as the interface between the 68000 
  1343. and ram is concerned, only byte size and word size 
  1344. operations are permissible.  The incongruency is resolved by 
  1345. separating a longword  operation that involves ram into two 
  1346. word operations.
  1347.      As you probably know, we can even manipulate a single 
  1348. bit in ram, as long as we restrict the area containing the 
  1349. bit to a particular byte.  That's what the Motorola manual 
  1350. means where it states that a bit operation is performed 
  1351. using the bit number, modulo 8, with zero referring to the 
  1352. least significant bit.  See the btst and bset instructions, 
  1353. for example.
  1354.      Now, considering the relationship between permissible 
  1355. operations and hardware configuration, in order that we be 
  1356. able to manipulate the data stored in ram, we must know 
  1357. precisely how it is stored, bit by bit.  Assume that you had 
  1358. the eight bits of data in the 8-bit register shown in figure 
  1359. 8.2.  Do you suppose that the microprocessor would place the 
  1360. data in ram according to method 1, where the data contained 
  1361. in each bit position of the ram byte is a replica of that 
  1362. contained in the register, or according to method 2, where 
  1363. the data in ram has been reversed so that what is in bit 
  1364. position seven of the register is in bit position zero, and 
  1365. so on, of the ram byte?
  1366.  
  1367. Figure 8.2. Storing the content of a register in ram.
  1368.  
  1369.  
  1370.  
  1371.  
  1372.  
  1373.  
  1374.  
  1375.  
  1376.  
  1377.  
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384.      I agree that method 1 seems to be the most logical, but 
  1385. suppose that an engineer found method 2 to be the least 
  1386. expensive, or the faster, or the more reliable.  Method 2 is 
  1387. at least a possible storage implementation.  Then, when we 
  1388. consider word size and longword size operations, the 
  1389. discussion can be extended to possibilities involving the 
  1390. placement of bytes rather than bits.  Will the highest byte 
  1391. of a register's word or longword content go into the highest 
  1392. byte of the ram word or longword, or will it go into the 
  1393. lowest?  We must know the answer to this question because, 
  1394. very soon, we will be involved in the manipulation of one 
  1395. bit of word of data that will be located within a stack.  
  1396. Figure 8.3 illustrates two possible implementations, 
  1397. involving the storage of a data word.
  1398.   
  1399. Figure 8.3. Some methods of storing a data word.
  1400.   
  1401.  
  1402.  
  1403.  
  1404.  
  1405.  
  1406.  
  1407.  
  1408.  
  1409.  
  1410.  
  1411.  
  1412.  
  1413.  
  1414.  
  1415.  
  1416.  
  1417.  
  1418.  
  1419.  
  1420.      Using the first method, the system would store the 
  1421. least significant byte of register content in the next 
  1422. available byte of ram, then it would store the most 
  1423. significant byte of register content in the following ram 
  1424. byte.  Using the second method, the system would store the 
  1425. most significant byte of register content, then it would 
  1426. store the least significant byte of register content.  Note 
  1427. that we assume each byte of data to be stored in ram at the 
  1428. next lowest addressable ram byte, at all times; that is 
  1429. sequentially from byte 0 to the highest available location.
  1430.      To eliminate the necessity for further speculation 
  1431. concerning the method of storage used in the ST, I have 
  1432. provided program 50, which is followed by three figures that 
  1433. illustrate the debugger output field for the entire program.  
  1434. These figures show precisely how the ST stores data in ram.  
  1435. Specifically, the example illustrates the manner in which a 
  1436. stack pointer is pre-decremented by the number of bytes 
  1437. necessary to provide the necessary ram space, and the manner 
  1438. in which the stored data occupies that space.
  1439.  
  1440.  
  1441. Program 50. Storing data in a stack.
  1442.  
  1443.  ; Program Name: PRG_6IR.S
  1444.  ;      Version: 1.001
  1445.  
  1446.  ; Function:
  1447.  
  1448.  ;    Illustrate the manner in which numbers are placed in the stack.
  1449.  
  1450.  ; Assembly Instruction:
  1451.  
  1452.  ;    Assemble in Relocatable mode, go to the debugger and click on the
  1453.  ; relocate button.
  1454.  
  1455.  ; Execution Instructions:
  1456.  
  1457.  ;    In the debugger, use the Single step button to execute only the
  1458.  ; instructions which load a stack address into register A7 and those which
  1459.  ; store values into the stack.  Move the PC cursor manually to jump over
  1460.  ; the stack space declarations.
  1461.  
  1462.  text
  1463. push_word_1:
  1464.  lea       stack_1, sp
  1465.  move.w    #5, -(sp)
  1466.            ds.l  1
  1467. stack_1:   ds.l  0
  1468.  
  1469.  text
  1470. push_longword_1:
  1471.  lea       stack_2, sp
  1472.  move.l    #5, -(sp)
  1473.            ds.l  2
  1474. stack_2:   ds.l  0
  1475.  
  1476.  text
  1477. push_word_2:
  1478.  lea       stack_3, sp
  1479.  move.w    #1770, -(sp)
  1480.            ds.l  1
  1481. stack_3:   ds.l  0
  1482.  
  1483.  text
  1484. push_longword_2:
  1485.  lea       stack_4, sp
  1486.  move.l    #1234567890, -(sp)
  1487.            ds.l  2
  1488. stack_4:   ds.l  0
  1489.  end
  1490.  
  1491.  
  1492.      In figure 8.4, you can see the assembled program.  The 
  1493. declared stack spaces, filled with zeroes, are highlighted 
  1494. by reversed video.  The very last line in the output field 
  1495. is not a part of the program.  Note that the Symbolic button 
  1496. has been clicked off, so that the entire program can fit 
  1497. within the field.
  1498.  
  1499.  
  1500. Figure 8.4. Debugger output field with PRG_6IR.PRG ready for 
  1501. execution.  Do not attempt to execute the statements which 
  1502. are declarations.
  1503.  
  1504.  
  1505.  
  1506.  
  1507.  
  1508.  
  1509.  
  1510.  
  1511.  
  1512.  
  1513.  
  1514.  
  1515.  
  1516.  
  1517.  
  1518.  
  1519.  
  1520.  
  1521.  
  1522.  
  1523.  
  1524.  
  1525.  
  1526.  
  1527.  
  1528.      The results of the execution are shown in figure 8.5.  
  1529. You can see that the ST stores the most significant byte of 
  1530. a data word in the least significant byte of a memory word.  
  1531. Then it stores the next data byte into the most significant 
  1532. memory byte.  For example, when it stored the data word 
  1533. 0005, it placed the 00 in memory location $069C3C and the 05 
  1534. in memory location $069C3D.  When it stored the data 
  1535. longword $499602D2, it placed the most significant byte, 
  1536. $49, in memory location $069C70.  Then it placed the next 
  1537. data byte, $96, into memory location $069C71.  From there, 
  1538. it placed decreasingly significant data bytes into 
  1539. increasingly significant memory bytes.
  1540.  
  1541.   
  1542. Figure 8.5. Debugger output field showing PRG_6IR.PRG after 
  1543. execution.
  1544.  
  1545.  
  1546.  
  1547.  
  1548.  
  1549.  
  1550.  
  1551.  
  1552.  
  1553.  
  1554.  
  1555.  
  1556.  
  1557.  
  1558.  
  1559.  
  1560.  
  1561.  
  1562.  
  1563.  
  1564.  
  1565.  
  1566.  
  1567.  
  1568.  
  1569.  
  1570.      Figure 8.6 is simply the normal disassembled display of 
  1571. the program.  It is included to give you another view of the 
  1572. data, as it appears in ram.  From the evidence presented in 
  1573. the three figures, we are able to compile the following 
  1574. assessment:
  1575.  
  1576.      1. Before the ST stores data in a stack, the stack 
  1577.         pointer is decremented by the number of bytes 
  1578.         required to contain the data.
  1579.  
  1580.      2. Then the data is stored, most significant byte 
  1581.         first, into the space in memory that is highlighted 
  1582.         by the decrementation.
  1583.  
  1584.      3. The content of the stack pointer is the address of 
  1585.         the most significant byte of the data.  This 
  1586.         knowledge is especially relevant whenever we want to 
  1587.         examine particular bits of the data.
  1588.  
  1589.         
  1590. Figure 8.6. PRG_6IR.PRG in normal disassembled format.
  1591.  
  1592.  
  1593.  
  1594.  
  1595.  
  1596.  
  1597.  
  1598.  
  1599.  
  1600.  
  1601.  
  1602.  
  1603.  
  1604.  
  1605.  
  1606.  
  1607.  
  1608.  
  1609.      Of course, I have a handy example with which to begin.  
  1610. During my study of various computer languages, I found that, 
  1611. although the bulk of the output produced by programs was 
  1612. directed to the video screen, there were times when I wanted 
  1613. that output to be replicated on my printer.  In addition, I 
  1614. often wanted a hardcopy of my keyboard inputs to a program.  
  1615. At first, I merely inserted the appropriate instructions in 
  1616. the program to direct the output as required.  There came a 
  1617. time, however, when I began to find the task of providing 
  1618. temporary printer output instructions in my programs to be 
  1619. tedious.
  1620.      After careful study, I realized that all operating 
  1621. system controlled output to the video screen and the printer 
  1622. had to eventually be processed by BIOS (trap #13) functions.  
  1623. Therefore, I decided to design a program that would trap 
  1624. screen directed output, and, if the printer was on, redirect 
  1625. that output to both the printer and the screen.  When it was 
  1626. completed, I used the program to provide hardcopy results at 
  1627. the end of program listings for myself and for programming 
  1628. class assignments.  Later, I found that it was also useful 
  1629. during compilations because it provided a printer copy of 
  1630. compilation errors.
  1631.  
  1632. Custom Trap Handlers
  1633.         
  1634.      Assume that the user stack is located within some block 
  1635. of memory and that the user stack pointer is pointing to 
  1636. (contains the address of) the top byte (the first 8 bits) of 
  1637. this stack.  We will call this first byte of the stack 
  1638. relative location 0.  We will refer to the second byte of 
  1639. the stack as relative location 1, the next as relative 
  1640. location 2, and so on.  Further, assume that the processor 
  1641. is in user mode.
  1642.      To generate a BIOS function call, the bconout function, 
  1643. for example, we push the character (or its code) to be 
  1644. printed onto the user stack.  Because we are stacking word 
  1645. data, the content of the user stack pointer (register A7) is 
  1646. pre-decremented by 2 (once for each byte of the word), then 
  1647. the pushed word (first byte equals zero, second byte equals 
  1648. the ASCII code for the character) will be stored at relative 
  1649. locations 0 and 1.  The USP now points to relative location 
  1650. 0, the new top of the user stack.
  1651.      Next, we push the code for the device which is to be 
  1652. the recipient of the character onto the stack.  Assume that 
  1653. the device is the screen, and the code is $2.  After the 
  1654. push, the character code is located at relative locations 2 
  1655. and 3, and the device code is located at relative locations 
  1656. 0 and 1.  As you can deduce, the stack pointer always points 
  1657. to relative location zero.  All other stack data is 
  1658. referenced by an offset value, which is, precisely, the 
  1659. location of the data within the stack, relative to the 
  1660. location of the data byte that is stored at the address 
  1661. contained in the stack pointer register.
  1662.      Now, we push the BIOS function code ($3) onto the 
  1663. stack.  The character is now at relative locations 4 and 5, 
  1664. the device code is at relative locations 2 and 3, and the 
  1665. function code is at relative locations 0 and 1.  We now 
  1666. invoke the BIOS call with the trap #13 instruction.
  1667.      When this instruction is executed, by definition, an 
  1668. exception is forced.  The exception is processed by the 
  1669. system in the following manner:
  1670.  
  1671.      l. An internal copy of the status register is 
  1672.         generated.
  1673.  
  1674.      2. The supervisor state is entered by setting (set to 
  1675.         1) the S bit of the status register.
  1676.  
  1677.      3. The trace state bit is reset (set to 0).
  1678.  
  1679.      4. The exception vector number is generated.
  1680.  
  1681.      5. The content of the program counter (PC) register, 
  1682.         which is the address of the next program instruction 
  1683.         to be executed, is pushed onto the supervisor stack 
  1684.         by the SSP.
  1685.  
  1686.      6. The saved copy of the status register is pushed onto 
  1687.         the supervisor stack by the SSP.
  1688.  
  1689.      7. Using the vector number, the address of the trap 
  1690.         handler is fetched and stored in the PC.
  1691.  
  1692.      8. Execution continues at the content of the PC.
  1693.  
  1694.      Now, suppose that we want to handle such an exception 
  1695. ourselves, preempting the system's responsibility.  Here is 
  1696. what must be accomplished:
  1697.  
  1698.      1. Our trap handler must be memory resident.
  1699.  
  1700.      2. The address of our trap handler must be that which 
  1701.         is loaded into the PC during the system's processing 
  1702.         of the exception.
  1703.      3. Our trap handler must execute the code necessary to 
  1704.         satisfy the expectations of the source that 
  1705.         generated the exception.
  1706.      4. Our trap handler must return control to the source 
  1707.         generating the exception, if that is the normal 
  1708.         expectation, otherwise it must relinquish control in 
  1709.         whatever manner it is expected to do so by the 
  1710.         exception generating source.
  1711.  
  1712.  
  1713. Program 51. Installing a custom trap handler.
  1714.   
  1715.  ; PROGRAM NAME: PRG_6JC.S
  1716.  ;      VERSION: 1.001
  1717.  
  1718.  ; Assembly Instructions:
  1719.  
  1720.  ;    Assemble in Relocatable mode and save with a TOS suffix.
  1721.  
  1722.  ; Execution Instructions:
  1723.  
  1724.  ;    Execute from the desktop.  Some versions of the ST operating system
  1725.  ; may require the printer to be turned on for proper operation of this
  1726.  ; trap handler.
  1727.  
  1728.  ; Program Function:
  1729.  
  1730.  ;    This program establishes itself in memory as a trap #13 handler.  While
  1731.  ; the program is resident, it intercepts all trap #13 calls.  If an
  1732.  ; intercepted call's function is $3, and if the device involved is the video
  1733.  ; screen, then the custom trap handler redirects the call to include output
  1734.  ; to the printer as well as to the video screen.  In this manner all text
  1735.  ; output to the screen, which is accomplished by BIOS function $3 calls
  1736.  ; (GEMDOS calls $2 and $9 are included because these functions rely on
  1737.  ; BIOS function $3.) is sent to both the printer and the video screen.
  1738.  
  1739.  ;    All redirected ASCII codes below $1B, except those for a carriage
  1740.  ; return and a linefeed, are filtered out of the data sent to the printer.
  1741.  ; This prevents certain ASCII codes that are suitable for the screen but
  1742.  ; which are undesirable for the printer from reaching the printer.
  1743.  
  1744.  ; MAJOR NOTE:
  1745.  
  1746.  ;    If this program is to be used when a software print buffer is to be
  1747.  ; simultaneously resident, then the software print buffer program MUST be
  1748.  ; executed first.  That is, the software print buffer must already be
  1749.  ; resident when this program is executed.
  1750.  
  1751.  ; Program Features:
  1752.  
  1753.  ; 1. Produces a hardcopy of program input and output that is sent to the  
  1754.  ;    screen.  This program eliminates the necessity of providing statements
  1755.  ;    in the program to accomplish that task.  It permits the printer listing
  1756.  ;    of a program to be followed by a printer listing of user/program
  1757.  ;    interaction.  Especially useful for providing the results of an 
  1758.  ;    execution for homework problems, or for programs under development.
  1759.  
  1760.  ; 2. When compiling, compiler output to the screen will be sent to the
  1761.  ;    printer.   Especially useful when debugging the error messages
  1762.  ;    that appear on the screen.  The printer output lets you go back to
  1763.  ;    the editor with the error list hardcopy.  
  1764.  
  1765.  ; 3. If the Show button is selected from the Show/Print/Cancel Desktop
  1766.  ;    dialog box, when this program is resident, the data which appears
  1767.  ;    on the video screen will also be sent to the printer.
  1768.  
  1769. program_start:                     ; Calculate program size and retain result.
  1770.  lea        program_end(pc), a3    ; Fetch program end address.
  1771.  movea.l    4(a7), a4              ; Fetch basepage address.
  1772.  suba.l     a4, a3                 ; Program size is in A3.
  1773.  lea        stack(pc), a7          ; Provide a user stack.
  1774.  
  1775. install_new_trap_13_vector:
  1776.  pea       custom_trap_handler(pc) ; Push new trap handler address onto stack.
  1777.  move.w    #$2D, -(sp)             ; Push trap 13 vector number.
  1778.  move.w    #5, -(sp)               ; Function = setexec = BIOS $5.
  1779.  trap      #13                     ; Current trap handler vector returned in D0.
  1780.  addq.l    #8, sp
  1781.  move.l    d0, preempted_handler_address
  1782.  
  1783. relinquish_processor_control:      ; Maintain memory residency.
  1784.  move.w    #0, -(sp)               ; See page 121 of Internals book.
  1785.  move.l    a3, -(sp)               ; Program size.
  1786.  move.w    #$31, -(sp)             ; Function = ptermres = GEMDOS $31.
  1787.  trap      #1
  1788.  
  1789.  ; NOTE:
  1790.  
  1791.  ;      The custom trap #13 handler is entered each time an application invokes
  1792.  ; the trap #13 call.  If the call does not involve printing a character to the
  1793.  ; screen, then a jump is performed to the preempted trap #13 handler.
  1794.  
  1795.  ;      If the call involves printing an escape sequence (An escape sequence
  1796.  ; is a two-character code, the first of which is $1B or 27 decimal.  Escape
  1797.  ; sequences provide screen control--see section 3.6 "The Atari VT52 Emulator",
  1798.  ; pages 245-249 of the Internals book.) for screen control, then the sequence
  1799.  ; is sent to the screen via the preempted trap #13 handler.
  1800.  
  1801.  ;      Otherwise, for each trap #13 call that is also a bconout invocation
  1802.  ; with device code #2, the custom_trap_13 routine is entered three times.
  1803.  ; The first time that the handler is entered, a variable is initialized, then,
  1804.  ; when it is entered subsequently, a jump, over the initialization sequence,
  1805.  ; to the preempted trap handler is performed.  The custom trap handler is
  1806.  ; entered three times because the custom handler prints to both the screen
  1807.  ; and the printer by invoking its own trap #13 calls; those calls are also
  1808.  ; intercepted by the custom handler.
  1809.  
  1810.  ;      The custom handler must be able to handle trap #13 calls made while
  1811.  ; the processor is in supervisor mode or user mode.
  1812.  
  1813. custom_trap_handler:
  1814.  tst.b     initialization_flag
  1815.  bne       skip_initialization
  1816.  
  1817.  ; Processing the stack data:
  1818.  
  1819.  ;      The location of the stack data that must be processed by this subroutine
  1820.  ; depends on the state of the processor when the exception occurs.  If it was
  1821.  ; in the supervisor state, then the data will be stacked, as indicated, at the
  1822.  ; following relative locations:
  1823.  
  1824.  ;                    location - 11 = character to be printed, byte length
  1825.  ; Old Top of Stack:  location - 10 = character to be printed, word length
  1826.  ;                    location -  8 = device to which character is to sent
  1827.  ;                    location -  6 = bios command to be executed
  1828.  ;                    location -  4 = program counter low word
  1829.  ;                    location -  2 = program counter high word
  1830.  ; SSP ->             location -  0 = invoking program's status register content
  1831.  
  1832.  ; In each case, above, the location listed is the location of the most
  1833.  ; significant byte of the data listed.  For example, relative location 10
  1834.  ; contains the most significant byte of the word which specifies the
  1835.  ; character to be printed.  Relative location 11 contains the least
  1836.  ; significant byte of that word.  Actually, relative location 11 is
  1837.  ; "the least significant byte" in computer vernacular, but, in fact, it
  1838.  ; is that byte which contains the character code.  The byte at relative
  1839.  ; location 10 contains only zeroes.
  1840.  
  1841.  ; The supervisor stack pointer (SSP) will be pointing to the new top of
  1842.  ; stack; the data there will be the content of the most significant byte
  1843.  ; of the status register, as it was when the exception occurred.  Now, as
  1844.  ; it turns out, this byte will be the one of significance, as far as the
  1845.  ; trap handler is concerned.
  1846.  
  1847.  ; If the processor was in the user state, then the data will be stacked,
  1848.  ; as indicated, at the following relative locations:
  1849.  
  1850.  ; Old Top of Stack:  location -  4 = character to be printed
  1851.  ;                    location -  2 = device to which character is to sent
  1852.  ; USP ->             location -  0 = bios command to be executed
  1853.  
  1854.  ; The user stack pointer (USP) will be pointing to the top of the stack;
  1855.  ; the data there will be the bios command to be executed.
  1856.  
  1857.  ; In order to process the stack data without regard to the processor state
  1858.  ; before invocation, if the processor was in user mode, then the offsets used
  1859.  ; to access the stack data with the USP as reference must be adjusted so that
  1860.  ; they match the offsets used to access the stack data with the SSP as
  1861.  ; reference.  Therefore, if the processor was in user mode when the trap #13
  1862.  ; call was made, the value six is subtracted from the register that is used
  1863.  ; to access the data.
  1864.  
  1865.  ; Then, common offset values, which will access the data correctly regardless
  1866.  ; of the stack pointer used as reference can be used.
  1867.  
  1868.  ; When we begin, we know that the processor is now is supervisor mode,
  1869.  ; however, we must determine its state at the time of the exception.  That
  1870.  ; processor state must be checked by testing bit 13 of the status register.
  1871.  ; We know that the SSP is now pointing to the content of the status register
  1872.  ; as it was at exception time.  In fact, it is pointing to the most significant
  1873.  ; byte of the status register word.
  1874.  
  1875.  ; There are two ways a bit can be tested with the 68000 BTST instruction.
  1876.  
  1877.  ; 1 - If the destination operand is a data register, then any of 32 bits
  1878.  ;     may be tested.
  1879.  
  1880.  ; 2 - If the destination operand is not a data register, then only 1 of
  1881.  ;     8 bits may be tested.
  1882.  
  1883.  ; In either case, the bit to be tested may be specified in a source data
  1884.  ; register or as immediate data.
  1885.  
  1886.  ; Since the bit we want to test is on the stack, we can only test a bit
  1887.  ; of a single memory byte.  The bit we must test (status register bit 13) is
  1888.  ; bit 5 of the byte that is being addressed by the SSP.
  1889.  
  1890. get_processor_status:
  1891.  movea.l   sp, a0               ; Fetch address of current top of stack.
  1892.  btst      #5, (sp)             ; Supervisor mode test.
  1893.  bne.s     supervisor_mode      ; No adjustment is necessary if the
  1894.                                 ; processor was in supervisor mode.
  1895.  move.l    usp, a0              ; Fetch address of current top of user stack.
  1896.  subq.l    #6, a0               ; Adjust user data access address.
  1897.  
  1898. user_mode:
  1899. supervisor_mode:                ; Processing for either mode follows.
  1900.  cmpi.w    #3, 6(a0)            ; Writing a character to a device?
  1901.  bne       not_bconout_call
  1902.  cmpi.w    #2, 8(a0)            ; Is device the screen?
  1903.  bne.s     not_screen
  1904.  
  1905.  ; NOTE:
  1906.  
  1907.  ;      The information desired, at this point, is the ON/OFF status of the
  1908.  ; printer.  My printer, the star NX-10 provides this information on pin 13
  1909.  ; of its parallel interface.  Unfortunately, the ST does not permit the
  1910.  ; utilization of this data.  This is an example of hardware/hardware
  1911.  ; incompatibility.
  1912.  
  1913.  ;      Because of this ST deficiency, the printers BUSY/NOT BUSY signal,
  1914.  ; on pin 11 of the interface is forced into double duty.  In addition to its
  1915.  ; normal function, this signal is used by the ST to determine if the printer
  1916.  ; is ON or OFF.
  1917.  
  1918.  ;      We would like to be able to direct output to the printer, at will,
  1919.  ; simply by manually turning the printer ON or OFF.
  1920.  
  1921.  ;      The problem here is this: because the response of the printer interface
  1922.  ; is so much slower than the video screen interface, we can't use the bcostat
  1923.  ; function (BIOS #8) to determine the printer status (before attempting to
  1924.  ; write to the printer) in between outputs to the screen.  When we attempt to
  1925.  ; do that, we find that pin 11 indicates that the printer is busy (same as OFF
  1926.  ; signal) most of the time, even when the printer is turned on.  Thus, the
  1927.  ; printer receives only some of the data sent to the screen.
  1928.  
  1929.  ;      Therefore, the trap handler must just assume that the printer is on.
  1930.  ; This is no problem with some versions of the ST operating system.  If the
  1931.  ; printer is off, nothing will be sent to it.  But some versions of the 
  1932.  ; operating system will wait (forever, it seems) for someone to turn on the
  1933.  ; printer.  If this happens, then you must turn on the printer while this
  1934.  ; trap handler is resident.
  1935.  
  1936. esc_sequence_test:
  1937.  tst.b     esc_sequence_flag
  1938.  bne.s     reset_esc_sequence_flag
  1939.  cmpi.w    #$1B, 10(a0)           
  1940.  bne.s     not_esc_sequence
  1941.  move.b    #1, esc_sequence_flag
  1942.  bra.s     use_preempted_handler
  1943. reset_esc_sequence_flag:
  1944.  move.b    #0, esc_sequence_flag
  1945. use_preempted_handler:
  1946.  movea.l   preempted_handler_address(pc), a0
  1947.  jmp       (a0)                 ; JUMP TO PREEMPTED TRAP #13 HANDLER.
  1948.  
  1949. not_esc_sequence:
  1950.  move.b    #1, initialization_flag
  1951.  move.w    10(a0), character    ; Store character for printer.
  1952. write_character_to_screen:
  1953.  move.w    10(a0), -(sp)        ; Push character onto stack.
  1954.  move.w    #2, -(sp)            ; Device = screen.
  1955.  move.w    #3, -(sp)            ; Function = bconout = BIOS $3.
  1956.  trap      #13
  1957.  addq.l    #6, sp
  1958.  
  1959. ascii_code_test:                ; Filter out undesirable codes.
  1960.  move.w    character(pc), d0
  1961.  cmpi.w    #$1B, d0
  1962.  bgt.s     write_character_to_printer
  1963.  cmpi.w    #$A, d0
  1964.  beq.s     write_character_to_printer
  1965.  cmpi.w    #$D, d0
  1966.  bne.s     undesirable_ascii
  1967. write_character_to_printer:
  1968.  move.w    d0, -(sp)            ; Push character onto stack.
  1969.  move.w    #0, -(sp)            ; Device = printer.
  1970.  move.w    #3, -(sp)
  1971.  trap      #13
  1972.  addq.l    #6, sp
  1973. undesirable_ascii:
  1974.  move.b    #0, initialization_flag
  1975.  rte
  1976.  
  1977. not_screen:
  1978. not_bconout_call:
  1979. skip_initialization:
  1980.  movea.l   preempted_handler_address(pc), a0
  1981.  jmp       (a0)                 ; JUMP TO PREEMPTED TRAP #13 HANDLER.
  1982.  
  1983.  bss
  1984. character:                  ds.w    1
  1985. preempted_handler_address:  ds.l    1
  1986. esc_sequence_flag:          ds.b    1
  1987. initialization_flag:        ds.b    1
  1988.  align
  1989.                             ds.l   48    ; Stack
  1990. stack:                      ds.l    1    ; Address of stack.
  1991. program_end:                ds.l    0
  1992.  end
  1993.  
  1994.  
  1995. Program 52. Another version of program 51.
  1996.   
  1997.  ; PROGRAM NAME: PRG_6KC.S
  1998.  ;      VERSION: 1.001
  1999.  
  2000.  ; The function of this program is identical to that of PRG_6JC.S.  I am
  2001.  ; including it because, in PRG_6JC's trap handler, I used an algorithm to
  2002.  ; access stack data which you are most likely to see in references.  In this
  2003.  ; program I show you another method, the method that I prefer. 
  2004.  
  2005. program_start:                  ; Compute program size and retain result.
  2006.  lea        program_end(pc), a3 ; Fetch program end address.
  2007.  movea.l    4(a7), a4           ; Fetch basepage address.
  2008.  suba.l     a4, a3              ; Yields size of memory that must remain
  2009.                                 ; resident.
  2010. load_stack_address:
  2011.  lea        stack(pc), a7
  2012.  
  2013. install_new_trap_13_vector:
  2014.  pea       custom_trap_handler(pc)
  2015.  move.w    #$2D, -(sp)          ; Trap 13 vector number.
  2016.  move.w    #5, -(sp)            ; Function = setexec = BIOS $5.
  2017.  trap      #13                  ; Current trap handler vector returned in D0.
  2018.  addq.l    #8, sp
  2019.  move.l    d0, preempted_handler_address
  2020.  
  2021. relinquish_processor_control:   ; Maintain memory residency.
  2022.  move.w    #0, -(sp)            ; See page 121 of Internals book.
  2023.  move.l    a3, -(sp)            ; Size of memory to remain resident.
  2024.  move.w    #$31, -(sp)          ; Function = ptermres = GEMDOS $31.
  2025.  trap      #1
  2026.  
  2027.  ; Here I illustrate another way to adjust one of the stack reference pointers
  2028.  ; so that common offset values may be used to access stack data.  In this
  2029.  ; program the value six is added to the register that is used to access the
  2030.  ; data if the processor was in supervisor mode before the invocation.
  2031.  
  2032. custom_trap_handler:
  2033.  tst.b     initialization_flag
  2034.  bne       skip_initialization
  2035.  move.l    usp, a0              ; Load address of current top of user stack.
  2036.  
  2037. get_processor_status:
  2038.  btst      #5, (sp)             ; User mode test.
  2039.  beq.s     was_user_mode        ; No adjustment is necessary if the
  2040.                                 ; processor was in user mode.
  2041.  movea.l   sp, a0               ; Load current top of supervisor stack.
  2042.  addq.l    #6, a0               ; Adjust SSP for user mode type data access.
  2043.  
  2044. was_supervisor_mode:
  2045. was_user_mode:                  ; Processing for either mode follows.
  2046.  cmpi.w    #3, (a0)             ; Writing a character to a device?
  2047.  bne.s     not_bconout_call
  2048.  cmpi.w    #2, 2(a0)            ; Is device the screen?
  2049.  bne.s     not_screen
  2050.  
  2051. esc_sequence_test:
  2052.  tst.b     esc_sequence_flag
  2053.  bne.s     reset_esc_sequence_flag
  2054.  cmpi.w    #$1B, 4(a0)           
  2055.  bne.s     not_esc_sequence
  2056.  move.b    #1, esc_sequence_flag
  2057.  bra.s     use_preempted_handler
  2058. reset_esc_sequence_flag:
  2059.  move.b    #0, esc_sequence_flag
  2060. use_preempted_handler:
  2061.  movea.l   preempted_handler_address(pc), a0
  2062.  jmp       (a0)                 ; JUMP TO PREEMPTED TRAP #13 HANDLER.
  2063.  
  2064. not_esc_sequence:
  2065.  move.b    #1, initialization_flag
  2066.  move.w    4(a0), character     ; Store character for printer.
  2067. write_character_to_screen:
  2068.  move.w    4(a0), -(sp)         ; Push character onto stack.
  2069.  move.w    #2, -(sp)            ; Device = screen.
  2070.  move.w    #3, -(sp)            ; Function = bconout = BIOS $3.
  2071.  trap      #13
  2072.  addq.l    #6, sp
  2073.  
  2074. ascii_code_test:                ; Filter out undesirable codes.
  2075.  move.w    character(pc), d0
  2076.  cmpi.w    #$1B, d0
  2077.  bgt.s     write_character_to_printer
  2078.  cmpi.w    #$A, d0
  2079.  beq.s     write_character_to_printer
  2080.  cmpi.w    #$D, d0
  2081.  bne.s     undesirable_ascii
  2082. write_character_to_printer:
  2083.  move.w    d0, -(sp)            ; Push character onto stack.
  2084.  move.w    #0, -(sp)            ; Device = printer.
  2085.  move.w    #3, -(sp)
  2086.  trap      #13
  2087.  addq.l    #6, sp
  2088. undesirable_ascii:
  2089.  move.b    #0, initialization_flag
  2090.  rte
  2091.  
  2092. not_screen:
  2093. not_bconout_call:
  2094. skip_initialization:
  2095.  movea.l   preempted_handler_address(pc), a0
  2096.  jmp       (a0)                 ; JUMP TO PREEMPTED TRAP #13 HANDLER
  2097.  
  2098.  bss
  2099. character:                  ds.w    1
  2100. preempted_handler_address:  ds.l    1
  2101. esc_sequence_flag:          ds.b    1
  2102. initialization_flag:        ds.b    1
  2103.  align
  2104.                             ds.l   48    ; Stack
  2105. stack:                      ds.l    1    ; Address of stack.
  2106. program_end:                ds.l    0
  2107.  end
  2108.  
  2109.  
  2110.      The intricacy of many exception handlers is greater 
  2111. than that of programs 51 and 52.  However, I want the 
  2112. example given to help you to realize that complexity is not 
  2113. an exception handler requirement.  Once you are able to 
  2114. handle even such rudimentary handlers, you will be able to 
  2115. force the computer to perform at your command.  In the next 
  2116. chapter, I will present a more powerful handler, after the 
  2117. discussions about some of the microprocessor's peripheral 
  2118. hardware.
  2119.      I will now introduce a group of programs that are 
  2120. designed to provide information which is indispensable to 
  2121. the effort required when designing efficient programs, 
  2122. especially programs that function as desk accessories and 
  2123. exception handlers.  Each program in this group reports 
  2124. alterations made to the contents of the 68000 data and 
  2125. address registers during an experiment.  While the results 
  2126. of each experiment can only be considered valid for the 
  2127. conditions imposed during the experiment, the methods used 
  2128. can be applied to any set of conditions that you may wish to 
  2129. impose on your own experiments.
  2130.  
  2131. Data and Address Register Corruption
  2132.   
  2133.      All applications which are executed on the Atari ST 
  2134. conduct most of their operations via the 68000 data and 
  2135. address registers.  When several applications are resident 
  2136. and sharing system resources, the contents of the registers 
  2137. that are being used by one application must not be corrupted 
  2138. by another beyond the expectations of the first application.  
  2139. Furthermore, when designing our applications, we must be 
  2140. cognizant of the extent to which system calls inflict 
  2141. register corruption, so that we can avoid the consequences 
  2142. of using contaminated registers.
  2143.      I have developed a naming pattern for a new group of 
  2144. programs, which is to be used in a series of register 
  2145. contamination experiments, that is slightly different than 
  2146. that which I have been using.  Each program in the group has 
  2147. the appellation REG_TSTn, where n is a digit.  Some of the 
  2148. experiments to be performed require the execution of more 
  2149. than one program, and at least one experiment requires the 
  2150. installation of a previously introduced program in the root 
  2151. directory of a specific hard disk partition.  If no hard 
  2152. disk is available, the path used in that experiment must be 
  2153. edited so that a new directory path is specified.
  2154.      This group of programs has been designed to provide the 
  2155. following information:
  2156.  
  2157.      1. The extent to which the registers being used 
  2158.         in a desk accessory are protected from corruption 
  2159.         by the operating system, and the 
  2160.         effect that such accessories have on the 
  2161.         registers of an executing application.
  2162.  
  2163.      2. The extent to which system exception handlers alter 
  2164.         the registers of an executing application.
  2165.  
  2166.      3. The extent to which the registers that are used in a 
  2167.         LSR program that is not a desk accessory are 
  2168.         protected from corruption by the operating 
  2169.         system, and the effect that such programs have on 
  2170.         the registers of an executing application.
  2171.  
  2172. In a later chapter, I will present similar program which 
  2173. determine the extent to which AES and VDI arrays are 
  2174. corrupted by AES and VDI calls.
  2175.  
  2176.   
  2177. Program 53. Experiment 1: System trap #13, BIOS 
  2178. function #$8 register contamination.
  2179.  
  2180.  ; PROGRAM NAME: REG_TST1.S
  2181.  ;      VERSION: 1.001
  2182.  
  2183.  ; Assembly Instructions:
  2184.  
  2185.  ;   Assemble in Relocatable mode and save assembled program with a TOS suffix.
  2186.  
  2187.  ; Program Function:
  2188.  
  2189.  ;   This program is one of a group that is used to determine the extent of
  2190.  ;   register alteration by exception handlers and to test the volatility
  2191.  ;   of desk accessory registers.
  2192.  
  2193.  ; EXPERIMENT 1:
  2194.  
  2195.  ;     This program will call a trap #13 handler.  Before the call, values
  2196.  ; that were stored in a register array while the program was being loaded
  2197.  ; into ram are placed in each register.
  2198.  
  2199.  ;     Immediately after the call, the content of each register is stored in
  2200.  ; a second register array.  Then the content of each register, as it was
  2201.  ; before the call, is printed.  Next, the content of the after_call array
  2202.  ; is compared to the content of the before-call array, and the values in
  2203.  ; the after-call array are printed; however, register values in the 
  2204.  ; after-call array which do not match the before-call value for each
  2205.  ; corresponding register are printed in reverse colors on the video screen.
  2206.  
  2207.  ;     In addition, if the trap #13 handler which redirects screen output to
  2208.  ; to both the printer and the screen is resident, and if the printer is on,
  2209.  ; then a hardcopy will be obtained, upon which the non-matching after_call
  2210.  ; values will be printed in boldface.  To place the redirecting trap #13
  2211.  ; handler in residence, execute PRG_6JC.TOS or PRG_6KC.TOS.
  2212.  
  2213.  ;     If you are going to execute this program with the printer on, the results
  2214.  ; you obtain will probably more closely match those I obtained if you turn on
  2215.  ; your printer before booting the ST and executing the redirecting handler
  2216.  ; mentioned above.
  2217.   
  2218. program_start:                  ; Calculate program size and retain result.
  2219.  lea        program_end(pc), a0
  2220.  movea.l    4(a7), a1          
  2221.  suba.l     a1, a0            
  2222.  
  2223. return_memory:                  ; Return unused memory to operating system.
  2224.  move.l     a0, -(sp)           ; Store total program length in stack.
  2225.  move.l     a1, -(sp)           ; Store basepage address in stack.
  2226.  move.w     d0, -(sp)           ; Dummy value, can be anything.
  2227.  move.w     #$4a, -(sp)         ; Function = m_shrink = GEMDOS $4A.
  2228.  trap       #1                  ; GEMDOS call.
  2229.  adda.l     #$C, sp             ; Reset stack pointer to top of stack.
  2230.  
  2231. place_values_in_registers:
  2232.  movem.l    registers_before(pc), d0-d7/a0-a6
  2233.  lea        stack(pc), a7
  2234.  move.l     a7, reg_A7_before
  2235.  
  2236. make_trap_call:                 ; Get and save printer status.
  2237.  move.w     #0, -(sp)           ; Device = printer.
  2238.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  2239.  trap       #13                 ; If printer is on, -1 will be returned
  2240.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  2241.  move.b     d0, printer_status
  2242.  
  2243. store_after_call_contents:
  2244.  movem.l    d0-d7/a0-a7, registers_after
  2245.  
  2246. print_before_call_contents:
  2247.  bsr        print_newline
  2248.  lea        experiment_head(pc), a0
  2249.  bsr        print_string
  2250.  lea        before_msg(pc), a0
  2251.  bsr        print_string
  2252.  lea        register_id(pc), a3
  2253.  lea        registers_before(pc), a4
  2254.  move.l     #15, d4
  2255. print_register:
  2256.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  2257.  bsr        bin_to_hex
  2258.  movea.l    a3, a0
  2259.  bsr        print_string        ; Print register number.
  2260.  add.l      #24, a3
  2261.  lea        hexadecimal(pc), a0
  2262.  bsr        print_string        ; Print register content.
  2263.  bsr        print_newline
  2264.  dbra       d4, print_register
  2265.  bsr        print_newline
  2266.  
  2267. _wait_for_keypress:
  2268.  move.w     #8, -(sp)
  2269.  trap       #1
  2270.  addq.l     #2, sp
  2271.  
  2272. print_after_call_contents:
  2273.  lea        after_msg(pc), a0      
  2274.  bsr        print_string
  2275.  lea        register_id(pc), a3
  2276.  lea        registers_before(pc), a4
  2277.  lea        registers_after(pc), a5
  2278.  move.l     #15, d4
  2279. compare_and_print:
  2280.  move.l     (a5)+, d3           ; Load after_call value.
  2281.  cmp.l      (a4)+, d3           ; Compare before_call to after_call.
  2282.  beq.s      no_change           ; Branch if no change occurred.
  2283.  tst.b      printer_status      ; Find out if printer is on or off.
  2284.  beq.s      printer_off
  2285.  bsr        bold_on             ; Turn on printer bold print.
  2286. printer_off:
  2287.  bsr        reverse_on          ; Turn on video reverse print.
  2288.  
  2289. no_change:
  2290.  bsr        bin_to_hex
  2291.  movea.l    a3, a0
  2292.  bsr        print_string   
  2293.  adda.l     #24, a3
  2294.  lea        hexadecimal(pc), a0
  2295.  bsr        print_string
  2296.  bsr        print_newline
  2297.  tst.b      printer_status      ; Find out if printer is on or off.
  2298.  beq.s      _printer_off
  2299.  bsr        bold_off            ; Turn printer bold print off.
  2300. _printer_off:
  2301.  bsr        reverse_off         ; Turn video reverse print off.
  2302.  dbra       d4, compare_and_print
  2303.  
  2304. wait_for_keypress:
  2305.  move.w     #8, -(sp)
  2306.  trap       #1
  2307.  addq.l     #2, sp
  2308.  
  2309. terminate:
  2310.  move.w     #0, -(sp)
  2311.  trap       #1
  2312.  
  2313.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  2314.  ; passed as a longword in register D3.  Beginning with the most significant
  2315.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  2316.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  2317.  ; buffer.
  2318.  
  2319. bin_to_hex:                     ; Converts a 32-bit binary number in D3 to
  2320.                                 ; ASCII hexadecimal.
  2321.  lea        hexadecimal(pc), a0 ; A0 is pointer to array "hexadecimal".
  2322.  lea        hex_table(pc), a1   ; A1 is pointer to array "hex_table".
  2323.  moveq      #7, d2              ; D2 is the loop counter.
  2324.  moveq      #0, d0              ; D0 is not zero.  That's what we have
  2325.                                 ; proven so it must be cleared before use.
  2326. discard_leading_zeroes: 
  2327.  rol.l      #4, d3              ; Rotate most significant nibble to the
  2328.                                 ; least significant nibble position.
  2329.  move.b     d3, d0              ; Copy least significant byte of D1 to D0.
  2330.  andi.b     #$F, d0             ; Mask out most significant nibble of D0.
  2331.  cmpi.b     #0, d0              ; Is it zero?
  2332.  bne.s      store_digit
  2333.  dbra       d2, discard_leading_zeroes
  2334.  move.b     0(a1,d0.w), (a0)+   ; If we get here, last nibble has been
  2335.                                 ; processed, so store digit in buffer.
  2336.  move.b     #0, (a0)            ; Terminate hexadecimal string with a null.
  2337.  rts
  2338.  
  2339. continue:
  2340.  rol.l      #4, d3              ; Rotate most significant nibble.
  2341.  move.b     d3, d0              ; Copy least significant byte of D1 to D0.
  2342.  andi.b     #$F, d0             ; Mask out most significant nibble of D0.
  2343. store_digit:
  2344.  move.b     0(a1,d0.w), (a0)+   ; Store ASCII hexadecimal digit in buffer.
  2345.  dbra       d2, continue        ; Continue looping until D2 = -1.
  2346.  move.b     #0, (a0)            ; Terminate hexadecimal string with a null.
  2347.  rts
  2348.  
  2349. print_string:                   ; Expects address of string to be in A0.
  2350.  move.l     a0, -(sp)           ; Push address of string onto stack.
  2351.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2352.  trap       #1                  ; GEMDOS call
  2353.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  2354.  rts
  2355.  
  2356. print_newline:                  ; Prints a carriage return and linefeed.
  2357.  pea        newline(pc)         ; Push address of string onto stack.
  2358.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2359.  trap       #1                  ; GEMDOS call
  2360.  addq.l     #6, sp
  2361.  rts
  2362.  
  2363. bold_on:
  2364.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  2365.  move.w     #0, -(sp)
  2366.  move.w     #3, -(sp)
  2367.  trap       #13
  2368.  addq.l     #6, sp
  2369.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  2370.  move.w     #0, -(sp)
  2371.  move.w     #3, -(sp)
  2372.  trap       #13
  2373.  addq.l     #6, sp
  2374.  rts
  2375.  
  2376. bold_off:
  2377.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  2378.  move.w     #0, -(sp)
  2379.  move.w     #3, -(sp)
  2380.  trap       #13
  2381.  addq.l     #6, sp
  2382.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  2383.  move.w     #0, -(sp)
  2384.  move.w     #3, -(sp)
  2385.  trap       #13
  2386.  addq.l     #6, sp
  2387.  rts
  2388.  
  2389. reverse_on:
  2390.  move.w     #$1B, -(sp)
  2391.  move.w     #2, -(sp)
  2392.  move.w     #3, -(sp)
  2393.  trap       #13
  2394.  addq.l     #6, sp
  2395.  move.w     #$70, -(sp)
  2396.  move.w     #2, -(sp)
  2397.  move.w     #3, -(sp)
  2398.  trap       #13
  2399.  addq.l     #6, sp
  2400.  rts
  2401.  
  2402. reverse_off:
  2403.  move.w     #$1B, -(sp)
  2404.  move.w     #2, -(sp)
  2405.  move.w     #3, -(sp)
  2406.  trap       #13
  2407.  addq.l     #6, sp
  2408.  move.w     #$71, -(sp)
  2409.  move.w     #2, -(sp)
  2410.  move.w     #3, -(sp)
  2411.  trap       #13
  2412.  addq.l     #6, sp
  2413.  rts
  2414.  
  2415.  data
  2416. hex_table:        dc.b  '0123456789ABCDEF'
  2417. newline:          dc.b  $D,$A,0
  2418. experiment_head:  dc.b  'EXPERIMENT 1: System Trap #13 Corruption',$D,$A
  2419.                   dc.b  '              Function = BIOS $8',$D,$A,$A,0
  2420. before_msg:       dc.b  '      Register contents before call',$D,$A,0
  2421. after_msg:        dc.b  '      Register contents after  call',$D,$A,0
  2422. printer_status:   dc.b  0
  2423. register_id:      dc.b  '          Data Reg 0: $',0
  2424.                   dc.b  '          Data Reg 1: $',0
  2425.                   dc.b  '          Data Reg 2: $',0
  2426.                   dc.b  '          Data Reg 3: $',0
  2427.                   dc.b  '          Data Reg 4: $',0
  2428.                   dc.b  '          Data Reg 5: $',0
  2429.                   dc.b  '          Data Reg 6: $',0
  2430.                   dc.b  '          Data Reg 7: $',0
  2431.                   dc.b  '          Add  Reg 0: $',0
  2432.                   dc.b  '          Add  Reg 1: $',0
  2433.                   dc.b  '          Add  Reg 2: $',0
  2434.                   dc.b  '          Add  Reg 3: $',0
  2435.                   dc.b  '          Add  Reg 4: $',0
  2436.                   dc.b  '          Add  Reg 5: $',0
  2437.                   dc.b  '          Add  Reg 6: $',0
  2438.                   dc.b  '          Add  Reg 7: $',0
  2439.  align
  2440.  
  2441.  ;bss MAJOR NOTE: Do not use the bss assembler op here.  If you do, the
  2442.  ;                value $44415441 will NOT be loaded into the elements
  2443.  ;                of the registers_before array.
  2444.  
  2445. trap_vector:      ds.l   1     ; Store for custom trap handler's vector.
  2446. hexadecimal:      ds.l   3     ; Output buffer.  Must be NULL terminated.
  2447. registers_before: ds.l  15, $44415441
  2448. reg_A7_before:    ds.l   1
  2449. registers_after:  ds.l  16     ; Array for register contents after call.
  2450.                   ds.l  48     ; Program stack.
  2451. stack:            ds.l   1     ; Address of program stack.
  2452. program_end:      ds.l   0
  2453.  end
  2454.  
  2455.  
  2456. Figure 8.7. Program 53 Execution Results.
  2457.  
  2458.       Figure 8.7A. Register Contents before call.
  2459.       
  2460.  
  2461.  
  2462.  
  2463.  
  2464.  
  2465.  
  2466.  
  2467.  
  2468.  
  2469.  
  2470.  
  2471.  
  2472.  
  2473.  
  2474.  
  2475.  
  2476.  
  2477.  
  2478.  
  2479.  
  2480.  
  2481.  
  2482.  
  2483.  
  2484.  
  2485.  
  2486.  
  2487.  
  2488.       Figure 8.7B. Register contents after call.
  2489.       
  2490.  
  2491.  
  2492.  
  2493.  
  2494.  
  2495.  
  2496.  
  2497.  
  2498.  
  2499.  
  2500.  
  2501.  
  2502.  
  2503.  
  2504.  
  2505.  
  2506.  
  2507.  
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.  
  2516.  
  2517.  
  2518.      A register preservation guide can be found on page 3-2 
  2519. of the Peel book.  There, it implies that registers d0-
  2520. d2/a0-a2 are subject to contamination during the execution 
  2521. of trap #13 (BIOS) calls.  This guide does not provide any 
  2522. suggestion concerning possible function differentiations.  
  2523. From the information given, we can only assume that all BIOS 
  2524. functions corrupt with equal impudence.  Page 152 of the 
  2525. Internals book (under the heading 3.2 The BIOS Functions) 
  2526. provides the same information.
  2527.      The results of our first experiment, however, indicate 
  2528. that only the registers d0/a0-a1 are contaminated during a 
  2529. BIOS $8 (bconout) call.  But there is one other parameter 
  2530. imposed on the experiment.  The device specified for the 
  2531. function call was the printer.  The results might not be the 
  2532. same if another device were specified.  Yet it is not my 
  2533. intent to explore the results of every conceivable system 
  2534. call.  That, itself, would be material for a book.  It is 
  2535. the method of obtaining experimental results and the 
  2536. contradiction between reference material and experimental 
  2537. evidence that I wish to bring to your attention.
  2538.      I prepared program 53 in as straight forward a manner 
  2539. as possible, in order to alleviate your comprehension of the 
  2540. algorithms involved.  The program is rather long, when 
  2541. compared to its accomplishments, and the manner in which the 
  2542. results are displayed is not harmonious.  Therefore, the 
  2543. rest of the programs in the group are shorter, referring to 
  2544. program 23 when necessary, and they present their 
  2545. information in a more compact display.  Program 54 provides 
  2546. the same data as does program 53.
  2547.  
  2548.   
  2549. Program 54. Experiment 1: System trap #13, BIOS function #$8 
  2550. register contamination.
  2551.  
  2552.  ; PROGRAM NAME: REG_TST2.S
  2553.  ;      VERSION: 1.001
  2554.  
  2555.  ; Assembly Instructions:
  2556.  
  2557.  ;   Assemble in Relocatable mode and save the assembled program with a TOS suffix.
  2558.  
  2559.  ; Program Function:
  2560.  
  2561.  ;   Same as REG_TST1, except this one is designed to print the before and
  2562.  ;   after values side by side.  Also, the leading zero portion has been
  2563.  ;   removed from the binary to hexadecimal conversion routine, so that
  2564.  ;   numerical alignment is achieved on the output.
  2565.  
  2566. program_start:                  ; Calculate program size and retain result.
  2567.  lea        program_end(pc), a0
  2568.  movea.l    4(a7), a1          
  2569.  suba.l     a1, a0             
  2570.  
  2571. return_memory:                  ; Return unused memory to operating system.
  2572.  move.l     a0, -(sp)           ; Store total program length in stack.
  2573.  move.l     a1, -(sp)           ; Store basepage address in stack.
  2574.  move.w     d0, -(sp)           ; Dummy value, can be anything.
  2575.  move.w     #$4a, -(sp)         ; Function = m_shrink = GEMDOS $4A.
  2576.  trap       #1                  ; GEMDOS call.
  2577.  adda.l     #$C, sp             ; Reset stack pointer to top of stack.
  2578.  
  2579. place_values_in_registers:
  2580.  movem.l    registers_before(pc), d0-d7/a0-a6
  2581.  lea        stack(pc), a7
  2582.  move.l     a7, reg_A7_before
  2583.  
  2584. make_trap_call:                 ; Get and save printer status.
  2585.  move.w     #0, -(sp)           ; Device = printer.
  2586.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  2587.  trap       #13                 ; If printer is on, -1 will be returned
  2588.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  2589.  move.b     d0, printer_status
  2590.  
  2591. store_after_call_contents:
  2592.  movem.l    d0-d7/a0-a7, registers_after
  2593.  
  2594.  ; The following algorithm prints the before value for a register, then,
  2595.  ; on the same line, it prints the after value.  This provides a more
  2596.  ; compact display, and one in which it is easier to compare the before
  2597.  ; and after values.
  2598.  
  2599. print_all_contents:
  2600.  bsr        print_newline
  2601.  lea        experiment_head(pc), a0
  2602.  bsr        print_string
  2603.  lea        heading(pc), a0
  2604.  bsr        print_string
  2605.  lea        register_id(pc), a3
  2606.  lea        registers_before(pc), a4
  2607.  lea        registers_after(pc), a5
  2608.  move.l     #15, d4
  2609. print_register:
  2610.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  2611.  move.l     d3, d5              ; Save for comparison with after-value.
  2612.  bsr        bin_to_hex
  2613.  movea.l    a3, a0
  2614.  bsr        print_string        ; Print register number.
  2615.  add.l      #16, a3
  2616.  lea        hexadecimal(pc), a0
  2617.  bsr        print_string        ; Print before-call register content.
  2618.  lea        space(pc), a0
  2619.  bsr        print_string
  2620. compare_and_print:
  2621.  move.l     (a5)+, d3           ; Load after_call value.
  2622.  cmp.l      d5, d3              ; Compare before_call to after_call.
  2623.  beq.s      no_change           ; Branch if no change occurred.
  2624.  tst.b      printer_status      ; Find out if printer is on or off.
  2625.  beq.s      printer_off
  2626.  bsr        bold_on             ; Turn on printer bold print.
  2627. printer_off:
  2628.  bsr        reverse_on          ; Turn on video reverse print.
  2629.  
  2630. no_change:
  2631.  bsr        bin_to_hex
  2632.  bsr        print_hex_sign
  2633.  lea        hexadecimal(pc), a0
  2634.  bsr        print_string        ; Print after-call value.
  2635.  bsr        print_newline
  2636.  tst.b      printer_status      ; Find out if printer is on or off.
  2637.  beq.s      _printer_off
  2638.  bsr        bold_off            ; Turn printer bold print off.
  2639. _printer_off:
  2640.  bsr        reverse_off         ; Turn video reverse print off.
  2641.  dbra       d4, print_register
  2642.  bsr        print_newline
  2643.  
  2644. wait_for_keypress:
  2645.  move.w     #8, -(sp)
  2646.  trap       #1
  2647.  addq.l     #2, sp
  2648.  
  2649. terminate:
  2650.  move.w     #0, -(sp)
  2651.  trap       #1
  2652.  
  2653.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  2654.  ; passed as a longword in register D3.  Beginning with the most significant
  2655.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  2656.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  2657.  ; buffer.  The leading zero portion has been removed from this routine.
  2658.  
  2659. bin_to_hex:                      ; Converts a 32-bit binary number in D3 to
  2660.                                  ; ASCII hexadecimal.
  2661.  lea        hexadecimal(pc), a0  ; A0 is pointer to array "hexadecimal".
  2662.  lea        hex_table(pc), a1    ; A1 is pointer to array "hex_table".
  2663.  moveq      #7, d2               ; D2 is the loop counter.
  2664.  moveq      #0, d0               ; D0 is not zero.  That's what we have
  2665.                                  ; proved, so it must be cleared before use.
  2666. rotate_and_convert:
  2667.  rol.l      #4, d3               ; Rotate most significant nibble to the
  2668.                                  ; least significant nibble position.
  2669.  move.b     d3, d0               ; Copy least significant byte of D1 to D0.
  2670.  andi.b     #$F, d0              ; Mask out most significant nibble of D0.
  2671. store_digit:
  2672.  move.b     0(a1,d0.w), (a0)+    ; Store ASCII hexadecimal digit in buffer.
  2673.  dbra       d2, rotate_and_convert
  2674.  move.b     #0, (a0)             ; Terminate hexadecimal string with a null.
  2675.  rts
  2676.  
  2677. print_string:                   ; Expects address of string to be in A0.
  2678.  move.l     a0, -(sp)           ; Push address of string onto stack.
  2679.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2680.  trap       #1                  ; GEMDOS call
  2681.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  2682.  rts
  2683.  
  2684. print_newline:                  ; Prints a carriage return and linefeed.
  2685.  pea        newline(pc)         ; Push address of string onto stack.
  2686.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2687.  trap       #1                  ; GEMDOS call
  2688.  addq.l     #6, sp
  2689.  rts
  2690.  
  2691. print_hex_sign:
  2692.  move.w     #$24, -(sp)
  2693.  move.w     #2, -(sp)
  2694.  move.w     #3, -(sp)
  2695.  trap       #13
  2696.  addq.l     #6, sp
  2697.  rts
  2698.  
  2699. bold_on:
  2700.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  2701.  move.w     #0, -(sp)
  2702.  move.w     #3, -(sp)
  2703.  trap       #13
  2704.  addq.l     #6, sp
  2705.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  2706.  move.w     #0, -(sp)
  2707.  move.w     #3, -(sp)
  2708.  trap       #13
  2709.  addq.l     #6, sp
  2710.  rts
  2711.  
  2712. bold_off:
  2713.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  2714.  move.w     #0, -(sp)
  2715.  move.w     #3, -(sp)
  2716.  trap       #13
  2717.  addq.l     #6, sp
  2718.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  2719.  move.w     #0, -(sp)
  2720.  move.w     #3, -(sp)
  2721.  trap       #13
  2722.  addq.l     #6, sp
  2723.  rts
  2724.  
  2725. reverse_on:
  2726.  move.w     #$1B, -(sp)
  2727.  move.w     #2, -(sp)
  2728.  move.w     #3, -(sp)
  2729.  trap       #13
  2730.  addq.l     #6, sp
  2731.  move.w     #$70, -(sp)
  2732.  move.w     #2, -(sp)
  2733.  move.w     #3, -(sp)
  2734.  trap       #13
  2735.  addq.l     #6, sp
  2736.  rts
  2737.  
  2738. reverse_off:
  2739.  move.w     #$1B, -(sp)
  2740.  move.w     #2, -(sp)
  2741.  move.w     #3, -(sp)
  2742.  trap       #13
  2743.  addq.l     #6, sp
  2744.  move.w     #$71, -(sp)
  2745.  move.w     #2, -(sp)
  2746.  move.w     #3, -(sp)
  2747.  trap       #13
  2748.  addq.l     #6, sp
  2749.  rts
  2750.  
  2751.  data
  2752. hex_table:        dc.b '0123456789ABCDEF'
  2753. newline:          dc.b $D,$A,0
  2754. experiment_head:  dc.b 'EXPERIMENT 1: System Trap #13 Corruption',$D,$A
  2755.                   dc.b '              Function = BIOS $8',$D,$A,$A,0
  2756. heading:          dc.b '       Before Call         After  Call',$D,$A,0
  2757. space:            dc.b '     ',0
  2758. printer_status:   dc.b 0
  2759. register_id:      dc.b '  Data Reg 0: $',0,'  Data Reg 1: $',0
  2760.                   dc.b '  Data Reg 2: $',0,'  Data Reg 3: $',0
  2761.                   dc.b '  Data Reg 4: $',0,'  Data Reg 5: $',0
  2762.                   dc.b '  Data Reg 6: $',0,'  Data Reg 7: $',0
  2763.                   dc.b '  Add  Reg 0: $',0,'  Add  Reg 1: $',0
  2764.                   dc.b '  Add  Reg 2: $',0,'  Add  Reg 3: $',0
  2765.                   dc.b '  Add  Reg 4: $',0,'  Add  Reg 5: $',0
  2766.                   dc.b '  Add  Reg 6: $',0,'  Add  Reg 7: $',0
  2767.  align
  2768.  
  2769.  ;bss MAJOR NOTE: Do not use the bss assembler op here.  If you do, the
  2770.  ;                value $44415441 will NOT be loaded into the elements
  2771.  ;                of the registers_before array.
  2772.  
  2773. trap_vector:      ds.l    1     ; Store for custom trap handler's vector.
  2774. hexadecimal:      ds.l    3     ; Output buffer.  Must be NULL terminated.
  2775. registers_before: ds.l   15, $44415441 ; Store this value in each before
  2776. reg_A7_before:    ds.l    1            ; array element during loading.
  2777. registers_after:  ds.l   16     ; Array for register contents after call.
  2778.                   ds.l   48     ; Program stack.
  2779. stack:            ds.l    1     ; Address of program stack.
  2780. program_end:      ds.l    0
  2781.  end
  2782.  
  2783.  
  2784. Figure 8.8. Program 54 Execution Results.  
  2785.   
  2786.  
  2787.  
  2788.  
  2789.  
  2790.  
  2791.  
  2792.  
  2793.  
  2794.  
  2795.  
  2796.  
  2797.  
  2798.  
  2799.  
  2800.  
  2801.  
  2802.  
  2803.  
  2804.  
  2805.  
  2806.  
  2807.  
  2808.  
  2809.  
  2810.  
  2811.  
  2812.  
  2813.  
  2814.  
  2815. Program 55. Experiment 2: System trap #1, GEMDOS
  2816. function #$4B register contamination.  
  2817.  
  2818.  ; PROGRAM NAME: REG_TST3.S
  2819.  ;      VERSION: 1.001
  2820.  
  2821.  ; Assembly Instructions:
  2822.  
  2823.  ;   Assemble in Relocatable mode.  Save the assembled program with a TOS suffix.
  2824.  
  2825.  ; Program Function:
  2826.  
  2827.  ;   Similar to REG_TST2, except this one calls a system trap #1 handler.
  2828.  
  2829.  ; MAJOR NOTES:
  2830.  
  2831.  ;     1. PRG_6KC.TOS must be in the root directory of partition D of your
  2832.  ;        hard disk, if you are to execute the program as prepared.  But
  2833.  ;        you can alter the variable "program_name" to suit your
  2834.  ;        requirements.
  2835.  
  2836.  ;     2. Since this program loads and executes PRG_6KC.TOS, which installs
  2837.  ;        a custom trap #13 handler, you should execute this program only
  2838.  ;        once, otherwise each letter sent to the video screen will be
  2839.  ;        printed twice on the printer.
  2840.  
  2841.  ;     3. PRG_6KC.TOS functions best when the printer is on line during
  2842.  ;        system boot.
  2843.  
  2844.  ;     4. After executing this program, you should reset your system,
  2845.  ;        preferably with a cold start, to clear the custom trap handler
  2846.  ;        from RAM, unless you want it there for other purposes.
  2847.  
  2848.  ;     5. Do not execute this program from within AssemPro.
  2849.  
  2850.  ; EXPERIMENT 2:
  2851.  
  2852.  ;     This program will call a system trap #1 handler.  Since some
  2853.  ; trap #1 handlers call trap #13 handlers, as with the previous program,
  2854.  ; we specify a handler which is not processed by the custom trap handler
  2855.  ; of PRG_6KC.TOS, which may be resident.  We do this because we want to
  2856.  ; avoid any possible complications imposed by a custom trap handler at
  2857.  ; this time.
  2858.  
  2859.  ;     GEMDOS function $4B (aka p_exec) was chosen for the experiment.  
  2860.  ; This function is discussed in the Internals book.  Mode 0 is used in
  2861.  ; this program to cause the system to load and execute PRG_6KC.TOS. 
  2862.  
  2863.  ;     A couple of warnings: you would not want to execute this program if
  2864.  ; PRG_6KC.TOS were already resident.  That program contains no provisions
  2865.  ; for determining its residency, therefore, it is possible for you to
  2866.  ; execute it repeatedly, thereby obtaining multiple copies of it in ram.
  2867.  
  2868.  ;     When using GEMDOS function $4B to execute programs from within an
  2869.  ; executing application, you must set up both of the programs so that 
  2870.  ; neither of them consumes all ram.  That is, you must use one of
  2871.  ; the functions which releases excess memory back to the operating system.
  2872.  
  2873.  ;     Note that this program uses GEMDOS function $4A to return unused
  2874.  ; memory to the operating system, while PRG_6KC.TOS uses GEMDOS function
  2875.  ; $31.  In addition, of course, both programs require a legitimate
  2876.  ; termination algorithm.  That is, they must relinquish processor control.
  2877.   
  2878. program_start:                  ; Calculate program size and retain result.
  2879.  lea        program_end(pc), a0
  2880.  movea.l    4(a7), a1          
  2881.  suba.l     a1, a0             
  2882.  
  2883. return_memory:                  ; Return unused memory to operating system.
  2884.  move.l     a0, -(sp)           ; Store total program length in stack.
  2885.  move.l     a1, -(sp)           ; Store basepage address in stack.
  2886.  move.w     d0, -(sp)           ; Dummy value, can be anything.
  2887.  move.w     #$4a, -(sp)         ; Function = m_shrink = GEMDOS $4A.
  2888.  trap       #1                  ; GEMDOS call.
  2889.  adda.l     #$C, sp             ; Reset stack pointer to top of stack.
  2890.  
  2891. get_printer_status:             ; Get and save printer status.
  2892.  move.w     #0, -(sp)           ; Device = printer.
  2893.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  2894.  trap       #13                 ; If printer is on, -1 will be returned
  2895.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  2896.  move.b     d0, printer_status
  2897.  
  2898. place_values_in_registers:
  2899.  movem.l    registers_before(pc), d0-d7/a0-a6
  2900.  lea        stack(pc), a7
  2901.  move.l     a7, reg_A7_before
  2902.  
  2903. make_the_trap_call:             ; This is Experiment 2.
  2904.  pea        environmental_string(pc)
  2905.  pea        command_line(pc)
  2906.  pea        program_name(pc)    ; Must be the complete path to the file.
  2907.  move.w     #0, -(sp)
  2908.  move.w     #$4B, -(sp)         ; Function = GEMDOS $4B = p_exec.
  2909.  trap       #1                  ; Load and execute program PRG_6KC.TOS.
  2910.  add.l      #$10, sp
  2911.  
  2912. store_after_call_contents:
  2913.  movem.l    d0-d7/a0-a7, registers_after
  2914.  
  2915. print_all_contents:
  2916.  bsr        print_newline
  2917.  lea        experiment_head(pc), a0
  2918.  bsr        print_string
  2919.  lea        heading(pc), a0
  2920.  bsr        print_string
  2921.  lea        register_id(pc), a3
  2922.  lea        registers_before(pc), a4
  2923.  lea        registers_after(pc), a5
  2924.  move.l     #15, d4
  2925. print_register:
  2926.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  2927.  move.l     d3, d5              ; Save for comparison with after-value.
  2928.  bsr        bin_to_hex
  2929.  movea.l    a3, a0
  2930.  bsr        print_string        ; Print register number.
  2931.  add.l      #16, a3
  2932.  lea        hexadecimal(pc), a0
  2933.  bsr        print_string        ; Print before-call register content.
  2934.  lea        space(pc), a0
  2935.  bsr        print_string
  2936. compare_and_print:
  2937.  move.l     (a5)+, d3           ; Load after_call value.
  2938.  cmp.l      d5, d3              ; Compare before_call to after_call.
  2939.  beq.s      no_change           ; Branch if no change occurred.
  2940.  tst.b      printer_status      ; Find out if printer is on or off.
  2941.  beq.s      printer_off
  2942.  bsr        bold_on             ; Turn on printer bold print.
  2943. printer_off:
  2944.  bsr        reverse_on          ; Turn on video reverse print.
  2945.  
  2946. no_change:
  2947.  bsr        bin_to_hex
  2948.  bsr        print_hex_sign
  2949.  lea        hexadecimal(pc), a0
  2950.  bsr        print_string        ; Print after-call value.
  2951.  bsr        print_newline
  2952.  tst.b      printer_status      ; Find out if printer is on or off.
  2953.  beq.s      _printer_off
  2954.  bsr        bold_off            ; Turn printer bold print off.
  2955. _printer_off:
  2956.  bsr        reverse_off         ; Turn video reverse print off.
  2957.  dbra       d4, print_register
  2958.  bsr        print_newline
  2959.  
  2960. wait_for_keypress:
  2961.  move.w     #8, -(sp)
  2962.  trap       #1
  2963.  addq.l     #2, sp
  2964.  
  2965. terminate:
  2966.  move.w     #0, -(sp)
  2967.  trap       #1
  2968.  
  2969.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  2970.  ; passed as a longword in register D3.  Beginning with the most significant
  2971.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  2972.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  2973.  ; buffer.  The leading zero portion has been removed from this routine.
  2974.  
  2975. bin_to_hex:                      ; Converts a 32-bit binary number in D3 to
  2976.                                  ; ASCII hexadecimal.
  2977.  lea        hexadecimal(pc), a0  ; A0 is pointer to array "hexadecimal".
  2978.  lea        hex_table(pc), a1    ; A1 is pointer to array "hex_table".
  2979.  moveq      #7, d2               ; D2 is the loop counter.
  2980.  moveq      #0, d0               ; D0 is not zero.  That's what we have
  2981.                                  ; proved, so it must be cleared before use.
  2982. rotate_and_convert:
  2983.  rol.l      #4, d3               ; Rotate most significant nibble to the
  2984.                                  ; least significant nibble position.
  2985.  move.b     d3, d0               ; Copy least significant byte of D1 to D0.
  2986.  andi.b     #$F, d0              ; Mask out most significant nibble of D0.
  2987. store_digit:
  2988.  move.b     0(a1,d0.w), (a0)+    ; Store ASCII hexadecimal digit in buffer.
  2989.  dbra       d2, rotate_and_convert
  2990.  move.b     #0, (a0)             ; Terminate hexadecimal string with a null.
  2991.  rts
  2992.  
  2993. print_string:                   ; Expects address of string to be in A0.
  2994.  move.l     a0, -(sp)           ; Push address of string onto stack.
  2995.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2996.  trap       #1                  ; GEMDOS call
  2997.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  2998.  rts
  2999.  
  3000. print_newline:                  ; Prints a carriage return and linefeed.
  3001.  pea        newline(pc)         ; Push address of string onto stack.
  3002.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  3003.  trap       #1                  ; GEMDOS call
  3004.  addq.l     #6, sp
  3005.  rts
  3006.  
  3007. print_hex_sign:
  3008.  move.w     #$24, -(sp)
  3009.  move.w     #2, -(sp)
  3010.  move.w     #3, -(sp)
  3011.  trap       #13
  3012.  addq.l     #6, sp
  3013.  rts
  3014.  
  3015. bold_on:
  3016.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  3017.  move.w     #0, -(sp)
  3018.  move.w     #3, -(sp)
  3019.  trap       #13
  3020.  addq.l     #6, sp
  3021.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  3022.  move.w     #0, -(sp)
  3023.  move.w     #3, -(sp)
  3024.  trap       #13
  3025.  addq.l     #6, sp
  3026.  rts
  3027.  
  3028. bold_off:
  3029.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  3030.  move.w     #0, -(sp)
  3031.  move.w     #3, -(sp)
  3032.  trap       #13
  3033.  addq.l     #6, sp
  3034.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  3035.  move.w     #0, -(sp)
  3036.  move.w     #3, -(sp)
  3037.  trap       #13
  3038.  addq.l     #6, sp
  3039.  rts
  3040.  
  3041. reverse_on:
  3042.  move.w     #$1B, -(sp)
  3043.  move.w     #2, -(sp)
  3044.  move.w     #3, -(sp)
  3045.  trap       #13
  3046.  addq.l     #6, sp
  3047.  move.w     #$70, -(sp)
  3048.  move.w     #2, -(sp)
  3049.  move.w     #3, -(sp)
  3050.  trap       #13
  3051.  addq.l     #6, sp
  3052.  rts
  3053.  
  3054. reverse_off:
  3055.  move.w     #$1B, -(sp)
  3056.  move.w     #2, -(sp)
  3057.  move.w     #3, -(sp)
  3058.  trap       #13
  3059.  addq.l     #6, sp
  3060.  move.w     #$71, -(sp)
  3061.  move.w     #2, -(sp)
  3062.  move.w     #3, -(sp)
  3063.  trap       #13
  3064.  addq.l     #6, sp
  3065.  rts
  3066.  
  3067.  data
  3068. hex_table:        dc.b '0123456789ABCDEF'
  3069. newline:          dc.b $D,$A,0
  3070. experiment_head:  dc.b 'EXPERIMENT 2: System Trap #1 Corruption',$D,$A
  3071.                   dc.b '              Function = GEMDOS $4B',$D,$A,$A,0
  3072. heading:          dc.b '       Before Call         After  Call',$D,$A,0
  3073. space:            dc.b '     ',0
  3074. printer_status:   dc.b 0
  3075. register_id:      dc.b '  Data Reg 0: $',0,'  Data Reg 1: $',0
  3076.                   dc.b '  Data Reg 2: $',0,'  Data Reg 3: $',0
  3077.                   dc.b '  Data Reg 4: $',0,'  Data Reg 5: $',0
  3078.                   dc.b '  Data Reg 6: $',0,'  Data Reg 7: $',0
  3079.                   dc.b '  Add  Reg 0: $',0,'  Add  Reg 1: $',0
  3080.                   dc.b '  Add  Reg 2: $',0,'  Add  Reg 3: $',0
  3081.                   dc.b '  Add  Reg 4: $',0,'  Add  Reg 5: $',0
  3082.                   dc.b '  Add  Reg 6: $',0,'  Add  Reg 7: $',0
  3083.  
  3084. program_name:         dc.b 'D:\PRG_6KC.TOS',0
  3085. environmental_string: dc.b 0
  3086. command_line:         dc.b 0
  3087.  
  3088.  align
  3089.  
  3090.  ;bss MAJOR NOTE: Do not use the bss assembler op here.  If you do, the
  3091.  ;                value $44415441 will NOT be loaded into the elements
  3092.  ;                of the registers_before array.
  3093.  
  3094. trap_vector:      ds.l    1     ; Store for custom trap handler's vector.
  3095. hexadecimal:      ds.l    3     ; Output buffer.  Must be NULL terminated.
  3096. registers_before: ds.l   15, $44415441 ; Store this value in each before
  3097. reg_A7_before:    ds.l    1            ; array element during loading.
  3098. registers_after:  ds.l   16     ; Array for register contents after call.
  3099.  
  3100.  ;MAJOR NOTE: Note that this program requires a larger stack than does
  3101.  ;            REG_TST1 and REG_TST2.  If the stack size is left at 48 words
  3102.  ;            here, 4 bombs will appear when this program is executed, just
  3103.  ;            it attempts to print to the printer.
  3104.  
  3105.                   ds.l   96     ; Program stack.
  3106. stack:            ds.l    1     ; Address of program stack.
  3107. program_end:      ds.l    0
  3108.  end
  3109.  
  3110.  
  3111. Figure 8.9. Program 55 Execution Results.
  3112.   
  3113.  
  3114.  
  3115.  
  3116.  
  3117.  
  3118.  
  3119.  
  3120.  
  3121.  
  3122.  
  3123.  
  3124.  
  3125.  
  3126.  
  3127.  
  3128.  
  3129.  
  3130.  
  3131.  
  3132.  
  3133.  
  3134.  
  3135.  
  3136.  
  3137.  
  3138.  
  3139.  
  3140.  
  3141.  
  3142.      The results of experiment 2 agree with the assessment 
  3143. given in the Internals book, under the heading 3.1 The 
  3144. GEMDOS, on page 106.  They do not even approach congruency 
  3145. with the information given in the Peel book on page 3-2.  
  3146. Performing these experiments will give us the verified data 
  3147. that we need in order to realize maximum speed via judicious 
  3148. register usage.
  3149.  
  3150.  
  3151. Program 56. Experiment 3: System trap #14, XBIOS function 
  3152. #$2 register contamination.
  3153.  
  3154.  ; PROGRAM NAME: REG_TST4.S
  3155.  ;      VERSION: 1.001
  3156.  
  3157.  ; Assembly Instructions:
  3158.  
  3159.  ;   Assemble in Relocatable mode.  Save the assembled program with a TOS suffix.
  3160.  
  3161.  ; Program Function:
  3162.  
  3163.  ;   Similar to REG_TST2, except this one calls a system trap #14 handler.
  3164.  
  3165.  ; EXPERIMENT 3:
  3166.  
  3167.  ;     This program will call a system trap #14 handler.
  3168.  
  3169.  ;     XBIOS function $2 (aka physbase) was chosen for the experiment.  
  3170.  ; This function is discussed in the Internals book, however, at the
  3171.  ; site of the function description, no mention is made of the register
  3172.  ; in which the desired address is returned.  We will be able to see the
  3173.  ; address in one of the registers when this program is executed.
  3174.  
  3175. program_start:                  ; Calculate program size and retain result.
  3176.  lea        program_end(pc), a0
  3177.  movea.l    4(a7), a1          
  3178.  suba.l     a1, a0             
  3179.  
  3180. return_memory:                  ; Return unused memory to operating system.
  3181.  move.l     a0, -(sp)           ; Store total program length in stack.
  3182.  move.l     a1, -(sp)           ; Store basepage address in stack.
  3183.  move.w     d0, -(sp)           ; Dummy value, can be anything.
  3184.  move.w     #$4a, -(sp)         ; Function = m_shrink = GEMDOS $4A.
  3185.  trap       #1                  ; GEMDOS call.
  3186.  adda.l     #$C, sp             ; Reset stack pointer to top of stack.
  3187.  
  3188. get_printer_status:             ; Get and save printer status.
  3189.  move.w     #0, -(sp)           ; Device = printer.
  3190.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  3191.  trap       #13                 ; If printer is on, -1 will be returned
  3192.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  3193.  move.b     d0, printer_status
  3194.  
  3195. place_values_in_registers:
  3196.  movem.l    registers_before(pc), d0-d7/a0-a6
  3197.  lea        stack(pc), a7
  3198.  move.l     a7, reg_A7_before
  3199.  
  3200. make_the_trap_call:             ; This is Experiment 3.
  3201.  
  3202.  ; We're going to use XBIOS function $2 to determine the register in
  3203.  ; which the ram address of the physical video screen is returned.  I
  3204.  ; already know that it is returned in D0, but this program will verify
  3205.  ; that information.
  3206.  
  3207. get_system_screen_address:
  3208.  move       #2, -(sp)            ; Function = XBIOS $2 = physbase.
  3209.  trap       #14                  ; 
  3210.  addq.l     #2, sp
  3211.  
  3212. store_after_call_contents:
  3213.  movem.l    d0-d7/a0-a7, registers_after
  3214.  
  3215. print_all_contents:
  3216.  bsr        print_newline
  3217.  lea        experiment_head(pc), a0
  3218.  bsr        print_string
  3219.  lea        heading(pc), a0
  3220.  bsr        print_string
  3221.  lea        register_id(pc), a3
  3222.  lea        registers_before(pc), a4
  3223.  lea        registers_after(pc), a5
  3224.  move.l     #15, d4
  3225. print_register:
  3226.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  3227.  move.l     d3, d5              ; Save for comparison with after-value.
  3228.  bsr        bin_to_hex
  3229.  movea.l    a3, a0
  3230.  bsr        print_string        ; Print register number.
  3231.  add.l      #16, a3
  3232.  lea        hexadecimal(pc), a0
  3233.  bsr        print_string        ; Print before-call register content.
  3234.  lea        space(pc), a0
  3235.  bsr        print_string
  3236. compare_and_print:
  3237.  move.l     (a5)+, d3           ; Load after_call value.
  3238.  cmp.l      d5, d3              ; Compare before_call to after_call.
  3239.  beq.s      no_change           ; Branch if no change occurred.
  3240.  tst.b      printer_status      ; Find out if printer is on or off.
  3241.  beq.s      printer_off
  3242.  bsr        bold_on             ; Turn on printer bold print.
  3243. printer_off:
  3244.  bsr        reverse_on          ; Turn on video reverse print.
  3245.  
  3246. no_change:
  3247.  bsr        bin_to_hex
  3248.  bsr        print_hex_sign
  3249.  lea        hexadecimal(pc), a0
  3250.  bsr        print_string        ; Print after-call value.
  3251.  bsr        print_newline
  3252.  tst.b      printer_status      ; Find out if printer is on or off.
  3253.  beq.s      _printer_off
  3254.  bsr        bold_off            ; Turn printer bold print off.
  3255. _printer_off:
  3256.  bsr        reverse_off         ; Turn video reverse print off.
  3257.  dbra       d4, print_register
  3258.  bsr        print_newline
  3259.  
  3260. wait_for_keypress:
  3261.  move.w     #8, -(sp)
  3262.  trap       #1
  3263.  addq.l     #2, sp
  3264.  
  3265. terminate:
  3266.  move.w     #0, -(sp)
  3267.  trap       #1
  3268.  
  3269.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  3270.  ; passed as a longword in register D3.  Beginning with the most significant
  3271.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  3272.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  3273.  ; buffer.  The leading zero portion has been removed from this routine.
  3274.  
  3275. bin_to_hex:                      ; Converts a 32-bit binary number in D3 to
  3276.                                  ; ASCII hexadecimal.
  3277.  lea        hexadecimal(pc), a0  ; A0 is pointer to array "hexadecimal".
  3278.  lea        hex_table(pc), a1    ; A1 is pointer to array "hex_table".
  3279.  moveq      #7, d2               ; D2 is the loop counter.
  3280.  moveq      #0, d0               ; D0 is not zero.  That's what we have
  3281.                                  ; proved, so it must be cleared before use.
  3282. rotate_and_convert:
  3283.  rol.l      #4, d3               ; Rotate most significant nibble to the
  3284.                                  ; least significant nibble position.
  3285.  move.b     d3, d0               ; Copy least significant byte of D1 to D0.
  3286.  andi.b     #$F, d0              ; Mask out most significant nibble of D0.
  3287. store_digit:
  3288.  move.b     0(a1,d0.w), (a0)+    ; Store ASCII hexadecimal digit in buffer.
  3289.  dbra       d2, rotate_and_convert
  3290.  move.b     #0, (a0)             ; Terminate hexadecimal string with a null.
  3291.  rts
  3292.  
  3293. print_string:                   ; Expects address of string to be in A0.
  3294.  move.l     a0, -(sp)           ; Push address of string onto stack.
  3295.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  3296.  trap       #1                  ; GEMDOS call
  3297.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  3298.  rts
  3299.  
  3300. print_newline:                  ; Prints a carriage return and linefeed.
  3301.  pea        newline(pc)         ; Push address of string onto stack.
  3302.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  3303.  trap       #1                  ; GEMDOS call
  3304.  addq.l     #6, sp
  3305.  rts
  3306.  
  3307. print_hex_sign:
  3308.  move.w     #$24, -(sp)
  3309.  move.w     #2, -(sp)
  3310.  move.w     #3, -(sp)
  3311.  trap       #13
  3312.  addq.l     #6, sp
  3313.  rts
  3314.  
  3315. bold_on:
  3316.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  3317.  move.w     #0, -(sp)
  3318.  move.w     #3, -(sp)
  3319.  trap       #13
  3320.  addq.l     #6, sp
  3321.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  3322.  move.w     #0, -(sp)
  3323.  move.w     #3, -(sp)
  3324.  trap       #13
  3325.  addq.l     #6, sp
  3326.  rts
  3327.  
  3328. bold_off:
  3329.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  3330.  move.w     #0, -(sp)
  3331.  move.w     #3, -(sp)
  3332.  trap       #13
  3333.  addq.l     #6, sp
  3334.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  3335.  move.w     #0, -(sp)
  3336.  move.w     #3, -(sp)
  3337.  trap       #13
  3338.  addq.l     #6, sp
  3339.  rts
  3340.  
  3341. reverse_on:
  3342.  move.w     #$1B, -(sp)
  3343.  move.w     #2, -(sp)
  3344.  move.w     #3, -(sp)
  3345.  trap       #13
  3346.  addq.l     #6, sp
  3347.  move.w     #$70, -(sp)
  3348.  move.w     #2, -(sp)
  3349.  move.w     #3, -(sp)
  3350.  trap       #13
  3351.  addq.l     #6, sp
  3352.  rts
  3353.  
  3354. reverse_off:
  3355.  move.w     #$1B, -(sp)
  3356.  move.w     #2, -(sp)
  3357.  move.w     #3, -(sp)
  3358.  trap       #13
  3359.  addq.l     #6, sp
  3360.  move.w     #$71, -(sp)
  3361.  move.w     #2, -(sp)
  3362.  move.w     #3, -(sp)
  3363.  trap       #13
  3364.  addq.l     #6, sp
  3365.  rts
  3366.  
  3367.  data
  3368. hex_table:        dc.b '0123456789ABCDEF'
  3369. newline:          dc.b $D,$A,0
  3370. experiment_head:  dc.b 'EXPERIMENT 3: System Trap #14 Corruption',$D,$A
  3371.                   dc.b '              Function = XBIOS $2',$D,$A,$A,0
  3372. heading:          dc.b '       Before Call         After  Call',$D,$A,0
  3373. space:            dc.b '     ',0
  3374. printer_status:   dc.b 0
  3375. register_id:      dc.b '  Data Reg 0: $',0,'  Data Reg 1: $',0
  3376.                   dc.b '  Data Reg 2: $',0,'  Data Reg 3: $',0
  3377.                   dc.b '  Data Reg 4: $',0,'  Data Reg 5: $',0
  3378.                   dc.b '  Data Reg 6: $',0,'  Data Reg 7: $',0
  3379.                   dc.b '  Add  Reg 0: $',0,'  Add  Reg 1: $',0
  3380.                   dc.b '  Add  Reg 2: $',0,'  Add  Reg 3: $',0
  3381.                   dc.b '  Add  Reg 4: $',0,'  Add  Reg 5: $',0
  3382.                   dc.b '  Add  Reg 6: $',0,'  Add  Reg 7: $',0
  3383.  align
  3384.  
  3385.  ;bss MAJOR NOTE: Do not use the bss assembler op here.  If you do, the
  3386.  ;                value $44415441 will NOT be loaded into the elements
  3387.  ;                of the registers_before array.
  3388.  
  3389. trap_vector:      ds.l    1     ; Store for custom trap handler's vector.
  3390. hexadecimal:      ds.l    3     ; Output buffer.  Must be NULL terminated.
  3391. registers_before: ds.l   15, $44415441 ; Store this value in each before
  3392. reg_A7_before:    ds.l    1            ; array element during loading.
  3393. registers_after:  ds.l   16     ; Array for register contents after call.
  3394.                   ds.l   48     ; Program stack.
  3395. stack:            ds.l    1     ; Address of program stack.
  3396. program_end:      ds.l    0
  3397.  end
  3398.  
  3399.  
  3400. Figure 8.10. Program 56 Execution Results.   
  3401.   
  3402.  
  3403.  
  3404.  
  3405.  
  3406.  
  3407.  
  3408.  
  3409.  
  3410.  
  3411.  
  3412.  
  3413.  
  3414.  
  3415.  
  3416.  
  3417.  
  3418.  
  3419.  
  3420.  
  3421.  
  3422.  
  3423.  
  3424.  
  3425.  
  3426.  
  3427.  
  3428.  
  3429.  
  3430.   
  3431.      The Internals book does not provide register 
  3432. contamination information for XBIOS calls.  The Peel book 
  3433. implies that registers d0-d2/a0-a2 are subject to 
  3434. contamination during such system calls.  The results of 
  3435. experiment 3 suggest that, at least when the function 
  3436. specified is XBIOS $2, only registers d0/a0-a1 are corrupted 
  3437. during the call.
  3438.  
  3439.  
  3440. Program 57. Experiment 4: Desk accessory register corruption 
  3441. by BIOS function $8.
  3442.  
  3443.  ; PROGRAM NAME: REG_TST5.S
  3444.  ;      VERSION: 1.001
  3445.  
  3446.  ;     Desk Accessory (No trap handler; no interrupt handler).
  3447.  
  3448.  ; Assembly Instructions:
  3449.  
  3450.  ;     Assemble in Relocatable mode and save with a PRG extension.  From the
  3451.  ; desktop, click on REG_TST5.PRG, click on Show Info under the File menu,
  3452.  ; backspace over the PRG on the name line, then type in ACC, thereby
  3453.  ; changing the object code file's name to REG_TST5.ACC.  Finally, place the
  3454.  ; file, REG_TST5.ACC in the root directory of your boot disk.  During the
  3455.  ; next power-up cycle, the program will be installed in memory as a desk
  3456.  ; accessory.
  3457.  
  3458.  ; Program Function: EXPERIMENT 4.
  3459.  
  3460.  ;     Similar to REG_TST2, but this one is a desk accessory.  It does not
  3461.  ; call a trap handler, as does the first 4 programs of the group.  
  3462.  
  3463.  ;     Furthermore, it does not copy values from the registers_before array
  3464.  ; into the registers.  Instead, processor control is relinquished via the
  3465.  ; AES $17 (aka evnt_mesag) function, with values in the registers that are
  3466.  ; placed there during the desk accessory initialization procedure.
  3467.  
  3468.  ;     You should exercise this experiment by selecting REG_TST5 from within
  3469.  ; a running application which allows access to the Desk Accessory menu;
  3470.  ; you can do this from within the AssemPro editor.  Or you can simply 
  3471.  ; select REG_TST5 as an accessory from the desktop.
  3472.  
  3473.  ;     First, you can convince yourself that all registers are altered by
  3474.  ; executing REG_TST1, REG_TST2, or REG_TST3 from within AssemPro.  Then
  3475.  ; select REG_TST5 from the Accessory menu.  
  3476.  
  3477.  ;     When this program is selected thus, a message will be sent to the
  3478.  ; program by the operating system, thereby forcing the message handling
  3479.  ; algorithm to execute.  The message handler will print the register
  3480.  ; contents, as does the other programs in this group.
  3481.  
  3482.  ;     Since this program does not produce its own screen, its output will
  3483.  ; obliterate the screen of the application from which it is selected.  To
  3484.  ; recover, press the Return key to force this program to relinquish
  3485.  ; processor control, then move the mouse arrow around, near the spot where
  3486.  ; the applications menu line should be.  When you do this, you will see
  3487.  ; items appear as they normally would.  You can then select any function
  3488.  ; that will cause the screen to be drawn, such as Debugger, if you are
  3489.  ; working within AssemPro.  Or you may be able to find the window close box
  3490.  ; in the upper left hand corner of the window.  If you can't get the hang
  3491.  ; of it, just reset the computer manually (a cold reset is best most often).
  3492.  
  3493.  ;     The knowledge we wish to gain from the results of this experiment is
  3494.  ; an answer to the question "Does the operating system protect desk 
  3495.  ; accessory registers from corruption while the accessory is waiting to be
  3496.  ; activated?".  That is, if we store the address of the control array in
  3497.  ; register a4 during the accessory initialization procedure, for example,
  3498.  ; will that address still be in register a4 when the accessory is activated.
  3499.  
  3500.  ;     If the desk accessory registers are protected from corruption while
  3501.  ; it is waiting for a message, then we can store values in the registers
  3502.  ; during the initialization procedure and use them in the message handler,
  3503.  ; without taking the time to store the values during the execution of the
  3504.  ; message handler.  That will permit faster message handler response.
  3505.         
  3506. program_start:
  3507.  lea       stack(pc), a7        ; A7 points to program's stack address.
  3508.  move.l    a7, reg_A7_before    ; Store stack address in register array.                            
  3509.  lea       aes_pb(pc), a3       ; aes_pb = AES parameter block.
  3510.  lea       control(pc), a4      ; A4 is pointer for array "control".
  3511.  move.w    #$C8, d3             ; *** AES call number in D3.
  3512.  
  3513.  ; I am interested in the volatility of the above registers.  Do they
  3514.  ; maintain their values until I alter them in this program, or are they
  3515.  ; corrupted when the desk accessory is entered via the evnt_mesag
  3516.  ; function?
  3517.  
  3518. initialize_application:
  3519.  move.w    #$A, (a4)            ; Function = appl_init = AES $A.
  3520.  move.w    #1, 4(a4)            ; Return one int_out parameter.
  3521.  move.l    a3, d1               ; A3 contains address of aes parameter block.
  3522.  move.w    d3, d0               ; D3 contains AES call number.
  3523.  trap      #2                   ; apid returned in int_out[0] and global[2].
  3524.  
  3525. menu_installation:             
  3526.  move.w    #$23, (a4)           ; Function = menu_register = AES $23.
  3527.  move.w    #1, 2(a4)            ; Pass one int_in parameter.         
  3528.  move.w    #1, 6(a4)            ; Pass one addr_in parameter.
  3529.  move.w    int_out(pc), int_in  ; Application identification to int_in[0].
  3530.  move.l    #menu_text, addr_in  ; Menu text address to addr_in[0].
  3531.  move.l    a3, d1               ; Address of aes parameter block to D1.
  3532.  move.w    d3, d0               ; D3 contains AES call number.
  3533.  trap      #2                   ; Menu identification number returned
  3534.                                 ; in int_out[0].
  3535.  move.w    int_out, d4          ; Store menu identification number in D4.
  3536.  
  3537.  ; MAIN ACCESSORY LOOP
  3538.  
  3539. wait_for_message:               ; Relinquish processor control.
  3540.  move.w    #$17, (a4)           ; Function = evnt_mesag = AES $17.
  3541.  move.w    #0, 2(a4)
  3542.  move.l    #message, addr_in    ; Address of message array to addr_in.
  3543.  
  3544. store_register_contents:
  3545.  movem.l   d0-d7/a0-a6, registers_before
  3546.  
  3547.  move.l    a3, d1               ; Address of aes parameter block to D1.
  3548.  move.w    d3, d0               ; AES call number to D0.
  3549.  trap      #2                   ; Message received is placed in array
  3550.                                 ; "message".
  3551.  
  3552. ; Enters here only when a message is received.
  3553.  
  3554. message_handler:                ; Entrance point when message is received.
  3555.  cmpi.w    #$28, message        ; Compare AC OPEN code with message[0].
  3556.  bne.s     wait_for_message     ; Execute the evnt_mesag function.
  3557.  move.w    message+8, d0        ; id_selected (message[4]) = menu ID of
  3558.                                 ; item user selected.
  3559.  cmp.w     d4, d0               ; Was this application selected.
  3560.  bne.s     wait_for_message     ; Execute the evnt_mesag function.
  3561.  
  3562. store_after_entrance_register_contents:
  3563.  movem.l    d0-d7/a0-a7, registers_after
  3564.  
  3565. get_printer_status:             ; Get and save printer status.
  3566.  move.w     #0, -(sp)           ; Device = printer.
  3567.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  3568.  trap       #13                 ; If printer is on, -1 will be returned
  3569.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  3570.  move.b     d0, printer_status
  3571.  
  3572.  ; The following algorithm prints the before value for a register, then,
  3573.  ; on the same line, it prints the after value.
  3574.  
  3575. print_all_contents:
  3576.  bsr        print_newline
  3577.  lea        experiment_head(pc), a0
  3578.  bsr        print_string
  3579.  lea        heading(pc), a0
  3580.  bsr        print_string
  3581.  lea        register_id(pc), a3
  3582.  lea        registers_before(pc), a4
  3583.  lea        registers_after(pc), a5
  3584.  move.l     #15, d4
  3585. print_register:
  3586.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  3587.  move.l     d3, d5              ; Save for comparison with after-value.
  3588.  bsr        bin_to_hex
  3589.  movea.l    a3, a0
  3590.  bsr        print_string        ; Print register number.
  3591.  add.l      #16, a3
  3592.  lea        hexadecimal(pc), a0
  3593.  bsr        print_string        ; Print before-call register content.
  3594.  lea        space(pc), a0
  3595.  bsr        print_string
  3596. compare_and_print:
  3597.  move.l     (a5)+, d3           ; Load after_call value.
  3598.  cmp.l      d5, d3              ; Compare before_call to after_call.
  3599.  beq.s      no_change           ; Branch if no change occurred.
  3600.  tst.b      printer_status      ; Find out if printer is on or off.
  3601.  beq.s      printer_off
  3602.  bsr        bold_on             ; Turn on printer bold print.
  3603. printer_off:
  3604.  bsr        reverse_on          ; Turn on video reverse print.
  3605.  
  3606. no_change:
  3607.  bsr        bin_to_hex
  3608.  bsr        print_hex_sign
  3609.  lea        hexadecimal(pc), a0
  3610.  bsr        print_string        ; Print after-call value.
  3611.  bsr        print_newline
  3612.  tst.b      printer_status      ; Find out if printer is on or off.
  3613.  beq.s      _printer_off
  3614.  bsr        bold_off            ; Turn printer bold print off.
  3615. _printer_off:
  3616.  bsr        reverse_off         ; Turn video reverse print off.
  3617.  dbra       d4, print_register
  3618.  bsr        print_newline
  3619.  
  3620. wait_for_keypress:
  3621.  move.w     #8, -(sp)
  3622.  trap       #1
  3623.  addq.l     #2, sp
  3624.  
  3625.  ; Now we have to restore the desk accessory registers because our printing
  3626.  ; algorithm has corrupted them.  Also, we have learned that the system
  3627.  ; corrupts our stack pointer.  We restore all registers, except A7, to the
  3628.  ; values they had when the accessory was activated.  Register A7 retains
  3629.  ; the original stack address.  During successive activations of the 
  3630.  ; accessory, we will be able to see any changes.
  3631.  
  3632.  movem.l    registers_after(pc), d0-d7/a0-a6
  3633.  bra        wait_for_message
  3634.  
  3635.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  3636.  ; passed as a longword in register D3.  Beginning with the most significant
  3637.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  3638.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  3639.  ; buffer.  The leading zero portion has been removed from this routine.
  3640.  
  3641. bin_to_hex:                      ; Converts a 32-bit binary number in D3 to
  3642.                                  ; ASCII hexadecimal.
  3643.  lea        hexadecimal(pc), a0  ; A0 is pointer to array "hexadecimal".
  3644.  lea        hex_table(pc), a1    ; A1 is pointer to array "hex_table".
  3645.  moveq      #7, d2               ; D2 is the loop counter.
  3646.  moveq      #0, d0               ; D0 is not zero.  That's what we have
  3647.                                  ; proved, so it must be cleared before use.
  3648. rotate_and_convert:
  3649.  rol.l      #4, d3               ; Rotate most significant nibble to the
  3650.                                  ; least significant nibble position.
  3651.  move.b     d3, d0               ; Copy least significant byte of D1 to D0.
  3652.  andi.b     #$F, d0              ; Mask out most significant nibble of D0.
  3653. store_digit:
  3654.  move.b     0(a1,d0.w), (a0)+    ; Store ASCII hexadecimal digit in buffer.
  3655.  dbra       d2, rotate_and_convert
  3656.  move.b     #0, (a0)             ; Terminate hexadecimal string with a null.
  3657.  rts
  3658.  
  3659. print_string:                   ; Expects address of string to be in A0.
  3660.  move.l     a0, -(sp)           ; Push address of string onto stack.
  3661.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  3662.  trap       #1                  ; GEMDOS call
  3663.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  3664.  rts
  3665.  
  3666. print_newline:                  ; Prints a carriage return and linefeed.
  3667.  pea        newline(pc)         ; Push address of string onto stack.
  3668.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  3669.  trap       #1                  ; GEMDOS call
  3670.  addq.l     #6, sp
  3671.  rts
  3672.  
  3673. print_hex_sign:
  3674.  move.w     #$24, -(sp)
  3675.  move.w     #2, -(sp)
  3676.  move.w     #3, -(sp)
  3677.  trap       #13
  3678.  addq.l     #6, sp
  3679.  rts
  3680.  
  3681. bold_on:
  3682.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  3683.  move.w     #0, -(sp)
  3684.  move.w     #3, -(sp)
  3685.  trap       #13
  3686.  addq.l     #6, sp
  3687.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  3688.  move.w     #0, -(sp)
  3689.  move.w     #3, -(sp)
  3690.  trap       #13
  3691.  addq.l     #6, sp
  3692.  rts
  3693.  
  3694. bold_off:
  3695.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  3696.  move.w     #0, -(sp)
  3697.  move.w     #3, -(sp)
  3698.  trap       #13
  3699.  addq.l     #6, sp
  3700.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  3701.  move.w     #0, -(sp)
  3702.  move.w     #3, -(sp)
  3703.  trap       #13
  3704.  addq.l     #6, sp
  3705.  rts
  3706.  
  3707. reverse_on:
  3708.  move.w     #$1B, -(sp)
  3709.  move.w     #2, -(sp)
  3710.  move.w     #3, -(sp)
  3711.  trap       #13
  3712.  addq.l     #6, sp
  3713.  move.w     #$70, -(sp)
  3714.  move.w     #2, -(sp)
  3715.  move.w     #3, -(sp)
  3716.  trap       #13
  3717.  addq.l     #6, sp
  3718.  rts
  3719.  
  3720. reverse_off:
  3721.  move.w     #$1B, -(sp)
  3722.  move.w     #2, -(sp)
  3723.  move.w     #3, -(sp)
  3724.  trap       #13
  3725.  addq.l     #6, sp
  3726.  move.w     #$71, -(sp)
  3727.  move.w     #2, -(sp)
  3728.  move.w     #3, -(sp)
  3729.  trap       #13
  3730.  addq.l     #6, sp
  3731.  rts
  3732.  
  3733.  data
  3734. hex_table:       dc.b '0123456789ABCDEF'
  3735. newline:         dc.b $D,$A,0
  3736. experiment_head: dc.b 'EXPERIMENT 4: Desk Accessory Corruption By',$D,$A
  3737.                  dc.b '              Function = BIOS $8',$D,$A,$A,0
  3738. heading:         dc.b '       Before Call         After  Call',$D,$A,0
  3739. space:           dc.b '     ',0
  3740. printer_status:  dc.b 0
  3741. register_id:     dc.b '  Data Reg 0: $',0,'  Data Reg 1: $',0
  3742.                  dc.b '  Data Reg 2: $',0,'  Data Reg 3: $',0
  3743.                  dc.b '  Data Reg 4: $',0,'  Data Reg 5: $',0
  3744.                  dc.b '  Data Reg 6: $',0,'  Data Reg 7: $',0
  3745.                  dc.b '  Add  Reg 0: $',0,'  Add  Reg 1: $',0
  3746.                  dc.b '  Add  Reg 2: $',0,'  Add  Reg 3: $',0
  3747.                  dc.b '  Add  Reg 4: $',0,'  Add  Reg 5: $',0
  3748.                  dc.b '  Add  Reg 6: $',0,'  Add  Reg 7: $',0
  3749. menu_text:       dc.b '  REG_TST5 ',0
  3750.  align                    ; Align storage on a word boundary.
  3751.  ;
  3752.  ; AES PARAMETER BLOCK
  3753.  ; 
  3754. aes_pb:          dc.l   control,global,int_in,int_out,addr_in,addr_out
  3755.  ;
  3756.  ; AES CONTROL TABLE
  3757.  ;
  3758.  bss
  3759. control:          ds.w    5
  3760. global:           ds.w    3
  3761.                   ds.l    6
  3762. int_in:           ds.w    1     ; Input parameter.
  3763. int_out:          ds.w    1     ; Output parameter.
  3764. addr_in:          ds.l    1     ; Input address.
  3765. addr_out:         ds.l    1     ; Output address.
  3766.  ;
  3767.  ; OTHER VARIABLES
  3768.  ;
  3769. message:          ds.w    8
  3770. trap_vector:      ds.l    1     ; Store for custom trap handler's vector.
  3771. hexadecimal:      ds.l    3     ; Output buffer.  Must be NULL terminated.
  3772. registers_before: ds.l   15
  3773. reg_A7_before:    ds.l    1     ; Keep stack separate and constant.
  3774. registers_after:  ds.l   16     ; Array for register contents after call.
  3775.                   ds.l   48
  3776. stack:            ds.l    0
  3777. program_end:      ds.l    0
  3778.  end
  3779.  
  3780.  
  3781. Figure 8.11. Program 57 Execution Results.
  3782.  
  3783.  
  3784.  
  3785.  
  3786.  
  3787.  
  3788.  
  3789.  
  3790.  
  3791.  
  3792.  
  3793.  
  3794.  
  3795.  
  3796.  
  3797.  
  3798.  
  3799.  
  3800.  
  3801.  
  3802.  
  3803.  
  3804.  
  3805.  
  3806.  
  3807.  
  3808.  
  3809.  
  3810.  
  3811.  
  3812.      The results obtain via experiment 4 are significant.  
  3813. It is easy to see that the ST's limited multi-tasking 
  3814. environment includes the code required to protect accessory 
  3815. registers from contamination.  Therefore, when the accessory 
  3816. is activated, the values placed in registers before the 
  3817. evnt_mesag function is executed are still valid.  The AES 
  3818. functions used in program 57 are explained in the Abacus GEM 
  3819. and the COMPUTE! AES books.
  3820.  
  3821.  
  3822. Program 58. Experiment 5A: Custom trap #13 handler 
  3823. corruption of calling application registers, and 
  3824. contamination of handler registers by the calling 
  3825. application.
  3826.  
  3827.  ; PROGRAM NAME: REG_TST6.S
  3828.  ;      VERSION: 1.001
  3829.  
  3830.  ; Assembly Instructions:
  3831.  
  3832.  ;     Assemble in Relocatable mode and save with a TOS extension.
  3833.  
  3834.  ; Program Function: EXPERIMENT 5A.
  3835.  
  3836.  ;     Similar to REG_TST2, but this one installs a custom trap #13 handler.
  3837.  ; The handler intercepts all BIOS calls, passing all but one custom function
  3838.  ; on to the system trap #13 handler.  Since there is no BIOS function $C, I
  3839.  ; use that number for the function to be processed by the custom handler.
  3840.  
  3841.  ;     In this program, I do not copy values from the registers_before
  3842.  ; array into the registers.  Instead, processor control is relinquished,
  3843.  ; via GEMDOS function $31 (aka ptermres), with values in the registers that
  3844.  ; are placed there during the load and stay resident (LSR) initialization
  3845.  ; procedure.
  3846.  
  3847.  ;     Execute this program from the desktop.  Then execute program REG_TST7,
  3848.  ; the program that is used to exercise this program.
  3849.  
  3850.  ;     As with the desk accessory, REG_TST5, you may want to execute one of
  3851.  ; the first three programs of the group from within AssemPro to alter all
  3852.  ; registers.  Then, from within AssemPro, you can execute REG_TST7, which
  3853.  ; calls the custom BIOS function $C.
  3854.  
  3855.  ;     Since this program does not produce its own screen, its output will
  3856.  ; obliterate the screen of the application from which it is selected.  To
  3857.  ; recover, press the Return key to force this program to relinquish
  3858.  ; processor control, then move the mouse arrow around, near the spot where
  3859.  ; the applications menu line should be.  When you do this, you will see
  3860.  ; items appear as they normally would.  You can then select any function
  3861.  ; that will cause the screen to be drawn, such as Debugger, if you are
  3862.  ; working within AssemPro.  Or you may be able to find the window close box
  3863.  ; in the upper left hand corner of the window.  If you can't get the hang
  3864.  ; of it, just reset the computer manually (a cold reset is best most often).
  3865.  
  3866.  ;     The knowledge we wish to gain from the results of this experiment is
  3867.  ; an answer to the question "does the operating system protect the registers
  3868.  ; of custom traps handlers that are installed by a program that is resident
  3869.  ; via the ptermres function.  This is the same kind of data we wanted to
  3870.  ; obtain with REG_TST5 for desk accessories.
  3871.  
  3872.  ;     If the trap handler registers are protected from corruption, then we
  3873.  ; can store values in the registers during the initialization procedure and
  3874.  ; use them in the trap handler, without taking the time to store the values
  3875.  ; during trap interceptions.  That would permit faster trap handler
  3876.  ; response.
  3877.  
  3878. program_start:                  ; Compute program size and retain result.
  3879.  lea        program_end(pc), a3
  3880.  movea.l    4(a7), a4          
  3881.  suba.l     a4, a3              ; Yields size of memory that must remain
  3882.                                 ; resident.
  3883. load_stack_address:
  3884.  lea        stack(pc), a7
  3885.  
  3886. install_new_trap_13_vector:
  3887.  pea       custom_trap_handler(pc)  ; Push new trap handler address onto stack.
  3888.  move.w    #$2D, -(sp)          ; Trap 13 vector number.
  3889.  move.w    #5, -(sp)            ; Function = setexec = BIOS $5.
  3890.  trap      #13                  ; Current trap handler vector returned in D0.
  3891.  addq.l    #8, sp
  3892.  move.l    d0, preempted_handler_address
  3893.  
  3894. store_register_contents:
  3895.  movem.l   d0-d7/a0-a7, registers_before
  3896.  
  3897. relinquish_processor_control:   ; Maintain memory residency.
  3898.  move.w    #0, -(sp)            ; See page 121 of Internals book.
  3899.  move.l    a3, -(sp)            ; Size of memory to remain resident.
  3900.  move.w    #$31, -(sp)          ; Function = ptermres = GEMDOS $31.
  3901.  trap      #1
  3902.  
  3903. custom_trap_handler:
  3904.  move.l    usp(pc), a0          ; Load address of current top of user stack.
  3905.  
  3906. get_processor_status:
  3907.  btst      #5, (sp)             ; User mode test.
  3908.  beq.s     was_user_mode        ; No adjustment is necessary if the
  3909.                                 ; processor was in user mode.
  3910.  movea.l   sp, a0               ; Load current top of supervisor stack.
  3911.  addq.l    #6, a0               ; Adjust SSP for user mode type data access.
  3912.  
  3913. was_user_mode:                  ; Processing for either mode follows.
  3914.  cmpi.w    #$C, (a0)            ; Calling the custom BIOS function?
  3915.  bne       not_desired_call
  3916.  
  3917. store_after_entrance_register_contents:
  3918.  movem.l    d0-d7/a0-a7, registers_after
  3919.  
  3920. get_printer_status:             ; Get and save printer status.
  3921.  move.w     #0, -(sp)           ; Device = printer.
  3922.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  3923.  trap       #13                 ; If printer is on, -1 will be returned
  3924.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  3925.  move.b     d0, printer_status
  3926.  
  3927. print_all_contents:
  3928.  bsr        print_newline
  3929.  lea        experiment_head(pc), a0
  3930.  bsr        print_string
  3931.  lea        heading(pc), a0
  3932.  bsr        print_string
  3933.  lea        register_id(pc), a3
  3934.  lea        registers_before(pc), a4
  3935.  lea        registers_after(pc), a5
  3936.  move.l     #15, d4
  3937. print_register:
  3938.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  3939.  move.l     d3, d5              ; Save for comparison with after-value.
  3940.  bsr        bin_to_hex
  3941.  movea.l    a3, a0
  3942.  bsr        print_string        ; Print register number.
  3943.  add.l      #16, a3
  3944.  lea        hexadecimal(pc), a0
  3945.  bsr        print_string        ; Print before-call register content.
  3946.  lea        space(pc), a0
  3947.  bsr        print_string
  3948. compare_and_print:
  3949.  move.l     (a5)+, d3           ; Load after_call value.
  3950.  cmp.l      d5, d3              ; Compare before_call to after_call.
  3951.  beq.s      no_change           ; Branch if no change occurred.
  3952.  tst.b      printer_status      ; Find out if printer is on or off.
  3953.  beq.s      printer_off
  3954.  bsr        bold_on             ; Turn on printer bold print.
  3955. printer_off:
  3956.  bsr        reverse_on          ; Turn on video reverse print.
  3957.  
  3958. no_change:
  3959.  bsr        bin_to_hex
  3960.  bsr        print_hex_sign
  3961.  lea        hexadecimal(pc), a0
  3962.  bsr        print_string        ; Print after-call value.
  3963.  bsr        print_newline
  3964.  tst.b      printer_status      ; Find out if printer is on or off.
  3965.  beq.s      _printer_off
  3966.  bsr        bold_off            ; Turn printer bold print off.
  3967. _printer_off:
  3968.  bsr        reverse_off         ; Turn video reverse print off.
  3969.  dbra       d4, print_register
  3970.  bsr        print_newline
  3971.  
  3972. wait_for_keypress:
  3973.  move.w     #8, -(sp)
  3974.  trap       #1
  3975.  addq.l     #2, sp
  3976.  
  3977.  ; Now we have to restore the LSR's registers because our printing
  3978.  ; algorithm has corrupted them.  Of course, we will want to know
  3979.  ; if this values are received by the calling source.  If they are, then
  3980.  ; the trap handler will have corrupted the calling source's registers.
  3981.  
  3982.  movem.l    registers_before(pc), d0-d7/a0-a6
  3983.  rte
  3984.  
  3985. not_desired_call:
  3986.  movea.l   preempted_handler_address(pc), a0
  3987.  jmp       (a0)                 ; JUMP TO PREEMPTED TRAP #13 HANDLER
  3988.  
  3989.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  3990.  ; passed as a longword in register D3.  Beginning with the most significant
  3991.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  3992.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  3993.  ; buffer.  The leading zero portion has been removed from this routine.
  3994.  
  3995. bin_to_hex:                      ; Converts a 32-bit binary number in D3 to
  3996.                                  ; ASCII hexadecimal.
  3997.  lea        hexadecimal(pc), a0  ; A0 is pointer to array "hexadecimal".
  3998.  lea        hex_table(pc), a1    ; A1 is pointer to array "hex_table".
  3999.  moveq      #7, d2               ; D2 is the loop counter.
  4000.  moveq      #0, d0               ; D0 is not zero.  That's what we have
  4001.                                  ; proved, so it must be cleared before use.
  4002. rotate_and_convert:
  4003.  rol.l      #4, d3               ; Rotate most significant nibble to the
  4004.                                  ; least significant nibble position.
  4005.  move.b     d3, d0               ; Copy least significant byte of D1 to D0.
  4006.  andi.b     #$F, d0              ; Mask out most significant nibble of D0.
  4007. store_digit:
  4008.  move.b     0(a1,d0.w), (a0)+    ; Store ASCII hexadecimal digit in buffer.
  4009.  dbra       d2, rotate_and_convert
  4010.  move.b     #0, (a0)             ; Terminate hexadecimal string with a null.
  4011.  rts
  4012.  
  4013. print_string:                   ; Expects address of string to be in A0.
  4014.  move.l     a0, -(sp)           ; Push address of string onto stack.
  4015.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  4016.  trap       #1                  ; GEMDOS call
  4017.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  4018.  rts
  4019.  
  4020. print_newline:                  ; Prints a carriage return and linefeed.
  4021.  pea        newline(pc)         ; Push address of string onto stack.
  4022.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  4023.  trap       #1                  ; GEMDOS call
  4024.  addq.l     #6, sp
  4025.  rts
  4026.  
  4027. print_hex_sign:
  4028.  move.w     #$24, -(sp)
  4029.  move.w     #2, -(sp)
  4030.  move.w     #3, -(sp)
  4031.  trap       #13
  4032.  addq.l     #6, sp
  4033.  rts
  4034.  
  4035. bold_on:
  4036.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  4037.  move.w     #0, -(sp)
  4038.  move.w     #3, -(sp)
  4039.  trap       #13
  4040.  addq.l     #6, sp
  4041.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  4042.  move.w     #0, -(sp)
  4043.  move.w     #3, -(sp)
  4044.  trap       #13
  4045.  addq.l     #6, sp
  4046.  rts
  4047.  
  4048. bold_off:
  4049.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  4050.  move.w     #0, -(sp)
  4051.  move.w     #3, -(sp)
  4052.  trap       #13
  4053.  addq.l     #6, sp
  4054.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  4055.  move.w     #0, -(sp)
  4056.  move.w     #3, -(sp)
  4057.  trap       #13
  4058.  addq.l     #6, sp
  4059.  rts
  4060.  
  4061. reverse_on:
  4062.  move.w     #$1B, -(sp)
  4063.  move.w     #2, -(sp)
  4064.  move.w     #3, -(sp)
  4065.  trap       #13
  4066.  addq.l     #6, sp
  4067.  move.w     #$70, -(sp)
  4068.  move.w     #2, -(sp)
  4069.  move.w     #3, -(sp)
  4070.  trap       #13
  4071.  addq.l     #6, sp
  4072.  rts
  4073.  
  4074. reverse_off:
  4075.  move.w     #$1B, -(sp)
  4076.  move.w     #2, -(sp)
  4077.  move.w     #3, -(sp)
  4078.  trap       #13
  4079.  addq.l     #6, sp
  4080.  move.w     #$71, -(sp)
  4081.  move.w     #2, -(sp)
  4082.  move.w     #3, -(sp)
  4083.  trap       #13
  4084.  addq.l     #6, sp
  4085.  rts
  4086.  
  4087.  data
  4088. hex_table:        dc.b '0123456789ABCDEF'
  4089. newline:          dc.b $D,$A,0
  4090. experiment_head:  dc.b 'EXPERIMENT 5A: System Trap #13 Corruption',$D,$A
  4091.                   dc.b '               Function = BIOS $C = Custom',$D,$A,$A,0
  4092. heading:          dc.b '       Before Call         After  Call',$D,$A,0
  4093. space:            dc.b '     ',0
  4094. printer_status:   dc.b 0
  4095. register_id:      dc.b '  Data Reg 0: $',0,'  Data Reg 1: $',0
  4096.                   dc.b '  Data Reg 2: $',0,'  Data Reg 3: $',0
  4097.                   dc.b '  Data Reg 4: $',0,'  Data Reg 5: $',0
  4098.                   dc.b '  Data Reg 6: $',0,'  Data Reg 7: $',0
  4099.                   dc.b '  Add  Reg 0: $',0,'  Add  Reg 1: $',0
  4100.                   dc.b '  Add  Reg 2: $',0,'  Add  Reg 3: $',0
  4101.                   dc.b '  Add  Reg 4: $',0,'  Add  Reg 5: $',0
  4102.                   dc.b '  Add  Reg 6: $',0,'  Add  Reg 7: $',0
  4103.  align
  4104. preempted_handler_address:  ds.l    1
  4105. trap_vector:      ds.l    1     ; Store for custom trap handler's vector.
  4106. hexadecimal:      ds.l    3     ; Output buffer.  Must be NULL terminated.
  4107. registers_before: ds.l   15, 0  ; Store this value in each before
  4108. reg_A7_before:    ds.l    1     ; array element during loading.
  4109. registers_after:  ds.l   16     ; Array for register contents after call.
  4110.                   ds.l   48     ; Stack
  4111. stack:            ds.l    1     ; Address of stack.
  4112. program_end:      ds.l    0
  4113.  end 
  4114.  
  4115.  
  4116. Figure 8.12. Program 58 Execution Results.
  4117.  
  4118.  
  4119.  
  4120.  
  4121.  
  4122.  
  4123.  
  4124.  
  4125.  
  4126.  
  4127.  
  4128.  
  4129.  
  4130.  
  4131.  
  4132.  
  4133.  
  4134.  
  4135.  
  4136.  
  4137.  
  4138.  
  4139.  
  4140.  
  4141.  
  4142.  
  4143.  
  4144.  
  4145.  
  4146.  
  4147. Program 59. Experiment 5B: Custom trap #13 handler 
  4148. corruption of calling application registers, and 
  4149. contamination of handler registers by the calling 
  4150. application.
  4151.  
  4152.  ; PROGRAM NAME: REG_TST7.S
  4153.       ; VERSION: 1.001
  4154.  
  4155.  ; Assembly Instructions:
  4156.  
  4157.  ;     Assemble in Relocatable mode and save with a TOS extension.
  4158.  
  4159.  ; Program Function: EXPERIMENT 5B.
  4160.  
  4161.  ;     Similar to REG_TST2, but this one calls a custom trap #13 handler.
  4162.  ; The handler intercepts all BIOS calls, passing all but one custom function
  4163.  ; on to the system trap #13 handler.  Since there is no BIOS function $C, I
  4164.  ; use that number for the function to be processed by the custom handler.
  4165.  
  4166.  ; Execution Instructions:
  4167.  
  4168.  ;     Execute program REG_TST6 from the desktop.  Then execute this program
  4169.  ; from the desktop or from within AssemPro.  
  4170.  
  4171.  ;     This program will call the custom trap #13 handler that must be 
  4172.  ; already resident in ram, having been established by REG_TST6.  Before the
  4173.  ; call, values that were stored in a register array, while the program was
  4174.  ; being loaded into ram, are placed in each register.  The custom trap
  4175.  ; handler will alter all registers.  We want to see if those alterations
  4176.  ; are passed back to this program.
  4177.  
  4178.  ;     The custom handler will print its before and after register contents,
  4179.  ; the relinquish processor control back to this program.
  4180.  
  4181.  ;     Immediately after the call, the content of each register is stored in
  4182.  ; a second register array.  Then the content of each register, as it was
  4183.  ; before the call, is printed.  Next, the content of the after_call array
  4184.  ; is compared to the content of the before-call array, and the values in
  4185.  ; the after-call array are printed; however, register values in the 
  4186.  ; after-call array which do not match the before-call value for each
  4187.  ; corresponding register are printed in reverse colors on the video screen.
  4188.  
  4189. program_start:                  ; Calculate program size and retain result.
  4190.  lea        program_end(pc), a0 
  4191.  movea.l    4(a7), a1       
  4192.  suba.l     a1, a0          
  4193.  
  4194. return_memory:                  ; Return unused memory to operating system.
  4195.  move.l     a0, -(sp)           ; Store total program length in stack.
  4196.  move.l     a1, -(sp)           ; Store basepage address in stack.
  4197.  move.w     d0, -(sp)           ; Dummy value, can be anything.
  4198.  move.w     #$4a, -(sp)         ; Function = m_shrink = GEMDOS $4A.
  4199.  trap       #1                  ; GEMDOS call.
  4200.  adda.l     #$C, sp             ; Reset stack pointer to top of stack.
  4201.  
  4202. get_printer_status:             ; Get and save printer status.
  4203.  move.w     #0, -(sp)           ; Device = printer.
  4204.  move.w     #8, -(sp)           ; Function = BIOS $8 = bcostat.
  4205.  trap       #13                 ; If printer is on, -1 will be returned
  4206.  addq.l     #4, sp              ; in D0, 0 will be returned otherwise.
  4207.  move.b     d0, printer_status
  4208.  
  4209. place_values_in_registers:
  4210.  movem.l    registers_before(pc), d0-d7/a0-a6
  4211.  lea        stack(pc), a7
  4212.  move.l     a7, reg_A7_before
  4213.  
  4214. make_trap_call:
  4215.  move.l    #0, -(sp)
  4216.  move.w    #$C, -(sp)
  4217.  trap      #13
  4218.  addq.l    #6, sp
  4219.  
  4220. store_after_call_contents:
  4221.  movem.l    d0-d7/a0-a7, registers_after
  4222.  
  4223.  ; The following algorithm prints the before value for a register, then,
  4224.  ; on the same line, it prints the after value.  This provides a more
  4225.  ; compact display, and one in which it is easier to compare the before
  4226.  ; and after values.
  4227.  
  4228. print_all_contents:
  4229.  bsr        print_newline
  4230.  lea        experiment_head(pc), a0
  4231.  bsr        print_string
  4232.  lea        heading(pc), a0
  4233.  bsr        print_string
  4234.  lea        register_id(pc), a3
  4235.  lea        registers_before(pc), a4
  4236.  lea        registers_after(pc), a5
  4237.  move.l     #15, d4
  4238. print_register:
  4239.  move.l     (a4)+, d3           ; Convert content from binary to hex.
  4240.  move.l     d3, d5              ; Save for comparison with after-value.
  4241.  bsr        bin_to_hex
  4242.  movea.l    a3, a0
  4243.  bsr        print_string        ; Print register number.
  4244.  add.l      #16, a3
  4245.  lea        hexadecimal(pc), a0
  4246.  bsr        print_string        ; Print before-call register content.
  4247.  lea        space(pc), a0
  4248.  bsr        print_string
  4249. compare_and_print:
  4250.  move.l     (a5)+, d3           ; Load after_call value.
  4251.  cmp.l      d5, d3              ; Compare before_call to after_call.
  4252.  beq.s      no_change           ; Branch if no change occurred.
  4253.  tst.b      printer_status      ; Find out if printer is on or off.
  4254.  beq.s      printer_off
  4255.  bsr        bold_on             ; Turn on printer bold print.
  4256. printer_off:
  4257.  bsr        reverse_on          ; Turn on video reverse print.
  4258.  
  4259. no_change:
  4260.  bsr        bin_to_hex
  4261.  bsr        print_hex_sign
  4262.  lea        hexadecimal(pc), a0
  4263.  bsr        print_string        ; Print after-call value.
  4264.  bsr        print_newline
  4265.  tst.b      printer_status      ; Find out if printer is on or off.
  4266.  beq.s      _printer_off
  4267.  bsr        bold_off            ; Turn printer bold print off.
  4268. _printer_off:
  4269.  bsr        reverse_off         ; Turn video reverse print off.
  4270.  dbra       d4, print_register
  4271.  bsr        print_newline
  4272.  
  4273. wait_for_keypress:
  4274.  move.w     #8, -(sp)
  4275.  trap       #1
  4276.  addq.l     #2, sp
  4277.  
  4278. terminate:
  4279.  move.w     #0, -(sp)
  4280.  trap       #1
  4281.  
  4282.  ; The binary to ASCII hexadecimal conversion routine expects a number to be
  4283.  ; passed as a longword in register D3.  Beginning with the most significant
  4284.  ; nibble (a nibble = four bits), each nibble is converted to its ASCII
  4285.  ; hexadecimal equivalent and stored in "hexadecimal", a null terminated
  4286.  ; buffer.  The leading zero portion has been removed from this routine.
  4287.  
  4288. bin_to_hex:                      ; Converts a 32-bit binary number in D3 to
  4289.                                  ; ASCII hexadecimal.
  4290.  lea        hexadecimal(pc), a0  ; A0 is pointer to array "hexadecimal".
  4291.  lea        hex_table(pc), a1    ; A1 is pointer to array "hex_table".
  4292.  moveq      #7, d2               ; D2 is the loop counter.
  4293.  moveq      #0, d0               ; D0 is not zero.  That's what we have
  4294.                                  ; proved, so it must be cleared before use.
  4295. rotate_and_convert:
  4296.  rol.l      #4, d3               ; Rotate most significant nibble to the
  4297.                                  ; least significant nibble position.
  4298.  move.b     d3, d0               ; Copy least significant byte of D1 to D0.
  4299.  andi.b     #$F, d0              ; Mask out most significant nibble of D0.
  4300. store_digit:
  4301.  move.b     0(a1,d0.w), (a0)+    ; Store ASCII hexadecimal digit in buffer.
  4302.  dbra       d2, rotate_and_convert
  4303.  move.b     #0, (a0)             ; Terminate hexadecimal string with a null.
  4304.  rts
  4305.  
  4306. print_string:                   ; Expects address of string to be in A0.
  4307.  move.l     a0, -(sp)           ; Push address of string onto stack.
  4308.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  4309.  trap       #1                  ; GEMDOS call
  4310.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  4311.  rts
  4312.  
  4313. print_newline:                  ; Prints a carriage return and linefeed.
  4314.  pea        newline(pc)         ; Push address of string onto stack.
  4315.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  4316.  trap       #1                  ; GEMDOS call
  4317.  addq.l     #6, sp
  4318.  rts
  4319.  
  4320. print_hex_sign:
  4321.  move.w     #$24, -(sp)
  4322.  move.w     #2, -(sp)
  4323.  move.w     #3, -(sp)
  4324.  trap       #13
  4325.  addq.l     #6, sp
  4326.  rts
  4327.  
  4328. bold_on:
  4329.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  4330.  move.w     #0, -(sp)
  4331.  move.w     #3, -(sp)
  4332.  trap       #13
  4333.  addq.l     #6, sp
  4334.  move.w     #$47, -(sp)         ; Your printer's code may be different.
  4335.  move.w     #0, -(sp)
  4336.  move.w     #3, -(sp)
  4337.  trap       #13
  4338.  addq.l     #6, sp
  4339.  rts
  4340.  
  4341. bold_off:
  4342.  move.w     #$1B, -(sp)         ; Your printer's code may be different.
  4343.  move.w     #0, -(sp)
  4344.  move.w     #3, -(sp)
  4345.  trap       #13
  4346.  addq.l     #6, sp
  4347.  move.w     #$48, -(sp)         ; Your printer's code may be different.
  4348.  move.w     #0, -(sp)
  4349.  move.w     #3, -(sp)
  4350.  trap       #13
  4351.  addq.l     #6, sp
  4352.  rts
  4353.  
  4354. reverse_on:
  4355.  move.w     #$1B, -(sp)
  4356.  move.w     #2, -(sp)
  4357.  move.w     #3, -(sp)
  4358.  trap       #13
  4359.  addq.l     #6, sp
  4360.  move.w     #$70, -(sp)
  4361.  move.w     #2, -(sp)
  4362.  move.w     #3, -(sp)
  4363.  trap       #13
  4364.  addq.l     #6, sp
  4365.  rts
  4366.  
  4367. reverse_off:
  4368.  move.w     #$1B, -(sp)
  4369.  move.w     #2, -(sp)
  4370.  move.w     #3, -(sp)
  4371.  trap       #13
  4372.  addq.l     #6, sp
  4373.  move.w     #$71, -(sp)
  4374.  move.w     #2, -(sp)
  4375.  move.w     #3, -(sp)
  4376.  trap       #13
  4377.  addq.l     #6, sp
  4378.  rts
  4379.  
  4380.  data
  4381. hex_table:        dc.b '0123456789ABCDEF'
  4382. newline:          dc.b $D,$A,0
  4383. experiment_head:  dc.b 'EXPERIMENT 5B: Custom Trap #13 Corruption',$D,$A
  4384.                   dc.b '               Function = BIOS $C = Custom',$D,$A,$A,0
  4385. heading:          dc.b '       Before Call         After  Call',$D,$A,0
  4386. space:            dc.b '     ',0
  4387. printer_status:   dc.b 0
  4388. register_id:      dc.b '  Data Reg 0: $',0,'  Data Reg 1: $',0
  4389.                   dc.b '  Data Reg 2: $',0,'  Data Reg 3: $',0
  4390.                   dc.b '  Data Reg 4: $',0,'  Data Reg 5: $',0
  4391.                   dc.b '  Data Reg 6: $',0,'  Data Reg 7: $',0
  4392.                   dc.b '  Add  Reg 0: $',0,'  Add  Reg 1: $',0
  4393.                   dc.b '  Add  Reg 2: $',0,'  Add  Reg 3: $',0
  4394.                   dc.b '  Add  Reg 4: $',0,'  Add  Reg 5: $',0
  4395.                   dc.b '  Add  Reg 6: $',0,'  Add  Reg 7: $',0
  4396.  align
  4397.  
  4398.  ;bss MAJOR NOTE: Do not use the bss assembler op here.  If you do, the
  4399.  ;                value $44415441 will NOT be loaded into the elements
  4400.  ;                of the registers_before array.
  4401.  
  4402. trap_vector:      ds.l    1     ; Store for custom trap handler's vector.
  4403. hexadecimal:      ds.l    3     ; Output buffer.  Must be NULL terminated.
  4404. registers_before: ds.l   15, $44415441 ; Store this value in each before
  4405. reg_A7_before:    ds.l    1            ; array element during loading.
  4406. registers_after:  ds.l   16     ; Array for register contents after call.
  4407.                   ds.l   48     ; Program stack.
  4408. stack:            ds.l    1     ; Address of program stack.
  4409. program_end:      ds.l    0
  4410.  end
  4411.  
  4412.  
  4413. Figure 8.13. Program 59 Execution Results.
  4414.  
  4415.  
  4416.  
  4417.  
  4418.  
  4419.  
  4420.  
  4421.  
  4422.  
  4423.  
  4424.  
  4425.  
  4426.  
  4427.  
  4428.  
  4429.  
  4430.  
  4431.  
  4432.  
  4433.  
  4434.  
  4435.  
  4436.  
  4437.  
  4438.  
  4439.  
  4440.  
  4441.  
  4442.  
  4443.  
  4444. Conclusion
  4445.  
  4446.      We have begun our journey into the belly of the beast.  
  4447. Learning to redirect the activities of the operating system 
  4448. is one of the more rewarding programming experiences.  In 
  4449. the next chapter, we will become more involved with this 
  4450. process, and I am sure that you will be delighted with the 
  4451. examples I have chosen.  However, I must impress you, not 
  4452. with the expanse of my knowledge, but, rather, with its 
  4453. limitations.
  4454.      I encourage you to perform your own experiments, using 
  4455. reference material when you can, exploring the unknown when 
  4456. you can't.  The ST specific magazines are an especially 
  4457. valuable source of fresh ideas and information.  In 
  4458. addition, the AssemPro debugger is a valuable tool that you 
  4459. can use to explore programs written by others.  Furthermore, 
  4460. do not neglect the other programming languages as sources 
  4461. for ideas.  Remember, no matter how much information I am 
  4462. able to pass on to you, it is merely a droplet.
  4463.  
  4464.