High Level Assembly
Encyclopedia
High Level Assembler (HLA) is an assembly language developed by Randall Hyde
Randall Hyde
Randall Hyde is best known as the author of , a popular book on assembly language programming. He created the Lisa assembler in the late 1970s and developed the High Level Assembly language....

. It allows the use of higher-level language constructs to aid both beginners and advanced assembly developers. It fully supports advanced data types and object-oriented assembly language programming. It uses a syntax loosely based on several high-level languages (HLL), such as Pascal
Pascal (programming language)
Pascal is an influential imperative and procedural programming language, designed in 1968/9 and published in 1970 by Niklaus Wirth as a small and efficient language intended to encourage good programming practices using structured programming and data structuring.A derivative known as Object Pascal...

, Ada
Ada (programming language)
Ada is a structured, statically typed, imperative, wide-spectrum, and object-oriented high-level computer programming language, extended from Pascal and other languages...

, Modula-2
Modula-2
Modula-2 is a computer programming language designed and developed between 1977 and 1980 by Niklaus Wirth at ETH Zurich as a revision of Pascal to serve as the sole programming language for the operating system and application software for the personal workstation Lilith...

, and C++
C++
C++ is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises a combination of both high-level and low-level language features. It was developed by Bjarne Stroustrup starting in 1979 at Bell...

, to allow creating readable assembly language programs, and to allow HLL programmers to learn HLA as fast as possible.

Origins and goals

HLA was originally conceived as a tool to teach assembly language programming at the college/university level. The goal is to leverage students' existing programming knowledge when learning assembly language to get them up to speed as fast as possible. Most students taking an assembly language programming course have already been introduced to high-level control structures such as IF, WHILE, FOR, etc. HLA allows students to immediately apply that programming knowledge to assembly language coding early in their course, allowing them to master other prerequisite subjects in assembly before learning how to code low-level forms of these control structures. "The Art of Assembly Language Programming" by Randall Hyde
Randall Hyde
Randall Hyde is best known as the author of , a popular book on assembly language programming. He created the Lisa assembler in the late 1970s and developed the High Level Assembly language....

 uses HLA for this purpose.

High vs. low-level assembler

The HLA v2.x assembler supports the same low-level machine instructions as a regular, low-level, assembler. The difference is that high-level assemblers (such as HLA, MASM, or TASM
Tasm
TASM can refer to:*Turbo Assembler, Borland's x86 assembler* Turbo Assembler, Omikron's Commodore 64-based 6502 assembler*Table Assembler, a table driven cross-assembler for small microprocessors.*Tomahawk Anti-Ship Missile...

 on the x86) also support high-level-language-like statements such as IF, WHILE, and so on, and fancier data declaration directives, such as structures/records, unions, and even classes.

The HLA language has caused some confusion in the PC assembly language community since its creation in 1999. Unlike most other assembler tools, the HLA compiler includes a Standard Library: thousands of functions, procedures, and macros that can be used to create full applications with the ease of a high-level language. While assembly language libraries are not new, a language that includes a large standardized library makes programmers far more likely to use such library code rather than simply writing their own library functions. For this reason, it's very common to see demo HLA programs that look like this:

program helloWorld;
#include("stdlib.hhf")
begin helloWorld;

stdout.put( "Hello World" nl );

end helloWorld;

There is nary a machine language instruction to be found here. Combined with the Pascal and C-like declarations that HLA supports, many people get the impression that HLA is not an assembly language at all, but rather some sort of high-level language. In fact, the "stdout.put" statement in the above code is nothing more than an assembly language macro that expands to code like this:

program HelloWorld;
#include( "stdlib.hhf" )

static
hwString :string := "Hello World", nl;

begin HelloWorld;

// Push the address of the "Hello World" string

push( hwString );

// Call an HLA Standard Library function that
// will print the string whose address has
// been pushed on the stack.

call stdout.puts;

end HelloWorld;

There is absolutely nothing stopping a programmer from writing the "Hello World" program in low-level assembly language, should they really want to do this. However, for the beginner who is experiencing their first hour with assembly language, the former code is far more approachable than this latter code (i.e., explaining the stack and how parameters are passed to a procedure via the stack is a relatively advanced subject).

HLA supports all the same low-level machine instructions as other x86 assemblers and, indeed, HLA's high-level control structures are based on the ones found in MASM and TASM
Tasm
TASM can refer to:*Turbo Assembler, Borland's x86 assembler* Turbo Assembler, Omikron's Commodore 64-based 6502 assembler*Table Assembler, a table driven cross-assembler for small microprocessors.*Tomahawk Anti-Ship Missile...

 (whose HLL-like features predated the arrival of HLA by several years). One can write low-level assembly code in HLA just as easily as with any other assembler by simply ignoring the HLL-control constructs. Indeed, in contrast to HLLs like Pascal and C(++), HLA doesn't require inline asm statements. HLL-like features appear in HLA to provide a learning aid for beginning programmers by smoothing the learning curve, with the assumption that they will discontinue the use of those statements once they master the low-level instruction set (in practice, many experienced programmers continue to use HLL-like statements in HLA, MASM, and TASM, long after they've mastered the low-level instruction set, but this is usually done for readability purposes).

Of course, as the earlier "Hello World" example demonstrates, it is possible to write "high-level" programs using HLA, avoiding much of the tedium of low-level assembly language programming. Some assembly language programmers reject HLA out of hand because it allows programmers to do this. However, supporting both high-level and low-level programming gives any language an expanded range of applicability. If one must do only low-level-only coding, that is possible. If one must write more readable code, using higher-level statements is an option.

Distinguishing features

Two HLA features set it apart from other x86 assemblers: its powerful macro system (compile-time language) and the HLA Standard Library.

Macro system

HLA's compile-time language allows programmers to extend the HLA language with ease, even creating their own little Domain Specific Language to help them easily solve common programming problems. The stdout.put macro briefly described earlier is a good example of a sophisticated macro that can simplify programmers' lives. Consider the following invocation of the stdout.put macro:

stdout.put( "I=", i, " s=", s, " u=", u, " r=", r:10:2, nl );

The stdout.put macro processes each of the arguments to determine the argument's type and then calls an appropriate procedure in the HLA Standard library to handle the output of each of these operands. Assuming i is a 32-bit signed integer, s is a string variable, u is a 32-bit unsigned integer, and r is a 32-bit floating point value, the macro invocation above expands to code like this:

push( iEqualsStr ); // "I=" string
call stdout.puts; // Print "I="

push( i );
call stdout.puti32; // Print i as a 32-bit signed integer.

push( sEqualsStr ); // " s=" string
call stdout.puts; // print " s="

push( s ); // Push the address of the string
call stdout.puts; // and print it.

push( uEqualsStr ); // Push the address of " u=" string
call stdout.puts; // and print it.

push( rEqualsStr ); // Push the address of " r=" string
call stdout.puts; // and print it.

push((type dword r)); // Push r's value onto the stack.
pushd( 10 ); // Push the field width.
pushd( 2 ); // Push decimal positions.
call stdout.putr32; // Print the value as real.

call stdout.newln; // Print a new line character sequence.

It should be rather clear that the former code is much easier to write, read, and maintain than this latter code; this is one of the advantages of using macros in assembly language code. Of course, most assemblers provide some sort of macro capability, the advantage that HLA offers over other assemblers is that it is capable of processing macro arguments like "r:10:2" using HLA's extensive compile-time string functions, and HLA's macro facilities can figure out the types of variables (such as i, u, s, and r in this example) and use that information to direct macro expansion, as was done in this example.

HLA's macro language provides a special "Context-Free" macro facility. This feature allows programmers to easily write macros that span other sections of code via a "starting" and "terminating" macro pair (along with optional "intermediate" macro invocations that are only available between the start/terminate macros). For example, one can write a fully recursive/nestable SWITCH/CASE/DEFAULT/ENDSWITCH statement using this macro facility . Here's the (rather lengthy) code that does this:
#if( ! @defined( hll_hhf ))
?hll_hhf := true;
#includeonce( "hla.hhf" )

namespace hll; @fast;

val
cswitch :boolean := boolean(0);
useBubbleSort :boolean := boolean(0);
_TotalCases_ :uns32 := 0;

const
_maxCases_ := 2048;
_hugeNumber_ := 512;

type
_caseRecord_:
record

value:uns32;
clabel:uns32;

endrecord;

val
leftItem :_caseRecord_ := _caseRecord_:[0,0];
rightItem :_caseRecord_ := _caseRecord_:[0,0];

#macro lessThan;
(@global:hll.leftItem.value < @global:hll.rightItem.value)
#endmacro
// _SortCases_
//
// This routine does a bubble sort on an array
// of _caseRecord_ objects. It sorts in ascending
// order using the "value" field as the key.
//
// The switch macro only uses this sorting function
// if the hll.useBubbleSort variable is set to true.
// The caller must set hll.useBubbleSort to true after
// the "switch" macro invocation and before the "endswitch"
// macro invocation.
//
// This is a good old fashioned bubble sort which
// turns out to be very efficient because:
//
// (1) The list of cases is usually quite small, and
// (2) The data is usually already sorted (or mostly sorted).

val
_sort_i_ :string;
_sort_bnd_ :string;
_sort_didswap_ :string;
_sort_temp_ :string;

#macro _SortCases_( _sort_array_, _sort_size_ );

?@global:hll._sort_bnd_ := _sort_size_ - 1;
?@global:hll._sort_didswap_ := true;
#while( @global:hll._sort_didswap_ )

?@global:hll._sort_didswap_ := false;
?@global:hll._sort_i_ := 0;
#while( @global:hll._sort_i_ < @global:hll._sort_bnd_ )

#if
(
_sort_array_[@global:hll._sort_i_].value >
_sort_array_[@global:hll._sort_i_+1].value
)

?@global:hll._sort_temp_ := _sort_array_[@global:hll._sort_i_];
?_sort_array_[@global:hll._sort_i_] := _sort_array_[@global:hll._sort_i_+1];
?_sort_array_[@global:hll._sort_i_+1] := @global:hll._sort_temp_;
?@global:hll._sort_didswap_ := true;

#endif
?@global:hll._sort_i_ := @global:hll._sort_i_ + 1;

#endwhile
?@global:hll._sort_bnd_ := @global:hll._sort_bnd_ - 1;

#endwhile;


#endmacro

end hll;

/*******************************************************************/
/* R U N T I M E L A N G U A G E S W I T C H S T A T E M E N T */
/*******************************************************************/
#id( switch )
#id( case )
#id( default )
#id( endswitch )
// HLA Macro to implement a C/C++ Switch Statement.
// Note that the switch parameter must be a register.

#macro switch( _switch_reg_ ):
_switch_minval_,
_switch_maxval_,
_switch_otherwise_,
_switch_endcase_,
_switch_jmptbl_,
_switch_cases_,
_switch_caseIndex_,
_switch_doCase_,
_switch_hasOtherwise_,
_switch_caseCntr_;

// Generate a unique ID for this case statement
// (for use by the labels it produces).

?_switch_caseCntr_: uns32 := hll._TotalCases_;
?hll._TotalCases_ := hll._TotalCases_ + 1;

// Default to using the built-in quicksort algorithm.
// Users may want to specify the bubble sort algorithm
// if they've got a lot of cases and they're already
// sorted (or mostly sorted).

?hll.useBubbleSort := false;

// Verify that we have a register operand.

#if( !@isReg32( _switch_reg_ ) )

#error( "Switch operand must be a register" )

#endif

// Create the _switch_cases_ array. Allow, at most, _maxCases_ cases.

?_switch_cases_:hll._caseRecord_[ hll._maxCases_ ] :=
[hll._maxCases_ dup [ hll._caseRecord_:[0,0]]];

// General initialization for processing cases.

?_switch_caseIndex_ := 0; // Index into _switch_cases_ array.
?_switch_minval_ := $FFFF_FFFF; // Minimum case value.
?_switch_maxval_ := 0; // Maximum case value.
?_switch_hasOtherwise_ := false;// Determines if DEFAULT section present.
// If the user wants strict C/C++ syntax, they need to set the
// symbol "hll.cswitch" to true before using this macro.
//
// If they have selected C/C++ syntax, then emit a forever
// loop around the code associated with this switch statement.
// This is to allow the user to specify the BREAK statement
// to exit a case. Note that even though this code emits for..endfor,
// it never loops because a BREAK is always emitted.

#if( hll.cswitch )

forever

#endif

// We need to process the cases to collect information like
// _switch_minval_ before emitting the indirect jump. So move the
// indirect jump to the bottom of the case statement.

jmp _switch_doCase_;
// "case" keyword macro handles each of the cases in the
// case statement.

#keyword case( _switch_parms_[] ):
_switch_parmIndex_,
_switch_parmCount_,
_switch_constant_;

?_switch_parmCount_:uns32;
?_switch_parmCount_ := @elements( _switch_parms_ );

#if( _switch_parmCount_ <= 0 )

#error( "Must have at least one case value" );
?_switch_parms_:uns32[1] := [0];

#endif

// If we have at least one case already, terminate
// the previous case by transferring control to the
// first statement after the endcase macro. Note
// that the semantics here differs from C/C++
// (C/C++ falls through to the next case).
//
// Of course, if this is a C/C++ syntax switch
// statement, don't emit the jump because it is
// the programmer's responsibility to specify the
// BREAK statement.

#if( (!hll.cswitch) & (_switch_caseIndex_ <> 0) )

jmp _switch_endcase_;

#endif

?_switch_parmIndex_:uns32;
?_switch_parmIndex_ := 0;
#while( _switch_parmIndex_ < _switch_parmCount_ )

?_switch_constant_: uns32;
?_switch_constant_: uns32 :=
uns32( @text( _switch_parms_[ _switch_parmIndex_ ]));

// Update minimum and maximum values based on the
// current case value.

#if( _switch_constant_ < _switch_minval_ )

?_switch_minval_ := _switch_constant_;

#endif
#if( _switch_constant_ > _switch_maxval_ )

?_switch_maxval_ := _switch_constant_;

#endif

// Emit a unique label to the source code for this case:

@text
(
"_case"
+ string( _switch_caseCntr_ )
+ "_"
+ string( _switch_caseIndex_ )
+ "_"
):

// Save away the case label and the case value so we
// can build the jump table later on.

?_switch_cases_[ _switch_caseIndex_ ].value := _switch_constant_;
?_switch_cases_[ _switch_caseIndex_ ].clabel := _switch_caseIndex_;

// Bump _switch_caseIndex_ value because we've just processed
// another case.

?_switch_caseIndex_ := _switch_caseIndex_ + 1;
#if( _switch_caseIndex_ >= hll._maxCases_ )

#error( "Too many cases in statement" );

#endif

?_switch_parmIndex_ := _switch_parmIndex_ + 1;

#endwhile

// Handle the default keyword/macro here.

#keyword default;

// If there was not a preceding case, this is an error.
// If so, emit a jmp instruction to skip over the
// default case.
//
// Of course, if this is a C/C++ syntax switch
// statement, don't emit the jump because it is
// the programmer's responsibility to specify the
// BREAK statement (or fall off the bottom of the
// switch statement).

#if( _switch_caseIndex_ < 1 )

#error( "Must have at least one case" );

#endif
#if( !hll.cswitch )

jmp _switch_endcase_;

#endif

// Emit the label for this default case and set the
// _switch_hasOtherwise_ flag to true.

_switch_otherwise_:
?_switch_hasOtherwise_ := true;
// The endswitch terminator/macro checks to see if
// this is a reasonable switch statement and emits
// the jump table code if it is.

#terminator endswitch:
_switch_i_,
_switch_j_,
_switch_curCase_,
_switch_string_;

#if( (_switch_maxval_ - _switch_minval_) > hll._hugeNumber_ )

#if( (_switch_maxval_ - _switch_minval_) > hll._maxCases_ )

// Perhaps in the future, this macro could
// switch to generating an if..elseif..elseif...
// chain if the range between the values is
// too great.

#error( "Range of cases is too great" );

#else

#print( "Warning: Range of cases is large" );

#endif

#endif

// Table emission algorithm requires that the _switch_cases_
// array be sorted by the case values.


#if( hll.useBubbleSort )

hll._SortCases_( _switch_cases_, _switch_caseIndex_ );

#else

?_switch_cases_ :=
@sort
(
_switch_cases_,
_switch_caseIndex_,
hll.leftItem,
hll.rightItem,
"hll.lessThan"
);
#endif

// Build a string of the form:
//
// _switch_jmptbl_:dword[ xx ] := [&case1, &case2, &case3...&casen];
//
// so we can output the jump table.

readonly

_switch_jmptbl_:dword[ _switch_maxval_ - _switch_minval_ + 2] := [

?_switch_i_ := 0;
#while( _switch_i_ < _switch_caseIndex_ )

?_switch_curCase_ := _switch_cases_[ _switch_i_ ].value;
#if( _switch_curCase_ = _switch_cases_[ _switch_i_ + 1 ].value )

#error
(
"The same CASE constant (" +
string( _switch_curCase_ ) +
") appears two or more times in SWITCH statement"
)

#endif

// Emit the label associated with the current case:

@text
(
"&"
+ "_case"
+ string( _switch_caseCntr_ )
+ "_"
+ string( _switch_cases_[ _switch_i_ ].clabel )
+ "_,"
)


// Emit "&_switch_otherwise_" table entries for any gaps present
// in the table:

?_switch_j_ := _switch_cases_[ _switch_i_ + 1 ].value;
?_switch_curCase_ := _switch_curCase_ + 1;

#while( _switch_curCase_ < _switch_j_ )

&_switch_otherwise_,
?_switch_curCase_ := _switch_curCase_ + 1;

#endwhile
?_switch_i_ := _switch_i_ + 1;

#endwhile

// Emit a dummy entry to terminate the table:

&_switch_otherwise_];
endreadonly;

#if( _switch_caseIndex_ < 1 )

#error( "Must have at least one case" );

#endif

// After the default case, or after the last
// case entry, jump over the code that does
// the conditional jump.
//
// Note that we do this even if the cswitch
// syntax is specified, because we've hit the
// endswitch at this point and we need to skip
// over the table jump.

jmp _switch_endcase_;

// Okay, here's the code that does the conditional jump.

_switch_doCase_:

#if( _switch_minval_ <> 0 )

cmp( _switch_reg_, _switch_minval_ );
jb _switch_otherwise_;

#endif
cmp( _switch_reg_, _switch_maxval_ );
ja _switch_otherwise_;
jmp( _switch_jmptbl_[ _switch_reg_*4 - _switch_minval_*4 ] );
// If cswitch is true (and, therefore, the user
// wants a C/C++ compatible switch statement),
// then we need to emit an endfor here to balance
// the for emitted earlier.
//
// In general, this extra jump never executes, however,
// it is possible for this code to execute if the user
// specifies a "continue" or "continueif" statement in
// their switch statement. This, of course, would cause
// the whole switch statement to repeat (might even be useful
// on occasion, who knows?).

#if( hll.cswitch )

endfor;

#endif

// If there was no default case, transfer control
// to the first statement after the "endcase" clause.

#if( !_switch_hasOtherwise_ )

_switch_otherwise_:

#endif

// When each of the cases complete execution,
// transfer control down here.

_switch_endcase_:

// The following statement deallocates the storage
// associated with the _switch_cases_ array.

?_switch_cases_ := 0;
// The following forces a semicolon after
// the SWITCH invocation.

static
;
endstatic;
#endmacro
#endif

There are four macros of interest in this code snippet: #macro switch (which begins a switch statement), #keyword case and #keyword default (that handle the CASE and DEFAULT clauses in the switch statement), and #terminator endswitch (that ends a switch statement. Without going into the details concerning the HLA compile-time language (CTL), suffice to say that the switch macro initializes several compile-time data objects that will track code generation, the CASE and DEFAULT macros emit code that will terminate the previous case (if one exists, emit the appropriate labels for the current case, and update the compile-time tables so the ENDSWITCH macro can generate an appropriate jump table for these statements.

Because of the HLA macro facilities context-free design, one can nest these switch..case..default..endswitch statements and the nested statements' emitted code will not conflict with the outside statements.

Here is a sample HLA program using this macro:
program t;
#include("switch.hhf")

begin t;

switch( eax );

case( 1 )

switch( ebx );

case( 100 )
mov( 100, ebx );

case( 101 )
mov( 101, ebx );

default
mov( 0, ebx );

endswitch;

case( 2 )

mov( 2, eax );

case( 3 )

mov( 3, eax );

default

mov( 0, eax );

endswitch;

end t;

Here is the macro expansion for the switch statement in this program:

readonly
_0053__switch_jmptbl____hla_104:DWord; @nostorage;
dword &_case1_0___hla_93;
dword &_case1_1___hla_98;
dword &_0051__switch_otherwise____hla_99;
_0044__switch_jmptbl____hla_119:DWord; @nostorage;
dword &_case0_0___hla_78;
dword &_case0_1___hla_109;
dword &_case0_2___hla_113;
dword &_0042__switch_otherwise____hla_114;
jmp _0047__switch_doCase____hla_74;

_case0_0___hla_78:
jmp _0056__switch_doCase____hla_89;

_case1_0___hla_93:
mov( 100, ebx );
jmp _0052__switch_endcase____hla_97;

_case1_1___hla_98:
mov( 101, ebx );
jmp _0052__switch_endcase____hla_97;

_0051__switch_otherwise____hla_99:
mov( 0, ebx );
jmp _0052__switch_endcase____hla_97;

_0056__switch_doCase____hla_89:
cmp( 100, ebx );
jb _0051__switch_otherwise____hla_99;
cmp( 101, ebx );
ja _0051__switch_otherwise____hla_99;
jmp( (type dword _0053__switch_jmptbl____hla_104[ebx*4-400]) );

_0052__switch_endcase____hla_97:
jmp _0043__switch_endcase____hla_108;

_case0_1___hla_109:
mov( 2, eax );
jmp _0043__switch_endcase____hla_108;

_case0_2___hla_113:
mov( 3, eax );
jmp _0043__switch_endcase____hla_108;

_0042__switch_otherwise____hla_114:
mov( 0, eax );
jmp _0043__switch_endcase____hla_108;

_0047__switch_doCase____hla_74:
cmp( 1, eax );
jb _0042__switch_otherwise____hla_114;
cmp( 3, eax );
ja _0042__switch_otherwise____hla_114;
jmp( (type dword _0044__switch_jmptbl____hla_119[eax*4-4]) );

_0043__switch_endcase____hla_108:

Note that the HLA language includes a switch statement as part of the language. This macro serves as an example of how to create HLL-like statements using HLA macros, it's not something that would actually be used in an HLA program.

The HLA Compile-Time Language

The HLA macro system is actually a subset of a larger feature known as the HLA Compile-Time Language (CTL). The HLA CTL is an interpreted language that is available in an HLA program source file. An interpreter executes HLA CTL statements during the compilation of an HLA source file (hence the name "compile-time language").

The HLA CTL includes many control statements such as #IF, #WHILE, #FOR, #PRINT, an assignment statement (?) and so on. One can also create compile-time variables and constants (including structured data types such as records and unions). The HLA CTL also provides hundreds of built-in functions (including a very rich set of string and pattern-matching functions). The HLA CTL allows programmers to create CTL "programs" that scan and parse strings, allowing those programmers to create "mini-languages" or Domain Specific Embedded Languages (DSEL, see Domain-specific language). The stdout.put macro appearing earlier is an example of such a DSEL. The put macro (in the stdout namespace, hence the name stdout.put) parses its macro parameter list and emits the code that will print its operands.

Here is part of the HLA CTL macro implementation of the put macro that demonstrates the use of the HLA CTL:

/////////////////////////////////////////////////////////////////////////
//
// Support macro for xxxxx.put:
//
// Generic put macro-
//
// For stdout.out, stderr.put, fileio.put, str.put, etc...
//
// Note: the following are made global rather than local in the macro
// in order to (vastly) speed up processing of the put macros.

val
_pType_:string;
_arg_:string;
_width_:string;
_decpts_:string;
_parmArray_:string;
_id_:string;
_idLen_:string;
_fieldCnt_:string;
_valid_:string;
_func_:string;
_sizeParms_:string;
_realsize_:string;
_prefix_:string;
_typename_:string;


#macro put( _package_, _dest_, _parameter_[] );


// The following loop repeats once for each PUT parameter
// we process.

// The following stmt frees up any storage
// currently in use by parmArray.

?@global:hla._parmArray_:uns32 := 0;

// Get the current parameter into parmArray.
// Parameter takes the form:
//
// value_to_print : Field_Width : Fractional_Width
//
// the "Field_Width" and "Fractional_Width" components
// are optional (or may not be allowed for certain types).
//
// The following call to @tokenize puts "value_to_print"
// into parmArray[0]. If present, it puts the "Field_Width"
// and "Fractional_Width" values into parmArray[1] and
// parmArray[2], respectively.

?@global:hla._parmArray_ :=
@tokenize
(
_parameter_[0],
0,
{':'},
{
'"',
',
'[',
']',
'(',
')',
'{',
'}'
}
);


// If this parameter begins with an identifier,
// there are some problems to deal with.
// The symbol table functions (e.g., @ptype) don't
// allow address expression components after the
// symbol name. Named constants, however, do allow
// such entities. The following code determines
// (1) is this a symbol? (2) if it is a symbol, is
// it a constant?
//
// For non-constant symbols, we need to strip any
// trailing non-symbol characters from the string
// (e.g., "[0]" ).

?@global:hla._arg_ := @trim( @global:hla._parmArray_[ 0 ], 0 );
#if( char( @global:hla._arg_ ) in @global:hla._idchars_ )

// If this parameter begins with an id character,
// then strip away any non-ID symbols from the
// end of the string. Then determine if we've
// got a constant or some other class (e.g.,
// variable or procedure). If not a constant,
// keep only the name. If a constant, we need
// to keep all trailing characters as well.

?@global:hla._id_ := @global:hla._arg_;
?@global:hla._idLen_ :=
@strspan( @global:hla._arg_, 0, @global:hla._idchars_ );

#if( @global:hla._idLen_ > 0 )

?@global:hla._id_ :=
@substr
(
@global:hla._arg_,
0,
@global:hla._idLen_
);

#endif
#if
(
@class( @global:hla._id_ ) = @global:hla.cConstant
| @class( @global:hla._id_ ) = @global:hla.cValue
)

?@global:hla._id_ := @global:hla._arg_;

#endif
#else

// If it's not an ID, we need to keep everything.

?@global:hla._id_ := @global:hla._arg_;

#endif

// Determine the type of this parameter so we can
// call the appropriate routine to print it.

?@global:hla._prefix_ :string := @string:_package_ + ".";
?@global:hla._pType_ := @pType( @text( @global:hla._id_ ));
#if( @pType( @text( @global:hla._id_ )) <> @global:hla.ptPointer )

?@global:hla._pType_ := @basepType( @text( @global:hla._id_ ));

#endif

// Assume the result is valid.

?@global:hla._valid_ := true;

// Okay, determine if the caller supplied a field width
// value with this parameter.


?@global:hla._fieldCnt_ := @elements( @global:hla._parmArray_ );
?@global:hla._width_ := "-1"; // Default width value.
?@global:hla._decpts_ := "-1"; // Default fractional value.
?@global:hla._sizeParms_ := ""; // Assume no size parameter(s)
?@global:hla._typename_ := @global:hla.ptypeStrs[ @global:hla._pType_ ];
?@global:hla._valid_ := true;

#if( @global:hla._fieldCnt_ = 1 )

#if( !_package_.validPutType[ @global:hla._pType_ ] )

?@global:hla._valid_ := false;
#error( "This output type is not supported" )

#else

?@global:hla._func_ :string :=
@global:hla._prefix_ +
_package_.putFunc[ @global:hla._pType_ ];

#endif


#elseif( @global:hla._fieldCnt_ = 2 )

#if( !_package_.validPutSizeType[ @global:hla._pType_ ] )

?@global:hla._valid_ := false;
#error( "This type does not support an output width" )

#else

?@global:hla._func_ :string :=
@global:hla._prefix_ +
_package_.putSizeFunc[ @global:hla._pType_ ];

?@global:hla._width_ := @trim( @global:hla._parmArray_[ 1 ], 0 );
?@global:hla._sizeParms_ := "," + @global:hla._width_ + ",' '";

#endif

#elseif( @global:hla._fieldCnt_ = 3 )

// Determine if the user supplied a fractional width
// value with this parameter.


#if( !_package_.validPutSize2Type[ @global:hla._pType_ ] )

?@global:hla._valid_ := false;
#error
(
"This type does not support a second (decpts) output width"
)

#else

?@global:hla._func_ :string :=
@global:hla._prefix_ +
_package_.putSize2Func[ @global:hla._pType_ ];

?@global:hla._width_ := @trim( @global:hla._parmArray_[ 1 ], 0 );
?@global:hla._decpts_ := @trim( @global:hla._parmArray_[ 2 ], 0 );
?@global:hla._sizeParms_:string :=
","
+ @global:hla._width_
+ ","
+ @global:hla._decpts_
+ ", ' '";

#endif

#else

?@global:hla._valid_ := false;
#error
(
"<<" + _parameter_[0] + ">>" +
" has too many width fields"
);

#endif

// Based on the type, call the appropriate library
// routine to print this value.

?@global:hla._valid_ :=
@global:hla._valid_
& (
(
@global:hla._pType_ >= @global:hla.ptBoolean
& @global:hla._pType_ <= @global:hla.ptPointer
)
| @global:hla._pType_ = @global:hla.ptClass
);


#if( @global:hla._valid_ )

#if( @global:hla._pType_ = @global:hla.ptClass )

#if( @defined( @text(@global:hla._arg_ + ".a_toString")))

push( esi );
push( edi );
push( eax );

@text( @global:hla._arg_ + ".a_toString;" )
@text
(
@global:hla._prefix_ +
_package_.putFunc[ @global:hla.ptString ]
+ "("
#if( _dest_ <> "" )
+ _dest_ +","
#endif
"(type string eax)" +
");"
)
str.free( eax );
pop( eax );
pop( edi );
pop( esi );

#else

#error( "Class does not have an 'a_toString' method/procedure" );

#endif

#elseif
(
@isconst( @text( @global:hla._arg_ ))
& @global:hla._typename_ = "string"
& @global:hla._arg_ = "#13 #10"
& @defined( @text( @global:hla._prefix_ + "newln" ))
)
@text( @global:hla._prefix_ + "newln" )
(
#if( _dest_ <> "" )
@text( _dest_ )
#endif
);

#elseif( @isconst( @text( @global:hla._arg_ )) )

@text( @global:hla._func_ )
(
#if( _dest_ <> "" )
@text( _dest_ ),
#endif
@text( @global:hla._arg_ )
@text( @global:hla._sizeParms_ )
);

#else
@text
(
@global:hla._func_
+ "("
#if( _dest_ <> "" )
+ _dest_ +","
#endif
"(type " + @global:hla._typename_ +
" " + @global:hla._arg_ +
" )" +
@global:hla._sizeParms_ +
");"
);


#endif


#else

#error
(
@string:_package_ +
".put: Unknown data type "
nl
"(" +
_parameter_[0] +
" is type """ +
@typename( @text( @global:hla._id_ )) +
""")"
);

#endif

#endmacro

Standard library

The HLA Standard Library is an extensive set of prewritten routines and macros (like the stdout.put macro described above) that make life easier for programmers, saving them from reinventing the wheel every time they write a new application. Perhaps just as important, the HLA Standard Library allows programmers to write portable applications that run under Windows or Linux with nothing more than a recompile of the source code. Like the C standard library, the HLA Standard Library allows abstracting away low-level OS calls, so the same set of OS APIs can serve for all operating systems that HLA supports. Of course, an assembly language allows making any needed OS calls, but as long as programmers use the HLA Standard Library API set, writing OS-portable programs is easy.

The HLA Standard Library provides thousands of functions, procedures, and macros. As of mid-2010 (and the list changes over time), HLA v2.12's Standard Library including functions in these categories:
  • Command-line argument processing
  • Array (dynamic) declaration and manipulation
  • Bit manipulation
  • Blob (binary large object) manipulation
  • Character manipulation
  • Conversions
  • Character set manipulation
  • Date and time functions
  • Object-oriented file I/O
  • Standard file I/O
  • File system manipulation functions (e.g., delete, rename, and change directory)
  • HLA-related declarations and functions
  • The HLA Object Windows Library (Object-Oriented Framework for Win32 programming)
  • Linked list manipulation
  • Mathematical functions
  • Memory allocation and management
  • FreeBSD-specific APIs
  • Linux-specific APIs
  • Mac OS X-specific APIs
  • Win32-specific APIs
  • Text console functions
  • Coroutine support
  • Environment variable support
  • Exception handling support
  • Memory-mapped file support
  • Sockets and client/server object support
  • Thread and synchronization support
  • Timer functions
  • Pattern matching (regular expressions and context-free languages) support
  • Random number generators
  • Remote Procedure Call support
  • Standard error output functions
  • Standard output functions
  • Standard input functions
  • String functions
  • Table (associative) support
  • Zero-terminated string functions

Design of the HLA system

The HLA v2.x language system is a command-line driven tool that consists of several components, including a "shell" program (e.g., hla.exe under Windows), the HLA language compiler (e.g., hlaparse.exe), a low-level translator (e.g., the HLABE, or HLA Back Engine), a linker (link.exe under Windows, ld under Linux), and other tools such as a resource compiler for Windows. Versions before 2.0 relied on an external assembler back end; versions 2.x and later of HLA use the built-in HLABE as the back-end object code formatter.

The HLA "shell" application processes command line parameters and routes appropriate files to each of the programs that make up the HLA system. It accepts as input ".hla" files (HLA source files), ".asm" files (source files for MASM, TASM, FASM, NASM, or Gas assemblers), ".obj" files for input to the linker, and ".rc" files (for use by a resource compiler.)

Source Code Translation

Originally, the HLA v1.x tool compiled its source code into an intermediate source file that a "back-end" assembler such as MASM, TASM
Tasm
TASM can refer to:*Turbo Assembler, Borland's x86 assembler* Turbo Assembler, Omikron's Commodore 64-based 6502 assembler*Table Assembler, a table driven cross-assembler for small microprocessors.*Tomahawk Anti-Ship Missile...

, FASM
FASM
FASM in computing is an assembler. It supports programming in Intel-style assembly language on the IA-32 and x86-64 computer architectures. It claims high speed, size optimizations, operating system portability, and macro abilities. It is a low-level assembler and intentionally uses very few...

, NASM, or Gas
GNU Assembler
The GNU Assembler, commonly known as GAS , is the assembler used by the GNU Project. It is the default back-end of GCC. It is used to assemble the GNU operating system and the Linux kernel, and various other software. It is a part of the GNU Binutils package.GAS' executable is named after as, a...

 would translate into the low-level object code file. As of HLA v2.0, HLA included its own "HLA Back Engine" (HLABE) that provided the low-level object code translation. However, via various command-line parameters, HLA v2.x still has the ability to translate an HLA source file into a source file that is compatible with one of these other assemblers.

Here is a sample HLA program (t.hla):

unit t;
?@noframe := true;

static
d:dword;
w:word;
b:byte;

procedure demoConversion;
var
ld :dword;
lw :word;
lb :byte;

begin demoConversion;

mov( d, eax );
add( ld, eax );

or( w, bx );
and( lw, cx );

cmp( al, b );
test( lb, dl );

mov( 0, edx );
mov( 10, d );
mov( 20, w );
mov( 30, b );
mov( 1, lb );
add( 2, lw );
or( 3, ld );

end demoConversion;

end t;

Compiling this file with the command line "hla -sourcemode -hla -s t.hla", makes HLA emit an HLA-pseudo-compatible source file. This is mainly useful for looking at macro expansions, HLL-like statement code generation, and so on. Here is the output file that HLA v2.12 emits for t.hla (t.asm):

// Assembly code emitted by HLA compiler
// Version 2.12 build 3968 (prototype)
// HLA compiler written by Randall Hyde
// pseudo-HLA compatible output
// (for human consumption only, cannot be recompiled)
program _HLAMain;

static
d__hla_1 :dword;
w__hla_2 :word;
b__hla_3 :byte;

readonly

procedure demoConversion__hla_4;
begin demoConversion__hla_4;

mov( d__hla_1, eax );
add( [ebp-12], eax );
or( w__hla_2, bx );
and( [ebp-14], cx );
cmp( b__hla_3, al );
test( (type byte [ebp-15]), dl );
mov( 0, edx );
mov( 10, d__hla_1 );
mov( 20, w__hla_2 );
mov( 30, b__hla_3 );
mov( 1, (type byte [ebp-15]) );
add( 2, (type word [ebp-14]) );
or( 3, (type dword [ebp-12]) );
end demoConversion__hla_4;
Compiling t.hla with the command line "hla -sourcemode -s -masm t.hla", makes HLA v2.12 emit this MASM-compatible source file:

; Assembly code emitted by HLA compiler
; Version 2.12 build 3968 (prototype)
; HLA compiler written by Randall Hyde
; MASM compatible output

if @Version lt 612
.586p
else
.686p
.mmx
.xmm
endif
.model flat, syscall
option noscoped
option casemap:none
offset32 equ

assume fs:nothing
ExceptionPtr__hla_ equ <(dword ptr fs:[0])>
.code
externdef _HLA_GET_EXCEPTIONPTREBP:near32
externdef _HLA_PUSH_EXCEPTIONPTR:near32
externdef _HLA_SET_EXCEPTIONPTR:near32

.code

demoConversion__hla_4 proc near32
mov eax, d__hla_1
add eax, [ebp-12]
or bx, w__hla_2
and cx, [ebp-14]
cmp al, b__hla_3
test dl, byte ptr [ebp-15]
mov edx, 0
mov d__hla_1, 10
mov w__hla_2, 20
mov b__hla_3, 30
mov byte ptr [ebp-15], 1
add word ptr [ebp-14], 2
or dword ptr [ebp-12], 3
demoConversion__hla_4 endp
.code

.data

externdef ExceptionPtr__hla_:dword
d__hla_1 dd 1 dup (?)
w__hla_2 dw 1 dup (?)
b__hla_3 db 1 dup (?)

end

Compiling t.hla with the command line "hla -sourcemode -s -fasm t.hla", makes HLA v2.12 emit this FASM
FASM
FASM in computing is an assembler. It supports programming in Intel-style assembly language on the IA-32 and x86-64 computer architectures. It claims high speed, size optimizations, operating system portability, and macro abilities. It is a low-level assembler and intentionally uses very few...

-compatible source file:

; Assembly code emitted by HLA compiler
; Version 2.12 build 3968 (prototype)
; HLA compiler written by Randall Hyde
; FASM compatible output

format MS COFF

ExceptionPtr__hla_ equ fs:0
offset32 equ
ptr equ

macro global [symbol]
{
local isextrn
if defined symbol & ~ defined isextrn
public symbol
else if used symbol
extrn symbol
isextrn = 1
end if
}

macro global2 [symbol,type]
{
local isextrn
if defined symbol & ~ defined isextrn
public symbol
else if used symbol
extrn symbol:type
isextrn = 1
end if
}
section '.text' code readable executable align 16
global _HLA_GET_EXCEPTIONPTREBP,near32
global _HLA_PUSH_EXCEPTIONPTR,near32
global _HLA_SET_EXCEPTIONPTR,near32
section '.text' code readable executable align 16
; procedure demoConversion__hla_4

demoConversion__hla_4:
mov eax, [d__hla_1]
add eax, [ebp-12]
or bx, [w__hla_2]
and cx, [ebp-14]
cmp al, [b__hla_3]
test dl, byte [ebp-15]
mov edx, 0
mov [d__hla_1], 10
mov [w__hla_2], 20
mov [b__hla_3], 30
mov byte [ebp-15], 1
add word [ebp-14], 2
or dword [ebp-12], 3
;demoConversion__hla_4 endp

section '.text' code readable executable align 16

section '.data' data readable writeable align 16

global2 ExceptionPtr__hla_,dword
d__hla_1 rd 1
w__hla_2 rw 1
b__hla_3 rb 1
dd 0ffffffffh
Compiling t.hla with the command line "hla -sourcemode -s -nasm t.hla", makes HLA v2.12 emit this NASM-compatible source file:

; Assembly code emitted by HLA compiler
; Version 2.12 build 3968 (prototype)
; HLA compiler written by Randall Hyde
; NASM compatible output

bits 32

%define ExceptionPtr__hla_ [dword fs:0]
section .text code align=16
extern _HLA_GET_EXCEPTIONPTREBP
extern _HLA_PUSH_EXCEPTIONPTR
extern _HLA_SET_EXCEPTIONPTR
section .text
; procedure demoConversion__hla_4

demoConversion__hla_4:
mov eax, [d__hla_1]
add eax, dword [ebp-12]
or bx, word [w__hla_2]
and cx, word [ebp-14]
cmp al, byte [b__hla_3]
test dl, byte [ebp-15]
mov edx, 0
mov dword [d__hla_1], 10
mov word [w__hla_2], 20
mov byte [b__hla_3], 30
mov byte [ebp-15], 1
add word [ebp-14], 2
or dword [ebp-12], 3
;demoConversion__hla_4 endp
section .text
section .data data align=16

extern ExceptionPtr__hla_
d__hla_1 times 4 db 0
w__hla_2 times 2 db 0
b__hla_3 times 1 db 0
Compiling t.hla with the command line "hla -sourcemode -s -gas t.hla", makes HLA v2.12 emit this GAS
GNU Assembler
The GNU Assembler, commonly known as GAS , is the assembler used by the GNU Project. It is the default back-end of GCC. It is used to assemble the GNU operating system and the Linux kernel, and various other software. It is a part of the GNU Binutils package.GAS' executable is named after as, a...

-compatible source file:

// Assembly code emitted by HLA compiler
// Version 2.12 build 3968 (prototype)
// HLA compiler written by Randall Hyde
// GAS compatible output
.text
.extern _HLA_GET_EXCEPTIONPTREBP
.extern _HLA_PUSH_EXCEPTIONPTR
.extern _HLA_SET_EXCEPTIONPTR
.text

demoConversion__hla_4:
mov d__hla_1, %eax
addl -12(%ebp), %eax
orw w__hla_2, %bx
andw -14(%ebp), %cx
cmpb b__hla_3, %al
testb -15(%ebp), %dl
mov $0, %edx
movl $10, d__hla_1
movw $20, w__hla_2
movb $30, b__hla_3
movb $1, -15(%ebp)
addw $2, -14(%ebp)
orl $3, -12(%ebp)
.text
.data

.extern ExceptionPtr__hla_
d__hla_1 :.space 4
w__hla_2 :.space 2
b__hla_3 :.space 1

The HLA Back Engine

The HLA Back Engine (HLABE) is a compiler back end that translates an internal intermediate language into low-level PE/COFF (see COFF
COFF
The Common Object File Format is a specification of a format for executable, object code, and shared library computer files used on Unix systems...

 and Portable Executable
Portable Executable
The Portable Executable format is a file format for executables, object code and DLLs, used in 32-bit and 64-bit versions of Windows operating systems. The term "portable" refers to the format's versatility in numerous environments of operating system software architecture...

), ELF
ELF
ELF may be:Science* Electron localization function, a concept in quantum mechanics* Extremely low frequency, the band of radio frequencies from 3 to 30 HertzLinguistics...

, or Mach-O
Mach-O
Mach-O, short for Mach object file format, is a file format for executables, object code, shared libraries, dynamically-loaded code, and core dumps. A replacement for the a.out format, Mach-O offered more extensibility and faster access to information in the symbol table.Mach-O was once used by...

 object code. An HLABE "program" mostly consists of data (byte) emission statements, 32-bit relocatable address statements, x86 control-transfer instructions, and various directives. In addition to translating the byte and relocatable address statements into the low-level object code format, HLABE also handles branch-displacement optimization (picking the shortest possible form of a branch instruction).

Although the HLABE is incorporated into the HLA v2.x compiler, it is actually a separate product. It is public domain and open source (hosted on Source Forge).

An HLABE "source file" is an ASCII text file that contains these statements:

/////////////////////////////////////////////////////////////////////////////////
//
// Scan an HLABE (HLA back engine) assembly file. Such files contain the
// following statements:
//
// .a Alignment
// .b Byte data
// .c Code section
// .d Dword data (includes relocatable)
// .e l1, l2 Equate
// .f l End of function
// .l Lword data
// .o sOurce file name
// .p l Public symbol
// .q Qword data
// .r Reserve Storage
// .s Static/Data section
// .t TByte data
// .ub , Duplicated byte data
// .uw , Duplicated word data
// .ud , Duplicated dword data
// .v BSS section
// .w Word data
// .x lbl External symbol
// .y READONLY/CONST section
// .z End of source
//
// :lbl Defines label at current program counter location.
//
// Except for label (which is terminated by a newline character), there
// is always at least one space between the statement and any operands.

// Numbers beginning with '$' are hexadecimal values, decimal if no '$' prefix.
// Decimal numbers may contain chars 0..9 and '_'. Hexadecimal numbers may
// also contain 'a'..'f' and 'A'..'F'.
//
// Labels always begin with alpha or '_' character and may contain
// alphanumeric, '_', '$', '?', and "@" characters after the first char.
// In general, labels can be any length, but the object file format or specific
// linkers may enforce their own limits. As a general rule, symbols should be
// unique within the first 32 characters.
//
//
// , , , and are simplified (absolute) arithmetic expressions
// defined as follows:
//
// $ -- Hexadecimal value
// -- Decimal (base 10) value
// + -- Sum of two subexpressions
// - -- Difference of two subexpressions
//
// Evaluation of subexpressions is strictly left-to-right with no precedence.
//
// Relocatable expressions are a vector with a label component and an
// absolute expression component. This is generally specified as .
// The syntax for a relative expression is one of the following:
//
// -- An absolute expression (which has a NULL relocatable value)
// id -- A relocatable identifier.
// id + -- An identifier (relocatable) followed by an abs expr.
// id - -- A relocatable identifier followed by an abs expr.
//
//
//
//
// Blank lines are permissible in the source file.
// Comments begin with a ';' and consume everything to the end of the line.
//
//
// .a
//
// The alignment statement will align the next byte emitted in the current
// section to some boundary that is a power of two. The operand is a single
// expression that evaluates to a small integer value. The alignment value
// must always be a power of two. It should also be in the range 1..16.
//
// Alignment statement will fill with zeros in static/data section, with no-
// operations in a code section (this could be "multi-byte NOPs", not
// necessarily a sequence of individual NOP instructions),
// and will do a reserve storage operation in the BSS section.
//
// .c, .s, .v, .y
//
// These four statements begin (or continue) a code (.c), readonly/const (.y),
// data/static (.s), BSS (.v) section in the program. Note that multiple
// instances of each section statement may appear within a single source file.
// When multiple instances of a given section in the source file exist,
// HLABE will combine the different instances into a single section.
//
// Within a section, order of data/code is strictly maintained, but if multiple
// section declarations for the same section appear in a source file, there is
// no guarantee of the order the subsections will be combined. If strict ordering
// is required, the caller should combine the sections and emit them as a single
// section when creating the HLABE source file.
//
// No explicit alignment is assumed when a section begins. Calling code
// must explicitly issue a ".a" statement if alignment is desired or required.
//
// .b, .w, .d, .q, .t, .l
//
// These directives emit bytes, word, doublewords, quadwords, tbytes, or
// lbytes (128-bit values) to the current section (code or data/static, these
// directives cannot appear in a BSS section). These directives have the
// following syntax and semantics:
//
// .b
// is a list of one or more byte items. A byte item is either an
// expression that evaluates to a value in the range 0..$ff
// (or -128..+127) or a sequence of characters surrounded by
// quotes. If more than one byte item appears in a , the
// byte items are comma-separated. Note that quote
// characters never appear within a string (they must be converted
// to '$22' byte items). Also, only printable ASCII characters
// may appear within a quoted string (characters whose codes are
// in the range $20..$7e). All other characters must be converted
// to numeric byte item entries.
//
// .w
// A is a list of one or more word items. Word items are
// expressions that evaluate to 16-bit (or smaller) values. Multiple
// items in a are comma-separated.
//
// .d
// A is a list of one or more dword items. Dword items are
// relocatable or absolute expressions that evaluate to 32-bit (or smaller)
// values. Multiple items in a are comma-separated. Pointer
// constants (relocatable objects) are also valid dword items. A pointer
// constant is one of the following:
//
// lbl
// lbl+
// lbl-
// (lbl+)
// (lbl-)
//
// where "lbl" is a relocatable statement label and
// is any valid dword expression. HLABE always emits a
// relocatable offset value for these items ( ).
// Note that all dword constants are always a tuple. If
// the dword constant is absolute, then the relocatable component ()
// is set to the NULL pointer.
//
// .q
// A is a list of one or more qword items. Qword items are
// numeric operands that evaluate to 64-bit (or smaller) values. Multiple
// items in a are comma-separated.
//
// .t
// A is a list of one or more tbyte items. TByte items are
// numeric operands that evaluate to 80-bit (or smaller) values. Multiple
// items in a are comma-separated.
//
// .l
// A is a list of one or more lword items. Lword items are
// numeric operands that evaluate to 128-bit (or smaller) values. Multiple
// items in an are comma-separated.
//
// .ub ,
// is a duplication count. is a data value, which should be
// a byte. This directive, which is valid only in the
// code and static/data sections (illegal in the BSS section) is used to
// fill a block of memory with a specific value. The values must be
// absolute.
//
// .uw ,
// is a duplication count. is a data value, which should be
// a word. This directive, which is valid only in the
// code and static/data sections (illegal in the BSS section) is used to
// fill a block of memory with a specific value. The values must be
// absolute.
//
// .ud ,
// is a duplication count. is a data value, which should be
// a relocatable dword value. This directive, which is valid only in the
// code and static/data sections (illegal in the BSS section) is used to
// fill a block of memory with a specific value. The values can be
// absolute or relative (absolute dword values are values with
// the relocatable field set to NULL).
//
// .r
// Reserves bytes of data at the current program counter location in
// the current section. If a code section, the reserved storage is filled
// with NOP-style instructions, if a data/static section, the reserved
// storage is filled with zeros. This statement is valid in, and is
// mainly intended for use in, a BSS section.
//
//
// .e lbl,
//
// Equates simply do a textual substitution of the operand for the label
// operand. I.e., everywhere "lbl" appears (in the example above), HLABE
// substitutes the remaining text on the line (up to the end of the line or up
// to a comment beginning with a ";") for the symbol. After substitution, HLABE
// continues processing the source line as though the data originally
// appeared in place of the symbol. Note that if the string contains
// other equate symbols, they will be processed as well. There is no check
// for infinite loops in the text substitution process. It is the responsibility
// of whomever created the equate(s) to ensure that a recursive definition
// does not exist. Note that only a single line of text substitution is
// possible (i.e., this is not a generalized macro facility).
//
//
// .f lbl
//
// Marks the end of a function. When generating ELF code, this will
// change the symbol type (of the corresponding label) in the symbol
// table and set the length of the function.
//
//
// In addition to the above statements, an HLABE program may also contain
// jmp, call, and any of the following conditional jump instructions:
// ja, jae, jb, jbe, jc, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe,
// jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo,
// js, jz, jcxz, jecxz, loop, loope, loopne, loopz, or loopnz.
//
// Any number of spaces and/or tabs may precede these statements. Any number
// of spaces and/or tabs may appear between the instruction mnemonic and
// the single label operand. After the label, at least one space will appear.
// After each jump, call, or conditional jump instruction, there will always
// be a comment of the form:
//
// ";filename, line#"
// ";filename, line# ;filename line#; ..."
//
// This is a list of filenames and line numbers in the original source
// file where the statement that emitted this code can be found. If the
// statement was emitted from a macro or include file, there will be more than
// one filename/line# pair (with the last entry being the file/line# of the
// actual statement within the macro or include file). The HLABE compiler
// should parse this information and display it if there is an error
// compiling the statement (e.g., branch out of range). Line numbers are always
// unsigned decimal integers.
//
// Note that call and jmp statements only appear in a source file for
// relative jumps and calls. Those that do indirect jumps or calls must be
// compiled directly to machine code by the caller.
//
// All reserved words use lower case characters only. Labels, however, may
// contain upper and lower case characters (and are case sensitive).

Consider this HLA source file (t.hla):

unit t;
?@noframe := true;

static
d:dword;
w:word;
b:byte;

procedure HLABE;
var
ld :dword;
lw :word;
lb :byte;

begin HLABE;

lbl:
mov( d, eax );
add( ld, eax );

or( w, bx );
and( lw, cx );

cmp( al, b );
test( lb, dl );
jmp lbl;
jne lbl;
call lbl;


end HLABE;

end t;

Compiling with the command line "hla -sourcemode -hlabe -s t.hla" emits this HLABE intermediate file:

; Assembly code emitted by HLA compiler
; Version 2.12 build 3968 (prototype)
; HLA compiler written by Randall Hyde
; HLA backend compatible output
.c

.x _HLA_GET_EXCEPTIONPTREBP
.x _HLA_PUSH_EXCEPTIONPTR
.x _HLA_SET_EXCEPTIONPTR
.c

; procedure HLABE__hla_4

:HLABE__hla_4

:lbl__hla_5
.b $a1
.d d__hla_1+0
.b $3
.b $45
.b $f4
.b $66
.b $b
.b $1d
.d w__hla_2
.b $66
.b $23
.b $4d
.b $f2
.b $3a
.b $5
.d b__hla_3
.b $84
.b $55
.b $f1
jmp lbl__hla_5 ;t.hla,27
jne lbl__hla_5 ;t.hla,28
call lbl__hla_5 ;t.hla,29
.f HLABE__hla_4
.y
.s
.x ExceptionPtr__hla_
:d__hla_1
.r $4
:w__hla_2
.r $2
:b__hla_3
.r $1

.z

Uses

HLA has been used to write HLA Adventure, a text adventure game in the public domain
Public domain
Works are in the public domain if the intellectual property rights have expired, if the intellectual property rights are forfeited, or if they are not covered by intellectual property rights at all...

.
HLA has also been used to develop the real-time digital control system for TRIGA
TRIGA
TRIGA is a class of small nuclear reactor designed and manufactured by General Atomics. The design team for TRIGA was led by the physicist Freeman Dyson.TRIGA is the acronym of Training, Research, Isotopes, General Atomics.-Design:...

 Reactors (General Atomics)

Further reading

  • Paul Panks (June 29, 2005), HLA: The High Level Assembly Programming Language, Linux Journal
    Linux Journal
    Linux Journal is a monthly technology magazine published by Belltown Media, Inc. of Houston, Texas. The magazine focuses specifically on Linux, allowing the content to be a highly specialized source of information for open source enthusiasts.-History:...

The source of this article is wikipedia, the free encyclopedia.  The text of this article is licensed under the GFDL.
 
x
OK