home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / C64-128Toolkit / ACE12-AS-SRC.ARC / ACE12-AS.DOC next >
Encoding:
Text File  |  2019-04-13  |  44.4 KB  |  894 lines

  1. ACE Assembler Documentation  for version 1.00   [October 23, 1994]
  2. ------------------------------------------------------------------------------
  3. 1. INTRODUCTION
  4.  
  5. The ACE assembler is a one-pass assembler.  The only real limitation on the
  6. size of assembly jobs is the amount of near+far memory you have available.
  7. Labels are "limited" to 240 characters (all significant), and the object size
  8. is limited to 64K (of course).  Numerical values are "limited" to 32-bits or
  9. less.  Relative labels ("+" and "-" labels) are implemented in the same way as
  10. in the Buddy assembler.  Only add and subtract dyadic operators are currently
  11. implemented for expressions with positive, negate, high-byte, and low-byte
  12. monadic oparators, and the planned macro and conditional assembly features are
  13. not yet implemented.  Expressions are limited to 17 operands (with 255 monadic
  14. operators each), but references to unresolved identifiers are allowed
  15. anywhere, including equate definitions.  Hierarchical inclusion of source
  16. files is not yet implemented.  The ACE source code will eventually be
  17. converted to use this assembler.  The assembler code itself has been
  18. converted.
  19.  
  20. The assembler is designed to be a "heavy hitter", operates at moderate speed,
  21. and uses a fair amount of dynamically allocated memory.  In fact, on an
  22. unexpanded 64, you won't be able to assemble programs that are too large,
  23. including the assembler itself (85K of source).  You'll be able to do larger
  24. jobs on an unexpanded 64 if you deactivate the soft-80 screen in the
  25. configuration.  I'll be working on making more RAM0 memory available to ACE
  26. applications.  (Of course, one could argue that any serious 64 hacker would
  27. have expanded memory anyways...).
  28.  
  29. In addition to the regular 6502 instructions, this release of the assembler
  30. has the following directives:
  31.  
  32. label = value               ;assign given value to the label
  33. label:                      ;assign the current assembly address to label
  34. +                           ;generate a temporary label, assign cur address
  35. -                           ;generate a temporary label, assign cur address
  36. org address                 ;set the origin of the assembly
  37. buf size                    ;reserve "size" bytes of space,filled with zeroes
  38. db val1, val2, ..., valN    ;put byte values into memory
  39. dw val1, val2, ..., valN    ;put word values into memory
  40. dt val1, val2, ..., valN    ;put "triple" (3-byte) values into memory, lo->hi
  41. dl val1, val2, ..., valN    ;put "long" (4-byte) values into memory, lo->hi
  42.  
  43. These features is described in more detail below.  Note that throughout the
  44. documentation, I use the terms "identifier", "symbol", and "label"
  45. interchangeably.
  46. ------------------------------------------------------------------------------
  47. 2. USAGE
  48.  
  49. The usage for the as command is, stated in Unix notation:
  50.  
  51. usage: as [-help] [-s] [-d] [file ...]
  52.  
  53. The "-help" flag will cause the assembler display the usage information and
  54. then exit, without assembling any code.  Actually, any flag that it doesn't
  55. understand will be taken as if you had said "-help", but note that if you type
  56. the "as" command alone on a command line that usage information will not be
  57. given.
  58.  
  59. The "-s" flag tells the assembler to generate a symbol-table listing when the
  60. assembly job is finished.  The table is formatted for an 80-column display.
  61. indicates that a symbol table should be generated when the assembly job is
  62. done.  The table will look like:
  63.  
  64. The "-d" flag tells the assembler to produce debugging information while it is
  65. working.  It will generate a lot of output, so you can see exactly what is
  66. going on.
  67.  
  68. The object-code module name will be "a.out" unless the name of the first
  69. source file ends with a ".s" extension, in which case the object module will
  70. be the base name of first source file (without the extension).  The object
  71. module will be written as a PRG file and will be in Commodore-DOS program
  72. format: the first two bytes will be the low and high bytes of the code
  73. address, and the rest will be the binary image of the assembled code.
  74.  
  75. If no source filename is given on the command line, then input is taken from
  76. the stdin file stream (and written to "a.out").  If more than one filename is
  77. given, the each is read, in turn, into the same assembly job (as if the files
  78. were "cat"ted together into one source file).  (This will change subtly when
  79. the assembler is completed).
  80.  
  81. This assembler does not produce a listing of the code assembled and will
  82. stop the whole assembly job on the first error it encounters.
  83. ------------------------------------------------------------------------------
  84. 3. TOKENS
  85.  
  86. While reading your source code, the assembler groups characters into tokens
  87. and interprets them as a complete unit.  The assembler works with five
  88. different types of tokens: identifiers, numeric literals, string literals,
  89. special characters, and end-of-file (eof).  Eof is special since it doesn't
  90. actually include any characters, and its only meaning is to stop reading from
  91. the current source.  Your input source file should consist only of characters
  92. that are printable in standard ASCII (don't be confused by this; the assembler
  93. expects its input to be in PETSCII) plus TAB and Carriage-Return.  Other
  94. characters may confuse the assembler.
  95.  
  96. Identifiers consist of a lowecase or uppercase letter or an underscore
  97. followed by a sequence of such letters or decimal digits.  This is a pretty
  98. standard definition of an identifier.  Identifiers are limited to 240
  99. characters in length and an error will be reported if you try to use one
  100. longer than that.  All of the characters of all identifiers are significant,
  101. and letters are case-sensitive.  Here are some examples of all-unique
  102. identifiers:
  103.  
  104. hello  Hello  _time4  a1_x140J  HelloThereThisIsA_LongOne
  105.  
  106. Numeric literals come in three types: decimal, hexadecimal, and binary.
  107. Decimal literals consist of an initial digit from 0 to 9 followed by any
  108. number of digits, provided that the value does not exceed 2^32-1 (approx. 4
  109. billion).  All types of literals can also have embedded underscore characters,
  110. which are ignored by the assembler.  Use them grouping digits (like the comma
  111. for big American numbers).
  112.  
  113. Hexadecimal literals consist of a dollar sign ($) followed by any number of
  114. hexadecimal digits, provided the value doesn't overflow 32 bits.  Hexadecimal
  115. digits include the decimal digits (0-9), and the first six uppercase or
  116. lowercase letters of the alphabet (either a-f or A-F).  Hexadecimal literals
  117. can also have embedded underscore characters for separators.
  118.  
  119. Binary literals consist of a percent sign (%) followed by any number of binary
  120. digits that don't overflow 32-bits values.  The binary digits are, of course,
  121. 0 and 1, and literals may include embedded underscore characters.  Note that
  122. negative values are not literals.  Here are some examples of valid literals:
  123.  
  124. 0  123  0001  4_294_967_295  $aeFF  $0123_4567  %010100 %110_1010_0111_1010
  125.  
  126. String literals are sequences of characters enclosed in either single (') or
  127. double (") quotation marks.  The enclosed characters are not interpreted to be
  128. independent tokens, nomatter what they are.  One exception is that the
  129. carriage-return character cannot be enclosed in a string (this normally
  130. indicates an error anyway).  To get special non-printable characters into your
  131. strings, an "escape" character is provided: the backslash (\).  If the
  132. backslash character is encountered, then the character following it is
  133. interpreted and a special character code is put into the string in place
  134. of the backslash and the following character.  Here are the characters
  135. allowed to follow a backslash:
  136.  
  137. CHAR   CODE   MEANING
  138. ----   ----   --------
  139. \        92   backslash character (\)
  140. n        13   carriage return (newline)
  141. b        20   backspace (this is a non-destructive backspace for ACE)
  142. t         9   tab
  143. r        10   goto beginning of line (for ACE, linefeed for CBM)
  144. a         7   bell sound
  145. z         0   null character (often used as a string terminator in ACE)
  146. '        39   single quote (')
  147. e        27   escape
  148. 0         0   null character
  149. q        34   quotation mark
  150. "        34   quotation mark
  151.  
  152. So, if you really want a backslash then you have to use two of them.  If you
  153. wish to include an arbitrary character in a liter string, no facility is
  154. provided for doing that.  However, the assembler will allow you to intermix
  155. strings and numeric expressions at a higher level, so you can do it this way.
  156. Strings are limited to include 240 (encoded) characters or less.  This is
  157. really no limitation to assembling, since you can put as many string literals
  158. contiguously into memory as you wish.  Here are some examples:
  159.  
  160. "Hello there"  "error!\a\a"  'file "output" could not be opened\n\0'
  161. "you 'dummy'!"  'you \'dummy\'!'  "Here are two backslashes: \\\\"
  162.  
  163. Special characters are single characters that cannot be interpreted as any of
  164. the other types of tokens.  These are usually "punctuation" characters, but
  165. carriage return is also a special-character token (it is a statement
  166. separator).  Some examples follow:
  167.  
  168. ,  (  #  &  )  =  /  ?  \  ~  {  
  169.  
  170. Tokens are separated by either the next character of input not being allowed
  171. to belong to the current token type, or are separated by whitespace.
  172. Whitespace characters include SPACE (" ") and TAB.  Note that carriage return
  173. is not counted as whitespace.  Comments are allowed by using a ";" character.
  174. Everything following the semicolon up to but not including the carriage return
  175. at the end of the line will be ignored by the assembler.  (I may implement an
  176. artifical-intelligence comment parser to make sure the assembler does what you
  177. want it to, but this will be strictly an optional, time-permitting feature).
  178. ------------------------------------------------------------------------------
  179. 4. EXPRESSIONS
  180.  
  181. Numeric expressions consist of operands and operators.  If you don't know what
  182. operands and operators are, then go buy an elementary-school math book.  There
  183. are six types of operands: numeric literals, single-character string literals,
  184. identifiers, the asterisk character, one or more plus signs, and one or more
  185. minus signs.  These last three types can make parsing an expression a bit
  186. confusing, but they are necessary and useful.
  187.  
  188. Numeric literals are pretty easy to think about.  They're just 32-bit numbers
  189. and work in the usual way.  Single-character string literals are also
  190. interpreted (in the context of a numeric expression) as being a numeric
  191. literal.  The value of a single-character string is simply the PETSCII
  192. code for the character.
  193.  
  194. Identifiers or "symbols" or "labels" used in expressions refer to numeric
  195. values that have been or will be assigned to the identifiers.  Binding values
  196. to identifiers is done by assembler directives discussed in a later section.
  197. If an identifier already has a value assigned to it by the time that the
  198. current expression is reached in assembly, then it is treated as if it were a
  199. numeric literal of the value assigned to the identifier.  If the identifier
  200. currently has no value assigned to it (i.e., it is "unresolved"), then the
  201. entire expression will be unresolved.  In this case, the expression will be
  202. recorded and will be evaluated at a later time when all of its identifiers
  203. become resolved.  A "hole" will be created where the expression should go, and
  204. the hole will be "filled in" later.  Note that there are a couple of
  205. directives for which an expression must be resolved at the time it is
  206. referenced.
  207.  
  208. The asterisk character operates much like a numeric literal, except that its
  209. value is the current code address rather than a constant.  The current code
  210. address will always be for the start of an assembler instruction.  I.e., the
  211. current code address is incremented only after an instruction is assembled.
  212. This has some subtle implications, and other assemblers may implement slightly
  213. different semantics.  Directives are a little different in that the address is
  214. incremented after every value in a "commalist" is put into memory.
  215.  
  216. Relative references, operands consisting of a number of pluses or minuses,
  217. operate much like identifiers.  They are provided for convenience and
  218. work exactly how they do in the Buddy assembler.  Operands of all minuses
  219. are backward references and operands of all pluses are forward references.
  220. Because of parsing difficulties, relative-reference operands must either
  221. be the last operand in an expression or must be followed by a ":" character.
  222.  
  223. The number of pluses or minuses tell which relative reference "point"
  224. is being referred to.  A reference point is set by the "+" and "-"
  225. assembler directives discussed later.  This gets difficult to explain with
  226. words, so here is a code example:
  227.  
  228.    ldy #5
  229. -  ldx #0
  230. -  lda name1,x
  231.    sta name2,x
  232.    beq +
  233.    cmp #"x"
  234.    beq ++
  235.    inx
  236.    bne -
  237. +  dey
  238.    bne --
  239. +  rts
  240.  
  241. This relatively bogus subroutine will copy a null-terminated character string
  242. from name1 to name2 five times, unless the string contains an "x" character,
  243. in which case the copy operation terminates immediately upon encountering the
  244. "x".  The "beq +" branches to the next "+" label to occur in the code, to the
  245. "dey" instruction.  The "beq ++" branches to the "rts", to the "+" label
  246. following the next "+" label encountered.  The "-" and "--" references work
  247. similarly, except that they refer to the previous "-" label and the previous
  248. to the previous "-" label.  You can use up to 255 pluses or minus signs in
  249. a relative-reference operand to refer to that many reference points away.
  250.  
  251. That I said relative-reference operands work much like identifiers above
  252. is no cooincidence.  For each definition of a reference point and reference
  253. to a point, an internal identifier is generated that looks like "L+123c" or
  254. "L-123c".  Note that you can't define or refer to these identifiers yourself.
  255.  
  256. There are two types of operators that can be used in expressions: monadic and
  257. diadic operators.  Monadic operators affect one operand, and dyadic operators
  258. affect two operands.  At about this point, I should spell out the actual form
  259. of an expression.  It is:
  260.  
  261. [monadic_operators] operand [ operator [monadic_operators] operand [...] ]
  262.  
  263. or:
  264.  
  265. 1 + 2
  266. -1 + -+-2 + 3
  267.  
  268. An expression may have up to 17 operands.
  269.  
  270. The monadic (one-operand) operators are: positive (+), negative (-), low-byte
  271. (<), and high-bytes (>).  You can have up to 255 of each of these monadic
  272. operators for each operand of an expression.  Positive doesn't actually do
  273. anything.  Negative will return the 32-bit 2's complement of the operand that
  274. it is attached to.  Low-byte will return the lowest eight bits of the operand
  275. it is attached to.  High-byte will return the high-order 24-bits of the 32-bit
  276. operand it is attached to.  All expressions are evaluated in full 32-bit
  277. precision.  Note that you can use the high-bytes operator more than once to
  278. extract even higher.  For example, "<>>value" will extract the second-highest
  279. byte of the 32-bit value.
  280.  
  281. The dyadic (two-operand) operators are currently only add (+) and subtract
  282. (-).  Yes, the plus and minus symbols are horribly overloaded.  I hope that we
  283. all know what add and subtract do.  I am planning to implement more dyadic
  284. operators in the future (multiply, divide, and, or, not, exclusive-or).
  285.  
  286. Evaluation of dyadic operators is strictly left-to-right, and value overflows
  287. and underflows are ignored.  Values are always considered to be positive,
  288. but this doesn't impact 2's complement negative arithmetic for add and subtract
  289. dyadic operators.
  290.  
  291. Monadic operators take precedence over dyadic operators.  Evaluation of
  292. monadic operators is done a little differently.  All positive operators are
  293. thrown out since they don't actually do anything.  Then, if there is an even
  294. number of negative operators, they are thrown out.  If there is an odd number
  295. of negative operators, then the 2's complement negative of the operand is
  296. returned.  Then, if there are any high-bytes operators, the value is shifted
  297. that number of bytes to the right and the highest-order byte of the value is
  298. set to zero.  Note that it really doesn't make any sense to perform any more
  299. than three high-bytes operators.  Then, the low-byte operator is preformed, if
  300. asked for.  It is equivalent to taking anding the value with $000000ff.  It
  301. really doesn't make much sense to perform this operator more than once.  Also,
  302. it doesn't make any difference in which order you place the monadic operators
  303. in an expression; they are always evaluated in the static order given above.
  304.  
  305. There is one exception here.  If the first operand of an expression has
  306. high-bytes and/or low-byte monadic operators, then the rest of the expression
  307. is evaluated first and then the high/low-byte monadic operators are performed
  308. on the result.  This is done to be consistent with other assemblers and with
  309. user expectations.
  310.  
  311. Parentheses are not supported.  Here are some examples of valid expressions:
  312.  
  313. 2
  314. +2+1
  315. 2+-1
  316. 2+-------------------------------------1
  317. ++++:-+++:+---
  318. 1+"x"-"a"+"A"
  319. <>>>4_000_000_000
  320. <label+1
  321. >label+1
  322. -1
  323.  
  324. This last one ends up with a value of negative one, which is interpreted
  325. as really being 4_294_967_295.  If you were to try and do something like
  326. "lda #-1", you would get an error because the value would be interpreted
  327. as being way too big.
  328.  
  329. Expressions results are currently considered to be one of two types: value and
  330. address.  (The complete set must be value, address, address-low-byte, and
  331. address-high-byte in order to be actually useful).  Values are what you would
  332. expect and come from numeric and single-character-string-literal operands.
  333. The address type comes from the asterisk and relative reference operands and
  334. from identifier operands which are defined to be addresses.  An address is
  335. defined to be only an address in the range of the assembled code.  Addresses
  336. outside of this range are considered to be values.  The distinction of values
  337. and addresses is currently not used, but will be in the future when code
  338. relocation features are implemented.  Keeping track of expression types makes
  339. it possible to generate a list of all values in memory that must be modified
  340. in order to relocate a program to a new address without reassembling it.
  341.  
  342. String "expressions" consist of only a single string literal.  No operators
  343. are allowed.  Some assembler directives accept either numeric or string
  344. expressions and interpret them appropriately (like "db").
  345. ------------------------------------------------------------------------------
  346. 5. PROCESSOR INSTRUCTIONS
  347.  
  348. This assembler accepts the 56 standard 6502 processor instructions.  It does
  349. not provide un-documented 6502 instructions nor 65c02 nor 65816 instructions
  350. nor custom pseudo-ops.  The latter will be provided by future macro features.
  351. All of the assembler instructions must be in lowercase or they will not be
  352. recognized.  Here are the instructions:
  353.  
  354. NUM  INS      NUM  INS      NUM  INS      NUM  INS      NUM  INS
  355. ---  ---      12.  bvc      24.  eor      36.  pha      48.  sta
  356. 01.  adc      13.  bvs      25.  inc      37.  php      49.  stx
  357. 02.  and      14.  clc      26.  inx      38.  pla      50.  sty
  358. 03.  asl      15.  cld      27.  iny      39.  plp      51.  tax
  359. 04.  bcc      16.  cli      28.  jmp      40.  rol      52.  tay
  360. 05.  bcs      17.  clv      29.  jsr      41.  ror      53.  tsx
  361. 06.  beq      18.  cmp      30.  lda      42.  rti      54.  txa
  362. 07.  bit      19.  cpx      31.  ldx      43.  rts      55.  txs
  363. 08.  bmi      20.  cpy      32.  ldy      44.  sbc      56.  tya
  364. 09.  bne      21.  dec      33.  lsr      45.  sec
  365. 10.  bpl      22.  dex      34.  nop      46.  sed
  366. 11.  brk      23.  dey      35.  ora      47.  sei
  367.  
  368. The assembler also supports 12 addressing modes.  The "accumulator" addressing
  369. mode that can be used with the rotate and shift instructions is treated like
  370. the immediate addressing mode, so a shift-left-accumulator instruction would
  371. be just "asl" rather than "asl a".  Many other assemblers get rid of the
  372. accumulator addressing mode also.  Also, the ",x" and ",y" addressing modes
  373. must be given with a lowercase "x" or "y" or they will not be recognized.
  374. Here is the token syntax for the addressing modes (CR means carriage return):
  375.  
  376. num  name       gen  byt  example   tokens
  377. ---  ---------  ---  ---  -------   -------
  378. 01.  implied    00.    1            CR
  379. 02.  immediate  00.    2  #123      #     / exp8  / CR
  380. 03.  relative   00.    2  *+20      exp16 / CR
  381. 04.  zeropage   07.    2  123       exp8  / CR
  382. 05.  zp,x       08.    2  123,x     exp8  / ,     / x   / CR
  383. 06.  zp,y       09.    2  123,y     exp8  / ,     / y   / CR
  384. 07.  absolute   00.    3  12345     exp16 / CR
  385. 08.  abs,x      00.    3  12345,x   exp16 / ,     / x   / CR
  386. 09.  abs,y      00.    3  12345,y   exp16 / ,     / y   / CR
  387. 10.  indirect   00.    3  (12345)   (     / exp16 / )   / CR
  388. 11.  (ind,x)    00.    2  (123,x)   (     / exp8  / ,   / x   / )  / CR
  389. 12.  (ind),y    00.    2  (123),y   (     / exp8  / )   / ,   / y  / CR
  390.  
  391. Each instruction takes a complete line and each addressing mode must be
  392. terminated by a carriage return token (comments are skipped).  The format of
  393. an instruction line is as follows:
  394.  
  395. [prefix_directives] instruction address_mode_operand
  396.  
  397. In the case that an expression in an addressing mode is resolved at the point
  398. it is encountered and its value is less than 256, the assembler will try to
  399. use the zero-page addressing modes if possible.  On the other hand, if a
  400. zero-page addressing mode is unavailable for an instruction, then the
  401. assembler will promote or generalize the zero-page addressing mode to
  402. an absolute addressing mode, if possible.  This is what the "gen" column in
  403. the table above shows.  If after attempting to generalize the addressing
  404. mode the given addressing mode still not valid with the given instruction,
  405. then an error will be generated.
  406.  
  407. In the case that an expression in an addressing mode cannot be resolved at
  408. the point where it is encountered in the assembler's single pass, a hole is
  409. left behind, and that hole is made as "large" as possible; it is assumed
  410. that you will fill in the hole with the largest value possible.  This means,
  411. for example, if you were to assemble the following instruction:
  412.  
  413. lda var,x
  414.  
  415. then the assembler would assume this is an absolute mode, and will fill in the
  416. hole later as such, even if it turns out that "var" is assigned a value less
  417. than 256 later on.  This results in slight inefficiency in the code produced
  418. by this assembler, but it causes most two-pass assemblers to fail completely
  419. on a "phase error".  An easy way to avoid this circumstance is to make sure
  420. that all zero-page labels are defined before they are referred to.
  421.  
  422. The addressing modes that require a single byte value and that will not
  423. "generalize" to an absolute mode will have a single-byte hole created for
  424. them.  Only the branching instructions will be interpreted as having the
  425. relative addressing mode, and a single-byte hole will be left.  Two exceptions
  426. to the above rules are the "stx zp,y" and "sty zp,x", which will leave a
  427. single-byte hole on an unresolved expression, since the absolute-mode
  428. generalizations for these instructions are not supported by the processor.
  429. ------------------------------------------------------------------------------
  430. 6. DIRECTIVES
  431.  
  432. There are currently five classes of assembler directives; there will be
  433. more in the future.
  434.  
  435. 6.1. DO-NOTHING DIRECTIVES
  436.  
  437. There are two do-nothing directives:
  438.  
  439. #                           ;does nothing
  440.                             ;blank line--does nothing
  441.  
  442. A blank line in your source code will simply be ignored.  This helps to make
  443. code much more readable.  The "#" directive is a prefix directive.  This means
  444. that it does not occupy an entire line but allows other directives and
  445. processor instructions to follow it on the same line (including other prefix
  446. directives). (But note that you can follow any prefix directive by the
  447. blank-line directive, effectively allowing prefix directives to be regular
  448. full-line directives (powerful combining forms)).  The "#" directive is simply
  449. ignored by the assembler, but you can use it to highlight certain lines of
  450. code or other directives, like the future "include" directive.
  451.  
  452. 6.2. ASSIGNMENT DIRECTIVES
  453.  
  454. There are four assignment directives.  They all assign (bind) a value to an
  455. identifier.  Here they are:
  456.  
  457. label = expression          ;assign given value to the label
  458. label:                      ;assign the current assembly address to label
  459. +                           ;generate a temporary label, assign cur address
  460. -                           ;generate a temporary label, assign cur address
  461.  
  462. The first (label=expr) is the most general.  It assigns the result of
  463. evaluating the expression to the given label.  Because this assembler is so
  464. gosh-darned awesome, the expression doesn't even have to be resolved; a "hole"
  465. will be created saying to fill in the assigned label when all of the
  466. unresolved identifiers in the expression eventually become resolved.  Most
  467. other assemblers (in fact, all that I have ever heard of) can't do this
  468. because it causes ugly implementation problems, like cascading label
  469. resolutions.  Consider the following example:
  470.  
  471. lda #a
  472. sta b,x
  473. a = b+3
  474. b = c-1
  475. c = 5
  476.  
  477. At the point where c becomes defined, there are no "memory holes" but the
  478. label hole "b" must be evaluated and filled in.  "b" gets assigned the value
  479. 4.  At this point, there are two holes: the one in the "sta" instruction and
  480. the label "a".  We fill them both in, assigning "a" the value 8, and we
  481. discover that we can fill in a hew hole: the one in the "lda" instruction.  We
  482. do that and we are finally done.  The implementation can handle any number of
  483. these recursive label hole-fillings, limited only by the amount of near+far
  484. memory you have.
  485.  
  486. A label can only be assigned a value once, and you will get an error if you
  487. try to redefine a label, even if it is currently unresolved.  Also, all
  488. exressions must be resolved by the end of the assembly job, or an error will
  489. be reported (but only one--naming the first unresolved label that the
  490. assembler runs across; I may fix this up in the future).
  491.  
  492. The second assignment directive is equivalent to "label = *", but it is more
  493. convenient and is also a prefix directive.  It assigns the current address (as
  494. of the start of the current line) to the given identifier.  The colon is used
  495. with this directive to make it easy and efficient to parse, and to make it
  496. easy for a human to see that a label is being defined.  Many other assemblers
  497. follow this directive with just whitespace and rely on other tricks, like
  498. putting an ugly dot before each directive, to bail them out.
  499.  
  500. The third and fourth set relative reference points.  They are equivalent
  501. to "rel_label = *", where "rel_label" is a specially generated internal
  502. identifier of the form "L+123c" mentioned in the expression section.  The
  503. labels defined by these directives show up in the symbol table dump, if you
  504. ask for one on the command line.  These are also prefix directives, so if
  505. you wanted to set a forward and a backward reference to the same address,
  506. then you would do something like:
  507.  
  508. +- lda #1
  509.  
  510. In fact, you could put as many or these directives on the front of a line as
  511. you want, though more than one of each will be of little use.  Note that
  512. backward relative labels will always be defined at the point that they are
  513. referenced and forward relative labels will always be undefined (unresolved)
  514. when they are referenced.  If at the end of your assembly job the assembler
  515. complains of an unresolved reference involving a label of the form "L+123c",
  516. then you refer to a forward-relative point that you don't set, and if the
  517. label is of the form "L-4000000000c", then you refer to a backward relative
  518. point that you don't define.
  519.  
  520. 6.3. ORIGIN DIRECTIVE
  521.  
  522. org address_expression      ;set the origin of the assembly
  523.  
  524. This directive will set the code origin to the given expression.  The
  525. expression MUST be resolved at the point where it appears, since it
  526. would be very difficult to fill in the type of hole this would leave
  527. behind (though not impossible, hmmm...).  The origin must be set before
  528. any processor instruction or assembler directive that generates memory
  529. values is encountered, and the code origin can only be set once.  This
  530. results in a contiguous code region, which is what ACE and the Commodore
  531. Kernal require.
  532.  
  533. 6.4. DEFINE-BYTES DIRECTIVES
  534.  
  535. db exp1, exp2, ..., expN    ;put byte values into memory
  536. dw exp1, exp2, ..., expN    ;put word values into memory
  537. dt exp1, exp2, ..., expN    ;put "triple" (3-byte) values into memory, lo->hi
  538. dl exp1, exp2, ..., expN    ;put "long" (4-byte) values into memory, lo->hi
  539.  
  540. These directives are all put byte values into code memory, at the current
  541. address.  The only difference between the four of them is the size of data
  542. values they put into memory: bytes (8 bits), words (16 bits), triples (24
  543. bits), and longs (32 bits).  The code address is incremented by the
  544. appropriate number of bytes between putting each value into memory.  Any
  545. number of values can be specified by separating them by commas.  All
  546. expressions are evaluated in full 32 bits, but must fit into the size for the
  547. directive.  The expressions don't have to be resolved at the time they appear.
  548.  
  549. These directives can also be given strings for arguments, which means that
  550. each character of the string will be stored as one byte/word/etc. in memory,
  551. for example:
  552.  
  553. db 123, abc+xyz+%1101-"a"+$1, "hello", 0, "yo!", "keep on hackin'\0"
  554.  
  555. 6.5. BUF DIRECTIVE
  556.  
  557. buf size_expression         ;reserve "size" bytes of space, filled with zeroes
  558.  
  559. This directive reserves the given number of bytes of space from the current
  560. code address and fills them with zeroes.  The expression must be resolved,
  561. and can be any value from 0 up to 65535 or the number of bytes remaining
  562. until the code address overflows the 64K code space limit.
  563.  
  564. 6.6. PARSING
  565.  
  566. Because of the way that the assembler parses the source code (it uses a
  567. one-character-peek-ahead ad-hoc parser), you can define labels that are also
  568. directive names or processor-instruction names.  This is not a recommended
  569. practice, since you can end up with lines that look like:
  570.  
  571. x: lda: lda lda,x
  572.  
  573. The parser will know what to do, but most humans won't.  Also, because of the
  574. tokenizer, can put arbitrary spacing between tokens, except between tokens
  575. that would otherwise merge together (like two adjacent identifiers or decimal
  576. numbers).
  577. ------------------------------------------------------------------------------
  578. 7. ERROR HANDLING
  579.  
  580. When an error is detected, the assembler will stop the whole assembly job and
  581. print out one error message (to the stderr file stream).  Here are two
  582. examples of error messages:
  583.  
  584. err ("k:":2:0) Value is too large or negative
  585.  
  586. err ("k:":3:0), ref("k:":2:0) Value is too large or negative
  587.  
  588. In both error messages, the stuff inside of the parentheses is the filename of
  589. the source file (the keyboard here), the source line where the error was
  590. detected, and the column number where the error was detected.  Currently, the
  591. column number is not implemented so it is always zero.  When it is
  592. implemented, the column numbers will start from 1, like in the Zed text
  593. editor, and it will point to the first character of the token where the
  594. error was discovered.
  595.  
  596. In the first example, the error occurred because the expression was resolved
  597. and the value was found to be too large for whatever operation was attempted.
  598. In the second example, an expression was used but unresolved on line 2 of the
  599. source file, and when its unresolved identifier(s) was finally filled in in
  600. line 3 of the source, the "hole" to be filled in was found to be too small for
  601. the value, so an error resulted.  This is what the "ref" file position means.
  602. Filenames are included in error messages because in the future, it will be
  603. possible to have errors crop up in included files and elsewhere.
  604.  
  605. Here is the entire list of possible error messages:
  606.  
  607. NUM  MEANING
  608. ---  -------
  609. 01.  "An identifier token exceeds 240 chars in length"
  610. 02.  "A string literal exceeds 240 chars in length"
  611. 03.  "Ran into a CR before end of string literal"
  612. 04.  "Invalid numeric literal"
  613. 05.  "Numeric literal value overflows 32-bits"
  614. 06.  "Syntax error"
  615. 07.  "Attempt to perform numeric operators on a string"
  616. 08.  "Expression has more than 17 operands"
  617. 09.  "Ran out of memory during compilation process"
  618. 10.  "Attempt to redefine a symbol"
  619. 11.  "Attempt to assemble code with code origin not set"
  620. 12.  "Internal error: attempt to assign to unexpected id"
  621. 13.  "Non-numeric symbol in a numeric expression"
  622. 14.  "Expecting an operator"
  623. 15.  "Expecting an operand"
  624. 16.  "Expecting a command"
  625. 17.  "Value is too large or negative"
  626. 18.  "Branch out of range"
  627. 19.  "Feature is not (yet) implemented"
  628. 20.  "Instruction does not support given address mode"
  629. 21.  "Address wraped around 64K code address space"
  630. 22.  "Error trying to write output object file"
  631. 23.  "Directive requires resolved expression"
  632. 24.  "Code origin already set; you can't set it twice"
  633. 25.  "Unresolved symbol: "
  634. 26.  "Thus assembler doesn't accept .dot commands, Buddy!"
  635.  
  636. A "Syntax error" (#06) will be reported whenever a token other than one that
  637. was expected is found.  "Ran out of memory" (#09) may turn up often on an
  638. unexpanded 64.  "Expecting command" (#16) means that the assembler was
  639. expecting either a processor instruction or directive but found something else
  640. instead.  "Not implemented" (#19) means that you've tried to use a directive
  641. that isn't implemented yet.  "Unresolved symbol" (#25) will be printed with a
  642. randomly chosen unresolved symbol, with the last place in the source code
  643. where it was referenced.  "Dot commands" (#26) is a reminder that directives
  644. in this assembler are not prefixed with a dot (.).
  645.  
  646. There are two main reasons behind the idea of stopping at the first error
  647. encountered: simplicity and interoperability.  When Zed is implemented for
  648. ACE, it will have a feature that will allow it to invoke the assembler (as a
  649. sub-process) and have the assembler return an error location and message to
  650. Zed, which will display the error message and position the cursor to the error
  651. location (if the source file is loaded).
  652.  
  653. While on the subject of messages coming out of the assembler, here is an
  654. example of the format of the symbol table dump that you can ask for on the
  655. command line.  One line is printed for each identifier.  The "hash" value is
  656. the bucket in the hash table chosen for the identifier.  This may not have a
  657. whole lot of meaning for a user, but a good distribution of these hash buckets
  658. in the symbol table is a good thing.  Next is the 32-bit "hexvalue" of the
  659. label followed by the value in "decimal".  Then comes the type.  A type of "v"
  660. means value and "a" means an in-code-range address.  Then comes the name of
  661. the identifier.  It comes last to give lots of space to print it.  If an
  662. identifier is ten or fewer characters long, its symbol-table-dump line will
  663. fit on a 40-column screen.  At the bottom, the number of symbols is printed.
  664. This table is directed to the stdout file stream, so you can redirect it to a
  665. file in order to save it.
  666.  
  667. HASH  HEXVALUE    DECIMAL  T  NAME
  668. ----  -------- ----------  -  -----
  669.    8  00000f06       3846  v  aceArgv
  670.  469  00007008      28680  a  main
  671. --
  672. Number of symbols: 2
  673. ------------------------------------------------------------------------------
  674. 8. IMPLEMENTATION
  675.  
  676. In each of the ways in which it is heavy-weight and slowed-down compared to
  677. other assemblers, it is also more powerful and more flexible.
  678.  
  679. - It uses far memory for storing symbols, so there is no static or arbitrarily
  680.   small limit on the number of symbols.  Macro sizes will also be limited by
  681.   only the amount of memory available, as well as the "hole table".
  682.  
  683. - It has to maintain a "hole table" because of its structure, but this means
  684.   that you can define labels in terms of other unresolved labels, that you
  685.   will never get a "sync error" because of incorrect assumptions made (and not
  686.   recorded) about unresolved labels, and that modular assembly can be
  687.   implemented without too much further effort (i.e., ".o" or ".obj" files),
  688.   since an unresolved external reference handling mechanism is already
  689.   implemented.
  690.  
  691. - The assembler keeps track of the "types" of labels, either "address" or
  692.   "value" that makes it possible to provide code relocation information that
  693.   will be needed by modular assembly and by future multitasking operating
  694.   systems.
  695.  
  696. - Because a "hole table" approach is used, the raw object code must be stored
  697.   internally until the assembly is complete and then it can be written out to
  698.   a file, but this also means that header information can be provided in an
  699.   output file since all assembly results will be known before any output is
  700.   written.
  701.  
  702. - I took the easy way out for handling errors; when an error is detected, an
  703.   error message is generated and printed and the assembler STOPs.  But the
  704.   exit mechanism provided by ACE makes it possible to integrate the assembler
  705.   with other programs, like a text editor, to move the text editor cursor to
  706.   the line and column containing the error and display a message in the text
  707.   editor.
  708.  
  709. There are two speed advantages that this assembler has over (some?) others:
  710.  
  711. - It uses a 1024-entry hash table of pointers to chains of labels, so, for a
  712.   program that has 800 or so symbols, each can be accessed in something like
  713.   1.3 tries.  For N total symbols, the required number of references is
  714.   approximately MAX( N/1024, 1 ).
  715.  
  716. - It is one-pass, so it only has to go through the overhead of reading the
  717.   source file once.  Depending on the type of device the file is stored on,
  718.   this may give a considerable savings.  This also makes it possible to
  719.   "pipe" the output of another program into the assembler, without any
  720.   "rewind" problems.
  721.  
  722. Here are some performace figures, compared to the Buddy assembler for the 128.
  723. All test cases were run on a C128 in 2-MHz mode with a RAMLink, REU, and 1571
  724. available.
  725.  
  726. ASSEMB   TIME(sec)   FILE DEVICE   FAR STORAGE
  727. ------   ---------   -----------   -----------
  728. Buddy         45.5   RAMLink       n/a
  729. ACE-as        61.5   RAMLink       REU
  730. ACE-as        49.5   ACE ramdisk   REU
  731. ACE-as        75.6   RAMLink       RAM0+RAM1
  732. ACE-as       150.5   1571          RAM0+RAM1
  733. Buddy        240.0   1571          n/a
  734.  
  735. Part of the assembly job was loaded into memory for the Buddy assembler, but
  736. the load time is included in the figure.  As you can see, buddy performs
  737. faster with a fast file device and slower with a slow file device (because it
  738. requires two passes).  I have a couple of tricks up my sleeve to improve the
  739. ACE assemble's performance.
  740.  
  741. There are also a couple of subtle errors in this implementation.  First, if it
  742. receives a "short block" from the source device, it will put whitespace
  743. between the current block and the next, thus potentially splitting a token.
  744. Also, if multiple files are used, the "ref" filename may not be valid.
  745.  
  746. Here are a few data structures for your enjoyment.
  747.  
  748. Identifier descriptor:
  749.  
  750. OFF   SIZ   DESCRIPTION
  751. ---   ---   ------------
  752.   0     4   next link in hash table bucket
  753.   4     4   value of symbol, pointer to reference list, or ptr to macro defn
  754.   8     1   offset of reference in expression of reference list
  755.   9     1   type: $00=value, $01=address, $80=unresolved, $ff=unresolved define
  756.  10     1   class: $00=normal, $01=private, $80=global (not used yet)
  757.  11     1   name length
  758.  12     *   null-terminated name string (1-240 chars)
  759.  
  760. Expression/Hole descriptor:
  761.  
  762. OFF   SIZ   DESCRIPTION
  763. ---   ---   -----------
  764.   0     1   hole type: $01=byte, $02=word, $03=triple, $04=long, $40=branch,
  765.             $80=label
  766.   1     1   expression length: maximum offset+1 in bytes
  767.   2     1   number of unresolved references in expression
  768.   3     1   source column of reference
  769.   4     4   address of hole
  770.   8     4   source line of reference
  771.  12     4   source file pointer
  772.  16    14   expression operand descriptor slot #1
  773.  30    14   expression operand descriptor slot #2
  774.  44    14   expression operand descriptor slot #3
  775.  58    14   expression operand descriptor slot #4
  776.  72    14   expression operand descriptor slot #5
  777.  86    14   expression operand descriptor slot #6
  778. 100    14   expression operand descriptor slot #7
  779. 114    14   expression operand descriptor slot #8
  780. 128    14   expression operand descriptor slot #9
  781. 142    14   expression operand descriptor slot #10
  782. 156    14   expression operand descriptor slot #11
  783. 170    14   expression operand descriptor slot #12
  784. 184    14   expression operand descriptor slot #13
  785. 198    14   expression operand descriptor slot #14
  786. 212    14   expression operand descriptor slot #15
  787. 226    14   expression operand descriptor slot #16
  788. 240    14   expression operand descriptor slot #17
  789. 254     -   END+1
  790.  
  791. Expression operand descriptor:
  792.  
  793. OFF   SIZ   DESCRIPTION
  794. ---   ---   -----------
  795.   0     1   operator: "+" or "-"
  796.   1     1   type of value: $00=number, $01=address, $80=unresolved identifier
  797.   2     1   monadic-operator result sign of value: $00=positive, $80=negative
  798.   3     1   hi/lo operator counts: high_nybble=">" count, low_nybble="<" cnt
  799.   4     4   numeric value or unresolved-identifier pointer
  800.   8     4   next unresolved reference in chain for unresolved identifier
  801.  12     1   offset in hole structure of next unresolved reference (operand)
  802.  13     1   reserved
  803.  14     -   END+1
  804. ------------------------------------------------------------------------------
  805. 9. THE FUTURE
  806.  
  807. This section is just random notes since I don't have the time right now to
  808. fill it in.  I will be implementing include files, conditional assembly, and
  809. macro assembly features in the future.  Modular assembly and relocatable-
  810. code generation are also in my plans.
  811.  
  812. ;todo: -implement storage classes: $00=internal, $01=rel.label, $80=exported
  813. ;      -implement all var types: 0=value, 1=address, 2=addr.high, 3=addr.low
  814. ;      -implement source column, make line:col point to start of cur token
  815. ;      -make it so you can use a "\<CR>" to continue a line
  816. ;      -add more operators: * / & | ~  full precedence?
  817. ;      -cache current symbol
  818. ;
  819. ; usage: as [-help] [-s] [-d] [-b] [-r] [-l] [-a addr] [file ...] [-o filename]
  820. ;
  821. ;     -help : produce this information, don't run
  822. ;        -s : produce symbol table dump at end
  823. ;        -d : provide debugging information (lots)
  824. ;        -b : produce binary module at end (default)
  825. ;        -r : produce relocatable module rather than binary module
  826. ;        -l : produce linkable ".o" module(s)
  827. ;        -a : set global code origin to given address
  828. ;        -o : put output into given filename
  829. ;
  830. ;     If -l option is not used, all files, including source and object modules,
  831. ; will be assembled together.  The output module name will be the base name of
  832. ; the first file given if it has a ".s" or ".o" extension, "a.out" if the first
  833. ; file has none of these extensions, or will be the filename given by the -o
  834. ; option if used.
  835. ;     If the -l option is used, then each given source module will be
  836. ; assembled independently into its own ".o" module.  Object modules will be
  837. ; ignored.
  838. ;     The global origin will be either that given by the -a option (if it is
  839. ; used) or by the local origin of the first source/object module.  Each
  840. ; source module that generates code must have a local code origin.
  841.  
  842. More Directives:
  843.  
  844. include "filename"
  845. if <expression> <relop> <expression>
  846. elsif <expression> <relop> <expression>
  847. else
  848. endif
  849. macro macroname
  850. endmacro
  851. export label1, label2, ..., labelN
  852. bss size_expression
  853.  
  854. macro blt there
  855.    bcc there
  856. endmacro
  857.  
  858. macro add ;?1=operand
  859.    clc
  860.    adc ?1
  861. endmacro
  862.  
  863. macro ldw ;?1=dest, ?2=source
  864.  if ?# != 2
  865.    error "the ldw macro instance doesn't have two arguments"
  866.  endif
  867.  if @1 = #
  868.    argshift 2 0
  869.    lda #<?2
  870.    sta ?1+0
  871.    lda #>?2
  872.    sta ?1+1
  873.  else
  874.    lda ?2+0
  875.    sta ?1+0
  876.    lda ?2+1
  877.    sta ?1+1
  878.  endif
  879. endmacro
  880. ------------------------------------------------------------------------------
  881. So, there is finally a powerful and convenient assembler universally available
  882. for both the 64 and 128... for free.  The source code for the assembler
  883. (written in the assembler's own assembly format, of course) is also available
  884. for free.  There are a few more features that need to be implemented, but I
  885. know exactly how to implement them.
  886.  
  887. Keep on Hackin'!
  888.  
  889. -Craig Bruce
  890. csbruce@ccnga.uwaterloo.ca
  891. "Give them applications and they will only want more; give them development
  892.  tools and they will give you applications, and more."
  893. ------------------------------------------------------------------------END---
  894.