home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / dev / struct-1.0.lha / Struct-1.0 / Struct.doc < prev    next >
Encoding:
Text File  |  1994-06-30  |  101.7 KB  |  2,564 lines

  1.                              Struct 1.0
  2.                            --------------
  3.  
  4.                  An Amiga compiler by Roland Acton
  5.  
  6.                            --- Manual ---
  7.  
  8.  
  9.  
  10. Contents
  11. --------
  12. 1. Introduction
  13. 2. Setup And Usage
  14. 3. Basic Concepts
  15. 4. Language Summary
  16. 5. Tutorial
  17. 6. Optimization
  18. 7. Q & A
  19. 8. Registration
  20. 9. Legalese
  21.  
  22. Appendices
  23. ----------
  24. A. How to alter the library definition file
  25. B. Possible errors
  26. C. Theoretical limits
  27. D. Speed tests
  28.  
  29.  
  30.  
  31. * Parts of this manual have asterisks along their left border. These
  32. * sections are optional reading material. Most of them deal with the
  33. * internal workings of the compiler and efficiency related issues.
  34. * Reading them is helpful, but not strictly necessary.
  35.  
  36.  
  37.  
  38. 1. Introduction
  39. ---------------
  40.   It was the summer of '92, and I was irritated. The cause of this
  41. condition was very simple: I didn't have any good languages for my
  42. Amiga.
  43.   Assembly was out. Oh, I knew assembly, and had a decent assembler.
  44. But writing assembly on the 68000 was a nightmare. It hadn't been so
  45. hard on the old C-64 where I had started programming - A, X, and Y
  46. were all you really needed to remember, and the occasional BNE. But
  47. the 68000...sixteen registers (more if you counted the SR and such),
  48. indirect addressing, library pointers... I quickly grew tired of
  49. looking back over my program to figure out what register I'd left a
  50. number in. I grew even more tired of spending hours in a monitor
  51. program because I'd made a typo.
  52.   It wasn't that hard if you wrote your code very carefully, but
  53. that usually meant passing up optimizations because you wouldn't be
  54. able to figure out what you had been doing later. What was the point
  55. of using assembly if you weren't going to squeeze every last bit of
  56. speed out of it?
  57.   C? Well, I'll admit it: I didn't know C at the time. I do now.
  58. Even if I had known it, though, I wouldn't have been very positive
  59. about it. C compilers for the Amiga tend to be expensive and/or
  60. take large amounts of memory. The ones that aren't probably don't
  61. produce very good object code. C was not an option.
  62.   AMOS Basic? It had bugs. (It still has bugs.) One of them was that
  63. programs written in it didn't work very well with each other. This
  64. was because they each tried to open up their own (exclusive) View
  65. and install their own (exclusive) input handler. This meant that
  66. writing a "serious" program in AMOS could be rather dangerous for
  67. the end user, particularly if it hung around on the system for
  68. awhile.
  69.   The other languages that I knew (Pascal, Fortran, Ada) hadn't made
  70. much of a mark on the Amiga. It looked like I would have to go with
  71. assembly - and writing a large program in THAT language was a scary
  72. thought indeed. Still, it was the only option, unless I felt like
  73. writing my OWN language.
  74.   Now that was an interesting idea. I still had nearly all of
  75. summer vacation left, and that gave me quite a bit of time to play
  76. around with. Could I do it? I thought so, but the only way to know
  77. for sure was to try. It was better than sleeping my summer vacation
  78. away. I ought to be able to do it in three months. No problem.
  79.   I write this a year later, as version 0.95 of Struct is about to
  80. be released to beta testers. It wasn't quite as easy as I thought,
  81. but it was always interesting. Almost always.
  82.   Although Struct is a considerable step above assembly, I still
  83. consider it to be a low-level language. This is because many
  84. features which have come to be expected in high-level languages are
  85. not present in Struct - features such as strings, arrays, and
  86. floating point variables. All the "real" high-level languages that
  87. I have seen have these features automagically. In Struct they must
  88. be built from scratch.
  89.   In my opinion, right now Struct has the following serious
  90. limitations:
  91.  
  92. 1. It's not possible to have the parameter of a command or function
  93. be another command or function. I can't recall what the formal name
  94. of this ability is, but Struct doesn't have it. This means more use
  95. of temporary variables, which can be very annoying.
  96.  
  97. 2. The compiler does not support "modular" programs. The user can
  98. split his program into multiple files with the Include Struct
  99. command, but the compiler always sees it as one big source file. A
  100. trivial change to a very large program could take quite a while to
  101. compile. Also, many assemblers do not like a source file that is
  102. more than a few hundred K long - and Struct would generate such a
  103. file with a sufficiently large program.
  104.  
  105. 3. You can have only a certain number of constants. This could cause
  106. "problems" for users wanting to include very large external constant
  107. definition files. I can set this limit quite high, but cannot
  108. eliminate it, so it still exists, waiting to wreak havoc on the
  109. unsuspecting user.
  110.  
  111.   I believe that I can get around all of these limitations, but the
  112. solutions require careful thought, and their implementation is a
  113. ways down the road.
  114.   Despite these problems, though, the compiler is quite usable. It
  115. will serve to make stand-alone programs or fast subroutines for
  116. programs written in other languages. It is my hope that Struct will
  117. be as useful to you as I intend to make it to me.
  118.   After all, any other language would be inefficient.
  119.  
  120.  
  121.  
  122. 2. Setup And Usage
  123. ------------------
  124.   Copy the file "StructLibraryDefinitions" into your S: directory.
  125. This file is the equivalent of the Commodore .fds and contains the
  126. library offsets and registers for all of the Amiga's OS functions.
  127. Struct will not run without it.
  128.   Struct itself is used from the command line, and should be copied
  129. into your C: directory to facilitate this. The compiler is invoked
  130. with the command:
  131.  
  132. Struct <source-file> <destination-file>
  133.  
  134.   Struct will not append any suffixes to either of the filenames.
  135.   Struct generates output files containing assembly code. This code
  136. must be assembled and linked before it can be run. The Phx directory
  137. contains the optimizing assembler PhxAss and linker PhxLnk; these
  138. should go into your C: directory.
  139.   There are a number of optimizations to the assembly code that
  140. Struct does not make because it knows that PhxAss will do them for
  141. it. For the fastest executables, an optimizing assembler should
  142. always be used.
  143.   Shortly before releasing version 0.99, I discovered to my great
  144. surprise that PhxAss does not work under Kickstart 3.0!
  145. Unfortunately, I know of no other optimizing assemblers that you
  146. don't have to pay for. For those people who have OS 3.0, I have
  147. included the A68k assembler and Blink linker in the archive. These
  148. programs don't optimize, but are extremely stable.
  149.   You can use the batch file included in the archive as a model for
  150. a script that will invoke the compiler, assembler, and linker with
  151. one command.
  152.   When Struct reads a line from a source file, it first REMOVES ALL
  153. SPACES and then CONVERTS THE LINE TO UPPERCASE. Thus, the line:
  154.  
  155. GLOBALVALUE=5
  156.  
  157. is the same as:
  158.  
  159. Global VALUE=5
  160.  
  161.   When you define variables or constants, Struct will check them to
  162. make sure that they don't begin with keywords. Thus, the situation
  163. just presented would probably end up causing a compilation error.
  164.   Struct's error reports always show you the line as the compiler
  165. sees it - uppercase and without spaces. This allows you to more
  166. easily put yourself in the compiler's position and find your
  167. mistake.
  168.   Besides errors, Struct can also generate warnings. If a
  169. compilation results in warnings but no errors it is considered a
  170. success. However, depending upon the situation, the object code may
  171. not assemble/work.
  172.   If there are errors, object code will still be generated, but it
  173. should NEVER be used! It may be missing vital sections that will
  174. cause the program to fail or crash the computer.
  175.  
  176.  
  177.  
  178. 3. Basic concepts
  179. -----------------
  180.   As mentioned before, Struct is a low-level language. There are no
  181. strings, pointers, or arrays as such - only those which you create
  182. yourself. The three basic data types are BYTE, WORD, and LONG. All
  183. variables are assumed to contain signed integers, though this really
  184. only makes a difference for multiplication and division. Floating
  185. point is not supported.
  186.   If a variable of one data type is assigned to a variable of
  187. another data type, Struct will do one of two things. If the
  188. assignment is one of these types:
  189.  
  190.   <byte-type>=<word-type>
  191.   <byte-type>=<long-type>
  192.   <word-type>=<long-type>
  193.  
  194. that is, if the contents of a variable are being assigned to one
  195. with lower precision, Struct will simply take the low byte or word,
  196. as appropriate, from the longer variable and place it in the shorter
  197. variable. Because of the way signed numbers are represented in the
  198. computer, this will work so long as the longer variable does not
  199. contain a larger number than the shorter variable can hold. There is
  200. no checking for this kind of situation. If you think it may happen,
  201. you must check for it explicitly in your program.
  202.   If the assignment is one of these types:
  203.  
  204.   <long-type>=<word-type>
  205.   <long-type>=<byte-type>
  206.   <word-type>=<byte-type>
  207.  
  208. that is, if the contents of a variable are being assigned to one
  209. with higher precision, Struct will perform an operation called
  210. "sign-extension" on the contents of the shorter variable and assign
  211. it to the longer variable. This will always work properly.
  212.  
  213. * Sign-extension is a pretty trivial and quick operation, but too
  214. * much of it will start to add up. It's best to structure your
  215. * programs so that variables are only assigned to variables of like
  216. * type; this will avoid sign-extension and the risk of truncating a
  217. * value.
  218.  
  219.   If an actual mathematical operation is performed (as opposed to
  220. the simple assignments shown above) it will be done in 32 bits. As
  221. much of the result as can fit will be placed in the destination
  222. variable after the math is complete.
  223.   For math operations, only the four basic operators are supported:
  224. + (plus), - (minus), * (multiplication), and / (division). Further,
  225. NO parentheses are supported and mathematical expressions are ALWAYS
  226. evaluated strictly left-to-right - there is no "operator
  227. precedence". Most other compilers use an internal system called
  228. "Reverse Polish Notation" (RPN) to provide these features. Struct
  229. does not. The disadvantage of this is that writing complex math
  230. expressions can be irritating and require the use of temporary
  231. variables. The significant advantage is that math under Struct does
  232. not require the use of the stack, and so can be much faster than
  233. other languages.
  234.   Be warned that the hardware instructions for multiplication and
  235. division are used, and their support for 32 bits is incomplete.
  236. Multiplication generates a 32-bit result, but only uses the low
  237. word of the multiplier (if it is a LONG) and of the intermediate
  238. value. Division uses the entire 32-bit intermediate value as the
  239. dividend, but only the low word of the divisor is used if it is a
  240. LONG, and the result will be only 16 bits. It will be sign-extended
  241. to 32 bits before becoming the new intermediate value.
  242.   The AND, OR, NOT, and NEGATE operations cannot be used in
  243. mathematical expressions. These operations have their own separate
  244. commands.
  245.  
  246. * Struct's parsing of math expressions is very thorough. Each
  247. * command has a chance to make local optimizations (the PEEK and
  248. * POKE commands, for example, can see a memory access to RANMA+GENMA+2
  249. * as 2(RANMA,GENMA.L) if appropriate). If the local optimizations
  250. * fail, or the operation was just an assignment in the first place,
  251. * the expression is passed through to an internal routine called
  252. * MATHEVALUATE. This routine knows the difference between situations
  253. * such as:
  254. *
  255. * X=Y ("Level 1", simple assignment)
  256. * X=X+Y ("Level 2", incrementation)
  257. * X=A+B+C ("Level 3", complex expression)
  258. *
  259. * and will act accordingly. It also knows that addition and
  260. * subtraction are commutative, and that numeric literals can be
  261. * concatenated.
  262.  
  263.   The tilde character (~) has a special meaning to Struct. It is the
  264. line continuation character. When Struct's line parsing routine
  265. encounters the tilde character, it will skip all characters after it
  266. until it reaches an end-of-line character. It will then resume
  267. reading characters as normal. Thus, if a line such as this appears
  268. in your source code:
  269.  
  270. Data Register Catty, Rabby, Lu~
  271. fy, Rumy, Patty
  272.  
  273. Struct will see it as:
  274.  
  275. Data Register Catty, Rabby, Lufy, Rumy, Patty
  276.  
  277.   Although this feature was intended to allow you to break up huge
  278. lines into sections that will fit on your text editor's screen, it
  279. can also be used to include comments in the source code:
  280.  
  281. ~ This routine opens up a Viewport, initializes a copper list, and
  282. ~ loads a backdrop picture onto the screen.
  283.  
  284.   The presence of the tilde will, as a side effect, cause the
  285. compiler to ignore these lines.
  286.   Labels can be placed at any point in the source code by the
  287. following method:
  288.  
  289. <label>:
  290.  
  291.   For example:
  292.  
  293. Eluza:
  294.  
  295.   These labels can be used with such commands as Load Address and
  296. Goto. A label that appears within a procedure is a local label, and
  297. can only be referenced by code within that procedure. A label that
  298. appears outside a procedure is a global label, and can be referenced
  299. by code in any procedure. If a global and local label both have the
  300. same name, all references to that label within that procedure are
  301. assumed to refer to the local label.
  302.  
  303.  
  304.  
  305. 4. Language Summary
  306. -------------------
  307. Address Absolute <[<variable1>][,][<variable2>][,][...]]>
  308.  
  309.   Summary: Assigns LONG variables to address registers A3-A5. The
  310. first variable specified is assigned to A3, the second to A4, and
  311. the third to A5.
  312.  
  313. Address Register [<variable1>[,<variable2>[,...]]]
  314.  
  315.   Summary: Assigns LONG variables to address registers A3-A5. Struct
  316. decides which variables are assigned to which registers.
  317.  
  318. And <mask-expression>,<variable>
  319.  
  320.   Summary: Performs a mathematical AND between the mask-expression
  321. and the variable. The result is left in the variable.
  322.  
  323. Asm
  324.  
  325.   Summary: Marks the beginning of a section of inline assembly.
  326.  
  327. Back If
  328.  
  329.   Summary: Changes the flow of control back to the beginning of the
  330. most recent If statement. The If statement is re-evaluated.
  331.  
  332. Byte Data [(label)] <data>
  333.  
  334.   Summary: The specified data is placed in the object file as
  335. byte-length values. If the command is given while in a procedure,
  336. the data is held until the end of the procedure, then output. The
  337. optional local label will point to the beginning of the data.
  338.  
  339. Byte Peek <variable>=<memory-location-expression>
  340.  
  341.   Summary: The memory-location-expression is evaluated, and the
  342. byte-length value found at that location is placed in the receiving
  343. variable.
  344.  
  345. Byte Poke <memory-location-expression>,<value-expression>
  346.  
  347.   Summary: The memory-location-expression is evaluated, and the
  348. result of the value-expression is written to that place as a byte.
  349.  
  350. Call <memory-location-expression>
  351.  
  352.   Summary: The memory-location-expression is evaluated, and a
  353. machine language subroutine located at that address is called.
  354.  
  355. Constant <constant1>[=<value>][,<constant2>[=<value>][,...]]
  356.  
  357.   Summary: Defines one or more constants. An optional initial value
  358. can be specified.
  359.  
  360. Constant Evaluate <ON/OFF>
  361.  
  362.   Summary: Turns ON or OFF an internal compiler switch that governs
  363. whether or not a constant assignment is evaluated immediately.
  364.  
  365. Data Absolute <[<variable1>][,][<variable2>][,][...]]]>
  366.  
  367.   Summary: Assigns variables to data registers D1-D7. The first
  368. variable specified is assigned to D1, the second to D2, and so
  369. forth.
  370.  
  371. Data Register [<variable1>[,<variable2>[,...]]]
  372.  
  373.   Summary: Assigns variables to data registers D1-D7. Struct
  374. decides which variables are assigned to which registers.
  375.  
  376. Define Procedure [<return-type>=]<procedure-name> [(<variable1>:
  377.   <type>[=default-parameter],<variable2>:<type>[=default-parameter]
  378.   [,...]>)]
  379.  
  380.   Summary: Tells Struct the names, types, and defaults of the
  381. parameters a procedure will accept, if any, and the type of value
  382. that it returns, if it does. A procedure must be defined before it
  383. can be either implemented or called.
  384.  
  385. Else
  386.  
  387.   Summary: Marks the beginning of a section of code that will be
  388. executed if its associated If is false.
  389.  
  390. End Asm
  391.  
  392.   Summary: Marks the end of a section of inline assembly.
  393.  
  394. End If
  395.  
  396.   Summary: Marks the end if an If statement.
  397.  
  398. End Loop
  399.  
  400.   Summary: Marks the end of a Loop structure.
  401.  
  402. End Procedure
  403.  
  404.   Summary: Marks the end of the implementation of a procedure.
  405.  
  406. Eor <mask-expression>,<variable>
  407.  
  408.   Summary: Performs a mathematical EOR between the mask-expression
  409. and the variable. The result is left in the variable.
  410.  
  411. Exit
  412.  
  413.   Summary: Exits prematurely from a loop structure. Program
  414. execution will continue immediately after the loop's terminating
  415. statement.
  416.  
  417. For <variable>=<start-expression>;<end-expression>
  418.   [;<step-expression>]
  419.  
  420.   Summary: Initiates a For/Next loop structure. The section of code
  421. in the loop will be repeated as the value of the variable ranges
  422. from the start-expression to the end-expression. An optional
  423. step-expression can be specified which is used in place of the
  424. default step of 1.
  425.  
  426. Global <variable1>:<type>[=<initial-value>][,<variable2>:<type>
  427.   [=<initial-value>][,...]]
  428.  
  429.   Summary: Defines the name, type, and possibly initial value of
  430. one or more global variables. The variables will be accessible from
  431. any procedure.
  432.  
  433. Goto <label>
  434.  
  435.   Summary: Changes program flow to the specified label. The label
  436. must be defined within the current procedure.
  437.  
  438. If <condition1> [<&/|> <condition2> [<&/|> <condition3> [...]]]
  439.  
  440.   Summary: Marks the beginning of an If statement. If the specified
  441. condition is determined to be true, the code immediately following
  442. the If will be executed. Otherwise, the program flow will jump to
  443. the End If (or an Else if one has been used).
  444.  
  445. Include Constant <filename>
  446.  
  447.   Summary: Causes inclusion of an external file of constants. The
  448. file must be in a similar format to that of the Commodore-Amiga
  449. assembly include files.
  450.  
  451. Include Struct <filename>
  452.  
  453.   Summary: Causes inclusion of an external file of Struct code. The
  454. code is compiled as if it had appeared at that place in the source
  455. program.
  456.  
  457. Lib Call [<result-variable>=]<function-name>[(<parameter1>
  458.   [,<parameter2>[,...]])]
  459.  
  460.   Summary: Calls an external function defined in the
  461. StructLibraryDefinitions file. The function's library must have been
  462. opened with the Open Library command.
  463.  
  464. Load Address <variable>=<program-element>
  465.  
  466.   Summary: Loads the memory address of the program-element (label,
  467. procedure, or variable) into the specified variable. Local elements
  468. have precedence over global elements of the same type.
  469.  
  470. Local <variable1>:<type>[=<initial-value>][,<variable2>:<type>
  471.   [=<initial-value>][,...]]
  472.  
  473.   Summary: Defines the name, type, and possibly initial value of
  474. one or more local variables. The variables will only be accessible
  475. from within the procedure in which they are defined.
  476.  
  477. Long Data [(label)] <data>
  478.  
  479.   Summary: The specified data is placed in the object file as
  480. longword values. If the command is given while in a procedure, the
  481. data is held until the end of the procedure, then output. The
  482. optional local label will point to the beginning of the data.
  483.  
  484. Long Peek <variable>=<memory-location-expression>
  485.  
  486.   Summary: The memory-location-expression is evaluated, and the
  487. longword value found at that location is placed in the receiving
  488. variable.
  489.  
  490. Long Poke <memory-location-expression>,<value-expression>
  491.  
  492.   Summary: The memory-location-expression is evaluated, and the
  493. result of the value-expression is written to that place as a
  494. longword.
  495.  
  496. Loop [start-assignment];[truth-expression];[step-assignment]
  497.  
  498.   Summary: Marks the start of a Loop structure. The
  499. start-assignment, if specified, will be performed and the block of
  500. code in the loop will be executed. Then the step-assignment, if
  501. specified, will be performed and the truth-expression will be
  502. checked. If the truth-expression is true then the block of code will
  503. be executed again. If no truth-expression is specified it will cause
  504. an infinite loop.
  505.  
  506. Negate <variable>
  507.  
  508.   Summary: A mathematical negation will be performed on the
  509. specified variable. This will have the effect of reversing the sign
  510. of the value contained in the variable.
  511.  
  512. Next
  513.  
  514.   Summary: Marks the end of a For/Next loop.
  515.  
  516. Not <variable>
  517.  
  518.   Summary: A mathematical NOT will be performed on the specified
  519. variable. This will have the effect of reversing the states of the
  520. bits that make up the value contained in the variable.
  521.  
  522. Open Library <library>
  523.  
  524.   Summary: Tells Struct to open the specified library during the
  525. program's startup code. An external function's parent library must
  526. be opened before the function can be used.
  527.  
  528. Or <mask-expression>,<variable>
  529.  
  530.   Summary: Performs a mathematical OR between the mask-expression
  531. and the variable. The result is left in the variable.
  532.  
  533. Peek <variable>=<memory-location-expression>
  534.  
  535.   Summary: The memory-location-expression is evaluated, and the
  536. value found at that location is placed in the receiving variable.
  537. Struct will determine the size of the memory access based upon the
  538. sizes of the variables and numeric literals in the
  539. memory-location-expression.
  540.  
  541. Poke <memory-location-expression>,<value-expression>
  542.  
  543.   Summary: The memory-location-expression is evaluated, and the
  544. result of the value-expression is written to that place. Struct will
  545. determine the size of the memory access based upon the sizes of the
  546. variables and numeric literals in the memory-location-expression.
  547.  
  548. Print <memory-location-expression>
  549.  
  550.   Summary: The memory-location-expression is evaluated, and the
  551. null-terminated string found at that location is printed to standard
  552. output. Dos library must be opened with the Open Library command
  553. before the Print command can be used.
  554.  
  555. Proc Call [<result-variable>=]<procedure-name>[([<parameter1>],
  556.   [<parameter2>][,...]>)]
  557.  
  558.   Summary: Calls the specified procedure. The optional parameters,
  559. if given, will be passed to the procedure. If a result-variable is
  560. given, the procedure's return value will be placed into it. The
  561. procedure must have been defined at some point before the Proc Call
  562. command.
  563.  
  564. Procedure [<return-type>=]<procedure-name> [(<variable1>:<type>
  565.   [=default-parameter],<variable2>:<type>[=default-parameter]
  566.   [,...]>)]
  567.  
  568.   Summary: Marks the beginning of the implementation of a procedure.
  569. The optional variables, if specified, will be used to accept
  570. parameters from other procedures calling this one. The return-type,
  571. if given, indicates that the procedure returns a value of the
  572. specified type.
  573.  
  574. Recursive <procedure-name> [([<parameter1>],[<parameter2>][,...]>)]
  575.  
  576.   Summary:  Calls the specified procedure. The optional parameters,
  577. if given, will be passed to the procedure. If a result-variable is
  578. given, the procedure's return value will be placed into it. The
  579. procedure must have been defined at some point before the Recursive
  580. command. Before the call is made, all of the local variables will
  581. be saved onto the stack.
  582.  
  583. Regload [<variable1>[,<variable2>[,...]]]
  584.  
  585.   Summary: Loads the specified register variables onto their
  586. registers. If no variables are specified, all register variables
  587. will be loaded.
  588.  
  589. Regsave [<variable1>[,<variable2>[,...]]]
  590.  
  591.   Summary: Saves the specified register variables off to memory. If
  592. no variables are specified, all register variables wil be saved.
  593.  
  594. Remainder <variable>
  595.  
  596.   Summary: Places the remainder from a previous hardware division
  597. into the specified variable.
  598.  
  599. Repeat
  600.  
  601.   Summary: Marks the beginning of a Repeat/Until loop.
  602.  
  603. Set Startup <procedure-name>
  604.  
  605.   Summary: Sets which procedure will be executed when the program is
  606. run. The specified procedure must already have been defined.
  607.  
  608. String [(label)] <data>
  609.  
  610.   Summary: Works exactly like the Byte Data command, except that a
  611. null character is appended to the end of the data.
  612.  
  613. Until <expression>
  614.  
  615.   Summary: Marks the end of a Repeat/Until loop. The code in the
  616. loop will be continually executed until the specified expression is
  617. determined to be true.
  618.  
  619. Version <version-number>
  620.  
  621.   Summary: Specifies which version of Struct a program was written
  622. for. Never produces any code, but may cause Struct to generate a
  623. warning.
  624.  
  625. Wend
  626.  
  627.   Summary: Marks the end of a While/Wend loop.
  628.  
  629. While <expression>
  630.  
  631.   Summary: Marks the beginning of a While/Wend loop. The code within
  632. the loop will be repeatedly executed until the expression is
  633. determined to be false. If the expression is already false before
  634. the first iteration, the code will be skipped.
  635.  
  636. Word Data [(label)] <data>
  637.  
  638.   Summary: The specified data is placed in the object file as
  639. word-length values. If the command is given while in a procedure,
  640. the data is held until the end of the procedure, then output. The
  641. optional local label will point to the beginning of the data.
  642.  
  643. Word Peek <variable>=<memory-location-expression>
  644.  
  645.   Summary: The memory-location-expression is evaluated, and the
  646. word-length value found at that location is placed in the receiving
  647. variable.
  648.  
  649. Word Poke <memory-location-expression>,<value-expression>
  650.  
  651.   Summary: The memory-location-expression is evaluated, and the
  652. result of the value-expression is written to that place as a word.
  653.  
  654. Zend
  655.  
  656.   Summary: Marks the end of a Zeroloop structure.
  657.  
  658. Zeroloop <variable>
  659.  
  660.   Summary: Marks the beginning of a Zeroloop structure. The code
  661. within the loop will be repeatedly executed as the variable ranges
  662. from its starting value to zero.
  663.  
  664.  
  665.  
  666. 5. Tutorial
  667. -----------
  668.  
  669. ------------------------------
  670. Define Procedure, Set Startup,
  671. Procedure, End Procedure
  672. Proc Call, Recursive
  673. ------------------------------
  674.  
  675.   All code you write is organized into procedures. First, a
  676. procedure is DEFINED as follows:
  677.  
  678. Define Procedure [<return-type>=]<procedure-name> [(<variable1>:
  679.   <type>[=default-parameter],<variable2>:<type>[=default-parameter]
  680.   [,...]>)]
  681.  
  682.   For example:
  683.  
  684. Define Procedure WORD=Video (Ai:BYTE=30, Youta:WORD=6, Moemi:LONG)
  685.  
  686.   Modern programming standards would require you to define all
  687. procedures at the start of your program; however, Struct will permit
  688. you to define a procedure at any point before it is actually
  689. implemented.
  690.   Next, a procedure is IMPLEMENTED as follows:
  691.  
  692. Procedure [<return-type>=]<procedure-name> [(<variable1>:<type>
  693.   [=default-parameter],<variable2>:<type>[=default-parameter]
  694.   [,...]>)]
  695.         [...]
  696. [program statements]
  697.         [...]
  698. End Procedure
  699.  
  700.   Within the procedure, the variables given will be defined as local
  701. variables of the type specified as if the Local command had been
  702. used.
  703.   The implementation for the above example would be:
  704.  
  705. Procedure WORD=Video (Ai:BYTE=30, Youta:WORD=6, Moemi:LONG)
  706.         [...]
  707. [program statements]
  708.         [...]
  709. End Procedure
  710.  
  711.   At some point in your program, you must use the Set Startup
  712. command, which takes the following form:
  713.  
  714. Set Startup <procedure-name>
  715.  
  716.   The named procedure must have been already defined. When your
  717. compiled and assembled program is run, it will first perform some
  718. set-up operations and then call the procedure named with Set
  719. Startup. The startup procedure may call other procedures, but when
  720. it ends the program terminates.
  721.   Procedures normally call other procedures by using the Proc Call
  722. command, which looks like this:
  723.  
  724. Proc Call [<result-variable>=]<procedure-name> [([<parameter1>],
  725.   [<parameter2>][,...]>)]
  726.  
  727.   When a procedure call occurs, all register variables (see the set
  728. of "Register" commands) are first saved off. Then the variables
  729. listed are loaded into the hardware registers and the specified
  730. procedure is called. When the called procedure takes control, it
  731. immediately saves off the passed values into the variables specified
  732. in its Define Procedure statement. The first parameter specified in
  733. the Proc Call command is placed into the first variable listed in
  734. the Define Procedure statement, and so forth.
  735.   For example:
  736.  
  737. Proc Call Len=Video (,Takashi,)
  738.  
  739.   Note that only the second parameter has been specified; the first
  740. and third have been omitted. Because the receiving variable Ai was
  741. given a default parameter in Video's Define Procedure statement, the
  742. compiler will pass that value as the first parameter. When
  743. procedure Video takes control, Ai will contain 30 and Youta will
  744. contain the value of Takashi, but Moemi will be undefined. A call
  745. of this type is legal, but must be used with care. If some
  746. variables are going to be undefined, the called procedure must have
  747. a way of detecting this.
  748.   If one or more of your parameters is of a different type than its
  749. receiving variable, Struct will warn you. The reason for this is
  750. that Struct does not perform any sign-extension on the parameters.
  751. If you pass a WORD parameter to a LONG variable, the contents of the
  752. upper word of the receiving variable will be uncertain. On the other
  753. hand, if you pass a WORD to a BYTE, you will face the truncation
  754. problem described under the Basic Concepts chapter.
  755.   If you indicate that a procedure returns a value, a local variable
  756. with the same name as the procedure, having the type you specified,
  757. will automatically be defined within it. In the above example, a
  758. WORD-length variable named VIDEO would be defined. When the
  759. procedure ends, the contents of this variable will be passed to the
  760. result-variable specified by the calling procedure, LEN in this
  761. example. Note that VIDEO is a normal local variable in all
  762. respects, and can have its value changed at whim during the
  763. procedure's execution. Only its value at the end of the procedure
  764. matters.
  765.   Although simple procedure calls will be fine for most situations,
  766. you may occasionally need to use recursion. You can't use Proc Call,
  767. as each procedure has only one section of memory for variables, and
  768. the original contents of that section would be overwritten during
  769. the call. You must instead use the Recursive command, which takes
  770. the following form:
  771.  
  772. Recursive [<result-variable>=]<procedure-name> [([<parameter1>],
  773.   [<parameter2>][,...]>)]
  774.  
  775.   The only difference between Recursive and Proc Call is that,
  776. before making the call, Recursive saves all of the procedure's local
  777. variables onto the stack. The variables are restored after the call
  778. is complete. Recursive should be used in any situation where a
  779. procedure calls itself, or where a chain of calls among procedures
  780. could form a loop that would cause a procedure to call itself.
  781.   Be warned that if a procedure has too many local variables and
  782. there are too many Recursive calls outstanding at one time, you may
  783. overflow the stack.
  784.  
  785. * Many languages use the stack for ALL local variables. This is why
  786. * only a few procedure calls in such languages can rapidly exhaust
  787. * the stack space. Struct, on the other hand, avoids mucking about
  788. * with the stack and instead keeps all of its variables in a
  789. * separate memory area. ANY Struct program which does not use
  790. * recursion should be able to run in only a few K of stack space.
  791. * The disadvantage to this approach is that if recursion IS used,
  792. * Struct must laboriously copy all local variables onto the stack,
  793. * whereas other languages must only change the stack pointer to
  794. * allocate more space. The decision to use the copy-method was made
  795. * because recursion shows up most often in situations that Struct is
  796. * not designed to handle - i.e. mathematical simulations. Another
  797. * important factor was that in programs conforming to modern style
  798. * guidelines, procedures will have relatively few variables. Making
  799. * procedures smaller tends to reduce the "clutter" within each of
  800. * them.
  801.  
  802. -------------
  803. GLOBAL, LOCAL
  804. -------------
  805.  
  806.   These commands allow you to define variables for use in your
  807. program. Their formats are:
  808.  
  809. Global <variable1>:<type>[=<initial-value>][,<variable2>:<type>
  810.   [=<initial-value>][,...]]
  811. Local <variable1>:<type>[=<initial-value>][,<variable2>:<type>
  812.   [=<initial-value>][,...]]
  813.  
  814. where the type of the variable is either BYTE, WORD, or LONG.
  815.   For example:
  816.  
  817. Local Farfoe:BYTE, Nearfoe:WORD, Zzgo:LONG
  818.  
  819. * It's a bad idea to use BYTE length variables unless there is a
  820. * specific need to deal with byte-length values (such as using the
  821. * variable with the Peek or Poke set of commands). It takes the
  822. * 68000 no longer to read a word from memory than it does to read a
  823. * byte, and some of the 68000's hardware operations cannot deal with
  824. * byte-length values. In these cases Struct must sign-extend the
  825. * variable in question. It's often faster just to use a WORD
  826. * variable in the first place.
  827.  
  828.   Global variables can be accessed from any procedure in your
  829. program. Local variables can be accessed only from the procedure in
  830. which they are defined. Variables can be defined at any time before
  831. they are used, but good programming style would demand that all
  832. global variables are defined at the top of the program, and all
  833. local variables are defined at the beginning of the procedure to
  834. which they belong.
  835.   It is possible for a global and a local variable to both have the
  836. same name. If so, all accesses within that procedure are assumed to
  837. be directed at the local variable. This allows procedures included
  838. with the Include Struct command to work properly no matter what
  839. global variables have been defined.
  840.   If an initial-value is specified for a global variable, it will be
  841. assigned to the variable at the beginning of program execution.
  842. An initial-value for a local variable is assigned each time the
  843. procedure is called. Initial values must be resolvable at compile
  844. time.
  845.  
  846. * There's no efficiency advantage or disadvantage to using an initial
  847. * value for a variable, as opposed to simply assigning it at the
  848. * beginning of the procedure or program. The initial value feature is
  849. * provided simply as an aid to program clarity.
  850.  
  851.   One additional variable type, STACK, is being provided on an
  852. experimental basis. See the Q & A chapter for more information.
  853.  
  854. ---------------------------
  855. CONSTANT, CONSTANT EVALUATE
  856. ---------------------------
  857.  
  858.   Constants are an important part of any language. They allow values
  859. which might change to be represented by a string of characters. This
  860. string of characters can be used in the code instead of the number.
  861. Changing the number becomes a matter of changing the constant
  862. assignment, rather than the search-and-replace horror there would be
  863. if the value was "hard-wired" into the code. In Struct, constants
  864. can be used in any situation where a numeric literal could legally
  865. appear.
  866.   A constant is defined in Struct like this:
  867.  
  868. Constant <constant1>[=<value>][,<constant2>[=<value>][,...]]
  869.  
  870.   For example:
  871.  
  872. Constant Dendybar, Eldulac=10, Kessel=17
  873.  
  874.   If a value is specified, it is assigned to the constant. An
  875. initial assignment is optional; at any time after the constant has
  876. been defined, it can be assigned a value as if it was a normal
  877. variable. For example:
  878.  
  879. Dendybar=17+Errtu
  880.  
  881.   In the above example, "Errtu" could be either a variable or another
  882. constant. It is permitted to be a variable because, by default,
  883. Struct constants are actually replacement strings. When the constant
  884. Dendybar is used in a variable assignment such as:
  885.  
  886. Zzgo=12+Dendybar-19
  887.  
  888. the assignment will be evaluated as if it was:
  889.  
  890. Zzgo=12+17+Errtu-19
  891.  
  892.   One implication of this is that a constant's value is not
  893. necessarily stable. If Dendybar is set to equal a math expression
  894. which contains the constant Eldulac, and Eldulac is later changed,
  895. then the effective value of Dendybar will probably change as well.
  896.   Struct has a switch, controlled by the command Constant Evaluate,
  897. which can be used to change this. The format of the command is:
  898.  
  899. Constant Evaluate <ON/OFF>
  900.  
  901.   When Constant Evaluate is set to OFF (the default), constants will
  902. operate as described above. When it is set to ON, however, all
  903. constants will be evaluated at the time they are assigned a value,
  904. instead of the time when they are used in an equation. This value
  905. can still be composed of numeric literals and other constants, but
  906. it must reduce to a numeric literal - variables cannot be used,
  907. because their values are not known at compile time. In the situation
  908. described above, since Dendybar would be evaluated at the time of
  909. assignment, its value would be fixed and would not change if
  910. Eldulac's did.
  911.   Constant Evaluate can be switched ON or OFF as desired at any time
  912. in your program.
  913.  
  914. * If you don't care what Constant Evaluate is set to, turn it ON.
  915. * This will speed up the compiler marginally since a constant's
  916. * value will only have to be determined once, rather than every time
  917. * it is used in an equation.
  918.  
  919.   If you're careless in assigning values to constants, you can
  920. accidentally cause a loop situation. If, for example, your program
  921. contained the following:
  922.  
  923. Local A:WORD
  924. Constant B, C
  925. B=C
  926. C=B
  927. A=B
  928.  
  929. Struct's constant evaluation subroutine would replace the B with C,
  930. notice that there were still constants to replace, replace the C
  931. with B, notice that there were still constants to replace, replace
  932. the B with C...
  933.   If constant replacement seems to be happening too many times, the
  934. subroutine will eventually give up and report an error.
  935.  
  936. --------------------------------
  937. DATA REGISTER, ADDRESS REGISTER,
  938. DATA ABSOLUTE, ADDRESS ABSOLUTE,
  939. REGLOAD, REGSAVE
  940. --------------------------------
  941.  
  942.   The "Register" commands are Struct's most powerful optimization
  943. features. They allow variables to be assigned to the 68000's
  944. hardware registers, which speeds up nearly all operations on those
  945. variables. These assignments can be changed at any point in your
  946. program. The formats of the Register commands are:
  947.  
  948. Data Register [<variable1>[,<variable2>[,...]]]
  949. Address Register [<variable1>[,<variable2>[,...]]]
  950.  
  951.   For example:
  952.  
  953. Address Register I-1, I-2, I-3, Atros
  954.  
  955.   Data Register will cause each of the specified variables to be
  956. assigned to one of 7 data registers (specifically, registers D1
  957. through D7), and Address Register will assign each variable to one
  958. of 3 address registers (A3 through A5). Thus, in the example shown
  959. above, Struct would report an error because too many variables have
  960. been specified.
  961.   Once a variable has been assigned to a register, it is still "off"
  962. the register, in memory. It will stay there until the variable is
  963. used in an operation. At that time the variable will be loaded onto
  964. the register it was assigned to and the operation will be performed.
  965.   Each Data Register command overrides all previous Data Register
  966. commands. If a variable which is currently assigned to a data
  967. register does not appear in the new Data Register command, its
  968. contents are saved off to memory (if appropriate) and it is no
  969. longer considered to be a register variable. The same is true of the
  970. Address Register command.
  971.  
  972. * The best variables to assign to data registers are loop variables.
  973. * "Pointer" variables (for use with the Poke and Peek sets of
  974. * commands) are best assigned to address registers.
  975. * Many of Struct's commands will not work very well if their
  976. * parameters are address register variables. For example, the And
  977. * command would have to move its parameter to a scratch data
  978. * register, perform the AND, and then move it back to the address
  979. * register. This is because the 68000's hardware AND operation
  980. * cannot be used on an address register. Even so, performing a
  981. * command on an address register variable is usually faster than
  982. * performing it on a variable stored in memory.
  983.  
  984.   Although the Register commands are fine for most situations,
  985. there are times when it is important to have a variable assigned to
  986. a particular register (usually due to inclusion of inline assembly
  987. with the Asm command). You cannot control which registers receive
  988. which variables with the Register commands - they will be assigned
  989. in the way that Struct believes is most efficient. In order to force
  990. variables onto certain registers, you must use the "Absolute"
  991. commands, which look like this:
  992.  
  993. Data Absolute <[<variable1>][,][<variable2>][,][...]]]>
  994. Address Absolute <[<variable1>][,][<variable2>][,][...]]>
  995.  
  996.   For Data Absolute the variables listed will be assigned, in
  997. order, to D1, D2, D3, and so on. Address Absolute will perform
  998. similarly, starting with A3.
  999.  
  1000.   For example:
  1001.  
  1002. Data Absolute I-2, , NG, Fiber
  1003.  
  1004. would assign I-2 to D1, NG to D3, and Fiber to D4. Note that the
  1005. extra comma causes D2 to be skipped. Unlike the Register commands,
  1006. the Absolute commands will not affect registers that are not
  1007. specified; whatever variable was assigned to the register will
  1008. remain assigned after the command. Thus, in the above example, D2
  1009. and D5 through D7 would not be altered (unless they happened to
  1010. contain one of the three variables that WERE specified).
  1011.   As previously mentioned, the Register and Absolute commands only
  1012. assign variables to registers; the variable is not actually brought
  1013. onto the register until it is used. There are times when it is
  1014. necessary to make sure that a variable is either on or off of a
  1015. register. Inline assembly is a good example; another important
  1016. situation is the flow-of-control pitfall described below. The
  1017. solution to this is the commands Regload and Regsave:
  1018.  
  1019. Regload [<variable1>[,<variable2>[,...]]]
  1020. Regsave [<variable1>[,<variable2>[,...]]]
  1021.  
  1022.   Regload can either be used with parameters to load specific
  1023. variables onto registers, or without parameters to load all
  1024. variables currently assigned to registers. In either case, the
  1025. variables in question are immediately brought onto the registers
  1026. they have been assigned to. If all of the variables are already on
  1027. their registers, nothing will happen.
  1028.   Regsave performs identically, except that it causes variables to
  1029. be saved back to memory. The variables are still considered to be
  1030. assigned to their registers, and will be reloaded the next time they
  1031. are accessed.
  1032.  
  1033.   For example:
  1034.  
  1035. Regsave AB, CB, UY
  1036.  
  1037. will cause variables AB, CB, and UY to be taken off their registers.
  1038. No other register variables will be affected.
  1039.  
  1040. * A problem with allowing register assignments to be changed at any
  1041. * time during the program can be illustrated by this example:
  1042. *
  1043. * Data Register A
  1044. * Repeat
  1045. * A=A+B
  1046. * Until A>1000
  1047. *
  1048. * If variable A was not a register variable before this code was
  1049. * reached, it will be assigned to a register but remain in memory.
  1050. * After the Repeat loop has started, Struct will process the
  1051. * statement A=A+B. At this point it loads A onto a register and
  1052. * performs the math. Now Struct hits the Until which terminates the
  1053. * loop, and it has a problem: the flow of control may well lead back
  1054. * to the beginning of the loop, but A was not on a register at the
  1055. * beginning of the loop! The only thing that Struct can do is to set
  1056. * A and all of the other register variables back to the way they
  1057. * were at the beginning of the loop. This means that as the loop is
  1058. * executed, A will be repeatedly loaded and saved to memory, making
  1059. * the loop take longer than if no register variables had been used
  1060. * at all! But by simply adding an extra line, like so:
  1061. *
  1062. * Data Register A
  1063. * Regload A
  1064. * Repeat
  1065. * A=A+B
  1066. * Until A>1000
  1067. *
  1068. * this problem can be avoided and the loop will run very fast. This
  1069. * pitfall can occur in conjunction with all of the commands and
  1070. * statements that change the flow of control within a program. This
  1071. * includes all of the loops, the If set of statements, and the Goto
  1072. * command. So be careful to Regload what a loop needs ahead of time,
  1073. * and don't change register assignments within a loop without a very
  1074. * good reason.
  1075.  
  1076. -------------------------
  1077. IF, ELSE, END IF, BACK IF
  1078. -------------------------
  1079.  
  1080.   An If statement in Struct takes the following form:
  1081.  
  1082. If <condition1> [<&/|> <condition2> [<&/|> <condition3> [...]]]
  1083.         [...]
  1084. [program statements]
  1085.         [...]
  1086. [Else]
  1087.         [...]
  1088. [program statements]
  1089.         [...]
  1090. End If
  1091.  
  1092. where a <condition> is of the form:
  1093.  
  1094. <math-expression1><relational-operators><math-expression2>
  1095.  
  1096.   For example:
  1097.  
  1098. If A>B | A=<B & A=C | A<>B & A<>C
  1099.  
  1100.   To Struct, the ampersand (&) is a logical AND and the vertical bar
  1101. (|) is a logical OR. Unlike other languages, Struct does not allow
  1102. parentheses to be used to alter the order of comparisons. The reason
  1103. for this is the same as the reason, described in the Basic Concepts
  1104. section, for not using RPN in math expressions. Instead, in an If
  1105. statement the AND operator ALWAYS has a higher priority than the OR
  1106. operator. That means that the example given above would be
  1107. considered to be true if any of the following were true:
  1108.  
  1109. 1. A>B
  1110. 2. A=<B and A=C
  1111. 3. A<>B and A<>C
  1112.  
  1113.   Evaluating the conditions in this way can make it difficult for
  1114. certain situations to be tested for. Consider, for example, the
  1115. following If statement as it might appear in another language:
  1116.  
  1117. If (X=Y or Y=Z) and (X<>Y or Y<>Z)
  1118.  
  1119.   It would be possible to write this as an If statement in Struct,
  1120. but it would be a rather long one, as it would be necessary to check
  1121. independently for four possible situations. A simpler solution under
  1122. Struct (and one which will run faster, too) is to split the If into
  1123. two Ifs:
  1124.  
  1125. If X=Y | Y=Z
  1126.    If X<>Y | Y<>Z
  1127.         [...]
  1128. [program statements]
  1129.         [...]
  1130.    End If
  1131. End If
  1132.  
  1133. * During program execution, the checks in an If statement are
  1134. * carried out in the same order that they appear in the statement.
  1135. * This means that the OR sections most likely to be true should be
  1136. * placed at the beginning of the If statement, and the conditions
  1137. * related by ANDs should be arranged so that the ones most likely to
  1138. * fail are at the beginning of that OR section.
  1139.  
  1140.   Struct includes a statement, unique to it, called Back If. The
  1141. syntax of it is simply:
  1142.  
  1143. Back If
  1144.  
  1145.   If the program flow reaches a Back If, it will be directed back to
  1146. the beginning of the most recent If statement. The condition(s) in
  1147. the If statement will be re-evaluated, and the program flow will be
  1148. altered accordingly. This may result in the path to the Back If
  1149. being taken again, which will eventually cause execution to return
  1150. to the If again.
  1151.   An If statement which includes a Back If in one of its blocks is
  1152. very similar to a While/Wend loop: as long as the condition is true
  1153. (or, perhaps, as long as it is false) the same sequence of program
  1154. code will be executed over and over again. In many situations,
  1155. however, Back If is more intuitive than While/Wend. This is
  1156. especially true in cases where taking the "loop" path is considered
  1157. to be an unusual event.
  1158.  
  1159. ----------------------------
  1160. FOR, NEXT, REPEAT, UNTIL,
  1161. WHILE, WEND, LOOP, END LOOP,
  1162. ZEROLOOP, ZEND, EXIT
  1163. ----------------------------
  1164.  
  1165.   The format of a For/Next loop is:
  1166.  
  1167. For <variable>=<start-expression>;<end-expression>
  1168.   [;<step-expression>]
  1169.         [...]
  1170. [program statements]
  1171.         [...]
  1172. Next
  1173.  
  1174.   For example:
  1175.  
  1176. For Millions=1;1000000;17
  1177.  
  1178.   The For loop structure repeats a block of code over and over,
  1179. altering the loop variable on each iteration, until the loop
  1180. variable has passed the value of the end-expression. The loop
  1181. variable is set to the start-expression before the first iteration
  1182. begins, and is altered by the value of the step-expression at the
  1183. end of each iteration. If no step-expression is specified, the
  1184. variable is incremented by 1. A step-expression which resolves to a
  1185. negative number is legal.
  1186.   The loop's to-value and step-value are computed and stored in
  1187. memory before the first iteration of the loop. If they are based
  1188. upon a variable, changing the value of that variable during the loop
  1189. will have no effect on the number of iterations performed.
  1190.  
  1191. * You should try to avoid having to use a step-expression. If you
  1192. * use a step-expression, the executable code must do additional work
  1193. * at the end of each iteration because of the possibility that the
  1194. * step will resolve to a negative number.
  1195.  
  1196.   The format of a Repeat/Until loop is:
  1197.  
  1198. Repeat
  1199.         [...]
  1200. [program statements]
  1201.         [...]
  1202. Until <expression>
  1203.  
  1204.   For example:
  1205.  
  1206. Until Cinemaware=Dead
  1207.  
  1208.   The Repeat/Until loop repeats a block of code, over and over,
  1209. until the specified expression is determined to be true. This check
  1210. is carried out at the end of the block of code, where the Until
  1211. appears. Normally, the expression will initially be false. During
  1212. one of the iterations one of the expression's components will be
  1213. altered so that the expression becomes true. However, even if the
  1214. expression is initially true, the block of code will always be
  1215. executed at least once.
  1216.  
  1217.   The format of a While/Wend loop is:
  1218.  
  1219. While <expression>
  1220.         [...]
  1221. [program statements]
  1222.         [...]
  1223. Wend
  1224.  
  1225.   For example:
  1226.  
  1227. While Streamline=<Macek
  1228.  
  1229.   In a While/Wend loop, the specified expression is first checked
  1230. for truth. If it is false, the block of code is skipped and never
  1231. executed. If it is true, the block is executed once. The expression
  1232. is again checked for truth, and if it is still true then the block
  1233. will be executed again. A check for truth will again be made, and
  1234. this will continue until the expression is finally determined to be
  1235. false.
  1236.  
  1237.   The format of a Loop/End Loop structure is:
  1238.  
  1239. Loop [start-assignment];[truth-expression];[step-assignment]
  1240.         [...]
  1241. [program statements]
  1242.         [...]
  1243. End Loop
  1244.  
  1245.   For example:
  1246.  
  1247. Loop X=0;X<10;X=X+1
  1248.  
  1249.   When the Loop statement is reached the assignment in the
  1250. start-expression, if specified, is performed. The block of code is
  1251. then executed. At the end of the block the assignment in the
  1252. step-expression, if one is provided, is performed. The
  1253. truth-expression is then evaluated. If it is determined to be true,
  1254. the block is executed again. The step-expression and
  1255. truth-expression are then used once again.
  1256.   The example above is the same as a For/Next loop from zero to ten.
  1257.   Note that the truth-expression is optional. If none is given, it
  1258. will result in an infinite loop. The only way to break out of the
  1259. loop is with the Exit statement.
  1260.  
  1261. * In the compiled code, the start-expression is treated as if it had
  1262. * appeared right before the Loop statement, and the step-expression
  1263. * is treated as if it appeared right before the End Loop. There's no
  1264. * efficiency advantage or disadvantage to including them in the Loop
  1265. * statement, so simply do whatever seems most intuitive.
  1266.  
  1267.   The format of a Zeroloop structure is:
  1268.  
  1269. Zeroloop <variable>
  1270.         [...]
  1271. [program statements]
  1272.         [...]
  1273. Zend
  1274.  
  1275.   For example:
  1276.  
  1277. Zeroloop Chalker
  1278.  
  1279.   The Zeroloop construct is unique to Struct.
  1280.   A Zeroloop is like a For/Next loop from the variable's current
  1281. value to zero, inclusive. The code within the loop will initially be
  1282. performed without changing the value of the loop variable. When the
  1283. Zend statement is reached, the value of the variable will be reduced
  1284. by one. If the variable was equal to zero during the previous
  1285. iteration, the loop terminates. Otherwise, the program flow branches
  1286. back to the Zeroloop statement and the block is executed again.
  1287.   A Zeroloop has a potential problem, because it is built around a
  1288. 68000 instruction called DBRA. The variable is considered to be an
  1289. UNSIGNED number. This means that, because of the way signed numbers
  1290. are implemented, a variable with an initially negative value will
  1291. keep being decremented until it finally "turns over" to a positive
  1292. value. The loop will continue to repeat until that positive number
  1293. is finally reduced to zero.
  1294.  
  1295. * It is very important to make sure that a Zeroloop's variable is a
  1296. * WORD or LONG. If you use a BYTE, it will work, but Struct will
  1297. * have to sign-extend the variable on each iteration, which wastes
  1298. * time. Furthermore, the variable should ALWAYS be assigned to a data
  1299. * register, for the DBRA instruction is only designed to work with
  1300. * data registers. If the variable is in memory, Struct must load it
  1301. * onto a register to perform the DBRA. There is little point in using
  1302. * Zeroloop without having the variable on a data register.
  1303.  
  1304.   The format of the Exit statement is simply:
  1305.  
  1306. Exit
  1307.  
  1308.   When the Exit statement is encountered, the program flow will
  1309. immediately jump to the end of the most recently defined loop
  1310. structure, as if the loop had terminated normally. A loop variable,
  1311. if there was one, will remain set to whatever it was when the Exit
  1312. statement was encountered.
  1313.  
  1314. ----------------------------
  1315. OPEN LIBRARY, LIB CALL, CALL
  1316. ----------------------------
  1317.  
  1318.   A very important feature of any language is the ability to make
  1319. calls to external functions. Struct implements this feature with the
  1320. Lib Call command and a text file, "StructLibraryDefinitions", that
  1321. contains all necessary information on those functions. Using the
  1322. procedure described in Appendix A, any function which is part of a
  1323. library conforming to standard Amiga library specifications can be
  1324. added to the library definition file and then used via the Lib Call
  1325. command.
  1326.   Before any function can be accessed, its parent library must be
  1327. opened via the Open Library command:
  1328.  
  1329. Open Library <library>
  1330.  
  1331.   Each library only needs to be opened once per program. The Open
  1332. Library command does not itself generate any code - it just tells
  1333. the compiler that you're going to be using functions contained in
  1334. that library. The library will be opened during the program's
  1335. startup phase.
  1336.   The pointer to the library will be placed in a global LONG
  1337. variable called "LIBRARY<library>BASE". For example, if you've just
  1338. opened the Graphics library, its pointer will be in a variable
  1339. called LIBRARYGRAPHICSBASE. If necessary, you can use this pointer
  1340. to access the library's data structures.
  1341.   Once the library has been opened, an actual function call is made
  1342. with the Lib Call command:
  1343.  
  1344. Lib Call [<result-variable>=]<function-name>[(<parameter1>
  1345.   [,<parameter2>[,...]])]
  1346.  
  1347.   For example:
  1348.  
  1349. Lib Call CopyBuffer1=AllocMem(8192,0)
  1350.  
  1351.   Using the information found in the library definition file, the
  1352. parameters, if any, are loaded onto their proper registers and the
  1353. function call is made. Specifying a result-variable is optional; if
  1354. one is provided then the function's return value is placed into it.
  1355. The function is assumed to return a LONG, so if the result-variable
  1356. is smaller than that the return value will be truncated.
  1357.   At times you may need to call functions which are not part of a
  1358. library. This can be done with the Call command, which looks like
  1359. this:
  1360.  
  1361. Call <memory-location-expression>
  1362.  
  1363.   The memory-location-expression will be resolved, and the 68000
  1364. instruction JSR will be used to call the subroutine at that address.
  1365. The address is assumed to point to valid machine language code; if
  1366. it does not, a guru or crash is likely.
  1367.   The Call command does not allow parameters or return variables to
  1368. be specified. If you need them, you can use the Absolute and
  1369. Regload commands to assign variables to specific registers. You can
  1370. use this method to pass parameters, and the called routine can
  1371. change the contents of one of the registers before returning to pass
  1372. a value back to the main program.
  1373.  
  1374. -------------------------
  1375. POKE, BYTEPOKE, WORDPOKE,
  1376. LONGPOKE, PEEK, BYTEPEEK,
  1377. WORDPEEK, LONGPEEK
  1378. -------------------------
  1379.  
  1380.   The "Poke" set of commands is designed to alter memory. The
  1381. formats of these commands are:
  1382.  
  1383. Poke <memory-location-expression>,<value-expression>
  1384. Byte Poke <memory-location-expression>,<value-expression>
  1385. Word Poke <memory-location-expression>,<value-expression>
  1386. Long Poke <memory-location-expression>,<value-expression>
  1387.  
  1388.   For example:
  1389.  
  1390. Word Poke YPos*8+XPos, Traverser+3
  1391.  
  1392.   The Poke commands all work very much the same: the
  1393. value-expression is computed, and then moved into the memory
  1394. location which is determined by computing the
  1395. memory-location-expression. The difference is in the size of the
  1396. memory access. The Byte, Word, and Long versions of the Poke command
  1397. force a byte, word, or longword at the specified address to be
  1398. affected.
  1399.   The general version of Poke, however, forces Struct to determine
  1400. the size of the memory access. Struct will examine the
  1401. value-expression and set the size of the memory access to the
  1402. type of the largest variable or constant it finds there. This, of
  1403. course, is very error-prone, so the general Poke is only provided as
  1404. a shorthand for programmers who know what they're doing. You should
  1405. never use the general Poke unless you're SURE that the types of the
  1406. variables involved will never be changed.
  1407.  
  1408.   The "Peek" set of commands reads memory into variables. Their
  1409. formats are:
  1410.  
  1411. Peek <variable>=<memory-location-expression>
  1412. Byte Peek <variable>=<memory-location-expression>
  1413. Word Peek <variable>=<memory-location-expression>
  1414. Long Peek <variable>=<memory-location-expression>
  1415.  
  1416.   For example:
  1417.  
  1418. Byte Peek BeamPos=Screenbase+Offset
  1419.  
  1420.   The Peek commands determine the value of the
  1421. memory-location-expression, then move the value found at that memory
  1422. location into the specified variable. For the general Peek, the size
  1423. of the memory access is the size of the receiving variable. In most
  1424. situations this is appropriate, but if it is not then the Byte,
  1425. Word, and Long versions can be used to cause a byte, word, or long
  1426. memory access instead. If necessary, the value found at the memory
  1427. location will be sign-extended or truncated as appropriate to make
  1428. it fit properly in the receiving variable.
  1429.  
  1430. ---------------------
  1431. BYTE DATA, WORD DATA,
  1432. LONG DATA, STRING
  1433. ---------------------
  1434.  
  1435.   This set of commands allows the inclusion of predefined data in
  1436. the object code. Since it is much easier to include large amounts of
  1437. data by using inline assembly and sending an assembler inclusion
  1438. directive, these commands are normally used for small amounts of
  1439. data, such as lookup tables and ASCII text. Their formats are:
  1440.  
  1441. Byte Data [(label)] <data>
  1442. Word Data [(label)] <data>
  1443. Long Data [(label)] <data>
  1444. String [(label)] <data>
  1445.  
  1446.   For example:
  1447.  
  1448. String (PrintString1) "IQ Stripper",10,"by Ren Hoek",10
  1449.  
  1450.   Normally, the data will consist of either numbers or strings of
  1451. text delimited by quotes, separated by commas. As seen in the
  1452. example, both can be used in the same line. If a label is provided,
  1453. it will point to the beginning of the given data.
  1454.   These commands can be used either inside or outside of a
  1455. procedure. If they are given inside a procedure, Struct will "hang
  1456. on" to the data until it reaches the end of the procedure, at which
  1457. point it will be output to the object file. Thus, there is no
  1458. problem with the data appearing in the middle of blocks of code.
  1459.   If a label is specified with the command, and the command is used
  1460. from within a procedure, the label will be defined as a local label.
  1461. If the command is used outside of a procedure, it will be defined as
  1462. a global label.
  1463.   Struct does not actually parse any of the given data; it simply
  1464. passes it on to the assembler. Thus, errors in the data will not be
  1465. flagged by Struct and will cause the assembler to report an error.
  1466.   The difference between the Byte, Word, and Long versions is that
  1467. each numerical value will be output as a byte, word, or longword
  1468. respectively. Strings of text normally cannot be used as part of the
  1469. data in the Word Data or Long Data commands; since text is assumed
  1470. to be part of the ASCII character set, in which all characters are
  1471. byte-length by definition, assemblers will usually refuse to output
  1472. it in a larger format.
  1473.   The String command is identical to the Byte Data command, except
  1474. that a null character is appended to the end of the data. This is
  1475. convenient if the data is going to be used with the Print command or
  1476. some operating system functions, as they require null-terminated
  1477. strings. If the example shown above was used with the Print command,
  1478. the result would be:
  1479.  
  1480. IQ Stripper
  1481. by Ren Hoek
  1482.  
  1483.   This is because each of the 10s is a linefeed, which is the
  1484. Amiga's end-of-line character. Another useful number to know about
  1485. is 34, which the the ASCII code for a quote.
  1486.  
  1487. -----------------------
  1488. INCLUDE STRUCT, INCLUDE
  1489. CONSTANT
  1490. -----------------------
  1491.  
  1492.   The format of Include Struct is:
  1493.  
  1494. Include Struct <filename>
  1495.  
  1496.   The filename should not have quotes around it. If a file with that
  1497. name exists, Struct will immediately compile it as if it was a part
  1498. of the original source file. When the end of the included file has
  1499. been reached, Struct will continue compiling the original file where
  1500. it left off. Included files can themselves contain the Include
  1501. Struct command.
  1502.   This command, used in conjunction with local variables, allows the
  1503. creation of a "library" of external procedures which can be included
  1504. in larger projects as necessary.
  1505.   In order to deal with external functions in general, and the Amiga
  1506. operating system routines in particular, it is important to be able
  1507. to use external files containing constant definitions. Struct's
  1508. support for these files is far from perfect, but should be
  1509. sufficient for most situations. Support is provided through the
  1510. Include Constant command, which looks like this:
  1511.  
  1512. Include Constant <filename>
  1513.  
  1514.   Most assembly language include files written to Commodore-Amiga
  1515. specifications should work with this command. Others may require
  1516. some alterations. Struct will look for the following things within
  1517. the constant file:
  1518.  
  1519. <constant> EQU <value>
  1520. <constant>=<value>
  1521. <constant> EQU <value> << <shift-amount>
  1522. <constant>=<value> << <shift-amount>
  1523.  
  1524.   The first two forms will cause a constant to be defined and
  1525. assigned the specified value. The second two forms cause the value
  1526. to be bit-shifted to the left by the shift-amount. This is
  1527. equivalent to multiplying the variable by 2 to the power of the
  1528. shift-amount. The value can be represented in hexadecimal if it is
  1529. preceeded by a dollar sign.
  1530.   The name of the constant must start at the left margin.
  1531.  
  1532. STRUCTURE <structure-name>,<start-value>
  1533.  
  1534.   This will cause an internal variable to be set to the start-value,
  1535. which is normally zero. The structure-name is provided only for
  1536. human benefit, and is ignored by Struct. The STRUCTURE directive
  1537. must not start at the left margin.
  1538.  
  1539. BYTE <constant>
  1540. UBYTE <constant>
  1541. WORD <constant>
  1542. UWORD <constant>
  1543. SHORT <constant>
  1544. USHORT <constant>
  1545. LONG <constant>
  1546. ULONG <constant>
  1547. APTR <constant>
  1548. BOOL <constant>
  1549. FLOAT <constant>
  1550. CPTR <constant>
  1551. RPTR <constant>
  1552.  
  1553.   All of these directives will cause the specified constant to be
  1554. defined and assigned the current value of the internal variable. The
  1555. internal variable's value will then be increased by a certain amount,
  1556. which depends on the directive used. APTR (Address PoinTeR), for
  1557. example, will raise the value by 4. The directive must not start at
  1558. the left margin.
  1559.  
  1560. STRUCT <constant>,<value>
  1561.  
  1562.   The specified constant will be defined and assigned the current
  1563. value of the internal variable. The variable will then be
  1564. incremented by the specified value. The STRUCT directive must not
  1565. start at the left margin.
  1566.  
  1567. LABEL <constant>
  1568.  
  1569.   The specified constant will be defined and assigned the current
  1570. value of the internal variable. The variable will not be altered.
  1571. The LABEL directive must not start at the left margin.
  1572.  
  1573. IFND <constant>
  1574. ENDIF
  1575.  
  1576.   If the specified constant has not been defined, the data between
  1577. the IFND and the ENDIF will be processed. Otherwise, it will be
  1578. ignored. This directive must not start at the left margin.
  1579.  
  1580. INCLUDE "<filename>"
  1581.  
  1582.   Struct will stop reading the current constant file and start
  1583. reading a different one with the specified filename. The quotes
  1584. around the filename are required. When Struct reaches the end of the
  1585. new constant file, it will continue reading the old one where it
  1586. left off. Included files can also contain the INCLUDE directive. It
  1587. must not start at the left margin.
  1588.   It is important to remember that Struct's mathematical order of
  1589. operations is not the same as other languages, and so some
  1590. expressions will need to be altered to still achieve the desired
  1591. result. If Constant Evaluate is set to ON before the constant file
  1592. is included, "scratch constants" can be used as temporary variables
  1593. to help rewrite expressions. This is better than simply doing the
  1594. math yourself and replacing the value, because people looking at the
  1595. constant file later on can more easily see how the result was
  1596. obtained.
  1597.  
  1598. ------------
  1599. ASM, END ASM
  1600. ------------
  1601.  
  1602.   Occasionally, you'll need to do things that can't be done in
  1603. Struct, or can't be done RIGHT. For these situations, you need Asm
  1604. and End Asm, which are as follows:
  1605.  
  1606. Asm
  1607.      [...]
  1608. [assembly code]
  1609.      [...]
  1610. End Asm
  1611.  
  1612.   Everything between Asm and End Asm will be passed directly through
  1613. to the assembler, whether or not it is actually valid assembly code.
  1614. This means that any mistakes in the assembly code will be reported
  1615. by the assembler, not by Struct.
  1616.   In order to use inline assembly properly, you need to understand
  1617. how Struct manages its variables and the 68000 hardware registers.
  1618. During a program's startup code, enough space is allocated via the
  1619. Exec AllocMem function to hold all of the program's variables. The
  1620. pointer to this memory area is permanently placed in register A2,
  1621. and A2 is used with an index for all accesses to the variable base.
  1622. Currently, the way to access a global variable is:
  1623.  
  1624. StructGlobalvar<variable-name>(A2)
  1625.  
  1626. and the way to access a local variable is:
  1627.  
  1628. StructP<current-procedure>var<variable-name>(A2)
  1629.  
  1630.   For example:
  1631.  
  1632. StructPFaramirvarSimarillion(A2)
  1633.  
  1634.   At some point in the assembly file there will be an equate such
  1635. as:
  1636.  
  1637. StructPFaramirvarSimarillion EQU 58
  1638.  
  1639. which will make everything work out all right. This method may
  1640. change at some point in the future, so it is a much better idea to
  1641. use one of the Absolute commands and a Regload command to bring the
  1642. variable onto a known register.
  1643.   Struct uses data registers D1-D7 and address registers A3-A5 to
  1644. hold register variables. You should make use of these registers only
  1645. if you have made SURE that they are empty. Register A6, by Amiga
  1646. convention, is used to hold library bases. Struct keeps track of
  1647. which library pointer is on A6 at any particular time, so if you
  1648. need to call library functions you must save the contents of A6 and
  1649. restore them after you're done. A7 is always used as the hardware
  1650. stack, so that leaves registers D0, A0, and A1. Struct uses these
  1651. only as scratch registers, so they can be safely used by your
  1652. assembly code.
  1653.   When accessing the variable base, remember to make sure that the
  1654. variable in question is not on a register. Struct saves the contents
  1655. of register variables to memory only when they are un-assigned or a
  1656. Regsave command is used. The variable's value in memory will almost
  1657. certainly not match its value on the register.
  1658.  
  1659. ----
  1660. GOTO
  1661. ----
  1662.  
  1663.   Goto causes a jump in program flow to the specified label. Its
  1664. format is:
  1665.  
  1666. Goto <label>
  1667.  
  1668.   For example:
  1669.  
  1670. Goto Iczer-4
  1671.  
  1672.   The label must be a local label, defined at some point within that
  1673. procedure. It is not legal to jump to global labels, or to labels
  1674. in other procedures. You cannot jump to labels defined with the
  1675. "Data" set of commands, as those labels actually point to locations
  1676. outside of the procedure.
  1677.  
  1678. -----
  1679. PRINT
  1680. -----
  1681.  
  1682.   The format of the Print command is:
  1683.  
  1684. Print <memory-location-expression>
  1685.  
  1686.   The memory-location-expression can include labels, variables, and
  1687. constants. After it is resolved, a search will be performed for the
  1688. first null character after the computed address. The address is
  1689. assumed to be that of a string, and the null character is assumed to
  1690. mark the end of it. The null character is not considered to be part
  1691. of the string. The address of the string, and its length, will be
  1692. passed to the Dos Write function with instructions to print it to
  1693. standard output. Normally, this is the CLI from which the program
  1694. was run.
  1695.   Before the Print command can be used, the Dos library must have
  1696. been opened with an Open Library command. Print does not actually
  1697. use the Output or Write offsets in the library definition file; it
  1698. simply requires the Dos library pointer that Open Library provides.
  1699.   Struct's Print is very limited compared to other languages, but
  1700. is enough for simple tasks.
  1701.  
  1702. ------------------
  1703. AND, OR, EOR, NOT,
  1704. NEGATE
  1705. ------------------
  1706.  
  1707.   This set of commands deals with bit operations.
  1708.   The And command looks like this:
  1709.  
  1710. And <mask-expression>,<variable>
  1711.  
  1712.   The mask-expression will be evaluated, and a mathematical AND will
  1713. be performed between it and the contents of the variable. The result
  1714. will be left in the variable.
  1715.   The format of the Or command is:
  1716.  
  1717. Or <mask-expression>,<variable>
  1718.  
  1719.   A mathematical OR will be performed between the mask-expression
  1720. and the value held in the variable. The result will be left in the
  1721. variable.
  1722.   The Eor command is used with:
  1723.  
  1724. Eor <mask-expression>,<variable>
  1725.  
  1726.   The result of the mask-expression will be determined, and a
  1727. mathematical EOR will be done between it and the contents of the
  1728. variable. The result will be left in the variable.
  1729.  
  1730. * And, Or, and Eor will usually work much faster if the variable is
  1731. * on a data register.
  1732.  
  1733.   The Not command is:
  1734.  
  1735. Not <variable>
  1736.  
  1737.   An operation called "one's complement" will be performed on the
  1738. value of the variable. This "inverts" the bits that make up the
  1739. number, turning all ones to zeros and vice versa. The result is left
  1740. in the variable
  1741.  
  1742.   The Negate command looks like this:
  1743.  
  1744. Negate <variable>
  1745.  
  1746.   Negate replaces the value in the variable by its "two's
  1747. complement". This is done by taking the one's complement and adding
  1748. one to it. Because of the way signed numbers are represented inside
  1749. the computer, this simply reverses the sign of the number. It is
  1750. exactly the same as subtracting the value in the variable from zero,
  1751. but faster.
  1752.  
  1753. ------------------------
  1754. REMAINDER, LOAD ADDRESS,
  1755. VERSION
  1756. ------------------------
  1757.  
  1758.   Remainder is a "dirty" command that should be used with caution.
  1759. Its syntax is:
  1760.  
  1761. Remainder <variable>
  1762.  
  1763.   For example:
  1764.  
  1765. Remainder ShipExcess
  1766.  
  1767.   Whenever hardware division is performed, the result is both a
  1768. quotient and a remainder, both of which are left in the 68000
  1769. register D0. Struct normally takes only the quotient for the
  1770. result, and ignores the remainder. The Remainder command, used
  1771. immediately after a hardware division, tells Struct to take the
  1772. remainder from D0 and place in the specified variable, possibly
  1773. sign-extending or truncating.
  1774.   This command is unreliable for several reasons. First, anything
  1775. that affects register D0, which Struct uses regularly as a scratch
  1776. register, will erase the remainder. This could be a multiplication
  1777. or another division in the same math expression as the original
  1778. divison. Second, there will only be a remainder if actual HARDWARE
  1779. division is performed. For example, this assignment:
  1780.  
  1781. TRZ=5+16/8
  1782.  
  1783. will be completely evaluated at compile time, and TRZ will be
  1784. assigned a value of 2. The 68000 division instruction is never used,
  1785. so there is no remainder on D0, and the Remainder command will not
  1786. work properly.
  1787.   Further, using the Remainder command affects the contents of D0,
  1788. so it cannot be used twice in a row.
  1789.   Often, it is better to do something like this:
  1790.  
  1791. Quot=Satori/3
  1792. Remain=Quot*3
  1793. Remain=Satori-Remain
  1794.  
  1795. or
  1796.  
  1797. Quot=Satori/3
  1798. Remain=Quot*3-Satori
  1799. Negate Remain
  1800.  
  1801. both of which will work. Remainder, of course, is faster, but
  1802. sometimes that just isn't a good enough reason.
  1803.   The format of Load Address is:
  1804.  
  1805. Load Address <variable>=<program-element>
  1806.  
  1807.   For example:
  1808.  
  1809. Load Address Caller=PrintString1
  1810.  
  1811.   Load Address places the memory location of the program-element in
  1812. the specified variable, which must be of type LONG. The
  1813. program-element must be either a label, a procedure, or a variable.
  1814. If two or more program-elements have the same name - for example, a
  1815. local variable and a global label - the compiler will give labels
  1816. priority over procedures, procedures priority over variables, and
  1817. local elements priority over same-type global elements. In the
  1818. example mentioned above, the compiler would load the address of the
  1819. global label into whatever variable was given.
  1820.   This command is normally used for examining data included with the
  1821. "Data" set of commands. Be aware that making any changes to that
  1822. data will destroy the program's reentrant property, as you will have
  1823. a self-modifying program.
  1824.   It is possible to have an external program call a Struct procedure
  1825. by using the Load Address command and passing the result to the
  1826. other program. Keep in mind, though, that the external program will
  1827. need to have the Struct program's variable base pointer on register
  1828. A2 at the time of the call. If this is not done, the computer will
  1829. almost certainly crash.
  1830.   The Version command looks like this:
  1831.  
  1832. Version <version-number>
  1833.  
  1834. For example:
  1835.  
  1836. Version 1.10
  1837.  
  1838.   Version generates no code. It is simply a way of saying, "This
  1839. program was made with this version of Struct". If the version number
  1840. given is higher than the version of Struct that is being used to
  1841. compile the program, Struct will generate a warning during the
  1842. compilation process. There will be no other direct effects.
  1843.  
  1844.  
  1845.  
  1846. 6. Optimization
  1847. ---------------
  1848.   Since the whole point behind Struct is speed, it seems appropriate
  1849. that there be a separate chapter on optimization.
  1850.   Struct tries very hard to make good object code out of what you
  1851. give it. But it's only a computer program, and computer programs
  1852. aren't as smart as humans. The way in which you write your code
  1853. will have an effect - possibly a very great one - on how fast it
  1854. runs.
  1855.   Most optimization is simply common sense.  Use WORD variables
  1856. whenever reasonable. BYTEs are no faster (and may be slower) and
  1857. LONGs are slower. Don't split your program into lots of procedures
  1858. unless there's a need to do so (normally either clarity or re-use of
  1859. code). Procedure calls take time. The example programs are
  1860. modularized much more than they need to be, simply because they're
  1861. example programs. If you've been reading this manual carefully you
  1862. should already have picked up on these, as well as others.
  1863.   The greatest enigma is the Register commands. How best to use
  1864. them? You know that variables used as pointers belong on address
  1865. registers, and variables used as indexes, counters, or holding areas
  1866. belong on data registers. But that leaves a lot of leeway. How do
  1867. you know when to put a variable on a register? When to change the
  1868. register assignments? How to tell if a variable shouldn't be on a
  1869. register at all?
  1870.   I've given this a great deal of thought myself, and I've come up
  1871. with some guidelines. These three principles don't apply to all
  1872. situations - they're only generalities - but they should work well
  1873. most of the time. As always, use your head before you use your
  1874. keyboard.
  1875.  
  1876. Principle #1: The time required to execute a 68000 instruction is
  1877. the time it takes to perform the memory accesses.
  1878.  
  1879. Justification: This is far more true than it first appears. The time
  1880. it takes the 68000 to do something is measured in what are called
  1881. "clock cycles". It takes 4 cycles to read/write a word to/from
  1882. memory. It takes 8 cycles to access a longword (as the 68000 does
  1883. this by reading two words). All instructions are one word long (not
  1884. counting their parameters) and so take 4 cycles to read.
  1885.   Consider this assignment statement:
  1886.  
  1887. A = B + C - D
  1888.  
  1889.   Assume that all variables are of type LONG. There are four
  1890. variables that need to be accessed here, so that's going to take 32
  1891. clock cycles. We can see that there is an addition and a subtraction
  1892. involved, each of which will take 4 cycles. Also, each instruction
  1893. contains a pointer to the variable that is going to be added or
  1894. subtracted. Each pointer is one word. Finally, the intermediate
  1895. value is going to have to be saved off into variable A. That will
  1896. take 4 cycles to read the MOVE instruction, 4 for the pointer to A,
  1897. and 8 to actually save intermediate value to A.
  1898.   The result: 64 cycles. Is that right? Not quite. We missed an
  1899. operation. We counted B, C, and D as being accessed, and mentioned
  1900. that C would be added to the intermediate value and D subtracted,
  1901. but we forgot about B. The intermediate value is actually going to
  1902. be SET to B originally, after which time C is added and D is
  1903. subtracted. That setting takes a MOVE instruction (4 cycles) and a
  1904. pointer to B (4 cycles). The final total is 72 cycles. The time it
  1905. would REALLY take the 68000 to do this is 80 cycles. That's because
  1906. the addition and subtraction instructions actually take 8 cycles,
  1907. and not the 4 we were assuming. We were very close though. If
  1908. they're all register variables, we don't need a pointer or a memory
  1909. access to use the variables; we only count the two MOVE instructions
  1910. and an ADD and SUBtract. Total time then is only 24 cycles!
  1911.   This principle helps you when you're trying to figure out if
  1912. something is accessed enough times to make it worth being a register
  1913. variable. With a WORD variable, it's going to take 10 cycles to
  1914. bring it onto the register and another 10 to save it off eventually.
  1915. If the variable is only used once before something makes it get
  1916. saved off, it's not worth it to assign it to a register.
  1917.   Remember that this principle is only a generalization. Some
  1918. instructions do take processing time in addition to their memory
  1919. access time - the multiplication instruction, at about 70 cycles, is
  1920. a good example. But the simple instructions - moving, adding,
  1921. subtracting, sign extending, and others - form the backbone of a
  1922. compiled Struct program.
  1923.  
  1924. Principle #2: The time spent outside a loop is insignificant
  1925. compared to the time spent within it.
  1926.  
  1927. Justification: Consider, first, an extreme case. You've just written
  1928. a demo. This demo contains a master loop that draws all of the
  1929. screen elements, updates them, plays the next fragment of music, and
  1930. then does it all again. The part of the demo outside the loop are
  1931. the setup and cleanup routines - and if the user spends a few
  1932. minutes looking at the demo, the percentage of time spent executing
  1933. those outside routines will be very small indeed. Clearly, much more
  1934. effort should go into optimizing the code inside the loop than
  1935. outside it. In this case, no effort at all should be wasted on the
  1936. setup routines, for no one is going to care whether the demo starts
  1937. up in one second or three. (The same reasoning was used in the UOut
  1938. example program, and no attempts at all were made to optimize the
  1939. initialization and finalization routines)
  1940.   Now consider a more realistic case, say a For/Next loop from one
  1941. to ten. The For/Next loop contains the following line of code:
  1942.  
  1943. A = B + C - D
  1944.  
  1945.   In the justification of the previous principle, we saw that this
  1946. line takes 24 cycles with register variables, and 80 without.
  1947. Assuming that we assign register variables before the loop, and
  1948. un-assign them afterwards, the total time related to this line of
  1949. code is 240+128=368 cycles with register variables, and 800+0=800
  1950. without. We had to pay 64 cycles to bring the variables onto the
  1951. registers, and another 64 to save them off, but we got a much
  1952. quicker assignment statement in return. With a higher loop value
  1953. and/or more statements in the loop using these variables, the time
  1954. savings would be so great that the register loading and unloading
  1955. cost would become trivial.
  1956.   Thus, this principle dictates that register assignments should be
  1957. changed at the start of each loop, even if those changes force the
  1958. un-assignment of variables that were still being used by the code
  1959. outside of the loop. (Another Register command could be used at the
  1960. end of the loop to set things back the way they were) Remember to
  1961. put the Register command(s) and the Regload command OUTSIDE of the
  1962. loop, to avoid the flow-of-control pitfall.
  1963.  
  1964. Principle #3: In every If-like construct, one block of code will be
  1965. executed more often than the other.
  1966.  
  1967. Justification: Consider this fragment of code, adapted from the
  1968. ReadLine procedure of the UOut example program:
  1969.  
  1970. data register length,z,bufferpos,buffercount
  1971. address register linepointer,linemem,bufferstart
  1972. regload
  1973. repeat
  1974.   bytepeek z=bufferstart+bufferpos
  1975.   bytepoke linemem+length,z
  1976.   bufferpos=bufferpos+1
  1977.   length=length+1
  1978.   if bufferpos=buffercount
  1979.     libcall buffercount=read(srcfilehandle,bufferstart,8192)
  1980.     bufferpos=0
  1981.   end if
  1982. until z=10 | buffercount=0 | length=256
  1983.  
  1984.   The first question to ask is, "What does this code actually do?"
  1985.   This procedure maintains an 8k buffer which contains part of the
  1986. file being processed. Whenever the higher-level routines are ready
  1987. to process another line of uuencoded data, they call this procedure
  1988. to get it. The procedure must copy the next line from the buffer
  1989. area, pointed to by BUFFERSTART, into the one-line buffer used by
  1990. the rest of the program, pointed to by LINEMEM. BUFFERCOUNT and
  1991. LENGTH mark how far the procedure has gone into each memory area.
  1992.   Notice that every single variable used in this code has been
  1993. assigned to a register - except for SRCFILEHANDLE. Why not
  1994. SRCFILEHANDLE?
  1995.   We can apply principle #3 to the If statement in this code
  1996. fragment if we consider it to have a blank Else block. Thus, either
  1997. the If block will be executed most of the time, or nothing will
  1998. happen most of the time.
  1999.   But the point of this If statement is to check to see if the
  2000. buffer is empty. How often will that happen? Well, we have an 8k
  2001. buffer, and we process one character per iteration, so it will take
  2002. 8,192 iterations before we need the code in the If block. Looking at
  2003. the purpose of the UOut program, we see that all lines should be
  2004. under 80 columns, and thus this procedure will likely be called over
  2005. 100 times before the If-block code is used. Clearly, loading and
  2006. unloading SRCFILEHANDLE at the end of each iteration is not
  2007. worthwhile.
  2008.   This is an example of a situation in which our human intelligence
  2009. is more powerful than a computer's number-crunching ability. A
  2010. computer program using principle #2 can run circles around you. It
  2011. doesn't need principle #1 because it can be programmed to know
  2012. exactly how long each operation will take. But it has no idea what
  2013. the program actually DOES, so it can't tell which branch of the If
  2014. will be taken more often. Principle #3 is usable only by humans.
  2015. With care, it can tip the optimization balance in your favor.
  2016.  
  2017.   The most important piece of advice that I can give you is that you
  2018. should look at your program and THINK carefully about what you're
  2019. really asking the computer to do.
  2020.  
  2021.  
  2022.  
  2023. 7. Q & A
  2024. --------
  2025. Q: What if the user tries to run my program with parameters? How can
  2026. I get ahold of them?
  2027.  
  2028. A: Two global variables are automatically defined before your
  2029. program begins. CommandPointer, a LONG, holds the address of the
  2030. parameter string that the user typed when he ran your program.
  2031. CommandLength, a WORD, holds the length of this string. You must
  2032. parse the parameter string yourself. If you are programming under
  2033. Kickstart 2.0 or higher, the operating system function ReadArgs can
  2034. be used to make things easier.
  2035.  
  2036. Q: How do I use structures in Struct?
  2037.  
  2038. A: Normally, the structure will be defined in an assembly language
  2039. include file which will included with the Include Constant command.
  2040. Let's say the structure looked like this:
  2041.  
  2042. Structure BitNode,0
  2043. APTR BN_NextNode
  2044. APTR BN_PrevNode
  2045. WORD BN_Value
  2046. LABEL BN_SizeOf
  2047.  
  2048.   The Include Constant command would define the following constants:
  2049.  
  2050. BN_NextNode = 0
  2051. BN_PrevNode = 4
  2052. BN_Value = 8
  2053. BN_SizeOf = 10
  2054.  
  2055.   To create an instance of this structure, you would normally use
  2056. AllocMem like this:
  2057.  
  2058. Lib Call BitNodePointer=AllocMem(BN_SizeOf,0)
  2059.  
  2060.   Now BitNodePointer points to a 10-byte memory area that you'll use
  2061. for the BitNode. If you wanted to set the Value in the BitNode to
  2062. 42, you would do this:
  2063.  
  2064. Word Poke BitNodePointer+BN_Value,42
  2065.  
  2066.   If this BitNode had already been set up, and made part of a list,
  2067. you could "step" to the next node in the list with:
  2068.  
  2069. Long Peek BitNodePointer=BitNodePointer+BN_NextNode
  2070.  
  2071.   It's that easy. Doing the sort of multi-structure indirection
  2072. that C is famous for may take some loops or temporary variables, but
  2073. it's really no more difficult than what's already been shown here.
  2074.  
  2075. Q: What's this STACK data type?
  2076.  
  2077. A:  Last semester, in one of my classes, I was given a test on which
  2078. I was asked the question, "How would you add direct support for
  2079. stacks to your favorite language?" I found the question very
  2080. interesting. The stack - and its complement, the queue - is an
  2081. important basic construct used in a wide variety of programs. Graph
  2082. traversals, for example, would be difficult or impossible without
  2083. them.
  2084.   Stacks are an integral part of assembly language...but what about
  2085. high-level languages? In Ada I would allocate some memory from the
  2086. OS, assign the pointer to a variable, and use it to step back and
  2087. forth through memory. In C I would do...the same thing. The same as
  2088. in Basic. And Fortran. And all the other languages that I knew. It
  2089. amazed me that NO high-level language I had ever seen had direct
  2090. support for stacks.
  2091.   After class I asked the teacher about this. He told me that he had
  2092. seen one or two - I believe he mentioned Algol and PL/1 - but none
  2093. that were in general use nowdays. He pointed out that you could put
  2094. the stack-handling code in a function, calling it whenever you
  2095. needed to push or pop, just as I'd done in my basic data structures
  2096. classes. I agreed, but argued that it would be tremendously
  2097. inefficient compared to the simple -(Ax) and (Ax)+ combinations
  2098. possible in assembly. And it seemed more like a way out to me than a
  2099. real solution.
  2100.   I was curious as to whether or not people would actually use such
  2101. a feature, if it was available. Being the creator of my own language
  2102. gave me the opportunity to find out. So I added a limited form of
  2103. stack support into Struct, using the method I had devised on the
  2104. test. The code output by the compiler to do stack accesses isn't up
  2105. to the standards of the rest of the compiler, but is sufficient for
  2106. an experiment.
  2107.   First, a variable must be defined as being of type STACK with a
  2108. statement like this:
  2109.  
  2110. Global <stack-variable):STACK(<stack-size>)
  2111.  
  2112.   For example:
  2113.  
  2114. Global Pai:STACK(256)
  2115.  
  2116.   Pai will now be a stack variable. The value between the
  2117. parentheses is the size of the stack, 256 bytes in this case. The
  2118. memory for the stack will be allocated automatically by the compiled
  2119. program's startup routine. Note that only the Global command can be
  2120. used to define a stack; the Local command will not work.
  2121.   Values can be pushed onto a stack like this:
  2122.  
  2123. <stack-variable>=<math-expression>
  2124.  
  2125. and popped off like this:
  2126.  
  2127. <normal-variable>=<stack-variable>
  2128.  
  2129.   When a value is pushed onto the stack, Struct will look at the
  2130. math-expression to determine the size of the pushed value. If it is
  2131. an expression, a longword containing the result of the expression
  2132. will be pushed onto the stack. If, however, the "expression" is
  2133. actually a single variable, the size of the push will be the same as
  2134. the size of the variable.
  2135.   When values are popped from the stack, the size of the pop will
  2136. always be the same as the size of the receiving variable.
  2137.   A stack variable cannot appear on both sides of the equals sign.
  2138. An assignment like:
  2139.  
  2140. <stack-variable>=<stack-variable>
  2141.  
  2142. is NOT allowed and WILL NOT WORK.
  2143.   Consider the following piece of code:
  2144.  
  2145. Global Pai:STACK(1024), A:WORD=17, B:WORD, C:WORD
  2146. Pai=A
  2147. Pai=5
  2148. A=Pai
  2149. B=Pai
  2150. C=Pai
  2151.  
  2152.   When the second line is executed, a word with the value of 17 will
  2153. be placed on the stack, as A is a WORD variable by itself. The
  2154. third line will place a longword with the value of 5 on the stack.
  2155. Even though 5 is a literal, it is considered an expression because
  2156. it is not a lone variable. At this point, if you were to view the
  2157. stack with a memory editor, it would look like this:
  2158.  
  2159. 00 00 00 05 00 17
  2160.  
  2161.   The next three lines cause three words to be pulled off of the
  2162. stack and assigned to A, B, and C. This will make A equal zero, B
  2163. equal five, and C equal seventeen.
  2164.   Be aware that although Struct's stacks are easier to use than the
  2165. ones you would make yourself, they have the same caveats. You must
  2166. be careful not to overflow or underflow the stack; Struct does not
  2167. check this for you. You must ensure that values are pushed onto and
  2168. popped from the stack in a logical order. And when dealing with
  2169. byte-length values, you must beware of a subtle pitfall. Consider
  2170. the following piece of code:
  2171.  
  2172. Global Yakumo:STACK(500), X:BYTE=5, Y:WORD=7
  2173. Yakumo=X
  2174. Yakumo=Y
  2175.  
  2176.   This code will CRASH THE COMPUTER!
  2177.   The problem lies in the design of the 68000. The 68000 requires
  2178. that all attempts to read (and write) words and longwords occur at
  2179. even memory addresses. When the second line is executed, the pointer
  2180. to the Yakumo stack will be decremented by one (making it point to
  2181. an odd memory address) and a five will be written to that address.
  2182. This is allowed, since the memory access is byte-length. But when
  2183. the third line is executed, the stack pointer will be decremented by
  2184. two (making it still odd), the computer will try to write a word to
  2185. that address, and a guru will result.
  2186.   At the moment, the STACK variable type is just a hack. That's why
  2187. you can only use the simple kinds of references shown above - only
  2188. the compiler's high-level parser knows about it. All other parts of
  2189. the compiler will see it as a LONG variable. I have not done any of
  2190. the detailed study and design that would be necessary to make it a
  2191. formal part of the language. There are serious problems involved in
  2192. trying to make STACK variables transparent to the rest of the
  2193. language - problems related to scratch registers and determinism. It
  2194. might be better to have a separate command to deal with them, though
  2195. this somewhat defeats their purpose. We'll see what happens.
  2196.  
  2197.  
  2198.  
  2199. 8. Registration
  2200. ---------------
  2201.   This version of Struct is not "crippled" or "broken" in any way,
  2202. and can be used to its full potential without registration. However,
  2203. registration has been known to cause feelings of righteousness and
  2204. pride in users, as the mail system whisks their offering of love
  2205. across the world. It also makes the recipient much more likely to
  2206. continue to support the program he has written.
  2207.   Registration is $25 and should be sent to:
  2208.  
  2209. Roland Acton
  2210. 8001 Bluebird Lane
  2211. La Palma, CA, 90623
  2212. U.S.A.
  2213.  
  2214. Internet address: xracton@ccvax.fullerton.edu
  2215.  
  2216.   There are advantages to being a registered user. Registered users
  2217. will be sent every "significant upgrade" (my decision) as soon as it
  2218. is available. Those with an Internet address, or on a system
  2219. reachable by Internet mail, will likely receive every single update
  2220. the day it comes out.
  2221.   I will also be MUCH more inclined to answer questions or do favors
  2222. for registered users.
  2223.   I can make no guarantees about the future, though, even to
  2224. registered users. I intend to continue upgrading and enhancing
  2225. Struct, but unforseen problems may make this impossible or
  2226. unreasonable. I reserve the right to stop supporting Struct at any
  2227. time.
  2228.  
  2229.  
  2230.  
  2231. 9. Legalese
  2232. -----------
  2233.   Struct is copyrighted 1994 by Roland Acton, but freely
  2234. redistributable, so long as the following conditions are met:
  2235.  
  2236. 1. The archive is distributed intact, with no programs or
  2237. documentation changed, removed or destroyed.
  2238. 2. A person or organization wishing to distribute Struct as part of
  2239. a collection of PD, shareware, freeware, or the like must place
  2240. Struct and all of its documentation and support programs on the same
  2241. disk, if at all possible. No more money may be charged for this disk
  2242. than Fred Fish would charge for a similar disk.
  2243.  
  2244.   Although I have put my best effort into making Struct work
  2245. properly, I cannot be held responsible if it does not work in the
  2246. way that I - or you - intend it to. Such are the dangers of using
  2247. programs that you don't have to pay for (and many of those that you
  2248. do).
  2249.   People wishing to use Struct to create programs, or parts of
  2250. programs, must abide by the following conditions:
  2251.  
  2252. If the program is intended for NON-COMMERCIAL release:
  2253. 1. It must be mentioned somewhere in either the program or the
  2254. documentation, or both, that the program was either partially or
  2255. completely written in Struct.
  2256. 2.  It is requested, but not required, that you send me a copy of
  2257. the program. If this is not possible, I would appreciate at least
  2258. being notified that the program exists. My address is listed in the
  2259. Registration section; my Internet address can also be used if I am
  2260. warned beforehand (there's only so much space in that account).
  2261. Sending the source code along would be nice, too.  I'm always
  2262. interested in seeing how other people deal with Struct.
  2263.  
  2264. If the program is intended for COMMERCIAL release:
  2265. 1. You MUST register, as described in the Registration section. If
  2266. Struct is good enough to write a commercial program with, it's good
  2267. enough to pay for.
  2268. 2. It is preferred that somewhere, in either the program or
  2269. documentation, it is mentioned that the program was written using
  2270. Struct. This is not absolutely required, though, unless the program
  2271. or documentation has a section that credits other utilities used to
  2272. make the program. In this case Struct must be mentioned there with
  2273. the other programs.
  2274. 3. The author of the program must make a reasonable attempt to send
  2275. me a copy of the program, without charge. My address can be found
  2276. in the Registration section. You can also use my Internet address
  2277. if you warn me beforehand.
  2278.  
  2279.  
  2280.  
  2281.                          --- Appendices ---
  2282.  
  2283.  
  2284.  
  2285. A. How to alter the library definition file
  2286. -------------------------------------------
  2287.   The library definition file, called "StructLibraryDefinitions" and
  2288. always looked for in the S: directory, contains a list of all of the
  2289. functions that can be used with the Lib Call command. Included in
  2290. this list is all of the "vital information" about the function: what
  2291. offset it is from the library base, what registers its parameters
  2292. (if any) go onto, and what register its return value (if any) is
  2293. passed on.
  2294.   A typical library definition looks like this:
  2295.  
  2296. Library <library-name>
  2297.             [...]
  2298. [list of function definitions]
  2299.             [...]
  2300. End Library
  2301.  
  2302.   Each individual function definition takes this form:
  2303.  
  2304. <function-name>(<library-offset>):[<return-register>=]
  2305.   [<parameter-register>[,<parameter-register>[,...]]]
  2306.  
  2307.   For example:
  2308.  
  2309. Library Pop
  2310.   Chaser(-8):D0=D0,A1,A2
  2311. End Library
  2312.  
  2313.   The registers are specified in the same order that the variables
  2314. containing values to pass are specified when the Lib Call command is
  2315. used.
  2316.   All registers except A6 and A7 can be used to pass parameters.
  2317. Return values normally come back on register D0. Is is possible to
  2318. specify any register except A6 and A7 as the return register, but
  2319. there is a problem: Struct only considers registers D0, D1, A0, and
  2320. A1 to be "scratch". Register variables assigned to other registers
  2321. are not saved off unless that register is needed to pass a
  2322. parameter. If the external function actually returned a value on a
  2323. non-scratch register, it might overwrite a register variable.
  2324.   Note that you might put variables on registers with the Absolute
  2325. and Regload commands and then call a function (normally of your own
  2326. creation) which deliberately overwrites them. This could be used as
  2327. a method of easily returning more than one value per call. It would
  2328. have to be done with care, however.
  2329.   The line continuation character can be used to place comments in
  2330. the library definition file, if desired. Also, after the definition
  2331. of a library has been finished with End Library, you can "come back"
  2332. to it with another Library.
  2333.   Here is a longer example:
  2334.  
  2335. Library Exec
  2336.   AddTail(-246):A0,A1
  2337.   AllocMem(-198):D0=D0,D1
  2338.   GetCC(-528):D0=
  2339. End Library
  2340.  
  2341. Library Dos
  2342.   Delay(-198):D1
  2343. End Library
  2344.  
  2345. Library Exec
  2346.   MakeLibrary(-84):D0=A0,A1,A2,D0,D1
  2347. End Library
  2348.  
  2349.   You can edit the library definition file to add libraries and
  2350. functions other than those that are part of the standard Amiga
  2351. operating system. As long as these other libraries conform to
  2352. standard Commodore-Amiga shared library specifications, they will
  2353. work with Struct.
  2354.  
  2355.  
  2356.  
  2357. B. Possible errors
  2358. ------------------
  2359.   Following is a list of most of the possible errors and warnings
  2360. that Struct can report during compilation. There are a few others
  2361. that are not part of the formal error list.
  2362.  
  2363.  #   Reported Error
  2364. --   --------------
  2365. 01   A mathematical expression cannot be a variable
  2366. 02   A mathematical expression cannot be a constant
  2367. 03   Variable has already been defined
  2368. 04   Constant has already been defined
  2369. 05   Already defined as a variable
  2370. 06   Already defined as a constant
  2371. 07   Expected assignment or label
  2372. 08   Two commas in a row
  2373. 09   Too many equals signs
  2374. 10   Undefined variable or constant
  2375. 11   A mathematical expression cannot be a label
  2376. 12   An expression must appear on each side of an equals sign
  2377. 13   You cannot assign a value to an expression
  2378. 14   Type definition expected
  2379. 15   An equals sign implies an initial assignment
  2380. 16   Invalid type definition
  2381. 17   An initial assignment must reduce to a numeric literal
  2382. 18   Constant has not been assigned a value
  2383. 19   Variable name begins with a Struct keyword
  2384. 20   Constant name begins with a Struct keyword
  2385. 21   A variable or constant must appear on each side of a mathematical operator
  2386. 22   MATHEVALUATE: Empty expression encountered
  2387. 23   Not currently a register variable
  2388. 24   You are limited to 7 data register variables
  2389. 25   You are limited to 3 address register variables
  2390. 26   An address register variable must be of type LONG
  2391. 27   This command requires one or more parameters
  2392. 28   Undefined variable
  2393. 29   Constant assignments must reduce to numeric literals when Constant Evaluate is ON
  2394. 30   A procedure must have a name
  2395. 31   Already in a procedure
  2396. 32   Not in a procedure
  2397. 33   A procedure with that name has already been defined
  2398. 34   The LOCAL instruction can only be used within a procedure
  2399. 35   Constant Evaluate can only be set to ON or OFF
  2400. 36   REPLACECONSTANT: 100 iterations performed
  2401. 37   Label has already been defined
  2402. 38   A default assignment must reduce to a numeric literal
  2403. 39   An expression must appear on each side of the relational operators
  2404. 40   A relational operator was used more than once
  2405. 41   You must specify at least one primary relational operator
  2406. 42   It makes no sense to use all three primary relational operators
  2407. 43   Else without If
  2408. 44   End If without If
  2409. 45   Back If without If
  2410. 46   A computed Goto is not allowed
  2411. 47   A right parenthesis is required to denote the end of the label
  2412. 48   A comma was expected but not found
  2413. 49   An equals sign was expected but not found
  2414. 50   A semicolon was expected but not found
  2415. 51   There must be an expression on each side of a semicolon
  2416. 52   NEXT without FOR
  2417. 53   Loop resolved out of order
  2418. 54   WEND without WHILE
  2419. 55   UNTIL without REPEAT
  2420. 56   Two semicolons must appear as separators in the LOOP command
  2421. 57   Expected an assignment
  2422. 58   This command does not use a parameter
  2423. 59   END LOOP without LOOP
  2424. 60   LIBREADER: Invalid function definition
  2425. 61   LIBREADER: Register used more than once
  2426. 62   LIBREADER: A function must be associated with a library
  2427. 63   LIBREADER: Duplicate function name
  2428. 64   Unknown library
  2429. 65   Library has already been opened
  2430. 66   A right parenthesis must appear at the end of the parameter list
  2431. 67   Function not found in library definition file
  2432. 68   Function definition does not specify a return value
  2433. 69   Function cannot accept this many parameters
  2434. 70   Library containing function has not been opened
  2435. 71   <UNUSED>
  2436. 72   Parameter must be of type LONG
  2437. 73   DOS library has not been opened
  2438. 74   Parameters must appear on each side of the equals sign
  2439. 75   This program was written for a later version of Struct
  2440. 76   File does not exist
  2441. 77   Error in included constant file
  2442. 78   No procedure with that name has been defined
  2443. 79   Too few parameters passed to procedure
  2444. 80   Too many parameters passed to procedure
  2445. 81   Procedure has already been implemented
  2446. 82   Procedure has not been defined
  2447. 83   Definition and implementation parameter lists must match
  2448. 84   Formal and actual parameter lengths do not match
  2449. 85   A procedure can accept a maximum of 7 parameters
  2450. 86   Not currently in a loop
  2451. 87   Startup procedure has already been set
  2452. 88   Only LONG parameters may be passed on address registers
  2453. 89   MAKEBRANCH: Blank condition encountered
  2454. 90   Value of constant exceeds variable capacity
  2455. 91   Definition and implementation return types must match
  2456. 92   Procedure returns a value
  2457. 93   Formal and actual return variable lengths do not match
  2458. 94   Procedure does not return a value
  2459.  
  2460. Notes:
  2461.   Errors #22 and #89 should never happen. There is supposed to be
  2462. enough high-level checking in the compiler to catch any problems
  2463. before they get to these routines.
  2464.   Error #36 will occur if you accidentally set up a replacement loop
  2465. as mentioned in the tutorial on the Constant command.
  2466.   Errors #60-63 refer to the StructLibraryDefinitions file.
  2467.   Error #90 will not always be reported, even in some situations
  2468. where it seems blatantly obvious. Due to internal pipelining, it can
  2469. be very difficult for Struct to tell where a value is going to end
  2470. up.
  2471.  
  2472.  
  2473.  
  2474. C. Theoretical Limits
  2475. ---------------------
  2476.   The following table shows the maximum number of instances of each
  2477. program element. Exceeding these limits will cause the compiler to
  2478. halt abruptly, though it shouldn't crash the computer. It is
  2479. possible, but unlikely, to overflow the compiler's internal buffer
  2480. area (also causing a halt) without exceeding these limits.
  2481.  
  2482. Element                             Per Program    Per Procedure
  2483. ----------------------------------------------------------------
  2484. Global variables                        255              -
  2485. Local variables                           -            255
  2486. Constants                              2000              -
  2487. Labels                                  500              *
  2488. Ifs                                       -            255
  2489. Gotos                                     -            255
  2490. Prints                                    -            255
  2491. Loops**                                   -            255
  2492. "Data" statements***                      -            255
  2493. Procedures                              255              -
  2494. External libraries                       32              -
  2495. External functions                      500              -
  2496. Load Address commands                     -            255
  2497.  
  2498. * Local and global labels are stored in the same data table. Local
  2499.   labels are discarded at the end of each procedure. The total
  2500.   number of outstanding labels may not exceed 500 at any time.
  2501. ** This includes For/Next, While/Wend, Repeat/Until, Loop/End Loop,
  2502.    and Zeroloop/Zend.
  2503. *** This includes Byte Data, Word Data, Long Data, and String.
  2504.  
  2505.  
  2506.  
  2507. D. Speed Tests
  2508. --------------
  2509.   I wrote some test programs to compare the efficiency of SAS C 6.2
  2510. amd Struct. Although I considered C to be Struct's primary
  2511. competition, I also ran the speed tests on the other two languages I
  2512. had available: AMOS and ACE.
  2513.   SAS C 6.2 is a commercial C compiler, published by SAS Institute.
  2514. The included assembler and linker were used. The optimization flag
  2515. was not used because the optimizer kept throwing the test code away.
  2516. (But, looking at the assembly code, I doubt it would have made much
  2517. difference)
  2518.   Struct is a copyrighted but freely redistributable compiler,
  2519. written by Roland Acton. The PhxAss optimizing assembler and PhxLnk
  2520. linker, included with Struct, were used.
  2521.   AMOS is a commercial BASIC interpreter, written by Francois Lionet
  2522. and published by Europress software. The AMOS compiler was used, and
  2523. the numbers given are for the compiled versions of the speed tests.
  2524.   ACE is a freeware BASIC compiler, written by David Benn. The A68k
  2525. assembler and Blink linker, included with ACE, were used.
  2526.   The tests were performed on an Amiga 500 with 1 MB chip and 2 MB
  2527. fast, running Kickstart 1.3.
  2528.  
  2529. Results (numbers represent seconds):
  2530.  
  2531.               C     Struct      AMOS      ACE
  2532. --------------------------------------------------
  2533. Test 1      114        107       662      504
  2534. Test 2      230        109      1694     1200
  2535. Test 3      441        107      2313      866
  2536. Test 4      119        119      1115      660
  2537. Test 5      114        114       887      571
  2538. Test 6      116         95       660      504
  2539. Test 7      130        107       709      538
  2540. Test 8      121         76       622      442
  2541.  
  2542.   The tests themselves were quite trivial and focused on loop
  2543. structures, comparisons, and simple mathematics. They are not
  2544. sufficiently general to be called true benchmarks. The tested
  2545. operations, however, are important parts of all useful programs.
  2546.   TEST 1 was a For/Next loop.
  2547.   TEST 2 used the Zeroloop construct, unique to Struct. For/Next
  2548. loops with negative steps were used in the other languages.
  2549.   TEST 3 was to acquire the remainder from a division and use it in
  2550. an If/Then. This was surrounded by a For/Next loop.
  2551.   TEST 4 was a Repeat/Until loop. Do/While was substituted in C.
  2552.   TEST 5 was a While/Wend loop.
  2553.   TEST 6 was a Loop/End Loop construct. For/Next was substituted in
  2554. the other languages.
  2555.   TEST 7 was a test of literal concatenation. An If/Then, with the
  2556. condition containing numeric literals added to a variable, was
  2557. surrounded with a For/Next loop.
  2558.   TEST 8 was to access memory. A Byte Peek with a computed memory
  2559. address was surrounded by For/Next loops. The memory reference
  2560. operator with char casting was used in C. Peek was used in AMOS and
  2561. ACE.
  2562.   The source code to all the tests is included in the SpeedTests
  2563. directory in the archive.
  2564.