Assembly Language Documentation
This page gives a brief presentation of the command input accepted by and pseudo opcodes used by the Heath HDOS assembler. This discussion does not include the 8080A opcodes themselves. For that information you should refer to 8080A Processor Technical Documentation.
Page Index | Command Line | |
Names & Numbers | ||
Pseudo Opcodes | ||
Data Definition | DB, DW, DS | |
Listing Control | TITLE, STL, EJECT, SPACE, LON, LOF, LARGE, ERR, WIDE | |
Address Assignment | ORG, EQU, SET, END | |
Copying Code | XTEXT, INCLU | |
Operators | +, -, *, /, # | |
Assumption Checking | ERRNZ, ERRZR, ERRPL, ERRMI | |
Conditional Assembly | IF, ELSE, ENDIF | |
Relocatable Code | CODE | |
Errors | ||
Cross Reference Listing |
Command Line
The general format of the command line is:
asm [out][,list][,xref]=source[,copydrv[,...]][/sw:val[/...]]Optional parameters are shown [inside brackets].
You may key the entire command on one line, as shown, or you can launch the assembler and then key the rest of the command at the prompt.
Most of the parameters are optional. We'll step through them one by one.
This is the only required parameter; it names the source file to be assembled. The drive may be specified; if left off the default is SY0: as normal. The extension may be specified; if left off the default is .ASM.
If this is in fact the only parameter specified on the command line, the source file will be assembled and any error messages displayed on the console. There will be no listing and the executable output will not be generated.
This optional parameter names the output executable file. The default file extension is .ABS. Example:
asm outfile=sourceThis will assemble the source file, keep the executable code generated by the assembly, and report any errors on the console.
This optional parameter names the listing output file. This can be a file or a serial device driver. If it is a file, the default file extention is .LST.
Examples:
asm outfile,lp:=sourceThe first example keeps the executable code and produces a listing on the printer. The second example does not generate executable code but does print a listing, which goes to a file named sy0:listing.lst.
This optional parameter names a work file for use by the cross reference utility, xref.abs. If this work file is included, all symbol definitions and references will be written to the work file, and the xref utility will be run after the assembly is complete. The cross reference listing is a continuation of the assembly listing. For further information, please see Cross Reference Listing below.
If you use the XTEXT or INCLU pseudo opcodes to copy source files into your main source file, the assembler will look for these files on the same disk as the source file. If they are not found there, the assembler will look at SY0:. You can name one or more alternate search drives with these parameters; alternate drives are searched first. Example:
asm sy1:myprog,lp:=sy1:myprog,sy2:This command says, assemble sy1:myprog.asm and save the executable file as sy1:myprog.abs. Create a listing on the printer. Included files are searched for on sy2:, then sy1:, and finally sy0:.
These optional parameters are command line switches used to override standard assembly parameters or options that you have selected in the source file.
These switches are available:
Switch | Function |
/form:nn |
This switch overrides the form size default, which is to ignore the size of the form and send a form-feed to the printer at the end of the page. By setting a line count, you disable form feeds and cause the assembler to send CR/LF's to space down to the next page. This switch does not need to be used with the emulator. |
/page:nn |
This switch overrides the standard page size of 60 lines. After nn lines are printed, the listing will advance to the next page. This is best used when you are going to print a listing in landscape format. Your page size may vary, but try this as a starting point: asm ,lp:=source/page:45 When prompted, select landscape output for the listing. This produces an excellent listing on the author's system. |
/lon:codes |
This overrides any listing options set by default or by any LON or LOF pseudo opcodes in the program source, by forcing the named listing options on. Valid codes are:
L - produce a listing |
/lof:codes |
This overrides any listing options set by default or by any LON or LOF pseudo opcodes in the program source, by forcing the named listing options off. Valid codes are the same as above. One very useful use of the /lof switch is to get a hardcopy listing of only the errors in a source file: asm ,lp:=source/lof:l This produces a listing but turns off the listing output, which seems silly. But errors are printed regardless of any listing options, so the result is a listing of just the errored lines. |
/large |
The assembler will normally keep the overlays in memory to speed processing. The /large switch will cause it to leave them on disk to make approx. 2.5K of additional memory available to the assembler. |
/err |
If you are creating a listing file, error messages will be written to the listing only and not to the console. This switch will cause error messages to be written to the console in addition to the listing file. |
/wide |
This tells the assembler that the printer has a wide carriage: 132 columes. The author has not found the use of this switch to be necessary in practice. |
Names & Numbers
Names are defined in the label field of an assembly language statement. Valid names consist of the characters A-Z, 0-9, $ and . (period). Lower-case characters can also be used, but the name will be case sensitive, and opcodes and register names cannot be entered in lower case, so it's traditional (and easier) to use upper-case characters only.
Names can be from one to seven characters in length. They must start with an alphabetic character, $ or . (period); they may not start with a digit. Following are examples of valid names:
AFIELD, TUESDAY, $123, .TICCNT, .27, LOOP.CT, OCT2DECLiteral numbers can be entered in decimal, octal, split octal, hexadecimal and binary notation. A number must begin with a digit (0-9). The last character in the number (the radix) will indicate the base of the number, according to the following table:
Radix | Number Base | Example Value |
none | Decimal | 8192 |
D | Decimal | 8192D |
Q | Octal | 300Q |
O | Octal | 300O |
A | Split Octal | 40200A |
H | Hexadecimal | 1BH |
B | Binary | 11001001B |
Because O ("oh") is so easily mistaken for 0 ("zero"), it's rarely used. Use Q instead to indicate Octal.
Take special care with hexadecimal numbers to make sure they start with a digit. The assembler will interpret AH as a symbol. 0AH is a hexadecimal number.
Take care to note the difference between octal and split octal notation: split octal notation, a Heathkit invention, I believe, uses the first three digits to represent the high-order byte of the number, and the last three digits to represent the low-order byte of the number. Octal simply uses a digit to represent 3 bits. Thus, while the maximum 16-bit split octal number is 377377A, the maximum 16-bit octal number is 177777Q. Both of these numbers will evaluate to 16 binary '1' bits, or decimal 65,535.
Pseudo Opcodes
Data Definition
These pseudo opcodes generate data output, or reserve space for data in the program.
The Define Byte opcode stores one or more individual bytes or strings of bytes at the current assembly address. Characters and strings are enclosed by 'single quotes'. Multiple parameters are separated by a comma. Any valid literal value can be used as well, as long as it resolves to an 8-bit value. The label is optional. Examples:
MESSAGE DB 'This is a string',212QThe Define Word opcode stores one or more 16-bit values at the current assembly address. Values are stored low-order byte first, as is standard for Intel processors. Characters and strings of two characters are valid, as are any literal or symbolic values that resolve to a 16-bit number. Multiple parameters are separated by a comma. The label is optional. Examples:
WORDS DW 12Q,10000Note that TWOBYTE will be stored as "BA" in memory, as words are always stored low-order byte first. When you execute an LHLD TWOBYTE instruction, 'A' will be in register H and 'B' will be in register L.
The Define Space opcode reserves storage space by advancing the output pointer 'parameter' bytes. The defined space should be considered to be uninitialized at run time.
LISTBUF DS 256Space reserved with DS can baloon the size of your .ABS file. In this example, there will be 11 sectors worth of empty space in the .ABS file. For this reason, this space is generally allocated at the end of the program, and the ORG pseudo opcode is used to eliminate it from the output file. See ORG for an example.
Listing Control
These pseudo opcodes simply effect the appearance of the listing and what gets printed on it. They have no effect on the real work of the assembler. Each of these statements is optional.
Specifies a string to be printed as a title at the top of each page of the listing. This statement, when used, is generally the first statement in the program.
Specifies a string to be printed as a subtitle on line #2 of each subsequent page of the listing. This statement, when used, generally precedes each major section of code.
Advances the listing to the top of the next page.
Advances the listing 'n' blank lines. If the second parameter is included, the assembler checks to see whether there are 'm' lines left on the current page. If not, the opcode is treated like an EJECT. Example:
POP HTurns on listing parameters for all following code up to the next LOF statement which names the same parameters. The parameters are the same as discussed above under Command Line. The command line LON and LOF switches override matching parameters set here. See example below.
Turns off listing parameters for all following code up to the next LON statement which names the same parameters. The parameters are the same as discussed above under Command Line. The command line LON and LOF switches override matching parameters set here.
LON and LOF example:
LOF C Don't print theseAddress Assignment
These opcodes assign values (frequently address values) to symbols, change the code output address, and set the entry point address.
Note: The character "*", when used in the place of a number or symbol, represents the current assembly address. You can do arithmatic on this address, just like any other number or symbol. So "*-10" means "10 bytes back from the current assembly address" and "**2" means "the current assembly address times 2."
The Origin pseudo opcode sets the assembler's output location pointer. It's generally used at the top of a program to set the load address, which is the same as the address of the first byte of assembly object output. Example:
XTEXT HDOSThe address given can be symbolic, as shown, or a literal address.
Programmers frequently use a series of DS statements to assign sequential values to a series of labels. ORG is used to set the origin to zero to start the list. You may need to save and restore the current origin (ref. SET opcode below):
. SET * Save originOne useful trick is to move all of your large DS assignments to the very end of your program and then ORG above them just before the END statement. This will prevent the assembler from writing all that empty space to your .ABS file, resulting in a smaller .ABS file. But be sure to use the .SETTOP syscall to allocate the space to your program. Example (ref. END opcode below):
ORG USERFWAThe net effect of this technique in the example was to reduce the size of the .ABS file by 9-10 sectors.
The Equate opcode permanently assigns a value to a symbol. The value can be any legal literal or address value, a calculated value, even a relocatable expression when writing position-independent code (ref. Relocatable Code). Examples:
.TICCNT EQU 40033AWhen the value expression contains a symbol, as in the ENL example above, the symbol must have been defined earlier in the program; symbols referenced in EQU statements must be known on the first assembly pass.
The SET opcode works exactly like EQU, except that the value is temporarily assigned to the name. The name can be redefined in a subsequent SET command.
There's a good example of the use of SET in ORG (above) and in Relocatable Code.
The END opcode is the last statement in the souce program. It is a required statement.
The entry-point address is required if the output code type is ABS. This is the address that HDOS jumps to after first loading your program into memory. The entry-point address must not be used if the output code type is PIC (ref. Relocatable Code).
See ORG above for an example of the END opcode in an ABS program.
Copying Code
This opcode is used to copy "external text" into your program for assembly. The file "filename.ACM" will be located and assembled as if it were included in your program in place of the XTEXT statement. This makes it very convenient to put common definitions into a so-called "common deck" for use by all the programs you write.
There are several examples of the use of XTEXT in previous sections; I won't repeat them here.
The assembler looks for the common deck on the same drive as the source file, on SY0:, and on any other named drive. Refer to Command Line for more information.
This is an optional spelling of XTEXT; the statement does the same thing as XTEXT.
Operators
These operators can be used to cause the assembler to do simple arithmatic for you. There are very few restrictions; almost any legal value can be operated on. For example, 'a'+3 will evaluate to 'd'.
Expression evaluation always procedes from left to right. There is no operator precedence, and you can't alter the order of evaluation with parenthesis. 1+2*3 is 9. If you wanted 7, you should have coded 3*2+1.
See Relocatable Code for restrictions on address manipulation when you are producing PIC output.
Add two values together:
NL+200Q - add the high bit to NLSubtract two values (3-2) or make a single number negative (-2).
Multiply two values.
Divide two values. This will return the integer result with no rounding, so 7/3=2, not 2.5.
Return the low-order byte of a 16-bit value. Assuming that the label BEGIN had a value of 42.200A in split octal notation, #BEGIN would return 200Q, a value that will fit in a single byte.
Assumption Checking
These pseudo opcodes help the programmer validate assumptions about the relative location of variables in a program, and produce error messages if those assumptions are not true.
If the evaluated expression is not zero, an error is flagged. This is the most commonly used form of the ERRxx series of pseudo opcodes. Example:
FRSTBYT DS 1In using the INX H instead of another LXI H, the programmer saved two bytes and some execution time, but created a potential future error. If someone comes along later and inserts a field between FRSTBYT and SCNDBYT, the code will no longer work. But there will be no other error generated, because the assembler has no way to know that you meant to step ahead from FRSTBYT to SCNDBYT.
The ERRNZ statement says, subtract the two addresses, giving 1, and then subtract 1 from that, which should give zero. That arrangement of these two fields will allow this code to work. If someone inserts a field between FRSTBYT and SCNDBYT, this will no longer evaluate to zero and an error will be flagged. The programmer can then decide whether to revise the code or move the new field.
If the evaluated expression is zero, an error is flagged.
If the evaluated expression is a positive number, an error is flagged.
If the evaluated expression is a negative number, an error is flagged.
Conditional Assembly
The IF/ELSE/ENDIF statements allow you to conditionally assemble parts of your program. This allows the source code to reconfigure itself based on options selected or changes made.
Evaluate the expression. If it is true, assemble the following code. If it is not true, do not assemble the following code.
The expression is considered 'true' if it evaluates to a value of zero. It is considered 'not true' if it evaluates to a non-zero value.
By default, code that is not assembled because of an IF statement, will not be listed in the listing file. This default condition can be changed by using the LON I pseudo opcode or command-line switch. See Command Line and Listing Control above.
Refer to the examples following the ENDIF opcode.
This is only valid inside an IF block. This reverses the evaluation from true to false or false to true.
Ends an IF block.
Examples:
TRUE EQU 0Here we can have one set of source code that can be assembled into both English and French versions. The example shown is the English version. You can change to French by simply changing one equate from FALSE to TRUE and reassembling.
Now, here's a different (somewhat contrived) way to solve the problem in the ERRNZ example above.
FRSTBYT DS 1Relocatable Code
A fair piece of HDOS, including HDOS.SYS, the overlays and all the drivers, were written in Position Independent Code, or PIC. PIC is assembled from an origin of zero. When the code is loaded into memory, a relocation table appended to the end of the PIC file is used to find any addresses that need relocation. Relocation is done by adding the load address to the address in the program, giving the relocated address.
Symbols that are defined relative to the current assembly address are considered to be relocatable, and 16-bit references to these symbols will be placed in the relocation table. There are three rules to keep in mind when doing arithmatic on relocatable symbols:
If you add or subtract a non-relocatable value, the result is still relocatable. So if BEGIN is relocatable, so is BEGIN+10.
If you subtract two relocatable symbols, the result is a simple number that is not relocatable. So END-START would be a constant not subject to relocation. (This should make sense: references to both END and START will be relocated at load time, but the distance between them remains constant.)
You can't subtract a relocatable symbol from a non-relocatable value. 10-BEGIN makes no sense and will be flagged as an error. This one causes us to have to jump a hurdle or two when writing device drivers; see below.
This is all controlled by the CODE statement. This statement must be issued before any output is written by the assembler:
CODE PICThis enables the generation of Position Independent Code.
(Note that there is a corresponding CODE ABS statement that specifies that the output should be in ABS format. However, this is the default case and you will never see one used in a program.)
A common problem occurs when writing device drivers: You frequently have to waste some space between the header and the entry point for SET processing (at label DVD.STE) and again between SET processing and the main driver entry point (at label DVD.ENT). The normal way to move the assembly location ahead would be something like:
DS DVD.ENT-*But that would violate rule #3, since "*" is, by definition, relocatable, and DVD.ENT is a simple constant. What we need to do is create a non-relocatable label that represents the current assembly address, using two different operands on the CODE statement:
* Waste space up to DVD entry pointCODE -REL turns relocation off. The subsequent SET command creates a non-relocatable symbol. We can then turn relocation back on with CODE +REL and DS the space between the entry point and where we are.
(The example is from the emulator's AT device driver source code.)
Errors
The assembler flags errors by writing one or more single characters at the left-hand margin of the assembly listing. Following is a listing of the errors that the author has been able to generate; it may not be an exhaustive list.
Error Code | Description |
O | Unknown opcode |
A | Illegal or missing argument (a string missing a quote, wrong digit like 239Q, no entry point on END, etc.) |
V | Value out of range (example: DB 1000) |
U | Undefined symbol |
R | Register unknown. Also relocation error (10-*) |
F | Illegal label |
D | Duplicate label |
Cross Reference Listing
You need to do two things to get a symbol cross reference listing:
Make sure that the program xref.abs is on the same disk as the assembler, asm.abs.
Specify the name of a cross reference work file on the assembly command line:
asm ,lp:,xref=sy1:source (ref. Command Line)The drive that you use for the cross reference work file must have sufficient free space in order to hold the file. Unfortunately, there is no good way to know how big that is; the file is deleted when the assembly is complete. The author uses a 2-sided 40-track drive as his development system volume and always has 200-300 free sectors available, and there has never been a problem.
There are two sections to the cross reference listing, the Symbol Table and the Cross Reference Table.
Symbol Table
The symbol table is a dump of the entire symbol table, in alphabetical order. The table shows the name of the symbol and the symbol's value in split-octal notation. Here is a small subset of a symbol table:
SYMBOL TABLECross Reference Table
The cross reference listing shows the name and value of each symbol used in the program, along with the line number of each source code statement that references the symbol. Here is a small subset of a cross reference table:
CROSS REFERENCE TABLEThe first colum lists the symbol name. The next column lists its value.
To the right of the value is a list of statement numbers where this symbol is referenced. The letters following the line number indicate where and how the symbol was defined. For example, ALEDS was defined on line 89 of the source listing, as the label of an opcode or pseudo opcode other than EQUate. The symbol is not used anywhere else in this program. (This symbol is defined as part of HDOS.ACM, which was copied into the program.) Symbol COUT was defined by an EQUate statement at source line number 442, and was referenced in three other places.