The HLA Standard Library

Long Table of Contents

 

1 80x86 Constants (x86.hhf) 1
2 The Arrays Module (arrays.hhf) 2
2.1 Declarations, Allocation, and Predicates 2

#macro array.dArray( type, dimensions) 2

#macro array.daAlloc( dynamicArrayName, <<list of dimension bounds>> ); 2

#macro array.daFree( dynamicArrayName ); 3

#macro array.IsItVar( objectName ) 3

#macro array.IsItDynamic( arrayName ) 3

2.2 Accessing Dynamic Array Objects 4

#macro array.index( reg32, arrayName, <<list of indicies>> ); 4

iterator array.element( arrayName ); 4

2.3 Array Operations/Functions 5

#macro array.cpy( srcArray, destArray ); 5

#macro array.transpose( srcArray, destArray, optionalDimension); 7

3 Bit Manipulation (bits.hhf) 9
3.1 Bit Counting Functions 9

bits.cnt( b:dword ); returns( "EAX" ); 9

3.2 Bit Movement, Insertion, and Extraction Functions 9

bits.reverse32( b:dword ); returns( "EAX" ); 9

bits.merge32( even:dword; odd:dword ); returns( "EDX:EAX" ); 10

bits.nibbles32( d:dword ); returns( "EDX:EAX" ); 10

procedure bits.extract( var d:dword ); 11

bits.distribute( source:dword; mask:dword; dest:dword ); 11

bits.coalese( source:dword; mask:dword ); 12

4 Character Classification and Utilities Module (chars.hhf) 14
4.1 Conversion Functions 14

chars.toUpper( c:byte ); returns( "AL" ); 14

chars.toLower( c:byte ); returns( "AL" ); 14

4.2 Predicates (Tests) 14

chars.isAlpha( c:byte ); returns( "EAX" ); 14

chars.isUpper( c:byte ); returns( "EAX" ); 14

chars.isLower( c:byte ); returns( "EAX" ); 14

chars.isAlphaNum( c:byte ); returns( "EAX" ); 14

chars.isDigit( c:byte ); returns( "EAX" ); 14

chars.isXDigit( c:byte ); returns( "EAX" ); 14

chars.isGraphic( c:byte ); returns( "EAX" ); 14

chars.isSpace( c:byte ); returns( "EAX" ); 15

chars.isASCII( c:byte ); returns( "EAX" ); 15

chars.isCtrl( c:byte ); returns( "EAX" ); 15

5 Character Sets (cset.hhf) 16
5.1 Predicates (tests) 16

cs.IsEmpty( src: cset ); returns( "AL" ); 16

cs.member( c:char; theSet:cset ); returns( "AL" ); 16

cs.subset( src1:cset; src2:cset ); returns( "AL" ); 16

5.2 Character Set Construction and Manipulation 17

cs.empty( var dest:cset ); 17

cs.cpy( src:cset; var dest:cset ); 17

procedure cs.charToCset( c:char; var dest:cset ); 17

procedure cs.rangeChar( first:char; last:char; var dest:cset ); 17

procedure cs.strToCset( s:string; var dest:cset ); 17

procedure cs.strToCset2( s:string; offs:uns32; var dest:cset ); 17

procedure cs.extract( var dest:cset ); returns( "EAX" ); 17

5.3 Set Operations 18

cs.setunion( src:cset; var dest:cset ); 18

cs.intersection( src:cset; var dest:cset ); 18

cs.difference( src:cset; var dest:cset ); 18

cs.complement( src:cset; var dest:cset ); 18

procedure cs.unionChar( c:char; var dest:cset ); 18

procedure cs.removeChar( c:char; var dest:cset ); 18

procedure cs.unionStr( s:string; var dest:cset ); 18

procedure cs.unionStr2( s:string; offs:uns32; offs:uns32; var dest:cset ); 18

procedure cs.removeStr( s:string; var dest:cset ); 18

procedure cs.removeStr2( s:string; offs:uns32; var dest:cset ); 18

6 Classification and Constants (hla.hhf) 19
6.1 Classification Macros 19

hla.IsUns( identifier ) 19

hla.IsInt( identifier ) 19

hla.IsHex( identifier ) 19

hla.IsNumber( identifier ) 19

hla.IsReal( identifier ) 19

hla.IsNumeric( identifier ) 19

hla.IsOrdinal( identifier ) 19

6.2 @class Constants 20
6.3 HLA pType Constants 20
6.4 @pClass Return Values 22
6.5 @section Return Results 23
6.6 hla.genLabel Macro 24
7 Command Line Arguments (args.hhf) 25

arg.cmdLn; returns( "eax" ); 25

arg.c; returns( "eax" ); 25

arg.v( index ); returns( "eax" ); 25

arg.delete( index:uns32 ) 25

arg.globalOptions( options:cset) 25

arg.localOptions( index:uns32; options:cset ); 25

8 The Console Module (console.hhf) 27

procedure console.info( var csbi:win.CONSOLE_SCREEN_BUFFER_INFO ); 27

8.1 Cursor Positioning Functions 28

procedure console.getX; returns( "eax" ); 28

procedure console.getY; returns( "eax" ); 28

procedure console.gotoxy( y:word; x:word ); 28

8.2 Console Clearing Functions 28

procedure console.cls; 28

procedure console.clrToEOLN; 28

procedure console.clrToEOScrn; 28

8.3 Character Insertion/Removal 28

procedure console.insertChars( n:word ); 28

procedure console.insertLn; 28

procedure console.insertLines( n:word ); 29

procedure console.deleteChars( n:word ); 29

procedure console.deleteLn; 29

procedure console.deleteLines( n:word ); 29

8.4 Console Rectangular Operations 29

procedure console.fillRect 29

procedure console.fillRectAttr 29

procedure console.getRect 29

procedure console.a_getRect 30

procedure console.putRect 30

procedure console.scrollUpRect 30

procedure console.scrollDnRect 30

8.5 Console Output Options 31

procedure console.setOutputAttr( Attribute:word ); 31

8.6 Console Window Titles 31

procedure console.setTitle( title:string ); 31

procedure console.getTitle( title:string ); 31

procedure console.a_getTitle; returns( "eax" ); 31

8.7 Console Input Functions 31

procedure console.peekInput( var input:win.INPUT_RECORD ); returns( "eax" ); 31

procedure console.readInput( var input: win.INPUT_RECORD ); 32

procedure console.flushInput; 32

procedure console.getMode; returns( "eax" ); 32

procedure console.setMode( theMode:dword ); 32

procedure console.numButtons; returns( "eax" ); 32

procedure console.gets( y:dword; x:dword; len:dword; s:string ); 32

procedure console.a_gets( y:dword; x:dword; len:dword ); returns( "eax" ); 32

procedure console.getc( y:dword; x:dword ); returns( "al" ); 32

8.8 Console Output Functions 33

procedure console.puts( y:word; x:word; s:string ); 33

procedure console.putsx( y:word; x:word; attr:word; len:word; s:string ); 33

8.9 Console Scrolling Functions 33

procedure console.scrollUp; 33

procedure console.scrollUpN( n:uns16 ); 33

procedure console.scrollUpx( fill:char; attr:word; n:uns16 ); 33

procedure console.scrollDn; 33

procedure console.scrollDnN( n:uns16 ); 33

procedure console.scrollDnx( fill:char; attr:word; n:uns16 ); 33

9 Control Structures (hll.hhf) 34

The switch/case/default/endswitch Statement 34

10 Conversions (conv.hhf) 36
10.1 Conversion Format Control 36

conv.setUnderscores( OnOff: boolean )
conv.getUnderscores; returns( "eax" ); 36

10.2 Hexadecimal Conversions 36

conv.byteToHex( b:byte in al ); 36

10.2.1 Hexadecimal Numeric to String Convesions 36

conv.bToStr ( b:byte; size:dword; fill:char; buffer:string ); 36

10.2.2 Hexadecimal String to Numeric Conversions 37

conv.strTob( s:string; index:dword ) 37

10.3 Integer Conversions 37
10.3.1 Integer Size Calculations 37

conv.i8Size( b:byte in al ) 37

10.3.2 Integer Numeric to String Conversions 38

conv.i8ToBuf( i8:int8 in al ) 38

conv.u8ToBuf( u8: uns8 in al ) 38

10.3.3 Integer String to Numeric Conversions 39

conv.atou( bufPtr: dword in esi ); 39

conv.strTob( s:string; index:dword ) 39

conv.strToi8( s:string; index:dword ) 39

conv.strTou8( s:string; index:dword ) 39

10.4 Floating Point Conversions 40
10.4.1 Floating Point Numeric to String Conversions, Exponential Form 40

conv.e80ToStr 40

conv.e64ToStr 41

conv.e32ToStr 41

10.4.2 Floating Point Numeric to String Conversions, Decimal Form 41

conv.r80ToStr 42

conv.r64ToStr 42

conv.r32ToStr 43

10.4.3 Floating Point String to Numeric Conversions 43

conv.atof( bufptr: dword in esi ); returns( "st0" ); 43

10.5 Zero Terminated String Conversions 43

conv.cStrToStr( var buffer:var; dest:string ) 43

conv.a_cStrToStr( var buffer:var ) 44

10.6 Roman Numeral Conversion 44

conv.roman( Arabic:uns32; rmn:string ) 44

conv.a_roman( Arabic:uns32 ) 44

11 The Coroutines Module (coroutines.hhf) 45

procedure coroutine.create( size:uns32; theProc:procedure ); 45

procedure coroutine.cocall; 45

procedure coret; 46

method coroutine.cofree; 46

static mainPgm:coroutine; external( "?MainPgmCoroutine" ); 46

12 The Date & Time Module (datetime.hhf) 47
12.1 Date Functions 47

date.daterec 47

date.IsLeapYear( y:uns32 ) returns( "al" ); 47

date.validate( m:byte; day:byte; year:word ); 47

date.isValid( m:byte; day:byte; year:word ); 47

date.outputFormat 47

date.setFormat( fmt : OutputFormat ); 47

date.setSeparator( chr:char ); 48

date.toString( m:byte; d:byte; y:word; s:string ); 48

date.a_toString( m:byte; d:byte; y:word ); returns( "eax" ); 48

date.print( m:byte; d:byte; y:word ); 48

date.Julian( m:byte; d:byte; y:word ); returns( "eax" ); 48

date.fromJulian( jd:uns32; var gd:date.daterec ); 48

date.dayNumber( m:byte; d:byte; y:word ); returns( "eax" ); 48

date.daysLeft( m:byte; d:byte; y:word ); returns( "eax" ); 49

date.dayOfWeek( m:byte; d:byte; y:word ); returns( "eax" ); 49

date.daysBetween( m1:byte; d1:byte; y1:word; m2:byte; d2:byte; y2:word ); 49

date.daysBetween( m1:byte; d1:byte; y1:word; dr:date.daterec ); 49

date.daysBetween( dr:date.daterec; m:byte; d:byte; y:word ); 49

date.daysBetween( dr1:date.daterec; dr2:date.daterec ); 49

date.datePlusDays( days:uns32; var dr:date.daterec ); 49

date.datePlusMonths( months:uns32; var dr:date.daterec ); 49

date.today( var dr:date.daterec ); 50

12.2 Time Functions 50

time.timerec 50

time.curTime( theTime: time.timerec ); 50

time.hmsToSecs( theTime: time.timerec); returns( "eax"); 50

time.secsToHMS( seconds:uns32; var HMS:time.timerec ); 50

13 Exceptions (excepts.hhf) 51
13.1 Exception Constants 51
13.1.1 ex.UnknownException (0) 51
13.1.2 ex.StringOverflow (1) 51
13.1.3 ex.StringIndexError (2) 51
13.1.4 ex.ValueOutOfRange (3) 51
13.1.5 ex.IllegalChar (4) 51
13.1.6 ex.ConversionError (5) 52
13.1.7 ex.BadFileHandle (6) 52
13.1.8 ex.FileOpenFailure (7) 52
13.1.9 ex.FileCloseError (8) 52
13.1.10 ex.FileWriteError (9) 52
13.1.11 ex.FileReadError (10) 52
13.1.12 ex.DiskFullError (11) 52
13.1.13 ex.EndOfFile (12) 52
13.1.14 ex.MemoryAllocationFailure (13) 52
13.1.15 ex.AttemptToDerefNULL (14) 53
13.1.16 ex.CannotFreeMemory (15) 53
13.1.17 ex.WidthTooBig (16) 53
13.1.18 ex.TooManyCmdLnParms (17) 53
13.1.19 ex.ArrayShapeViolation (18) 53
13.1.20 ex.ArrayBounds (19) 53
13.1.21 ex.InvalidDate (20) 53
13.1.22 ex.InvalidDateFormat (21) 53
13.1.23 ex.TimeOverflow (22) 54
13.1.24 ex.AssertionFailed (23) 54
13.1.25 ex.ExecutedAbstract (24) 54
13.1.26 ex.AccessViolation ($c0000005) 54
13.1.27 ex.Breakpoint ($80000003) 54
13.1.28 ex.SingleStep ($80000004) 54
13.1.29 ex.PrivInstr ($c0000096) 54
13.1.30 ex.IllegalInstr ($c000001d) 54
13.1.31 ex.BoundInstr ($c000008c) 54
13.1.32 ex.IntoInstr ($c0000095) 55
13.1.33 ex.DivideError ($c0000094) 55
13.1.34 ex.fDenormal ($c000008d) 55
13.1.35 ex.fDivByZero ($c000008e) 55
13.1.36 ex.fInexactResult ($c000008f) 55
13.1.37 ex.fInvalidOperation ($c0000090) 55
13.1.38 ex.fOverflow ($c0000091) 55
13.1.39 ex.fStackCheck ($c0000092) 55
13.1.40 ex.fUnderflow ($c0000093) 55
13.1.41 ex.InvalidHandle ($c0000008) 56
13.1.42 ex.StackOverflow ($c00000fd) 56
13.1.43 ex.ControlC ($c000013a) 56
13.2 PrintExceptionError 56
13.3 Assertions 56
13.4 Miscellaneous 56
14 File Class (fileclass.hhf) 57
14.1 General File Operations 57

filevar.create; returns( "esi" ); 57

filevar.handle; returns( "eax" ); 57

filevar.open( filename:string; access:dword ) 58

filevar.openNew( filename:string ) 58

filevar.close; 58

14.2 File Class Output Routines 58
14.2.1 Miscellaneous Output 58

filevar.write( var buffer:byte; count:dword ) 58

filevar.putbool( b:boolean ); 58

filevar.newln( ); 58

14.2.2 Character, Character Set, and String Output 59

filevar.putc( c:char ) 59

filevar.putcSize( c:char; width:int32; fill:char ) 59

filevar.putcset( cst:cset ); 59

filevar.puts( s:string ); 59

filevar.putsSize( s:string; width:int32; fill:char ) 59

14.2.3 Hexadecimal Numeric Output 59

filevar.putb( b:byte )
filevar.putbSize( b:byte; size:dword; fill:char ) 59

filevar.putw( w:word )
filevar.putwSize( w:word; size:dword; fill:char ) 59

filevar.putd( dw:dword )
filevar.putdSize( d:dword; size:dword; fill:char ) 59

filevar.putq( qw:qword )
filevar.putqSize( q:qword; size:dword; fill:char ) 60

filevar.puttb( tb:tbyte ) 60

14.2.4 Signed Integer Numeric Output 60

filevar.puti64Size( q:qword; width:int32; fill:char ) 60

filevar.puti32Size( d:dword; width:int32; fill:char ) 60

filevar.puti16Size( w:word; width:int32; fill:char ) 60

filevar.puti8Size ( b:byte; width:int32; fill:char ) 61

filevar.puti64( q:qword ) 61

filevar.puti32( d:dword ) 61

filevar.puti16( w:word ) 61

filevar.puti8 ( b:byte ) 61

14.2.5 Unsigned Integer Numeric Output 61

filevar.putu64Size( q:qword; width:int32; fill:char ) 61

filevar.putu32Size( d:dword; width:int32; fill:char ) 62

filevar.putu16size( w:word; width:int32; fill:char ) 62

filevar.putu8size( b:byte; width:int32; fill:char ) 62

filevar.putu64( q:qword ) 62

filevar.putu32( d:dword ) 62

filevar.putu16( w:word ) 62

filevar.putu8 ( b:byte ) 62

14.2.6 Floating Point Output 62
14.2.6.1 Real Output Using Scientific Notation 62

filevar.pute80( r:real80; width:uns32 ) 63

filevar.pute64( r:real64; width:uns32 ) 63

filevar.pute32( r:real32; width:uns32 ) 63

14.2.6.2 Real Output Using Decimal Notation 63

filevar.putr80pad( r:real80; width:uns32; decpts:uns32; pad:char ) 64

filevar.putr64pad( r:real64; width:uns32; decpts:uns32; pad:char ) 64

filevar.putr32pad( r:real32; width:uns32; decpts:uns32; pad:char ) 64

filevar.putr80( r:real80; width:uns32; decpts:uns32 ) 64

filevar.putr64( r:real64; width:uns32; decpts:uns32 ) 64

filevar.putr32( r:real32; width:uns32; decpts:uns32 ) 64

14.2.7 Generic File Output 65

filevar.put( parameter_list ) 65

14.3 File Input 66
14.3.1 Generic File Input 66

filevar.read( var buffer:byte; count:dword ) 66

filevar.readln 66

filevar.eoln 66

14.3.2 Character and String Input 67

filevar.getc; returns( "al" ); 67

filevar.gets( s:string ) 67

filevar.a_gets; returns( "eax" ); 67

14.3.3 Signed Integer Input 67

filevar.geti8; returns( "al" ); 67

filevar.geti16; returns( "ax" ); 67

filevar.geti32; returns( "eax" ); 68

filevar.geti64( var q:qword ); 68

14.3.4 Unsigned Integer Input 68

filevar.getu8; returns( "al" ); 68

filevar.getu16; returns( "ax" ); 68

filevar.getu32; returns( "eax" ); 68

filevar.getu64( var q:qword ); 69

14.3.5 Hexadecimal Input 69

filevar.getb; returns( "al" ); 69

filevar.getw; returns( "ax" ); 69

filevar.getd; returns( "eax" ); 69

filevar.getq( var q:qword ); 69

14.3.6 Floating Point Input 70

filevar.getf; returns( "st0" ); 70

14.3.7 Generic File Input 70

filevar.get( List_of_items_to_read ); 70

15 The File I/O Module (fileio.hhf) 71
15.1 General File I/O Functions 71

fileio.open( FileName: string; Access:dword ); returns( "eax" ); 71

fileio.openNew( FileName: string ); returns( "eax" ); 71

fileio.close( Handle:dword ); 71

fileio.eof( Handle:dword ); returns( "al" ); 71

15.2 Directory and File Manipulation Functions 71

fileio.rewind( Handle:dword ); returns( "eax" ); 71

fileio.append( handle:dword ); returns( "eax" ); 72

fileio.position( Handle:dword ); returns( "eax" ); 72

fileio.seek( Handle:dword; offset:dword ); returns( "eax" ); 72

fileio.rSeek( Handle:dword; offset:dword ); returns( "eax" ); 72

fileio.truncate( Handle:dword ); returns( "eax" ); 72

fileio.copy( source:string; dest:string; failIfExists:boolean ); returns( "eax" ); 72

fileio.move( source:string; dest:string ); returns( "eax" ); 72

fileio.delete( filename:string ); returns( "eax" ); 72

fileio.mkdir( dirname:string ); returns( "eax" ); 72

fileio.cd( dirname:string ); returns( "eax" ); 72

fileio.gwd( dest:string ); 72

fileio.size( Handle:dword ); returns( "eax" ); 73

fileio.size( filename:string ); returns( "eax" ); 73

15.3 File Output Routines 73
15.3.1 Miscellaneous Output Routines 73

fileio.write( Handle:dword; var buffer:byte; count:uns32 ) 73

fileio.newln( Handle:dword ) 73

fileio.putbool( Handle:dword; b:boolean ) 73

15.3.2 Character, String, and Character Set Output Routines 73

fileio.putc( Handle:dword; c:char ) 73

fileio.putcSize( Handle:dword; c:char; width:int32; fill:char ) 73

fileio.putcset( Handle:dword; cs:cset ) 73

fileio.puts( Handle:dword; s:string ) 74

fileio.putsSize( Handle:dword; s:string; width:int32; fill:char ) 74

15.3.3 Hexadecimal Output Routines 74

fileio.putb( Handle:dword; b:byte )
fileio.putbSize( Handle:dword; b:byte; size:dword; fill:char ) 74

fileio.putw( Handle:dword; w:word )
fileio.putwSize( Handle:dword; w:word; size:dword; fill:char ) 74

fileio.putd( Handle:dword; dw:dword )
fileio.putdSize( Handle:dword; d:dword; size:dword; fill:char ) 74

fileio.putq( Handle:dword; qw:qword )
fileio.putqSize( Handle:dword; q:qword; size:dword; fill:char ) 74

fileio.puttb( Handle:dword; tb:tbyte ) 74

fileio.putl( Handle:dword; l:lword )
fileio.putlSize( Handle:dword; l:lword; size:dword; fill:char ) 75

15.3.4 Signed Integer Output Routines 75

fileio.puti8Size ( Handle:dword; b:byte; width:int32; fill:char ) 75

fileio.puti16Size( Handle:dword; w:word; width:int32; fill:char ) 75

fileio.puti32Size( Handle:dword; d:dword; width:int32; fill:char ) 75

fileio.puti64Size( Handle:dword; q:qword; width:int32; fill:char ) 76

fileio.puti128Size( Handle:dword; l:lword; width:int32; fill:char ) 76

fileio.puti8 ( Handle:dword; b:byte ) 76

fileio.puti16( Handle:dword; w:word ) 76

fileio.puti32( Handle:dword; d:dword ) 76

fileio.puti64( Handle:dword; q:qword ) 76

fileio.puti128( Handle:dword; l:lword ) 76

15.3.5 Unsigned Integer Output Routines 76

fileio.putu8Size( Handle:dword; b:byte; width:int32; fill:char ) 77

fileio.putu16Size( Handle:dword; w:word; width:int32; fill:char ) 77

fileio.putu32Size( Handle:dword; d:dword; width:int32; fill:char ) 77

fileio.putu64Size( Handle:dword; q:qword; width:int32; fill:char ) 77

fileio.putu128Size( Handle:dword; l:lword; width:int32; fill:char ) 77

fileio.putu8 ( Handle:dword; b:byte ) 77

fileio.putu16( Handle:dword; w:word ) 77

fileio.putu32( Handle:dword; d:dword ) 77

fileio.putu64( Handle:dword; q:qword ) 78

fileio.putu128( Handle:dword; l:lword ) 78

15.3.6 Floating Point Output Routines 78
15.3.6.1 Real Output Using Scientific Notation 78

fileio.pute80( Handle:dword; r:real80; width:uns32 ) 78

fileio.pute64( Handle:dword; r:real64; width:uns32 ) 78

fileio.pute32( Handle:dword; r:real32; width:uns32 ) 79

15.3.6.2 Real Output Using Decimal Notation 79

fileio.putr80pad( Handle:dword; r:real80; width:uns32; decpts:uns32; pad:char ) 79

fileio.putr64pad( Handle:dword; r:real64; width:uns32; decpts:uns32; pad:char ) 79

fileio.putr32pad( Handle:dword; r:real32; width:uns32; decpts:uns32; pad:char ) 79

fileio.putr80( Handle:dword; r:real80; width:uns32; decpts:uns32 ) 80

fileio.putr64( Handle:dword; r:real64; width:uns32; decpts:uns32 ) 80

fileio.putr32( Handle:dword; r:real32; width:uns32; decpts:uns32 ) 80

15.3.7 Generic File Output Routine 80

fileio.put( list_of_items ) 80

15.4 File Input Routines 81
15.4.1 General File Input Routines 81

fileio.read( Handle:dword; var buffer:byte; count:uns32 ) 81

fileio.readLn( Handle:dword ); 81

fileio.eoln( Handle:dword ); returns( "al" ); 81

15.4.2 Character and String Input Routines 81

fileio.getc( Handle:dword ); returns( "al" ); 82

fileio.gets( Handle:dword; s:string ); 82

fileio.a_gets( Handle:dword ); returns( "eax" ); 82

15.4.3 Signed Integer Input Routines 82

fileio.geti8( Handle:dword ); returns( "al" ); 82

fileio.geti16( Handle:dword ); returns( "ax" ); 82

fileio.geti32( Handle:dword ); returns( "eax" ); 82

fileio.geti64( Handle:dword ); 83

fileio.geti128( Handle:dword; var dest:lword ); 83

15.4.4 Unsigned Integer Input Routines 83

fileio.getu8( Handle:dword ); returns( "al" ); 83

fileio.getu16( Handle:dword ); returns( "ax" ); 83

fileio.getu32( Handle:dword ); returns( "eax" ); 83

fileio.getu64( Handle:dword ); 84

fileio.getu128( Handle:dword; var dest:lword ); 84

15.4.5 Hexadecimal Input Routines 84

fileio.getb( Handle:dword ); returns( "al" ); 84

fileio.getw( Handle:dword ); returns( "ax" ); 84

fileio.getd( Handle:dword ); returns( "eax" ); 84

fileio.getq( Handle:dword ); 84

fileio.getl( Handle:dword; var dest:lword ); 85

15.4.6 Floating Point Input 85

fileio.getf( Handle:dword ); 85

15.4.7 Generic File Input 85

fileio.get( List_of_items_to_read ); 85

16 The Linux Module (linux.hhf) 87
17 The Lists Module (lists.hhf) 88
17.1 List Maintenance 89

procedure list.create; returns( "esi" ); 89

method list.destroy; 89

17.2 Adding Nodes to a List 89

method list.append_index( var n:node; posn: dword ); returns( "esi" ); 89

method list.append_node( var n:node; var after: node ); returns( "esi" ); 90

method list.append_last( var n:node ); returns( "esi" ); 90

method list.insert_index( var n:node; posn:dword ); returns( "esi" ); 90

method list.insert_node( var n:node; var before:node ); returns( "esi" ); 90

method list.insert_first( var n:node ); returns( "esi" ); 90

17.3 Removing Nodes from a List 90

method list.delete_index( posn:dword ); returns( "esi" ); 90

method list.delete_node( var n:node ); returns( "esi" ); 90

method list.delete_first; returns( "esi" ); 91

method list.delete_last; returns( "esi" ); 91

17.4 Accessing Nodes in a List 91

method list.index( posn:dword ); returns( "esi" ); 91

iterator list.itemInList; 91

method list.numNodes; returns( "eax" ); 91

18 Mathematical Functions, Trigonometic and Logarithmic (math.hhf) 92

math.cot; returns( "st0" ); // Macro that overloads the following functions: 97

math._cot; returns( "st0" ); 97

math.csc 97

math._csc; returns( "st0" ); 97

math.sec 97

math._sec; returns( "st0" ); 97

math.asin 98

math._asin; returns( "st0" ); 98

math.acos 98

math._acos; returns( "st0" ); 98

math.acot 98

math._acot; returns( "st0" ); 98

math.acsc 99

math._acsc; returns( "st0" ); 99

math.asec 99

math._asec; returns( "st0" ); 99

math.twoToX 99

math._twoToX; returns( "st0" ); 99

math.TenToX 99

math._tenToX; returns( "st0" ); 99

math.exp 100

math._exp; returns( "st0" ); 100

math.ytoX; returns( "st0" ); 100

math._yToX; // Y is at ST1, X is at ST0. 100

math.log 100

math._log; returns( "st0" ); 100

math.ln 101

math._ln; returns( "st0" ); 101

19 Memory Allocation (memory.hhf) 102
19.1 Generic Memory Allocation 102

malloc( size:dword ); returns( "eax" ); 102

free( memptr:dword ); 102

realloc( memptr:dword; newsize:dword ); returns( "eax" ); 102

isInHeap( memptr:dword ); 102

talloc( size ); (returns "eax" as macro result) 102

19.2 String Memory Allocation 102

stralloc( size:dword ); returns( "eax" ); 102

tstralloc( size ); (returns pointer to new string in EAX ). 103

20 HLA Pattern Matching Routines (patterns.hhf) 104
20.1 Pat.match and Pat.endmatch Syntax 104
20.2 Alternation 106
20.3 Pattern Matching Macros 106

pat.EOS 106

pat.position( n ) 106

pat.atPos( n ) 106

pat.skip( n ) 107

pat.getPos( dest ) 107

pat.fail 107

pat.fence 107

pat.onePat; 107

<< pattern matching statements >> 107

pat.endOnePat; 107

pat.zeroOrOnePat; 108

<< pattern matching statements >> 108

pat.endZeroOrOnePat; 108

pat.zeroOrMorePat; 108

<< pattern matching statements >> 108

pat.endZeroOrMorePat 108

pat.oneOrMorePat( Pattern ) 108

<< pattern matching statements >> 108

pat.endOneOrMorePat 108

20.4 Pattern Matching Functions 109
20.4.1 Character Set Matching Procedures 109

procedure pat.peekCset( cst:cset ); 109

procedure pat.oneCset( cst:cset ); 109

procedure pat.upToCset( cst:cset ); 109

procedure pat.zeroOrOneCset( cst:cset ) 109

procedure pat.l_ZeroOrOneCset( cst:cset ) 109

procedure pat.zeroOrMoreCset( cst:cset ); 109

procedure pat.l_ZeroOrMoreCset( cst:cset ); 109

procedure pat.oneOrMoreCset( cst:cset ); 110

procedure pat.l_OneOrMoreCset( cst:cset ); 110

procedure pat.exactlyNCset( cst:cset; n:uns32 ); 110

procedure pat.firstNCset( cst:cset; n:uns32 ); external; 110

procedure pat.norLessCset( cst:cset; n:uns32 ); 110

procedure pat.l_NorLessCset( cst:cset; n:uns32 ); 110

procedure pat.norMoreCset( cst:cset; n:uns32 ); 110

procedure pat.l_NorMoreCset( cst:cset; n:uns32 ); 111

procedure pat.ntoMCset( cst:cset; n:uns32; m:uns32 ); 111

procedure pat.l_NtoMCset( cst:cset; n:uns32; m:uns32 ); external; 111

procedure pat.exactlyNtoMCset( cst:cset; n:uns32; m:uns32 ); 111

procedure pat.l_ExactlyNtoMCset( cst:cset; n:uns32; m:uns32 ); external; 111

20.4.2 Character Matching Procedures 112

procedure pat.peekChar( c:char ); 112

procedure pat.oneChar( c:char ); 112

procedure pat.upToChar( c:char ); 112

procedure pat.zeroOrOneChar( c:char ); 112

procedure pat.l_ZeroOrOneChar( c:char ); 112

procedure pat.zeroOrMoreChar( c:char ); 112

procedure pat.l_ZeroOrMoreChar( c:char ); 112

procedure pat.oneOrMoreChar( c:char ); 112

procedure pat.l_OneOrMoreChar( c:char ); 113

procedure pat.exactlyNChar( c:char; n:uns32 ); 113

procedure pat.firstNChar( c:char; n:uns32 ); 113

procedure pat.norLessChar( c:char; n:uns32 ); 113

procedure pat.l_NorLessChar( c:char; n:uns32 ); 113

procedure pat.norMoreChar( c:char; n:uns32 ); 113

procedure pat.ntoMChar( c:char; n:uns32; m:uns32 ); 114

procedure pat.l_NtoMChar( c:char; n:uns32; m:uns32 ); 114

procedure pat.exactlyNtoMChar( c:char; n:uns32; m:uns32 ); 114

procedure pat.l_ExactlyNtoMChar( c:char; n:uns32; m:uns32 ); 114

20.4.3 Case Insensitive Character Matching Routines 114

procedure pat.peekiChar( c:char ); 114

procedure pat.oneiChar( c:char ); 115

procedure pat.upToiChar( c:char ); 115

procedure pat.zeroOrOneiChar( c:char ); 115

procedure pat.l_ZeroOrOneiChar( c:char ); 115

procedure pat.zeroOrMoreiChar( c:char ); 115

procedure pat.l_ZeroOrMoreiChar( c:char ); 115

procedure pat.oneOrMoreiChar( c:char ); 115

procedure pat.l_OneOrMoreiChar( c:char ); 116

procedure pat.exactlyNiChar( c:char; n:uns32 ); 116

procedure pat.firstNiChar( c:char; n:uns32 ); 116

procedure pat.norLessiChar( c:char; n:uns32 ); 116

procedure pat.l_NorLessiChar( c:char; n:uns32 ); 116

procedure pat.norMoreiChar( c:char; n:uns32 ); 116

procedure pat.l_NorMoreiChar( c:char; n:uns32 ); 116

procedure pat.ntoMiChar( c:char; n:uns32; m:uns32 ); 117

procedure pat.l_NtoMiChar( c:char; n:uns32; m:uns32 ); 117

procedure pat.exactlyNtoMiChar( c:char; n:uns32; m:uns32 ); 117

procedure pat.l_ExactlyNtoMiChar( c:char; n:uns32; m:uns32 ); 117

20.4.4 String matching procedures 117

procedure pat.matchStr( s:string ); 117

procedure pat.matchToStr( s:string ); 117

procedure pat.upToStr( s:string ); 118

procedure pat.matchToiStr( s:string ); 118

procedure pat.upToiStr( s:string ); 118

procedure pat.matchWord( s:string ); 118

procedure pat.matchiWord( s:string ); 118

procedure pat.getWordDelims( var cst:cset ); 118

procedure pat.setWordDelims( cst:cset); 118

20.4.5 String Extraction Procedures 118

procedure pat.extract( s:string ); 118

procedure pat.a_extract( var s:string ); 118

20.4.6 Whitespace and End of String Matching Procedures 119

procedure pat.getWhiteSpace( var cst:cset ); 119

procedure pat.setWhiteSpace( cst:cset); 119

procedure pat.zeroOrMoreWS; 119

procedure pat.oneOrMoreWS; 119

procedure pat.WSorEOS; 119

procedure pat.WSthenEOS; 119

procedure pat.peekWS; 119

procedure pat.peekWSorEOS; 119

20.4.7 Match an Arbitrary Sequence of Characters 120

procedure pat.arb; 120

procedure pat.l_arb; external; 120

20.4.8 Writing Your Own Pattern Matching Routines 120
21 Random Number Generators (rand.hhf) 131

rand.randomize; 131

rand.uniform; returns( "eax" ); 131

rand.urange( startRange:int32; endRange:int32 ); returns( "eax" ); 131

rand.random; returns( "eax" ); 131

rand.range( startRange:int32; endRange:int32 ); returns( "eax" ); 131

rand.deal( count:uns32 ); // Iterator returns value in EAX. 131

22 Standard Input Routines (stdin.hhf) 133
22.1 General Input Functions 133

stdin.handle; returns( "eax" ); 133

stdin.flushInput; 133

stdin.readln; 133

stdin.eoln; returns( "al" ); 133

22.2 Character and String Input Functions 133

stdin.peekc; returns( "al" ); 133

stdin.getc; returns( "al" ); 134

stdin.gets( s:string ); 134

stdin.a_gets; returns( "eax" ); 134

22.3 Hexadecimal Input Functions 134

stdin.getb; returns( "al" ); 134

stdin.getw; returns( "ax" ); 134

stdin.getd ; returns( "eax" ); 134

stdin.getq; returns( "edx:eax" ); 135

This function reads a 64-bit hexadecimal integer in the range 0..$FFFF_FFFF_FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getq function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF_FFFF_FFFF. This function returns the 64-bit result in EDX:EAX. 135

stdin.getl( l:lword ) ; 135

22.4 Signed Integer Input Functions 135

stdin.geti8; returns( "al" ); 135

stdin.geti16; returns( "ax" ); 135

stdin.geti32; returns( "eax" ); 135

stdin.geti64; returns( "edx:eax" ); 136

stdin.geti128( var l: lword ); 136

22.5 Unsigned Integer Input Routines 136

stdin.getu8; returns( "al" ); 136

stdin.getu16; returns( "ax" ); 136

stdin.getu32; returns( "eax" ); 136

stdin.getu64; returns( "edx:eax" ); 137

stdin.getu128( var q:qword ); 137

22.6 Floating Point Input 137

stdin.getf; returns( "st0" ); 137

23 Standard Output Routines (stdout.hhf) 138
23.1 General Output Functions 138

stdout.handle; returns( "eax" ); 138

stdout.newln; 138

23.2 Boolean Output 138

stdout.putbool( b:boolean ); 138

23.3 Character, String, and Cset Output 138

stdout.putcset( cs:cset ); 138

stdout.putc( c:char ); 138

stdout.putcSize( c:char; width:int32; fill:char ); 139

stdout.puts( s:string ); 139

stdout.putsSize( s:string; width:int32; fill:char ); 139

23.4 Hexadecimal Output 139

stdout.putb( b:byte );
stdout.putbSize( b:byte; size:dword; fill:char) 139

stdout.putw( w:word );
stdout.putwSize( w:word; size:dword; fill:char) 139

stdout.putd( dw:dword );
stdout.putdSize( d:dword; size:dword; fill:char) 139

stdout.putq( qw:qword );
stdout.putqSize( q:qword; size:dword; fill:char) 139

stdout.puttb( tb:tbyte ); 139

stdout.putl( l:lword );
stdout.putlSize( l:lword; size:dword; fill:char) 140

23.5 Signed Integer Output 140

stdout.puti8size ( b:byte; width:int32; fill:char ); 140

stdout.puti16Size( w:word; width:int32; fill:char ); 140

stdout.puti32Size( d:dword; width:int32; fill:char ); 140

stdout.puti64Size( q:qword; width:int32; fill:char ); 140

stdout.puti128Size( l:lword; width:int32; fill:char ); 141

stdout.puti8 ( b:byte ); 141

stdout.puti16( w:word ); 141

stdout.puti32( d:dword ); 141

stdout.puti64( q:qword ); 141

stdout.puti128( l:lword ); 141

23.6 Unsigned Integer Output 141

stdout.putu8Size ( b:byte; width:int32; fill:char ); 142

stdout.putu16Size( w:word; width:int32; fill:char ); 142

stdout.putu32Size( d:dword; width:int32; fill:char ); 142

stdout.putu64Size( q:qword; width:int32; fill:char ); 142

stdout.putu128Size( l:lword; width:int32; fill:char ); 142

stdout.putu8 ( b:byte ); 142

stdout.putu16( w:word ); 142

stdout.putu32( d:dword ); 142

stdout.putu64( d:dword ); 142

stdout.putu128( d:dword ); 142

23.7 Floating Point Output 142
23.7.0.1 Real Output Using Scientific Notation 143

stdout.pute80( r:real80; width:uns32 ); 143

stdout.pute64( r:real64; width:uns32 ); 143

stdout.pute32( r:real32; width:uns32 ); 143

23.7.1 Real Output Using Decimal Notation 144

stdout.putr80pad( r:real80; width:uns32; decpts:uns32; pad:char ); 144

stdout.putr64pad( r:real64; width:uns32; decpts:uns32; pad:char ); 144

stdout.putr32pad( r:real32; width:uns32; decpts:uns32; pad:char ); 144

stdout.putr80( r:real80; width:uns32; decpts:uns32 ); 144

stdout.putr64( r:real64; width:uns32; decpts:uns32 ); 145

stdout.putr32( r:real32; width:uns32; decpts:uns32 ); 145

23.8 The stdout.put Macro 145

stdout.put( output_list ); 145

24 Strings (strings.hhf) 147
24.1 String Allocation Macros and Functions 149

str.strvar( size ) 149

str.init( var b:var; numBytes:dword ); returns( "eax" ); 149

24.2 String Length Calculation 149

str.length( src ); returns( "eax" ); 149

str.mLength( src ); // returns the value in EAX 150

24.3 String Assignment, Substring, and Concatenation Functions 150

str.cpy( src:string; dest:string ); 150

str.a_cpy( src:string ); returns( "eax" ); 150

str.setstr( src:char; dest:string; cnt:uns32 ); 150

str.cat( src:string; dest:string ) 150

24.4 String Comparison, Search, and Scanning Functions 150

str.eq( left:string; right:string ); returns( "al" ); 150

str.ne( left:string; right:string ); returns( "al" ); 150

str.lt left:string; right:string ); returns( "al" ); 150

str.le( left:string; right:string ); returns( "al" ); 150

str.gt( left:string; right:string ); returns( "al" ); 151

str.ge( left:string; right:string ); returns( "al" ); 151

str.ieq( left:string; right:string ); returns( "al" ); 151

str.ine( left:string; right:string ); returns( "al" ); 151

str.ilt left:string; right:string ); returns( "al" ); 151

str.ile( left:string; right:string ); returns( "al" ); 151

str.igt( left:string; right:string ); returns( "al" ); 151

str.ige( left:string; right:string ); returns( "al" ); 151

str.index( src1:string; src2:string ); returns( "eax" ); 151

str.index2( src1:string; offs:uns32; src2:string ); returns( "eax" ); 151

str.rindex( src1:string; src2:string ); returns( "eax" ); 151

str.rindex2( src1:string; offs:uns32; src2:string ); returns( "eax" ); 152

str.prefix( src1:string; src2:string ); returns( "al" ); 152

str.prefix2( src1:string; offs:uns32; src2:string ); returns( "al" ); 152

str.chpos( src1:string; src2:char ); returns( "eax" ); 152

str.chpos2( src1:string; offs:uns32; src2:char ); returns( "eax" ); 152

str.rchpos( src1:string; src2:char ); returns( "eax" ); 152

str.rchpos2( src1:string; offs:uns32; src2:char ); returns( "eax" ); 152

str.span( src1: string; src2:cset ); returns( "eax" ); 152

str.span2( src1: string; start:int32; src2:cset ); returns( "eax" ); 152

str.rspan( src1: string; src2:cset ); returns( "eax" ); 152

str.brk( src1: string; src2:cset ); returns( "eax" ); 152

str.brk2( src1: string; start:int32; src2:cset ); returns( "eax" ); 153

str.rbrk( src1: string; src2:cset ); returns( "eax" ); 153

str.tokenize( src: string; var dest:dword ); returns( "eax" ); 153

24.5 String Insertion, Deletion and Extraction Functions 154

str.substr( src:string; dest:string; start:dword; len:dword ) 154

str.a_substr( src:string; start:dword; len:dword ); returns( "eax" ); 154

str.insert( src:string; dest:string; start:dword ) 154

str.a_insert( src1:string; src2:string; start:dword ); returns( "eax" ); 154

str.delete( dest:string; start:dword; length:dword ) 154

str.a_delete( src:string; start:dword; length:dword ); returns( "eax" ); 155

str.replace( dest: string; fromStr:string; toStr:string ) 155

24.6 String Conversion Functions 155

str.upper( dest: string ) 155

str.delspace( dest: string ) 155

str.trim( dest: string ) 155

24.7 String Construction Functions 155
24.7.1 Boolean Concatenation Function 156

str.catbool( b:boolean; dest:string ); 156

24.7.2 Character and String Concatenation Functions 156

str.catc( c:char; dest:string ); 156

str.catcSize( c:char; width:int32; fill:char; dest:string ); 156

str.cats( s:string; dest:string ); 156

str.catsSize( s:string; width:int32; fill:char; dest:string ); 156

str.catcset( c:cset; dest:string ); 156

24.7.3 Hexadecimal String Concatentation Functions 156

str.catb( b:byte; dest:string );
str.catbSize( b:byte; size:dword; fill:char ) 156

str.catw( w:word; dest:string );
str.catwSize( w:word; size:dword; fill:char ) 156

str.catd( d:dword; dest:string )
str.catdSize( d:dword; size:dword; fill:char ) 157

str.catq( q:qword; dest:string )
str.catqSize( q:qword; size:dword; fill:char ) 157

24.7.4 Unsigned Integer String Concatenation Functions 157

str.catu8size 157

str.catu16size 158

str.catu32size 158

str.catu64size 158

str.catu8( u8:byte; dest:string ); 158

str.catu16( u16:word; dest:string ); 158

str.catu32( u32:dword; dest:string ); 159

str.catu64( u64:qword; dest:string ); 159

24.7.5 Signed Integer String Concatenation Functions 159

str.cati8size 159

str.cati16size 160

str.cati32size 160

str.cati64size 160

str.cati8( i8:byte; dest:string ); 160

str.cati16( i16:word; dest:string ); 160

str.cati32( i32:dword; dest:string ); 161

str.cati64( i64:qword; dest:string ); 161

24.7.6 Floating Point String Concatenation Using Scientific Notation 161

str.cate80 161

str.cate64 162

str.cate32 162

24.7.7 Floating Point String Concatentation Using Decimal Notation 162

str.catr80 163

str.catr64 163

str.catr32 164

24.8 Generic String Formatting Function 164

str.put( dest:string, values_to_convert ); 164

24.9 Zero-Terminated String Support 165
24.9.1 ZString Length 166

str.zlen( var zstring:var ); @returns( "eax" ); 166

24.9.2 Converting a ZString to an HLA String 167

str.cpyz( var zsrc:var; dest:string );
str.a_cpyz( var zsrc:var ); @returns( "eax" ); 167

24.9.3 Concatenting a ZString to the End of an HLA String 168
24.9.4 Copying a ZString 168
24.9.5 Concatenating ZStrings 169
24.9.6 Comparing ZStrings 170
25 HLA Table Support (tables.hhf) 171

table.create( HashSize:uns32 ); 171

table.destroy( FreeValue:procedure ); 172

table.lookup( id:string ); 172

table.getNode( id:string ); 172

table.item; 173

26 The HLA Timer Module (timer.hhf) 174

timer.Accumulated 174

timer.DateStarted 174

timer.Running 174

timer.create; 175

timer.start; 175

timer.checkPoint; 175

timer.restart; 175

27 Win32 Constants (win32.hhf) 176

The HLA Standard Library

v1.39 Added descriptions for the zero-terminated string functions: str.cpyz, str.a_cpyz, str.catz, str.zcpy, str.zcat, and str.zcmp.

v1.38: Added descriptions for the 64-bit and 128-bit arithmetic and I/O routines.

v1.37: Changed description of str.trim, str.a_trim, stridelspace, and str.a_delspace to match the changes in the code.

v1.35: Began maintaining version numbers for this document.

The following sections provide a basic description of some of the routines in the HLA Standard Library. Keep in mind that the HLA Standard Library is a work in progress and the following sections may not be totally up to date. The HLA Standard Library source code is the final arbitor if there is a question how the routines operate.

Unless otherwise noted, you can assume that the Standard Library routines preserve all the general purpose registers. They generally do not preserve the flags.

80x86 Constants (x86.hhf)

The x86.hhf header file contains record structures and constants of interest to 80x86 assembly langage programmers. Please consult this header file for more details.

The Arrays Module (arrays.hhf)

The HLA Arrays module provides a set of datatypes, macros, and procedures that simplify array access in assembly language (especially multidimensional array access). In addition to supporting standard HLA arrays with static size declarations, the HLA arrays module also supports dynamic arrays that let you specify the array size at run-time1.

Declarations, Allocation, and Predicates
#macro array.dArray( type, dimensions)

The first feature in the array package to consider is the support for dynamic arrays. HLA provides a macro/data type that lets you tell HLA that you want to specify the array size under program control. This macro/data type is array.dArray (dArray stands for dynamic array). You use this macro invocation in place of a standard data type identifier in an HLA variable declaration.

The first macro parameter is the desired datatype; this would typically be an HLA primitive data type like int32 or char, although any data type identifier is legal.

The second parameter is the number of dimensions for this array data type Generally this value is two or greater (since creating dynamic single dimensional arrays using only malloc is trivial). Because of the way array indicies are computed by HLA, it is not possible to specify the number of dimensions dynamically2.

Note: since array.dArray is not a data type identifier (it's a macro), you cannot directly create a dynamic array of dynamic arrays. I.e., the following is not legal:

 

static

DAofDAs: array.dArray( array.dArray( uns32, 2), 2 );

 

However, you can achieve exactly the same thing by using the following code:

 

type

DAs: array.dArray( uns32, 2 );

 

static

DAofDAs: array.dArray( DAs, 2 );

 

The TYPE declaration creates a type identifier that is a dynamic array. The STATIC variable declaration uses this type identifier in the array.dArray invocation to create a dynamic array of dynamic arrays.

#macro array.daAlloc( dynamicArrayName, <<list of dimension bounds>> );

The array.dArray macro allocates storage for a dynamic array variable. It does not, however, allocate storage for the dynamic array itself; that happens at run-time. You must use the array.daAlloc macro to actually allocate storage for your array while the program is running. The first parameter must be the name of the dynamic array variable you've declared via the array.dArray macro. The remaining parameters are the number of elements for each dimension of the array. This list of dimension bounds must contain the same number of values as specified by the second parameter in the array.dArray declaration. The dimension list can be constants or memory locations (note, specifically, that registers are not currently allowed here; this may be fixed in a future version).

The following code demonstrates how to declare a dynamic array and allocate storage for it at run-time:

 

program main;

static

i:uns32;

j:uns32;

k:uns32;

MyArray: array.dArray( uns32, 3 );

 

begin main;

 

stdout.put( "Enter size of first dimension: " );

stdin.get( i );

stdout.put( "Enter size of second dimension: " );

stdin.get( j );

stdout.put( "Enter size of third dimension: " );

stdin.get( k );

 

// Allocate storage for the array:

 

array.daAlloc( MyArray, i, j, k );

 

<< Code that manipulates the 3-D dynamic array >>

 

end main;

Dynamic Array Allocation Example
#macro array.daFree( dynamicArrayName );

Use the array.daFree macro to free up storage you've allocated via the array.daAlloc call. This returns the array data storage to the system so it can be reused later. Warning: do not continue to access the array's data after calling array.daFree . The system may be using the storage for other purposes after you release the storage back to the system with array.daFree.

Note: You should only call array.daFree for arrays you've allocated via array.daAlloc .

 

#macro array.IsItVar( objectName )

This is a macro that evaluates to a compile-time expression yielding true if the object is a variable identifier. Variable identifiers are those IDs you declare in a VAR, STATIC, READONLY, or STORAGE declaration section, or IDs you declare as parameters to a procedure. This macro returns false for all other parameters.

#macro array.IsItDynamic( arrayName )

This is a macro that expands to a compile-time expression yielding true or false depending upon whether the parameter was declared with the array.dArray data type. If so, this function returns true; else it returns false. Note that a return value of false does not necessarily indicate that the specified parameter is a static array. Anything except a dynamic array object returns false. For example, if you pass the name of a scalar variable, an undefined variable, or something that is not a variable, this macro evaluates false. Note that you can use the HLA @type function to test to see if an object is a static array; however, @type will not return hla.ptArray for dynamic array objects since array.dArray objects are actually records. Hence the array.IsItDynamic function to handle this chore.

Accessing Dynamic Array Objects
#macro array.index( reg32, arrayName, <<list of indicies>> );

This macro computes a row-major order index into a multidimensional array. The array can be a static or dynamic array. The list of indicies is a comma separate list of constants, 32-bit memory locations, or 32-bit registers. You should not, however, specify the register appearing as the first parameter in the list of indicies.

If the VAL constant " array.BoundsChk " is true, this macro will emit code that checks the bounds of the array indicies to ensure that they are valid. The code will raise an " ex.ArrayBounds " exception if any index is out of bounds. You may disable the code generation for the bounds checking by setting the " array.BoundsChk " VAL object to false using a statement like the following:

 

?array.BoundsChk := false;

 

You can turn the bounds checking on and off in segments of your code by using statements like the above that set " array.BoundsChk " to true or false.

This macro leaves pointer into the array sitting in the specified 32-bit register.

Example:

 

static

arrayS: uns32[ 2,3,4 ];

arrayD: array.dArray( uns32, 3 );

 

.

.

.

// copy arrayS[i, j, k] to arrayD[m,n,p]:

 

array.index( ebx, arrayS, i, j, k );

mov( [ ebx ], eax );

array.index( ebx, arrayD, m, n, p );

mov( eax, [ebx] );

 

 

iterator array.element( arrayName );

This iterator returns each successive element of the specified array. It returns the elements in row major order (that is, the last dimension increments the fastest and the first dimension increments the slowest when returning elements of a multidimensional array). This iterator returns byte objects in the AL register; it returns word objects in the AX register; it returns dword objects in the EAX register; it returns 64-bit (non-real) objects in the EDX:EAX register pair. This routine returns all floating point (real) objects on the top of the FPU stack.

Note that array.element is actually a macro, not an iterator. The macro, however, simply provides overloading to call one of seven different iterators depending on the size and type of the operand. However, this macro implementation is transparent to you. You would use this macro exactly like any other iterator.

Note that array.element works with both statically declared arrays and dynamic arrays you've declared with array.dArray and you've allocated via array.daAlloc .

Examples:

static

arrayS: uns32[ 2,3,4 ];

arrayD: array.dArray( uns32, 3 );

 

.

.

.

 

foreach array.element( arrayS ) do

 

stdout.put( "Current arrayS element = ", eax, nl );

 

endfor;

.

.

.

foreach array.element( arrayD ) do

 

stdout.put( "Current arrayD element = ", eax, nl );

 

endfor;

.

.

.

 

Array Operations/Functions
#macro array.cpy( srcArray, destArray );

This macro copies a source array to a destination array. Both arrays must be the same size and shape (shape means that they have the same number of dimensions and the bounds on all the dimensions correspond between the source and destination arrays). Both static and dynamic array variables are acceptable for either parameter.

Example:

static

arrayS: uns32[ 2,3,4 ];

arrayD: array.dArray( uns32, 3 );

 

.

.

.

 

// note: for the following to be legal at run-time,

// the arrayD dynamic array must have storage allocated

// for it with a statement like

// "array.daAlloc( arrayD, 2, 3, 4 );"

 

array.cpy( arrayS, arrayD );

 

#macro array.reduce( srcArray, destArray );

#keyword array.beforeRow;

#keyword array.reduction;

#keyword array.afterRow;

#terminator array.endreduce;

The array.reduce macro emits code to do a "row-reduction" on an array. A row reduction is a function that compresses all the rows (that is, the elements selected by running through all the legal values of the last dimension) to a single element. Effectively, this macro reduces an array of arity3 n to an array of arity n-1 by eliminating the last dimension.

Reduction is not accomplished by simply throwing away the data in the last dimension (although it's possible to do this). Instead, you've got to supply some code that the array.reduce macro will use to compress each row in the array.

A very common reduction function, for example, is addition. Reduction by addition produces a new array that contains the sums of the rows in the previous array. For example, consider the following matrix:

 

1 2 3 4

6 5 4 1

5 9 8 0

 

This is a 3x4 array. Reducing it produces a one dimensional array with three elements containing the value 10, 16, 22 (the sums of each of the above rows).

The best way to understand how the array.reduce macro works is to manual implement addition reduction manually. To reduce the 3x4 array above to a single array with three elements, you could use the following code:

 

// (a) Any initialization required before loops

// (this example requires no such initialization.)

 

for( mov( 0, i ); i < 3; inc( i )) do

 

mov( 0, eax ); // (b) Initialize sum for each row.

 

for( mov( 0, j); j < 4; inc( j )) do

 

// (c) Sum up each element in this row into EAX:

 

index( ebx, array3x4, i, j );

add( [ebx], eax );

 

endfor;

 

// (d) At the end of each row, store the sum away

// into the destination array.

 

mov( i, ebx );

mov( eax, array3[ ebx*4 ]);

 

endfor;

 

The array.reduce macro is an example of an HLA context-free macro construct. This means that the call to array.reduce consists of multiple parts, just like the REPEAT..UNTIL and SWITCH..CASE..ENDSWITCH control structures. Specifically, an array.reduce invocation consists of the following sequence of macro invocations:

 

array.reduce( srcArray, destArray );

 

<< Initialization statements needed before

loops,(a) in the code above >>

 

array.beforeRow;

 

<< Initialization before each row, (b) in the

code above. Note that edi contains the row

number times the size of an element and esi contains

an index into the array to the current element. >>

 

array.reduction;

 

<< Code that compresses the data for each

row, to be executed for each element

in the row. Corresponds to (c) in the

the code above. Note that ecx contains

the index into the current row. >>

 

array.afterRow;

 

<< Code to process the compressed data at

the end of each row. Corresponds to (d)

in the code above. >>

 

array.endreduce;

 

 

A conversion of the previous code to use the array.reduce macro set looks like the following:

 

array.reduce( array3x4, array3 )

 

// No pre-reduction initialization...

 

array.beforeRow

 

mov( 0, eax ); // Initialize the sum for each row.

 

array.reduction

 

add( array3x4[esi], eax );

 

array.afterRow

 

mov( i, edx );

mov( eax, array3[ edx*4 ]);

 

array.endreduce;

 

Note that the array.reduce macro set makes extensive use of the 80x86 register set. The EAX and EDX registers are the only free registers you can use (without restoring) within the macro. Of course, array.reduce will preserve all the regsiters is uses, but within the macro itself it assumes it can use all registers except EAX and EDX for its own purposes.

#macro array.transpose( srcArray, destArray, optionalDimension);

The array.transpose macro copies the source array to the destination array transposing the elements of the last dimension with the dimension specified as the last parameter. For the purposes of this macro, the array dimensions of an n-dimensional array are numbered as follows:

 

SomeArray[ n-1, n-2, ..., 3, 2, 1, 0 ];

 

Therefore, array.transpose will transpose dimension zero with some other dimension (1..n-1) in the source array when copying the data to the destination array. By default (if you don't supply the optional, third parameter), array.transpose will transpose dimensions zero and one when copying the source array to the destination array.

The source and destination arrays must have at least two dimensions. They can be static or dynamic arrays. Note that array.transpose emits special, efficient, code when transposing dimensions zero and one.

The source and destination arrays must have compatible shapes. The shapes are compatible if the arrays have the same number of dimensions and all the dimensions have the same bounds except dimension zero and the transpose dimension (which must be swapped). For example, the following two arrays are transpose-compatible when transposing dimensions zero and two:

 

static

s: uns32[ 2, 2, 3];

d: uns32[ 3, 2, 2];

 

Generally, one uses array.transpose to transpose a two-dimensional matrix. However, the transposition operation is defined for any number of dimensions. To understand how array.transpose works, it is instructive to look at the code you'd write to manually transpose the data in an array. Consider the transposition of the data in the s and d arrays above:

 

for( mov(0, i); i<2; inc(i)) do

 

for( mov(0,j); j<2; inc(j)) do

 

for( mov(0,k); k<3; inc(k)) do

 

index( edx, s, i, j, k );

mov( [edx], eax );

index( edx, d, k, j, i );

mov( eax, [edx] );

 

endfor;

 

endfor;

 

endfor;

 

Note that when storing away the value into the destination array, the i and k indicies were swapped. The following example demonstrates the use of array.transpose :

 

static

s: uns32[2,3] := [1,2,3,4,5,6];

d: uns32[3,2];

 

.

.

.

array.transpose( s, d );

.

.

.

 

note: The code above copies s, as

 

1 2 3

4 5 6

 

to d, as

 

1 4

2 5

3 6

 

Bit Manipulation (bits.hhf)

The HLA BITS module contains several procedures useful for bit manpulation. Currently, this includes routines like counting bits, reversing bits, and merging bit streams.

Bit Counting Functions
bits.cnt( b:dword ); returns( "EAX" );

This procedure returns the number of one bits present in the "b" parameter. It returns the count in the EAX register. To count the number of zero bits in the parameter value, invert the value of the parameter before passing it to bits.cnt . If you want to count the number of bits in a 16-bit operand, simply zero extend it to 32 bits prior to calling this function. Here are a couple of examples:

 

// Compute the number of bits in a 16-bit register:

 

pushw( 0 );

push( ax );

call bits.cnt;

 

// If you prefer to use a higher-level syntax, try the following:

 

bits.cnt( #{ pushw(0); push(ax); }# );

 

// Compute the number of bits in a 16-bit memory location:

 

pushw( 0 );

push( mem16 );

bits.cnt;

 

If you want to compute the number of bits in an eight-bit operand it's probably faster to write a simple loop that rotates all the bits in the source operand and adds the carry into the accumulating sum. Of course, if performance isn't an issue, you can zero extend the byte to 32 bits and call the bits.cnt procedure.

Note: to count the number of zero bits in an object, first invert than object and then call bits.cnt .

Bit Movement, Insertion, and Extraction Functions
bits.reverse32( b:dword ); returns( "EAX" );

bits.reverse16( b:word ); returns( "AX" );

bits.reverse8( b:byte ); returns( "AL" );

These three routines return their parameter with the bits reversed.

 
Bit Reversal Operation
bits.merge32( even:dword; odd:dword ); returns( "EDX:EAX" );

bits.merge16( even:word; odd:word ); returns( "EAX" );

bits.merge8( even:byte; odd:byte ); returns( "AX" );

These routines merge two streams of bits to produce a value whose size is the combination of the two parameters. The bits from the even parameter occupy the even bits in the result, the bits from the odd parameter occupy the odd bits in the result. Note that the result is always twice as long as the individual parameters.

 
Bit Merge Operation
bits.nibbles32( d:dword ); returns( "EDX:EAX" );

bits.nibbles16( w:word ); returns( "EAX" );

bits.nibbles8( b:byte ); returns( "AX" );

These routines extract each nibble from the parameter and place those nibbles into individual bytes. nibbles8 extracts the two nibbles from the b parameter and places the L.O. nibble in AL and the H.O. nibble in AH. nibbles16 extracts the four nibbles from the w parameter and places the four nibbles in the four bytes of the EAX register. The nibbles32 function spreads the eight nibbles in the d parameter across the eight bytes in EDX:EAX.

 
Nibble Extraction Operation
procedure bits.extract( var d:dword );

returns( "EAX" ); // Really a macro.

This function extracts the first set bit in d searching from bit #0 and returns the index of this bit in the EAX register; the function will also return the zero flag clear in this case. This function also clears that bit in the operand. If d contains zero, then this function returns the zero flag set and EAX will contain -1.

Note that HLA actually implements this function as a macro, not a procedure. This means that you can pass any double word operand as a parameter (i.e., a memory or a register operand). However, the results are undefined if you pass EAX as the parameter (since this function returns the bit number in EAX).

 
Bit Extraction
bits.distribute( source:dword; mask:dword; dest:dword );

returns( "EAX" );

This function takes the L.O. n bits of source, where n is the number of "1" bits in mask , and inserts these bits into dest at the bit positions specified by the "1" bits in mask . This function does not change the bits in dest that correspond to the zeros in the mask value. This function does not affect the value of the actual dest parameter, instead, it returns the new value in the EAX register.

Example:

source = $FF00_AA55

mask = $F0FF_000F

dest = $1234_5678

The bits.distribute function grabs the L.O. 16 bits of source and inserts these bits at positions 0, 1, 2, 3, 16, 17, 18, 19, 20, 21, 22, 23, and 28, 29, 30, and 31 into the value $1234_5678 and returns the result in EAX. For this example, bits.distribute begins by grabbing the L.O. four bits of source and inserts them at bit positions 0..3 of $1234_5678 (since mask contains four set bits at positions 0..3). This yields the temporary result $1234_5675. Next, bits.distribute grabs bits 4..11 from source ($A5) and inserts these bits into bit positions 16..23 since mask contains eight consecutive bits between positions 16 and 23. The produces the temporary result $12A5_5675. Finally, bits.distribute grabs bits 12..15 from the source operand ($A) and inserts these into the result at bit positions 28..31 (since the mask value contains set bits at these positions). The final result this function returns in EAX is $A2A5_5675.

 
Bit Distribution
bits.coalese( source:dword; mask:dword );

returns( "EAX" );

This function is the converse of bits.distribute . It extracts all the bits in source whose corresponding positions in mask contain a one. This function coalesces (right justifies) these bits in the L.O. bit positions of the result and returns the result in EAX.

Example:

source = $afff_ffce

mask = $aaaa_5555

bits.coalesce grabs bits 0, 2, 4, 6, 8, 10, 12, 14, 17, 19, 21, 23, 25, 27, 29, and 31 from source and packs these into the L.O. 16 bits of EAX (it also sets the H.O. bits of EAX to zero). The final result in EAX is $FFFA.

 
Bit Coalesce Operation
Character Classification and Utilities Module (chars.hhf)

The HLA CHARS module contains several procedures that classify and convert various character subtypes. Conversion routines include upper and lower case conversion. Classification routines include checking for alphabetic characters, numeric characters, whitespace characters, etc.

Conversion Functions
chars.toUpper( c:byte ); returns( "AL" );

This routine returns the character passed as a parameter in the AL register. If the character passed as a parameter was a lower case alphabetic character, this procedure converts it to upper case before returning it.

chars.toLower( c:byte ); returns( "AL" );

This routine returns the character passed as a parameter in the AL register. If the character passed as a parameter was an upper case alphabetic character, this procedure converts it to lower case before returning it.

Predicates (Tests)

The following functions test characters in the seven-bit ASCII character set. These functions produce undefined results for other character sets.

chars.isAlpha( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is an alphabetic character.

chars.isUpper( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is an upper case alphabetic character.

chars.isLower( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is a lower case alphabetic character.

chars.isAlphaNum( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is an alphanumeric character.

chars.isDigit( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is a decimal digit character.

chars.isXDigit( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is a hexadecimal digit character.

chars.isGraphic( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is a printable character (this excludes spaces and control characters; also, this function only applies to ASCII characters).

chars.isSpace( c:byte ); returns( "EAX" );

This routine returns true in the EAX register if the parameter is a white space character. A white space character is a space, carriage return, linefeed, or tab character.

chars.isASCII( c:byte ); returns( "EAX" );

This routine returns true in EAX if the parameter byte is an ASCII character (value in the range $0..$7F).

chars.isCtrl( c:byte ); returns( "EAX" );

This function returns true in EAX if the parameter is a control character ($0..$1F or $7F).

Character Sets (cset.hhf)

The HLA Standard Library contains several routines that provide the power of the HLA compile-time character set facilities at run-time (i.e., within your programs).

HLA uses a 128-bit bitmap (16 consecutive bytes) to implement sets of seven-bit ASCII characters. This has a very important implication: you cannot pass byte values greater than $7F to a character set function. Currently, the HLA Standard Library routines do not check for values out of range (for performance reasons). In the future, this checking may be added as a compilable option. For the time being, however, it is your responsibility to verify that all character values are in the range #$0..#$7F (and, in general, #$0 is an exceeding bad value to specify in many cases since the null character terminates strings).

The bitmap consists of 128 consectutive bits numbered 0..127. If a bit in a character set is one, then the corresponding character (whose ASCII code matches the bit number) is a member of the character set. Conversely, if a bit is zero, the corresponding character is not a member of the set.

Note that many routines pass character sets by value. This means you can pass HLA character set constants as parameters to these procedures/functions. HLA emits four MOV (doubleword) instructions to copy a character set by value, so passing character sets by value is not horribly

inefficient (though not quite as fast as a 32-bit integer!).

Warning: All of the character set routines are members of the cs namespace. This means you cannot use the name cs within your programs. ( cs is a common character set name that lazy programmers use; sorry, it's already been taken!)

The following sections describe each of the character set routines in the HLA Standard Library.

Predicates (tests)

Although the "returns" value for each of the following functions is "AL", these tests always set EAX to zero or one. Therefore, you may refer to the AL or EAX register after these tests, whichever is more convenient for you. If you use instruction composition and bury one of these function calls in another statement, that statement will use the AL register as the operand.

Note that these functions generally pass their character set parameters by value. This involves pushing 16 bytes on the stack for each cset parameter (typically four push instructions). Keep this in mind if efficiency is your utmost concern.

cs.IsEmpty( src: cset ); returns( "AL" );

This function returns true (1) in the AL register if the specified character set is empty (has no members). It returns false (0) in AL/EAX otherwise.

cs.member( c:char; theSet:cset ); returns( "AL" );

This function returns true (1) or false (0) in AL/EAX if the specified character is a member of the specified character set.

cs.subset( src1:cset; src2:cset ); returns( "AL" );

cs.superset( src1:cset; src2:cset ); returns( "AL" );

cs.psubset( src1:cset; src2:cset ); returns( "AL" );

cs.psuperset( src1:cset; src2:cset ); returns( "AL" );

cs.eq( src1:cset; src2:cset ); returns( "AL" );

cs.ne( src1:cset; src2:cset ); returns( "AL" );

These functions determine if one set is equal to another, or if one set is a subset or superset of another. They all return the boolean values true (1) or false (0) in AL/EAX if the relationship holds.

The cs.subset function returns true if src1 <= src2 (that is, all of src1 's members are members of src2 ).

The cs.psubset (proper subset) function returns true if src1 < src2 (that is, all of src1 's members are members of src2 but src1 <> src2 ).

The cs.ssuperset function returns true if src1 >= src2 (that is, all of src2 's members are members of src1 ).

The cs.spsuperset (proper superset) function returns true if src1 > src2 (that is, all of src2 's members are members of src1 but src2 <> src1 ).

The cs.eq and cs.ne return the appropriate values based upon the equality of the two sets.

Character Set Construction and Manipulation
cs.empty( var dest:cset );

This function clears all the bits in a character set to create the empty set. Note that the single character set parameter is passed by reference.

cs.cpy( src:cset; var dest:cset );

This routine copies the data from the source character set ( src ) to the destination character set ( dest ). Note that the dest set is passed by reference. Although this routine is convenient, you should consider writing a macro to do this same function (copy 16 bytes from src to dest ) if you call this function in time critical sections of your code.

procedure cs.charToCset( c:char; var dest:cset );

The cs.charToCset procedure takes the character passed as a parameter and creates a singleton set containing that character (a singleton is a set with exactly one member). The resulting set is stored into the destination parameter (which is passed by reference).

procedure cs.rangeChar( first:char; last:char; var dest:cset );

This function creates a set whose member range between the first character specified and the last character specified. For example, cs.rangeChar ( 'A', 'Z', UpperCaseSet) will create a character set whose members are the upper case alphabetic characters. Any previous members in the destination set are lost.

procedure cs.strToCset( s:string; var dest:cset );

This function first sets the destination character set to the empty set. Then it "unions in" all the characters found in the string parameter to the destination set.

procedure cs.strToCset2( s:string; offs:uns32; var dest:cset );

This function first sets the destination character set to the empty set. Then it "unions in" all the characters found in the string parameter starting at offset offs to the destination set.

procedure cs.extract( var dest:cset ); returns( "EAX" );

This function removes a single character from the character set and returns that character in the AL register. Currently, this function removes characters by order of their ASCII character codes (that is, each call returns the character in the set with the lowest ASCII code). However, you should not make this assumption. You should assume that this function could return the characters in an arbitrary order. If the specified character set is empty, this routine returns -1 ($FFFF_FFFF) in the EAX register; in all other cases the H.O. three bytes of EAX contain zero upon return.

Note: unlike the HLA compile-time function "@extract", this function actually removes the character from the character set ("@extract" leaves the character in the set). Keep this in mind. (In the future, the name of the HLA @extract function will probably be changed to something else to clean up this conflict.)

Set Operations
cs.setunion( src:cset; var dest:cset );

This function computes the union of two sets, storing the result back into the destination set. Note that the destination set parameter is passed by reference.

Note: The name " setunion " was used rather than the more obvious choice of "union" because "union" is an HLA reserved word.

cs.intersection( src:cset; var dest:cset );

This function computes the set intersection of the two sets passed as parameters and stores the result back into the destination set. Note that the dest parameter is passed by reference.

cs.difference( src:cset; var dest:cset );

This function computes the set difference of two sets (i.e., the members in the destination set that are not also members of the source set). It stores the result back into the dest set (which is passed by reference).

cs.complement( src:cset; var dest:cset );

This function computes the set complement of a set (i.e., the members in the destination set are those elements that are not in the source set.). It stores the complemented version of the set in the destination operand (which is passed by reference).

procedure cs.unionChar( c:char; var dest:cset );

The cs.unionChar function adds the character (supplied as a parameter) to the specified destination character set (passed by reference). If the character was already a member of the set, this function does not affect the character set.

procedure cs.removeChar( c:char; var dest:cset );

This function removes a single character from the specified destination set (passed by reference). If the character was not previously a member of the destination set, this function does not affect that set.

procedure cs.unionStr( s:string; var dest:cset );

This function will union in all the characters in a string to the destination set. Unlike the cs.strToCset function, this function does not clear the destination character set before processing the characters in the string.

procedure cs.unionStr2( s:string; offs:uns32; offs:uns32; var dest:cset );

This function will union in all the characters in a string to the destination set. Unlike the cs.unionStr function, this function starts at character position offs in s rather than at character position zero.

procedure cs.removeStr( s:string; var dest:cset );

This function removes characters found in the string from the specified character set. If a character in the string was not previously a member of the character set, the specified character has no effect on the destination set.

procedure cs.removeStr2( s:string; offs:uns32; var dest:cset );

This function removes characters found in the string at character position offs and beyond from the specified character set. If a character in the string was not previously a member of the character set, the specified character has no effect on the destination set.

Classification and Constants (hla.hhf)

This header file contains numeric constants produced by some of the HLA symbol-table compile-time functions. Please see the HLA.HHF header file for a complete list of the supported constants.

Classification Macros

The hla.hhf module contains some macros that test the type of an identifier at compile time. These macros/functions are the following:

hla.IsUns( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is an uns8, uns16, or uns32 object.

hla.IsInt( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is an int8, int16, or int32 object.

hla.IsHex( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is a byte, word, or dword object.

hla.IsNumber( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is an uns8, uns16, uns32, int8, int16, int32, byte, word, or dword object.

hla.IsReal( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is a real32, real64, or real80 object.

hla.IsNumeric( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is an uns8, uns16, uns32, int8, int16, int32, byte, word, dword, real32, real64, or real80 object.

hla.IsOrdinal( identifier )

This macro returns a compile-time expression that evaluates true if the specified identifier is an uns8, uns16, uns32, int8, int16, int32, boolean, char, byte, word, dword, or enumerated data type object.

@class Constants

The HLA compile-time @class function returns the following values to denote the classification of an identifier. If a symbol appears more than once in a program, the @class function returns the classification value for the symbol currently in scope.

@Class Return Values

Name

Value

Description

hla.cIllegal

0

Symbol doesn't have a legal HLA classification.

hla.cConstant

1

Symbol was defined in the CONST section.

hla.cValue

2

Symbol was defined in the VAL section.

hla.cType

3

Symbol was defined in the TYPE section.

hla.cVar

4

Symbol was defined in the VAR section

hla.cParm

5

Symbol is a parameter.

hla.cStatic

6

Symbol was defined in a STATIC, READONLY, or STORAGE section.

hla.cLabel

7

Symbol is a statement label.

hla.cMacro

8

Symbol is a macro.

hla.cKeyword

9

Symbol is an HLA reserved word.

hla.cTerminator

10

Symbol is an HLA TERMINATOR macro.

hla.cProgram

11

PROGRAM or UNIT identifier.

hla.cProc

12

Identifier is the name of a (non-class) procedure.

hla.cClassProc

13

Identifier is the name of a class procedure.

hla.cClassIter

14

Identifier is the name of a class iterator.

hla.cMethod

15

Identifier is the name of a class method.

hla.cIterator

16

Identifier is the name of a (non-class) iterator.

hla.cNamespace

17

Identifier is a name space ID.

hla.cRegister

18

Identifier is an 80x86 register name.

hla.cNone

19

Reserved.

 

 

HLA pType Constants

The HLA @ptype compile-time function returns the following values for the symbol you pass as a parameter to the function.

 

: @pType Return Values

Symbol

Value

Description

hla.ptIllegal

0

Symbol is undefined or is not an object to which a type can be applied.

hla.ptBoolean

1

Symbol is of type boolean.

hla.ptEnum

2

Symbol is an enumerated type.

hla.ptUns8

3

Symbol is an UNS8 object.

hla.ptUns16

4

Symbol is an UNS16 object.

hla.ptUns32

5

Symbol is an UNS32 object.

hla.ptByte

6

Symbol is a BYTE object.

hla.ptWord

7

Symbol is a WORD object.

hla.ptDWord

8

Symbol is a DWORD object.

hla.ptInt8

9

Symbol is an INT8 object.

hla.ptInt16

10

Symbol is an INT16 object.

hla.ptInt32

11

Symbol is an INT32 object.

hla.ptChar

12

Symbol is of type CHAR.

hla.ptReal32

13

Symbol is a REAL32 object.

hla.ptReal64

14

Symbol is a REAL64 object.

hla.ptReal80

15

Symbol is a REAL80 object.

hla.ptString

16

Symbol has the STRING type.

hla.ptCset

17

Symbol's type is CSET.

hla.ptArray

18

The symbol is an array object.

hla.ptRecord

19

The symbol is a record object.

hla.ptUnion

20

The symbol is a union object.

hla.ptClass

21

The symbol is a class object.

hla.ptProcptr

22

The symbol's type is "pointer to a procedure".

hla.ptThunk

23

The symbol is a THUNK type.

hla.ptPointer

24

The symbol is a POINTER object.

hla.ptQWord

25

The symbol is a QWORD object.

hla.ptTByte

26

The symbol is a TBYTE (ten-byte) object.

hla.ptLabel

27

The symbol is a statement label object.

hla.ptProc

28

The symbol denotes a procedure.

hla.ptMethod

29

The symbol denotes a method.

hla.ptClassProc

30

The symbol is a procedure within a class.

hla.ptClassIter

31

The symbol denotes an iterator within a class.

hla.ptProgram

32

The symbol is the program's or unit's identifier.

hla.ptMacro

33

The identifier is a macro.

hla.ptText

34

The identifier is a text object (note: @ptype does not return this value since HLA expands the text prior to processing by @ptype).

hla.ptNamespace

35

The identifier is a namespace ID.

hla.ptSegment

36

The identifier is a segment ID.

hla.ptAnonRec

37

The identifier is an anonymous record within a union (internal use only, @ptype will never return this value).

hla.ptVariant

38

This value is reserved for internal use by HLA.

hla.ptError

39

This value indicates a cascading error in an expression. Generally, you will not get this value from @ptype unless there was some sort of error in the parameter to pass to @ptype.

 

@pClass Return Values

The HLA @pClass function expects a procedure's parameter name as its sole parameter. It returns one of the following constants that denotes the parameter passing mechanism for the parameter. Note that @pClass' return values are defined only for parameter identifiers.

 

@pClass Return Values

Symbol

Value

Description

hla.illegal_pc

0

May be returned if the symbol is not a parameter.

hla.valp_pc

1

Returned if parameter is passed by value.

hla.refp_pc

2

@pClass returns this value if you pass the parameter by reference.

hla.vrp_pc

3

Denotes that you've passed the parameter by value/result.

hla.result_pc

4

This value means that you've passed the parameter by result.

hla.name_pc

5

This value indicates that you've passed the parameter by name.

hla.lazy_pc

6

This value indicates that you've passed the parameter by lazy evaluation.

 

@section Return Results

The following constants correspond to bits in the value returned by @section. They denote the current position of the compiler in the code.

 

 

Symbol

Value

Description

hla.inConst

1

Bit zero is set if HLA is current processing definitions in a CONST section.

hla.inVal

2

Bit one is set if HLA is current processing definitions in a VAL section.

hla.inType

4

Bit two is set if HLA is current processing definitions in a TYPE section.

hla.inVar

8

Bit three is set if HLA is current processing definitions in a VAR section.

hla.inStatic

$10

Bit four is set if HLA is current processing definitions in a STATIC section.

hla.inReadonly

$20

Bit five is set if HLA is current processing definitions in a READONLY section.

hla.inStorage

$40

Bit six is set if HLA is current processing definitions in a STORAGE section.

hla.inMain

$1000

Bit 12 is set if HLA is current processing statements in the main program.

hla.inProcedure

$2000

Bit 13 is set if HLA is current processing statements in a procedure.

hla.inMethod

$4000

Bit 14 is set if HLA is current processing statements in a method.

hla.inIterator

$8000

Bit 15 is set if HLA is current processing statements in an iterator.

hla.inMacro

$1_0000

Bit 16 is set if HLA is current processing statements in a macro.

hla.inKeyword

$2_0000

Bit 17 is set if HLA is current processing statements in a keyword macro.

hla.inTerminator

$4_0000

Bit 18 is set if HLA is current processing statements in a terminator macro.

hla.inThunk

$8_0000

Bit 19 is set if HLA is current processing statements in a thunk's body.

hla.inUnit

$80_0000

Bit 23 is set if HLA is current processing statements in a unit.

hla.inProgram

$100_0000

Bit 24 is set if HLA is current processing statements in a program (not a unit).

hla.inRecord

$200_0000

Bit 25 is set if HLA is current processing declarations in a record definition.

hla.inUnion

$400_0000

Bit 26 is set if HLA is current processing declarations in a union.

hla.inClass

$800_0000

Bit 27 is set if HLA is current processing declarations in a class.

hla.inNamespace

$1000_0000

Bit 28 is set if HLA is current processing declarations in a union.

 

hla.genLabel Macro

The hla.genlabel macro generates a sequence of strings that are unique, legal, HLA identifiers (within the current compilation, do not use these as public symbols). Typically, you would take the string that this macro returns and convert that string to an actual symbol using the @TEXT function.

Here's the definition of getLabel in the HLA header file:

 

val

_hla_labelCnt_ := 0;

 

 

#macro genLabel;

 

"_genLabel_" + string( hla._hla_labelCnt_ ) + "_"

?hla._hla_labelCnt_ := hla._hla_labelCnt_ + 1;

 

#endmacro;

 

Command Line Arguments (args.hhf)

The HLA args module provides access to, and support for, Windows Command Line Interpreter or Linux shell command line parameters. This library module provides the following routines:

arg.cmdLn; returns( "eax" );

Returns a string containing the entire command line (including the program name). Returns a pointer to the string in EAX.

arg.c; returns( "eax" );

Returns the number of command line parameters in EAX. Note that the program name is part of this count.

arg.v( index ); returns( "eax" );

Returns a string corresponding to the specified (by index ) command line parameter. If index is greater than or equal the number of command line parameters, this procedure raises an ex.BoundsError exception. Note that arg.v (0) returns the program's invocation name. The arg.v (1) call returns a string to what most people would consider to be the first command line parameter.

arg.delete( index:uns32 )

Deletes the specified command line parameter from the arg.v array and decrements arg.c by one. Note that this procedure does not affect the string returned by arg.CmdLn . This procedure raises an ex.BoundsError exception if index is greater than or equal to the arg.c value.

arg.globalOptions( options:cset)

This is an iterator (i.e., you use it in a FOREACH loop) that yields a sequence of command line parameter options. A command line parameter option is a command line parameter that begins with a '-' or '/' character. arg.globalOptions only returns those command line parameters whose first character is a member of the "options" character set.

A typical command line might be something like the following:

c:> pgmName -o2 -warn filename1 -c filename2 -d filename3 -x

 

The command line options in this example are "-o2", "-warn", "-c", "-d", and "-x". The arg.globalOptions iterator only considers those command line parameters that begin with "-" or "/" and whose first character is a member of the options parameter. Assuming options contains at least {'c', 'd', 'o', 'w', 'x'} then the arg.globalOptions iterator will return the strings "o2", "warn", "c", "d", and "x", in that order (that is, in the order they appear on the command line). If the first character of a command line option is not in the options character set, then arg.globalOptions does not return that particular command line parameter.

Note that arg.globalOptions does not remove the command line parameters from the command line string or from the arg.v array. If you want to remove them, you must explicitly do so using the arg.delete function. Note, however, that you must not delete command line arguments while scanning through the arguments in a FOREACH loop using the arg.globalOptions iterator.

[comment attached *** Here I am]

arg.localOptions( index:uns32; options:cset );

This is an iterator that yields the sequence of command line options begining at parameter " index ". This iterator only yields strings as long as successive parameters begin with a "-" or "/". It fails upon encountering a command line parameter that is not an option (that is, begins with "-" or "/"). Note that this iterator only yields those command line options whose first character beyond the "-" or "/" character is a member of the options character set.

Typically, you would use the arg.localOptions iterator inside a FOREACH loop to obtain the command line parameters for a specific filename on the command line. That is, some programs process multiple files and let you associate command line parameters with a single filename. Consider the following simple example:

c:> pgm -o2 -c file1 -o5 file2 -c file3

 

In this example, the "pgm" program (presumably) associates "-o2" and "-c" with file1 , "-o5" with file2 , and "-c" with file3 .

Were you to call arg.localOptions as follows:

foreach arg.localOption( 1, {'o', 'c'} ) do ... endfor;

 

then the arg.localOption iterator would return two strings: the first would be "o2" and the second would be "c". Within that iterator your code should save these options and count the number of command line parameters processed so it will know the index of the associated filename command line parameter once it is done processing the options. Typically, you would bury this FOREACH loop (with some minor modifications) inside some other loop that processes each filename (or other command line parameter preceded by command line options).

See the CmdLnDemo.hla file in the Examples directory of the HLA distribution for an example of each of these routines.

 

The Console Module (console.hhf)

The HLA Console module provides support for text applications running in a Win32 console window. Currently the console module has not been ported to Linux. A Linux console module is planned for the furtuer, although Linux does not support anywhere near the console features that Windows supports; so expect an abbreviated module and don't expect too much portability from this module to Linux.

The routines in this module let you write "really-smart-terminal" applications.

procedure console.info( var csbi:win.CONSOLE_SCREEN_BUFFER_INFO );

This procedure returns information about the current console window in the csbi (console screen buffer info) data structure. The CONSOLE_SCREEN_BUFFER_INFO data structure takes the following form:

 

CONSOLE_SCREEN_BUFFER_INFO:

record

 

dwSize: win.COORD;

dwCursorPosition: win.COORD;

wAttributes: word;

srWindow: win.small_rect;

dwMaximumWindowSize: win.COORD;

 

endrecord;

 

dwSize.X and dwSize.Y provide the width and height (in character positions) of the current console window.

dwCursorPosition.X and dwCursorPosition.Y provide the coordinates of the current cursor location.

wAttributes is the screen attributes value used when writing character to the display. Bits 0..3 are the foreground colors, bits 4..7 are the background colors, as defined by the following constants:

 

bgnd_Black := $00;

bgnd_Blue := $10;

bgnd_Green := $20;

bgnd_Cyan := $30;

bgnd_Red := $40;

bgnd_Magenta := $50;

bgnd_Brown := $60;

bgnd_LightGray := $70;

bgnd_DarkGray := $80;

bgnd_LightBlue := $90;

bgnd_LightGreen := $a0;

bgnd_LightCyan := $b0;

bgnd_LightRed := $c0;

bgnd_LightMagenta := $d0;

bgnd_Yellow := $e0;

bgnd_White := $f0;

 

fgnd_Black := $00;

fgnd_Blue := $01;

fgnd_Green := $02;

fgnd_Cyan := $03;

fgnd_Red := $04;

fgnd_Magenta := $05;

fgnd_Brown := $06;

fgnd_LightGray := $07;

fgnd_DarkGray := $08;

fgnd_LightBlue := $09;

fgnd_LightGreen := $0a;

fgnd_LightCyan := $0b;

fgnd_LightRed := $0c;

fgnd_LightMagenta := $0d;

fgnd_Yellow := $0e;

fgnd_White := $0f;

 

srWindow.top , srWindow.left , srWindow.bottom , and srWindow.right provide the coordinates of this window on the screen.

dwMaximumWindowSize.X and dwMaximumWindowSize.Y provide the maximum width and height values of the current screen (in character units).

Cursor Positioning Functions
procedure console.getX; returns( "eax" );

Returns the current cursor X position in the EAX register.

procedure console.getY; returns( "eax" );

Returns the current cursor Y position in the EAX register.

procedure console.gotoxy( y:word; x:word );

Positions the cursor at the specified (x,y) coordinate. Note that the parameters are reversed (y, or the row value, comes first; x, or the column value, comes second). This was done because people intuitively write (row,column) rather than (column, row).

Console Clearing Functions
procedure console.cls;

This procedure clears the screen and moves the cursor to the home (0,0) position.

procedure console.clrToEOLN;

This procedure clears the text (by writing spaces) from the current cursor position to the end of the line that the cursor is on.

procedure console.clrToEOScrn;

This procedure clears the screen from the current cursor position to the end of the screen.

Character Insertion/Removal
procedure console.insertChars( n:word );

This procedure inserts room for n characters by shifting the characters under the cursor and to the right of the cursor right n positions. The vacated positions are filled with spaces. The last n characters on the line are lost.

procedure console.insertLn;

This procedure inserts a blank line before the line the cursor is on by pushing the line under the cursor, and the lines below the cursor, down one line on the screen. The new line is filled with blanks. The last line on the screen is lost.

procedure console.insertLines( n:word );

This procedure opens up n new blank lines at the current cursor position by pushing the lines at and below the cursor down n lines on the screen. The last n lines on the screen will be lost.

procedure console.deleteChars( n:word );

This procedure deletes n characters under and to the right of the cursor by shifting the characters after the cursor n positions to the left. The n character positions at the end of the line are filled with blanks.

procedure console.deleteLn;

This procedure deletes the line the cursor is one by shifting all the lines below the cursor position up one line. The last line on the screen is filled with blanks.

procedure console.deleteLines( n:word );

The console.deleteLines procedure deletes n lines at and below the current cursor position.

Console Rectangular Operations
procedure console.fillRect

(

top: word;

left: word;

bottom: word;

right: word;

charToWrite: char;

Attribute: word

);

This procedure fills the rectangle specified by ( top, left ) and ( bottom, right ) with the specified character and attribute. The attribute value is a combination of the color constants mentioned earlier, e.g., win.fgnd_Red or win.bgnd_White .

procedure console.fillRectAttr

(

top: word;

left: word;

bottom: word;

right: word;

Attribute: word

);

This function fills the specified rectangular region with the Attribute value. It does not affect the characters within the specified rectangle.

procedure console.getRect

(
top:word;
left:word;
bottom:word;
right:word;
var buf:byte
);

This routine fetches a rectangular block of characters from the screen. It stores the characters into the specified buffer (buffer must be large enough to hold the result; no range checking is done.

procedure console.a_getRect

(
top:word;
left:word;
bottom:word;
right:word
);

This routine also fetches a rectangular block of characters from the screen; however, this routine automatically allocates storage for the block on the heap and returns a pointer to the block in the EAX register. You must free this storage by calling free when you are done with it.

procedure console.putRect

(
top:word;
left:word;
bottom:word;
right:word;
var buf:byte
);

This routine outputs a rectangular block of characters (generally created using console.getRect or console.a_getRect ) to the display.

procedure console.scrollUpRect

(
n:uns16;
fill:char;
attr:word;
top:word;
left:word;
bottom:word;
right:word
);

This procedure scrolls the specified rectangular area of the screen up n lines, filling the vacated lines with the specified character and attribute.

procedure console.scrollDnRect

(
n:uns16;
fill:char;
attr:word;
top:word;
left:word;
bottom:word;
right:word
);

This procedure scrolls the specified rectangular portion of the screen down n lines, filling the vacated lines with fill and attr .

Console Output Options
procedure console.setOutputAttr( Attribute:word );

This procedure sets the console internal attribute value to be used for all following character output. Use the routine to set the color of the characters you wish to print.

Console Window Titles
procedure console.setTitle( title:string );

This procedure changes the title string on the console window to the specified value.

procedure console.getTitle( title:string );

This procedure retrieves the current title string and stores it into the specified string variable. The destination string must be large enough to hold the result or an ex.StringOverflow exception occurs.

procedure console.a_getTitle; returns( "eax" );

This procedure also retrieves the current console title bar string. However, this routine allocates storage for the string on the heap and passes the address of the string back in the EAX register.

Console Input Functions
procedure console.peekInput( var input:win.INPUT_RECORD ); returns( "eax" );

This routine returns true (in EAX) if there are any "input events" pending for the current console. If at least one event is pending, then this function returns a "peek" at that event in the "input" parameter. This call does not remove the input from the console event queue. If no input events are pending, this function immediately returns false. The win.INPUT_RECORD and related data structures take the following form:

 

KEY_EVENT_RECORD:

record

_padding_: word;

bKeyDown: boolean[4];

wRepeatCount: word;

wVirtualKeyCode: word;

wVirtualScanCode: word;

AsciiChar: char[2];

dwControlKeyState: word;

 

endrecord;

 

MOUSE_EVENT_RECORD:

record

 

_padding_: word;

dwMousePosition: COORD;

dwButtonState: dword;

dwControlKeyState: dword;

dwEventFlags: dword;

 

endrecord;

 

 

 

INPUT_RECORD:

record

 

EventType: word;

Event: union

 

KeyEvent: KEY_EVENT_RECORD;

MouseEvent: MOUSE_EVENT_RECORD;

WindowBufferSizeEvent: COORD;

 

endunion;

endrecord;

 

 

procedure console.readInput( var input: win.INPUT_RECORD );

The console.readInput procedure returns the first console event appearing at the front of the console event queue. If no such events are available, this routine waits until an event occurs. See peekInput for a description of the INPUT_RECORD format and other details.

procedure console.flushInput;

This procedure flushes all the events from the console event queue.

procedure console.getMode; returns( "eax" );

This function returns, in EAX, the following mode bits:

 

ENABLE_PROCESSED_INPUT := $01;

ENABLE_LINE_INPUT := $02;

ENABLE_ECHO_INPUT := $04;

ENABLE_WINDOW_INPUT := $08;

ENABLE_MOUSE_INPUT := $10;

 

procedure console.setMode( theMode:dword );

This function sets the current input mode, using a combination of the same mode bits described above.

procedure console.numButtons; returns( "eax" );

This function returns the number of mouse buttons in the EAX register.

procedure console.gets( y:dword; x:dword; len:dword; s:string );

This procedure captures a string of characters from the screen starting at the specified coordinate of the specified length. The string is stored into the s variable, which must be large enought to hold the string.

procedure console.a_gets( y:dword; x:dword; len:dword ); returns( "eax" );

Like gets above, except this routine allocates storage on the stack for the string. The storage must be freed up using the strfree function.

procedure console.getc( y:dword; x:dword ); returns( "al" );

This procedure retrieves the character at position ( x,y ) on the screen and returns it in AL.

Console Output Functions
procedure console.puts( y:word; x:word; s:string );

This function writes the specified string to the row,column coordinates specified by

the y and x parameters.

procedure console.putsx( y:word; x:word; attr:word; len:word; s:string );

This procedure also writes a string to the display at the selected coordinate. However, this call also lets you set the size of the string (padding with space or truncating, as necessary) and the attribute used to write the characters to the display.

Console Scrolling Functions
procedure console.scrollUp;

This procedure scrolls the entire screen up one line.

procedure console.scrollUpN( n:uns16 );

This procedure scrolls the screen up n lines.

procedure console.scrollUpx( fill:char; attr:word; n:uns16 );

This procedure scrolls the screen up one line, filling the vacated line with the specified character and attribute.

procedure console.scrollDn;

This routine scrolls the screen down one line. The vacated line at the top of the screen is blank filled.

procedure console.scrollDnN( n:uns16 );

This procedure scrolls the screen down n lines.

procedure console.scrollDnx( fill:char; attr:word; n:uns16 );

This procedure scrolls the screen down n lines, filling the vacated lines with the specified character and attribute.

Control Structures (hll.hhf)

The hll.hhf library module adds a switch/case/default/endswitch statement that is similiar to the C/C++ switch statement.

The switch/case/default/endswitch Statement

A commonly used high level language statement missing from HLA's basic set is the the C/C++ switch statement (the case statement in most other languages). The SWITCH/CASE/DEFAULT/ENDSWITCH macro set in the hll.hhf header file provides this missing HLL statement.

The HLL module's switch statement actually provides two different user-selectable syntaxes. The first is a Pascal-like syntax. It takes the following form:

 

switch( reg32 )

 

case( constant_list )

<<body>>

 

<< additional, optional cases >>

 

default // This section is optional too!

<< body >>

 

endswitch;

 

As you might expect, the reg32 parameter has to be an 80x86 32-bit general purpose register. The constant_list operand has to be a sequence of one or more positive ordinal constants. There must be at least one case present in the statement ( default does not count as a case) and there may be a maximum of 256 cases in the switch statement. Furthermore, the range between the largest and smallest values for all the cases must be less than or equal to 1,024. Note that, unlike C/C++, you do not end each case with a break statement; nor does control fall through from one case to the next. Here is a simple example of a Pascal-like switch statement:

 

switch( ebx )

 

case( 1 )

stdout.put( "case 1 encountered" nl );

 

case( 3 )

stdout.put( "case 3 encountered" nl );

 

case( 10 )

stdout.put( "case 10 encountered" nl );

 

case( 15, 20, 25 )

stdout.put( "case 15, 20, or 25 encountered" nl );

 

default

stdout.put( "Some other case was encountered" nl );

 

endswitch;

 

Although C/C++ semantics for a switch statement are stylistically inferior to Pascal, some people might prefer a C/C++ version of the switch statement. The HLL switch statement uses a special predefined boolean VAL constant, hll.cswitch , that lets you choose C/C++ semantics. By default, the hll.cswitch constant is set to false. By placing the statement "?hll.cswitch:=true;" before a switch statement, you can instruct the switch macro to use C/C++ semantics rather than Pascal semantics. The difference between the two is that for C/C++ semantics you must end each case with an explicit break statement. The Pascal version is preferable since it is slightly more efficient and a bit more readable.

Conversions (conv.hhf)

This HLA unit contains routines that perform general conversions from one data type to another. Primarily, this unit supplies the routines that convert HLA data types to and from string form.

Conversion Format Control
conv.setUnderscores( OnOff: boolean )
conv.getUnderscores; returns( "eax" );

When converting numeric data types to strings, HLA offers the option of inserting underscores at appropriate places in the numbers (i.e., where you would normally expect a comma to go). This feature in the library can be activated or deactivated with the conv.setUnderscores function. A parameter value of true activates this feature, false deactivates it.

You can test the current state of the underscore conversion by calling conv.getUnderscores which returns the boolean result in EAX (true means underscores will be output).

conv.setDelimiters( Delims: cset )

conv.getDelimiters( var Delims: cset )

During the conversion from string to a numeric form, HLA will generate an exception if it encounters a character that is not a numeric digit in the specified base or the character is not an appropriate delimiter character. By default, the delimiter characters are members of the following set:

 

Delimiters: cset :=

{

#0, #9, #10, #13,

' ',

',',

';',

':'

};

 

You can obtain the current delimiter set by calling the conv.getDelimiters function. You can change the current set of delimiter characters by calling the conv.setDelimiters function.

Hexadecimal Conversions
conv.byteToHex( b:byte in al );

This is an internal routine intended for use by the conversions unit. It converts the byte in AL to two hex characters and outputs them starting at location [EDI]. You should use the conv.hToBuf routine rather than this routine. Unlike conv.hToBuf , this routine does not preserve the value in the EAX register.

Hexadecimal Numeric to String Convesions
conv.bToStr ( b:byte; size:dword; fill:char; buffer:string );

conv.wToStr ( w:word; size:dword; fill:char; buffer:string );

conv.dToStr ( d:dword; size:dword; fill:char; buffer:string );

conv.qToStr ( q:qword; size:dword; fill:char; buffer:string );

conv.tbToStr( tb:tbyte; size:dword; fill:char; buffer:string );

conv.lToStr( tb:tbyte; size:dword; fill:char; buffer:string );

These routines are the general-purpose hexadecimal conversion routines. They convert their first parameter to a string of characters and store those character into the string variable passed as the last parameter. The size parameter specifies the minimum string length for the conversion. If the number requires more than this many character positions, the conversion routine will attempt to use however many characters as necessary. The string's maximum length must be large enough to hold the full result or an exception will occur. If the string conversion requires fewer than size characters, these routine right justify the value in the string and pad the remaining positions with the fill character.

Hexadecimal String to Numeric Conversions
conv.strTob( s:string; index:dword )

conv.strTow( s:string; index:dword )

conv.strTod( s:string; index:dword )

conv.strToq( s:string; index:dword )

conv.strTol( s:string; index:dword; var dest:lword )

These routines convert characters from a string to the corresponding numeric forms. The index parameter is the index of the position in the string where conversion begins.

The eight-bit routines returns its result in AL; the 16-bit routine returns its result in AX; the 32-bit routine returns its result in EAX; the 64-bit routine returns its result in EDX:EAX; the 128-bit return returns its result in the destination operand specified as a parameter.

Integer Conversions
Integer Size Calculations
conv.i8Size( b:byte in al )

conv.i16Size( w:word in ax )

conv.i32Size( d:dword in eax )

conv.i64Size( q:qword )

conv.i128Size( l:lword )

conv.u8Size( b:byte in al )

conv.u16Size( w:word in al )

conv.u32Size( d:dword in eax )

conv.u64Size( q:qword )

conv.u128Size( l:lword )

conv.bSize( b:byte );

conv.wSize( w:word );

conv.dSize( d:dword );

conv.qSize( q:qword );
conv.lSize( l:lword );
conv._i8Size
conv._i16Size
conv._i32Size
conv._u8Size
conv._u16Size
conv._u32Size
conv._bSize
conv._wSize
conv._dSize

These routines return the size, in screen print positions, it would take to print the integer (signed, unsigned, or hexadecimal) passed in the specified parameter or in the AL register (_ i8Size/_u8Size/_bSize ), AX register (_ i16Size/_u16Size/_wSize ), or EAX register (_ i32Size/_u32Size/_dSize ). They return their value in the EAX register. The count includes room for a minus sign if the number is negative ( iXXSize routines, only). Note that these routines do not count spaces required by underscores if you've enabled underscore output in values.

Integer Numeric to String Conversions
conv.i8ToBuf( i8:int8 in al )

conv.i16ToBuf( i16: int16 in ax )

conv.i32ToBuf( i32: int32 in eax )

conv.i64ToBuf( q:qword )

conv.i128ToBuf( l:lword )

conv.u8ToBuf( u8: uns8 in al )

conv.u16ToBuf( u16: uns16 in ax )

conv.u32ToBuf( u32: uns32 in eax)

conv.u64ToBuf( q:qword )

conv.u128ToBuf( l:lword )

These routines are passed a parameter in AL ( x8ToBuf ), AX ( x16ToBuf ), EAX ( x32ToBuf ), or on the stack ( xi64ToBuf and x128ToBuf ). These routines convert the input parameter to a sequence of characters and store those characters starting at location [EDI]. They return EDI pointing at the first character beyond the converted string.

conv.i8ToStr ( b:int8; width:int32; fill:char; buffer:string );

conv.i16ToStr( w:int16; width:int32; fill:char; buffer:string );

conv.i32ToStr( d:int32; width:int32; fill:char; buffer:string );

conv.i64ToStr( q:qword; width:int32; fill:char; buffer:string );

conv.i128ToStr( l:lword; width:int32; fill:char; buffer:string );

 

conv.u8ToStr ( b:uns8; width:int32; fill:char; buffer:string );

conv.u16ToStr( w:uns16; width:int32; fill:char; buffer:string );

conv.u32ToStr( d:uns32; width:int32; fill:char; buffer:string );

conv.u64ToStr( q:qword; width:int32; fill:char; buffer:string );

conv.u128ToStr(l:lword; width:int32; fill:char; buffer:string );

 

These routines translate their first parameter to a string that has a minimum of width print positions. If the number would require fewer than width print positions, the routines copy the fill character to the remaining positions in the destination string. If width is positive, the number is right justified in the string. If width is negative, the number is left justified in the string. If the string representation of the value requires more than width print positions, then these functions ignore the width and fill paramenters and use however many positions are necessary to properly display the value.

 
conv.xxxToStr Integer Conversion Format
Integer String to Numeric Conversions
conv.atou( bufPtr: dword in esi );

conv.atoi( bufptr: dword in esi );

conv.atoh( bufptr: dword in esi );

These routines assume that ESI points at a sequence of characters representing an integer, hexadecimal, or floating point value. They convert the characters to a 64-bit numeric form and return the result in EDX:EAX. The atou and atoi routines are almost the same except that atoi allows a leading minus sign. These routines leave ESI pointing just beyond the last converted character.

An exception occurs if the number does not end with a legal delimiter character or does not begin with a legal numeric character.

conv.strTob( s:string; index:dword )

conv.strTow( s:string; index:dword )

conv.strTod( s:string; index:dword )

conv.strToq( s:string; index:dword )

conv.strTol( s:string; index:dword; var dest:lword )

conv.strToi8( s:string; index:dword )

conv.strToi16( s:string; index:dword )

conv.strToi32( s:string; index:dword )

conv.strToi64( s:string; index:dword )

conv.strToi128( s:string; index:dword; var dest:lword )

conv.strTou8( s:string; index:dword )

conv.strTou16( s:string; index:dword )

conv.strTou32( s:string; index:dword )

conv.strTou64( s:string; index:dword )

conv.strTou128( s:string; index:dword; var dest:lword )

These routines convert characters from a string to the corresponding numeric forms. The index parameter is the index of the position in the string where conversion begins. These functions raise an exception if the string of characters (starting at position index) do not begin with a (heaxadeimal or decimal) numeric digit or optional sequence of delimiter characters followed by a numeric digit, or do not end with a delimiter (or the end of string).

The eight-bit routines return their result in AL; the 16-bit routines return their result in AX; the 32-bit routines return their result in EAX; the 64-bit routines return their result in EDX:EAX; and the 128-bit routines return their values in the specified destination operand.

Floating Point Conversions

These functions convert betweeen the three IEEE/Intel floating point formats and their string representation.

Floating Point Numeric to String Conversions, Exponential Form

The floating point numeric to string conversion routines translate the three different binary floating point formats to their string representation. There are two generic classes of these routines: those that convert their values to exponential/scientific notation and those that convert their string to a decimal form.

The conv.e80ToStr , conv.e64ToStr , and conv.e32ToStr routines convert their values to a string using scientific notation. These three routines each have four parameters: the value to convert, the field width of the result, and a destination string. These routines produce a string with the following format:

 
conv.exxToStr Conversion Format

The width parameter specifies the exact number of print positions the value will consume (i.e., the length of the resulting string). The first position holds the sign of the value. This is a space for positive values or a minus sign for negative values. If you do not want a leading space in front of positive values you can either store a "+" over the top of this space character (if the number is zero or positive) or you can call str.trim to remove any leading space.

The exponent field ('x') always uses the minimum number of digits to exactly represent the exponent. If the exponent is non-negative, then these routines preface the value with a '+'. If the exponent is negative, then these functions preface the exponent value with a '-' character.

The minimum field width you should specify is five. This allows one print position for the leading sign character, one digit for the mantissa, the "E", the exponent sign, and one exponent digit. Obviously, values greater than 1E+9 or less than 1E-9 will require additional print positions to handle the additional exponent digits.

The number of fractional digits this routines produce is "(width - 5) - # exponent digits". So you should choose your width according to the expected exponent size and the number of digits you would like to have to the right of the decimal point.

conv.e80ToStr

(

r80: real80;
width: uns32;
buffer: string
)

This function converts the 80-bit extended precision r80 value to its string representation using exponential/scientific notation. This function stores the resulting string in the buffer variable whose MaxLength field must be at least width or this function will raise an exception. Note that the 80-bit extended precision format supports approximately 18 decimal digits of precision. Therefore, any digits beyond the 18th significant digit will contain garbage. Hence, your choice of width should not produce more than 18 mantissa digits.

conv.e64ToStr

(
r64: real64;
width: uns32;
buffer: string
)

This function converts the 64-bit double precision r64 value to its string representation using exponential/scientific notation. This function stores the resulting string in the buffer variable whose MaxLength field must be at least width or this function will raise an exception. Note that the 64-bit double precision format supports approximately 15 decimal digits of precision. Therefore, any digits beyond the 15th significant digit will contain garbage. Hence, your choice of width should not produce more than 15 mantissa digits.

conv.e32ToStr

(
r32: real32;
width: uns32;
buffer: string
)

This function converts the 32-bit single precision r32 value to its string representation using exponential/scientific notation. This function stores the resulting string in the buffer variable whose MaxLength field must be at least width or this function will raise an exception. Note that the 32-bit single precision format supports approximately 6-7 decimal digits of precision. Therefore, any digits beyond the seventh significant digit will contain garbage. Hence, your choice of width should not produce more than seven mantissa digits.

Floating Point Numeric to String Conversions, Decimal Form

Although scientific (exponential) notation is the most general display format for real numbers, real numbers you display in this format are very difficult to read. Therefore, the HLA conversions module also provides a set of functions that convert real values to the decimal string equivalent. Although you cannot (practically) use these decimal conversion routines for all real values, they are applicable to a wide variety of common numbers you will use in your programs.

These functions all require five parameters: the real value to convert, the width of the converted value, the number of digit positions to the right of the decimal point, a padding character, and a destination string to hold the result. These functions convert their values to the following string format:

 
conv.rxxToStr Conversion Format

The width parameter specifies the length of the resulting string. This value must be less than or equal to the destination string's MaxLength value or these functions will raise an exception. The decimalpts parameter to these functions specify the number of digits to the right of the decimal point. If this parameter contains zero, then these functions display the value as an integer (no fractional digits and no decimal point). If this parameter is non-zero, then these routines produce the specified number of decimal digits along with a decimal point.

The width parameter specifies the total size of the resulting string. If decimalpts is zero, then the width value must be at least one greater than the number of digits that appear to the left of the decimal point (the extra position is for the sign character. If the decimalpts parameter is non-zero, then width must be at least ( decimalpts + 2 + # integer digits ). If width is not sufficiently large, then these functions produce a string containing width "#" characters to denote a conversion error.

If the width value is sufficiently large and the decimalpts sufficiently small then these routines will fill the extra print positions using the fill character you pass as a parameter. For example, if you convert the value -1.5 with a width of six, a decimalpts value of two, and a fill character of "*" these routines produce the string "*-1.50".

conv.r80ToStr

(

r80: real80;
width: uns32;
decimalpts: uns32;
fill: char;
buffer: string
)

This function converts the 80-bit extended precision r80 value to its string representation using decimal notation. This function stores the resulting string in the buffer variable whose MaxLength field must be at least width or this function will raise an exception. Note that the 80-bit single precision format supports approximately 18 decimal digits of precision. Therefore, any digits beyond the 18th significant digit will contain garbage. Hence, your choice of width should not produce more than 18 mantissa digits. Do keep in mind that the average person has trouble comprehending value with more than six or seven digits. For values that are routinely outside this range you may want to use exponential form to display the number with a limited number of significant digits.

 

conv.r64ToStr

(
r64: real64;
width: uns32;
decimalpts: uns32;
fill: char;
buffer: string

)

This function converts the 64-bit double precision r64 value to its string representation using decimal notation. This function stores the resulting string in the buffer variable whose MaxLength field must be at least width or this function will raise an exception. Note that the 64-bit single precision format supports approximately 15 decimal digits of precision. Therefore, any digits beyond the 15th significant digit will contain garbage. Hence, your choice of width should not produce more than 15 mantissa digits. Do keep in mind that the average person has trouble comprehending value with more than six or seven digits. For values that are routinely outside this range you may want to use exponential form to display the number with a limited number of significant digits.

conv.r32ToStr

(

r32: real32;
width: uns32;
decimalpts: uns32;
fill: char;
buffer: string

)

This function converts the 32-bit single precision r32 value to its string representation using decimal notation. This function stores the resulting string in the buffer variable whose MaxLength field must be at least width or this function will raise an exception. Note that the 32-bit single precision format supports approximately 6-7 decimal digits of precision. Therefore, any digits beyond the seventh significant digit will contain garbage. Hence, your choice of width should not produce more than seven mantissa digits.

Floating Point String to Numeric Conversions
conv.atof( bufptr: dword in esi ); returns( "st0" );

This routine assumes that ESI is pointing at a sequence of characters that represents a floating point number. The characters are converted to numeric form and the result is returned in ST0. ESI is left pointing at the first character beyond the converted characters. This function raises an exception if the value begins with something other than a standard numeric delimiter character or ends with something other than a standard delimiter character (or the end of string).

This routine accepts floating point input in either decimal or exponential form.

 

conv.strToFlt( s:string; index:dword ); returns( "st0" );

This function converts the sequence of characters starting at position index in s to the equivalent extended precision floating point value and it leaves the result in ST0. This function raises an exception if the value begins with something other than a standard numeric delimiter character or ends with something other than a standard delimiter character (or the end of string).

This routine accepts floating point input in either decimal or exponential form.

Zero Terminated String Conversions
conv.cStrToStr( var buffer:var; dest:string )

This function converts a "C-String" (zero terminated sequence of characters) to an HLA string. The buffer parameter points at the zero terminated string, conv.cStrToStr stores the resulting string into the dest operand.

Note: a function that converts HLA strings to zero-terminated strings is not necessary since HLA strings are already zero-terminated and the string variable points at the first character of the string; hence, HLA strings are already downwards compatible with C-Strings.

This function name is an alias of the str.cpyz function. That is, if you use conv.cStrToStr HLA actually links in the str.cpyz function from the strings module.

conv.a_cStrToStr( var buffer:var )

This function also converts zero terminated strings to HLA strings. However, instead of storing the string data at a specified location, this routine allocates storage for the string on the heap and returns a pointer to the new string in EAX. You should use strfree to clean up the storage after you are done with the string.

This function name is an alias of the str.a_cpyz function. That is, if you use conv.a_cStrToStr HLA actually links in the str.a_cpyz function from the strings module.

 

Roman Numeral Conversion
conv.roman( Arabic:uns32; rmn:string )

This procedure converts the specified integer value (Arabic) into a string that contains the Roman numeral representation of the value. Note that this routine only converts integer values in the range 1..3,999 to Roman numeral form. Since ASCII text doesn't allow overbars (that multiply roman digits by 1,000), this function doesn't handle really large Roman numbers. A different character set would be necessary for that.

conv.a_roman( Arabic:uns32 )

Just like the routine above, but this one allocates storage for the string and returns a pointer to the string in the EAX register.

The Coroutines Module (coroutines.hhf)

HLA provides a powerful coroutines class that lets you easy use coroutines in your programs. The coroutine class provides three procedures and methods you can use to initialize a coroutine, transfer control between coroutines, and free up the storage associated with a coroutine when it completes execution. The coroutine class also has several data fields, but you should treat these as private fields and never disturb their values.

In addition to these class procedures and methods, the coroutine package provides a coret procedure that is useful for returning from a coroutine to whomever "cocalled" the coroutine. This makes it very easy to implement Generators using coroutines.

Finally, the coroutine module provides a special coroutine variable, mainPgm , that you can use to cocall the "coroutine" corresponding to the main HLA program.

procedure coroutine.create( size:uns32; theProc:procedure );

The coroutine.create procedure is the typical HLA class constructor for the coroutine class. Since this is a class procedure, you can call create one of two different ways:

(1) You can call it via the statement "coroutine.create( size, proc);" This form assumes that you wish to create a dynamic coroutine object on the heap. When called this way, the coroutine.create procedure allocates storage for a coroutine object on the heap and returns a pointer to this new coroutine object in the ESI register. Otherwise it behaves identically to the second form of the coroutine.create procedure.

(2) You can call coroutine.create using an invocation of the form "objectName.create( size,proc);" where "objectName" is the name of a coroutine variable or a pointer to a coroutine object (that, presumably, has been initialized with a valid pointer to a coroutine object).

Either form of the call to create will initialize the coroutine object, allowing subsequent cocalls to the coroutine object.

Coroutines execute using their own stack (independent of other coroutine stacks and independent of the stack the main program uses). The size parameter specifies the number of bytes of stack space to reserve for the coroutine. A good minimum value for a coroutine stack is between 256 and 1,024 bytes. If the coroutine allocates lots of local/automatic variables, or calls other procedures that allocate lots of local/automatic storage, you will need to allocate a larger stack as appropriate. Likewise, if your coroutine calls procedures that are recursive, additional stack space may be necessary.

The theProc parameter is a pointer to a procedure. This procedure is the code that will execute when you cocall this coroutine. The only thing special about the procedure is that it should never be possible to return to the procedure's caller by executing a RET instruction. You exit coroutine using the coroutine.cocall procedure or the coroutine.coret procedure. If your code accidentally "falls off the end of the procedure" or otherwise attempts to return to the caller via a RET instruction, the coroutine will go into a special state in which any attempt to cocall it forces an immediate return by the coroutine to the cocaller.

procedure coroutine.cocall;

This is the mechanism you use to invoke a coroutine. Note that this is a procedure for performance reasons. You should never invoke the static procedure coroutine.cocall as this will raise a run-time exception. Instead, you should always invoke this procedure using an object invocation of the form "objectName.cocall();" This will switch the thread of execution from the current coroutine (or the main program) to the coroutine code associated with "objectName". Note that coroutines rarely begin execution at the first statement of the procedure associated with the coroutine (in fact, this happens exactly once, when you invoke the coroutine for the very first time).

The cocall mechanism provides the standard way of leaving a coroutine. Cocalling some other coroutine switches the execution context from the current coroutine to that other coroutine. The next time some code cocalls a coroutine that leaves via cocall, execution continues with the first statement following the cocall (it's almost as though you had called that other coroutine using a CALL instruction).

procedure coret;

coret is nearly identical to coroutine.cocall with two major exceptions. First, note that this procedure is not a member of the coroutine class. Therefore, you do not specify an object name in front of the call to the coret procedure. Second, coret returns control to whomever cocalled the current coroutine. The current coroutine does not have to know who called it; coret figures this out and cocalls the appropriate coroutine.

Note that coret is not a "return" in the usual sense that the coroutine completes execution upon calling coret . coret is identical to a coroutine.cocall to the coroutine that called the current coroutine. In particular, after a coroutine returns to another, any future cocalls to this coroutine will continue execution with the first statement following the coret call.

method coroutine.cofree;

When you are done with a coroutine, you should call the coroutine.cofree method to free up the stack space associated with that coroutine. You must not call coroutine.cofree from inside the coroutine you're cleaning up since it still needs its stack to transfer control to some other coroutine.

static mainPgm:coroutine; external( "?MainPgmCoroutine" );

This is a special coroutine variable that contains the control information for the main program. If, inside a coroutine, you wish to cocall the main program, just use a cocall of the form "MainPgm.cocall();" and control in the main program will continue at the point of the last cocall executed in the main program. (Note: the term "main program" here does not imply that the cocall has to be in the actual main program of an HLA program, it simply refers to the thread of execution that starts in the main program. Your main program can call a procedure that transfers control to some coroutine via cocall. MainPgm.cocall will transfer control back into that procedure.)

The Date & Time Module (datetime.hhf)

HLA contains a set of procedures and functions that simplify correct date and time calculations. Since these are two logical modules combined into a single module, this documentation will discuss the date and time routines separately within this section.

Date Functions

The date namespace defines the following useful identifiers:

date.daterec

Date representation. This is a dword object containing m , d , and y fields holding the obvious values. The y field is a 16-bit quantity supporting years 0..9,999. No Y2K problems here! (Of course, it does suffer from Y10K, but that's probably okay.) Since the Gregorian calendar began use in Oct, 1582, there is really no need to represent dates any earlier than this. In fact, many date calcuations will not allow dates earlier than Jan 1, 1600 for this very reason. The limitation of year 9999 is an arbitrary limit set in the library to help catch wild values. If you really need dates beyond 9999, feel free to modify the date validation code. The m and d fields are both byte objects. The date validation routines enforce the month limits of 1..12 and appropriate day limits (depending on the month and year).

date.IsLeapYear( y:uns32 ) returns( "al" );

date.IsLeapYear( dr:date.daterec ) returns( "al" );

This is an overloaded function. You may either pass it an unsigned integer containing the year or a date.daterec value specifying a m/d/y value. These functions return true or false in the AL register depending upon whether the parameter is a leap year (true if it is a leap year). Note that this function will be correct until sometime between the years 3000 and 4000, at which point people will probably have to agree upon adding an extra leap day in at some point (no such agreement has been made today, hence the absence from this function).

date.validate( m:byte; day:byte; year:word );

date.validate( dr:date.daterec )

These two functions check the date passed as a parameter and raise an ex.InvalidDate exception if the data in the fields (or the m/d/y ) parameter is not a valid date between 1/1/1600 and 12/31/9999.

date.isValid( m:byte; day:byte; year:word );

date.isValid( dr:date.daterec )

Similar to the date.validate procedures, except these functions return true/false in the AL register if the date is valid/invalid. They do not raise an exception.

date.outputFormat

This is an enumerated data type that defines the following constants:

mdyy, mdyyyy, mmddyy, mmddyyyy, yymd, yyyymd, yymmdd, yyyymmdd, MONdyyyy , and MONTHdyyyy . These constants control the date output format in the (mostly) obvious way. Note that mdyy can output one digit for the day and month while mmddyy always inputs two digits for each field. The MONdyyyy format outputs dates in the form "Jan 1, 2000" while the MONTHdyyyy outputs the dates using the format "January 1, 2000".

date.setFormat( fmt : OutputFormat );

This sets the internal format variable to the date.OutputFormat value you specify. This constant must be one of the date.OutputFormat enumerated constants or date.SetFormat will raise an ex.InvalidDateFormat exception.

date.setSeparator( chr:char );

This procedure sets the internal date separator character (default is '/') to the character you pass as a parameter. This is used when printing dates and converting dates to strings.

date.toString( m:byte; d:byte; y:word; s:string );

date.toString( dr:date.daterec; s:string);

These functions will convert the specified date to a string (using the output format specified by date.SetFormat and the separator character specified by date.SetSeparator ) and store the result in the specified string. An ex.StringOverflow exception occurs if the destination string's MaxStrLen field is too small (generally, 20 characters handles all string formats).

date.a_toString( m:byte; d:byte; y:word ); returns( "eax" );

date.a_toString( dr:date.daterec ); returns( "eax" );

This procedures are similar to the date.toString procedures above except they automatically allocate the storage for the string and return a pointer to the string object in the EAX register. You should free the string storage with strfree with you are done with this string.

date.print( m:byte; d:byte; y:word );

date.print( dr:date.daterec );

These two procedures write the specified string to the standard output device. They call date.a_toString to convert the date and then they print it using stdout.puts . Sorry, no file versions (yet), but you can always call date.toString yourself. In the future it would be better to implement the date object as a class so that the XXXX.put macros handle dates automatically.

date.Julian( m:byte; d:byte; y:word ); returns( "eax" );

date.Julian( dr:date.daterec ); returns( "eax" );

These functions convert the Gregorian (i.e., standard) date passed as a parameter into a "Julian day number." A Julian day number treats Jan 1, 4713 as day zero and simply numbers dates forward from that point. For example, Oct 9, 1995 is JD 2,450,000. Jan 1, 2000 is JD 2,452,545. Julian dates make date calculations of the form "what date is it X days from now?" trivial. You just compute JD+X to get the new date. In fact, for all X greater than some number (probably between 365 and 1,000) it's more efficient to compute a Gregorian date to a Julian date, add the days to the Julian Date, and then convert the Julian date back to a Gregorian date, than it is to add the dates directly into the Gregorian date (which is a real pain).

Note: technically, a "Julian Date" is not the same thing as a "Julian Day Number". A Julian date is based on the Julian Calendar created by Julius Caesar in about 45 BC. It was very similar to our current calendar except they didn't get the leap years quite right. Julian Day numbers are a different calendar system that, as explained above, number days consecutively after Jan 1 4713 BC (resetting to day one 7980 years later). Out of sheer laziness, this document will use the term "Julian Date" as a description of the calendar based on Julian day numbers despite that fact that this is technically incorrect.

date.fromJulian( jd:uns32; var gd:date.daterec );

This procedure converts a Julian date to a Gregorian date. The Julian date is the first parameter, the second (reference) parameter is a Gregorian date variable ( data.daterec ). See the note above about adding some number of days to a Gregorian date via translation to Julian. Note that adding months or years to a Julian date is a real pain in the rear. It's generally easier and faster to convert the Julian date to a Gregorian date, add the months and/or years to the Gregorian date (which is relatively easy), and then convert the whole thing back to a Julian day number.

date.dayNumber( m:byte; d:byte; y:word ); returns( "eax" );

date.dayNumber( dr:date.daterec ); returns( "eax" );

These functions convert the Gregorian date passed as a parameter into a day number into the current year (often erroneously called a "Julian Date" since NASA adopted this terminology in the late sixties). These functions return a value between 1 and 365 or 366 (for leap years) in the EAX register. Jan 1 is day 1, Dec 31 is day 365 or day 366.

date.daysLeft( m:byte; d:byte; y:word ); returns( "eax" );

date.daysLeft( dr:date.daterec ); returns( "eax" );

These functions return the number of days left in the current year counting the date passed as a parameter (hence Dec 31, yyyy always returns one).

date.dayOfWeek( m:byte; d:byte; y:word ); returns( "eax" );

date.dayOfWeek( dr:date.daterec ); returns( "eax" );

These functions return, in EAX, a value between zero and six denoting the day of the week of the given Gregorian date (0=sun, 1=mon, etc.)

date.daysBetween( m1:byte; d1:byte; y1:word; m2:byte; d2:byte; y2:word );

returns( "eax" );

date.daysBetween( m1:byte; d1:byte; y1:word; dr:date.daterec );

returns( "eax" );

date.daysBetween( dr:date.daterec; m:byte; d:byte; y:word );

returns( "eax" );

date.daysBetween( dr1:date.daterec; dr2:date.daterec );

returns( "eax" );

These functions return an uns32 value in EAX that gives the number of days between the two specified dates. These functions work directly on the Gregorian dates. As noted earlier, these functions work okay if the number of days between the two dates is small (the smaller the better, but anything beyond 1,000 days is probably getting out of hand). This function runs in approximately O(n) time where n is the number of years between the dates. As such, it can be quite slow for dates that are widely separated. To calculate the number of dates between two dates that are widely separate, it is probably a lot better to convert the two Gregorian dates to Julian day numbers and take the difference of those two day numbers. For dates that are near one another (e.g., within the same year or within a few years of one another), date.daysBetween is probably more efficient that the Gregorian->Julian calculation. Certainly date.daysBetween is more convenient to use!

date.datePlusDays( days:uns32; var dr:date.daterec );

This procedure adds the first parameter (in days) directly to the Gregorian data variable passed by reference as the second parameter. As with date.daysBetween , this function runs in O(n) time where n is the number of years. If n is small, this is a very efficient routine. However, if n is large, you would be well advised to convert the Gregorian date to a Julian day number, add the days to the Julian day number, and then convert the result back to Gregorian. For n less than a couple years, date.datePlusDays is probably the best way to go.

date.datePlusMonths( months:uns32; var dr:date.daterec );

This procedure adds the first parameter (in months) directly to the Gregorian data variable passed by reference as the second parameter. This is a very efficient routine. In fact, if you want to add some number of months to a Julian day value, the only reasonable way to do it is to convert the Julian day number to a Gregorian date, use this function, and then convert it back to a Julian day number.

Note: there is no date.datePlusYears function. You can use an ADD instruction to directly add a years value to a Gregorian date. For Julian dates, it's still easier to convert it to Gregorian, add the years value to the date, and then convert it back (this handles leap years properly).

date.today( var dr:date.daterec );

Stores the local date (today's date) into the specified parameter.

Time Functions

While not as extensive as the date functions, the set of time functions the HLA library provides are still useful. (There aren't as many time functions because time isn't as messed up as the calendar.)

time.timerec

This is a data structure with the following fields:

 

s:byte;

m:byte;

h:word;

 

The HLA time module defines time in these terms. The fields are stored in memory so you may compare two time structures as uns32 objects to determine <, =, >.

time.curTime( theTime: time.timerec );

This returns the local time (provided by the system clock) in the specified time variable.

time.hmsToSecs( theTime: time.timerec); returns( "eax");

time.hmsToSecs( h:uns16; m:byte; s:byte ); returns( "eax");

These two functions convert a time span in HHMMSS format to some number of seconds (if HHMMSS is the time of day, then these functions return the time in seconds since midnight). Note that HHMMSS does not have to be a 12-hour or 24-hour clock value. You may specify any number of hours between 0 and 65535, and any number of seconds or minutes between 0 and 255 for this function.

time.secsToHMS( seconds:uns32; var HMS:time.timerec );

This function converts the seconds parameter to an HMS time value. The first parameter must be less than 235,929,600 since this is the maximum time representable by 65535 hours. If the seconds parameter exceeds this value, then time.secsToHMS will raise an ex.TimeOverflow exception.

Someday I'll have to add time output routines and millisecond time routines. But that's all for now!

Exceptions (excepts.hhf)

The exceptions units contains several things of interest. First, it defines the ExceptionValues enumerated data type that lists out all the standard exceptions in the HLA Standard Library. The second thing provided in the excepts unit is the ex.PrintExceptionError procedure which prints a string associated with the exception number in EAX. Next, the excepts.hhf header file defines the "assert( expr )" macro. Finally, the excepts.hhf header file defines some procedures that the HLA run-time system uses to maintain the exception handling system; these procedures are of interest only to those who want to override the default HLA exception handling mechanisms.

Exception Constants

The following subsections describe each of the standard HLA exception constants and describe the conditions that lead to the Standard Library routines raising these exceptions. The "excepts.hhf" header file defines these constants. Since this list changes frequently, please refer to the excepts.hhf header file for the most recent list of exception names. HLA and the HLA Standard Library only raise these exceptions; user applications, however, may define other exceptions in addition to these. Of course, user applications may also raise exceptions using these exception constants.

ex.UnknownException (0)

This is a reserved value that HLA's Standard Library functions do not raise. The HLA run-time system displays this exception value if it cannot figure out the source of the interrupt. ex.PrintExceptionError calls also use this value to display an appropriate message for unhandled user exceptions.

ex.StringOverflow (1)

The string functions in the HLA Standard Library raise this exception if the caller attempts to store too many characters into a string variable (causing a string overflow error).

ex.StringIndexError (2)

Some string functions require a parameter that supplies an index into a string. If those functions require that the index be within the range 0..length-1, they will raise this exception to denote an index out of range error.

ex.ValueOutOfRange (3)

Several HLA Standard Library routines raise this exception if an integer calculation overflows. The best examples are the integer input routines (e.g., stdin.geti8) that will raise this exception if the user's input is otherwise legal but out of range for the specific data type (i.e., -128..+127 for stdin.geti8).

ex.IllegalChar (4)

Certain input and conversion routines raise this exception if an unexpected character comes along. An unexpected character is usually a non-ASCII character (character codes in the range $80..$FF). Note that the conversion and input routines do not raise this exception if a non-digit character comes along. See ex.ConversionError to see how the HLA Standard Library handles that exception.

ex.ConversionError (5)

HLA raises this exception whenever there is some sort of error converting data from one from to another (usually, this exception occurs when converting string data to numeric data). For example, when converting a string to an integer value, the HLA Standard Library will raise this exception if it encounters a character that is not legal for that numeric type and is not a delimiter character.

ex.BadFileHandle (6)

The file class and fileio library modules raise this exception if you attempt to read from or write to a file with an illegal file handle (i.e., the file has not been opened or has already been closed).

ex.FileOpenFailure (7)

The HLA file open routines raise this error if there was a catastrophic error opening a file.

ex.FileCloseError (8)

The HLA file close routines raise this error if there was an error closing a file.

ex.FileWriteError (9)

The HLA Standard Library file output routines raise this exception if there is an error while attempting to write data to a file. This is usually a catastrophic error such as file I/O or some hardware error.

ex.FileReadError (10)

The HLA Standard Library file output routines raise this exception if there is an error while attempting to read data from a file. This is usually a catastrophic error such as file I/O or some hardware error.

ex.DiskFullError (11)

The HLA Standard Library raises this exception if you attempt to write data to a disk that is full.

ex.EndOfFile (12)

The HLA Standard Library file I/O routines raise this exception if you attempt to read data from a file after you've reached the end of file. Note that HLA does not raise this exception upon reaching the EOF. You must actually attempt to read beyond the end of the file.

ex.MemoryAllocationFailure (13)

HLA raises this exception if a function attempts to allocate storage and the memory allocation operation fails (because of insufficent storage).

ex.AttemptToDerefNULL (14)

Many HLA Standard Library routines expect a pointer to some object as a parameter. If they do not allow a NULL pointer value (zero) the routines may explicitly test for a NULL value and raise this exception if the user inadventently passes in a NULL pointer. Also see the ex.AccessViolation exception.

ex.CannotFreeMemory (15)

The HLA memory free routines raise this exception if there is an error deallocating memory that was (presumably) allocated earlier.

ex.WidthTooBig (16)

Certain numeric conversion and output functions let you specify a field width value for the conversion. Those routines raise this exception if that field width value is too large (this is nominally 256, but the exact value may be different).

ex.TooManyCmdLnParms (17)

The args.hhf module raises this exception if you specify too many command line parameters. The exact maximum value may vary between versions of the HLA Standard Library, but it's typically a value like 64 or 128.

ex.ArrayShapeViolation (18)

The arrays.hhf module raise this exception if you attempt to copy data from one array to another or otherwise operate on two arrays with incompatible "shapes." The "shape" of an array is the number of dimensions and the bounds on each dimension of that array. Compatible arrays typically have the same number of dimensions and the same bounds on each dimensions (though there are some exceptions to this rule).

ex.ArrayBounds (19)

The arrays.hhf module raises this exception if you attempt to supply the wrong number of array dimensions or one of the array indices is out of bounds for that array.

ex.InvalidDate (20)

The HLA datetime.hhf module raises this expression if you supply an illegal date to a date function. Note that legal dates must fall between Jan 1, 1600 and Dec 31, 9999 and must have valid day and month values (depending on the month and year).

ex.InvalidDateFormat (21)

The HLA date conversion routines raise this exception if the internal date format value is illegal.

ex.TimeOverflow (22)

The HLA datetime.hhf module raises this exception if, during a time calculation, an overflow occurs.

ex.AssertionFailed (23)

The HLA assert statement raises this expression if the value of the assertion expression evaluates false. See the section on assertions later in this section for more details.

ex.ExecutedAbstract (24)

The HLA run-time system will raise this exception if you attempt to execute an abstract method from an abstract class.

ex.AccessViolation ($c0000005)

This is a hardware exception that the CPU raises if you attempt to access an illegal memory or I/O location.

ex.Breakpoint ($80000003)

This is a hardware exception that the CPU raises if you execute an INT 3 (breakpoint) instruction.

ex.SingleStep ($80000004)

This is a hardware exception that the CPU raises after each instruction if the trace flag is set in the EFLAGs register.

ex.PrivInstr ($c0000096)

This is a hardware exception that the CPU raises if you attempt to execute a priviledged instruction while in user (non-kernel) mode.

ex.IllegalInstr ($c000001d)

This is a hardware exception that the CPU raises if you attempt to execute an opcode that is not a legal 80x86 instruction.

ex.BoundInstr ($c000008c)

This is a hardware exception that the CPU raises if you execute a BOUND instruction and the register value is not within the bounds specified by the BOUND memory operand(s).

ex.IntoInstr ($c0000095)

This is a hardware exception that the CPU raises if you execute an INTO instruction and the overflow flag is set.

ex.DivideError ($c0000094)

This is a hardware exception that the CPU raises if you attempt to divide by zero or if the quotient will not fit in the destination operand.

ex.fDenormal ($c000008d)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and a floating point operation produces a demormalized result.

ex.fDivByZero ($c000008e)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and a floating point division by zero occurs.

ex.fInexactResult ($c000008f)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and a floating point operation produces an inexact result.

ex.fInvalidOperation ($c0000090)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and you attempt an illegal operation on the FPU.

ex.fOverflow ($c0000091)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and a floating point operation produces an overflow (see ex.fDenormal and ex.fUnderflow for underflows).

ex.fStackCheck ($c0000092)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and an FPU stack overflow occurs.

ex.fUnderflow ($c0000093)

This is a hardware exception that the FPU raises if you've enable floating point exceptions and an underflow occurs.

 

ex.InvalidHandle ($c0000008)

Windows raises this exception if you pass it an invalid handle value.

ex.StackOverflow ($c00000fd)

Windows raises this exception if the hardware (80x86) stack exceeds the bounds set by the linker.

ex.ControlC ($c000013a)

If control-C checking is enabled, Windows will raise this exception whenever the user presses control-C on the console device.

PrintExceptionError

The ex.PrintExceptionError procedure which displays a string associated with the exception number in EAX. Normally, you wouldn't need to call this routine since the HLA run-time system will do this automatically if you don't catch a particular exception. However, if you have an ANYEXCEPTION clause in a TRY..ENDTRY statement, you might want to call this function to display the appropriate message. Note that the value you pass to ex.PrintExceptionError must be one of the values described in the previous section or this function will simply display an "Unknown Exception Error".

The ex.PrintExceptionError procedure opens up a dialog box and displays the associated message. After the user presses the "OK" button in the dialog box, the procedure returns. If the HLA run-time system called this function, then the program aborts. If your program called this function, then it returns control back to your code.

Assertions

The excepts.hhf header file also defines the "assert( expr )" macro. This macro evaluates the specified expression and raises an exception if the expression is false. The ex.PrintExceptionError routine will display the text of the assert parameter if this macro raises the specified exception. By default, the assert macro is active in a program. However, you may disable asserts within a program by setting the ex.NDEBUG constant (VAL object, actually) to false. By setting ex.NDEBUG to true and false at different points in your program, you may activate and deactivate the assert statements on a line by line basis, if necessary.

The expression in the assert macro must be an expression acceptable to an HLA IF or WHILE statement (specifically, HLA compiles an assert to a JT pseudo-instruction). Note that if ex.NDEBUG is true, HLA does not emit any code whatsoever at all to the output file. Hence there is no run-time penalty for using asserts if you've set ex.NDEBUG to true.

Miscellaneous

The "excepts.hhf" header file is a special file known to the compiler. If your main program contains a #include( "excepts.hhf" ); statement, the HLA compiler will automatically emit code to call ex.PrintExceptionError if an unclaimed exception occurs. Without this header file, HLA simply prints that an unclaimed exception occurred with no other indication of the problem.

File Class (fileclass.hhf)

The HLA Standard Library provides a file class that simplifies file I/O. The name of this class is file and you should declare all objects to be of this type or a pointer to this type, e.g.,

 

var

MyOuputFile: file;

filePtr: pointer to file;

 

Once you declare a file variable, you access the remaining methods, procedures, and fields in the file class by specifying the file variable name and a period as a prefix to the field name.

 

Note: HLA also provides a fileio library module that does file I/O using traditional procedures rather than class objects. If you're more comfortable using such a programming paradigm, or you prefer your code to be a bit more efficient, you should use the fileio module.

 

Warning: Don't forget that HLA objects modify the values in the ESI and EDI registers whenever you call a class procedure, method, or iterator. Do not leave any important values in either of these register when making calls to the following routines. If the use of ESI and EDI is a problem for you, you might consider using the fileio module that does not suffer from this problem.

Note : Although the file class is convenient to use and provides some nice features to object-oriented programming, the way that the classes work pretty much means that you will be linking in the entire file class if you use only a single method from the class. If you're trying to write a small program, you should use the fileio module rather than the file class.

General File Operations
filevar.create; returns( "esi" );

file.create; returns( "esi" ); [for dynamic objects]

The file class provides a file.create constructor which you should always call before making use of a file variable. For file variables (as opposed to file pointer variables), you should call this routine specifying the name of the file variable. For file pointer variables, you should call this routine using the class name and store the pointer returned in EAX into your file variable. For example, to initialize the two files in the previous example, you would use code like the following:

 

MyOutputFile.create();

 

file.create();

mov( eax, filePtr );

 

Note that the file.create constructor simply initializes the virtual method table pointer and does other necessary internal initialization. The constructor does not open a file or perform other file-related activities.

filevar.handle; returns( "eax" );

This method returns the file handle in the EAX register. The returned value is invalid if you have not opened the file. You can pass this handle value to any of the Standard Library file routines (e.g., fileio.putc ) that expect a handle. You may also pass this value to Windows or Linux API functions that expect a file handle.

filevar.open( filename:string; access:dword )

This method opens an existing file. The filename parameter is a string specifying the name of the file you wish to open. The access parameter is one of the following:

The fileio.r constant tells filevar.open to open the file for read-only access. The fileio.w constant tells filevar.open to open the file for writing. Using the fileio.rw constant tells fileio.open to open the file for reading and writing. The fileio.a option tells the filevar.open function to open the file for writing and append all written data to the end of the file.

Before accessing the data in a file, you must open the file (which initializes the file handle). The filevar.open and filevar.openNew methods are excellent tools for this purpose. You may also open the file using direct calls to the Windows or Linux API, but you must initialize the filevar.fileHandle field of the class variable before making any other method calls in the file class.

filevar.openNew( filename:string )

This function opens a new file for writing (if the file already exists, it is first deleted and then a new file is opened for writing). The file is given the "normal" attribute.

Before accessing the data in a file, you must open the file (which initializes the file handle). The filevar.open and filevar.openNew methods are excellent tools for this purpose. You may also open the file using direct calls to the Windows or Linux API, but you must initialize the filevar . fileHandle field of the class variable before making any other method calls in the file class.

filevar.close;

This method closes a file opened via file.Open or file.OpenNew and flushes any buffered data to the disk.

The following routines behave just like the routines of the same name described earlier in this document, except, of course, they write their data to the specified output file.

File Class Output Routines
Miscellaneous Output

The following file output routines all assume that you've opened the filevar file variable via a call to filevar.open and you've successfully opened the file for output.

filevar.write( var buffer:byte; count:dword )

This method writes the number of bytes specified by the count variable to the file. The bytes starting at the address of the buffer byte are written to the file. No range checking is done on the buffer, it is your responsibility to ensure that the buffer contains at least count valid data bytes.

filevar.putbool( b:boolean );

This procedure writes the string "true" or "false" to the filevar output file depending on the value of the b parameter.

filevar.newln( );

This function writes a newline sequence (carriage return/line feed) to the specified output file ( filevar ).

 

Character, Character Set, and String Output
filevar.putc( c:char )

Writes the character specified by the c parameter to the filevar file.

filevar.putcSize( c:char; width:int32; fill:char )

Outputs the character c to the file filevar using at least width output positions. If the absolute value of width is greater than one, then this function writes fill characters as padding characters during the output. If width is a positive value greater than one, then filevar.putcSize writes c left justfied in a field of width characters; if width is a negative value less than one, then filevar.putcSize writes c right justified in a field of width characters.

filevar.putcset( cst:cset );

This function writes all the members of the cst character set parameter to the specified file variable.

filevar.puts( s:string );

This procedure writes the value of the string parameter to the specified file.

filevar.putsSize( s:string; width:int32; fill:char )

This function writes the s string to the filevar file using at least width character positions. If the absolute value of width is less than or equal to the length of s , then this function behaves exactly like filevar.puts . On the other hand, if the absolute value of width is greater than the length of s , then filevar.putsSize writes width characters to the output file. This procedure emits the fill character in the extra print positions. If width is positive, then filevar.putsSize right justifies the string in the print field. If width is negative, then filevar.putsSize left justifies the string in the print field. Generally, people expect the string to be left justified, so you should ensure that this value is negative to achieve this.

Hexadecimal Numeric Output
filevar.putb( b:byte )
filevar.putbSize( b:byte; size:dword; fill:char )

This procedure writes the value of b to filevar using exactly two hexadecimal digits (including a leading zero if necessary). The filevar.putbSize function lets you specify a minimum field width and a fill character. The filevar.putb routine uses a minimum size of two and a fill character of '0'.

filevar.putw( w:word )
filevar.putwSize( w:word; size:dword; fill:char )

This procedure writes the value of w to filevar using exactly four hexadecimal digits (including leading zeros if necessary). The filevar.putwSize function lets you specify a minimum field width and a fill character. The filevar.putw routine uses a minimum size of two and a fill character of '0'.

filevar.putd( dw:dword )
filevar.putdSize( d:dword; size:dword; fill:char )

This procedure writes the value of d to filevar using exactly eight hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled). The filevar.putdSize function lets you specify a minimum field width and a fill character. The filevar.putd routine uses a minimum size of two and a fill character of '0'.

filevar.putq( qw:qword )
filevar.putqSize( q:qword; size:dword; fill:char )

This procedure writes the value of q to filevar using exactly sixteen hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled). The filevar.putqSize function lets you specify a minimum field width and a fill character. The filevar.putq routine uses a minimum size of two and a fill character of '0'.

filevar.puttb( tb:tbyte )

This procedure writes the value of tb to filevar using exactly 20 hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled).

Signed Integer Numeric Output

These routines convert signed integer values to string format and write that string to the filevar file. The filevar.putxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

Note that unlike floating point values, these functions do not print a space in front of the value if it is non-negative.

 
filevar.xxxSize Output Format
filevar.puti64Size( q:qword; width:int32; fill:char )

This function writes the 64-bit value you pass as a signed integer to the specified output file using the width and fill values as specified above.

filevar.puti32Size( d:dword; width:int32; fill:char )

This function writes the 32-bit value you pass as a signed integer to the specified output file using the width and fill values as specified above.

filevar.puti16Size( w:word; width:int32; fill:char )

This function writes the 16-bit signed integer value you pass to the specified output file using the width and fill values as specified above.

filevar.puti8Size ( b:byte; width:int32; fill:char )

This function writes the eight-bit signed integer value you pass to the specified output file using the width and fill values as specified above.

filevar.puti64( q:qword )

This function converts the 64-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

filevar.puti32( d:dword )

This function converts the 32-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

filevar.puti16( w:word )

This function converts the 16-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

filevar.puti8 ( b:byte )

This function converts the eight-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

Unsigned Integer Numeric Output

These routines convert unsigned integer values to string format and write that string to the filevar file. The filevar.putxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

 
filevar.uxxxSize Output Format
filevar.putu64Size( q:qword; width:int32; fill:char )

This function writes the unsigned 64-bit value you pass to the specified output file using the width and fill values as specified above.

filevar.putu32Size( d:dword; width:int32; fill:char )

This function writes the unsigned 32-bit value you pass to the specified output file using the width and fill values as specified above.

filevar.putu16size( w:word; width:int32; fill:char )

This function writes the unsigned 16-bit value you pass to the specified output file using the width and fill values as specified above.

filevar.putu8size( b:byte; width:int32; fill:char )

This function writes the unsigned eight-bit value you pass to the specified output file using the width and fill values as specified above.

filevar.putu64( q:qword )

This function converts the 64-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

filevar.putu32( d:dword )

This function converts the 32-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

filevar.putu16( w:word )

This function converts the 16-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

filevar.putu8 ( b:byte )

This function converts theeight-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by filevar ) using the minimum number of print positions the number requires.

Floating Point Output

The HLA file I/O class provides several procedures you can use to write floating point files to a text file. The following subsections describe these routines.

Real Output Using Scientific Notation

The floating point numeric output routines translate the three different binary floating point formats to their string representation and then write this string to the file that filevar specifies. There are two generic classes of these routines: those that convert their values to exponential/scientific notation and those that convert their string to a decimal form.

The filevar.pute80 , filevar.pute64 , and filevar.pute32 routines convert their values to a string using scientific notation. These three routines each have two parameters: the value to output and the field width of the result. These routines produce a string with the following format:

 
filevar.pute80( r:real80; width:uns32 )

This function writes the 80-bit extended precision floating point value passed in r to the file using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 80-bit extended precision floating point values support about 18 significant digits. So a width value that yeilds more than 18 mantissa digits will produce garbage output in the low order digits of the number.

filevar.pute64( r:real64; width:uns32 )

This function writes the 64-bit double precision floating point value passed in r to the file using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 64-bit double precision floating point values support about 15 significant digits. So a width value that yeilds more than 15 mantissa digits will produce garbage output in the low order digits of the number.

filevar.pute32( r:real32; width:uns32 )

This function writes the 32-bit single precision floating point value passed in r to the file using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 32-bit extended precision floating point values support about 6-7 significant digits. So a width value that yeilds more than seven mantissa digits will produce garbage output in the low order digits of the number.

Real Output Using Decimal Notation

Although scientific (exponential) notation is the most general display format for real numbers, real numbers you display in this format are very difficult to read. Therefore, the HLA file class module also provides a set of functions that output real values using the decimal representation. Although you cannot (practically) use these decimal output routines for all real values, they are applicable to a wide variety of common numbers you will use in your programs.

These functions come in two varieties. The first variety requires four parameters: the real value to convert, the width of the converted value, the number of digit positions to the right of the decimal point, and a padding character. The second variety only requires the first three parameters and assumes the padding character is a space. These functions write their values using the following string format:

 
filevar.putrxx Conversion Format
filevar.putr80pad( r:real80; width:uns32; decpts:uns32; pad:char )

This procedure writes an 80-bit extended precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

filevar.putr64pad( r:real64; width:uns32; decpts:uns32; pad:char )

This procedure writes a 64-bit double precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

filevar.putr32pad( r:real32; width:uns32; decpts:uns32; pad:char )

This procedure writes a 32-bit single precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

filevar.putr80( r:real80; width:uns32; decpts:uns32 )

This procedure writes an 80-bit extended precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

filevar.putr64( r:real64; width:uns32; decpts:uns32 )

This procedure writes a 64-bit double precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

filevar.putr32( r:real32; width:uns32; decpts:uns32 )

This procedure writes a 32-bit single precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

Generic File Output
filevar.put( parameter_list )

filevar.put is a macro that automatically invokes an appropriate filevar output routine based on the type of the parameter(s) you pass it. This is a very convenient output routine and is probably the file class output call you will use most often in your programs. Keep in mind that this macro is not a single function call; instead, HLA translates this macro into a sequence of calls to procedures like filevar.puti32 , filevar.puts , etc.

filevar.put is a macro that provides a flexible syntax for outputting data to the standard output device. This macro allows a variable number of parameters. For each parameter present in the list, filevar.put will call the appropriate routine to emit that data, according to the type of the parameter. Parameters may be constants, registers, or memory locations. You must separate each macro parameter with a comma.

Here is an example of a typical invocation of stdout.put:

 

filevar.put( "I=", i, " j=", j, nl );

 

The above is roughly equivalent to

 

filevar.puts( "I=" );

filevar.puti32( i );

filevar.puts( " j=" );

filevar.puti32( j );

filevar.newln();

 

This assumes, of course, that i and j are int32 variables.

The filevar.put macro also lets you specify the minimum field width for each parameter you specify. To print a value using a minimum field width, follow the object you wish to print with a colon and the value of the minimum field width. The previous example, using field widths, could look like the following:

 

filevar.put( "I=", i:2, " j=", j:5, nl );

 

Although this example used the literal decimal constants two and five for the field widths, keep in mind that register values and memory value (integers, anyway) are prefectly legal here.

For floating point numbers you wish to display in decimal form, you can specify both the minimum field width and the number of digits to print to the right of the decimal point by using the following syntax:

 

filevar.put( "Real value is ", f:10:3, nl );

 

The filevar.put macro can handle all the basic primitive types, including boolean, unsigned (8, 16, 32), signed (8, 16, 32), character, character set, real (32, 64, 80), string, and hexadecimal (byte, word, dword).

If you specify a class variable (object) and that class defines a " toString " method, the filevar.put macro will call the associated toString method and output that string to the file. Note that the toString method must dynamically allocate storage for the string by calling stralloc . This is because filevar.put will call strfree on the string once it outputs the string.

There is a known "design flaw" in the filevar.put macro. You cannot use it to print HLA intermediate variables (i.e., non-local VAR objects). The problem is that HLA's syntax for non-local accesses takes the form "reg32:varname" and filevar.put cannot determine if you want to print reg32 using varname print positions versus simply printing the non-local varname object. If you want to display non-local variables you must copy the non-local object into a register, a static variable, or a local variable prior to using stdout.put to print it. Of course, there is no problem using the other filevar.putXXXX functions to display non-local VAR objects, so you can use those as well.

Important(!), don't forget that method calls (e.g., the routines that filevar.put translates into) modify the values in the ESI and EDI registers. Therefore, it never makes any sense to attempt to print the values of ESI and EDI within the parameter list. All you will wind up doing is printing the address of the file variable (ESI) or the address of its virtual method table (EDI). If you need to write these two values to a file, move them to another register or a memory location first.

File Input

The following file input routines behave just like their standard input and file input counterparts (unless otherwise noted):

Generic File Input
filevar.read( var buffer:byte; count:dword )

This function reads count bytes from the file and stores them into memory starting with the first byte of the buffer variable. This routine does not do any range checking. It is your responsibility to ensure that buffer is large enough to hold the data read.

Note: for the file.read and file.write methods, it is very common to use type coercion to allow you to pass some other type as a parameter. Consider the following example:

 

program fileWriteDemo;

#include( "fileclass.hhf" );

 

static

f:file;

s:string := "Hello World";

begin fileWriteDemo;

 

f.create();

f.OpenNew( "Hello.txt" );

mov( s, esi );

f.write( (type byte [esi]), 11 );

f.close();

end fileWriteDemo;

File Output Demo
filevar.readln

This function reads and discards all characters up to and including the newline sequence in the file.

filevar.eoln

This function returns true (1) in AL if the file is currently at the end of a line, false (0) otherwise. This function reads and discards the newline sequence.

Character and String Input

The following functions read character data from an input file specified by filevar . Note that HLA's file class module does not provide the ability to read character set data directly from the user. However, you can always read a string and then convert that string to a character set using the appropriate function in the cset module.

filevar.getc; returns( "al" );

This function reads a single character from the filevar file and returns that chraacter in the AL register. This function assumes that the file you've opened is a text file. Note that filevar.getc does not return the end of line sequence as part of the input stream. Use the filevar.eoln function to determine when you've reached the end of a line of text. Because filevar.getc preprocesses the text file (removing end of line sequences) you should not use it to read binary data, use it only to read text files.

filevar.gets( s:string )

This function reads a sequence of characters from the current filevar file position through to the next end of line sequence and stores these characters (without the end of line sequence) into the string variable you pass as a parameter. Before calling this routine, you must allocate sufficient storage for the string. If filevar.gets attempts to read a larger string than the string's MaxStrLen value, filevar.gets raises a string overflow exception.

Note that this function does not store the end of line sequence into the string, though it does consume the end of line sequence. The next character a filevar function will read from the file will be the first character of the following line.

If the current file position is at the end of some line of text, then filevar.gets consumes the end of line and stores the empty string into the s parameter.

filevar.a_gets; returns( "eax" );

Like filevar.gets , this function also reads a string from the filevar file. However, rather than storing the string data into a string you supply, this function allocates storage for the string on the heap and returns a pointer to this string in the EAX register. You code should call strfree to release this storage when you're done with the string data.

The filevar.a_gets function imposes a line length limit of 1,024 characters. If this is a problem, you should modify the source code for this function to raise the limit. This functions raises an exception if you attempt to read a line longer than this internal limit.

Signed Integer Input
filevar.geti8; returns( "al" );

This function reads a signed eight-bit decimal integer in the range -128..+127 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.geti8 function raises an appropriate exception if the input violates any of these rules or the value is outside the range -128..+127. This function returns the binary form of the integer in the AL register.

filevar.geti16; returns( "ax" );

This function reads a signed 16-bit decimal integer in the range -32768..+32767 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.geti16 function raises an appropriate exception if the input violates any of these rules or the value is outside the range -32768..+32767. This function returns the binary form of the integer in the AX register.

filevar.geti32; returns( "eax" );

This function reads a signed 32-bit decimal integer in the (approximate) range ±2 Billion from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.geti32 function raises an appropriate exception if the input violates any of these rules or the value is outside the range plus or minus two billion. This function returns the binary form of the integer in the EAX register.

 

filevar.geti64( var q:qword );

This function reads a signed 64-bit decimal integer from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.geti64 function raises an appropriate exception if the input violates any of these rules or the value is outside the range of a 64-bit signed integer. This function stores the 64-bit result in the qword you pass as a reference parameter.

Unsigned Integer Input
filevar.getu8; returns( "al" );

This function reads an unsigned eight-bit decimal integer in the range 0..+255 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getu8 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..255. This function returns the binary form of the integer in the AL register.

filevar.getu16; returns( "ax" );

This function reads an unsigned 16-bit decimal integer in the range 0..+65535 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getu16 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..65535. This function returns the binary form of the integer in the AX register.

filevar.getu32; returns( "eax" );

This function reads an unsigned 32-bit decimal integer in the range 0..+4,294,967,295 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getu32 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..4,294,967,295. This function returns the binary form of the integer in the EAX register.

filevar.getu64( var q:qword );

This function reads an unsigned 64-bit decimal integer from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getu64 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..264-1. This function returns the binary form of the integer in the qword parameter you pass by reference.

Hexadecimal Input
filevar.getb; returns( "al" );

This function reads an eight-bit hexadecimal integer in the range 0..$FF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getb function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FF. This function returns the binary form of the value in the AL register.

filevar.getw; returns( "ax" );

This function reads a 16-bit hexadecimal integer in the range 0..$FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getw function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF. This function returns the binary form of the value in the AX register.

filevar.getd; returns( "eax" );

This function reads a 32-bit hexadecimal integer in the range 0..$FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getd function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF. This function returns the binary form of the value in the EAX register.

filevar.getq( var q:qword );

This function reads a 64-bit hexadecimal integer in the range 0..$FFFF_FFFF_FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The filevar.getq function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF_FFFF_FFFF. This function stores the 64-bit result into the variable you pass as a reference parameter.

Floating Point Input
filevar.getf; returns( "st0" );

This function reads an 80-bit floating point value in either decimal or scientific from from the file and leaves the result sitting on the FPU stack. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a sequence of characters that represent a floating point value. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. This function raises an appropriate exception if an error occurs.

Generic File Input
filevar.get( List_of_items_to_read );

This is a macro that allows you to specify a list of variable names as parameters. The file.get macro reads an input value for each item in the list and stores the resulting value in each corresponding variable. This macro determines the type of each variable that you pass it and emits a call to the appropriate filevar.getxxx function to read the actual value. As an example, consider the following call to filevar.get :

filevar.get( i32, charVar, u16, strVar );

 

The macro invocation above expands into the following:

push( eax );

filevar.geti32( i32 );

filevar.getc();

mov( al, charVar );

filevar.geti16();

mov( ax, u16 );

filevar.gets( strVar );

pop( eax );

 

Notice that filevar.get preserves the value in the EAX register even though various filevar.getxxx functions use this register. Note that filevar.get automatically handles the case where you specify EAX as an input variable and writes the value to [esp] so that in properly modifies EAX upon completion of the macro expansion.

Note that fielvar.get only supports eight-, sixteen-, and thirty-two bit integer input. If you need to read 64-bit values, you must use the appropriate filevar.getx64 function to achieve this.

The File I/O Module (fileio.hhf)

The file I/O functions are quite similar to the file class functions except that you explicitly pass a file handle to these routines rather than invoking a method via a file class variable. In fact, the file class methods call the corresponding fileio functions whenever you invoke a file object's methods. Therefore, if you want your programs to be a little smaller, you should use the fileio functions rather than the file class package.

General File I/O Functions

Here are the file output routines provided by the HLA fileio unit:

fileio.open( FileName: string; Access:dword ); returns( "eax" );

The fileio.open routine opens the file by the specified name. The Access parameter is one of the following:

The fileio.r constant tells HLA to open the file for read-only access. The fileio.w constant tells HLA to open the file for writing. Using the fileio.rw constant tells fileio.open to open the file for reading and writing. The fileio.a option tells the fileio.open function to open the file for writing and append all written data to the end of the file.

This routine raise an exception if there is a problem opening the file (e.g., the file does not exist). If the file is successfully opened, this function returns the file handle in the EAX register.

fileio.openNew( FileName: string ); returns( "eax" );

This function opens a new file for writing. The single parameter specifies the file's (path) name. This function raises an exception if there is an error opening the file. If the file is opened successfully, this function returns the file handle in the EAX register. If the file already exists, this function will successfully open the file and delete any existing data in the file.

fileio.close( Handle:dword );

This function closes the file specfied by the handle passed as the parameter. You should close all files as soon as you are done using them. Note that successful program termination automatically closes all files, but it is exceeding poor programming practice to rely on the operating system to close any files you've left open. Were the machine to crash, data could be lost; for this reason, you should close all files as soon as you are finished reading and writing data.

fileio.eof( Handle:dword ); returns( "al" );

This function returns true (1) in AL if the specified file is at the end of file. It returns false (0) otherwise. Note that this function actually returns true/false in EAX even though the "returns" value is "AL". So don't count on it preserving the value in AH or the upper 16 bits of EAX.

Directory and File Manipulation Functions
fileio.rewind( Handle:dword ); returns( "eax" );

The Handle parameter specifies the handle of an open file. This function positions the file pointer to the beginning of the file (file position zero). This function returns the error code in EAX.

fileio.append( handle:dword ); returns( "eax" );

This function positions the file pointer of the file specified by the handle parameter to the end of that file. The file should have been opened for writing.

fileio.position( Handle:dword ); returns( "eax" );

This function returns the file position (in bytes) of the file specified by the handle parameter. It returns the file position offset in the EAX register.

fileio.seek( Handle:dword; offset:dword ); returns( "eax" );

This function sets the file position in the file specified by the Handle parameter to the position specified by the offset parameter. The offset parameter specifies the file position in bytes from the beginning of the file. It returns the error status in EAX.

fileio.rSeek( Handle:dword; offset:dword ); returns( "eax" );

This function sets the file position in the file specified by the Handle parameter to the position specified by the offset parameter. The offset parameter specifies the file position in bytes from the end of the file. It returns the error status in EAX.

fileio.truncate( Handle:dword ); returns( "eax" );

This function deletes all bytes in the file specified by the Handle parameter from the current file position to the end of the file. It returns the error status in EAX.

fileio.copy( source:string; dest:string; failIfExists:boolean ); returns( "eax" );

This function copies the file specified by the source filename to the file specified by the dest filename. This function returns an error if the fileIfExists parameter contains true and the destination file already exists.

fileio.move( source:string; dest:string ); returns( "eax" );

This function moves the file specified by the source filename to the destination file. If the paths are the same except for the filename, then this function renames the source file to use the dest filename.

fileio.delete( filename:string ); returns( "eax" );

This function deletes the specified filename. Obviously, use this function with care. It returns the Windows error status in EAX.

fileio.mkdir( dirname:string ); returns( "eax" );

This function creates a directory using the pathname you supply as a parameter. It returns the error status in EAX.

fileio.cd( dirname:string ); returns( "eax" );

This function sets the current working directory to the filename you pass as a parameter. It returns the error status in the EAX register.

fileio.gwd( dest:string );

This function returns a string containing the current working directory's pathname in the string you pass as a parameter. The string must have storage allocated for it and it must be large enough to hold the pathname or HLA will raise a string overflow exception.

fileio.size( Handle:dword ); returns( "eax" );

This function returns the current size of an open file whose handle you pass as a parameter. It returns the size in the EAX register. Note the overloaded version below.

fileio.size( filename:string ); returns( "eax" );

This function returns the size of the file whose filename you pass as a parameter. It returns the file's size in the EAX register. Also see the overloaded version above.

File Output Routines

The file output routines in the fileio module are very similar to the file output routines in the file class module as well as the output routines in the standard output library module. In general, these routines require (at least) two parameters; the first is the file handle that you obtain via the fileio.open or fileio.openNew call, the second parameter is usually the value to write to the file. Some function contain additional parameters that provide formatting information. Note that these functions require that you've opened the file for writing, reading and writing, for for appending. If the file is not open or you've only opened it for reading, these routines will raise an appropriate exception.

Miscellaneous Output Routines
fileio.write( Handle:dword; var buffer:byte; count:uns32 )

This procedure writes the number of bytes specified by the count variable to the file. The bytes starting at the address of the buffer byte are written to the file. No range checking is done on the buffer, it is your responsibility to ensure that the buffer contains at least count valid data bytes.

fileio.newln( Handle:dword )

This function writes a newline sequence (carriage return/line feed) to the specified output file.

fileio.putbool( Handle:dword; b:boolean )

This procedure writes the string "true" or "false" to the output file depending on the value of the b parameter.

Character, String, and Character Set Output Routines
fileio.putc( Handle:dword; c:char )

Writes the character specified by the c parameter to the file.

fileio.putcSize( Handle:dword; c:char; width:int32; fill:char )

Outputs the character c to the file using at least width output positions. If the absolute value of width is greater than one, then this function writes fill characters as padding characters during the output. If width is a positive value greater than one, then fileio.putcSize writes c left justfied in a field of width characters; if width is a negative value less than one, then fileio.putcSize writes c right justified in a field of width characters.

fileio.putcset( Handle:dword; cs:cset )

This function writes all the members of the cst character set parameter to the specified file variable.

fileio.puts( Handle:dword; s:string )

This procedure writes the value of the string parameter to the specified file.

fileio.putsSize( Handle:dword; s:string; width:int32; fill:char )

This function writes the s string to the file using at least width character positions. If the absolute value of width is less than or equal to the length of s , then this function behaves exactly like fileio.puts . On the other hand, if the absolute value of width is greater than the length of s , then fileio.putsSize writes width characters to the output file. This procedure emits the fill character in the extra print positions. If width is positive, then fileio.putsSize right justifies the string in the print field. If width is negative, then fileio.putsSize left justifies the string in the print field. Generally, people expect the string to be left justified, so you should ensure that this value is negative to achieve this.

Hexadecimal Output Routines
fileio.putb( Handle:dword; b:byte )
fileio.putbSize( Handle:dword; b:byte; size:dword; fill:char )

This procedure writes the value of b to the file using exactly two hexadecimal digits (including a leading zero if necessary). The fileio.putbSize function lets you specify a minimum field width and a fill character. The fileio.putb routine uses a minimum size of two and a fill character of '0'.

fileio.putw( Handle:dword; w:word )
fileio.putwSize( Handle:dword; w:word; size:dword; fill:char )

This procedure writes the value of w to the file using exactly four hexadecimal digits (including leading zeros if necessary). The fileio.putwSize function lets you specify a minimum field width and a fill character. The fileio.putw routine uses a minimum size of two and a fill character of '0'.

fileio.putd( Handle:dword; dw:dword )
fileio.putdSize( Handle:dword; d:dword; size:dword; fill:char )

This procedure writes the value of d to the file using exactly eight hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled). The fileio.putdSize function lets you specify a minimum field width and a fill character. The fileio.putd routine uses a minimum size of two and a fill character of '0'. Note that if underscore output is enabled, this routine will actually emit nine characters (eight digits plus an underscore).

 

fileio.putq( Handle:dword; qw:qword )
fileio.putqSize( Handle:dword; q:qword; size:dword; fill:char )

This procedure writes the value of q to the file using exactly sixteen hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled). The fileio.putqSize function lets you specify a minimum field width and a fill character. The fileio.putq routine uses a minimum size of two and a fill character of '0'. Note that if underscore output is enabled, this routine will emit 19 characters (16 digits plus three underscores).

 

fileio.puttb( Handle:dword; tb:tbyte )

This procedure writes the value of tb to the file using exactly 20 hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled).

 

fileio.putl( Handle:dword; l:lword )
fileio.putlSize( Handle:dword; l:lword; size:dword; fill:char )

This procedure writes the value of l to the file using exactly 32 hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled). The fileio.putlSize function lets you specify a minimum field width and a fill character. The fileio.putl routine uses a minimum size of two and a fill character of '0'. Note that if underscore output is enabled, then this routine will actually emit 39 characters (32 digits plus seven underscores).

 

Signed Integer Output Routines

These routines convert signed integer values to string format and write that string to the file specified by the Handle parameter. The fileio.putxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

Note that unlike floating point values, these functions do not print a space in front of the value if it is non-negative.

 
fileio.xxxSize Output Format

 

fileio.puti8Size ( Handle:dword; b:byte; width:int32; fill:char )

This function writes the eight-bit signed integer value you pass to the specified output file using the width and fill values as specified above.

fileio.puti16Size( Handle:dword; w:word; width:int32; fill:char )

This function writes the 16-bit signed integer value you pass to the specified output file using the width and fill values as specified above.

fileio.puti32Size( Handle:dword; d:dword; width:int32; fill:char )

This function writes the 32-bit value you pass as a signed integer to the specified output file using the width and fill values as specified above.

fileio.puti64Size( Handle:dword; q:qword; width:int32; fill:char )

This function writes the 64-bit value you pass as a signed integer to the specified output file using the width and fill values as specified above.

fileio.puti128Size( Handle:dword; l:lword; width:int32; fill:char )

This function writes the 128-bit value you pass as a signed integer to the specified output file using the width and fill values as specified above.

fileio.puti8 ( Handle:dword; b:byte )

This function converts the eight-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.puti16( Handle:dword; w:word )

This function converts the 16-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.puti32( Handle:dword; d:dword )

This function converts the 32-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.puti64( Handle:dword; q:qword )

This function converts the 64-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.puti128( Handle:dword; l:lword )

This function converts the 128-bit signed integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

Unsigned Integer Output Routines

These routines convert unsigned integer values to string format and write that string to the file specified by the Handle parameter. The fileio.putxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

 
fileio.uxxxSize Output Format
fileio.putu8Size( Handle:dword; b:byte; width:int32; fill:char )

This function writes the unsigned eight-bit value you pass to the specified output file using the width and fill values as specified above.

fileio.putu16Size( Handle:dword; w:word; width:int32; fill:char )

This function writes the unsigned 16-bit value you pass to the specified output file using the width and fill values as specified above.

fileio.putu32Size( Handle:dword; d:dword; width:int32; fill:char )

This function writes the unsigned 32-bit value you pass to the specified output file using the width and fill values as specified above.

fileio.putu64Size( Handle:dword; q:qword; width:int32; fill:char )

This function writes the unsigned 64-bit value you pass to the specified output file using the width and fill values as specified above.

fileio.putu128Size( Handle:dword; l:lword; width:int32; fill:char )

This function writes the unsigned 128-bit value you pass to the specified output file using the width and fill values as specified above.

fileio.putu8 ( Handle:dword; b:byte )

This function converts the eight-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.putu16( Handle:dword; w:word )

This function converts the 16-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.putu32( Handle:dword; d:dword )

This function converts the 32-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.putu64( Handle:dword; q:qword )

This function converts the 64-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

fileio.putu128( Handle:dword; l:lword )

This function converts the 128-bit unsigned integer you pass as a parameter to a string and writes this string to the file (specified by Handle ) using the minimum number of print positions the number requires.

 

Floating Point Output Routines

The HLA file I/O class provides several procedures you can use to write floating point files to a text file. The following subsections describe these routines.

Real Output Using Scientific Notation

The floating point numeric output routines translate the three different binary floating point formats to their string representation and then write this string to the file that the Handle parameter specifies. There are two generic classes of these routines: those that convert their values to exponential/scientific notation and those that convert their string to a decimal form.

The fileio.pute80 , fileio.pute64 , and fileio.pute32 routines convert their values to a string using scientific notation. These three routines each have two parameters: the value to output and the field width of the result. These routines produce a string with the following format:

 
Exponential (Scientific Notation) Output Format
fileio.pute80( Handle:dword; r:real80; width:uns32 )

This function writes the 80-bit extended precision floating point value passed in r to the file using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 80-bit extended precision floating point values support about 18 significant digits. So a width value that yeilds more than 18 mantissa digits will produce garbage output in the low order digits of the number.

fileio.pute64( Handle:dword; r:real64; width:uns32 )

This function writes the 64-bit double precision floating point value passed in r to the file using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 64-bit double precision floating point values support about 15 significant digits. So a width value that yeilds more than 15 mantissa digits will produce garbage output in the low order digits of the number.

fileio.pute32( Handle:dword; r:real32; width:uns32 )

This function writes the 32-bit single precision floating point value passed in r to the file using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 32-bit extended precision floating point values support about 6-7 significant digits. So a width value that yeilds more than seven mantissa digits will produce garbage output in the low order digits of the number.

Real Output Using Decimal Notation

Although scientific (exponential) notation is the most general display format for real numbers, real numbers you display in this format are very difficult to read. Therefore, the HLA fileio module also provides a set of functions that output real values using the decimal representation. Although you cannot (practically) use these decimal output routines for all real values, they are applicable to a wide variety of common numbers you will use in your programs.

These functions come in two varieties. The first variety requires five parameters: the real value to convert, the width of the converted value, the number of digit positions to the right of the decimal point, and a padding character. The second variety only requires the first four parameters and assumes the padding character is a space. These functions write their values using the following string format:

 
fileio.putrxx Conversion Format
fileio.putr80pad( Handle:dword; r:real80; width:uns32; decpts:uns32; pad:char )

This procedure writes an 80-bit extended precision floating point value to the file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

fileio.putr64pad( Handle:dword; r:real64; width:uns32; decpts:uns32; pad:char )

This procedure writes a 64-bit double precision floating point value to the file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

fileio.putr32pad( Handle:dword; r:real32; width:uns32; decpts:uns32; pad:char )

This procedure writes a 32-bit single precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

fileio.putr80( Handle:dword; r:real80; width:uns32; decpts:uns32 )

This procedure writes an 80-bit extended precision floating point value to the file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

fileio.putr64( Handle:dword; r:real64; width:uns32; decpts:uns32 )

This procedure writes a 64-bit double precision floating point value to the file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

fileio.putr32( Handle:dword; r:real32; width:uns32; decpts:uns32 )

This procedure writes a 32-bit single precision floating point value to the filevar file as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

Generic File Output Routine
fileio.put( list_of_items )

fileio.put is a macro that automatically invokes an appropriate fileio output routine based on the type of the parameter(s) you pass it. This is a very convenient output routine and is probably the fileio output call you will use most often in your programs. Keep in mind that this macro is not a single function call; instead, HLA translates this macro into a sequence of calls to procedures like fileio.puti32 , fileio.puts , etc.

fileio.put is a macro that provides a flexible syntax for outputting data to the standard output device. This macro allows a variable number of parameters. For each parameter present in the list, fileio.put will call the appropriate routine to emit that data, according to the type of the parameter. Parameters may be constants, registers, or memory locations. You must separate each macro parameter with a comma.

Here is an example of a typical invocation of stdout.put:

 

fileio.put( "I=", i, " j=", j, nl );

 

The above is roughly equivalent to

 

fileio.puts( "I=" );

fileio.puti32( i );

fileio.puts( " j=" );

fileio.puti32( j );

fileio.newln();

 

This assumes, of course, that i and j are int32 variables.

The fileio.put macro also lets you specify the minimum field width for each parameter you specify. To print a value using a minimum field width, follow the object you wish to print with a colon and the value of the minimum field width. The previous example, using field widths, could look like the following:

 

fileio.put( "I=", i:2, " j=", j:5, nl );

 

Although this example used the literal decimal constants two and five for the field widths, keep in mind that register values and memory value (integers, anyway) are prefectly legal here.

For floating point numbers you wish to display in decimal form, you can specify both the minimum field width and the number of digits to print to the right of the decimal point by using the following syntax:

 

fileio.put( "Real value is ", f:10:3, nl );

 

The fileio.put macro can handle all the basic primitive types, including boolean, unsigned (8, 16, 32, 64, 128), signed (8, 16, 32, 64, 128), character, character set, real (32, 64, 80), string, and hexadecimal (byte, word, dword, qword, lword).

If you specify a class variable (object) and that class defines a " toString " method, then fileio.put macro will call the associated toString method and output that string to the file. Note that the toString method must dynamically allocate storage for the string by calling stralloc . This is because fileio.put will call strfree on the string once it outputs the string.

There is a known "design flaw" in the fileio.put macro. You cannot use it to print HLA intermediate variables (i.e., non-local VAR objects). The problem is that HLA's syntax for non-local accesses takes the form "reg32:varname" and fileio.put cannot determine if you want to print reg32 using varname print positions versus simply printing the non-local varname object. If you want to display non-local variables you must copy the non-local object into a register, a static variable, or a local variable prior to using fileio.put to print it. Of course, there is no problem using the other fileio.putXXXX functions to display non-local VAR objects, so you can use those as well.

File Input Routines

The HLA Standard Library provides a complementary set of file input routines. These routines behave in a fashion quite similar to the stdin.XXXX routines. See those routines for a additional examples of these procedures.

General File Input Routines
fileio.read( Handle:dword; var buffer:byte; count:uns32 )

This routine reads a sequence of count bytes from the specified file, storing the bytes into memory at the address specified by buffer .

fileio.readLn( Handle:dword );

This function reads, and discards, all characters in the file up to the next newline sequence (or end of file).

fileio.eoln( Handle:dword ); returns( "al" );

This function returns true (1) in EAX if the file is at the end of a line (note that the "returns" value is "al" even though this function returns its result in all of EAX). This function eats the newline sequence from the input.

Character and String Input Routines

The following functions read character data from an input file specified by filevar . Note that HLA's fileio module does not provide the ability to read character set data directly from the user. However, you can always read a string and then convert that string to a character set using the appropriate function in the cset module.

fileio.getc( Handle:dword ); returns( "al" );

This function reads a single character from the file and returns that chraacter in the AL register. This function assumes that the file you've opened is a text file. Note that fileio.getc does not return the end of line sequence as part of the input stream. Use the fileio.eoln function to determine when you've reached the end of a line of text. Because fileio.getc preprocesses the text file (removing end of line sequences) you should not use it to read binary data, use it only to read text files.

fileio.gets( Handle:dword; s:string );

This function reads a sequence of characters from the current file position through to the next end of line sequence and stores these characters (without the end of line sequence) into the string variable you pass as a parameter. Before calling this routine, you must allocate sufficient storage for the string. If fileio.gets attempts to read a larger string than the string's MaxStrLen value, fileio.gets raises a string overflow exception.

Note that this function does not store the end of line sequence into the string, though it does consume the end of line sequence. The next character a fileio function will read from the file will be the first character of the following line.

If the current file position is at the end of some line of text, then fileio.gets consumes the end of line and stores the empty string into the s parameter.

fileio.a_gets( Handle:dword ); returns( "eax" );

Like fileio.gets , this function also reads a string from the file. However, rather than storing the string data into a string you supply, this function allocates storage for the string on the heap and returns a pointer to this string in the EAX register. You code should call strfree to release this storage when you're done with the string data.

The fileio.a_gets function imposes a line length limit of 1,024 characters. If this is a problem, you should modify the source code for this function to raise the limit. This functions raises an exception if you attempt to read a line longer than this internal limit.

Signed Integer Input Routines
fileio.geti8( Handle:dword ); returns( "al" );

This function reads a signed eight-bit decimal integer in the range -128..+127 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.geti8 function raises an appropriate exception if the input violates any of these rules or the value is outside the range -128..+127. This function returns the binary form of the integer in the AL register.

fileio.geti16( Handle:dword ); returns( "ax" );

This function reads a signed 16-bit decimal integer in the range -32768..+32767 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.geti16 function raises an appropriate exception if the input violates any of these rules or the value is outside the range -32768..+32767. This function returns the binary form of the integer in the AX register.

fileio.geti32( Handle:dword ); returns( "eax" );

This function reads a signed 32-bit decimal integer in the (approximate) range ±2 Billion from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.geti32 function raises an appropriate exception if the input violates any of these rules or the value is outside the range plus or minus two billion. This function returns the binary form of the integer in the EAX register.

fileio.geti64( Handle:dword );

This function reads a signed 64-bit decimal integer from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.geti64 function raises an appropriate exception if the input violates any of these rules or the value is outside the range of a 64-bit signed integer. This function returns the 64-bit result in EDX:EAX.

fileio.geti128( Handle:dword; var dest:lword );

This function reads a signed 128-bit decimal integer from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.geti128 function raises an appropriate exception if the input violates any of these rules or the value is outside the range of a 128-bit signed integer. This function stores the 128-bit result in the lword you pass as a reference parameter.

Unsigned Integer Input Routines
fileio.getu8( Handle:dword ); returns( "al" );

This function reads an unsigned eight-bit decimal integer in the range 0..+255 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getu8 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..255. This function returns the binary form of the integer in the AL register.

fileio.getu16( Handle:dword ); returns( "ax" );

This function reads an unsigned 16-bit decimal integer in the range 0..+65535 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getu16 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..65535. This function returns the binary form of the integer in the AX register.

fileio.getu32( Handle:dword ); returns( "eax" );

This function reads an unsigned 32-bit decimal integer in the range 0..+4,294,967,295 from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getu32 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..4,294,967,295. This function returns the binary form of the integer in the EAX register.

fileio.getu64( Handle:dword );

This function reads an unsigned 64-bit decimal integer from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getu64 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..264-1. This function returns the binary form of the integer in the the EDX:EAX registers.

fileio.getu128( Handle:dword; var dest:lword );

This function reads an unsigned 128-bit decimal integer from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getu128 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..2128-1. This function returns the binary form of the integer in the lword parameter you pass by reference.

Hexadecimal Input Routines
fileio.getb( Handle:dword ); returns( "al" );

This function reads an eight-bit hexadecimal integer in the range 0..$FF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.geth function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FF. This function returns the binary form of the value in the AL register.

fileio.getw( Handle:dword ); returns( "ax" );

This function reads a 16-bit hexadecimal integer in the range 0..$FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getw function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF. This function returns the binary form of the value in the AX register.

fileio.getd( Handle:dword ); returns( "eax" );

This function reads a 32-bit hexadecimal integer in the range 0..$FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getd function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF. This function returns the binary form of the value in the EAX register.

fileio.getq( Handle:dword );

This function reads a 64-bit hexadecimal integer in the range 0..$FFFF_FFFF_FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getq function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF_FFFF_FFFF. This function returns the 64-bit result in the EDX:EAX register pair.

fileio.getl( Handle:dword; var dest:lword );

This function reads a 128-bit hexadecimal integer in the range zero through $FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The fileio.getq function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF. This function stores the 128-bit result into the variable you pass as a reference parameter.

Floating Point Input
fileio.getf( Handle:dword );

This function reads an 80-bit floating point value in either decimal or scientific from from the file and leaves the result sitting on the FPU stack. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a sequence of characters that represent a floating point value. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. This function raises an appropriate exception if an error occurs.

Generic File Input
fileio.get( List_of_items_to_read );

This is a macro that allows you to specify a list of variable names as parameters. The fileio.get macro reads an input value for each item in the list and stores the resulting value in each corresponding variable. This macro determines the type of each variable that you pass it and emits a call to the appropriate fileio.getxxx function to read the actual value. As an example, consider the following call to filevar.get :

fileio.get( i32, charVar, u16, strVar );

 

The macro invocation above expands into the following:

push( eax );

fileio.geti32( i32 );

fileio.getc();

mov( al, charVar );

fileio.geti16();

mov( ax, u16 );

fileio.gets( strVar );

pop( eax );

 

Notice that fileio.get preserves the value in the EAX and EDX registers even though various fileio.getxxx functions use these registers. Note that fileio.get automatically handles the case where you specify EAX as an input variable and writes the value to [esp] so that in properly modifies EAX upon completion of the macro expansion.

Note that fileio.get supports eight-bit, 16-bit, 32-bit, 64-bit, and 128-bit input values. It automatically selects the approriate input routine based on the type of the variable you specify.

 

The Linux Module (linux.hhf)

The Linux module contains constants, data types, procedure prototypes, and other declarations needed to make Linux system calls. Obviously, only HLA/Linux users should be making calls to this module. Warning: it is perfectly possible to compile the Linux module under Windows and attempt to run the resulting code. However, this will surely crash the system.

Linux systems calls are made via the INT($80) instruction. The HLA Linux module provides wrappers for all these calls so you can invoke them using a high level syntax. Calling the HLA Linux wrappers is a much better idea than embedding INT($80) invocations directly in your code. Sometimes the Linux system calls change (hopefully for the better). Although the Linux developers have done a good job of maintaining older calls in the kernel, your programs that make these older calls may not benefit from additional functionality added to the kernal. On the other hand, if all your programs call the HLA wrapper routines for Linux, then you've only got to change the call in one location (in the wrapper), rather than throughout all your projects, whenever a system call changes.

This section will not attempt to document each of the Linux calls that HLA's Linux module provides. You can read all about these functions in Linux use the "man -S 2 function_name" command. The calling sequence is (usually) identical to the "C" interface, so there is no need to regurgitate that information here. Because C and HLA have different sets of reserved words, there were a few conflicts between the standard (C-based) function names and the names that HLA uses, this section will elaborate on those. Also, C's structs and functions use a different namespace and the Linux (UNIX) kernel programmers have employed the dubious style of using the same name for functions and structures. Since HLA doesn't allow this, some type names have been changed, as well. Finally, to prevent namespace pollution in HLA programs, HLA actually uses a NAMESPACE to hold the Linux identifiers (much like other library modules in HLA), so common Linux function names and datatypes will require a "linux." prefix.

It is not good programming style to use all uppercase within identifiers (despite the long-standing C/Unix tradition). Therefore, most all-uppercase constant identifiers you'd normally find in a Linux header file use the HLA convention of lowercase (which is easier to read). Generally, when there is a conflict between a C identifier and an HLA reserved word, the conflict is resolved by prepending an underscore. For example, the Linux system call "exit" becomes "_exit" in HLA (since exit is an HLA reserved word). The two common exceptions to this rule are for the identifiers "name" and "value" (both HLA reserved words) which are usually converted to "theName" or "theValue" in the linux.hhf header file.

Whenever a type name conflicts with a procedure name, the linux.hhf header file appends "_t" to the type name. This is a common Unix practice and one wonders why these structure names didn't have the "_t" suffix to begin with.

Certain Linux functions are overloaded allowing one, two, or possibly three parameters. The linux.hhf header file contains two or three prototypes for each function, each with a fixed number of parameters. However, you can still call the functions using the standard C syntax because HLA provides macros to simulate function overloading (i.e., a variable number of parameters) for these particular functions. Generally, the macro uses the standard C/Linux name (e.g. linux.sysfs) while the actual HLA procedures use a numeric suffix to denote the number of parameters (e.g., linux.sysfs1, linux.sysfs2, and linux.sysfs3).

Please see the "linux.hhf" header file for more details on the spelling of Linux constants, types, and function names.

You should also note that there is a list of constants that begin with "sys_" at the end of the header file. These are the function opcodes that one passes in EAX to the INT($80) system call. If you're going to make the INT($80) calls directly yourself, you should, at least, use these symbol names (e.g., "sys_exit").

The Lists Module (lists.hhf)

The HLA Standard Library provides a generic list abstract data type via the lists module. The lists module provides two classes: a generic list class and a generic, abstract, node class. These classes have the following definitions:

 

node:

class

 

var

Prev: pointer to node;

Next: pointer to node;

 

procedure create; returns( "esi" ); external;

method destroy; abstract;

 

endclass;

 

list:

class

 

var

Head: pointer to node;

Tail: pointer to node;

Cnt: uns32;

#if( @CurOffset mod 4 <> 0 )

 

Padding: byte[ 4 - (@CurOffset mod 4) ];

 

#endif

 

procedure create; returns( "esi" );

method destroy;

method numNodes; returns( "eax" );

 

method append_index( var n:node; posn: dword ); returns( "esi" );

method append_node( var n:node; var after: node ); returns( "esi" );

method append_last( var n:node ); returns( "esi" );

method insert_index( var n:node; posn:dword ); returns( "esi" );

method insert_node( var n:node; var before:node ); returns( "esi" );

method insert_first( var n:node ); returns( "esi" );

method delete_index( posn:dword );

method delete_node( var n:node );

method delete_first;

method delete_last;

method index( posn:dword );

iterator itemInList;

 

endclass;

LIST Class Definitions

The node class is an abstract base class from which you must derive a node type for the nodes in your list. You would normally override the node.create procedure and write a procedure that specifically allocates storage for an object of type node and initializes any important data fields. If you like, your overloaded create procedure can call node.create to initialize the link fields of the node you create, although this is not strictly necessary.

The node.destroy method is an abstract method that you must override. The list.destory method calls node.destroy (or, at least, your overloaded version of it) in order to free the storage associated with a given node. A typical concrete implementation of this function looks like the following:

 

method MyNode.destroy; @nodisplay; @noframe;

begin destroy;

 

// On entry, ESI points at the current node object.

// Free the storage associated with this node.

 

if( isInHeap( esi )) then

 

free( esi );

 

endif;

 

end destroy;

 

For a typical example of an overloaded node class, see the listDemo.hla example in the HLA examples subdirectory.

The list class is an abstract data type used to maintain lists of nodes. Internally, the list class represents lists of nodes using a doubly-linked list, although your applications should not be aware of the internal implementation. Likewise, for efficiency reasons the list class maintains a pointer to the head of the list, a pointer to the tail of the list, and a count of the number of nodes currently in the list. Your applications should ignore these fields (note that you can obtain the number of nodes in the list by calling the numNodes method).

The following subsections describe each of the methods available in the lists class

List Maintenance
procedure list.create; returns( "esi" );

If you call this class procedure via "list.create( )" it will allocate storage for a new list object, initialize the fields of that object (to the empty list), and return a pointer to that list object. If you call this class procedure via "someListVarName.create( )" then this procedure will initialize the (presumably) allocated list object (again, to the empty list).

method list.destroy;

This method frees the storage associated with each node in the list (if the individual nodes were allocated on the heap), it then frees the storage associated with the list object itself, assuming the list was allocated on the heap. Note that successful execution of this method requires that you create a derived class from the abstract base class node and that you've overridden the node.destroy method. The list.destroy method deallocates the nodes in the list by calling the node.destroy method for each node in the list.

Adding Nodes to a List
method list.append_index( var n:node; posn: dword ); returns( "esi" );

This method appends node n to the list after node posn in the list. If posn is greater than or equal to the number of nodes in the list, then this method appends node n to the end of the list. Normally, you would not call this method directly. Instead, you would use the

 

listVar.append(n,posn)

 

macro to call this method. This function returns a pointer to node n in ESI. As with most method invocations, this call wipes out the value in EDI.

method list.append_node( var n:node; var after: node ); returns( "esi" );

This method inserts node n in the object list immediately after node after in that list. This method assumes that after is a node in the object's list; it does not validate this fact. Therefore, you must ensure that after is a member of the object's list. Normally, you would not call this function directly; instead, you would invoke the listVar.append( n, after ) macro to do the work. This function returns a pointer to node n in ESI. As with most method invocations, this call wipes out the value in EDI.

method list.append_last( var n:node ); returns( "esi" );

This method appends node n to the end of the object list. Normally you would not call this method directly, instead you would just invoke the listVar.append(n) macro. This function returns a pointer to node n in ESI. As with most method invocations, this call wipes out the value in EDI.

method list.insert_index( var n:node; posn:dword ); returns( "esi" );

This method inserts node n before the posnth node in the list. If posn is greater than or equal to the number of nodes in the list, this method simply appends the node to the end of the list (remember, nodes are numbered from 0..Cnt-1; so if posn=Cnt then that would imply inserting the node at the end of the list). Normally you would not call this method directly; instead, you'll invoke the listVar.insert( n, posn) macro to do the job. This function returns a pointer to node n in ESI. As with most method invocations, this call wipes out the value in EDI.

method list.insert_node( var n:node; var before:node ); returns( "esi" );

This method inserts node n before node before in the object's list. This method assumes that before is an actual member of the list, it does not verify this prior to insertion. You would not normally call this routine directly. Instead, invoke the listVar.insert( n, before ) macro to do th actual work. This function returns a pointer to node n in ESI. As with most method invocations, this call wipes out the value in EDI.

method list.insert_first( var n:node ); returns( "esi" );

This function inserts node n at the beginning of the object's list. You would not normally call this method directly; you should normally invoke the listVar.insert( n ) macro and let it do all the work. This function returns a pointer to node n in ESI. As with most method invocations, this call wipes out the value in EDI.

Removing Nodes from a List
method list.delete_index( posn:dword ); returns( "esi" );

This method removes the posn th node from the list and returns a pointer to this node in ESI. Normally you would invoke the listVar.delete( posn ) macro rather than calling this method directly.

method list.delete_node( var n:node ); returns( "esi" );

This method removes node n from the list and returns a pointer to this node in ESI. This method assumes that node n actually is in the list; it does not verify this. Normally, you would invoke the listVar.delete(n) macro rather than call this method directly.

method list.delete_first; returns( "esi" );

This method removes the first node from the list and returns a pointer to this node in ESI. Normally you would not call this method directly but you would invoke the listVar.delete() macro instead.

method list.delete_last; returns( "esi" );

This method removes the last node from the list and returns a pointer to this node in ESI.

Accessing Nodes in a List
method list.index( posn:dword ); returns( "esi" );

This method returns a pointer to the posn th node in the list in the ESI register. It returns NULL if the list is empty. It returns the address of the last node in the list if posn >= Cnt .

iterator list.itemInList;

This iterator returns a pointer to each node in a list in the ESI register. Like most iterators, you normally use this iterator within a FOREACH loop.

method list.numNodes; returns( "eax" );

This function returns the number of nodes currently in the list in the EAX register. You should always call this routine rather than access the list.Cnt field directly.

 

Mathematical Functions, Trigonometic and Logarithmic (math.hhf)

The HLA math library module provides several large integer arithmetic/logical, trigonometic, and logarithmic routines that extend those provided directly in the FPU. Note that many of the transcendental functions place strict limits on the values of their parameters. Since a reasonable math text or the Intel documentation for details.

math.addq( left:qword; right:qword; var dest:qword );

math.addl( left:lword; right:lword; var dest:lword );

These routines add two quad-word 64-bit (addq) or long word 128-bit (addl) integer values. The values may be signed or unsigned. These two routines compute the following:

dest := left + right;

 

These routines set the 80x86 flags exactly the same way that the standard ADD instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry and overflow flags denote unsigned or signed overflow (respectively).

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision addition requires about the same number of instructions has it takes to simply pass the parameters to the math.addq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision add sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "three-address" form that allows a different destination address (i.e., the destination does not have to be the same as one of the source operands). Certainly the math.addl routine is more cost effective to use than the math.addq routine, since there's a lot more work involved in doing a 128-bit addition vs. a 64-bit addition (the 64-bit addition takes only six instructions whereas the 128-bit addition requires twelve; therefore, the extra instructions involved in calling math.addl don't completely overshadow the actual work done).

The Standard Library does not provide an "ADd with Carry" version of this procedure. If you need to do extended precision arithmetic beyond 128 bits (or some size other than 64 or 128 bits), then use discrete x86 instructions.

 

math.andq( left:qword; right:qword; var dest:qword );

math.andl( left:lword; right:lword; var dest:lword );

These routines logically AND two quad-word 64-bit (andq) or long word 128-bit (andl) integer values. These two routines compute the following:

dest := left & right;

 

These routines set the 80x86 flags exactly the same way that the standard AND instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry and overflow flags are both clear.

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision AND requires about the same number of instructions has it takes to simply pass the parameters to the math.andq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision AND sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "three-address" form that allows a different destination address (i.e., the destination does not have to be the same as one of the source operands). Certainly the math.andl routine is more cost effective to use than the math.andq routine, since there's a lot more work involved in doing a 128-bit AND vs. a 64-bit AND (the 64-bit AND takes only six instructions whereas the 128-bit AND requires twelve; therefore, the extra instructions involved in calling math.andl don't completely overshadow the actual work done).

 

math.divq( left:qword; right:qword; var dest:qword );

math.divl( left:lword; right:lword; var dest:lword );

These routines divide two quad-word 64-bit (divq) or long word 128-bit (divl) unsigned integer values. These two routines compute the following:

dest := left div right;

 

Since the 80x86 flags don't contain useful values after the execution of the div instruction, these routines also leave the flags scrambled and you can't count on flag values upon return. This routines will raise an ex.DivideError exception if you attempt a division by zero.

 

math.idivq( left:qword; right:qword; var dest:qword );

math.idivl( left:lword; right:lword; var dest:lword );

These routines divide two quad-word 64-bit (idivq) or long word 128-bit (idivl) signed integer values. These two routines compute the following:

dest := left div right;

 

Since the 80x86 flags don't contain useful values after the execution of the idiv instruction, these routines also leave the flags scrambled and you can't count on flag values upon return. This routines will raise an ex.DivideError exception if you attempt a division by zero.

 

math.modq( left:qword; right:qword; var dest:qword );

math.modl( left:lword; right:lword; var dest:lword );

These routines divide two quad-word 64-bit (modq) or long word 128-bit (modl) unsigned integer values and compute their remainder. These two routines compute the following:

dest := left mod right;

 

Since the 80x86 flags don't contain useful values after the execution of the div instruction, these routines also leave the flags scrambled and you can't count on flag values upon return. This routines will raise an ex.DivideError exception if you attempt a division by zero.

 

math.imodq( left:qword; right:qword; var dest:qword );

math.imodl( left:lword; right:lword; var dest:lword );

These routines divide two quad-word 64-bit (imodq) or long word 128-bit (imodl) signed integer values and compute their remainder. These two routines compute the following:

dest := left mod right;

 

Since the 80x86 flags don't contain useful values after the execution of the idiv instruction, these routines also leave the flags scrambled and you can't count on flag values upon return. This routines will raise an ex.DivideError exception if you attempt a division by zero.

 

math.imulq( left:qword; right:qword; var dest:qword );

math.imull( left:lword; right:lword; var dest:lword );

These routines multiply two quad-word 64-bit (mulq) or long word 128-bit (mull) unsigned integer values and compute their product. These two routines compute the following:

dest := left * right;

 

These instructions set the carry and overflow flags if there is an unsigned overflow during the operation.

 

math.imulq( left:qword; right:qword; var dest:qword );

math.imull( left:lword; right:lword; var dest:lword );

These routines divide two quad-word 64-bit (imodq) or long word 128-bit (imodl) signed integer values and compute their remainder. These two routines compute the following:

dest := left mod right;

 

Since the 80x86 flags don't contain useful values after the execution of the idiv instruction, these routines also leave the flags scrambled and you can't count on flag values upon return. This routines will raise an ex.DivideError exception if you attempt a division by zero.

 

math.negq( source:qword; var dest:qword );

math.negl( source:lword; var dest:lword );

These routines negate the source operand and store the result into the destination operand. These two routines compute the following:

dest := -source;

 

These functions leave the 80x86 flags containing the same values one would expect after the execution of the neg instruction. Extended precision NEG is a trivial operation to compute manually; you should carefully consider whether you really want to use this function since doing the NEG manually isn't a whole lot more work.

 

math.notq( source:qword; var dest:qword );

math.notl( source:lword; var dest:lword );

These routines invert all the bits in the source operand and store the result into the destination operand. These two routines compute the following:

dest := ~source;

 

These functions leave the 80x86 flags containing the same values one would expect after the execution of the not instruction. Extended precision NOT is an especially trivial operation to compute manually; you should carefully consider whether you really want to use this function since doing the NOT manually isn't a whole lot more work.

 

math.orq( left:qword; right:qword; var dest:qword );

math.orl( left:lword; right:lword; var dest:lword );

These routines logically OR two quad-word 64-bit (andq) or long word 128-bit (andl) integer values. These two routines compute the following:

dest := left | right;

 

These routines set the 80x86 flags exactly the same way that the standard OR instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry and overflow flags are both clear.

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision OR requires about the same number of instructions has it takes to simply pass the parameters to the math.orq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision OR sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "three-address" form that allows a different destination address (i.e., the destination does not have to be the same as one of the source operands). Certainly the math.orl routine is more cost effective to use than the math.orq routine, since there's a lot more work involved in doing a 128-bit OR vs. a 64-bit OR (the 64-bit OR takes only six instructions whereas the 128-bit OR requires twelve; therefore, the extra instructions involved in calling math.orl don't completely overshadow the actual work done).

 

math.shlq( count:uns32; source:qword; var dest:qword );

math.shll( count:uns32; source:lword; var dest:lword );

These routines logically shift left a quad-word 64-bit (shlq) or long word 128-bit (shll) integer value the number of bits specified by the count operand. These two routines compute the following:

dest := source << count;

 

These routines set the 80x86 flags exactly the same way that the standard SHL instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry flag contains the last carry out of the H.O. bit, and the overflow flag is set if the last shift caused a sign change.

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision SHL requires fewer instructions than it takes to simply pass the parameters to the math.shlq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision SHL sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "two-address" form that allows a different destination address (i.e., the destination does not have to be the same as the source operand). Certainly the math.shll routine is more cost effective to use than the math.shlq routine, since there's a lot more work involved in doing a 128-bit SHL vs. a 64-bit SHL.

 

math.shrq( count:uns32; source:qword; var dest:qword );

math.shrl( count:uns32; source:lword; var dest:lword );

These routines logically shift right a quad-word 64-bit (shlq) or long word 128-bit (shll) integer value the number of bits specified by the count operand. These two routines compute the following:

dest := source >> count;

 

These routines set the 80x86 flags exactly the same way that the standard SHR instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry flag contains the last carry out of the L.O. bit, and the overflow flag is set if the last shift caused a sign change.

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision SHR requires fewer instructions than it takes to simply pass the parameters to the math.shrq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision SHR sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "two-address" form that allows a different destination address (i.e., the destination does not have to be the same as the source operand). Certainly the math.shrl routine is more cost effective to use than the math.shrq routine, since there's a lot more work involved in doing a 128-bit SHR vs. a 64-bit SHR.

 

math.subq( left:qword; right:qword; var dest:qword );

math.subl( left:lword; right:lword; var dest:lword );

These routines subtract two quad-word 64-bit (subq) or long word 128-bit (subl) integer values. The values may be signed or unsigned. These two routines compute the following:

dest := left - right;

 

These routines set the 80x86 flags exactly the same way that the standard SUB instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry and overflow flags denote unsigned or signed overflow (respectively).

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision subtraction requires about the same number of instructions has it takes to simply pass the parameters to the math.subq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision subtract sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "three-address" form that allows a different destination address (i.e., the destination does not have to be the same as one of the source operands). Certainly the math.subl routine is more cost effective to use than the math.subq routine, since there's a lot more work involved in doing a 128-bit subtraction vs. a 64-bit subtractopm (the quad word takes only six instructions whereas the long word subtraction requires twelve; therefore, the extra instructions involved in calling math.subl don't completely overshadown the actual work done).

The Standard Library does not provide an "SuBtract with Borrow" version of this procedure. If you need to do extended precision arithmetic beyond 128 bits (or some size other than 64 or 128 bits), then use discrete x86 instructions.

 

math.xorq( left:qword; right:qword; var dest:qword );

math.xorl( left:lword; right:lword; var dest:lword );

These routines logically XOR two quad-word 64-bit (xorq) or long word 128-bit (xorl) integer values. These two routines compute the following:

dest := left ^ right;

 

These routines set the 80x86 flags exactly the same way that the standard X OR instruction does. In particular, you may test the zero flag afterwards for a zero result, you can test the sign flag to determine if there was a negative result, the carry and overflow flags are both clear.

Extended precision arithmetic (especially 64-bits) is fairly trivial and an in-line coding of these functions will always be faster than calling these functions. For example, a full 64-bit extended precision XOR requires about the same number of instructions has it takes to simply pass the parameters to the math.xorq routine. Therefore, do not call these routines if performance is important, use in-line code instead. Another reason (beyond the procedure call overhead) that these procedures are slower than the in-line code is that the standard extended precision XOR sequence does not set the zero flag properly; these procedures have to execute several additional instructions to preserve the carry, sign, and overflow flags as well as properly set the zero flag. If you don't use the value of the zero flag upon return, all this extra work goes to waste.

These procedures are convenient to use and are perfectly acceptable when performance is not an issue. Another advantage is that these routines work memory to memory and don't disturb the values in any registers; and also, these routines use a "three-address" form that allows a different destination address (i.e., the destination does not have to be the same as one of the source operands). Certainly the math.orl routine is more cost effective to use than the math.xorq routine, since there's a lot more work involved in doing a 128-bit SXOR vs. a 64-bit XOR (the 64-bit XOR takes only six instructions whereas the 128-bit XOR requires twelve; therefore, the extra instructions involved in calling math.xorl don't completely overshadow the actual work done).

 

 

math.cot; returns( "st0" ); // Macro that overloads the following functions:
math._cot; returns( "st0" );

math.cot32( r32:real32 ); returns( "st0" );

math.cot64( r64: real64 ); returns( "st0" );

math.cot80( r80: real80 ); returns( "st0" );

These five functions compute the cotangent (1/tan) of their parameter value. The parameter value must specify an angle in radians.

The math.cot function is actually a macro that overloads the remaining four functions. If a math.cot invocation doesn't contain any parameters, then this macro expands to a call to the math._cot function which computes the cotangent of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._cot();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.csc
math._csc; returns( "st0" );

math.csc32( r32:real32 ); returns( "st0" );

math.csc64( r64: real64 ); returns( "st0" );

math.csc80( r80: real80 ); returns( "st0" );

These five functions compute the cosecant (1/sin) of their parameter value. The parameter value must specify an angle in radians.

The math.csc function is actually a macro that overloads the remaining four functions. If a math.csc invocation doesn't contain any parameters, then this macro expands to a call to the math._csc function which computes the cosecant of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._csc;" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.sec
math._sec; returns( "st0" );

math.sec32( r32:real32 ); returns( "st0" );

math.sec64( r64: real64 ); returns( "st0" );

math.sec80( r80: real80 ); returns( "st0" );

These five functions compute the secant (1/cos) of their parameter value. The parameter value must specify an angle in radians.

The math.sec function is actually a macro that overloads the remaining four functions. If a math.sec invocation doesn't contain any parameters, then this macro expands to a call to the math._sec function which computes the secant of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ sec ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.asin
math._asin; returns( "st0" );

math.asin32( r32:real32 ); returns( "st0" );

math.asin64( r64: real64 ); returns( "st0" );

math.asin80( r80: real80 ); returns( "st0" );

These five functions compute the arc sine (sin-1) of their parameter value. They return an angle in radians.

The math.asin function is actually a macro that overloads the remaining four functions. If a math.asin invocation doesn't contain any parameters, then this macro expands to a call to the math._asin function which computes the arc sin of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ asin ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.acos
math._acos; returns( "st0" );

math.acos32( r32:real32 ); returns( "st0" );

math.acos64( r64: real64 ); returns( "st0" );

math.acos80( r80: real80 ); returns( "st0" );

These five functions compute the arc cosine (cos-1) of their parameter value. They return an angle in radians.

The math.acos function is actually a macro that overloads the remaining four functions. If a math.acos invocation doesn't contain any parameters, then this macro expands to a call to the math._acos function which computes the arc cosine of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ acos ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.acot
math._acot; returns( "st0" );

math.acot32( r32:real32 ); returns( "st0" );

math.acot64( r64: real64 ); returns( "st0" );

math.acot80( r80: real80 ); returns( "st0" );

These five functions compute the arc cotangent (cot-1) of their parameter value. They return an angle in radians.

The math.acos function is actually a macro that overloads the remaining four functions. If a math.acos invocation doesn't contain any parameters, then this macro expands to a call to the math._acos function which computes the arc cotangent of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ acos ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.acsc
math._acsc; returns( "st0" );

math.acsc32( r32:real32 ); returns( "st0" );

math.acsc64( r64: real64 ); returns( "st0" );

math.acsc80( r80: real80 ); returns( "st0" );

These five functions compute the arc cosecant (csc-1) of their parameter value. They return an angle in radians.

The math.acsc function is actually a macro that overloads the remaining four functions. If a math.acsc invocation doesn't contain any parameters, then this macro expands to a call to the math._acsc function which computes the arc cosecant of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ acsc ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.asec
math._asec; returns( "st0" );

math.asec32( r32:real32 ); returns( "st0" );

math.asec64( r64: real64 ); returns( "st0" );

math.asec80( r80: real80 ); returns( "st0" );

These five functions compute the arc secant (sec-1) of their parameter value. They return an angle in radians.

The math.asec function is actually a macro that overloads the remaining four functions. If a math.asec invocation doesn't contain any parameters, then this macro expands to a call to the math._asec function which computes the arc secant of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ asec ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.twoToX
math._twoToX; returns( "st0" );

math.twoToX32( r32:real32 ); returns( "st0" );

math.twoToX64( r64: real64 ); returns( "st0" );

math.twoToX80( r80: real80 ); returns( "st0" );

These five functions compute 2x of their parameter value (which is the value of x ).

The math.twoToX function is actually a macro that overloads the remaining four functions. If a math.twoToX invocation doesn't contain any parameters, then this macro expands to a call to the math._twoToX function which computes 2x of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ twoToX ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.TenToX
math._tenToX; returns( "st0" );

math.tenToX32( r32:real32 ); returns( "st0" );

math.tenToX64( r64: real64 ); returns( "st0" );

math.tenToX80( r80: real80 ); returns( "st0" );

These five functions compute 10x of their parameter value (which is the value of x ).

The math.tenToX function is actually a macro that overloads the remaining four functions. If a math.tenToX invocation doesn't contain any parameters, then this macro expands to a call to the math._tenToX function which computes the 10x of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ tenToX ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.exp
math._exp; returns( "st0" );

math.exp32( r32:real32 ); returns( "st0" );

math.exp64( r64: real64 ); returns( "st0" );

math.exp80( r80: real80 ); returns( "st0" );

These five functions compute ex of their parameter value (which is the value of x ).

The math.exp function is actually a macro that overloads the remaining four functions. If a math.exp invocation doesn't contain any parameters, then this macro expands to a call to the math._exp function which computes ex of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ exp ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.ytoX; returns( "st0" );
math._yToX; // Y is at ST1, X is at ST0.

math.yToX32( y32Var, x32Var ); returns( "st0" );

math.yToX64( y64Var, x64Var ); returns( "st0" );

math.yToX80( y80Var, x80Var ); returns( "st0" );

These five functions compute Yx of their parameter values (which are the values of y and x ).

The math.yToX function is actually a macro that overloads the remaining four functions. If a math.yToX invocation doesn't contain any parameters, then this macro expands to a call to the math._yToX function which computes Yx using the values on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ yToX ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.log
math._log; returns( "st0" );

math.log32( r32:real32 ); returns( "st0" );

math.log64( r64: real64 ); returns( "st0" );

math.log80( r80: real80 ); returns( "st0" );

These five functions compute log10(x) of their parameter value (which is the value of x ).

The math.log function is actually a macro that overloads the remaining four functions. If a math.log invocation doesn't contain any parameters, then this macro expands to a call to the math._log function which computes the base 10 log of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ log ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

math.ln
math._ln; returns( "st0" );

math.ln32( r32:real32 ); returns( "st0" );

math.ln64( r64: real64 ); returns( "st0" );

math.ln80( r80: real80 ); returns( "st0" );

ln( x ) [loge(x)]

These five functions compute loge(x) of their parameter value (which is the value of x ).

The math.ln function is actually a macro that overloads the remaining four functions. If a math.ln invocation doesn't contain any parameters, then this macro expands to a call to the math._ln function which computes the base e log of the value on the FPU stack (ST0). With a single real parameter, the macro expands to one of the other three functions with the appropriate parameter type.

The "math._ ln ();" call expects the parameter on the FPU stack, the other three forms pass their parameter by value on the stack using the standard HLA parameter passing mechanism.

Memory Allocation (memory.hhf)

The memory unit (header file is memory.hhf) contains the routines used to allocate and deallocate dynamic storage on the heap. There are a set of routines that allocate storage for general objects and a set of routines used to specifically allocate storage for strings.

Generic Memory Allocation
malloc( size:dword ); returns( "eax" );

The malloc routine allocates the requested number of bytes. If successful, this routine returns a pointer to the allocated storage in the EAX register. This routine raises an exception if it fails.

free( memptr:dword );

This function frees up storage previously allocated by the malloc routine. A pointer returned from malloc must be passed as the parameter to this function.

realloc( memptr:dword; newsize:dword ); returns( "eax" );

The realloc routine resizes a previous allocated block of memory. The first parameter is the pointer to the original block, the second parameter is the new size. If the new block is smaller, this routine truncates the data beyond the new size. If the new block is larger, this routine will copy the data if it cannot expand the block in-place.

isInHeap( memptr:dword );

This function returns true in EAX if memptr points into the heap. It returns false if memptr is not a pointer into the heap structure. You can use this function to determine whether an object was previously allocated via a call to malloc (and should be free'd via a call to free ).

talloc( size ); (returns "eax" as macro result)

This is a macro that "temporarily" allocates the specified storage. This macro allocates the specified storage on the stack and returns the address of the storage (i.e., the ESP value) in the EAX register. The address is always dword aligned; talloc will allocate up to three additional bytes to ensure dword alignment.

You may use the talloc call anywhere a single instruction is legal (including using talloc as an operand to another instruction).

There is no corresponding "tfree " routine since leaving the current procedure automatically deallocates the storage. That is, when a standard procedure exits, it resets the stack pointer, automatically removing the talloc'd data.

Warning: do not use this routine in a procedure that does not have a standard activation record (e.g., those routines with the @noframe option). Also keep in mind that you should not use this routine after pushing data on the stack since you will not be able to retrieve that data unless you first (manually) remove the talloc'd data.

Obviously, you cannot continue referencing the data allocated by talloc once the enclosing procedure returns.

String Memory Allocation
stralloc( size:dword ); returns( "eax" );

strrealloc( theStr:dword; size:dword ); returns( "eax" );

strfree( memptr:dword );

strIsInHeap( memptr:dword ); returns( "eax" );

The string allocation routines are used just like the general memory allocation routines except they allocate storage for a string variable and initialize the maxlength and length fields of the string data structure.

tstralloc( size ); (returns pointer to new string in EAX ).

This is a macro that initializes storage on the stack for a string capable of holding size characters. This routine has the same benefits and drawbacks as the talloc routine.

Note that the size parameter is the actual number of characters needed. the tstralloc routine automatically bumps this value up by nine to make room for the length , MaxStrLen , and zero terminator fields of the string object. This macro also ensures that the stack (and, therefore, the string) is dword aligned in memory (it does this by adding up to three additional bytes to the string).

The tstralloc routine can be used only as a stand-alone statement in a program. Because of the nature of this macro, you cannot embed this call inside another instruction.

HLA Pattern Matching Routines (patterns.hhf)

The HLA Standard Library provides a set of string/pattern matching routines that are similar in use to those provided by the SNOBOL4 and Icon programming languages. These pattern matching routines support recursion and backtracking, allowing the specification of context-free grammars as well as regular expressions.

Note: all the HLA pattern matching routines are members of the pat namespace, hence you must preface each pattern matching routine with "pat." or HLA will report an error.

Pat.match and Pat.endmatch Syntax

The HLA pat.match and pat.endmatch macros provide the basic tools for pattern matching. These macro statement allow one of the following two syntaxes:

 

// Match syntax #1:

 

pat.match( StringValue );

<< Sequence of match functions>>

<< Code to execute on a successful match >>

 

pat.if_failure

 

<< Code to execute if the match fails >>

 

pat.endmatch;

 

StringValue is either an HLA string variable or a string constant.

 

 

// Match syntax #2:

 

pat.match( StartOfStr, EndOfStr );

<< Sequence of match functions>>

<< Code to execute on a successful match >>

 

pat.if_failure

 

<< Code to execute if the match fails >>

 

pat.endmatch;

 

The StartOfStr and EndOfStr parameters (in syntax #2) must be dword pointers to characters. StartOfStr points at the first character of a sequence of characters to match against. EndOfStr must point at the first byte beyond the last character in the sequence to consider.

The pat.match statement, along with many of the matching functions, pushes data onto the stack that may not be cleaned up until execution of the pat.endmatch statement. Therefore, you must never jump into a pat.match..pat.endmatch block . Likewise, unless you are prepared to clean up the stack yourself, you should not jump out of a pat.match..pat.endmatch block4.

During a normal match operation, the pat.match block executes the sequence of string matching functions. If all the functions in the list execute and successfully match their portion of the string, control falls through to the statements after the match sequence. This code should do whatever is necessary if the pattern matches.

On the other hand, if a failure occurs and the pattern matching routines cannot match the specified string, then control transfers to the pat.if_failure section and the associated statements execute. Like an IF..THEN..ELSE statement, the program automatically jumps over the pat.if_failure section if the "successful match" statements execute.

Consider the following example that matches a string containing a single HLA identifier:

 

pat.match( StrToTest );

 

pat.oneCset( { 'a'..'z', 'A'..'Z', '_'} );

pat.zeroOrMoreCset( { 'a'..'z', 'A'..'Z', '0'..'9', '_'});

pat.EOS;

 

stdout.put( "The string is a valid HLA identifier" nl );

 

pat.if_failure

 

stdout.put( "The string is not a valid HLA id" nl );

 

pat.endmatch;

 

The pat.oneCset function matches a single character in StrToTest that is a member of the character set appearing in the parameter list. This call requires that the first character of StrToTest be an alphabetic character or an underscore.

After pat.oneCset matches a character, the pattern matching routines advance a cursor into StrToTest so that it points just beyond the character matched by pat.oneCset . Indeed, all pattern matching routines operate in this manner, they maintain a cursor (in ESI) that points beyond the characters just matched. So had StrToTest contained the string "Hello", ESI would be pointing at the "e" in "Hello" immediately after the execution of the pat.oneCset pattern matching routine.

The HLA pattern matching routines also return EBX pointing at the first character matched by the routine. In the current example being considered, EBX would be returned pointing at the "H" in "Hello" by the pat.oneCset routine.

The pat.zeroOrMoreCset routine continues where pat.oneCset leaves off. It matches zero or more characters (starting at the location pointed at by ESI). In this particular example, pat.zeroOrMoreCset matches zero or more alphanumeric and underscore characters, hence the code will match "ello" in "Hello".

The pat.EOS macro matches the end of the string, just to make sure there aren't any other illegal (nonalphanumeric) characters in the string. Note that pat.zeroOrMoreCset stops upon encountering the first non-alphanumeric character. The remainder of the pattern (EOS, in this case) must verify that pat.zeroOrMoreCset didn't stop on an illegal character.

Had the StrToTest variable contained the string "Hello", then the pattern would successfully match the string and the program would print "The string is a valid HLA identifier" and continue execution after the pat.endmatch statement.

Because of the way HLA pattern matching routines implement backtracking, each matching routine may leave data on the stack when it successfully returns. This information is necessary to implement backtracking. Although the pat.endmatch code cleans up the stack upon exit, it is important to realize that stack is not static. In particular, you cannot push data on the stack before one pattern matching routine and expect to pop it off the stack when that matching routine returns. Instead, you'll pop the data that the matching routine left on the stack (which will probably crash the system if backtracking occurs). It is okay to manipulate the stack in the code section following all the matching functions (or in the failure section), but you must leave the stack intact between calls to pattern matching routines5.

Alternation

Another way to handle failure is with the pat.alternate macro. A pat.match..pat.endmatch macro invocation may optionally contain one or more pat.alternate sections before the (required) pat.if_failure section. The pat.alternate sections "intercept" failures from the previous section(s) and allow an attempt to rematch the string with a different pattern (somewhat like the ELSEIF clause of an IF..THEN..ELSEIF..ELSE..THEN statement). The following example demonstrates how you could use this:

 

pat.match( StrToTest );

 

pat.oneCset( { 'a'..'z', 'A'..'Z', '_'} );

pat.zeroOrMoreCset( { 'a'..'z', 'A'..'Z', '0'..'9', '_'});

pat.EOS;

 

stdout.put( "The string is a valid HLA identifier" nl );

 

pat.alternate

 

pat.oneOrMoreCset( {'0'..'9', '_'} );

pat.EOS;

 

stdout.put( "The string is a valid HLA unsigned integer constant" nl );

 

pat.if_failure

 

stdout.put( "The string is not a valid HLA id or integer constant" nl );

 

pat.endmatch;

 

In this example, if the pattern fails to match an HLA identifier, the pattern matching code attempts to see if it matches an integer constant (in the pat.alternate section). If this fails as well, then the whole pattern fails to match.

Pattern Matching Macros

The HLA patterns library implements several of the pattern matching routines as keyword macros within the pat.match macro. These include pat.EOS , pat.position, pat.atPos, pat.skip, pat.getPos, pat.fail, pat.fence, pat.zeroOrOnePat, pat.zeroOrMorePat , and pat.oneOrMorePat . The following sections describe each of these functions:

pat.EOS

The pat.EOS macro matches the end of the string. It succeeds if the current "cursor" value (ESI) is pointing at the end of the string to match. It fails otherwise.

pat.position( n )

This function repositions the cursor to character n in the string that pat.match is processing. This function fails if repositioning the cursor would move it outside the bounds of the string. Note that the index of the first character in the string is zero.

pat.atPos( n )

This function succeeds if the cursor is currently at position n in the string that pat.match is processing. It fails otherwise.

pat.skip( n )

This function advances the cursor n positions from its current location. This function succeeds if the new cursor position is within the bounds of the string; it fails otherwise.

pat.getPos( dest )

This function places the current cursor position in the specified destination operand. This function always succeeds.

pat.fail

This forces an immediate failure, backtracking if necessary.

pat.fence

This function cleans all the backtracking information off the stack. Any pattern matching function following fence will not be able to backtrack to the routines immediately preceding fence in the current pat.match statement.

pat.onePat;
<< pattern matching statements >>
pat.endOnePat;

<< pattern matching statements >> are some statemennts that correspond to an HLA pattern sequence (it may contain pattern matching function calls, x86 code, and pat.alternate sections; it may not contain a pat.if_failure section or a pat.fence invocation). The program evaluates the pattern. If it succeeds, control falls to the next statement following the pat.pattern call. If it fails, then control transfers directly to the pat.if_failure section in the surrounding pat.match call.

This macro is primarily used to create "parenthetical patterns" as a convenience when creating complex patterns. Here's an example:

 

pat.match( SomeString );

 

pat.onePat

 

pat.matchStr( "Black" );

 

pat.alternate

 

pat.matchStr( "Blue" );

 

pat.endOnePat;

 

pat.onePat;

 

pat.matchStr( "bird" );

 

pat.alternate

 

pat.matchStr( "berry" );

 

pat.endOnePat;

 

stdout.put

(

"It was 'blackbird', 'bluebird', 'blackberry', or 'blueberry'",

nl

);

 

pat.if_failure

 

stdout.put( "Failed to match the pattern" nl );

 

pat.endmatch;

 

This (rather famous) pattern matches the same thing as the regular expression:

 

( "Black" | "Blue" )( "bird" | "berry" )

 

Immediately after the pat.endOnePat statement, EBX points at the start of the text associated with the pattern match between the pat.onePat and pat.endOnePat calls. Therefore, you can call functions like pat.extract to extract the entire string matched by the pattern between the pat.onePat and pat.endOnePat calls. This function fully supports backtracking, even across the patterns within the parenthetical pattern expression.

pat.zeroOrOnePat;
<< pattern matching statements >>
pat.endZeroOrOnePat;

<< pattern matching statements >> are some statemennts that correspond to an HLA pattern sequence (it may contain pattern matching function calls, x86 code, and pat.alternate sections; it may not contain a pat.if_failure section or a pat.fence invocation). This call invokes the pattern matching function zero or one times to match additional characters in the current string. This function always succeeds since it can match zero times. This function fully supports backtracking.

pat.zeroOrMorePat;
<< pattern matching statements >>
pat.endZeroOrMorePat

Pattern is sequence of pattern matching function calls (just like pat.pattern above; including allowing a pat.alternate section but not allowing a pat.if_failure section). This call invokes the pattern matching function zero or more times to match additional characters in the current string.

pat.oneOrMorePat( Pattern )
<< pattern matching statements >>
pat.endOneOrMorePat

<< pattern matching statements >> are some statemennts that correspond to an HLA pattern sequence (it may contain pattern matching function calls, x86 code, and pat.alternate sections; it may not contain a pat.if_failure section or a pat.fence invocation). This call invokes the pattern matching function one or more times to match additional characters in the current string. It must match at least one occurrence of the pattern in order to succeed.

Pattern Matching Functions

The following sections describe each of the pattern matching functions provided by the HLA patterns module.

Character Set Matching Procedures
procedure pat.peekCset( cst:cset );

Succeeds if the following character is in the specified set. Fails otherwise. Does not advance the cursor if the character is an element of cst.

procedure pat.oneCset( cst:cset );

Succeeds, and advances the cursor, if the character at the cursor position is in cst . Fails otherwise.

procedure pat.upToCset( cst:cset );

Advances the cursor until it finds a character in cst . Fails if none ofthe characters following the cursor position (to the end of the string) are in cst .

procedure pat.zeroOrOneCset( cst:cset )

Optionally matches a single character in the string. If the following character is in the character set, this routine advances the cursor and signals success. If the following character is not in the string, this routine simply signals success.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match the character before returning. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. If the following match routine still fails, then this routine fails.

procedure pat.l_ZeroOrOneCset( cst:cset )

Optionally matches a single character in the string. If the following character is in the character set, this routine advances the cursor and signals success. If the following character is not in the string, this routine simply signals success.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will start by matching zero characters in the string. If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. If the following routine still fails, then this routine signals failure.

procedure pat.zeroOrMoreCset( cst:cset );

Matches zero or more characters in the specified character set.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up beyond the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_ZeroOrMoreCset( cst:cset );

Matches zero or more characters in the specified character set.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.oneOrMoreCset( cst:cset );

Matches one or more characters in the specified character set. Immediately fails if there isn't at least one character in cst .

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_OneOrMoreCset( cst:cset );

Matches one or more characters in the specified character set. Immediately fails if there isn't at least one character in cst .

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., one). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.exactlyNCset( cst:cset; n:uns32 );

Matches exactly n characters that are members of cst . If any of the next n characters are not in cst , this routines returns failure.

Note: The character at position ( n +1) must not be a member of cst or this routine fails.

procedure pat.firstNCset( cst:cset; n:uns32 ); external;

Matches n characters that are members of cst .

Note: The character at position ( n +1) may be a member of cst. Whether or not it is, this routine succeeds if the first n characters are members of cst .

procedure pat.norLessCset( cst:cset; n:uns32 );

This routine matches n or fewer characters belonging to the cst set. The character at position ( n +1) does not affect the success or failure of this routine. Note that this routine returns true even if it matches zero characters.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NorLessCset( cst:cset; n:uns32 );

This routine matches n or fewer characters belonging to the cst set. The character at position ( n +1) does not affect the success or failure of this routine.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.norMoreCset( cst:cset; n:uns32 );

This routine matches at least n characters belonging to the cst set. If fewer than n characters match the set, this routine returns failure.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NorMoreCset( cst:cset; n:uns32 );

This routine matches at least n characters belonging to the cst set. If fewer than n characters match the set, this routine returns failure.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.ntoMCset( cst:cset; n:uns32; m:uns32 );

This routine matches at least n characters and no more than m characters belonging to the cst set. If fewer than n characters match the set, this routine returns failure. This routine does not fail if more than m characters belong to the set. However, it only matches through position m .

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NtoMCset( cst:cset; n:uns32; m:uns32 ); external;

This routine matches at least n characters and no more than m characters belonging to the cst set. If fewer than n characters match the set, this routine returns failure. This routine does not fail if more than m characters belong to the set. However, it only matches through position m .

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond position m (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.exactlyNtoMCset( cst:cset; n:uns32; m:uns32 );

This routine matches at least n characters and no more than m characters belonging to the cst set. If fewer than n characters match the set, this routine returns failure. This routine fails if more than m characters belong to the set.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_ExactlyNtoMCset( cst:cset; n:uns32; m:uns32 ); external;

This routine matches at least n characters and no more than m characters belonging to the cst set. If fewer than n characters match the set, this routine returns failure. This routine fails if more than m characters belong to the set.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond position m (in which case this routine fails) or the following match routine(s) succeed.

Character Matching Procedures
procedure pat.peekChar( c:char );

This routine succeeds if the character pointed at by the cursor (ESI) is equal to c ; it fails otherwise. This routine does not advance the cursor.

procedure pat.oneChar( c:char );

This routine succeeds if the character pointed at by the cursor (ESI) is equal to c ; it fails otherwise. If it succeeds, this routine advances the cursor.

procedure pat.upToChar( c:char );

This routine matches all characters in a string from the cursor position up to the specified parameter. It fails if the specified character is not in the string. Note that this routine leaves the cursor pointing at the character specified by the parameter (i.e., it still remains to be matched).

procedure pat.zeroOrOneChar( c:char );

This routine matches zero or one occurrences of the character parameter.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match one character in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine.

procedure pat.l_ZeroOrOneChar( c:char );

This routine matches zero or one occurrences of the character parameter.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. If that fails, then this routine fails.

procedure pat.zeroOrMoreChar( c:char );

This routine matches zero or more occurrences of the character parameter. It leaves the cursor pointing at the end of the string or the first character that is not equal to c .

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_ZeroOrMoreChar( c:char );

This routine matches zero or more occurrences of the character parameter. It leaves the cursor pointing at the end of the string or the first character that is not equal to c .

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.oneOrMoreChar( c:char );

This routine matches one or more occurrences of the character parameter. It leaves the cursor pointing at the end of the string or the first character that is not equal to c . It fails if there isn't at least one copy of c at the cursor position.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_OneOrMoreChar( c:char );

This routine matches one or more occurrences of the character parameter. It leaves the cursor pointing at the end of the string or the first character that is not equal to c . It fails if there isn't at least one copy of c at the cursor position.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., one). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.exactlyNChar( c:char; n:uns32 );

This routine matches exactly n copies of the character c in the string. If more, or less, copies of c appear in the string, this routine fails.

procedure pat.firstNChar( c:char; n:uns32 );

This routine matches exactly n copies of the character c in the string. If less, copies of c appear in the string, this routine fails. If more copies of c appear in the string, this routine succeeds, however, it only matches the first n copies.

procedure pat.norLessChar( c:char; n:uns32 );

This procedure matches n or fewer copies of c in the current string. If additional copies of c appear in the string, this routine still succeeds but it only matches the first n copies.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NorLessChar( c:char; n:uns32 );

This procedure matches n or fewer copies of c in the current string. If additional copies of c appear in the string, this routine still succeeds but it only matches the first n copies.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.norMoreChar( c:char; n:uns32 );

This procedure matches n or more copies of c in the current string. It fails if there are fewer than n copies of c .

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to position n (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NorMoreChar( c:char; n:uns32 );

This procedure matches n or more copies of c in the current string. It fails if there are fewer than n copies of character c in the string.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.ntoMChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position. This routine succeeds even if there are more than m copies of the character, however, it will only match the first m characters in the string.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to position n (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NtoMChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position. This routine succeeds even if there are more than m copies of the character, however, it will only match the first m characters in the string.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n ). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond position m (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.exactlyNtoMChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position. This routine fails if there are more than m copies of the character in the string.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to position n (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_ExactlyNtoMChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position. This routine fails if there are more than m copies of the character in the string.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n ). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond position m (in which case this routine fails) or the following match routine(s) succeed.

Case Insensitive Character Matching Routines

These routines are semantically identical to the above routines with one difference- when they compare the characters they use a case insensitive comparison. Please see the descriptions above for an explanation of these routines.

procedure pat.peekiChar( c:char );

This routine succeeds if the character pointed at by the cursor (ESI) is equal to c using a case insensitive comparison; it fails otherwise. This routine does not advance the cursor.

procedure pat.oneiChar( c:char );

This routine succeeds if the character pointed at by the cursor (ESI) is equal to c using a case insensitive comparison it fails otherwise. If it succeeds, this routine advances the cursor.

procedure pat.upToiChar( c:char );

Using a case insensitive comparison, this routine matches all characters in a string from the cursor position up to the specified parameter. It fails if the specified character is not in the string. Note that this routine leaves the cursor pointing at the character specified by the parameter (i.e., it still remains to be matched).

procedure pat.zeroOrOneiChar( c:char );

This routine matches zero or one occurrences of the character parameter using a case insenstive comparison.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match one character in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine.

procedure pat.l_ZeroOrOneiChar( c:char );

This routine matches zero or one occurrences of the character parameter using a case insenstive comparison.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. If that fails, then this routine fails.

procedure pat.zeroOrMoreiChar( c:char );

This routine matches zero or more occurrences of the character parameter using a case insenstive comparison. It leaves the cursor pointing at the end of the string or the first character that is not equal to c .

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_ZeroOrMoreiChar( c:char );

This routine matches zero or more occurrences of the character parameter using a case insenstive comparison. It leaves the cursor pointing at the end of the string or the first character that is not equal to c .

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.oneOrMoreiChar( c:char );

This routine matches one or more occurrences of the character parameter using a case insenstive comparison. It leaves the cursor pointing at the end of the string or the first character that is not equal to c . It fails if there isn't at least one copy of c at the cursor position.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_OneOrMoreiChar( c:char );

This routine matches one or more occurrences of the character parameter using a case insenstive comparison. It leaves the cursor pointing at the end of the string or the first character that is not equal to c . It fails if there isn't at least one copy of c at the cursor position.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., one). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.exactlyNiChar( c:char; n:uns32 );

This routine matches exactly n copies of the character c in the string using a case insenstive comparison. If more, or less, copies of c appear in the string, this routine fails.

procedure pat.firstNiChar( c:char; n:uns32 );

This routine matches exactly n copies of the character c in the string using a case insenstive comparison. If less, copies of c appear in the string, this routine fails. If more copies of c appear in the string, this routine succeeds, however, it only matches the first n copies.

procedure pat.norLessiChar( c:char; n:uns32 );

This procedure matches n or fewer copies of c in the current string using a case insenstive comparison. If additional copies of c appear in the string, this routine still succeeds but it only matches the first n copies.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to the original cursor position (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NorLessiChar( c:char; n:uns32 );

This procedure matches n or fewer copies of c in the current string using a case insenstive comparison. If additional copies of c appear in the string, this routine still succeeds but it only matches the first n copies.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., zero). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.norMoreiChar( c:char; n:uns32 );

This procedure matches n or more copies of c in the current string using a case insenstive comparison. It fails if there are fewer than n copies of c .

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to position n (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NorMoreiChar( c:char; n:uns32 );

This procedure matches n or more copies of c in the current string using a case insenstive comparison. It fails if there are fewer than n copies of character c in the string.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond the end of the string (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.ntoMiChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position using a case insenstive comparison. This routine succeeds even if there are more than m copies of the character, however, it will only match the first m characters in the string.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to position n (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_NtoMiChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position using a case insenstive comparison. This routine succeeds even if there are more than m copies of the character, however, it will only match the first m characters in the string.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n ). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond position m (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.exactlyNtoMiChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position using a case insenstive comparison. This routine fails if there are more than m copies of the character in the string.

This function uses an "aggressive" or "eager" pattern matching algorithm. It will attempt to match as many characters as possible in the string. If doing so would cause a following match routine to fail, this routine will backtrack one character and retry the following match routine. This continues until it backs up to position n (in which case this routine fails) or the following match routine(s) succeed.

procedure pat.l_ExactlyNtoMiChar( c:char; n:uns32; m:uns32 );

This procedure matches between n and m copies of the character c starting at the current cursor (ESI) position using a case insenstive comparison. This routine fails if there are more than m copies of the character in the string.

This function uses a "deferred" or "lazy" pattern matching algorithm. It will attempt to match as few characters as possible in the string (i.e., n ). If doing so would cause a following match routine to fail, this routine will backtrack and advance one character and then retry the following match routine. This continues until it advances beyond position m (in which case this routine fails) or the following match routine(s) succeed.

String matching procedures
procedure pat.matchStr( s:string );

If the sequence of characters at the current cursor position (ESI) match the specified string, this routine succeeds, otherwise it fails.

procedure pat.matchiStr( s:string );

Like pat.matchStr , except this routine does a case insensitive comparison.

procedure pat.matchToStr( s:string );

This routine matches all characters up to, and including, the specified string. If it matches a string and a following pattern matching routine fails, this routine handles the backtracking and searches for the next string that matches. The backtracking is lazy insofar is this routine will always match the minimum number of occurrences of s in the string in order to succeed.

procedure pat.upToStr( s:string );

This routine matches all characters up to, but not including, the specified string. If it matches a string and a following pattern matching routine fails, this routine handles the backtracking and searches for the next string that matches. The backtracking is lazy insofar is this routine will always match the minimum number of occurrences of s in the string in order to succeed.

procedure pat.matchToiStr( s:string );

Like pat.matchToStr , except this routine does a case insensitive comparison.

procedure pat.upToiStr( s:string );

Like pat.upToStr , except this routine does a case insensitive comparison.

procedure pat.matchWord( s:string );

This routine is similar to pat.matchStr except that it requires a delimiter character after the string it matches. The delimiter character is a member of the WordDelims character set (internal to the patterns.hhf code). WordDelims is, by default, the character set "-{'a'..'z', 'A'..'Z', '0'..'9', '_'}" (that is, all character except the alphanumeric characters and the underscore). See the getWordDelims and setWordDelims procedures if you are interested in changing the word delimiters set.

procedure pat.matchiWord( s:string );

Just like pat.matchWord , except this routine does a case insensitive comparison.

procedure pat.getWordDelims( var cst:cset );

This function makes a copy of the internal WordDelims character set and places this copy in the specified cst parameter.

procedure pat.setWordDelims( cst:cset);

This function stores the value of the cst character set into the WordDelims character set. This allows you to change the WordDelims character set to your liking.

String Extraction Procedures
procedure pat.extract( s:string );

Whenever a pattern matching routine successfully matches zero or more characters in the string, the pattern matching routine returns a pointer to the start of the matched characters in EBX and a pointer to the position just beyond the last matched position in ESI. You may use the pat.a_extract procedure to create an HLA-compatible string of these matched characters. This routine will raise an exception if the destination string isn't big enough to hold the extracted characters.

procedure pat.a_extract( var s:string );

Whenever a pattern matching routine successfully matches zero or more characters in the string, the pattern matching routine returns a pointer to the start of the matched characters in EBX and a pointer to the position just beyond the last matched position in ESI. You may use the pat.a_extract procedure to create an HLA-compatible string of these matched characters. pat.a_extract will allocate storage for the string on the heap, copy the matched characters to this string, and then store a pointer to the new string in the string variable passed as a reference parameter to pat.a_extract .

Warning: pat.extract and pat.a_extract should only be called in the "success" section of a pat.match..pat.endmatch block. Any other invocation could create a problem. In general, you must ensure that EBX and ESI point at reasonable spots within the same string. Note that pattern match failure does not guarantee that EBX contains a reasonable value. Therefore, you should not use pat.extract or pat.a_extract at a point where string failure could have occurred unless you explicitly set up EBX (and, possibly, ESI) yourself.

Whitespace and End of String Matching Procedures

These convenient routines match a sequence of whitespace characters as well as the end of the current string. By default, these routines assume that whitespace consists of all the control characters, the ASCII space (#$20), and the del code (#$7f). You can change this definition using the pat.getWhiteSpace and pat.setWhiteSpace procedures.

procedure pat.getWhiteSpace( var cst:cset );

This function returns the current value of the internal WhiteSpace character set. It stores the result in the reference parameter.

procedure pat.setWhiteSpace( cst:cset);

This procedure copies the specified character set to the internal WhiteSpace character set. All future whitespace matching procedures will use this new value when matching white space characters.

procedure pat.zeroOrMoreWS;

This routine matches zero more more whitespace characters. This routine uses an "eager" matching algorithm.

procedure pat.oneOrMoreWS;

This routine matches zero more more whitespace characters. This routine uses an "eager" matching algorithm.

procedure pat.WSorEOS;

This routine matches a single whitespace character or the end of the string. It fails if there are characters left in the string and the character at the cursor position is not a white space character.

procedure pat.WSthenEOS;

This routine matches zero or more white space characters that appear at the end of the current string. It fails if there are any other characters before the end of the string.

procedure pat.peekWS;

This routine succeeds if the next character in the string is a whitespace character. However, it does not advance the cursor over the character.

procedure pat.peekWSorEOS;

This routine succeeds if the next character in the string is a white space character or if there are no more characters in the string. It does not advance the cursor.

Match an Arbitrary Sequence of Characters
procedure pat.arb;

This routine matches zero or more characters. It uses an "aggressive" or "eager" matching algorithm, immediately matching all the remaining characters in the string. If following matching routines fail, this routine backtracks one character at a time until reaching the initial starting position (in which case this routine fails) or the following matching routine(s) succeed.

procedure pat.l_arb; external;

This is a "lazy" or "deferred" version of the above routine. It matches zero characters and succeeds; if a following match routine fails, this routine backtracks by advancing the cursor one position for each failure. If this routine advances beyond the end of the string during backtracking, it reports failure.

Writing Your Own Pattern Matching Routines

Although HLA provides a wide variety of pattern matching functions, from which you can probably synthesize any pattern you desire, there are several reasons why you might want to write your own pattern matching routines. Some common reasons include: (1) You would like a more efficient pattern matching function than is possible by composing existing pattern matching functions. (2) You need a particular pattern matching routine to produce a side effect and the standard matching routines do not produce the desired side effect. A common example is a pattern matching routine that returns an attribute value for an item it matches. For example, a routine that matches a string of decimal digits may return the numeric equivalent of that string as an attribute of that pattern. (3) You need a pattern matching routine that considers other machine states (i.e., variable values) besides the string the pattern is processing. (4) You need to handle some context-sensitive issues. (5) You want to understand how the pattern matching algorithm works. Writing your own pattern matching functions can achieve all these goals and many more.

The first issue you must address when writing your own pattern matching routine is whether or not the routine supports back tracking. Generally, this decision depends upon whether the function matches strings that are always a fixed length or can match strings of differing lengths. For example, the pat.oneCset routine always matches a string of length one whereas the pat.zeroOrMoreCset function can match strings of any length. If a function can only match strings having a fixed length, then the function does not need to support back tracking. Generally, pattern matching functions that can match strings of varying lengths should support backtracking6. Since supporting back tracking is more work and less efficient, you should only support it when necessary.

Once you've decided that you're going to support back tracking in a matching function, the next issue that concerns you is whether the function supports eager evaluation or lazy/deferred evaluation. (Note: when writing general matching routines for library use, it's generally a good idea to supply two functions, one that supports eager evaluation and one that supports lazy/deferred evaluation.)

A function that supports eager evaluation tries to match the longest possible string when the program calls the function. If the function succeeds and a later matching functions fails (invoking the backtracking operation), then the matching function backs off the minimum number of characters that will still match. This process continues until the following code succeeds or the function backs off so much that it, too, fails.

If function that support lazy/deferred evaluations tries to match the shortest possible string. Once it matches the shortest string it can, it passes control on to the following pattern matching functions. If they fail and back tracking returns control to the function, it tries to match the next smallest string larger than the one it currently matches. This process repeats until the following match functions succeed or the current function fails to match anything.

Note that the choice of eager vs. lazy/deferred evaluation does not generally affect whether a pattern will match a given string7. It does, however, affect the efficiency of the pattern matching operation. Backtracking is a relatively slow operation. If an eager match causes the following pattern functions to fail until the current pattern matching function backs off to the shortest possible string it can match, the program will run much slower than one that uses lazy evaluation for the function (since it starts with the shortest possible string to begin with). On the other hand, if a function needs to match the longest possible string in order for the following matching functions to succeed, choosing lazy evaluation will run much more slowly than eager evaluation. Therefore, the choice of which form is best to use is completely data dependent. If you have no idea which evaluation form should be better, choose eager evaluation since it is more intuitive to those defining the pattern to match.

All pattern matching routines have two implicit parameters passed to them in the ESI and EDI registers. ESI is the current cursor position while EDI points at the byte immediately after the last character available for matching. That is, the characters between locations ESI and EDI-1 form the string to match against the pattern.

The primary purpose of a pattern matching function is to return "success" or "failure" depending upon whether the pattern matches the characters in the string (or however else you define "success" versus "failure"). In addition to returning success or failure, pattern matching functions must also return certain values in some of the registers. In particular, the function must preserve the value in EDI (that is, it must still point at the first byte beyond the end of the string to match). If the function succeeds, it must return EBX pointing at the start of the sequence it matched (i.e., EBX must contain the original value in ESI) and ESI must point at the first character beyond the string matched by the function (so the string matched is between addresses EBX and ESI-1). If the function fails, it must return the original values of ESI and EDI in these two registers. EBX's value is irrelevant if the function fails. Except for EBP, the routine need not preserve any other register values (and, in fact, a pattern matching function can use the other registers to return attribute values to the calling code)8.

Pattern matching routines that do not support backtracking are the easiest to create and understand. Therefore, it makes sense to begin with a discussion of those types of pattern matching routines.

A pattern matching routine that does not support backtracking succeeds by simply returning to its caller (with the registers containing the appropriate values noted above). If the function fails to match the characters between ESI and EDI-1, it must call the " pat._fail_ " function passing the " pat.FailTo " object as its parameter, e.g.,

pat._fail_( pat.FailTo );

 

As a concrete example, consider the following implementation of the pat.matchStr function:

 

unit patterns;

#include( "pat.hhf" );

 

procedure pat.matchStr( s:string ); @nodisplay; @noframe;

begin matchStr;

 

push( ebp ); // must do this ourselves since noframe

mov( esp, ebp ); // is specified as an option.

cld();

// Move a copy of ESI into EBX since we need to return

// the starting position in EBX if we succeed.

 

mov( esi, ebx );

 

// Compute the length of the remaining

// characters in the sequence we are attempting

// to match (i.e., EDI-ESI) and compare this against

// the length of the string passed as a parameter.

// If the parameter string is longer than the number

// of characters left to match, then we can immediately

// fail since there is no way the string is going to

// to match the string parameter.

 

mov( s, edx );

mov( (type str.strRec [edx]).length, ecx );

mov( edi, eax );

sub( esi, eax );

if( ecx > eax ) then

 

// At this point, there aren't enough characters left

// in the sequence to match s, so fail.

pat._fail_( pat.FailTo );

 

endif;

 

// Okay, compare the two strings up to the length of s

// to see if they match.

 

push( edi );

mov( edx, edi );

repe.cmpsb();

pop( edi );

if( @ne ) then

 

// At this point, the strings are unequal, so fail.

// Note that this code must restore ESI to its

// original value if it returns failure.

mov( ebx, esi );

pat._fail_( pat.FailTo );

 

endif;

// Since this routine doesn't have to handle backtracking,

// a simple return indicates success.

 

pop( ebp );

ret();

end matchStr;

end patterns;

Pat.matchstr Source Code

If your function needs to support back tracking, the code will be a little more complex. First of all, your function cannot return to its caller by using the RET instruction. To support backtracking, the function must leave its activation record on the stack when it returns. This is necessary so that when backtracking occurs, the function can pick up where it left off. It is up to the pat.match macro to clean up the stack after a sequence of pattern matching functions successfully match a string.

If a pattern matching function supports backtracking, it must preserve the values of ESP, ESI, and EDI upon initial entry into the code. It will also need to maintain the currrent cursor position during backtracking and it will need to reserve storage for a special " pat.FailRec " data structure. Therefore, almost every pattern matching routine you'll write that supports backtracking will have the following VAR objects:

 

var

cursor: misc.pChar; // Save last matched posn here.

startPosn: misc.pChar; // Save start of str here.

endStr: misc.pChar; // End of string goes here.

espSave: dword; // To clean stk after back trk.

FailToSave:pat.FailRec;// Save global FailTo value here.

 

Warning: you must declare these variables in the VAR section; they must not be static objects.

Upon reentry from backtracking, the ESP register will not contain an appropriate value. It is your code's responsibility to clean up the stack when backtracking occurs. The easiest way to do this is to save a copy of ESP upon initial entry into your function (in the espSave variable above) and restore ESP from this value whenever backtracking returns control to your function (you'll see how this happens in a moment). Likewise, upon reentry into your function via backtracking, the registers are effectively scrambled. Therefore, you will need to save ESI's value into the startPosn variable and EDI's value into the endStr variable upon initial entry into the function. The startPosn variable contains the value that EBX must have whenever your function returns success. The cursor variable contains ESI's value after you've successfully matched some number of characters. This is the value you reload into ESI whenever backtracking occurs. The FailToSave data structure holds important pattern matching information. The pattern matching library automatically fills in this structure when you signal success; you are only responsible for supplying this storage, you do not have to initialize it.

You signal failure in a function that supports backtracking the same way you signaled failure in a routine that does not support backtracking: by invoking "pat._fail_( pat.FailTo );" Since your code is failing, the caller will clean up the stack (including removing the local variables you've just allocated and initialized). If the pattern matching system calls your pattern matching function after backtracking occurs, it will reenter your function at its standard entry point where you will, once again, allocate storage for the local variables above and initialize them as appropriate.

If your function succeeds, it usually signals success by invoking the pat._success_ macro. This macro invocation takes the following form:

 

pat._success_( FailToSave, FailToHere );

 

The first parameter is the pat.FailRec object you declared as a local variable in your function. The pat._success_ macro stores away important information into this object before returning control to the caller. The FailToHere symbol is a statement label in your function. If backtracking occurs, control transfers to this label in your function (i.e., this is the backtracking reentry point). The code at the FailToHere label must immediately reload ESP from espSave , EDI from endStr , EBX from startPosn , and ESI from cursor . Then it does whatever is necessary for the backtrack operation and attempts to succeed or fail again.

The pat._success_ macro (currently) takes the following form9:

 

// The following macro is a utility for

// the pattern matching procedures.

// It saves the current global "FailTo"

// value in the "FailRec" variable specified

// as the first parameter and sets up

// FailTo to properly return control into

// the current procedure at the "FailTarget"

// address. Then it jumps indirectly through

// the procedure's return address to transfer

// control to the next (code sequential)

// pattern matching routine.

 

#macro _success_( _s_FTSave_, _s_FailTarget_ );

// Preserve the old FailTo object in the local

// FailTo variable.

 

mov( pat.FailTo.ebpSave, _s_FTSave_.ebpSave );

mov( pat.FailTo.jmpAdrs, _s_FTSave_.jmpAdrs );

// Save current EBP and failto target address

// in the global FailTo variable so backtracking

// will return the the current routine.

 

mov( ebp, pat.FailTo.ebpSave );

mov( &_s_FailTarget_, pat.FailTo.jmpAdrs );

 

// Push the return address onto the stack (so we

// can return to the caller) and restore

// EBP to the caller's value. Then jump

// back to the caller without cleaning up

// the current routine's stack.

 

push( [ebp+4] );

mov( [ebp], ebp );

ret();

 

#endmacro

Pat._success Macro Source Code

As you can see, this code copies the global pat.FailTo object into the FailToSave data structure you've created. The FailTo structure contains the EBP value and the reentry address of the most recent function that supports backtracking. You code must save these values in the event your code (ultimately) fails and needs to backtrack to some previous pattern matching function.

After preserving the old value of the global pat.FailTo variable, the code above copies EBP and the address of the FailToHere label you've specified into the global pat.FailTo object.

Finally, the code above returns to the user, without cleaning up the stack, by pushing the return address (so it's on the top of the stack) and restoring the caller's EBP value. The RET() instruction above returns control to the function's caller (note that the original return address is still on the stack, the pattern matching routines will never use it).

Should backtracking occur and the program reenters your pattern matching function, it will reenter at the address specified by the second parameter of the pat._success_ macro (as noted above). You should restore the appropriate register (as noted above) and use the value in the cursor variable to determine how to proceed with the backtracking operation. When doing eager evaluation, you will generally need to decrement the value obtained from cursor to back off on the length of the string your program has matched (failing if you decrement back to the value in startPosn ). When doing lazy evaluation, you generally need to increment the value obtained from the cursor variable in order to match a longer string (failing if you increment cursor to the point it becomes equal to endStr ).

When executing code in the reentry section of your procedure, the failure and success operations are a little different. Prior to failing, you must manually restore the value in pat.FailTo that pat._success_ saved into the FailToSave local variable. You must also restore ESI with the original starting position of the string. The following instruction sequence will accomplish this:

 

// Need to restore FailTo address because it

// currently points at us. We want to jump

// to the correct location.

 

mov( startPosn, esi );

mov( FailToSave.ebpSave, pat.FailTo.ebpSave );

mov( FailToSave.jmpAdrs, pat.FailTo.jmpAdrs );

pat._fail_( pat.FailTo );

 

Likewise, succeeding in the backtrack reentry section of your program is a little different. You do not want to invoke the pat._success_ macro because it will overwrite the FailToSave value with the global pat.FailTo . The global value, however, points at your routine; were you to overwrite this value you'd never be able to fail back to previous matching functions in the current pattern match. Therefore, you should always execute code like the following when succeeding in the reentry section of your code:

 

mov( esi, cursor ); //Save current cursor value.

push( [ebp+4] ); //Make a copy of the rtn adrs.

mov( [ebp], ebp ); //Restore caller's EBP value.

ret(); //Return to caller.

 

The following is the code for the pat.oneOrMoreCset routine (that does an eager evaluation) that demonstrates pattern matching with backtracking.

 

unit patterns;

#include( "pat.hhf" );

 

/************************************************************/

/* */

/* OneOrMoreCset- */

/* */

/* Matches one or more characters in a string from */

/* the specified character set. */

/* */

/* Disposition: Eager */

/* BackTrackable: Yes */

/* */

/* Entry Parameters: */

/* */

/* ESI: Pointer to sequence of characters to match. */

/* EDI: Pointer to byte beyond last char to match. */

/* cst: Character set to match with. */

/* */

/* Exit Parameters (if success): */

/* */

/* EBX: Points at the start of matched sequence. */

/* ESI: Points at first character not in cst. */

/* EDI: Unchanged from entry value. */

/* */

/* Exit Parameters (if failure): */

/* */

/* EDI: Unchanged from entry value. */

/* */

/* Unless noted, assume all other registers can be modified */

/* by this code. */

/* */

/************************************************************/

 

procedure pat.oneOrMoreCset( cst:cset ); @nodisplay;

var

cursor: misc.pChar; // Save last matched posn here.

startPosn: misc.pChar; // Save start of str here.

endStr: misc.pChar; // End of string goes here.

espSave: dword; // To clean stk after back trk.

FailToSave: pat.FailRec; // Save global FailTo value here.

begin oneOrMoreCset;

 

// If some routine after this one fails and transfers

// control via backtracking to this code, the stack

// will be a mess. So save esp so we can clean up

// the stack if backtracking is necessary.

mov( esp, espSave );

// Save the pointer to the start of the string

// to match. This is used as a "fence" value

// to prevent backtracking past the start of

// the string if things go really wrong.

mov( esi, startPosn );

mov( esi, ebx );

 

// Save pointer to end of string to match.

// This is needed to restore this value when

// backtracking occurs.

 

mov( edi, endStr );

// Okay, eagerly match as many characters in

// the character set as possible.

xor( eax, eax );

dec( esi );

repeat

inc( esi ); // Move to next char in string.

breakif( esi >= edi ); // Stop at end of string.

mov( [esi], al ); // Get the char to test.

bt( eax, (type dword cst)); // See if in cst.

until( @nc ); // Carry is set if al in cst.

// So we can easily back track, save a pointer

// to the first non-matching character.

mov( esi, cursor );

// If we matched at least one character, then

// succeed by jumping to the return address, without

// cleaning up the stack (we need to leave our

// activation record laying around in the event

// backtracking is necessary).

 

if( esi > ebx ) then

pat._success_( FailToSave, FailToHere );

endif;

// If we get down here, we didn't match at

// least one character. So transfer control

// to the previous routine that supported

// backtracking.

mov( startPosn, esi );

pat._fail_( pat.FailTo );

 

 

 

 

// If someone after us fails and invokes

// backtracking, control is transfered to

// this point. First, we need to restore

// ESP to clean up the junk on the stack.

// Then we back up one character, failing

// if we move beyond the beginning of the

// string. If we don't fail, we jump to

// the code following the call to this

// routine (having backtracked one character).

FailToHere:

 

mov( espSave, esp ); // Clean up stack.

mov( cursor, esi ); // Get last posn we matched.

dec( esi ); // Back up to prev matched char.

mov( endStr, edi );

mov( startPosn, ebx );

if( esi <= ebx ) then

// We've backed up to the beginning of

// the string. So we won't be able to

// match at least one character.

 

mov( ebx, esi );

mov( FailToSave.ebpSave, pat.FailTo.ebpSave );

mov( FailToSave.jmpAdrs, pat.FailTo.jmpAdrs );

pat._fail_( pat.FailTo );

endif;

// If we drop down here, there is at least one

// character left in the string that we've

// matched, so call the next matching routine

// (by jumping to the return address) to continue

// the pattern match.

mov( esi, cursor );

mov( [ebp+4], eax );

mov( [ebp], ebp );

jmp( eax );

 

end oneOrMoreCset;

 

 

end patterns;

Backtracking Demonstration (pat.oneOrMoreCset Source Code)

The following example code demonstrates the pat.l_OneOrMoreCset routine. This is the same routine as the code above except this code supports lazy/deferred evaluation rather than eager evaluation.

 

unit patterns;

#include( "pat.hhf" );

 

/************************************************************/

/* */

/* l_OneOrMoreCset- */

/* */

/* Matches one or more characters in a string from */

/* the specified character set. Matches the shortest */

/* possible string that yields (overall) success. */

/* */

/* Disposition: Deferred */

/* BackTrackable: Yes */

/* */

/* Entry Parameters: */

/* */

/* ESI: Pointer to sequence of characters to match. */

/* EDI: Pointer to byte beyond last char to match. */

/* cst: Character set to match with. */

/* */

/* Exit Parameters (if success): */

/* */

/* ESI: Points at first character not in cst. */

/* EDI: Unchanged from entry value. */

/* */

/* Exit Parameters (if failure): */

/* */

/* EDI: Unchanged from entry value. */

/* ESI: Unchanged from entry value. */

/* */

/* Unless noted, assume all other registers can be modified */

/* by this code. */

/* */

/************************************************************/

 

procedure pat.l_OneOrMoreCset( cst:cset ); @nodisplay;

var

cursor: misc.pChar; // Save last matched posn here.

startPosn: misc.pChar; // Save start of str here.

endStr: misc.pChar; // End of string goes here.

espSave: dword; // To clean stk after back trk.

FailToSave: pat.FailRec; // Save global FailTo value here.

begin l_OneOrMoreCset;

 

// If some routine after this one fails and transfers

// control via backtracking to this code, the stack

// will be a mess. So save esp so we can clean up

// the stack if backtracking is necessary.

mov( esp, espSave );

// Save the pointer to the start of the string

// to match. This is used as a "fence" value

// to prevent backtracking past the start of

// the string if things go really wrong.

mov( esi, startPosn );

mov( esi, ebx );

 

// Save pointer to end of string to match.

// This is needed to restore this value when

// backtracking occurs. If we're already

// beyond the end of the chars to test, then

// fail right away.

 

mov( edi, endStr );

if( esi >= edi ) then

 

pat._fail_( pat.FailTo );

 

endif;

// Okay, this is a deferred version. So match as

// few characters as possible. For this routine,

// that means match exactly one character.

xor( eax, eax );

mov( [esi], al ); // Get the char to test.

bt( eax, (type dword cst)); // See if in cst.

if( @nc ) then

 

pat._fail_( pat.FailTo );

 

endif;

 

// So we can easily back track, save a pointer

// to the next character.

inc( esi );

mov( esi, cursor );

// Save existing FailTo address and

// point FailTo at our back tracking code,

// then transfer control to the success

// address (jump to our return address).

 

pat._success_( FailToSave, FailToHere );

 

 

 

 

// If someone after us fails and invokes

// backtracking, control is transfered to

// this point. First, we need to restore

// ESP to clean up the junk on the stack.

// Then we need to advance one character

// and see if the next char would match.

FailToHere:

 

mov( espSave, esp ); // Clean up stack.

 

mov( cursor, esi ); // Get last posn we matched.

mov( endStr, edi ); // Restore to original value.

 

// If we've exceeded the maximum limit on the string,

// or the character is not in cst, then fail.

 

xor( eax, eax );

if

{

cmp( esi, edi );

jae true;

mov( [esi], al );

bt( eax, (type dword cst ));

jc false;

}

 

// Need to restore FailTo address because it

// currently points at us. We want to jump

// to the correct location.

 

mov( startPosn, esi );

mov( FailToSave.ebpSave, pat.FailTo.ebpSave );

mov( FailToSave.jmpAdrs, pat.FailTo.jmpAdrs );

pat._fail_( pat.FailTo );

 

endif;

 

// If we drop down here, there is at least one

// character left in the string that we've

// matched, so call the next matching routine

// (by jumping to the return address) to continue

// the pattern match.

mov( startPosn, ebx );

inc( esi ); // Advanced to next posn

mov( esi, cursor ); // save for backtracking,

mov( [ebp+4], eax ); // and call next routine.

mov( [ebp], ebp );

jmp( eax );

end l_OneOrMoreCset;

 

end patterns;

Backtracking Demonstration #2
Random Number Generators (rand.hhf)

The rand.hhf header file contains definitions for HLA's random number generators. These functions provide a variety of pseudo-random number generators and support routines. The supplied routines include the following:

rand.randomize;

This function "randomizes" the seed used by the random number generators. If you call rand.randomize , the random number generators should begin generating a sequence starting at a random point in the normal sequence put out by the random number generator.

The randomization function is based on the number of CPU clock cycles that have occurred since the CPU was last powered up. This function uses the Pentium's RDTSC instruction, hence you should only call this function on machines that have this instruction available (Intel Pentium and later as well as other manufacturer's CPUs that have this instruction).

Because of the nature of the RDTSC instruction, you should not call rand.randomize frequently or you will compromise the quality of the random numbers. Similarly, you should avoid calling this function from a fixed script after power-on since that may also degrade the quality of the randomization. (These two suggestions are only important to those who are extremely concerned about the quality of the randomness of the generated numbers).

rand.uniform; returns( "eax" );

This function generates a new random number on each call. This function returns a new 32-bit value in the EAX register on each call (bit 31 is randomly set, you may choose to interpret this value as a signed or unsigned integer). This function generates uniformly-distributed random numbers.

rand.urange( startRange:int32; endRange:int32 ); returns( "eax" );

This function generates a uniformly distributed random number in the range " startRange..endRange " (inclusive). This function generates its random numbers using the rand.uniform function. This function returns the value in the EAX register.

rand.random; returns( "eax" );

This function generates a uniformly distributed random number using a linear congruential random number generator. It returns its result in the EAX register.

rand.range( startRange:int32; endRange:int32 ); returns( "eax" );

This function generates a uniformly distributed random number in the range " startRange..endRange " (inclusive). This function generates its random numbers using the rand.random function. This function returns the value in the EAX register.

rand.deal( count:uns32 ); // Iterator returns value in EAX.

The rand.deal iterator returns a sequence of count unique randomly arranged values in the range 0.. count -1. Therefore, it returns all values in the range 0..count-1 , but in a random order.

 

Since rand.deal is an iterator, you must only use it within a FOREACH loop, e.g.,

 

foreach deal( 52 ) do

 

<< EAX contains a value in the range 1..51 here>>

 

endfor;

 

This function uses the rand.uniform function to randomly arrange the values.

Standard Input Routines (stdin.hhf)

The HLA Standard I/O unit provides a set of buffer input routines that read data from the Windows/Linux Standard Input Device.

Whenever you request input, by calling one of the following input routines, the Standard Library routines first check to see if there is any data available in an internal buffer. If so, the routines read the data from the buffer; if not, the routines fill the buffer by reading a line of text from the Windows/Linux Standard Input Device. Once a line is read, the routine will read its data from the newly acquired buffer. Additional calls to the standard input routines continue to read their data from this same buffer until the input line is exhausted, at which point the library routines will read more data from the Windows/Linux Standard Input Device.

The internal data buffer has sufficient storage for 1,024 characters10. This is generally sufficient for interactive input. If you feel you need more, feel free to go in an modify the source code to the library.

General Input Functions
stdin.handle; returns( "eax" );

This routine returns the handle of the Linux/Windows Standard Input Device in the EAX register.

stdin.flushInput;

This routine flushes the internal buffer. The next call to a Standard Library input routine will force the system to read a new line of text from the user. All current data in the internal input buffer is lost.

Please note that this routine does not immediately force the input of a new line of text from the user unless the internal buffer is already empty. If the internal buffer is empty and you call this routine, it will read a new line of text from the user and then flush this text from the internal buffer.

stdin.readln;

This routine flushes the current input buffer and immediately read a new line of text from the user.

stdin.eoln; returns( "al" );

stdin.eoln2; returns( "al" );

These functions return true if the input buffer is at the end of the current line. The stdin.eoln2 function will first remove any delimiter characters from the input buffer before testing for the end of the current line. These functions return true (1) or false (0) in the AL/EAX register.

These functions do not force a new line of input on the next stdin. getXX operation. I.e., if you read a string after stdin.eoln returns true, you will get the empty string as the result. Call stdin.readln to force the input of a new line.

Character and String Input Functions
stdin.peekc; returns( "al" );

This routine returns the character character from the standard input device without actually "reading" that character. That is, after a call to stdin.peekc , the next call to stdin.getc will return the same character as the one stdin.peekc returns. A call to stdin.peekc does not force the input of a new line of text. If the current input buffer is empty, calls to stdin.peekc return zero in the AL register. This routine returns the character in the AL register and it returns zeros in the upper three bytes of EAX.

stdin.getc; returns( "al" );

This routine reads a single character from the standard input and returns this character in the AL register. (Note that this routine also zeros out the H.O. three bytes of EAX.)

stdin.gets( s:string );

This routine reads a string from the standard input device and store the string into the string variable passed as the parameter. The string read consists of all characters remaining in the buffer to the end of the line, but the string will not contain the end of line sequence. If all characters have been read from the buffer then this call returns the empty string. If some routine removes the new line sequence from the end of the buffer, then calling this routine will force the input of a new line of text.

If the string is not large enough to hold the characters read from the input buffer, these routines will raise an exception.

When the standard input device is the Windows console device, the stdin.gets function allows you to use the standard Windows console editing functions. For example, pressing the ESC key erasing the current input line. You can use the arrow keys and the insert/delete keys to perform intraline editing on the input line. Ditto for Linux, though the console editing keys are more limited.

stdin.a_gets; returns( "eax" );

This routine is very similar to stdin.gets . However, it allocates storage for its input string rather than store the string into some specified location. This routine returns a pointer to the newly allocated string in the EAX register. You should call strfree to free the storage associated with this string when you are done with it. Internally, HLA limits the input line to 1,024 characters. Note, however, that the Windows console device generallly limits input to fewer than this number of characters (typically 128) so you may not be able to read a full 1,024 characters into the string. Note, however, that this Windows Console input buffer limitation does not apply if you're redirected the input from a file to the standard input device.

Hexadecimal Input Functions
stdin.getb; returns( "al" );

This function reads an eight-bit hexadecimal integer in the range 0..$FF from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getb function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FF. This function returns the binary form of the value in the AL register.

stdin.getw; returns( "ax" );

This function reads a 16-bit hexadecimal integer in the range 0..$FFFF from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getw function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF. This function returns the binary form of the value in the AX register.

stdin.getd ; returns( "eax" );

This function reads a 32-bit hexadecimal integer in the range 0..$FFFF_FFFF from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getd function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF. This function returns the binary form of the value in the EAX register.

stdin.getq; returns( "edx:eax" );
This function reads a 64-bit hexadecimal integer in the range 0..$FFFF_FFFF_FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getq function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF_FFFF_FFFF. This function returns the 64-bit result in EDX:EAX.
stdin.getl( l:lword ) ;

This function reads a 128-bit hexadecimal integer in the range zero through $FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF from the file. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more hexadecimal digits. Note that the value may not have a leading "$" unless you add this character to the delimiter character set. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getl function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..$FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF. This function stores the 128-bit result into the variable you pass as a reference parameter.

Signed Integer Input Functions
stdin.geti8; returns( "al" );

This function reads a signed eight-bit decimal integer in the range -128..+127 from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.geti8 function raises an appropriate exception if the input violates any of these rules or the value is outside the range -128..+127. This function returns the binary form of the integer in the AL register.

stdin.geti16; returns( "ax" );

This function reads a signed 16-bit decimal integer in the range -32768..+32767 from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.geti16 function raises an appropriate exception if the input violates any of these rules or the value is outside the range -32768..+32767. This function returns the binary form of the integer in the AX register.

stdin.geti32; returns( "eax" );

This function reads a signed 32-bit decimal integer in the (approximate) range ±2 Billion from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.geti32 function raises an appropriate exception if the input violates any of these rules or the value is outside the range plus or minus two billion. This function returns the binary form of the integer in the EAX register.

stdin.geti64; returns( "edx:eax" );

This function reads a signed 64-bit decimal integer from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.geti64 function raises an appropriate exception if the input violates any of these rules or the value is outside the range of a 64-bit signed integer. This function returns the 64-bit result in EDX:EAX.

stdin.geti128( var l: lword );

This function reads a signed 128-bit decimal integer from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.geti128 function raises an appropriate exception if the input violates any of these rules or the value is outside the range of a 128-bit signed integer. This function stores the 128-bit result in the lword you pass as a reference parameter.

Unsigned Integer Input Routines
stdin.getu8; returns( "al" );

This function reads an unsigned eight-bit decimal integer in the range 0..+255 from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getu8 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..255. This function returns the binary form of the integer in the AL register.

stdin.getu16; returns( "ax" );

This function reads an unsigned 16-bit decimal integer in the range 0..+65535 from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getu16 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..65535. This function returns the binary form of the integer in the AX register.

stdin.getu32; returns( "eax" );

This function reads an unsigned 32-bit decimal integer in the range 0..+4,294,967,295 from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getu32 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..4,294,967,295. This function returns the binary form of the integer in the EAX register.

stdin.getu64; returns( "edx:eax" );

This function reads an unsigned 64-bit decimal integer from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getu64 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..264-1. This function returns the binary form of the integer in the qword parameter you pass by reference.

stdin.getu128( var q:qword );

This function reads an unsigned 128-bit decimal integer from the standard input device. The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by a string of one or more decimal digits. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. The stdin.getu128 function raises an appropriate exception if the input violates any of these rules or the value is outside the range 0..2128-1. This function returns the binary form of the integer in the lword parameter you pass by reference.

Floating Point Input
stdin.getf; returns( "st0" );

This function reads an 80-bit floating point value in either decimal or scientific from from the standard input device and leaves the result sitting on the FPU stack (i.e., in ST0). The number may begin with any number of delimiter characters (see the conv.setDelimiter and conv.getDelimiter functions for details on the delimiter characters) followed by an optional minus sign and a sequence of characters that represent a floating point value. The number must end with a valid delimiter character or the end of the file. This function allows underscores in the interior of the number. This function raises an appropriate exception if an error occurs.

Standard Output Routines (stdout.hhf)

The Standard output unit (see stdio.hhf, stdin.hhf, and stdout.hhf) is probably the largest and most sophisticated module in the HLA Standard Library. It currently contains several defined constants (see the stdio.hhf header file for details), routines for sending data to the standard output, routines for send data to a file, and routines for reading data from the standard input (reading from files is coming, it's just not written yet).

Many routines have a "Size" suffix. For example, consider the stdout.putcsize routine:

stdout.putcSize ( c:char; width:int32; fill:char )

Routines with the Size suffix output their value (typically the first parameter) in a minimum field width specified by the width parameter. If the number of screen print positions needed to print the value is less than the value of this width parameter, the routine will pad the output with extra characters so that the output is at least width characters long. These routines use the fill character as the padding character. If the width value is positive and greater than the number of print positions required to print the value, then the value is output right-justified in the print field (that is, the routine prints the padding characters first). If the width value is negative, then the routine outputs its value left justified, printing the padding characters after the value.

The Standard I/O unit provides the following routines.

General Output Functions
stdout.handle; returns( "eax" );

This routine returns the LInux/Windows handle for the Standard Output Device in the EAX register. You may use this handle with the file I/O routines to write data to the standard output device.

stdout.newln;

This routine writes a newline sequence to the standard output device. Under Windows, the newline sequence is a carriage return followed by a line feed. For Linux, this is just a line feed.

Note: the stdio.hhf header file also defines the string constant nl that you can print to print a newline sequence.

Boolean Output
stdout.putbool( b:boolean );

This routine outputs the string "true" or "false" to the standard output device.

Character, String, and Cset Output
stdout.putcset( cs:cset );

This routine outputs its character set parameter to the standard output device. In theory, the routine may output its data in any order, in practice, it outputs the members of its sets in lexicographical order.

stdout.putc( c:char );

This routine outputs a single character to the standard output device.

stdout.putcSize( c:char; width:int32; fill:char );

This routine outputs a character to the standard output device that is justified within the specified field width. This routine prints a minimum of width characters. If the width parameter is positive, the character is printed right justified in the print field. If the width value is negative, the character is printed left justified in the print field. If width is greater than one, then the fill character is printed in the other print positions.

stdout.puts( s:string );

This routine prints the specified string to the standard output device. Keep in mind that HLA strings are pointer objects. Therefore the parameter is a pointer to an HLA string. See the section on the HLA string library for more details.

stdout.putsSize( s:string; width:int32; fill:char );

This routine outputs a string to the standard output device using the specified minimum field width and fill character.

Hexadecimal Output
stdout.putb( b:byte );
stdout.putbSize( b:byte; size:dword; fill:char)

This procedure writes the value of b to the standard output device using exactly two hexadecimal digits (including a leading zero if necessary). The stdout.putbSize function lets you specify a minimum field width and a fill character. The stdout.putb routine uses a minimum size of two and a fill character of '0'.

stdout.putw( w:word );
stdout.putwSize( w:word; size:dword; fill:char)

This procedure writes the value of w to the standard output device using exactly four hexadecimal digits (including leading zeros if necessary). The stdout.putwSize function lets you specify a minimum field width and a fill character. The stdout.putw routine uses a minimum size of two and a fill character of '0'.

stdout.putd( dw:dword );
stdout.putdSize( d:dword; size:dword; fill:char)

This procedure writes the value of d to the standard output device using exactly eight hexadecimal digits (including leading zeros if necessary and an intervening underscore if underscore output is enabled). The stdout.putdSize function lets you specify a minimum field width and a fill character. The stdout.putd routine uses a minimum size of two and a fill character of '0'.

stdout.putq( qw:qword );
stdout.putqSize( q:qword; size:dword; fill:char)

This procedure writes the value of q to the standard output device using exactly sixteen hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled). The stdout.putqSize function lets you specify a minimum field width and a fill character. The stdout.putq routine uses a minimum size of two and a fill character of '0'.

stdout.puttb( tb:tbyte );

This procedure writes the value of tb to the standard output device using exactly 20 hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled).

stdout.putl( l:lword );
stdout.putlSize( l:lword; size:dword; fill:char)

This procedure writes the value of l to the standard output device using exactly 32 hexadecimal digits (including leading zeros if necessary and an intervening underscores if underscore output is enabled, in which case there will be 39 output characters). The stdout.putlSize function lets you specify a minimum field width and a fill character. The stdout.putl routine uses a minimum size of two and a fill character of '0'.

 

Signed Integer Output

These routines convert signed integer values to string format and write that string to the standard output device. The stdout.putxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

Note that unlike floating point values, these functions do not print a space in front of the value if it is non-negative.

 
stdout.ixxSize Output Format
stdout.puti8size ( b:byte; width:int32; fill:char );

This function writes the eight-bit signed integer value you pass to the standard output device using the width and fill values as specified above.

stdout.puti16Size( w:word; width:int32; fill:char );

This function writes the 16-bit signed integer value you pass to the standard output device using the width and fill values as specified above.

stdout.puti32Size( d:dword; width:int32; fill:char );

This function writes the 32-bit value you pass as a signed integer to the standard output device using the width and fill values as specified above.

stdout.puti64Size( q:qword; width:int32; fill:char );

This function writes the 64-bit value you pass as a signed integer to the standard output device using the width and fill values as specified above.

stdout.puti128Size( l:lword; width:int32; fill:char );

This function writes the 128-bit value you pass as a signed integer to the standard output device using the width and fill values as specified above.

stdout.puti8 ( b:byte );

This function converts the eight-bit signed integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.puti16( w:word );

This function converts the 16-bit signed integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.puti32( d:dword );

This function converts the 32-bit signed integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.puti64( q:qword );

This function converts the 64-bit signed integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.puti128( l:lword );

This function converts the 128-bit signed integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

Unsigned Integer Output

These routines convert unsigned integer values to string format and write that string to the standard output device. The stdout.putuxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

 
stdout.uxxxSize Output Format
stdout.putu8Size ( b:byte; width:int32; fill:char );

This function writes the unsigned eight-bit value you pass to the standard output device using the width and fill values as specified above.

stdout.putu16Size( w:word; width:int32; fill:char );

This function writes the unsigned 16-bit value you pass to the standard output device using the width and fill values as specified above.

stdout.putu32Size( d:dword; width:int32; fill:char );

This function writes the unsigned 32-bit value you pass to the standard output device using the width and fill values as specified above.

stdout.putu64Size( q:qword; width:int32; fill:char );

This function writes the unsigned 64-bit value you pass to the standard output device using the width and fill values as specified above.

stdout.putu128Size( l:lword; width:int32; fill:char );

This function writes the unsigned 128-bit value you pass to the standard output device using the width and fill values as specified above.

stdout.putu8 ( b:byte );

This function converts theeight-bit unsigned integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.putu16( w:word );

This function converts the 16-bit unsigned integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.putu32( d:dword );

This function converts the 32-bit unsigned integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.putu64( d:dword );

This function converts the 64-bit unsigned integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

stdout.putu128( d:dword );

This function converts the 128-bit unsigned integer you pass as a parameter to a string and writes this string to the standard output device using the minimum number of print positions the number requires.

Floating Point Output

The HLA standard output module provides several procedures you can use to write floating point values to athe standard output. The following subsections describe these routines.

Real Output Using Scientific Notation

The floating point numeric output routines translate the three different binary floating point formats to their string representation and then write this string to the standard output device. There are two generic classes of these routines: those that convert their values to exponential/scientific notation and those that convert their string to a decimal form.

The stdout.pute80 , stdout.pute64 , and stdout.pute32 routines convert their values to a string using scientific notation. These three routines each have two parameters: the value to output and the field width of the result. These routines produce a string with the following format:

 
stdout.putexx Output Using Scientific Notation
stdout.pute80( r:real80; width:uns32 );

This function writes the 80-bit extended precision floating point value passed in r to the standard output device using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 80-bit extended precision floating point values support about 18 significant digits. So a width value that yeilds more than 18 mantissa digits will produce garbage output in the low order digits of the number.

stdout.pute64( r:real64; width:uns32 );

This function writes the 64-bit double precision floating point value passed in r to the standard output device using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 64-bit double precision floating point values support about 15 significant digits. So a width value that yeilds more than 15 mantissa digits will produce garbage output in the low order digits of the number.

stdout.pute32( r:real32; width:uns32 );

This function writes the 32-bit single precision floating point value passed in r to the standard output device using scientific/exponential notation. This procedure prints the value using width print positions in the file. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 32-bit extended precision floating point values support about 6-7 significant digits. So a width value that yeilds more than seven mantissa digits will produce garbage output in the low order digits of the number.

Note: all HLA floating point library routines assume the presence of an FPU. They do not save the state of the FPU.

Real Output Using Decimal Notation

Although scientific (exponential) notation is the most general display format for real numbers, real numbers you display in this format are very difficult to read. Therefore, the HLA standard output module also provides a set of functions that output real values using the decimal representation. Although you cannot (practically) use these decimal output routines for all real values, they are applicable to a wide variety of common numbers you will use in your programs.

These functions come in two varieties. The first variety requires four parameters: the real value to convert, the width of the converted value, the number of digit positions to the right of the decimal point, and a padding character. The second variety only requires the first three parameters and assumes the padding character is a space. These functions write their values using the following string format:

 
stdout.putrxx Conversion Format
stdout.putr80pad( r:real80; width:uns32; decpts:uns32; pad:char );

This procedure writes an 80-bit extended precision floating point value to the standard output device as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

stdout.putr64pad( r:real64; width:uns32; decpts:uns32; pad:char );

This procedure writes a 64-bit double precision floating point value to the standard output device as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

stdout.putr32pad( r:real32; width:uns32; decpts:uns32; pad:char );

This procedure writes a 32-bit single precision floating point value to the standard output device as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses the value of pad as the padding character to fill the output with width characters.

stdout.putr80( r:real80; width:uns32; decpts:uns32 );

This procedure writes an 80-bit extended precision floating point value to the standard output device as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

stdout.putr64( r:real64; width:uns32; decpts:uns32 );

This procedure writes a 64-bit double precision floating point value to the standard output device as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

stdout.putr32( r:real32; width:uns32; decpts:uns32 );

This procedure writes a 32-bit single precision floating point value to the standard output device as a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters.

Note: all HLA floating point library routines assume the presence of an FPU. They do not save the state of the FPU.

The stdout.put Macro
stdout.put( output_list );

stdout.put is a macro that provides a flexible syntax for outputting data to the standard output device. This macro allows a variable number of parameters. For each parameter present in the list, stdout.put will call the appropriate routine to emit that data, according to the type of the parameter. Parameters may be constants, registers, or memory locations.

Here is an example of a typical invocation of stdout.put:

 

stdout.put( "I=", i, " j=", j, nl );

 

The above is roughly equivalent to

 

puts( "I=" );

puti32( i );

puts( " j=" );

puti32( j );

newln();

 

This assumes, of course, that "i" and "j" are int32 variables.

The stdout.put macro also lets you specify the minimum field width for each parameter you specify. To print a value using a minimum field width, follow the object you wish to print with a colon and the value of the minimum field width. The previous example, using field widths, could look like the following:

 

stdout.put( "I=", i:2, " j=", j:5, nl );

 

Although this example used the literal decimal constants two and five for the field widths, keep in mind that register values and memory value (integers, anyway) are prefectly legal here.

For floating point numbers you wish to display in decimal form, you can specify both the minimum field width and the number of digits to print to the right of the decimal point by using the following syntax:

 

stdout.put( "Real value is ", f:10:3, nl );

 

The stdout.put macro can handle all the basic primitive types, including boolean, unsigned (8, 16, 32, 64, 128), signed (8, 16, 32, 64, 128), character, character set, real (32, 64, 80), string, and hexadecimal (byte, word, dword, qword, lword).

If you specify a class variable (object) and that class defines a " toString " method, the stdout.put macro will call the associated toString method and print that string. Note that the toString method must dynamically allocate storage for the string by calling stralloc . This is because stdout.put will call strfree on the string once it prints the string.

There is a known "design flaw" in the stdout.put macro. You cannot use it to print HLA intermediate variables (i.e., non-local VAR objects). The problem is that HLA's syntax for non-local accesses takes the form "reg32:varname" and stdout.put cannot determine if you want to print reg32 using varname print positions versus simply printing the non-local varname object. If you want to display non-local variables you must copy the non-local object into a register, a static variable, or a local variable prior to using stdout.put to print it. Of course, there is no problem using the other stdout.putXXXX functions to display non-local VAR objects, so you can use those as well.

Strings (strings.hhf)

HLA provides a sophisticated string handling package. The string data type has been carefully designed for high performance operations and there are lots of routines that perform almost every imaginable standard operation on the string data.

The first place to start is with the discussion of the string data type itself. A string variable is nothing more than a four-byte pointer that points at the actual string data. So anytime you pass a string by value to a procedure or method, you're actually passing a pointer value. Note that taking the address of a string variable (with the LEA instruction) takes the address of the pointer, not the address of the actual character data. Therefore, if you are calling a routine that expects the address of some character data in a register, you would normally move the contents of a string variable into that register, not load the address of that string variable. For example, the atoi routine (see the section on conversions, earlier) expects a pointer to a string variable in the ESI register. If you wish to pass the address of the first character of a string in ESI, you would use the "mov( s, esi);" instruction, not "lea( esi, s );".

The HLA Standard Library makes a couple of important assumptions about where string variables are pointing. First, and most important, string variables must always point at a buffer that is an even multiple of four bytes long. Many string operations move double words, rather than bytes, around to improve performance. If the buffer is not an even multiple of four bytes long, some data transfers may inadvertently wipe out data adjacent to the string buffer or, worse still, cause a general protection fault.

The second assumption the HLA Standard Library makes is that the string data is prefaced by two dword objects. The first (at offset -8 from the beginning of the character data) contains the maximum number of characters that can be stored into this string (not counting a zero terminating byte). This value is fixed when storage is allocated for the string. The HLA string routines use this value to detect a string overflow condition.

The second dword object before the character data (at offset -4) is the current dynamic length of the string (that is, the actual number of characters currently in the string). Since the maximum and dynamic length fields are four bytes long, HLA supports (in theory) strings whose lengths are up to four gigabytes in length. In practice, of course, strings generally don't grow very large.

HLA strings always contain a zero terminating byte. Strictly speaking, this zero terminating byte is not absolutely necessary because the HLA string type includes a dynamic length field. As such, the HLA Standard Library routines tend to ignore the zero terminating byte other than for use as a delimiter in the conversion routines. However, having this zero terminating byte allows you to pass HLA strings as parameters to Windows and Linux API functions and other functions that expect C/C++ style zero terminated strings.

Although not necessary for correct operation, HLA always aligns strings on a double word boundary. This allows certain string operations to run nearly twice as fast as they would if they were not aligned on a double word boundary.

To simplify access to the fields of a string, the string.hhf header file contain a record template you may use to access those fields in a structured fashion. This structure has the following definition:

 

type

strRec: record := -8;

 

MaxStrLen: int32;

length: int32;

strData: char[12];

 

endrecord;

 

(The index value after the char type is arbitrary.)

For example, suppose you have a string variable s and you wish to know the current length of this string. You could obtain the length as follows:

 

mov( s, esi );

mov( (type str.strRec [esi]).length, eax );

 

(Note that the str.strRec type definition appears within the str namespace, hence the " str ." prefix).

As a general rule, you should always use stralloc (or some routine that winds up calling stralloc ) to allocate storage for string variables. If you must allocate the storage yourself, be sure the storage allocation follows all the rules specified earlier.

Consider what HLA does when you declare an initialized string object as follows:

static

s :string := "SomeString";

 

One might be tempted to think that HLA allocates the string data as part of the " s " variable. In fact, this is not the case. HLA places the actual string data (including the length values, terminating byte, and any necessary padding bytes) somewhere else and then initializes the s object with the address of data data (appearing elsewhere). There is no direct way by only referencing s at compile-time, to treat the address of this string object as a constant. This feature would be useful, for example, for initializing string fields of a record constant with the address of the actual string data.

The HLA Standard Library strings.hhf header file provides a macro that lets you declare string constants and attach a label to the first character of that string (which is the address you generally want to assign to a string variable or field). You use this macro almost like the string data type, except you also supply a literal string constant argument, e.g.,

static

s :str.constant( "SomeString" );

 

This creates a string object in memory with s 's address corresponding to the first character of the string object. Note that s is not an HLA string; remember, an HLA string is a pointer to a string object. The address of s is what would normally appear in a string variable. Now consider the following code:

type

r:record

s:string;

b:byte;

endrecord;

 

static

somestr :str.constant( "SomeString" );

a :r := r:[ &somestr, 0 ];

 

This initializes the s field of a with a pointer to the string containing the characters "SomeString". This is the proper way to initialize a string field of a record. Note that HLA will accept the following without complaint, but it is not correct:

static

somestr :string := "SomeString";

a :r := r:[ &somestr, 0 ];

 

This example initializes the s field of a with the address of somestr . But this is not string data, rather, it's the address of some string data. Therefore, this code initializes field s with a pointer to the pointer of some character data, rather than the pointer to the character data (which is what you probably want).

Here's the implementation of the str.constant macro, just in case you're wondering how it works:

 

// str.constant( literal_constant )

//

// This macro creates a string constant object whose address

// you may assign to a string variable. This is useful, for

// example, when initializing fields of a record with string data.

#macro constant( __strconst ):__strname,__padding;

forward( __strname );

align(4);

dword @length( __strconst ), @length( __strconst );

__strname:char; @nostorage;

byte __strconst, 0;

?__padding := ((4 - ((@length( __strconst ) + 1) mod 4)) mod 4);

#while( __padding > 0 )

byte 0;

?__padding -= 1;

#endwhile

#endmacro;

 

String Allocation Macros and Functions
str.strvar( size )

str.strvar is a macro that will statically allocate storage for a string in the STATIC variable declaration section (you cannot use str.strvar in any of the other variable declaration sections, including the other static sections: READONLY, and STORAGE; you can only use it in the STATIC section). This macro emits the appropriate code to initialize a string pointer variable with the address of appropriate string storage that has sufficient room to hold a string of at least size characters (size is the parameter passed to this macro).

Example:

 

static

StaticString: str.strvar( 32 );

 

Since the storage is statically allocated for StaticString , there is no need to call stralloc or any other string/memory allocation procedure to allocate storage for this variable.

The following are brief descriptions of the string routines provided in the HLA strings library. Note that most routines raise an exception if an index error or string overflow occurs. See the source listings for more details.

str.init( var b:var; numBytes:dword ); returns( "eax" );

This function initializes a block of memory for use as a string object. It takes the address of the buffer variable b and aligns this address on a dword boundary. Then it initializes the MaxLength , length , and zero terminating byte fields at the resulting address. Finally, it returns a pointer to the newly created string object in EAX. The numBytes field specifies the size of the entire buffer area, not the desired maximum length of the string. The numBytes field must be 16 or greater, else this routine will raise an ex.ValueOutOfRange exception. Note that string initialization may consume as many as 15 bytes (up to three bytes to align the address on a dword boundary, four bytes for the maxlength field, four bytes for the length field, and the string data area must be a multiple of four bytes long (including the zero terminating byte). This is why the numBytes field must be 16 or greater. Note that this function initializes the resulting string to the empty string. The maxlength field will contain the maxium number of character that you can store into the resulting string after subtracting the zero terminating byte, the sizes of the length fields, and any alignment bytes that were necessary.

String Length Calculation
str.length( src ); returns( "eax" );

The src parameter can either be a string variable or a 32-bit register that points at valid string data. This macro returns the length field of the specified string in the EAX register.

str.mLength( src ); // returns the value in EAX

Macro implementation of the above procedure. Much faster, but you cannot use it buried inside another expression or instruction. (e.g., "mov( str.mLength(s), ecx);" is illegal).

String Assignment, Substring, and Concatenation Functions
str.cpy( src:string; dest:string );

Copies the characters and dynamic length from the source string to the destination string.

str.a_cpy( src:string ); returns( "eax" );

This routine allocates sufficient storage on the heap to make a copy of the source string. After copying the string, this routine returns a pointer to the new string in EAX. When you are done with this string's data, call strfree to deallocate the storage.

str.setstr( src:char; dest:string; cnt:uns32 );

str.a_setstr( src:char; count:uns32 ); returns( "eax" );

These routines construct a string by concatenating cnt copies of the src character together. The str.strset routine stores these characters into the destination string. The str.a_strset routine allocates storage for the string. Both routines return a pointer to the newly created string in EAX. When you are finished with the data created by str.a_strset , you should call strfree to release the storage associated with the string.

str.cat( src:string; dest:string )

str.a_cat( src1:string; src2:string ); returns( "eax" );

These routines concatenate two strings. The str.cat routine concatenates the source string to the end of the destination string (the destination string's MaxLength must be large enough to hold both strings). The str.a_cat routine allocates storage on the heap, copies the src2 string to the new storage, and then concatenates the src1 string to the end of the src2 string. Both routines return a pointer to the string in EAX. When you are done with the data created by str.a_cat , you should call strfree to release the storage.

String Comparison, Search, and Scanning Functions
str.eq( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX11 if the two strings are equal.

str.ne( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the two strings are not equal.

str.lt left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is less than the right string.

str.le( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is less than or equal to the right string.

str.gt( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is greater than the right string.

str.ge( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is greater than or equal to the right string.

str.ieq( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX12 if the two strings are equal using a case insenstive comparison.

str.ine( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the two strings are not equal using a case insenstive comparison.

str.ilt left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is less than the right string using a case insenstive comparison.

str.ile( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is less than or equal to the right string using a case insenstive comparison.

str.igt( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is greater than the right string using a case insenstive comparison.

str.ige( left:string; right:string ); returns( "al" );

Compares the left string to the right string and returns true in AL/EAX if the left string is greater than or equal to the right string using a case insenstive comparison.

str.index( src1:string; src2:string ); returns( "eax" );

This function returns the zero-based index of src2 within src1 (that is, it locates the first occurrence of the string src2 appearing inside src1 ). The index is returned in the EAX register. If src2 does not appear in src1 , then this function returns -1 in the EAX register.

str.index2( src1:string; offs:uns32; src2:string ); returns( "eax" );

This function returns the zero-based index of src2 within src1 starting at character position offs in src1 (that is, it locates the first occurrence of the string src2 appearing inside src1 at or after src1[offs] ). The index is returned in the EAX register. If src2 does not appear in src1 , then this function returns -1 in the EAX register.

str.rindex( src1:string; src2:string ); returns( "eax" );

This function returns the zero-based index of the last occurrence of src2 within src1 (that is, it searches backwards in src1 ). The index is returned in the EAX register. If src2 does not appear in src1 , then this function returns -1 in the EAX register.

str.rindex2( src1:string; offs:uns32; src2:string ); returns( "eax" );

This function returns the zero-based index of the last occurrence of src2 within src1 that occurs before character postion offs in the string (that is, it searches backwards in src1 starting at character position offs ). The index is returned in the EAX register. If src2 does not appear in src1 , then this function returns -1 in the EAX register.

str.prefix( src1:string; src2:string ); returns( "al" );

This function returns true if src2 is a "prefix" of src1 (if src1 begins with the string src2 ). This function returns false in EAX if src2 is not a prefix of src1 .

str.prefix2( src1:string; offs:uns32; src2:string ); returns( "al" );

This function returns true if src2 is a prefix of the substring of src1 starting at character position offs in src1 . This function returns false in EAX if src2 is not a prefix of src1 starting at character position offs within src1 .

str.chpos( src1:string; src2:char ); returns( "eax" );

This function returns the zero-based index of the first occurrence of the specified character ( src2 ) within src1 . This function returns -1 if src2 is not found within src1 .

str.chpos2( src1:string; offs:uns32; src2:char ); returns( "eax" );

This function returns the zero-based index of the first occurrence of the specified character ( src2 ) within src1, starting at character position offs within src1 . This function returns -1 if src2 is not found within src1 at or after position offs in the string.

str.rchpos( src1:string; src2:char ); returns( "eax" );

This function returns the zero-based index of the last occurrence of the specified character ( src2 ) within src1 . This function returns -1 if src2 is not found within src1 .

str.rchpos2( src1:string; offs:uns32; src2:char ); returns( "eax" );

This function returns the zero-based index of the last occurrence of the specified character ( src2 ) before (or at) position offs within the src1 . This function returns -1 if src2 is not found within src1 .

str.span( src1: string; src2:cset ); returns( "eax" );

This function skips over all characters in src1 that are elements of the character set src2 . It returns the index of the first character in src1 that is not a member of the src2 set. This function returns minus one if all characters in src1 are elements of src2 .

str.span2( src1: string; start:int32; src2:cset ); returns( "eax" );

Similar to str.span above, except this routine starts at character position start rather than at character position zero when searching through the string.

str.rspan( src1: string; src2:cset ); returns( "eax" );

str.rspan2( src1: string; start:int32; src2:cset ); returns( "eax" );

Similar to the previous two routines, except these search backwards through the string.

str.brk( src1: string; src2:cset ); returns( "eax" );

This function skips over all characters in src1 that are not elements of the character set src2 . It returns the index of the first character in src1 that is a member of the src2 set. This function returns minus one if all characters in src1 are not elements of src2 .

str.brk2( src1: string; start:int32; src2:cset ); returns( "eax" );

Similar to str.brk above, except this routine starts at character position start rather than at character position zero when searching through the string.

str.rbrk( src1: string; src2:cset ); returns( "eax" );

str.rbrk2( src1: string; start:int32; src2:cset ); returns( "eax" );

Similar to the previous two routines, except these search backwards through the string.

str.tokenize( src: string; var dest:dword ); returns( "eax" );

str.tokenize2( src: string; var dest:dword; delims:cset ); returns( "eax" );

These two routines lexically scan13 a string and break it up into "lexemes" (words), returning an array of pointers to each of the lexemes. The only difference between the two routines is that the tokenize routine uses the following default set of delimiter characters:

 

{' ', #9, ',', '<', '>', '|', '\', '/', '-'}

 

This character set roughly corresponds to the delimiters used by the Windows Command Window interpreter or typical Linux shells. If you do not wish to use this particular set of delimiter characters, you may call str.tokenize2 and specify the characters you're interested in.

The tokenize routines begin by skipping over all delimiter characters at the beginning of the string. Once they locate a non-delimiter character, they skip forward until they find the end of the string or the next delimiter character. Then they allocate storage for a new string on the heap and copy the delimited text to this new string. A pointer to the new string is stored into the dword array passed as the first parameter to tokenize(2) . This process is repeated for each lexeme found in the src string.

Warning: the dest parameter should be an array of strings. This array must be large enough to hold pointers to each lexeme found in the string. In theory, there could be as many as str.length(src)/2 lexemes in the source string.

On return from these functions, the EAX register will contain the number of lexemes found and processed in the src string (i.e., EAX will contain the number of valid elements in the dest array).

When you are done with the strings allocated on the heap, you should free them by calling strfree . Note that you need to call strfree for each active pointer stored in the dest array.

Here is an example of a call to the str.tokenize routine:

 

program tokenizeDemo;

#include( "stdio.hhf" );

#include( "string.hhf" );

#include( "memory.hhf" );

 

static

strings: string[16];

ParseMe: string := "This string contains five words";

begin tokenizeDemo;

 

str.tokenize( strings, ParseMe );

mov( 0, ebx );

while( ebx < eax ) do

stdout.put

(

"string[",

(type uns32 ebx),

"]=""",

strings[ebx*4],

"""",

nl

);

strfree( strings[ebx*4] );

inc( ebx );

endwhile;

end tokenizeDemo;

Demonstration of str.tokenize

This program produces the following output:

 

string[0]="This"

string[1]="string"

string[2]="contains"

string[3]="five"

string[4]="words"

 

String Insertion, Deletion and Extraction Functions
str.substr( src:string; dest:string; start:dword; len:dword )

This substring routine extracts characters src[start]..src[start+len-1] and copies them to the destination string.

str.a_substr( src:string; start:dword; len:dword ); returns( "eax" );

This routine is similar to the previous routine, except it allocates storage for the substring on the heap and returns a pointer to this storage in the EAX register. You should call strfree to free up the storage allocated by this routine when you are done with the substring.

str.insert( src:string; dest:string; start:dword )

This routine inserts the string src into the destination string dest immediately before the character at index start .

str.a_insert( src1:string; src2:string; start:dword ); returns( "eax" );

This routine creates a new string on the heap and copies src2 to the new storage. Then it inserts the string src1 into the new string immediately before the character at index start . It returns a pointer to this new string in the EAX register. You should call the strfree procedure to free up the storage allocated by this routine when you are done with the new string.

str.delete( dest:string; start:dword; length:dword )

This routine removes length characters from the string dest starting with the character at position start .

str.a_delete( src:string; start:dword; length:dword ); returns( "eax" );

This routine makes a copy of src on the heap and then deletes length characters from this new string beginning at index start . This routine returns a pointer to the newly allocated string in the EAX register. When you are done with this string data, you should call strfree to free up the storage.

str.replace( dest: string; fromStr:string; toStr:string )

str.a_replace( src: string; fromStr:string; toStr:string ); returns( "eax" );

These two routines modify the destination string by replacing occurrences of characters in the fromStr with the corresponding character found in the toStr string. That is, while scanning through the dest or src strings, these routines compare each character in the string against the characters found in fromStr . If the current character in src/dest is found in fromStr , then these routines take the corresponding character in toStr (i.e., the character at the same offset in toStr as the match in fromStr ) and they replace the character in the output string. If toStr is shorter than fromStr , then these routines will delete all characters found in fromStr that do not have a corresponding character in toStr . If toStr is longer than fromStr , these routines ignore the extra characters in toStr .

The str.replace routine directly replaces the characters in the dest string. The str.a_replace routine first makes a copy of the src string (by calling stralloc ) and then operates on the copy of the string. str.a_replace returns a pointer to the newly allocated (and converted) string in the EAX register. You should call strfree when you are done with this string.

String Conversion Functions
str.upper( dest: string )

str.lower( dest: string )

str.a_upper( src: string ); returns( "eax" );

str.a_lower( src: string ); returns( "eax" );

These routines convert all the alphabetic characters in a string to upper or lower case (as the name suggests). The str.upper and str.lower routines operate directly on the string passed as a parameter. The str.a_upper and str.a_lower routines make a copy of the string on the heap and process the characters in this copy; they return a pointer to the new string in EAX You should call the strfree routine to deallocate the storage created by str.a_lower and str.a_upper when you are done with the strings they've created.

str.delspace( dest: string )

str.a_delspace( src: string ); returns( "eax" );

These two routines delete leading spaces and tabs from a string. str.delspace operates directly on the string passed as a parameter, str.a_delspace makes a copy of the string on the heap and then deletes the spaces from that string. The str.a_delspace procedure returns a pointer to the new string in EAX. After you are done with the string created by str.a_delspace , you should call strfree to release the storage.

str.trim( dest: string )

str.a_trim( src: string ); returns( "eax" );

The str.trim and str.a_trim routines delete both leading and trailing spaces and tabs from a string. The str.trim routine operates directly on the string passed as a parameter, the str.a_trim routine makes a copy of the source string and operates on that copy. The str.a_trim procedure returns a pointer to the new string in EAX. When you are done with the string created by str.a_trim , you should call strfree to release its storage.

String Construction Functions

The string construction functions convert their parameter data to a string and append this string to the end of another string. These routines are very similar to the stdout procedures except that they write their output to a string instead of the standard output device.

Boolean Concatenation Function
str.catbool( b:boolean; dest:string );

This function concatenates the string "true" or "false" to the end of the destination string you supply as a parameter. The destination string must be large enough to hold the concatenated result or this function will raise an exception.

Character and String Concatenation Functions
str.catc( c:char; dest:string );

This function concatenates the character parameter to the end of the destination string. The destination string must be large enough to hold the concatenated result or this function will raise an exception.

str.catcSize( c:char; width:int32; fill:char; dest:string );

This function builds a string whose length is the absolute value of width (or length one if width is zero) and concatenates this string to the end of dest . If width is negative, the string this function constructs consists of width-1 fill characters followed by the value of c. If width is positive, then the string this function creates consists of c's value followed by width-1 fill chars.

str.cats( s:string; dest:string );

This function is functionally equivalent to str.cat. It concatentates s to the end of dest .

str.catsSize( s:string; width:int32; fill:char; dest:string );

This function builds a string whose length is the absolute value of width (or length one if width is zero) or the length of s , whichever is greater, and concatenates this string to the end of dest . If width is negative, the string this function constructs consists of the characters in s followed by the fill character. If width is positive, then the string this function puts s ' characters at the end of the string.

str.catcset( c:cset; dest:string );

This function translates the character set to a string (holding each character in the set in some sequence) and appends this string to the end of the dest string.

Hexadecimal String Concatentation Functions
str.catb( b:byte; dest:string );
str.catbSize( b:byte; size:dword; fill:char )

This function translates the value of b to a two-character string that is the hexadecimal representation of b and concatenates these two characters to the end of dest . The str.catbSize function lets you specify a minimum field width and a fill character. The str.catb routine uses a minimum size of two and a fill character of '0'.

str.catw( w:word; dest:string );
str.catwSize( w:word; size:dword; fill:char )

This function translates the value of w to a four-character string that is the hexadecimal representation of w and concatenates these four characters to the end of dest . The str.catwSize function lets you specify a minimum field width and a fill character. The str.catw routine uses a minimum size of two and a fill character of '0'.

str.catd( d:dword; dest:string )
str.catdSize( d:dword; size:dword; fill:char )

This function translates the value of d to an eight-character string that is the hexadecimal representation of d and concatenates these eight characters to the end of dest . The str.catdSize function lets you specify a minimum field width and a fill character. The str.catd routine uses a minimum size of two and a fill character of '0'.

str.catq( q:qword; dest:string )
str.catqSize( q:qword; size:dword; fill:char )

This function translates the value of q to a 16-character string that is the hexadecimal representation of q and concatenates these 16 characters to the end of dest . The str.catqSize function lets you specify a minimum field width and a fill character. The str.catq routine uses a minimum size of two and a fill character of '0'.

Unsigned Integer String Concatenation Functions

These routines convert unsigned integer values to a string and concatenate that string to the end of the dest string you supply as a parameter. The str.catxxxSize functions contain width and fill parameters that let you specify the minimum field width when converting the value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

 
str.catuxxxSize Output Format

 

str.catu8size

(

u8:byte;

width:int32;

fill:char;

dest:string

);

This function converts the unsigned 8-bit value u8 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.catu16size

(

u16:word;

width:int32;

fill:char;

dest:string

);

This function converts the unsigned 16-bit value u16 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.catu32size

(

u32:dword;

width:int32;

fill:char;

dest:string

);

This function converts the unsigned 32-bit value u32 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.catu64size

(

u64:qword;

width:int32;

fill:char;

dest:string

);

This function converts the unsigned 64-bit value u64 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.catu8( u8:byte; dest:string );

This function converts the eight-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.catu16( u16:word; dest:string );

This function converts the 16-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.catu32( u32:dword; dest:string );

This function converts the 32-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.catu64( u64:qword; dest:string );

This function converts the 64-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

Signed Integer String Concatenation Functions

These routines convert signed integer values to string and concatenate that string to the end of the string you specify via the dest parameter. The str.catxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.

If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.

These functions print the fill character as the padding value for the extra print positions.

Note that unlike floating point values, these functions do not print a space in front of the value if it is non-negative.

 
str.catixxSize Output Format

 

str.cati8size

(

i8:byte;

width:int32;

fill:char;

dest:string

);

This function converts the eight-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.cati16size

(

i16:word;

width:int32;

fill:char;

dest:string

);

This function converts the 16-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.cati32size

(

i32:dword;

width:int32;

fill:char;

dest:string

);

This function converts the 32-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.cati64size

(

i64:qword;

width:int32;

fill:char;

dest:string

);

This function converts the 64-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

str.cati8( i8:byte; dest:string );

This function converts the eight-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.cati16( i16:word; dest:string );

This function converts the 16-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.cati32( i32:dword; dest:string );

This function converts the 32-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.cati64( i64:qword; dest:string );

This function converts the 64-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

Floating Point String Concatenation Using Scientific Notation

The floating point numeric string conversion routines translate the three different binary floating point formats to their string representation and then concatenate this string the end of the string that the dest parameter specifies. There are two generic classes of these routines: those that convert their values to exponential/scientific notation and those that convert their string to a decimal form.

The str.cate80 , str.cate64 , and str.cate32 routines convert their values to a string using scientific notation. These three routines each have two parameters: the value to output and the field width of the result. These routines produce a string with the following format:

 
Floating Point Scientific Notation Conversion Format

 

str.cate80

(

r:real80;

width:int32;

dest:string

);

This function converts the 80-bit extended precision floating point value passed in r to a string using scientific/exponential notation. This procedure converts the value using width print positions. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 80-bit extended precision floating point values support about 18 significant digits. So a width value that yeilds more than 18 mantissa digits will produce garbage output in the low order digits of the number. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.cate64

(

r:real64;

width:int32;

dest:string

);

This function converts the 64-bit double precision floating point value passed in r to a string using scientific/exponential notation. This procedure converts the value using width print positions. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 64-bit extended precision floating point values support about 15 significant digits. So a width value that yeilds more than 15 mantissa digits will produce garbage output in the low order digits of the number. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.cate32

(

r:real32;

width:int32;

dest:string

);

This function converts the 32-bit single precision floating point value passed in r to a string using scientific/exponential notation. This procedure converts the value using width print positions. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 32-bit extended precision floating point values support about 6-7 significant digits. So a width value that yeilds more than seven mantissa digits will produce garbage output in the low order digits of the number. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

 

Floating Point String Concatentation Using Decimal Notation

Although scientific (exponential) notation is the most general display format for real numbers, real numbers you display in this format are very difficult to read. Therefore, the HLA strings module also provides a set of functions that convert real values to strings using the decimal representation. Although you cannot (practically) use these decimal output routines for all real values, they are applicable to a wide variety of common numbers you will use in your programs.

These functions come in two varieties. The first variety requires four parameters: the real value to convert, the width of the converted value, the number of digit positions to the right of the decimal point, and a padding character. The second variety only requires the first three parameters and assumes the padding character is a space. These functions write their values using the following string format:

 
str.catrxx Conversion Format

 

str.catr80

(

r:real80;

width:int32;

decpts:uns32;

pad:char;

dest:string

);

This procedure converts an 80-bit extended precision floating point value to a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.catr64

(

r:real64;

width:int32;

decpts:uns32;

pad:char;

dest:string

);

This procedure converts a 64-bit double precision floating point value to a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

str.catr32

(

r:real32;

width:int32;

decpts:uns32;

pad:char;

dest:string

);

This procedure converts a 32-bit single precision floating point value to a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.

Generic String Formatting Function
str.put( dest:string, values_to_convert );

str.put is a macro that automatically invokes an appropriate str.catxxx output routine based on the type of the parameter(s) you pass it. This is a very convenient output routine and is probably the string conversion output call you will use most often in your programs. Keep in mind that this macro is not a single function call; instead, HLA translates this macro into a sequence of calls to procedures like str.cati32 , str.cats , etc.

str.put is a macro that provides a flexible syntax for converting data to a string. This macro allows a variable number of parameters. For each parameter present in the list, str.put will call the appropriate routine to emit that data, according to the type of the parameter. Parameters may be constants, registers, or memory locations. You must separate each macro parameter with a comma.

Here is an example of a typical invocation of str.put:

 

str.put( dest, "I=", i, " j=", j );

 

The above is roughly equivalent to

 

str.puts( "I=", dest );

str.puti32( i, dest );

str.puts( " j=", dest );

str.puti32( j, dest );

 

This assumes, of course, that i and j are int32 variables.

The str.put macro also lets you specify the minimum field width for each parameter you specify. To print a value using a minimum field width, follow the object you wish to print with a colon and the value of the minimum field width. The previous example, using field widths, could look like the following:

 

str.put( dest, "I=", i:2, " j=", j:5 );

 

Although this example used the literal decimal constants two and five for the field widths, keep in mind that register values and memory value (integers, anyway) are prefectly legal here.

For floating point numbers you wish to display in decimal form, you can specify both the minimum field width and the number of digits to print to the right of the decimal point by using the following syntax:

 

str.put( dest, "Real value is ", f:10:3, nl );

 

The str.put macro can handle all the basic primitive types, including boolean, unsigned (8, 16, 32), signed (8, 16, 32), character, character set, real (32, 64, 80), string, and hexadecimal (byte, word, dword).

If you specify a class variable (object) and that class defines a " toString " method, the str.put macro will call the associated toString method and concatenate that string to the end of the destination string. Note that the toString method must dynamically allocate storage for the string by calling stralloc . This is because str.put will call strfree on the string once it outputs the string.

There is a known "design flaw" in the str.put macro. You cannot use it to print HLA intermediate variables (i.e., non-local VAR objects). The problem is that HLA's syntax for non-local accesses takes the form "reg32:varname" and str.put cannot determine if you want to print reg32 using varname print positions versus simply printing the non-local varname object. If you want to display non-local variables you must copy the non-local object into a register, a static variable, or a local variable prior to using stdout.put to print it. Of course, there is no problem using the other str.catXXXX functions to display non-local VAR objects, so you can use those as well.

Zero-Terminated String Support

Although HLA's string format is more efficient (with respect to speed) than the zero-terminated string format that languages like C, C++, and Java use, HLA programs must often interact with code that expects zero-terminated strings. Examples include HLA (assembly) code you like with C/C++/Java programs and calls you make to operating systems like Windows and Linux (that expect zero terminated strings). Therefore, the HLA Standard Library provides a limited amount of support for zero-terminated strings so it can efficiently interact with external code that requires such strings.

When passing read-only string data to some code that expects a zero-terminated string, HLA's string format is upwards compatible with zero-terminated strings. No conversion is necessary. An HLA string variable holds the address of a sequence of characters that end with a zero byte (the zero-terminated format). So as long as the code you're calling doesn't attempt to write any data to the string object, you can pass HLA string objects to functions and procedures that expect zero-terminated strings.

If the procedure or function you're calling stores data into a destination string variable, then you generally should not pass an HLA string to that function. There are two problems with this: first, the function does not check the HLA string's maximum length field to ensure that string overflow does not occur; second, the external function does not properly set the HLA string's length field before returning. Furthermore, the external code may create it's own string data in some buffer and does not even allocate space for HLA's maximum length and dynamic length fields. To workaround these limitations, HLA provides various procedures in the Standard Library that manipulate zero-terminated strings so your programs can effectively communicate with external code that operates on such strings.

Before describing the support functions that HLA provides for zero-terminated strings, it's probably worthwhile to first discuss how one writes code that comfortably co-exists with such strings. As noted above, there are three major problems one must deal with when external code processes zero-terminated strings. We'll deal with these issues one at a time.

The first problem is that the external code does not check the maximum string length field before writing character data to a string object. Therefore, the external code cannot determine if a buffer overflow will occur when that function extends the string's length. Algorithms that depend upon the string function raising an exception when a buffer overflow occurs will not work properly when calling external code that manipulates zero-terminated strings. The solution to this problem is the same as the solution in C and C++: the programmer must take the responsibility of ensuring that there is sufficient buffer space available to hold the string the external function produces. Exactly how much space you must allocate as a maximum varies on a call by call basis, but usually you can pick a sufficiently large value that is safe and preallocate storage for an HLA string whose maximum length satisifies the program's requirements. Note that most operating system API functions that return variable length strings will let you specify a maximum length parameter so the OS will not overflow your string buffer; well-written library routines and other code that create variable length zero-terminated strings and generally provide this same functionality.

The second problem, the fact that the external code that manipulates the string's data does not update HLA's string length field, is solvable by computing the length of the zero-terminated string upon return from the external code and updating the length field yourself. A convenient way to handle this operation is to write a wrapper function that you call from your code. The wrapper function calls the external code and then computes and updates the HLA string length field before returning to the original caller. This saves having to compute the length on each and every invocation of the external code. The HLA Standard Library provides a string length function that efficiently computes the length of a zero-terminated string. You can call this function upon return from the external code and then store the return result into the HLA dynamic length field.

Some external functions may create their own zero-terminated strings rather than store their string data in a buffer you supply. Such functions will probably not allocate storage for the dynamic and maximum length fields that the HLA string format requires. Therefore, you cannot directly use such string data as an HLA string in your assembly code. There are two ways to handle such string data: (1) copy the zero-terminated string to an HLA string and then manipulate the HLA string, or, (2) process the zero-terminated string using functions that directly manipulate such strings. The HLA strings module provides a set of zero-terminated string functions that let you choose either mechanism. The choice of method (1) or (2) depends entirely upon how you intend to use the string data upon return to your HLA code. If you're going to do considerable string manipulation on that string data within your HLA code (and you want to use the full set of HLA string and pattern matching functions on the string data), it makes a lot of sense to first convert the string to the HLA format. On the other hand, if you're going to do very little manipulation, or if the external function expects your code to update the string data in place (so it can refer to a modified version of the original string data at the original address the external code allocates), then it's probably best to manipulate the string data in-place using a set of zero-terminated string functions. If you need to do considerable string manipulation on some data, but the external code expects you to leave the manipulated string in the original buffer it allocates, you can convert the string to an HLA string, do the modification, and then copy the resulting string back into the original buffer; however, all this copying can be expensive, so you should be careful about using this approach.

The HLA Standard Library provides a small handful of important zero-terminated string functions. This set certainly isn't as extensive as the set of functions available for HLA strings, nor is it as extensive as the set of functions available, for example, in the C Standard Library. However, this small set of functions will probably cover 90-95% of the requirements you'll have for processing zero-terminated strings in HLA code. Generally, if you need other functionality, you can obtain it by calling C Standard Library functions from your HLA code or by first converting the string to an HLA string (and then copying the data back to the original buffer, if necessary). The following subsections describe the functions that the HLA Standard Library provides to support zero-terminated strings.

ZString Length
str.zlen( var zstring:var ); @returns( "eax" );

The single parameter is the address of a zero-terminated string. This function returns the length of that string in the EAX register.

Note that the str.zlen function has a single untyped reference parameter. Generally, you'd pass the name of a buffer variable as the parameter to this function. If the address of the zero-terminated string appears in a register, you'll need to use one of the following three invocations to call this function:

// Manual invocation- assumes the string pointer is in EBX:

 

push( ebx );

call str.zlen;

<< length is in EAX >>

.

.

.

str.zlen( [ebx] ); // zlen expects a memory operand

.

.

.

str.zlen( val ebx ); // Tell HLA to use value of ebx.

 

The str.zlen function is especially useful for updating the length field of an HLA string you've passed to some external code that generates a zero-terminated string. Consider the following code that updates the length upon return from an external function:

 

// Allocate sufficient storage to hold the string result the external

// code will produce. 1024 was chosen at random for this example, you'll

// have to pick an appropriate value based on the size of the string

// the external procedure in your code produces.

 

stralloc( 1024 );

mov( eax, strVar );

.

.

.

externalFunction( strVal ); // externalFunction overwrites strVal data.

str.zlen( strVal ); // Compute the result string's length

mov( strVar, ebx ); // Get pointer to string data.

if( eax > (type str.strRec [ebx]).MaxStrLen )) then

 

// If there was a string overflow, the overflow may

// have wiped out some important data somewhere, so

// it may be too late to raise this exception. However,

// better late than not notifying the caller at all.

// Because the buffer overflow may have corrupted the application's

// data, the application should attempt to terminate as

// gracefully as possible at this point.

 

raise( ex.StringOverflow );

 

endif;

 

// Okay, the string didn't overflow the buffer, update the

// HLA string dynamic length field:

 

mov( eax, (type str.strRec [ebx]).length );

 

Converting a ZString to an HLA String
str.cpyz( var zsrc:var; dest:string );
str.a_cpyz( var zsrc:var ); @returns( "eax" );

These two functions convert a zero-terminated string to an HLA string. The str.cpyz string copies the zero-terminated string the zsrc parameter specifies to the HLA string the dest parameter specifies. The dest parameter must point at a pre-allocated HLA string whose maximum length is large enough to hold a copy of the source string (HLA will raise an exception if this is not the case). The str.a_cpyz function allocates sufficient storage for the new string on the heap and then copies the zero-terminated string to the new string it creates (since str.a_cpyz always allocates sufficient storage, string overflow cannot occur; this function, however, can raise a memory allocation failure exception). The str.a_cpyz function returns a point to the new HLA string in the EAX register. It is the caller's responsibility to free this storage (via a call to strfree ) when the application is done using this string.

Note that the conv.cStrToStr and conv.a_cStrToStr functions are alias of str.cpyz and str.a_cpyz (respectively). The prototypes for these conv.XXXX routines simply specify the external (linker) names of the corresponding str.XXXX procedures.

These two functions are especially useful when some external C/C++ or OS code returns a pointer to a zero-terminated string that you want to manipulate in your HLA code as an HLA string. However, keep in mind that there is a bit of overhead involved with the conversion (specially, these functions must first compute the length of the zero-terminated string and then copy the string data to the HLA string, so they make two passes over the string's data). While the code that does this conversion is fairly efficient, you may want to consider the overhead of this conversion if you're writing time-critical code and you don't use the resultant HLA string very much once the conversion is complete.

Concatenting a ZString to the End of an HLA String

str.catz( var zsrc:var; dest:string );

This function concatenates a zero-terminated string to the end of an existing HLA string. It raises an exception if the resulting string's length would be greater than the maximum length the HLA string allows.

At this time, the HLA Standard Library does not provide the equivalent of an str.a_catz function. If you need this functionality, you can easily achieve it with the following code:

procedure a_catz( hlaStr:string; var zsrc:var );

@nodisplay;

@noalignstack;

@returns( "eax" );

 

begin a_catz;

 

push( ebx );

str.zlen( val zsrc );

mov( hlaStr, ebx );

add( (type str.strRec [ebx]).length, eax );

stralloc( eax );

str.cpy( hlaStr, (type string eax) );

str.catz( val zsrc, (type string eax) );

pop( ebx );

 

end a_catz;

 

Copying a ZString

str.zcpy( var zsrc:var; var dest:var );

The str.zcpy function copies one zero-terminated string to another. The destination buffer must be large enough to hold the source string and it is the caller's responsbility to ensure this. The str.zcpy routine has no way to determine the maximum size of the destination buffer, so it cannot check for buffer overflow (this is typical for zero-terminated string functions).

Since HLA strings are zero-terminated, you can use this function to copy an HLA string to a zero-terminated string. Note, however, that the str.zcpy parameters are both untyped reference parameters, so if you pass an HLA string as the source parameter, you'll typically invoke str.zcopy as follows:

 

// Assumptions: hlaString is the name of an HLA String variable and

// destZStr is the name of an array of characters or byte array.

 

str.zcpy( val hlaString, destZStr );

 

// Use this call if destZStr is a pointer variable that points at

// an array of bytes/characters:

 

str.zcpy( val hlaString, val destZStr );

 

Remember, untyped reference parameters always take the address of their actual parameter, even if the actual parameter is a pointer. You must use the VAL operator to tell HLA to use the value of the pointer, rather than the address of the pointer, when passing an HLA string (which is a pointer variable) to this function.

Of course, you can also use the str.zcpy function to copy one zero-terminated string to another. You'd typically use str.zcpy in this capacity to copy a string returned by one external function to a buffer for use by another external function that expects a zero-terminated string. Note that str.zcpy has approximately the same overhead as the str.cpyz function, so if you need to make a copy of a zero-terminated string that you're going to pass on to another function that uses zero-terminated strings and you might want to manipulate that string as an HLA string at some point, it's probably better to use the str.cpyz function.

Concatenating ZStrings

str.zcat( var zsrc:var; var dest:var );

This function concatenates one zero-terminated string to the end of another. The caller must ensure that the destination buffer is large enough to hold the resulting string; the str.zcat function has no way to verify the size of the destination buffer, so it cannot check for buffer overflow (this is typical for zero-terminated string functions).

This string is useful for manipulating zero-terminated strings some external code provides without the overhead of first converting the strings to HLA strings. If you call two external functions that return zero-terminated strings and you need to pass their concatenated result to some other external function that expects a zero-terminated string, and there is no string manipulation in your HLA code, then using str.zcat is more efficent than converting the strings to an HLA string and using the HLA string concation function.

When using this function, don't forget that it's parameters are untyped reference parameters. When passing the address of a buffer variable you may specify the name of the buffer directly. However, when passing a pointer to the buffer, you'll probably need to use the VAL operator to tell HLA to pass the pointer's value rather than the pointer's address. Here are some examples of str.zcat invocations:

static

buffer1 :char[256];

buffer2 :char[254];

bufptr1 :pointer to char;

bufptr2 :pointer to char;

.

.

.

str.zcat( buffer1, buffer2 );

str.zcat( val bufptr1, buffer1 );

str.zcat( buffer2, [edi] );

str.zcat( val bufptr2, val esi );

 

You can also use the str.zcat procedure to copy data from an HLA string to a zero-terminated string. Don't forget that HLA string variables are actually pointers, so you have to use the VAL operator when passing an HLA string as the source operand to this function:

static

hlaStr :string;

zString :char[256];

zPtr :pointer to char;

.

.

.

str.zcat( val hlaStr, zString );

str.zcat( val hlaStr, val zptr );

str.zcat( val hlaStr, [edi] );

str.zcat( val hlaStr, val esi );

 

It really does not make any sense to specify an HLA string variable as the destination operand. str.zcat does not update the HLA string's length field, so if you supply an HLA string as the destination operand, the str.zcat procedure may corrupt the HLA string, forcing you to manually compute the length yourself. If you need to copy a zero-terminated string to an HLA string, use the str.cpyz function instead.

Comparing ZStrings

str.zcmp( var zsrc1:var; var zsrc2:var ); @returns( "eax" );

The str.zcmp function compares two zero-terminated strings and returns the comparison results in the EAX register and in the x86 flags. This comparison function sets the condition code bits so you can use the standard unsigned condition instructions (jump and set instructions) immediately upon return to test for less than, less than or equal, equal, not equal, greater than, or greater than or equal. This function also returns -1 ($FFFF_FFFF), zero, or one in EAX to indicate less than, equal, or greater than (respectively). Note that this function compares zsrc1 to zsrc2. Therefore, this function returns -1 if zsrc1 < zrc2, zero if zsrc1 = zsrc2, and one if zsrc1 > zsrc2.

This function is especiially useful for comparing two zero-terminated strings that some external code returns to your HLA program if you don't need to do any further manipulation of the string data. This function is also useful for comparing an HLA string against a zero-terminated string (since HLA strings are zero terminated). Technically, you could use this function to compare two HLA strings (since they are zero-terminated), but the standard HLA string comparison functions are probably more efficient for this purpose.

Note that when supplying an HLA string variable as an operand to this function (or, in general, any pointer variable) you will need to use the VAL operator so that HLA passes the pointer to the string (rather than the address of the pointer to the string) as the parameter, e.g.:

static

hlaString :string;

strPtr :pointer to char;

zstring :char[256];

.

.

.

str.zcmp( val hlaString, zstring );

str.zcmp( zstring, val strPtr );

str.zcmp( zstring, val edi );

 

 

HLA Table Support (tables.hhf)

The HLA Tables module provides support for associative lookup tables. You can view a table as an array of objects that uses a string index rather than an integer index. The HLA table routines use a hash table to rapidly look up the specified string in the table and return a pointer to the specified element in the table.

 

To begin with, each element (called a node) in an HLA table uses the following structure:

 

tableNode:

record

 

link: pointer to tableNode;

Value: dword;

id: string;

 

endrecord;

 

The link field is for internal use by the table management routines; you should never modify its value.

The id field points at the string that indexes the current node in the table. You may use the value of this string, but you must never modify the characters in the string. The hashing function utilitized by the table management code will not be able to locate this string if you change the characters in the string after entering the string into the table.

The Value field is reserved for your own purposes. Other than initializing this field to zero when they create a table entry, the table management routines ignore this field. If you wish to associate data with an entry in the table you can either store the data directly in this field (if the data is four bytes or less), or you can allocate storage for the data outside the table entry and store a pointer to the data in this field (remember, pointers are four-byte objects).

The table data type is a class that provides the following methods and procedures14:

 

procedure table.create( HashSize:uns32 );

method table.destroy( FreeValue:procedure );

method table.getNode( id:string );

method table.lookup( id:string );

iterator table.item();

 

Like most HLA classes, the table class provides a constructor named table.create and a destructor named table.destroy . The class also provides two additional methods and an iterator, table.getNode , table.lookup, and table.item . Although there doesn't appear to be much to this class, these four routines provide considerable power.

table.create( HashSize:uns32 );

The create procedure, being a static procedure constructor, is typically called in one of two fashions:

When dynamically allocating a table object on the heap and storing the pointer away into a variable whose type is "pointer to table":

 

table.create( some_constant );

mov( esi, PtrToTableVar );

 

When you've got a var or static variable object (named table_var_name in this example), you can use code like the following to construct the table object:

 

table_var_name.create( some_constant );

 

The table constructor requires a single uns32 parameter. This value should be approximately the number of entries (elements) you expect to insert into the table. This value does not have to be exact. Anytime you want to add a new element to the table, you may do so; there are no limitations (other than available memory) on the number of elements in a table. However, this parameter value is a hint to the table management routines so it can allocate a hash table of an appropriate size so that (1) access to the table elements is fast, and (2) the hash table doesn't waste an inordinate amount of space. If the hint value you supply is too small, the table lookup routines will still function properly, but they will run a little slower. If the hint value you supply is too large, the table management routines will waste some memory.

table.destroy( FreeValue:procedure );

The table.destroy method frees up the data in the table. This routine deallocates the storage associated with the hash table, it deallocates the storage associated with each node in the table, and it deallocates the storage associated with each string (the id field in record tableNode above) in the table. Unfortunately, the table.destroy method doesn't know anything at all about the Value field of each node. If this is just some simple data, then the destructor probably doesn't need to do anything with the Value field. On the other hand, if Value is a pointer to some other data that was dynamically allocated, destroy should probably deallocate the storage associated with the Value field. Unfortunately, destroy has no apriori knowledge about the Value field, so it cannot determine if (or how) it should deallocate storage associated with Value .

To resolve the problem above, table.destroy calls a user-defined function that is responsible for cleaning up the data associated with Value . You will notice that table.destroy has a single parameter: FreeValue . This parameter must be the address of a procedure whose job is to handle the destruction of the Value field. If no clean up is necessary, you must still provide the address of some routine. That routine should simply return without further activity.

Upon entry into the FreeValue procedure, the EBX register contains a pointer to the current tableNode record being deallocated. At this point, none of the other fields have been modified; in particular, the id field is still pointing at the string associated with the node. The FreeValue procedure may access the id field, but it must not deallocate the storage associated with this string. The table.destroy method takes care of that after FreeValue returns.

The "tabledemo.hla" file accompanying the HLA release gives another example of how you could use the FreeValue procedure. This code doesn't deallocate any storage in this procedure (named PrintIt in this file), instead, it uses this call to dump the data associated with the node to the display when the table is freed.

table.lookup( id:string );

The table.lookup method locates a node in the table. The string parameter specifies which node to find in the table. On return, EAX contains the address of the corresponding tableNode record in the table, if the specified node is present (that is, the node's id field matches the string passed to table.lookup ). If the node is not present in the table, then the table.lookup method returns NULL (zero) in the EAX register.

table.getNode( id:string );

The table.getNode method serves two purposes. First of all, it looks up the specified string value in the table. If it finds a node in the table corresponding to the string parameter, it returns a pointer to that ( table.tableNode ) node in the EAX register. If it does not find the string in the table, then it creates a new node and inserts the string into that new node; it also initializes the Value field to zero in this case. Note that table.getNode makes a copy (using str.a_cpy ) of the string, it does not store the string pointer you pass directly into the id field. Upon return, EAX will point at the new node. Note that whether or not an existing node is present in the table, table.getNode will always return a pointer to the node associated with the specified string. It either returns a pointer to a pre-existing node or it returns the pointer to the new node15.

If you want to insert a new node into the table and fail if the node already exists, you will need to first call table.lookup to see if the node previously exists (and fail if it does). You may then call table.getNode to insert a new node into the table. While it would be easy to add this functionality to the table class, it would be rarely used and probably isn't needed.

table.item;

The table.item iterator yields each node in the table during the execution of the corresponding foreach loop. Note that table.item does not yield the nodes in any particular (discernable) order. However, it will yield each item in the list exactly once. The iterator returns with EAX pointing at a table.tableNode object.

The HLA Timer Module (timer.hhf)

The HLA Timer module provides a set of routines that let you time events with millisecond precision. The Timer module is actually a class with the following definition:

 

timer: class

 

var

Accumulated: qword;

 

DateStarted: date.daterec;

TimeStarted: time.timerec;

msStarted: uns32;

 

DateStopped: date.daterec;

TimeStopped: time.timerec;

msStopped: uns32;

 

Running: boolean;

Valid: boolean;

 

procedure create; external;

 

method start; external;

method restart; external;

method stop; returns( "edx:eax" ); external;

method checkPoint; returns( "edx:eax" ); external;

 

endclass;

 

Don't forget that the timer class, like all class objects, will modify the values of the ESI and EDI registers whenever you call a class procedure or method. So don't expect values in ESI or EDI to be preserved across the calls in this module.

timer.Accumulated

This field contains the computed time in milliseconds. This field is only valid if the timer.Valid field contains true. If timer.Running contains true, then the timer is still running and the timer.Accumulated field contains the number of milliseconds at the last timer.checkPoint or timer.restart operation.

timer.DateStarted

timer.TimeStarted

timer.msStarted

timer.DateStopped

timer.TimeStopped

timer.msStopped

These are internal variables to the class. You should not modify their values nor should you read their values and use them for anything.

timer.Running

This boolean variable indicates that the timer object is currently timing some event. You may read this variable but you should not modify its value.

timer.create;

This is the constructor for the class. If you call it via "someObjectName.create();" then this static class procedure will initialize the fields of the specified object. If you call it via "timer.create();" then timer.create will dynamically allocate storage for a timer object and initialize that storage. This call will return a pointer to the new object in the ESI register.

timer.start;

This method will initialize the timer so it can begin timing some sequence. Note that this call will set the timer.Running field to true and the timer.Valid field to false.

timer.stop;

This method will stop the timer accumulation and returns the accumulated time in EDX:EAX (e.g., the time since the last timer.start call). This call sets timer.Valid to true and timer.Running to false.

timer.checkPoint;

This computes the current time in timer.Accumulated without stopping the timer. That is, timer.Valid will be set to true and timer.Running will remain true.

timer.restart;

This method restarts the timer after you've stopped the timing via timer.stop . Note that the result accumulated will be the sum of the previous accumulation plus the new time.

Win32 Constants (win32.hhf)

The win32.hhf header file contains constant and type declarations of interest to those making Win32 API calls. This file will grow over time, so be sure to download a copy of HLA often to get the latest updates to this file.

 

Symbols

_acos (math module) 98

_acot (math module) 98

_acsc (math module) 99

_asec (math module) 99

_asin (math module) 98

_bSize (conversions module) 37

_cot (math module) 97

_csc (math module) 97

_dSize (conversions module) 38

_exp (math module) 100

_i16Size (conversions module) 37

_i32Size (conversions module) 37

_i8Size (conversions module) 37

_ln (math module) 101

_log (math module) 100

_sec (math module) 97

_tenToX (math module) 99

_twoToX (math module) 99

_u16Size (conversions module) 37

_u32Size (conversions module) 37

_u8Size (conversions module) 37

_wSize (conversions module) 38

_YtoX (math module) 100

A

a_cpy (strings module) 150

a_cStrToStr (conversions module) 44

a_delete (strings module) 155

a_delspace (strings module) 155

a_extract (patterns module) 118

a_getRect (console module) 30

a_gets (console module) 32

a_gets (file class module) 67

a_gets (file I/O module) 82

a_gets (stdin module) 134

a_getTitle (console module) 31

a_insert (strings module) 154

a_lower (strings module) 155

a_replace (strings module) 155

a_roman (conversions module) 44

a_setstr (strings module) 150

a_substr (strings module) 154

a_toString (date/time module) 48

a_trim (strings module) 155

a_upper (strings module) 155

AccessViolation (exception) 54

Accumulated (timer module) 174

acos (math module) 98

acos32 (math module) 98

acos64 (math module) 98

acos80 (math module) 98

acot (math module) 98

acot32 (math module) 98

acot64 (math module) 98

acot80 (math module) 98

acsc (math module) 99

acsc32 (math module) 99

acsc64 (math module) 99

acsc80 (math module) 99

addl (math routine) 92

addq (math routine) 92

afterRow (arrays module) 5

Allocating storage for dynamic arrays 2

Alphabetic predicate (test) 14

Alphanumeric predicate (test) 14

Alternation (in pattern matching) 106

andl (math routine) 92

andq (math routine) 92

append_index (list module) 89

append_last (list module) 90

append_node (list module) 90

arb (patterns module) 120

arg.c 25

arg.CmdLn 25

arg.delete 25

arg.GlobalOptions 25

arg.LocalOptions 25

arg.v 25

args.hhf 25

array.afterRow 5

array.beforeRow 5

array.BoundsChk 4

array.cpy 5

array.daAlloc 2

array.daFree 3

array.dArray 2

array.element 4

array.endreduce 5

array.index 4

array.IsItDynamic 3

array.IsItVar 3

array.reduce 5

array.reduction 5

array.transpose 7

ArrayBounds (exception) 53

Arrays 2

arrays.hhf 2

ArrayShapeViolation (exception) 53

asec (math module)' 99

asec32 (math module) 99

asec64 (math module) 99

asec80 (math module) 99

asin (math module) 98

asin32 (math module) 98

asin64 (math module) 98

asin80 (math module) 98

AssertionFailed (exception) 54

Assertions (exceptions) 56

atof (conversions module) 43

atoh (conversions module) 39

atoi (conversions module) 39

atou (conversions module) 39

atPos (patterns module) 106

AttemptToDerefNULL (exception) 53

B

Back tracking 120

Back tracking support in pattern matching functions 122

BadFileHandle (exception) 52

beforeRow (arrays module) 5

Bit manipulation 9

bits.cnt 9

bits.cnt function 9

bits.coalese 12

bits.distribute function 11

bits.extract 11

bits.merge16 10

bits.merge32 10

bits.merge8 10

bits.nibbles16 10

bits.nibbles32 10

bits.nibbles8 10

bits.reverse16 9

bits.reverse32 9

bits.reverse8 9

BoundInstr (exception) 54

Bounds checking array indices 4

BoundsChk (arrays module) 4

Breakpoint (exception) 54

brk (strings module) 152

brk2 (strings module) 153

bSize (conversions module) 37

bToStr (conversions module) 36

byteToHex (conversions module) 36

C

c (args module) 25

CannotFreeMemory (exception) 53

case 34

Case insensitive character matching routines 114

catbool (strings module) 156

catbSize (strings module) 156

catc (strings module) 156

catcset (strings module) 156

catcsize (strings module) 156

catdSize (strings module) 157

catdw (strings module) 157

cate32 (strings module) 162

cate64 (strings module) 162

cate80 (strings module) 161

cath (strings module) 156

cati16 (strings module) 160

cati16size (strings module) 160

cati32 (strings module) 161

cati32size (strings module) 160

cati64 (strings module) 161

cati64size (strings module) 160

cati8 (strings module) 160

cati8size (strings module) 159

catqSize (strings module) 157

catqw (strings module) 157

catr32 (strings module) 164

catr64 (strings module) 163

catr80 (strings module) 163

cats (strings module) 156

catssize (strings module) 156

catu16 (strings module) 158

catu16size (strings module) 158

catu32 (strings module) 159

catu32size (strings module) 158

catu64size (strings module) 158

catu8 (strings module) 158

catu8size (strings module) 157

catw (strings module) 156

catwSize (strings module) 156

catz (string function) 168

Character matching routines 112

Character sets 16

chars.isAlpha 14

chars.isAlphaNum 14

chars.isASCII 15

chars.isCtrl 15

chars.isDigit 14

chars.isGraphic 14

chars.isLower 14

chars.isSpace 15

chars.isUpper 14

chars.isXDigit 14

chars.toLower 14

chars.toUpper 14

charToCset (character sets) 17

Checking the bounds of an array 4

checkPoint (timer module) 175

chpos (strings module) 152

chpos2 (strings module) 152

Close (file class module) 58

Close (file I/O module) 71

clrToEOLN (console module) 28

clrToEOScrn (console module) 28

cls (console module) 28

CmdLn (args module) 25

cnt (bits module) 9

coalese (bits module) 12

cocall (coroutines module) 45

cofree (coroutines module) 46

Command line arguments 25

complement (character sets) 18

console.a_getRect 30

console.a_gets 32

console.a_getTitle 31

console.clrToEOLN 28

console.clrToEOScrn 28

console.cls 28

console.deleteChars 29

console.deleteLines 29

console.deleteLn 29

console.fillRect 29

console.fillRectAttr 29

console.getc 32

console.getRect 29

console.gets 32

console.getTitle 31

console.getX 28

console.getY 28

console.gotoxy 28

console.info 27

console.insertChars 28

console.insertLines 29

console.insertLn 28

console.numButtons 32

console.peekInput 31

console.putRect 30

console.puts 33

console.putsx 33

console.scrollDn 33

console.scrollDnN 33

console.scrollDnRect 30

console.scrollDnx 33

console.scrollUp 33

console.scrollUpN 33

console.scrollUpRect 30

console.scrollUpx 33

console.setMode 32

console.setOutputAttr 31

console.setTitle 31

Context-free macros 6

Control structures 34

ControlC (exception) 56

conv._bSize 37

conv._dSize 38

conv._i16Size 37

conv._i32Size 37

conv._i8Size 37

conv._u16Size 37

conv._u32Size 37

conv._u8Size 37

conv._wSize 38

conv.a_cStrToStr 44

conv.a_roman 44

conv.atof 43

conv.atoh 39

conv.atoi 39

conv.atou 39

conv.bSize 37

conv.bToStr 36

conv.byteToHex 36

conv.cStrToStr 43

conv.dSize 37

conv.dToStr 36

conv.e32ToStr 41

conv.e64ToStr 41

conv.e80ToStr 40

conv.getDelimiters 36

conv.getUnderscores 36

conv.hhf 36

conv.i128Size 37

conv.i128ToBuf 38

conv.i128ToStr 38

conv.i16Size 37

conv.i16ToBuf 38

conv.i16ToStr 38

conv.i32Size 37

conv.i32ToBuf 38

conv.i32ToStr 38

conv.i64Size 37

conv.i64ToBuf 38

conv.i64ToStr 38

conv.i8Size 37

conv.i8ToBuf 38

conv.i8ToStr 38

conv.lSize 37

conv.lToStr 36

conv.qSize 37

conv.qToStr 36

conv.r32ToStr 43

conv.r64ToStr 42

conv.r80ToStr 42

conv.roman 44

conv.setDelimiters 36

conv.setUnderscores 36

conv.strTob 37, 39

conv.strTod 37, 39

conv.strToFlt 43

conv.strToi128 39

conv.strToi16 39

conv.strToi32 39

conv.strToi64 39

conv.strToi8 39

conv.strTol 37, 39

conv.strToq 37, 39

conv.strTou16 39

conv.strTou32 39

conv.strTou64 39

conv.strTou8 39

conv.strTow 37, 39

conv.tbToStr 37

conv.u128Size 37

conv.u128ToBuf 38

conv.u128ToStr 38

conv.u16Size 37

conv.u16ToBuf 38

conv.u16ToStr 38

conv.u32Size 37

conv.u32ToBuf 38

conv.u32ToStr 38

conv.u64Size 37

conv.u64ToBuf 38

conv.u64ToStr 38

conv.u8Size 37

conv.u8ToBuf 38

conv.u8ToStr 38

conv.wSize 37

conv.wToStr 36

ConversionError (exception) 52

Conversions 36

Copying data from one array to another 5

coret (coroutines module) 46

coroutine.cocall 45

coroutine.cofree 46

coroutine.create 45

Coroutines 45

coroutines.hhf 45

cot (math module) 97

cot32 (math module) 97

cot64 (math module) 97

cot80 (math module) 97

Counting the number of command line arguments 25

Counting the number of set bits in an object 9

cpy (arrays module) 5

cpy (character sets) 17

cpy (strings module) 150

cpyz (string function) 167

create (coroutines module) 45

create (file class module) 57

create (list module) 89

create (tables module) 171

create (timer module) 175

cs.charToCset 17

cs.complement 18

cs.cpy 17

cs.difference 18

cs.empty 17

cs.eq 16

cs.extract 17

cs.intersection 18

cs.IsEmpty 16

cs.member 16

cs.ne 16

cs.psubset 16

cs.psuperset 16

cs.rangeChar 17

cs.removeChar 18

cs.removeStr 18

cs.removeStr2 18

cs.setunion 18

cs.strToCset 17

cs.subset 16

cs.superset 16

cs.unionChar 18

cs.unionStr 18

cs.unionStr2 18

csc (math module) 97

csc32 (math module) 97

csc64 (math module) 97

csc80 (math module) 97

cset.hhf 16

cStrToStr (conversions module) 43

Cursor position within a pattern 121

curTime (date/time module) 50

D

daAlloc 2

daFree (arrays module) 3

dArray (arrays module) 2

date.a_toString 48

date.datePlusDays 49

date.datePlusMonths 49

date.daterec 47

date.dayNumber 48

date.dayOfWeek 49

date.daysBetween 49

date.daysLeft 49

date.fromJulian 48

date.IsLeapYear 47

date.isValid 47

date.Julian 48

date.OutputFormat 47

date.print 48

date.SetFormat 47

date.SetSeparator 48

date.today 50

date.toString 48

date.validate 47

datePlusDays (date/time module) 49

datePlusMonths (date/time module) 49

daterec (date/time module) 47

Dates and times 47

DateStarted (timer module) 174

DateStopped (timer module) 174

datetime.hhf 47

dayNumber (date/time module) 48

dayOfWeek (date/time module) 49

daysBetween (date/time module) 49

daysLeft (date/time module) 49

deal (random module) 131

Decimal digit predicate (test) 14

default 34

Deferred evaluation 120

delete (args module) 25

delete (strings module) 154

delete_first (list module) 91

delete_index (list module) 90

delete_last (list module) 91

delete_node (list module) 90

deleteChars (console module) 29

deleteLines (console module) 29

deleteLn (console module) 29

Deleting an argument from the command line 25

delspace (strings module) 155

destroy (list module) 89

difference (character sets) 18

Disabling bounds checking on arrays 4

DiskFullError (exception) 52

distribute (bits module) 11

DivideError (exception) 55

divl (math routine) 93

divq (math routine) 93

dSize (conversions module) 37

dToStr (conversions module) 36

Dynamic arrays 2

E

e32ToStr (conversions module) 41

e64ToStr (conversions module) 41

e80ToStr (conversions module) 40

Eager evaluation 120

Element (arrays module) 4

empty (character sets) 17

Enabling bounds checking 4

EndOfFile (exception) 52

endOneOrMorePat (patterns module) 108

endOnePat (patterns module) 107

endreduce (arrays module) 5

endswitch 34

endZeroOrMorePat (patterns module) 108

endZeroOrOnePat (patterns module) 108

eof (file I/O module) 71

eoln (file class module) 66

eoln (file I/O module) 81

eoln (stdin module) 133

eoln2 (stdin module) 133

EOS (patterns module) 106

eq (character sets) 16

eq (strings module) 150

ex.AccessViolation 54

ex.ArrayBounds 53

ex.ArrayBounds exception 4

ex.ArrayShapeViolation 53

ex.AssertionFailed 54

ex.AttemptToDerefNULL 53

ex.BadFileHandle 52

ex.BoundInstr 54

ex.Breakpoint 54

ex.CannotFreeMemory 53

ex.ControlC 56

ex.ConversionError 52

ex.DiskFullError 52

ex.DivideError 55

ex.EndOfFile 52

ex.ExecutedAbstract 54

ex.fDenormal 55

ex.fDivByZero 55

ex.FileCloseError 52

ex.FileOpenFailure 52

ex.FileReadError 52

ex.FileWriteError 52

ex.fInexactResult 55

ex.fInvalidOperation 55

ex.fOverflow 55

ex.fStackCheck 55

ex.fUnderflow 55

ex.IllegalChar 51

ex.IllegalInstr 54

ex.IntoInstr 55

ex.InvalidDate 53

ex.InvalidDateFormat 53

ex.InvalidHandle 56

ex.MemoryAllocationFailure 52

ex.NDEBUG (assertion control) 56

ex.PrivInstr 54

ex.SingleStep 54

ex.StackOverflow 56

ex.StringIndexError 51

ex.StringOverflow 51

ex.TimeOverflow 54

ex.TooManyCmdLnParms 53

ex.UnknownException 51

ex.ValueOutOfRange 51

ex.WidthTooBig 53

exactlyNChar (patterns module) 113

exactlyNCset (patterns module) 110

exactlyNiChar (patterns module) 116

exactlyNtoMChar (patterns module) 114

exactlyNtoMCset (patterns module) 111

exactlyNtoMiChar (patterns module) 117

Exception constants 51

Exceptions 51

excepts.hhf 51

ExecutedAbstract (exception) 54

exp (math module) 100

exp32 (math module) 100

exp64 (math module) 100

exp80 (math module) 100

extract (bits module) 11

extract (character sets) 17

extract (patterns module) 118

F

fail (patterns module) 107

fDenormal (exception) 55

fDivByZero (exception) 55

fence (patterns module) 107

File class 57

File input routines 81

File output routines 71

file.create 57

fileclass.hhf 57

FileCloseError (exception) 52

fileio.a_gets 82

fileio.Close 71

fileio.eof 71

fileio.eoln 81

fileio.get (fileio module) 85

fileio.getc 82

fileio.getdw 84

fileio.getf 85

fileio.geth 84

fileio.geti128 83

fileio.geti16 82

fileio.geti32 82

fileio.geti64 83

fileio.geti8 82

fileio.getl 85

fileio.getq 84

fileio.gets 82

fileio.getu128 84

fileio.getu16 83

fileio.getu32 83

fileio.getu64 84

fileio.getu8 83

fileio.getw 84

fileio.hhf 71

fileio.newln 73

fileio.Open 71

fileio.OpenNew 71

fileio.put 80

fileio.putbool 73

fileio.putbSize 74

fileio.putc 73

fileio.putcset 73

fileio.putcsize 73

fileio.putd 74

fileio.putdSize 74

fileio.pute32 79

fileio.pute64 78

fileio.pute80 78

fileio.puth 74

fileio.puti128 76

fileio.puti128size 76

fileio.puti16 76

fileio.puti16size 75

fileio.puti32 76

fileio.puti32size 75

fileio.puti64 76

fileio.puti64size 76

fileio.puti8 76

fileio.puti8size 75

fileio.putl 75

fileio.putlSize 75

fileio.putq 74

fileio.putqSize 74

fileio.putr32 80

fileio.putr32pad 79

fileio.putr64 80

fileio.putr64pad 79

fileio.putr80 80

fileio.putr80pad 79

fileio.puts 74

fileio.putssize 74

fileio.puttb 74

fileio.putu128 78

fileio.putu16 77

fileio.putu16size (file I/O module) 77

fileio.putu32 77

fileio.putu32size 77

fileio.putu64 78

fileio.putu64size 77

fileio.putu8 77

fileio.putu8size 77

fileio.putw 74

fileio.putwSize 74

fileio.read 81

fileio.ReadLn 81

fileio.write 73

FileOpenFailure (exception) 52

FileReadError (exception) 52

filevar.a_gets 67

filevar.Close 58

filevar.eoln 66

filevar.get 70

filevar.getc 67

filevar.getdw 69

filevar.getf 70

filevar.geth 69

filevar.geti16 67

filevar.geti32 68

filevar.geti64 68

filevar.geti8 67

filevar.getqw 69

filevar.gets 67

filevar.getu16 68

filevar.getu32 68

filevar.getu64 69

filevar.getu8 68

filevar.getw 69

filevar.handle 57

filevar.newln 58

filevar.Open 58

filevar.OpenNew 58

filevar.put 65

filevar.putbool 58

filevar.putbSize 59

filevar.putc 59

filevar.putcset 59

filevar.putcsize 59

filevar.putdw 59

filevar.pute32 63

filevar.pute64 63

filevar.pute80 63

filevar.puth 59

filevar.puti16 61

filevar.puti16size 60

filevar.puti32 61

filevar.puti32size 60

filevar.puti64 61

filevar.puti64size 60

filevar.puti8 61

filevar.puti8size 61

filevar.putqSize 60

filevar.putqw 60

filevar.putr32 64

filevar.putr32pad 64

filevar.putr64 64

filevar.putr64pad 64

filevar.putr80 64

filevar.putr80pad 64

filevar.puts 59

filevar.putssize 59

filevar.puttb 60

filevar.putu16 62

filevar.putu16size 62

filevar.putu32 62

filevar.putu32size 62

filevar.putu64 62

filevar.putu64size 61

filevar.putu8 62

filevar.putu8size 62

filevar.putw 59

filevar.putwSize 59

filevar.read 66

filevar.ReadLn 66

filevar.write 58

FileWriteError (exception) 52

fillRect (console module) 29

fillRectAttr (console module) 29

fInexactResult (exception) 55

fInvalidOperation (exception) 55

firstNChar (patterns module) 113

firstNCset (patterns module) 110

firstNiChar (patterns module) 116

FlushInput (stdin module) 133

fOverflow (exception) 55

free (memory module) 102

fromJulian (date/time module) 48

fStackCheck (exception) 55

fUnderflow (exception) 55

G

ge (strings module) 151

get (file class module) 70

get (fileio module) 85

getc (console module) 32

getc (file class module) 67

getc (file I/O module) 82

getc (stdin module) 134

getd (stdin module) 134

getDelimiters (conversions module) 36

getdw (file class module) 69

getdw (file I/O module) 84

getf (file class module) 70

getf (file I/O module) 85