home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 5 / DATAFILE_PDCD5.iso / utilities / f / forthmac / !Forthmacs / docs / ascii / armastut < prev    next >
Encoding:
Text File  |  1997-05-01  |  18.1 KB  |  456 lines

  1.  
  2. Assembler Tutorial
  3. ******************
  4.  
  5. This chapter explains how to use the RISC OS Forthmacs ARM assembler in order 
  6. to create short machine language code sequences.  This chapter is a companion 
  7. to the "ARM Assembler" chapter.  That chapter describes the syntax of 
  8. individual assembly language instructions.  This chapter addresses "higher 
  9. level" issues, such as how to begin and end the assembly process and how to 
  10. communicate arguments and result between Forth and assembly language.  
  11.  
  12.  
  13. Motivation
  14. ==========
  15.  
  16. For nearly all debugging jobs, writing assembly language is unnecessary.  Test 
  17. loops can be usually be written more quickly and easily in high-level Forth, 
  18. and will execute quickly enough to get the job done.  
  19.  
  20. However, in some cases the ultimate in speed is needed for certain critical 
  21. operations, and assembly language may be the best way to go.  In other cases, 
  22. very specific combinations of machine instructions may exhibit problem 
  23. behavior, and those combinations may need to be reproduced.  Finally, some 
  24. maintainers of the RISC OS Forthmacs system software itself may need to 
  25. understand the assembler.  
  26.  
  27.  
  28. Assumptions
  29. ===========
  30.  
  31. The chapter assumes that you already understand the ARM instruction set, 
  32. including such issues as processor modes, interrupts and registers sets.  If 
  33. not, you should first study a ARM reference, such as the manual published by 
  34. the chip manufacturer.  
  35.  
  36. Please note the sysntax of this ARM assembler, it uses - as most Forth 
  37. assemblers - the operand first - operator last syntax.  
  38.  
  39.  
  40. Example: a simple "code word"
  41. =============================
  42.  
  43. Here is a very simple assembly language program.  It adds "1" to the contents 
  44. of a register then returns to the Forth interpreter.  This register r10 holds 
  45. the top of stack value.  
  46.  
  47.     code addone  ( n -- n+1 )
  48.     r10 r10  1 # add
  49.     c;
  50.  
  51. To execute it and display the result, you would type, for example, 
  52.  
  53.     5 addone .
  54.  
  55. Here's what is happening, line by line: 
  56.  
  57.     code addone  ( n -- n+1 )
  58.  
  59. code is a "defining word"; it creates a new command which can be executed by 
  60. typing its name.  The name of the new command in this case is ADDONE .  The 
  61. name could have been anything; I have chosen the name ADDONE because it 
  62. describes the action of the program.  You may already be familiar with another 
  63. Forth defining word " : or COLON ". ":" also creates a new command; the 
  64. difference between code and ":" is that ":" creates a new command whose 
  65. behavior is described by a sequence of other Forth commands, whereas code 
  66. creates a new command whose behavior is described by a sequence of assembly 
  67. language instructions.  After code creates the new command, it starts the 
  68. assembler so that assembly language instructions may be entered.  
  69.  
  70. The stuff inside the parentheses is a comment; this particular comment 
  71. indicates that the new command expects one argument ("n") on the stack before 
  72. the word is executed, and after the command is executed, one result ("n+1") is 
  73. left on the stack.  The comment is optional, but its inclusion is strongly 
  74. recommended.  
  75.  
  76.     r10 r10  1 # add
  77.  
  78. This is the assembly language instruction which defines the action of the new 
  79. command.  As you will recall from the "ARM Assembler" chapter, the 
  80. RISC OS Forthmacs assembler syntax has the destination register first, 
  81. followed by the source operand(s), followed by the operation name.  So, in 
  82. this case, the source operands are the global register r10 and the immediate 
  83. number 1, the destination operand is the global register r10, and the 
  84. operation is add, i.e.  1 is added to the contents of register r10, and the 
  85. result is placed back in register r10.  
  86.  
  87.     c;
  88.  
  89. c; terminates the definition of a code definition.  At the end of the 
  90. instructions you have assembled, c; automatically appends one machine 
  91. instruction, its effect is to return to Forth after the user-specified 
  92. instructions have been executed.  
  93.  
  94.     5 addone .
  95.  
  96. In order to invoke the new command, we enter the number 5 on the Forth stack, 
  97. type the name of the command ADDONE , and then display the result by typing 
  98. the print command "." .  
  99.  
  100. Perhaps you now wonder how the number got off the Forth stack and into the 
  101. register r10, and afterwards how the number got out of r10 and back onto the 
  102. Forth stack.  The answer is simple: the top element of the Forth stack is 
  103. always (!) kept in r10 , so no movement was necessary.  That is why I chose 
  104. r10 for the register in this example.  
  105.  
  106.  
  107. Register Usage in Forth
  108. =======================
  109.  
  110. To use the assembler effectively, you need to know which registers are 
  111. available for use, and which of them must be left alone.  Here are the rules: 
  112.  
  113. r8, r9, r12, and r14 are used internally by the Forth interpreter or operating 
  114. system, their values must be left alone (otherwise the system will crash).  
  115.  
  116. r10 contains the top of the Forth stack.  It is used for passing arguments and 
  117. results back and forth between Forth and assembly language.  
  118.  
  119. r13 contains a pointer to a memory area containing the rest of the Forth stack 
  120. (all elements other than the topmost one).  That stack area is used for extra 
  121. arguments and results.  The section entitled "Stack Usage" tells you more 
  122. about managing the stack area.  
  123.  
  124. r0 - r6 may be used freely within assembly language code sequences.  Forth 
  125. does not depend on the contents of these registers.  However, some Forth 
  126. commands do use these registers as scratch registers, so your code should not 
  127. attempt to keep important values in these registers from one time to the next.  
  128. While your code is being executed, Forth will not change the contents of any 
  129. of these registers, so you can depend on them for the duration of your 
  130. assembly language sequence.  When your code finishes and returns to Forth, the 
  131. next time that you execute your code the register values may have changed.  
  132.  
  133. You can find more information about this subject in the "ARM Assembler" and 
  134. "Forthmacs Implementation" chapters.  
  135.  
  136. While your machine code is executing, it will run at the full speed of the 
  137. system, without any interference or overhead imposed by RISC OS Forthmacs.  
  138. RISC OS Forthmacs does not itself use interrupts, so the processor will 
  139. execute exactly the sequence of instructions which you have coded.  It is 
  140. possible that other software in the system may have set up some interrupts, 
  141. but that is beyond the control of RISC OS Forthmacs.  
  142.  
  143.  
  144. Disassembler
  145. ============
  146.  
  147. The RISC OS Forthmacs disassembler may be used to review the assembly language 
  148. you have created: 
  149.  
  150.     see addone
  151.  
  152. The result will look something like this: 
  153.     code addone
  154.     (  1e878 )  add     r10,r10,#1
  155.     (  1e87c )  ldr     pc,[r8],#4
  156.  
  157.  
  158. The numbers along the left hand side are the addresses at which the various 
  159. instructions appear.  The addresses shown here will almost certainly be 
  160. different from the addresses that you see.  
  161.  
  162. You will notice that even though our example contained only one assembly 
  163. language instruction the disassembler shows 1 extra instruction.  This extra 
  164. instruction was automatically assembled by the c; command.  Their purpose is 
  165. to return control to Forth after the assembly language sequence has finished 
  166. its execution (this is called the next instruction).  
  167.  
  168. The see command reads the name of a Forth command (in this case "addone"), 
  169. determines what type of command it is (in this case "code ", meaning that the 
  170. command's behavior was defined by the assembler), and then displays a 
  171. reconstruction of the source code for that command.  see also works for 
  172. "colon" definitions, whose behaviour is defined in Forth instead of in 
  173. assembly language.  For an example of this, type "see find".  
  174.  
  175. Many of the normal Forth commands are defined in assembly language, and see 
  176. can be used to look at how they are implemented.  For example, type "see @" to 
  177. see how the Forth "@" operator works (pronounced fetch, this operator takes an 
  178. address from the top of the stack, reads the 32-bit contents of that address, 
  179. and puts those contents back on top of the stack).  You should try this right 
  180. now and make sure you understand how it works.  Note that the last 
  181. instructions of "@" is exactly the same as the last instruction of "addone".  
  182. Every code definition in RISC OS Forthmacs ends with these same three 
  183. instructions.  
  184.  
  185. see automatically locates the address where the code for particular command 
  186. begins.  That address was allocated by code when the new command was defined.  
  187. The disassembler can also be used to inspect machine code beginning at 
  188. arbitrary addresses, not only that code which is created by code .  Suppose 
  189. that you know there is some code starting at address 100000 and you wish to 
  190. look at it: 
  191.  
  192.     100000 dis
  193.  
  194. On your system, this example probably won't work exactly as shown because your 
  195. system may not have any code at address 100000 (in fact, it may not even have 
  196. any memory there.  The main point, though, is that you type the address of the 
  197. code you wish to disassemble, followed by "dis".  
  198.  
  199. The disassembler will continue until it reaches a "definition ending" 
  200. instruction, or until you stop it by typing the character "q", for "quit".  It 
  201. will also pause at the end of a screen and prompt you for a continuation 
  202. character.  
  203.  
  204. After the disassembler has stopped, you can make it continue where it left off 
  205. by typing +dis 
  206.  
  207.  
  208. Setting the Starting Address
  209. ============================
  210.  
  211. In most cases, you won't need to specify a starting address for the code you 
  212. assemble.  When you use the code defining word to begin assembling, 
  213. RISC OS Forthmacs will find some appropriate memory for you and assemble your 
  214. code there ( at here). You can then locate the memory RISC OS Forthmacs has 
  215. chosen by using the see command to disassemble the code, looking at the 
  216. addresses displayed alongside the machine instructions.  
  217.  
  218. If you really need to assemble at a specific address, you can do so as follows 
  219. (Note: in nearly all cases, this technique is unnecessary; very rarely does it 
  220. matter where exactly you locate a bit of code, and allowing RISC OS Forthmacs 
  221. to allocate the memory for you is sufficient and convenient).  
  222.  
  223. Set the dp by 
  224.     here @
  225.     your-adr dp !
  226.     code demo
  227.            ...... c;
  228.     here !
  229.  
  230.  
  231. Conditional branches
  232. ====================
  233.  
  234. In order to implement conditional operations and loops, most assemblers 
  235. provide branch instructions and labels.  RISC OS Forthmacs has branches and 
  236. labels too, but it also has a much better way, which eliminates most of the 
  237. troublesome aspects of coding conditionals and loops in assembly language.  
  238. The RISC OS Forthmacs way is called "structured conditionals".  For example, 
  239. suppose we want to test a condition and execute some code only if the 
  240. condition is true.  Specifically, we want to compare r0 and r1, and execute 
  241. some code only if r0 is less than r1 .  
  242.  
  243.     Traditional assembler:
  244.     
  245.                  cmp   r0, r1
  246.                  bge   temp
  247.                     ..some code we want to conditionally execute
  248.          temp:
  249.  
  250.          Forthmacs assembler with structured conditionals:
  251.     
  252.                  r0 r1  cmp
  253.                  < if
  254.                     ..some code we want to conditionally execute..
  255.                  then
  256.  
  257. As you can see, RISC OS Forthmacs eliminates the need to mentally reverse the 
  258. sense of the comparison, eliminates the need to invent and keep track of label 
  259. names, and uses conventional mathematical comparison symbols (e.g.  "<"), 
  260. rather then alphabetic mnemonics.  The complete set of comparison symbols is 
  261. given in the "ARM Assembler" chapter.  
  262.  
  263. The "if ..  then" construct can also include an "else" clause: 
  264.  
  265.                  r0 r1 s cmp  \ the s is optional
  266.                  < if
  267.                     ..code to execute if r0 < r1..
  268.                  else
  269.                     ..code to execute if r0 >= r1..
  270.                  then
  271.  
  272. Of course, the assembler actually generates conditional branch instructions 
  273. because that's what the hardware supports directly, but RISC OS Forthmacs 
  274. takes care of the "bookkeeping" for you.  
  275.  
  276. Another way would be to use the conditional instructions offered by the ARM 
  277. cpu.  
  278.  
  279.                  r0  r1 cmp
  280.                  xx xx  lt xxx
  281.                  yy yy  ge xxx
  282.  
  283.  
  284.  
  285. Delayed Branches
  286. ================
  287.  
  288. ARM doesn't uses delayed branches at all, so don't worry.  
  289.  
  290.  
  291. Loops
  292. =====
  293.  
  294. RISC OS Forthmacs structured conditionals also have features for easily 
  295. creating loops.  Here is a loop which executes forever: 
  296.  
  297.                  Source                  Generates
  298.     
  299.                  begin                   Label1:
  300.                     top  r0 ) ldr              ldr  r10,[r10,#0]
  301.                  again                         b Label1
  302.  
  303. This code assumes that the r10 register (top of stack, remember?) contains the 
  304. address of a memory location, and the contents of that memory location is 
  305. continuously read into the r0 register.  This is an infinite loop; it won't 
  306. stop until the system is reset, or power cycled, or externally interrupted in 
  307. some way.  
  308.  
  309. Suppose we want the loop to execute 9 times then quit: 
  310.  
  311.   r1  9 #    mov
  312.   begin
  313.      r0   top ) ldr
  314.      r1   r1 1 # s sub
  315.   <= until
  316.  
  317.  
  318. We continue to loop "until" r1 <= 1 .  
  319.  
  320. Finally, here's an example where we perform a test at the top of the loop 
  321. rather than at the bottom, illustrating "while": 
  322.  
  323.   r1  9 #     mov
  324.   begin
  325.       r1 r1 1 s sub
  326.   > while
  327.       r0  top ) ldr
  328.   repeat
  329.  
  330.  
  331. This loop continues to execute "while" r11 > 1, and the "repeat" sends it back 
  332. to the "begin".  
  333.  
  334. Structured conditionals and loops nest in the expected manner, to an arbitrary 
  335. depth.  For instance, a "begin ..  until" can be completely contained within 
  336. an "if ..  then", which itself may be contained within a "begin ..  while ..  
  337. repeat".  
  338.  
  339.  
  340. Scope Loops - Assembler vs. Forth
  341. =================================
  342.  
  343. You can use assembly language for creating scope loops, but it is usually 
  344. preferable to write them in Forth, because the Forth version is usually easier 
  345. to write, easier to read, and easier to debug.  The one advantage of an 
  346. assembly language loop is that it is tighter.  However this rarely matters.  
  347. For comparison, suppose that you want to continually read location 1000 so 
  348. that you can observe the action on an oscilloscope.  This is how you would do 
  349. it in assembly language: 
  350.  
  351.          code test
  352.          r0 th 1000 # mov
  353.             begin
  354.               r1  r0 ) ldr
  355.             again
  356.  
  357. Here's how you would do the same thing in Forth: 
  358.  
  359.     begin  1000 @ drop  again
  360.  
  361. Additionally, the Forth version may be easily adapted to stop looping as soon 
  362. as a key is typed: 
  363.  
  364.     begin  1000 @ drop  key? until
  365.  
  366. More importantly, many of today's complicated chips require fairly extensive 
  367. initialization sequences in order to configure them to the correct operating 
  368. mode.  Such code is much easier to write and debug in Forth, because you can 
  369. "try things out" by typing commands at the keyboard, the looking at the 
  370. registers to see what happened.  
  371.  
  372. A set of simple Forth commands sufficient to do most hardware debugging jobs 
  373. can easily be described on a single page, and many engineers and technicians 
  374. have learned enough Forth in 30 minutes to be able to write sophisticated 
  375. diagnostics for complicated hardware.  
  376.  
  377.  
  378. Stack Usage
  379. ===========
  380.  
  381. A previous example has shown how to access the top element on the stack which 
  382. is stored in r10.  Things get a little more complicated if more than 1 stack 
  383. argument is needed.  Remember that the top of the stack is stored in r10, and 
  384. subsequent stack items are stored in a memory area whose address is contained 
  385. in r13.  For convenience, the assembler provides alternate names for r10 and 
  386. r13, reflecting the use of these registers for the stack.  r10 is also known 
  387. as top (Top of Stack), and r13 is also known as sp (Stack Pointer).  
  388.  
  389. The basic rules for the Forth stack are: 
  390.  
  391. a) Upon entry to a code definition (assembly language), the top of the stack 
  392. is contained in top. The next item on the stack is in the memory location 
  393. whose address is contained in sp. The item after that is in memory at SP+4 , 
  394. the next at SP+8 , etc.  Note that successive stack items are 4 bytes 
  395. (32-bits) apart.  
  396.  
  397. b) A definition may modify the stack contents, and upon exit from the 
  398. definition the new top of the stack should be in top, and the next item should 
  399. be in memory at that address contained in sp. 
  400.  
  401. c) Assembly code should not access memory at negative offsets from sp. This 
  402. restriction safeguards against problems in an interrupt-driven environment, in 
  403. case the same stack happens to be used for interrupt handlers.  
  404.  
  405. If items are removed from the stack by a code definition, care must be taken 
  406. to make sure the correct top of stack value is left in top. Also remember that 
  407. the RISC OS Forthmacs assembler provides macros to assist in managing the 
  408. stack.  Here are some examples; study them carefully: 
  409.  
  410. code and        (s n1 n2 -- n3 )
  411.                 r0      sp      pop
  412.                 top     top     r0 and c;
  413. code min        (s n1 n2 -- n1|n2 )
  414.                 r0      sp      pop
  415.                 top     r0      cmp
  416.                 top     r0      gt mov c;
  417. code drop       (s n1 n2 -- n1 )
  418.                 top     sp      pop c;
  419. code dup        (s n1 -- n1 n1 )
  420.                 top     sp      push c;
  421. code 1+         (s n -- n+1 )    top 1     incr c;
  422. code @          (s a_adr -- n )
  423.                 top     top )   ldr c;
  424. \ a somewhat optimized fill
  425. code fill       (s adr cnt char -- )
  426.                 r2      top     top  8 #lsl orr
  427.                 r0 r1 top 3 sp ia!  ldm \ r0-cnt r1-adr r2-data
  428.                 r0      4 #     cmp
  429.   gt if
  430.         begin   r3      r1      3 # s and
  431.                 r0      1       ne decr
  432.                 r2      r1 byte )+ ne str
  433.         eq until
  434.                 r0      8       s decr
  435.                 r2      r2      r2  10 #lsl orr
  436.                 r3      r2      mov
  437.         begin   r2 r3 2 r1 ia!  ge stm
  438.                 r0      8       ge s decr
  439.         lt until
  440.                 r0      4       s incr
  441.                 r2      r1 )+   ge str
  442.                 r0      4       lt decr
  443.   then
  444.         begin   r0      1       s decr
  445.                 r2      r1 byte )+ ge str
  446.         lt until c;
  447. code >name      \ (s cfa -- nfa )
  448.                 top     1       decr    \ skip flag byte
  449.         begin   r0      top byte -( ldr
  450.                 r0      0 #     cmp
  451.         ne until
  452.         begin   r0      top byte -( ldr
  453.                 r0      20 #    cmp
  454.         lt until c;
  455.  
  456.