home *** CD-ROM | disk | FTP | other *** search
/ APDL Public Domain 1 / APDL_PD1A.iso / program / language / forthmacs / !Forthmacs / docs / ascii / RO_impleme < prev    next >
Encoding:
Text File  |  1994-07-29  |  16.2 KB  |  448 lines

  1.  
  2.  
  3. Forthmacs Implementation
  4. ************************
  5.  
  6. This chapter describes how Risc-OS Forthmacs implements the Forth 
  7. virtual machine on the ARM processors.  The chapter assumes that you 
  8. have a fairly good knowledge of conventional Forth implementations; it 
  9. does not attempt to be a tutorial on how Forth works.  
  10.  
  11.  
  12. Dialect
  13. =======
  14.  
  15. Risc-OS Forthmacs is an implementation of the Forth-83 standard, with a 
  16. few exceptions.  It is a descendent of the public-domain F83 
  17. implementation by Laxen and Perry, and contains most of the F83 
  18. extensions, as well as many new ones.  It is compatible with the other 
  19. implementations for Sun-68k, Sparc, Atari, Macintosh and OS-9 computers.  
  20.  
  21.  
  22. Stack Width and Addressing
  23. ==========================
  24.  
  25. Risc-OS Forthmacs deviates from the 83 Standard in the width of the 
  26. stack.  Forth 83 specifies that stack items are 16-bit numbers, and that 
  27. the address space is 64K.  This wastes much of the power of modern CPUs 
  28. and is almost impossible to implement on ARM based computers.  
  29.  
  30. In Risc-OS Forthmacs, all stack items as well as memory cells are 32-bit 
  31. wide, remember this when writing portable programs.  
  32.  
  33. The address could conceivably grow to 2 to the 32nd power (4 gigabytes), 
  34. but this is restricted by the current CPU/MMU versions to 16 MBytes.  
  35. 16-bit or 2-byte memory accesses are not supported any longer and must 
  36. be emulated if necessary.  
  37.  
  38. Note: word accesses are simulated by two byte accesses, take care about 
  39. interrupts occurring here! 
  40.  
  41. The current ARM MMUs don't support non-aligned memory accesses.  NOTE: 
  42. They don't abort or run any exception vector but just do something not 
  43. clearly defined and CPU dependent.  Take care of this, it took me hours 
  44. to find a bug! 
  45.  
  46. All accesses must be one of: 
  47.  
  48. 1) byte-wide access to any address in the address area 
  49.  
  50. 2) cell-wide ( 32-bit ) access to any aligned address 
  51.  
  52.  
  53. Both stacks are pre-decrementing/post-incrementing.  The parameter stack 
  54. holds its top-of-stack in the top-register r10, this allowes much faster 
  55. code definitions because of the CPUs load-and-store architecture.  
  56.  
  57.  
  58. Register Usage
  59. ==============
  60.  
  61.  
  62.     r7                              rx  reserved for later usage
  63.     r8      instruction pointer     ip
  64.     r9      user area pointer       up
  65.     r10     top-of-stack register   top
  66.     r11     returnstack pointer     rp
  67.     r12     Risc-Os frame pointer   fp  never use this!
  68.     r13     stack pointer           sp
  69.     r14     link register           lk
  70.     r15     pc + status + flags     pc
  71.     r15     sr                      sr  hold the flags part of r15
  72.  
  73. Note: In future CPU Versions the internal structure of the pc-register 
  74. might be different, it seems to be better, to imagine pc and status 
  75. register as two registers.  The hardware-errors and the `.registers` 
  76. instruction know about this.  
  77.  
  78. r0, r1, r2, r3, r4, r5, and r6 are available for use within code 
  79. definitions.  Don't try to use them for permanent storage, because they 
  80. are used by many code words with no attempt to preserve the previous 
  81. contents.  
  82.  
  83.  
  84. Inner <address> Interpreter
  85. ===========================
  86.  
  87. The inner interpreter `next` is direct threaded, post incrementing.  The 
  88. compilation address of all definitions contain machine code to be 
  89. executed, not a pointer.  Each `code` definition ends with the `next` 
  90. code, assembled in-line.  The `next` code is: 
  91.          pc      ip )+   ldr
  92. This means: Load the programm-counter `pc` ( don't affect the CPU status 
  93. ) from the 4-byte cell pointed to by the instruction pointer ip, 
  94. postincrement the instruction-pointer.  So the `next` is only one CPU 
  95. instruction and very fast.  It is much faster than 
  96.          address  dolink branch
  97.          ...
  98.          pc link mov
  99. constructions because of only one pipeline reload per `next.` But on the 
  100. other hand, there is definitly a larger overhead for calling 
  101. secondaries.  
  102.  
  103. For discussions about subroutine threaded ( macro extended ) versus 
  104. threaded code implementations see the Forth literature.  Generally 
  105. macros bring some advantage in execution speed but give less information 
  106. about the code itself, so debuggers are less usefull.  The penalty for 
  107. direct threaded code is very hard to predict, it depends very much on 
  108. the type of application.  Something like 50% sounds reasonable, so 
  109. optimizing the bottlenecks could bring large advantages.  The 'runtimer ' 
  110. utilities might help you doing this.  
  111.  
  112. The assembler macro `c;` assembles the `next` instruction and ends 
  113. assembling by `end-code.` A fast conditional next can be done by 
  114.          ...
  115.          r2 0 cmp
  116.          eq next
  117.          ...
  118.  
  119.  
  120. Other Definitions
  121. =================
  122.  
  123. Any word that is not a code definition contains a branch+link 
  124. instruction at the code-field, this makes a relative branch to an 
  125. inline-address and saves the pc+sr to the lk register.  
  126.          runtime-addr    dolink branch
  127. The inline address points to a code fragment (headerless in most cases) 
  128. that implements the run-time action of the word.  The parameter field 
  129. starts just after this branch+link instruction and can be found by 
  130. clearing the flags in the link register like this: 
  131.          r0 lk    th fc000003 #   bic
  132.          r0  get-link
  133.  
  134. The run-time codes may have to push the top-register to the stack, save 
  135. the return pointer to the return-stack and set the instruction or stack 
  136. pointer to the parameter field address.  All standard runtime codes 
  137. (those of variables, constants, colon definitions, user variables ...) 
  138. have been optimized for best cache-hit rates on ARM3/6 machines.  
  139.  
  140. Note: `word-type` ( cfa -- addr ) finds the address of the words runtime 
  141. code in this implementation.  
  142.  
  143.  
  144. Colon definitions
  145. =================
  146.  
  147. The runtime code: 
  148.     mlabel docolon  assembler
  149.          ip      rp      push
  150.          ip      get-link c;
  151. The body of a Colon Definition starts 4 bytes after the compilation 
  152. address.  The body contains a list of compilation addresses of other 
  153. words.  Each such compilation address is a 32-bit number which is an 
  154. absolute address.  
  155.  
  156.  
  157. Variable
  158. ========
  159.  
  160. The Parameter Field of a `variable` contains a 32-bit number which is 
  161. the value of the variable.  The runtime code: 
  162.     mlabel dovariable  assembler
  163.          top     sp      push
  164.          top     get-link c;
  165.  
  166.  
  167. Constants
  168. =========
  169.  
  170. The Parameter Field of a `constant` contains the 32-bit value of the 
  171. constant.  The runtime code: 
  172.     mlabel doconstant  assembler
  173.          top     sp      push
  174.          r0      get-link
  175.          top     r0 )    ldr c;
  176.  
  177.  
  178. User Variables
  179. ==============
  180.  
  181. The value of a `user` variable is stored in the `user` area as a 32-bit 
  182. number.  The Parameter Field of a user variable contains a 32-bit offset 
  183. into the user area of the current task.  r8 contains the base address of 
  184. the current user area.  r8 is symbolically defined as `up` in the 
  185. assembler.  The runtime code: 
  186.     mlabel douser  assembler
  187.          top     sp      push
  188.          r0      get-link
  189.          r0      r0 )    ldr
  190.          top     r0      up add c;
  191.  
  192.  
  193. Deferred words
  194. ==============
  195.  
  196. The compilation address of the word to be executed by a `defer` word is 
  197. stored as a 32-bit absolute address in the `user` area.  The Parameter 
  198. Field of a deferred word contains a 32-bit number which is an offset 
  199. into the user area of the current task.  The runtime code: 
  200.     mlabel dodefer  assembler
  201.          r0      get-link
  202.          r0      r0 )    ldr
  203.          pc      r0      up  ib ldr end-code
  204. The last line holds a somewhat optimized `next` instruction, it means: 
  205. Load the pc from the address in the user area with the offset r0.  
  206.  
  207.  
  208. ;code
  209. =====
  210.  
  211. The compilation address of a word created by a `create` ...  `;code` 
  212. data type construction contains the standard branch+link instruction 
  213. that branches to the runtime code.  
  214.  
  215. The runtime code is defined by the programmer in the `;code` part of the 
  216. definition.  At first the `;code` instruction assembles 
  217.          top     sp      push
  218.          top     get-link
  219. before the programmers code.  This is mainly for convenience, so the top 
  220. is already saved to the stack and points to the parameter field.  The 
  221. programmer might do '-2 /n* allot ' to forget this for speed optimized 
  222. code.  
  223.  
  224.  
  225. does>
  226. =====
  227.  
  228.  
  229.     mlabel dodoes  assembler
  230.          ip      rp      push
  231.          ip      get-link c;
  232. The runtime code is defined by the programmer in the `does>` part of the 
  233. definition.  Before branching to the dodoes code, the does> instruction 
  234. assembles 
  235.          top     sp      push
  236.          top     lk      th fc000003 # bic
  237. to get the parameter field address.  
  238.  
  239.  
  240. local variables
  241. ===============
  242.  
  243. Risc-OS Forthmacs has built in ANS Forth conforming local variables 
  244. spending their lifetime on the return-stack in stack-frames.  The 
  245. stack-frames are linked via a `user` variable LOCAL-FRAME which is also 
  246. used to locate a local variables value.  The frame structure is like: 
  247.     | cfa:frame>   | old-frame     | old-rs        | loc   | loc   | .........
  248. with cfa:pop-frame on top of the return-stack.  pop-frame removes the 
  249. current frame and switches to the last frame.  
  250.     headerless code pop-frame \ this routine is pushed on return stack by push-locals
  251.          here-t /token-t + token,-t
  252.         r0 rp 2    rp ia    ldm
  253.         r0    'user local-frame str
  254.         ip    rp    pop c;
  255.     
  256. The local variables are accessed using (loc) followed by an stack frame 
  257. index.  
  258.     code (loc)    \ ( -- n )  runtime-code of any local
  259.         r0    'user local-frame ldr
  260.         r1    ip )+    ldr
  261.         top    sp    push
  262.         top    r0 r1 2 #asl db ldr c;
  263.  
  264. Note: The disassembler can `not` know the local variables names, so it 
  265. assumes names like V0 V1 ... .  
  266.  
  267.  
  268. Vocabularies
  269. ============
  270.  
  271. Each `vocabulary` has `#threads` ( currently 16 ) 32-bit pointers which 
  272. are called "threads".  A thread is the head of a linked list of words.  
  273. A hashing function selects which of the 16 linked lists a particular 
  274. word belongs to.  The threads are stored in the `user` area.  The 
  275. Parameter Field of a `vocabulary` contains the 32-bit offset of the 
  276. threads in the `user` area, followed by the vocabulary-link, a 32-bit 
  277. pointer to the previous `vocabulary.` The runtime high-level code is: 
  278.          does> body> context token!
  279.  
  280.  
  281. Tokens
  282. ======
  283.  
  284. Within the body of a colon definition, calls to other Forth words are 
  285. compiled as the 32-bit absolute compilation address of those words.  
  286. These tokens have a corresponding bit in the relocation table.  
  287.  
  288.  
  289. Branching
  290. =========
  291.  
  292. Branch targets are offsets relative to the location that contains the 
  293. branch offset.  They are stored as 32-bit twos-complement numbers 
  294. representing the number of bytes between the offset location and the 
  295. branch target.  For example, a branch to the following location could be 
  296. compiled with: 
  297.  
  298.          compile branch   4 ,
  299.  
  300.  
  301. Header format - # of bytes in parentheses
  302. =========================================
  303.  
  304. Source Field (4), Link Field (4), Name Field (n), Padding (0 to 3), 
  305. Flags (1), Code Field (4), Parameter Field (n).  
  306.  
  307. As all addresses must be, the Link Field, Name Field, and Code Field are 
  308. all aligned.  
  309.  
  310. Links point to links ( `not` to Name Fields, as in FIG Forth! ) 
  311.  
  312. The name field is a normal Forth packed string.  (Many Forth 
  313. implementations set the high bit in the first and last characters of the 
  314. name field; Risc-OS Forthmacs does not).  
  315.  
  316. Name Field: length-byte, 0-31 character name.  
  317.  
  318.  
  319.  
  320. Vocabulary Format
  321. =================
  322.  
  323. Vocabularies have 8-way hashing.  This means that each vocabulary has 16 
  324. separate linked lists of words.  Before searching a vocabulary, a 
  325. hashing function is applied to the name to be located.  The hashing 
  326. function selects one of the 8 linked lists to search.  
  327.  
  328. The hashing function is very simple.  The lower 3 bits of the first 
  329. character in the name (the first name character, not the length byte) 
  330. are interpreted as a number from 0 to 7, selecting a linked lists.  
  331.  
  332. Vocabularies are not chained to one another.  Search order is 
  333. implemented using the `also` / `only` scheme.  Each vocabulary thread is 
  334. terminated with a special link field in the final word.  The special 
  335. link address is the address of the origin of the Forth system (which may 
  336. change from session to session due to the relocation that the operating 
  337. system applies when loading and executing the Forth system.  
  338.  
  339. The parameter field for a vocabulary looks like: 
  340.  
  341. User number (4), Voc-link (4) 
  342.  
  343. The user number selects the place in the user area where the head of 
  344. list pointers for the 4 vocabulary threads are stored.  Each vocabulary 
  345. requires 8 bytes of user area storage for these 4 threads.  The values 
  346. stored in the user area are the Link field Addresses for the top word in 
  347. each thread.  
  348.  
  349.  
  350. Relocation
  351. ==========
  352.  
  353. In the RISC-OS environment all programs of the absolute type are loaded 
  354. at $8000 and executed from there.  So on first sight the relocation 
  355. table doesn't make much sense in this version.  
  356.  
  357. But the relocation table can be used for target/meta-compiling or for 
  358. relocating code during run-time.  
  359.  
  360. The executable file contains a relocation list used to identify the 
  361. locations in the program's binary image which contain absolute 
  362. addresses.  When the program is loaded, each of these locations is 
  363. modified by adding the starting address of the program to the number 
  364. contained in that location.  Only 32-bit numbers may be so modified.  
  365.  
  366. While Risc-OS Forthmacs is running, it maintains its own relocation 
  367. table, identifying those locations in the Forth dictionary which must be 
  368. relocated during COLD-CODE.  Each bit in the map represents the address 
  369. of one aligned location.  This relocation table is completely different 
  370. from the standard Risc OS relocation tables, it is only used from within 
  371. Risc-OS Forthmacs.  
  372.  
  373. In order for this to work properly, the programmer must be careful to 
  374. use `token,` or `token!` to store an address in the dictionary, both set 
  375. the relocation flags.  If , or ! is used instead, the address will not 
  376. be properly relocated if `save-forth` has been used to write the 
  377. dictionary image to an executable file.  
  378.  
  379. See: `token!` `token,` `set-relocation-bit,` `relocation-map` 
  380.  
  381.  
  382. Program header
  383. ==============
  384.  
  385. The header of the executable binary image looks like this: 
  386.  
  387.  h_magic   (  0)    \ Magic Number
  388.  h_tlen    (  4)    \ length of text (code)
  389.  h_dlen    (  8)    \ length of initialized data
  390.  h_blen    (  c)    \ length of BSS unitialized data
  391.  h_slen    (  10)   \ length of symbol table
  392.  h_entry   (  14)   \ Entry address
  393.  h_trlen   (  18)   \ Text Relocation Table length
  394.  h_drlen   (  1c)   \ Data Relocation Table length
  395.  
  396. the magic number is the branch+link instruction just behind this header.  
  397. Note: this header might be changed with future releases according to 
  398. Acorns executable binary code standard.  
  399.  
  400.  
  401. Heap memory
  402. ===========
  403.  
  404. Risc-OS Forthmacs is loaded to $8000 and will have as much memory 
  405. available as was defined by 'WimpSlot' .  
  406.  
  407. The `main-tasks` user area immediatly follows the first instructions at 
  408. $8050.  
  409.  
  410. $600 byte will be allocated in module-heap, it will hold the ENV-AREA, 
  411. the command-line area, the `interrupt-code` at TICKER plus all handlers 
  412. used by shelled programs.  
  413.  
  414. At the top of the programs private memory is the programs heap, it's 
  415. default size is 64kB but can be set to any possible size by setting '
  416. Forthmacs$heapsize' (see "General Information").  All `buffer:s` will be 
  417. allocated during bootup.  
  418.  
  419. The bottom of the heap is `main-heap,` it's size is `heap-size.` All 
  420. dynamic memory handling words as `allocate,` `free,` `available` and `
  421. resize` all handle memory inside this heap.  Note: Of course you may 
  422. install another memory manager or add more heaps.  
  423.  
  424.  
  425. Dictionary memory
  426. =================
  427.  
  428. At the top of the dictionary are both stacks defined by `rp0` - RS-SIZE 
  429. and `sp0` - `ps-size` and the `tib,` below this are MBytes of free 
  430. memory (well, hopefully).  `here` marks the end of the allocated 
  431. dictionary, classically `pad` is `here` plus something.  
  432.  
  433. Risc-OS Forthmacs knows about two dictionary areas, the `resident` 
  434. (which is the dictionary you know in all implementations) and the `
  435. transient.` The transient dictionary is in the heap memory, definitions 
  436. defined here won't use dictionary space in the target application.  So 
  437. it might be useful to do: 
  438.     transient
  439.       fload assembler
  440.       fload debugger
  441.     resident
  442.       fload myapplication
  443. Now the debugger and asembler will be in transient address space.  To 
  444. remove all links, pointers etc.  into the transient address space use `
  445. dispose,` it will do this for you.  .DISPOSE will also give some 
  446. informations what is removed.  
  447.  
  448.