Search   
Home  Print View  

 

Programmer's Manual

Branch Content

Syntax Details

Expressions

Expressions are used to directly represent numbers or strings. Since arithmetic operations are not included in the current assembly language, expressions are nothing but "literals".

As Heritage/1 is a strict 16-bits machine (memory is organized in 16-bits words), numerical expressions are always 16-bits integers (singed or unsigned) and values greater than 65,535 will generate overflow errors at assembling-time. Bytes (8-bits) are NOT represented within assembly code in any way. NOTE: A notable exception to this is the instruction INT which takes an 8-bits vector number as argument in assembly code (this byte is actually embedded within the op. code); in this case the argument is written as a normal 16-bits word although the assembler will report overflow error if it is greater than 0x00ff.

The following numerical expressions are valid:
---------------------------------------------
0x03ff    Hexadecimal
0b010101  Binary
44        Decimal
-44       Decimal negative
65000     Decimal. Valid but it may represent a negative binary (two's complement)


The following expressions are illegal:
--------------------------------------
-0x03ff   Only decimals can be explicitly signed.
68456     Overflow.
64,000    Commas as delimiters in numbers are not accepted.


A string is a null-terminated sequence of ASCII characters occupying a 16-bits word each (the assembler fills the MSB with zeros). The null termination consists of a word with all bits cleared.

Strings are written in assembly code starting with dollar sign ($) as in the following example:

:ERROR_MSGS
    #data $Drive not on-line
    #data $Volume not mounted on drive

A words about signed and unsigned integers

The Heritage/1 ALU makes use of two's complement arithmetic for adding and subtracting 16-bits numbers. However, this does not limit the programmer to the use of signed integers in assembly code, as signed or unsigned is mostly a matter of interpretation.

For instance, using the instruction ADD for adding 0xf000 to 0x0001 will result in 0xf001 which can be interpreted either as positive 61441 or negative or -4095. Moreover, when building more powerful arithmetic by software (such as BCD or Floating Point) the programmer is responsibly for defining data types and to make the correct representation and interpretation of the arithmetic sign.

Symbols

Simply put, symbols represent expressions. You define symbols by using the directive #define or the equ construct. The assembler also auto-define some symbols (labels in particular) while parsing the code; in the following example, the value for label QUIT is not given explicitly but calculated by the assembler.

; Listing #2
; Examples of symbols used as both address and data
;
STU_STOP     equ   0x0000
CMD_RWD      equ   0x0004
TAPE_REG     equ   0x4000
START        equ   0x0400

#org   START
       ; Check if tape is stopped.
       ld     a, TAPE_REG
       cmp    STU_STOP      
       jnz    QUIT

      ; Sent Rewind command to the tape driver:
       mvi     a, CMD_RWD
       sto     a, TAPE_REG
:QUIT
    hlt

As seen in code, a valid symbol contains nothing but alphabetic and numeral characters as well as underscores ( _ ). The length is restricted to 40 characters and the first one can not be numeric.

Each symbol must be defined only once in the entire project scope, so caution must be observed specially with large projects composed by many files. Duplicated symbols will be reported as errors.

Variables

Variables differ from symbols in that they can appear more than once within the code. You assign a value (either a symbol, an expression or another variable) to a variable by using the #set directive.

Variables don't need to be defined as they get auto-defined with the first assignment encountered during the parsing process.

The following statements are valid:

#set foo = 0x044
#set foo = SOME_SYMBOL
#set other_var = foo

Directives

By directive we understand those commands placed in assembly code that are targeted to the Assembler, as opposite to instructions which are targeted to the computer running the resulting binary code.

Directives start with the sharp character (#) followed by the directive's name, then the argument. These three parts are separated from each other by the mean of spaces or tabs.

The following directives are currently available.

Include

Syntax:

#include FILE_NAME

This directive causes the given file (FILE_NAME) to be opened and processed immediately as if it were part of the current file. This action is recursive so further #include directives found in included file will be processed in the encountered order.

The #include's argument (FILE_NAME) is the name of a source file expected to be in the "include directory"; the later was passed explicitly in the command line (second argument) when the assembler was launched, or was automatically extracted otherwise from the source-file path (first argument) at that time.

You can also specify a full path in the #include directive. That may be the case of being using reusable code from files placed in separate directories for better organization. The assembler will realize whether the #include argument is a filename or a full path and it will act accordingly. Either case, if the file does not exist, an error will be reported.

A given file can be #included more than once. This might be used to compensate for the lack of Macros, as in the following example:

      ; Using reusable code for sorting a list in memory.
      ; The included code accepts arguments in registers d, c.      

      mvi d, BUFF        ; List in memory to be sort out
      ld  c, BUFF_SIZE   ; Certain var in memory holding the buffer's size

      ; The included code does the job...
      #include /home/armando/src/lib/quick_sort.asm
      

Org

Syntax:

#org ADDRESS

Sets the origin address, effective since the first instruction following the directive's line. ADDRESS can be either a symbol or a numerical expression.

The following statements are valid:

START  equ 0x400
#org   START
#org   0x400

The following will result in error: "Illegal use of string":

#org  $START

Data

Syntax:

#data EXPRESSION

This directive is useful for filling areas of memory with fixed data such as lookup tables and string messages, as illustrated below.

#define   MSG_TABLE = 0x800

#org MSG_TABLE
#data     $File not found
#data     $Stack overflow

Starting at address 0x800 will be the (null-terminated) string "File not found" followed by the string "Stack overflow" (30 words total).

Define

Syntax:

#define SYMBOL = VALUE

This directive defines a symbol by indicating the expression (VALUE) it represents. The same can also be done with the equ construct for numeric expressions as illustrated in previous examples.

Actually, support for equ was introduced in H1ASM for compatibility with such "traditional" construct. However, the #define directive is conceptually more robust and more powerful in practice since it allows for symbolic strings too.

The previous example could also be written this way:

#define   MSG_TABLE         = 0x800
#define   ERR_MSG_F_NOFOUND = $File not found
#define   ERR_MSG_S_OV      = $Stack overflow

:MSG_TABLE

#data     ERR_MSG_F_NOFOUND
#data     ERR_MSG_S_OV

Set

Syntax:

#set VAR = VALUE

The set directive is used to assign a value to a variable. If the variable didn't exist, it is created at that time. VALUE can be either a symbol, an expression, another variable or even the same variable (which doesn't sound too much useful but it is legal anyways).

Once a variable is created, it can be used in instructions in place of the operand, as illustrated in the following example.

#define    BUFF_TAPE_1    = 0x4000
#define    BUFF_TAPE_2    = 0x4100

; Call subroutine passing argument in variable:
#set  @_buff = BUFF_TAPE_1
call READ_TAPE

; Call subroutine again passing a different argument in the same variable:
#set  @_buf  = BUFF_TAPE_2
call READ_TAPE


;
; -- SUBROUTINE --
;
:READ_TAPE
      ; Variable used as operand:
      ld   e, @_buff

Symbols and variables can not share names; failure to observe this will result in errors reported by the assembler. To overcome this limitation, naming conventions must be employed. We suggest to write symbols with uppercases and variables, written with lowercases, to start with the '@' character as illustrated in the previous example.

The need for variables comes from the lack of macros in H1ASM. In fact, you can "encapsulate" reusable generic code into separate source files, then #include it in your "client code" passing arguments through variables. Here is an example:

#set  @_quick_sort_buff       = BUFF_ADDR
#set  @_quick_sort_buff_size  = BUFF_SIZE_VAR

; The included code does the job. It uses the above variables:
#include /home/armando/src/h1_lib/quick_sort.asm

Format

Syntax:

#format FORMAT

Specifies the output format as explained in section Assembler Output.

Instructions

Homebuilt CPUs WebRing

JavaScript by Qirien Dhaela

Join the ring?

David Brooks, the designer of the Simplex-III homebrew computer, has founded the Homebuilt CPUs Web Ring. To join, drop David a line, mentioning your page's URL. He will then add it to the list.
You will need to copy this code fragment into your page.

Project start date: May 13 of 2009