HLA Standard Library Reference Manual

1 Passing Parameters to Standard Library Routines 1

1.1 Parameter Passing 1

1.2 Passing Parameters by Reference and by Value 2

1.3 Passing Byte Parameters on the Stack 3

1.4 Passing Word Parameters on the Stack 5

1.5 Passing DWord Parameters on the Stack 5

1.6 Passing QWord Parameters on the Stack 6

1.7 Passing TByte Parameters on the Stack 7

1.8 Passing LWord Parameters on the Stack 7

2 Command-Line Arguments (args.hhf) 9

2.1 The Args Module 9

2.2 Retrieving the Command Line 9

2.3 Argument Count and Item 10

2.4 Deleting Command Line Arguments 11

2.5 Argument Iterators 12

3 Arrays Module (arrays.hhf) 15

3.1 The Arrays Module 15

3.2 Array Data Types 15

3.3 Array Allocation and Deallocation 16

3.4 Array Predicates 17

3.5 Array Element Access 17

3.6 Array Operations 18

3.7 Lookup Tables 22

4 Bit Manipulation (bits.hhf) 25

4.1 Bit Module 25

4.2 Bit Counting Function 25

4.3 Bit Reversal Functions 26

4.4 Bit Merging Functions 28

4.5 Bit Extraction Functions 30

4.6 Bit Distribution Functions 32

5 The Blobs Module (blobs.hhf) 35

5.1 Conversion Format Control 35

5.2 Blob Synopsis 35

5.3 Blob Internal Representation 36

5.4 Declaring Blob Variables 36

5.4.1 Initializing and Allocating Blob Variables 37

5.5 Blob Accessor Functions 39

5.6 Blob Assignment Functions 42

5.7 Blob Extraction Functions 44

5.8 Blob Comparison Functions 45

5.9 Blob Scanning Functions 45

5.10 Blob Concatenation Functions 48

5.11 Blob Conversion Functions 51

5.12 General Blob I/O Functions 53

5.13 Blob Binary I/O Routines 55

5.14 Blob Output Routines 63

5.15 Blob Input Routines 64

6 Character Classification and Utilities Module (chars.hhf) 65

6.1 Conversion Functions 65

6.2 Predicates (Tests) 66

7 Console Display Control (console.hhf) 73

7.1 The Console Module Module 73

7.2 Cursor Positioning Functions 73

7.3 Console Clearing Functions 76

7.4 Character Insertion/Removal Functions 78

7.5 Console Scrolling 80

7.6 Console Output Colors 81

8 Conversions (conv.hhf) 83

8.1 Buffer vs. String Conversions 83

8.2 Conversion Format Control 84

8.2.1 Underscore Control 84

8.2.2 Delimiter Control 87

8.3 Hexadecimal Conversions 91

8.3.1 Internal Routines 92

8.3.2 Hexadecimal Numeric Size Functions 92

8.3.2.1 Fixed Size Hexadecimal Size Functions 92

8.3.2.2 Standard Hexadecimal Size Functions 96

8.3.3 Hexadecimal Numeric to Buffer Conversions 101

8.3.3.1 Fixed Length Hexadecimal Numeric to Buffer Conversions 101

8.3.3.2 Variable Length Hexadecimal Numeric to Buffer Conversions 108

8.3.4 Hexadecimal Numeric to String Conversions 115

8.3.4.1 Fixed-Length Numeric to Hexadecimal String Conversions 115

8.3.4.2 Variable-Length Numeric to Hexadecimal String Conversions 130

8.3.5 Hexadecimal Buffer to Numeric Conversions 146

8.3.6 Hexadecimal String to Numeric Conversions 151

8.4 Signed Integer Conversions 157

8.4.1 Internal Functions 157

8.4.2 Integer Size Calculations 157

8.4.3 Signed Integer Numeric to Buffer Conversions 161

8.4.4 Integer Numeric to String Conversions 167

8.4.5 Signed Integer String to Numeric Conversions 182

8.5 Unsigned Integer Conversions 192

8.5.1 Internal Routines 193

8.5.2 Unsigned Integer Size Calculations 193

8.5.3 Unsigned Integer Numeric to Buffer Conversions 197

8.5.4 Unsigned Integer Numeric to String Conversions 203

8.5.5 Unsigned Integer String to Numeric Conversions 219

8.6 Floating Point Conversions 229

8.6.1 Exponential Floating-Point Conversions 229

8.6.2 Floating Point Numeric to Buffer Conversions, Exponential Form 230

8.6.3 Floating Point Numeric to String Conversions, Exponential Form 232

8.6.4 Floating Point Numeric to Character Conversions, Decimal Form 236

8.6.5 Floating-Point Numeric to String Conversions, Decimal Form 240

8.6.6 Floating Point String/Buffer to Numeric Conversions 244

8.6.7 Roman Numeral Conversion 245

9 Coroutines Module (coroutines.hhf) 247

9.1 The Coroutine Module 247

9.2 The Coroutine Class Definition 247

9.3 Coroutine Functions 248

10 Character Sets (cset.hhf) 251

10.1 Predicates (tests) 251

10.2 Character Set Construction and Manipulation 259

10.3 Set Operations 267

11 Date Functions (datetime.hhf) 279

11.1 The Date Module 279

11.2 Date Data Types 279

11.3 Date Tables 280

11.4 Date Predicates 281

11.5 Date Conversions 283

11.6 Date Arithmetic 286

11.7 Reading the Current System Date 289

11.8 Date Output and String Conversion 290

11.9 Date Class Types 291

11.9.1 Date Class Methods/Procedures 292

11.9.2 Creating New Date Class Types 293

11.9.3 Date Class Functions 297

12 Environment Variables Module (env.hhf) 301

12.1 The Env Module 301

12.2 Retrieving Environment Strings 301

13 Exceptions Module (excepts.hhf) 303

13.1 The Exceptions Module 303

13.2 Exception Resource Reduction 303

13.3 Exception Constants 303

13.4 Exception Messages 310

14 File Class Module (fileclass.hhf) 313

14.1 File Class Methods/Procedures 313

14.2 A Quick Note 314

14.3 General File Operations 314

14.4 Opening and Closing Files 315

14.5 File Predicates 316

14.6 Miscellaneous Output 317

14.7 Character, Character Set, and String Output 318

14.8 Hexadecimal Numeric Output 319

14.9 Signed Integer Numeric Output 323

14.10 Unsigned Integer Numeric Output 326

14.11 Floating-Point Numeric Output Using Scientific Notation 329

14.12 Floating-Point Numeric Output Using Decimal Notation 330

14.13 Generic File Output 332

14.14 Generic File Input 333

14.15 Character and String Input 333

14.16 Signed Integer Input 334

14.17 Unsigned Integer Input 336

14.18 Hexadecimal Input 337

14.19 Floating-Point Input 339

14.20 Generic File Input 339

15 The File I/O Module (fileio.hhf) 341

15.1 Conversion Format Control 341

15.2 General File I/O Functions 341

15.3 File Output Routines 354

15.3.1 Miscellaneous Output Routines 354

15.3.2 Character, String, and Character Set Output Routines 358

15.3.3 Hexadecimal Output Routines 365

15.3.4 Signed Integer Output Routines 388

15.3.5 Unsigned Integer Output Routines 402

15.3.6 Floating Point Output Routines 416

15.3.6.1 Real Output Using Scientific Notation 416

15.3.6.2 Real Output Using Decimal Notation 420

15.3.7 Generic File Output Routine 424

15.4 File Input Routines 425

15.4.1 General File Input Routines 425

15.4.2 Character and String Input Routines 428

15.4.3 Signed Integer Input Routines 430

15.4.4 Unsigned Integer Input Routines 434

15.4.5 Hexadecimal Input Routines 437

15.4.6 Floating Point Input 442

15.4.7 Generic File Input 443

16 The File System Module (filesys.hhf) 445

16.1 Filename and Pathname String Functions 445

16.2 Directory and File Predicates 461

16.3 File Information Functions 462

16.4 Directory and File Manipulation Functions 463

17 HLA Related Macros and Constants (hla.hhf) 469

17.1 The HLA Module 469

17.2 Classification Macros 469

17.3 String to Integer Macros 470

17.4 Label Generation Macro 470

17.5 Procedure Overloading Macro 470

17.6 Generic PUT Macro 473

17.7 @class Constants 478

17.8 HLA pType Constants 479

17.9 @pclass Return Values 481

17.10 @section Return Values 482

18 The Linux Module (linux.hhf) 485

18.1 The Linux Module 485

18.2 The Linux Header File 485

19 Lists Module (lists.hhf) 487

19.1 The Lists Module 487

19.1.0.1 List Data Types 487

19.2 List_t Class Function Types 489

19.3 Creating New List Class Types 490

19.4 List Procedures, Methods, and Iterators 491

19.5 List Constructor and Destructor 492

19.6 Accessor Functions 493

19.6.0.1 Adding Nodes to a List 493

19.7 Removing Nodes From a List 495

19.8 Accessing Nodes in a List 497

19.9 Miscellaneous List Functions 499

20 Math Module (math.hhf) 503

20.1 The Math Module 503

20.2 Math Data Types 503

20.3 64-Bit Arithmetic and Logical Operations 503

20.4 128-Bit Arithmetic and Logical Operations 511

20.5 Transcendental, Logarithmic, and Other Floating-Point Operations 520

21 Memory-Mapped I/O (mmap.hhf) 547

21.1 MMAP Module 547

21.2 Class Fields 547

21.3 Class Procedures and Methods 547

22 Memory (memory.hhf) 551

22.1 Memory Module 551

22.2 Deprecated Names 551

22.3 Generic Memory Allocation 551

22.4 String Memory Allocation 558

23 OS Module (os.hhf) 561

23.1 The OS Module 561

23.2 Executing Shell Commands 561

23.3 Delaying Program Execution 562

24 Patterns Module (patterns.hhf) 563

24.1 The Patterns Module 563

24.2 An Introduction to Pattern Matching (a tutorial) 563

24.3 Pattern Matching Functions Versus User Code 567

24.4 Register and Stack Usage in Pattern Matching Statements 568

24.5 Nesting Pattern Matching Statements 570

24.6 Cleanly Nesting Patterns 574

24.7 Backtracking 576

24.8 Pattern Components 578

24.9 Lazy / Eager Evaluation and Pattern Matching Performance 579

24.10 Regular Expressions 580

24.11 Pattern Matching Statements 585

24.12 Alternation 586

24.13 Pattern Matching Macros 587

24.14 Character Set Matching Functions 590

24.15 Character Matching Functions 595

24.16 Case Insensitive Character Matching Routines 600

24.17 String Extraction Functions 606

24.18 Whitespace and End of String Matching Functions 607

24.19 Matching an Arbitrary Sequence of Characters 608

24.20 Writing Your Own Pattern Matching Routines 609

25 Random Number Generator Module (rand.hhf) 621

25.1 The Random Module 621

25.2 The Random Number Generators 621

26 Sockets Module (sockets.hhf) 625

26.1 The SOCK Module 625

26.2 Socket Initialization and Cleanup 625

26.3 Generic Socket Functions 625

26.4 Low-Level BSD-Style Socket Functions 626

26.5 Socket Classes 634

26.6 A Quick Note 635

26.7 Client/Server Applications Using the Socket Classes 635

26.8 A Simple Server Application 635

26.9 A Simple Client Application 638

26.10 Client/Server Communication 638

26.11 General Socket Class Operations 639

26.12 Miscellaneous Output 642

26.13 Character, Character Set, and String Output 643

26.14 Hexadecimal Numeric Output 645

26.15 Signed Integer Numeric Output 649

26.16 Unsigned Integer Numeric Output 652

26.17 Floating-Point Numeric Output Using Scientific Notation 654

26.18 Floating-Point Numeric Output Using Decimal Notation 656

26.19 Generic File Output 657

26.20 Generic File Input 658

26.21 Character and String Input 659

26.22 Signed Integer Input 660

26.23 Unsigned Integer Input 661

26.24 Hexadecimal Input 663

26.25 Floating-Point Input 664

26.26 Generic File Input 665

27 The Standard Error Module (stderr.hhf) 667

27.1 Conversion Format Control 667

27.2 File I/O Routines and the Standard Error Handle 667

27.3 Standard Error Routines 667

27.4 Miscellaneous Output Routines 668

27.5 Boolean Output 669

27.6 Character, String, and Character Set Output Routines 670

27.7 Hexadecimal Output Routines 676

27.8 Signed Integer Output Routines 698

27.9 Unsigned Integer Output Routines 711

27.10 Floating Point Output Routines 725

27.10.1 Real Output Using Scientific Notation 725

27.10.2 Real Output Using Decimal Notation 728

27.11 Generic Error Output Routine 733

28 The Standard Input Module (stdin.hhf) 735

28.1 Conversion Format Control 735

28.2 File I/O Routines and the Standard Output Handle 735

28.3 Standard Input Routines 735

28.4 General Standard Input Routines 736

28.5 Character and String Input Routines 738

28.6 Hexadecimal Input Routines 739

28.7 Signed Integer Input Routines 742

28.8 Unsigned Integer Input Routines 744

28.9 Floating Point Input 747

28.10 Generic File Input 747

29 The Standard Output Module (stdout.hhf) 749

29.1 Conversion Format Control 749

29.2 File I/O Routines and the Standard Output Handle 749

29.3 Standard Output Routines 749

29.4 Miscellaneous Output Routines 750

29.5 Boolean Output 751

29.6 Character, String, and Character Set Output Routines 752

29.7 Hexadecimal Output Routines 759

29.8 Signed Integer Output Routines 781

29.9 Unsigned Integer Output Routines 794

29.10 Floating Point Output Routines 807

29.10.1 Real Output Using Scientific Notation 807

29.10.2 Real Output Using Decimal Notation 810

29.11 Generic Standard Output Routine 815

30 The HLA Standard Template Library 817

30.1 Introduction to the HLA STL 817

30.2 Type Declarations Created by a Template 818

30.3 Template Objects are Classes 818

30.4 Class Traits 819

30.4.1 isSTL_c Trait 819

30.4.2 Compile-Time Traits 820

30.4.3 Run-Time Traits 820

30.4.4 Trait Constants 821

30.4.4.1 stl.isContainer_c Trait 821

30.4.4.2 stl.isRandomAccess_c Trait 822

30.4.4.3 stl.isArray_c Trait 822

30.4.4.4 stl.isVector_c Trait 822

30.4.4.5 stl.isDeque_c Trait 822

30.4.4.6 stl.isList_c Trait 822

30.4.4.7 stl.isTable_c Trait 822

30.4.4.8 stl.supportsOutput_c Trait 822

30.4.4.9 stl.supportsCompare_c Trait 822

30.4.4.10 stl.supportsInsert_c Trait 822

30.4.4.11 stl.supportsRemove_c Trait 822

30.4.4.12 stl.supportsAppend_c Trait 822

30.4.4.13 stl.supportsPrepend_c Trait 822

30.4.4.14 stl.supportsForEach_c and supportsrForeach_c Traits 823

30.4.4.15 stl.supportsCursor_c Trait 823

30.4.4.16 stl.supportsSearch_c Trait 823

30.4.4.17 stl.supportsElementSwap_c Trait 823

30.4.4.18 stl.supportsObjSwap_c Trait 823

30.4.4.19 stl.elementsAreObjects_c Trait 823

30.4.4.20 stl.fastInsert_c Trait 823

30.4.4.21 stl.fastRemove_c Trait 823

30.4.4.22 stl.fastAppend_c Trait 823

30.4.4.23 stl.fastPrepend_c Trait 823

30.4.4.24 stl.fastSwap_c Trait 824

30.4.4.25 stl.fastSearch_c Trait 824

30.4.4.26 stl.fastElementSwap_c Trait 824

30.4.5 Other Run-Time Traits 824

30.5 The Vector Template 824

30.6 The Deque Template 824

30.7 The List Template 824

30.8 The Table Template 824

31 The Strings Module (strings.hhf) 825

31.1 The HLA String Data Type 825

31.2 String Allocation Macros and Functions 827

31.3 String Length Calculations 828

31.4 String Assignment 828

31.5 Substring Functions 830

31.6 String Insertion and Deletion Functions 836

31.7 String Comparison Functions 844

31.8 String Searching Functions 850

31.9 Character Set Searching Functions 863

31.10 String Parsing Functions 869

31.11 String Formatting Functions 885

31.12 String Conversion Functions 897

31.13 String Concatentation Functions 904

31.14 String Value Concatentation Functions 911

31.14.1 Boolean Output 911

31.14.2 Character, String, and Character Set Concatenation Routines 912

31.14.3 Hexadecimal Concatenation Routines 917

31.14.4 Signed Integer Concatenation Routines 932

31.14.5 Unsigned Integer Concatenation Routines 942

31.15 Floating-Point Concatenation Routines 952

31.15.1 Real to String Output Using Scientific Notation 953

31.15.2 Real To String Output Using Decimal Notation 955

31.16 Generic String Format Output Routine 959

32 High-Level Language Module (hll.hhf) 961

32.1 The HLL Module 961

32.2 The switch/case/default/endswitch Macro 961

33 Tables Module (tables.hhf) 963

33.1 The Tables Module 963

33.2 The Table Class 963

34 Threads Module (threads.hhf) 967

34.1 Threads Module 967

34.2 Thread Creation 967

34.3 Thread Identification 968

34.4 Thread Local Storage 969

34.5 Events 971

34.6 Critical Sections 972

34.7 Semaphores 974

35 Time Functions (datetime.hhf) 977

35.1 Time Module 977

35.2 Time Data Types 977

35.3 Time Predicates 978

35.4 Time Conversions 980

35.5 Time Arithmetic 983

35.6 Reading the Current System Time 986

35.7 Time String Conversions and Output 987

35.8 Time Class Types 988

35.9 Time Class Methods/Procedures 988

35.10 Creating New Time Class Types 989

35.11 Time Class Functions 993

36 Timer Class and Module (timer.hhf) 997

36.1 Timer Module 997

36.2 Timer Class/Data Structure 997

36.3 Timer Operation 998

36.4 Timer Class Fields 998

36.5 Timer Procedures and Methods 998

37 Zero-terminated String Functions (zstring.hhf) 1001

37.1 ZStrings Module 1002

37.2 Zstring Functions 1002

38 HOWL: The HLA Object Windows Library 1007

38.1 The HOWL Application Framework 1007

38.2 The HOWL Declarative Language 1017

38.2.1 wForm 1018

38.2.2 wMainMenu..endwMainMenu 1019

38.2.2.1 wMenuItem 1020

38.2.2.2 wMenuSeparator 1020

38.2.2.3 wSubMenu..endwSubMenu 1020

38.2.2.4 Menu Example 1020

38.2.3 wTab 1021

38.2.4 Check Boxes 1022

38.2.4.1 wCheckBox 1023

38.2.4.2 wCheckBox3 1023

38.2.4.3 wCheckBox3LT 1023

38.2.4.4 wCheckBoxLT 1023

38.2.5 wComboBox 1024

38.2.6 wDragListBox 1024

38.2.7 wEditBox 1025

38.2.8 wEllipse 1026

38.2.9 wIcon 1027

38.2.10 wGroupBox..endwGroupBox 1028

38.2.11 wLabel 1028

38.2.12 wListBox 1029

38.2.13 wPasswdBox 1029

38.2.14 wPie 1030

38.2.15 wPolygon 1030

38.2.16 wBitmap 1031

38.2.17 wProgressBar 1032

38.2.18 wPushButton 1032

38.2.19 Radio Button Objects 1032

38.2.19.1 wRadioButton 1032

38.2.19.2 wRadioButtonLT 1033

38.2.19.3 wRadioSet..endwRadioSet 1033

38.2.19.3.1 wRadioSetButton 1034

38.2.19.3.2 wRadioSetButtonLT 1034

38.2.20 wRectangle 1035

38.2.21 wRoundRect 1035

38.2.22 wScrollBar 1035

38.2.23 wTextEdit 1036

38.2.24 wTrackBar 1037

38.2.25 wUpDown 1038

38.2.26 wUpDownEditBox 1039

38.2.27 wTimer 1040

38.2.28 wWindow..endwWindow 1040

38.3 The HOWL Run-time Library 1041

38.3.1 Private Data Fields 1043

38.3.2 Abstract Classes 1043

38.3.2.1 wBase_t 1043

38.3.2.2 wVisual_t 1046

38.3.2.3 wClickable_t 1049

38.3.2.4 wButton_t 1050

38.3.2.5 wCheckable_t 1052

38.3.2.6 wSurface_t 1053

38.3.2.7 wFilledFrame_t 1054

38.3.2.8 wabsEditBox_t 1055

38.3.2.9 wContainer_t 1059

38.3.3 Containers 1061

38.3.3.1 Forms and Menus 1061

38.3.3.1.1 wForm_t 1061

38.3.3.1.2 wMenu_t 1063

38.3.3.1.3 wMenuItem_t 1063

38.3.3.2 Tabbed Forms 1065

38.3.3.2.1 wTabs_t 1065

38.3.3.3 wGroupBox_t 1067

38.3.4 Graphic Objects 1068

38.3.4.1 wBitmap_t 1068

38.3.4.2 wEllipse_t 1070

38.3.4.3 wPie_t 1071

38.3.4.4 wPolygon_t 1073

38.3.4.5 wRectangle_t 1075

38.3.4.6 wRoundRect_t 1076

38.3.5 Buttons 1077

38.3.6 wCheckBox_t 1077

38.3.7 wCheckBox3_t 1078

38.3.8 wCheckBox3LT_t 1079

38.3.9 wCheckBoxLT_t 1080

38.3.10 wPushButton_t 1080

38.3.11 wRadioButton_t 1081

38.3.12 wRadioButtonLT_t 1082

38.3.13 wRadioSet_t 1083

38.3.13.1 wRadioSetButton_t 1084

38.3.13.2 wRadioSetButtonLT_t 1085

38.3.14 Editors and Edit Boxes 1086

38.3.14.1 wEditBox_t 1086

38.3.14.2 wPasswdBox_t 1087

38.3.14.3 wTextEdit_t 1088

38.3.15 List, Drag, and Combo Boxes 1090

38.3.15.1 wListBox_t 1090

38.3.15.2 wDragListBox_t 1093

38.3.15.3 wComboBox_t 1094

38.3.16 Progress Bars 1096

38.3.16.1 wProgressBar_t 1096

38.3.17 Scroll Bars and Track Bars 1097

38.3.17.1 wScrollBar_t 1097

38.3.17.2 wTrackBar_t 1103

38.3.18 Up/Down Arrows 1106

38.3.18.1 wUpDown_t 1106

38.3.18.2 wUpDownEditBox_t 1108

38.3.19 Icons 1111

38.3.19.1 wIcon_t 1111

38.3.20 Text 1112

38.3.20.1 wFont_t 1112

38.3.20.2 wLabel_t 1114

38.3.21 Views, Windows, and Tab Pages 1116

38.3.21.1 wTabPage_t 1116

38.3.21.2 wView_t 1119

38.3.21.3 window_t 1119

38.3.22 Timers 1121

38.3.22.1 wTimer_t 1121

Passing Parameters to Standard Library Routines

Parameter Passing

Standard library functions typically compute some value based on a set of input values. Understanding how to pass these parameter values to the standard library routines is important if you want to make efficient calls to the library routines.

Most standard library functions expect their parameters in one of two locations – in one or more registers or on the stack (or both). Generally, standard library functions preserve all the general-purpose register values across a call, with the exception of those functions that explicitly return a value in a register (or multiple registers, if needed). Therefore, the standard library functions are careful about passing values to a function in a register because such semantics might require the caller to use a register to hold a parameter value that it is already using for a different purpose. However, if a function returns some result in a register, then the standard library routines assume that the register’s contents are fair game on input and may specify the use of that register as an input parameter. In almost every other case, the standard library routines expect the caller to pass parameters on the stack, though there are a few exceptions to this rule.

In most cases, the standard library routines only use a register to pass a parameter when there is exactly one input parameter and one function return result. Typically, the functions will use the EAX register as both the input and output value. In a few rare cases, a function may have two or more parameters with one parameter being passed in a register and the remaining parameters being passed on the stack.

If you are using a high-level calling syntax, you should take care when calling routines that pass multiple parameters in registers. Consider the conv.bToBuf (byte/hex string conversion to buffer) function:

procedure conv.bToBuf( b:byte in al; var buffer:var in edi );

HLA will automatically emit code that loads the registers when you call this function. E.g., the following

 

conv.bToBuf( b, myBuffer );

will emit the following code:

mov( b, al );

lea( edi, buffer );

call conv.bToBuf;

Suppose, however, that you write code like the following:

conv.bToBuf( b, [eax] ); // EAX points at the buffer

In this case, HLA will generate the following code, which will probably not do what you want:

mov( bl, al ); // Load b parameter into AL, as before

mov( eax, edi ); // EAX’s value was munged by the instruction above

call conv.bToBuf;

While the problem is obvious when writing low-level code, the high-level invocation hides the problem. This is one drawback to using a high-level invocation of the library code: it tends to hide what’s going on and problems like this, though they are very rare, are more easily spotted using low-level code. In defense of the high-level invocation style, it catches far more common errors than it misses, so this isn’t sufficient reason to avoid using high-level invocations.

The correct solution, by the way, is to always be aware of where the standard library routines pass their parameters. When the standard library passes a given parameter in a register, you should attempt to have that value sitting in the register when you call the function. This is safest and generates the most efficient code. For example, consider the following call to the conv.btoBuf library routine:

conv.bToBuf( al, [edi] );

Because the parameter data is already sitting in the registers where conv.bToBuf expects them, this generates the following (very efficient) code:

call bToBuf;

Passing Parameters by Reference and by Value

The standard library routines generally employ one of two different parameter passing mechanisms – pass by value and pass by reference. Pass by value, as its name implies, passes the value of a parameter to a function. For small objects (say, less than 16 bytes or so), pass by value is very efficient. For large objects (e.g., arrays or records larger than 16 bytes or so) the caller must make a copy of the data when passing it to a subroutine, this data copy operation can be very slow if the data object is large. Therefore, the standard library does not use pass by value when passing arrays, records, or other large data structures to a library routine.

Note that the decision to use pass by reference or pass by value is entirely up to the routine’s designer. If you’re calling a standard library routine you must use the same parameter passing mechanism the designer used when writing the function. The function’s documentation will tell you whether a parameter is passed by reference or by value (or you can read the HLA prototype for a function which will also tell you the parameter passing mechanism).

Pass by reference parameters pass the 32-bit address of the actual parameter to their corresponding function. Because an address is always 32 bits, regardless of the actual parameter data size, passing a large parameter by reference can be far more efficient than passing that same parameter by value. Another benefit (or drawback, in certain cases) to using pass by reference parameters is that the function can modify the value of the actual parameter object because it has the memory address of that object. For more details on the semantics of pass by reference versus pass by value, check out the chapter on procedures and functions in The Art of Assembly Language.

When passing a parameter by reference to a function, you must first compute the address of that object and pass the address to the function. For example, consider the following function prototype:

procedure someFunction( var refParm:byte );

(for those unfamiliar with HLA syntax, the "var" prefix tells HLA that refParm is a pass by reference parameter.) To call someFunction, you must push the address of the actual parameter onto the stack. If the parameter is a static object (e.g., an HLA STATIC, READONLY, or STORAGE variable), then you can compute the address using the HLA "&" (address-of operator), thusly:

pushd( &actualByteParameter );

call someFunction;

If the actual parameter is an automatic variable, or you reference it using some complex addressing mode (other than displacement-only), then you’d use code like the following to pass actualByteParameter to someFunction:

lea( eax, actualByteParameter );

push( eax );

call someFunction;

Note that this scheme, unfortunately, makes use of a general-purpose 32-bit register.

Of course, you can use the HLA high-level function invocation syntax to automatically generate the calling sequence for you:

someFunction( actualByteParameter );

HLA is smart enough to determine the storage class of the actualByteParameter object and generate appropriate code to pass that parameter’s address on the stack. Note that, unlike the example given earlier, HLA does not wipe out the value in the EAX register if it needs to compute the parameter’s address with an LEA instruction; HLA will always preserve all register values unless you explicitly state that you’re passing the parameter in a 32-bit register. For simple local variables or other variables allocated on the stack (e.g., parameters passed into the current function), HLA will actually generate code like the following:

push( ebp ); // Assumes EBP points at the current stack frame

add( @offset( actualByteParameter ), (type dword [esp]));

This pushes the address of a local variable or parameter onto the stack without affecting any general-purpose register values (other than ESP, which we expect).

Note that registers do not have addresses. Therefore, you cannot pass a register by reference to a function; i.e., the following is always illegal:

someFunction( edi );

Of course, what most programmer’s mean when they attempt something like this is that EDI contains the address of the variable to pass to the function. However, HLA assumes that you’re trying to take the address of EDI, which is always illegal. The quick and dirty way to handle this issue is to explicitly tell HLA that EDI is a pointer to the data using an invocation like this:

someFunction( [edi] );

This approach works great when the 32-bit address appears in a register. In the more general case, the 32- bit value could appear in any 32-bit object (e.g., in a pointer variable). You can use HLA’s VAL prefix to explicitly tell HLA to treat a 32-bit value as an address to be passed as a reference parameter, e.g.,

someFunction( val edi );

someFunction( val dwordVar );

HLA is actually smart enough to recognize certain special cases where you’re trying to pass the contents of a pointer variable as a reference parameter. Consider the following HLA code fragment:

static

ptrVar :pointer to byte := &someStaticByteVar;

.

.

.

someFunction( ptrVar );

HLA realizes that ptrVar is a pointer to a byte object and will automatically push ptrVar’s value onto the stack rather than generating an error. No explicit VAL will be necessary. Note that the base type of the pointer variable must match the reference parameter’s type for this call to be successful.

HLA supports a special "untyped pass by reference parameter" syntax. The following example demonstrates this:

procedure untypedRefParm( var anyMemoryType: var );

Note that the parameter does not have an explicit type. The keyword "var" tells the compiler that the parameter is an untyped reference parameter and any memory variable can be passed to this function.

If you pass a variable name as an actual parameter for an untyped reference parameter, HLA will always take the address of that object, regardless of it’s type. If you want to pass the value of a pointer variable, rather than the address of that pointer variable, then you must explicitly supply the VAL prefix, e.g.,

untypedRefParm( val ptrVal );

HLA string objects are hybrid pointer/value objects. An HLA string variable is a dword object that contains a pointer to the actual string data. Consider, though, the following HLA procedure declaration:

procedure someStrFunction( s:string );

This function has a single pass-by-value parameter. It might seem confusing that this is a pass-by-value object as it passes the address of the actual character data that makes up the string to the function. However, keep in mind that a string object is a pointer and what you’re really passing by value is the value of that pointer variable. If you were to pass a string object by reference, what you would be passing is the address of the string variable (that is, the address of the address of the actual character data). For this reason, if a 32-bit register contains the value of a string variable (that is, the register contains the address of the actual character data), then the following call to someStrFunction is perfectly legitimate:

someStrFunction( eax );

Passing Byte Parameters on the Stack

For efficiency reasons, standard library routines always pass all parameters as a multiple of four bytes. When passing a byte-sized parameter on the stack by value, the actual parameter value consumes the L.O. byte of the double word passed on the stack. The function ignores the H.O. three bytes of the value passed for this parameter, though by convention (to make debugging a little easier) you should try to pass zeros in the H.O. three bytes if it is not inconvenient to do so.

When passing a byte-sized constant, you should simply push the dword containing the 8-bit value, e.g,

pushd( 5 );

call someLibraryRoutine;

When passing the 8-bit value of the 8-bit registers AL, BL, CL or DL onto the stack, you should simply push the 32-bit register that holds the 8-bit register, e.g.,

push( eax ); // Pushes AL onto the stack

call someLibraryRoutine;

push( ebx ); // Pushes BL onto the stack

call someOtherLibraryRoutine;

Note that this trick does not apply to the AH, BH, CH, or DH registers. The best code to use when you need to push these registers is to drop the stack down by four bytes and then move the desired register into the memory location you’ve just created on the stack, e.g.,

sub( 4, esp );

mov( AH, [esp] ); // Pushes AH onto the stack

call someLibraryRoutine;

sub( 4, esp );

mov( BH, [esp] ); // Pushes BH onto the stack

call anotherLibraryRoutine;

Here’s another way you can accomplish this (a little slower, but leaves zeros in the H.O. three bytes):

pushd( 0 );

mov( CH, [esp] ); // Pushes CH onto the stack

call someLibraryRoutine;

When passing a byte-sized variable, you should try to push the variable’s value and the following three bytes, using code like the following (HLA syntax):

pushd( (type dword eightBitVar ));

call someLibraryroutine;

There is one drawback to the approach above. In three very rare cases the code above could cause a segmentation fault. If the 8-bit variable is located on the last three bytes of a page in memory (4,096 bytes) and the next memory page is not readable, the system will generate a fault if you attempt to push all four bytes. In such a case, the next best solution, if a register is available, is to move the 8-bit value into AL, BL, CL, or DL and push the corresponding 32-bit register. If no registers are available, then you can write code like the following:

push( eax );

push( eax );

mov( byteVar, al );

mov( al, [esp+4] );

pop( eax );

call someLibraryRoutine;


This code is ugly and slightly inefficient, but it will always work (assuming, of course, you don’t get a stack overflow).

The HLA compiler will generate code similar to this last example if you pass a byte variable as the actual parameter to a library function expecting an 8-bit value parameter:

someLibraryRoutine( byteVar );

Therefore, if efficiency is a concern to you, you should try to load the byte variable (byteVar in this example) into AL, BL, CL, or DL prior to calling someLibraryRoutine, e.g.,

mov( boolVar, al );

someLibraryRoutine( al );

Another solution, if you want to use an HLA high-level-like calling sequence, is to use a hybrid calling sequence and explicitly specify the instruction(s) to use to pass a byte-sized parameter on the stack. For example,

someLibraryRoutine( #{ push( (type dword boolVar) ); }# );

Unfortunately, you lose the benefit of type checking and other semantic checks the compiler would normally do for you when using this hyrbrid syntax. Nevertheless, this scheme does have the advantage of encapsulating the parameter pushing code into a single sequence, so that it’s obvious which instruction(s) go with the particular parameter. This is not the case when you manually push the parameters onto the stack as in the earlier examples.

Passing Word Parameters on the Stack

For efficiency reasons, standard library routines always pass all parameters as a multiple of four bytes. When passing a word-sized parameter on the stack by value, the actual parameter value consumes the L.O. two bytes of the double word passed on the stack. The function ignores the H.O. word of the value passed for this parameter, though by convention (to make debugging a little easier) you should try to pass zeros in the H.O. word if it is not inconvenient to do so.

When passing a word-sized constant, you should simply push the double word containing the 16-bit value, e.g,

pushd( 5 );

call someLibraryRoutine;

When passing the 16-bit value of a 16-bit register (AX, BX, CX, DX, SI, DI, BP, or SP) onto the stack, you should simply push the 32-bit register that holds the 16-bit register, e.g.,

push( eax ); // Pushes AX onto the stack

call someLibraryRoutine;

push( ebx ); // Pushes BX onto the stack

call someOtherLibraryRoutine;

When passing a word-sized variable, you should try to push the variable’s value and the following two bytes, using code like the following (HLA syntax):

pushd( (type dword sixteenBitVar ));

call someLibraryroutine;

There is one drawback to the approach above. In three very rare cases the code above could cause a segmentation fault. If the 16-bit variable is located on the last three bytes of a page in memory (4,096 bytes) and the next memory page is not readable, the system will generate a fault if you attempt to push all four bytes. In such a case, the next best solution, is to use two consecutive pushes:

pushw( 0 ); // H.O. word is zeros

push( sixteenBitVar );

call someLibraryRoutine;


The HLA compiler will generate code similar to this last example if you pass a word variable as the actual parameter to a library function expecting a 16-bit value parameter:

someLibraryRoutine( wordVar );

Passing DWord Parameters on the Stack

Because 32-bit dword objects are the native x86 data type, there are only a few issues with passing 32-bit parameters on the stack to a standard library routine.

First of all, and this applies to all stack operations not just 32-bit pushes and pops, you should always keep the stack 32-bit aligned. That is, the value in ESP should always contain a value that is a multiple of four (i.e., the L.O. two bits of ESP must always contain zeros). If this is not the case, many standard library routines will fail.

When passing a 32-bit value onto the stack, just about any mechanism you can use to push that value is perfectly valid. You can efficiently push constants, registers, and memory locations using a single push instruction, e.g.,

pushd( 12345 ); // Passing a 32-bit constant

push( mem32 ); // Passing a dword variable

push( eax ); // Passing a 32-bit register

call someLibraryRoutine;

One type of double word parameter deserves special mention – a reference parameter. Reference parameters pass the address of their object on the stack rather than the value of the object (that is, they pass a pointer to the actual object). HLA actually supports several different reference parameter types including pass by reference (VAR) parameters, pass by result (RESULT) parameters, and pass by value/returned (VALRES) parameters. Of these three parameter types, the standard library only uses the VAR (pass by reference) type, though if you ever see a function that uses one of these other parameter mechanisms the calling sequence is exactly the same.

When passing a parameter by reference, you must push the address of the actual parameter (rather than its value) onto the stack. For static objects, you can use the push immediate instruction, e.g., (in HLA syntax):

pushd( &staticVar );

call someLibraryRoutine;

For automatic variables, or objects whose address is not a simple static offset (e.g., a complex pointer address involving registers and what-not), you’ll have to use the LEA instruction to first compute the address and then push that register’s value, e.g.,

lea( eax, anAutomaticVar ); // Variable allocated on the stack

push( eax );

call someLibraryRoutine;

If the variable’s address is a simple offset from a single register (such as automatic variables declared in the stack frame and referenced off of the EBP register), you can push the address of the variable by pushing the base register and adding the offset of that variable to the value left on the stack, thusly:

push( ebp ); // anAutoVar is found at EPB+@offset(anAutoVar)

add( @offset( anAutoVar ), (type dword [esp]));

call someLibraryRoutine;

If the address you want to pass in a reference parameter is a complex address, you’ll have to use the LEA instruction to compute that address and push it onto the stack. This, unfortunately, requires a free 32-bit register. If no 32-bit registers are free, you can use code like the following to achieve this:

sub( 4, esp ); // Reserve space for parameter on stack

push( eax ); // Preserve EAX

lea( eax, [ebp+@offset(autoVar)][ecx*4+3] );

mov( eax, [esp+4] ); // Store in parameter location

pop( eax ); // Restore EAX

call someLibraryRoutine;

Of course, it’s much nicer to use the HLA high-level syntax for calls like this as the HLA compiler will automatically handle all the messy code generation details for you.

Passing QWord Parameters on the Stack

Because qword (64-bit) objects are a multiple of 32 bits in size, manually passing qword objects on the stack is very easy. All you need do is push two dword values. Because the stack grows downward in memory and the x86 is a little endian machine, you must push the H.O. dword first and the L.O. dword second.

If the qword value is held in a register pair, then push the register containing the H.O. dword first and the L.O. dword second. For example, if EDX:EAX contains the 64-bit value, then you’d push the qword as follows:

push( edx ); // Push H.O. dword

push( eax ); // Push L.O. dword

call someLibraryRoutine;

If the qword value is held in a qword variable, then you must first push the H.O. dword of that variable followed by the L.O. dword, e.g.,

push( (type dword qwordVar[4])); // Push H.O. dword first

push( (type dword qwordVar)); // Push L.O. dword second

call someLibraryRoutine;

If the qword value you wish to pass is a constant, then you’ve got to compute the L.O. and H.O. dword values for that constant and push those. When using HLA, you can use the compile-time computational capabilities of HLA to do this for you, e.g.,

pushd( ((some64bitConst) >> 32);

pushd( ((some64bitConst) & $FFFF_FFFF );

call someLibraryRoutine;

If this is something you do frequently, you might want to create a macro to break up the 64-bit value and push it for you.

Of course, you can always use the HLA high-level syntax to pass a 64-bit object to a standard library routine. HLA automatically generates the appropriate code to pass the qword object as a parameter on the stack.

Passing TByte Parameters on the Stack

For efficiency reasons, standard library routines always pass all parameters as a multiple of four bytes. When passing a tbyte-sized parameter on the stack by value, the actual parameter value consumes the L.O. ten bytes of the three double words passed on the stack. The function ignores the H.O. word of the value passed for this parameter, though by convention (to make debugging a little easier) you should try to pass zeros in the H.O. word if it is not inconvenient to do so.

The following code demonstrates how to pass a ten-byte object to a standard library routine:

pushw( 0 ); // Dummy H.O. word of zero

push( (type word tbyteVar[8])); // Push H.O. byte of tbyte object

push( (type dword tbyteVar[4])); // Push bytes 4-7 of tbyte object

push( (type dword tbyteVar[0])); // Push L.O. dword of tbyte object

call someLibraryRoutine;

If your tbyte object is not at the very end of allocated memory, you could probably combine the first two instructions in this sequence to produce the following (slightly more efficient) code:

push( (type dword tbyteVar[8])); // Pushes two extra bytes.

This pushes the two bytes beyond tbyteVar onto the stack, but presumably the function will ignore all bytes beyond the tenth byte passed on the stack, so the actual values in those H.O. two bytes are irrelevant. Note the earlier discussion (in the section on pushing byte parameters) about the rare possibility of a memory access error when using this trick.

Of course, you can always use the HLA high-level syntax to pass an 80-bit object to a standard library routine. HLA automatically generates the appropriate code to pass the tbyte object as a parameter on the stack.

Passing LWord Parameters on the Stack

Because lword (128-bit) objects are a multiple of 32 bits in size, manually passing lword objects on the stack is very easy. All you need do is push four dword values. Because the stack grows downward in memory and the x86 is a little endian machine, you must push the H.O. dword first and the L.O. dword last.

If the lword value is held in an lword variable, then you must first push the H.O. dword of that variable followed by the lower-order dwords, down to the L.O. dword, e.g.,

push( (type dword qwordVar[12])); // Push H.O. dword first

push( (type dword qwordVar[8])); // Push bytes 8-11 second

push( (type dword qwordVar[4])); // Push bytes 4-7 third

push( (type dword qwordVar)); // Push L.O. dword last

call someLibraryRoutine;

If the lword value you wish to pass is a constant, then you’ve got to compute the four dword values for that constant and push those. When using HLA, you can use the compile-time computational capabilities of HLA to do this for you, e.g.,

pushd( ((some128bitConst) >> 96);

pushd( ((some128bitConst) >> 64 & $FFFF_FFFF );

pushd( ((some128bitConst) >> 32 & $FFFF_FFFF );

pushd( ((some128bitConst) & $FFFF_FFFF );

call someLibraryRoutine;

If this is something you do frequently, you might want to create a macro to break up the 128-bit value and push it for you.

Of course, you can always use the HLA high-level syntax to pass a 128-bit object to a standard library routine. HLA automatically generates the appropriate code to pass the lword object as a parameter on the stack.

Command-Line Arguments (args.hhf)

The HLA args module provides access to, and support for, Windows Command Line Interpreter or Linux/*nix shell command line parameters.

Note: be sure to read the chapter on "Passing Parameters to Standard Library Routines" (parmpassing.rtf) before reading this chapter.

A Note About Thread Safety: The args module maintains a couple of static global variables that maintain the command-line values. Currently, these values apply to all threads in a process. You should take care when changing these values in threads. The command-line is a resource that must be shared amongst all threads in an application. If you write multi-threaded applications, it is your responsibility to serialize access to the command-line functions.

The Args Module

To use the args functions in your application, you will need to include one of the following statements at the beginning of your HLA application:

 

#include( "args.hhf" )

or

#include( "stdlib.hhf" )

Retrieving the Command Line

The arg.cmdLn and arg.a_cmdLn functions retrieve the entire command-line as a string. This string typically consists of the list of command-line parameters, each parameter separated by a single space. Note that on some operating systems, the HLA standard library command-line functions might actually synthesize this string by concatenating the command-line arguments together, so you can not expect this string to be an exact representation of the command line that the user typed (that is, the user may have typed extra spaces or other delimiter characters that the OS’ shell discarded before passing the command line text to the program). Also, the command-line string will not contain I/O redirection or other process-related items (e.g., pipes) found on the command line.

procedure arg.cmdLn();

 

arg.cmdLn returns a pointer to a static string held inside the stdlib code. This function returns the string pointer in EAX. The caller must not modify any data in this string (use arg.a_cmdLn if you need a malleable string).

HLA high-level calling sequence examples:

arg.cmdLn();

stdout.put( "Command line: ", (type string eax), nl );

HLA low-level calling sequence examples:

call arg.cmdLn;

push( eax );

call stdout.puts;

procedure arg.a_cmdLn();

arg.a_cmdLn returns, in EAX, a string containing a copy of the command-line parameter text. This is a pointer to a string allocated on the heap. It is the caller’s responsibility to free this storage (via a call to str.free) when it is done using the string.

HLA high-level calling sequence examples:

arg.a_cmdLn();

stdout.put( "Command line: ", (type string eax), nl );

str.free( eax );

HLA low-level calling sequence examples:

call arg.cmdLn;

push( eax );

call stdout.puts;

push( eax );

call str.free;

Argument Count and Item

The functions in this category are the standard argument functions that most programs use: arg.c, arg.v, and arg.a_v. The arg.c function returns the number of command-line parameters and arg.v/arg.a_v return a pointer to a string that corresponds to an individual parameter.

procedure arg.c();

arg.c returns the number of command-line parameters in EAX. This count includes the program name on the command line.

HLA high-level calling sequence examples:

arg.c();

stdout.put( "Number of arguments: ", (type uns32 eax), nl );

HLA low-level calling sequence examples:

call arg.c;

mov( eax, argCount );

procedure arg.v( whichArg:dword );

arg.v returns a pointer to the string that corresponds to the specified command-line argument. Argument indexes are in the range 0..arg.c()-1. This function will raise an exception if you pass it an argument value outside this range. This function returns a pointer to a string whose storage is internal to the standard library. You must treat this data as read-only data and not modify this data.

HLA high-level calling sequence examples:

arg.c();

mov( eax, edx );

for( mov( 0, ecx ); ecx < edx; inc( ecx )) do

arg.v(ecx);

stdout.put( "arg[", (type uns32 ecx), "]=", (type string eax), nl );

endfor;

HLA low-level calling sequence examples:

pushd( 0 );

call arg.v;

mov( eax, firstArg );

procedure arg.a_v( whichArg:dword );

arg.a_v returns a pointer to the string that corresponds to the specified command-line argument. Argument indexes are in the range 0..arg.c()-1. This function will raise an exception if you pass it an argument value outside this range. This function returns a pointer to a string it allocates on the heap. The caller must free this storage (via a call to str.free) when it is done using the string.

HLA high-level calling sequence examples:

arg.c();

mov( eax, edx );

for( mov( 0, ecx ); ecx < edx; inc( ecx )) do

arg.a_v(ecx);

stdout.put( "arg[", (type uns32 ecx), "]=", (type string eax), nl );

str.free( eax );

endfor;

HLA low-level calling sequence examples:

pushd( 0 );

call arg.a_v;

mov( eax, firstArg );

.

.

.

str.free( firstArg );

Deleting Command Line Arguments

The functions in this category delete command-line arguments from the internal array or deallocate all the command-line arguments from the internal array. When an individual command-line parameter is deleted, the indexes of the following command-line arguments are reduced by one (that is, argument n+1 becomes argument n, argument n+2 becomes argument n+1, etc., up to the number of command-line arguments). Also note that deleting a command-line argument reduces the value that arg.c returns by one.

procedure arg.delete( index:uns32 );

arg.delete removes the specified command-line parameter from the internal argv array. The index parameter must be in the range 0..argc, where argc is the current value that arg.c() returns. If the index parameter is outside this range, this function will raise an ex.BoundsError exception. This function decrements the value that arg.c returns by one. Note that this function does not affect the value that arg.cmdLn returns.

HLA high-level calling sequence examples:

arg.c();

stdout.put( "Number of arguments: ", (type uns32 eax), nl );

arg.delete( 0 );

arg.c();

stdout.put

(

"Number of arguments after delete: ",

(type uns32 eax),

nl

);

HLA low-level calling sequence examples:

pushd( 2 );

call arg.delete;

procedure arg.destroy();

arg.destroy deletes all of the command line parameters from internal storage and resets the command-line processor. The next function that requests a command line parameter value will force the run-time system to regenerate the command-line argument list.

HLA high-level calling sequence examples:

arg.destroy();

arg.c(); // Regenerates original command-line

mov( eax, originalArgc );

HLA low-level calling sequence examples:

call arg.destroy;

call arg.c;

mov( eax, originalArgc );

Argument Iterators

The iterators in this category process command-line arguments within a foreach loop.

iterator arg.args();

arg.args is an iterator (used in an HLA foreach loop) that returns successive command line parameters on each iteration of the foreach loop. This iterator returns a pointer to a newly allocated string in the EAX register. It is the caller’s responsibility (usually in the body of the foreach loop) to free the allocated storage with a call to str.free.

HLA high-level calling sequence examples:

foreach arg.args() do

stdout.put( "current Arg: ", (type string eax), nl );

str.free( eax );

endfor;

HLA low-level calling sequence examples:

(You really should only use the high-level calling sequence

for a foreach loop that calls an iterator.)

iterator arg.globalOptions( options:cset );

arg.globalOptions 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 "-" 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.

Note that this iterator allocates storage for each string it returns on the heap. It is the caller’s responsibility to free the storage when the caller is done using the string.

HLA high-level calling sequence examples:

foreach arg.globalOptions( {‘a’, ‘b’, ‘c’ } ) do

stdout.put( "current option: ", (type string eax), nl );

str.free( eax );

endfor;

HLA low-level calling sequence examples:

(You really should only use the high-level calling

sequence for a foreach loop that calls an iterator.)

iterator arg.localOptions( options:cset );

arg.localOption 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 "-". It fails upon encountering a command line parameter that is not an option (that is, begins with "-"). Note that this iterator only yields those command line options whose first character beyond the "-" character is a member of the options character set.

Typically, you would use the arg.localOption 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.localOption 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).

Note that this iterator allocates storage for each string it returns on the heap. It is the caller’s responsibility to free the storage when the caller is done using the string.

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

HLA high-level calling sequence examples:

foreach arg.localOptions( 4, {‘a’, ‘b’, ‘c’ } ) do

stdout.put( "current option: ", (type string eax), nl );

str.free( eax );

endfor;

HLA low-level calling sequence examples:

(You really should only use the high-level calling

sequence for a foreach loop that calls an iterator.)

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-time.

Note: be sure to read the chapter on "Passing Parameters to Standard Library Routines" (parmpassing.rtf) before reading this chapter.

The Arrays Module

To use the array macros in your application, you will need to include one of the following statements at the beginning of your HLA application:

 

#include( "arrays.hhf" )

or

#include( "stdlib.hhf" )

Array Data Types

The array namespace defines the following useful data types:

#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 data type; 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 dynamically.

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.

 

Array Allocation and Deallocation

#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;

#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.

Example:

// Allocate storage for the array:

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

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

array.daFree( MyArray );

Array Predicates

#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.

Array Element Access

#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 ); // EAX := arrayS[I,j,k];

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

mov( eax, [ebx] ); // EAX := arrayD[m,n,p];

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

#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 arity 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

Lookup Tables

The array.lookupTable macro lets you easily construct a standard lookup table. This macro invocation must appear within a STATIC or a READONLY declaration section in your program. A lookup table declaration takes the following form:

readonly

tableName:

array.lookupTable

(

element_data_type,

default_table_value,

value: list_of_indexes,

value: list_of_indexes,

.

.

.

value: list_of_indexes,

value: list_of_indexes

);

where:

element_data_type is the data type for each element of the array, for example, byte.

default_table_value is a value to use for "holes" in the table for which you do not supply an explicit index/value.

value is some value that you want to use to initialize a sequence of one or more table entries with.

list_of_indexes is a list of values that specify indexes into the lookup table. Each entry in a specific list is separated from the other entries with a space (not commas!). Note that each index value you specify must be unique across all lists of indexes in this table (that is, you cannot put two values into the array element specified by a single index). The array.lookupTable macro will report an error if you specify a non-unique index value in one of the lists.

Here is a concrete example:

 

static

tableName:

array.lookupTable

(

int32,

-1,

0: 1 2 3 4,

1: 5 6 7 8,

2: 9 10 11 12,

3: 13 14 15,

4: 20 22 24,

5: 16 21 25,

6: 23 19 18

);

This declaration creates a table with 25 dword entries, that will hold the values 1..25. The table will be initialized as follows:

tableName:int32[25] :=

[

0, 0, 0, 0, // Elements 0..3

1, 1, 1, 1, // Elements 4..7

2, 2, 2, 2, // Elements 8..11

3, 3, 3, // Elements 12..14

5, // Element 15

-1, // Element 16 (no index 17 specified above)

6, 6, // Elements 17 & 18

4, // Element 19

5, // Element 20

4, // Element 21

6, // Element 22

4, // Element 23

5 // Element 24

];

The number of elements appearing in the lookup table will be the difference between the largest index value you supply in all the lists (25 in this example) and the smallest value (1 in this example) plus one. This particular lookup table has 25 entries because (25-1+1) = 25.

Note that each line in the example above specifies the value to store into each of the table entries in the list that immediately follows. This is probably backwards to what your intution would suggest. But the nice thing about this arrangement is that it lets you specify a single value to be placed into several different array indices. If there are any gaps in the array indexes you specify (as the value 17 is missing above), then the array.lookupTable macro will fill in those entries with the default value specified as the second parameter.

In order to access this lookup table at run-time, you must know the minimum index into the array so you can subtract this from the calculated index you use to access the table. The array.lookupTable macro generates four constants for you to help you do this (and other things):

tableName_maxValue

tableName_minValue

tableName_maxIndex

tablename_maxIndex

The tableName_minValue and tableName_maxValue constants specify the minimum and maximum index values for the table. In the current example, these constants would be 1 and 25, respectively. The tableName_minIndex and tableName_maxIndex values are the product of the array element’s size with the _minValue and _maxValue constants. In the table above, the element size is four, so tableName_minIndex will be four and tableName_maxIndex will be 100. Whenever you access an element of the tableName array (in this example), you’ll want to subtract the tableName_minIndex value from your computed index in order to adjust for non-zero starting indexes, e.g.,

mov( someIndex, ebx );

mov( tableName[ebx*4 - tableName_minIndex], eax );

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.

A Note About Thread Safety: The routines in this module are all thread safe.

Note about stack diagrams: this documentation includes stack diagrams for those functions that pass parameters on the stack. To conserve space, this documentation does not include a stack diagram for any function that does not pass data on the stack (that is, only a return address appears on the stack).

Bit Module

To call functions in the Bits module, you must include one of the following statements in your HLA application:

 

#include( "bits.hhf" )

or

#include( "stdlib.hhf" )

 

Note: be sure to read the chapter on "Passing Parameters to Standard Library Routines" (parmpassing.rtf) before reading this chapter.

Bit Counting Function

bits.cnt( b:dword in eax ); @returns( "EAX" );

This procedure returns the number of one bits present in the "b" parameter (passed in the EAX register). 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 an 8-bit or 16-bit operand, simply zero extend it to 32 bits prior to calling this function. Here are a couple of examples:

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 .

HLA high-level calling sequence examples:

bits.cnt( mem32 );

mov( eax, count );

mov( bits.cnt( ebx ), count ); // Note: count is in EAX

// Count the number of bits in 8-bit and 16-bit operands:

bits.cnt( movzx( al, eax ) );

mov( eax, count8 );

mov( bits.cnt( movzx( ax, eax )), count16 ); // Count is left in EAX.

HLA low-level calling sequence examples:

mov( mem32, eax );

call bits.cnt;

mov( eax, count );

mov( ebx, eax );

call bits.cnt;

mov( eax, count );

movzx( al, eax );

call bits.cnt;

mov( eax, count8 );

movzx( ax, eax );

call bits.cnt;

mov( eax, count16 );

Bit Reversal Functions

The functions in this category swap the bits in their input operand. That is, they exchange the H.O. and L.O. bits, the next-to-high-order bit with bit #1, and so on.

 

bits.reverse32( b:dword in eax ); @returns( "eax" );

This function reverses the bits passed to it in EAX and returns the swapped value in EAX. This function swaps bit 31 and 0, bits 30 and 1, bits 29 and 2, bits 28 and 3, and so on. See the diagram for bits.reverse8 and generalize that diagram to 32 bits for a pictorial example.

HLA high-level calling sequence examples:

bits.reverse32( mem32 );

mov( eax, reversed32 );

mov( bits.reverse32( ebx ), ebxReversed ); // Note: result is in EAX

HLA low-level calling sequence examples:

mov( mem32, eax );

call bits.reverse32;

mov( eax, reversed32 );

mov( ebx, eax );

call bits.reverse32;

mov( eax, ebxReversed );

bits.reverse16( b:word in ax ); @returns( "eax" );

This function reverses the bits passed to it in AX and returns the swapped value in AX. This function swaps bit 15 and 0, bits 14 and 1, bits 13 and 2, bits 12 and 3, and so on. Note that this function does not zero or sign-extend the result into EAX. Although this function currently preserves the H.O. word of EAX, it’s safer to assume that the H.O. word of EAX contains an undefined value upon return. See the diagram for bits.reverse8 and generalize that diagram to 16 bits for a pictorial example.

HLA high-level calling sequence examples:

bits.reverse16( mem16 );

mov( ax, reversed16 );

mov( bits.reverse16( bx ), bxReversed ); // Note: result is in AX

HLA low-level calling sequence examples:

mov( mem16, ax );

call bits.reverse16;

mov( ax, reversed16 );

mov( bx, ax );

call bits.reverse16;

mov( ax, bxReversed );

bits.reverse8( b:byte in al ); @returns( "eax" );

This function reverses the bits passed to it in AL and returns the swapped value in AL. This function swaps bit 7 and 0, bits 6 and 1, bits 5 and 2, and bits 4 and 3 (see the diagram for bits.reverse8). Note that this function does not zero or sign-extend the result into EAX. Although this function currently preserves the H.O. three bytes of EAX, it’s safer to assume that the H.O. three bytes of EAX contain an undefined value upon return. See the diagram for bits.reverse8 and generalize that diagram to 16 bits for a pictorial example.

HLA high-level calling sequence examples:

bits.reverse8( mem8 );

mov( al, reversed8 );

mov( bits.reverse8( bl ), blReversed ); // Note: result is in AL

HLA low-level calling sequence examples:

mov( mem8, al );

call bits.reverse8;

mov( al, reversed8 );

mov( bl, al );

call bits.reverse8;

mov( al, blReversed );

 

 

Bit Merging Functions

The bit merging operands take two small values and produce a single larger value by interleaving the bits from the source operands in the destination operand. One operand’s bits are spread out into the destination operand’s even bit positions, the other operand’s bits are distributed in the odd bit positions (see the following diagram).

 

bits.merge32( even:dword in eax; odd:dword in edx ); @returns( "EDX:EAX" );

This function merges two dword values to produce a single qword value. The bits in the even operand are placed in the even bit positions of the qword result, the bits in the odd operand are merged into the odd bit positions. This function returns the qword result in edx:eax (edx contains the H.O. dword).

Note: because this function passes the parameters in EAX and EDX, you may get an undefined result if you specify EDX as the even parameter or EAX as the odd parameter.

HLA high-level calling sequence examples:

bits.merge32( mem32Even, mem32Odd );

mov( edx, (type dword mergedQword[4]) );

mov( eax, (type dword mergedQword[0]) );

bits.merge32( ecx, ebx );

mov( edx, (type dword mergedQword[4]) );

mov( eax, (type dword mergedQword[0]) );

HLA low-level calling sequence examples:

mov( mem32Even, eax );

mov( mem32Odd, edx );

call bits.merge32;

mov( edx, (type dword mergedQword[4]) );

mov( eax, (type dword mergedQword[0]) );

mov( ecx, eax );

mov( ebx, edx );

call bits.merge32;

mov( edx, (type dword mergedQword[4]) );

mov( eax, (type dword mergedQword[0]) );

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

This function merges two word values to produce a single dword value. The bits in the even operand are placed in the even bit positions of the dword result, the bits in the odd operand are merged into the odd bit positions. This function returns the dword result in EAX.

HLA high-level calling sequence examples:

bits.merge16( mem16Even, mem16Odd );

mov( eax, mergedDword );

bits.merge16( cx, bx );

mov( eax, mergedDword );

HLA low-level calling sequence examples:

// If mem16Even and mem16Odd are not at the end

// of some page in memory, you can safely do the following

push( (type dword mem16Even) );

push( (type dword mem16Odd) );

call bits.merge16;

mov( eax, mergedDword );

// To be absolutely safe, do something like the following

// (EAX is free to use because the result comes back in EAX):

movzx( mem16Even, eax );

push( eax );

movzx( mem16Odd, eax );

push( eax );

call bits.merge16;

mov( eax, mergedDword );

push( ecx );

push( edx );

call bits.merge16;

mov( eax, mergedDword );

bits.merge8( even:byte in al; odd:byte in ah ); @returns( "AX" );

This function merges two byte values to produce a single word value. The bits in the even operand are placed in the even bit positions of the word result, the bits in the odd operand are merged into the odd bit positions. This function returns the word result in AX.

Note: because this function passes the parameters in AL and AH, you may get an undefined result if you specify AH as the even parameter or AL as the odd parameter.

HLA high-level calling sequence examples:

bits.merge8( mem8Even, mem8Odd );

mov( ax, mergedWord );

bits.merge8( cl, bh );

mov( eax, mergedWord );

HLA low-level calling sequence examples:

mov( mem8Even, al );

mov( mem8Odd, ah );

call bits.merge8;

mov( ax, mergedWord );

mov( cl, al );

mov( bh, ah );

call bits.merge8;

mov( ax, mergedWord );

Bit Extraction Functions

These functions extract nibbles from their source operands, zero extend those nibbles to bytes, and return the values in an operand twice the size of the original operand (e.g., AL->AX, AX->EAX, and EAX->EDX:EAX). For example, the bits.nibbles8 function does the following:

bits.nibbles32( d:dword in eax ); @returns( "EDX:EAX" );

This function extracts the 8 nibbles from EAX, zero extends each of them to 8 bits, and then places them in the following locations

 

EAX[0:3] -> EAX[0:7]

EAX[4:7] -> EAX[8:15]

EAX[8:11] -> EAX[16:23]

EAX[12:15] -> EAX[24:31]

EAX[16:19] -> EDX[0:7]

EAX[20:23] -> EDX[8:15]

EAX[24:27] -> EDX[16:23]

EAX[28:31] -> EDX[24:31]

HLA high-level calling sequence examples:

bits.nibbles32( mem32 );

mov( eax, LONibbles );

mov( edx, HONibbles );

bits.nibbles32( ecx );

mov( eax, LONibbles );

mov( edx, HONibbles );

HLA low-level calling sequence examples:

mov( mem32, eax );

call bits.nibbles32;

mov( ax, mergedWord );

mov( ecx, eax );

call bits.nibbles32;

mov( eax, LONibbles );

mov( edx, HONibbles );

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

This function extracts the 4 nibbles from AX, zero extends each of them to 8 bits, and then places them in the following locations

AX[0:3] -> EAX[0:7]

AX[4:7] -> EAX[8:15]

AX[8:11] -> EAX[16:23]

AX[12:15] -> EAX[24:31]

HLA high-level calling sequence examples:

bits.nibbles16( mem16 );

mov( eax, Nibbles );

bits.nibbles16( cx );

mov( eax, Nibbles );

HLA low-level calling sequence examples:

mov( mem16, ax );

call bits.nibbles16;

mov( ax, mergedWord );

mov( cx, ax );

call bits.nibbles16;

mov( eax, LONibbles );

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

This function extracts the 2 nibbles from AL, zero extends each of them to 8 bits, and then places them in the following locations

AL[0:3] -> AL[0:7]

AL[4:7] -> AH[0:7]

HLA high-level calling sequence examples:

bits.nibbles8( mem8 );

mov( ax, TwoNibbles );

bits.nibbles8( ch );

mov( ax, TwoNibbles );

HLA low-level calling sequence examples:

mov( mem8, al );

call bits.nibbles8;

mov( ax, TwoNibbles );

mov( ch, al );

call bits.nibbles8;

mov( ax, TwoNibbles );

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). Note that the macro will report an error message if you try to pass EAX as the parameter.

Bit Distribution Functions

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.

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.

The Blobs Module (blobs.hhf)

This unit contains routines that read data from and write data to memory-based streams (blobs, or Binary Large OBjects). The blob functions can be broken down into five generic categories: general functions that initialize and allocate blobs, functions that convert between blobs and generic memory buffers, functions that do blob file I/O, accessor functions that provide access to the internal blob data structure, and blob I/O functions.

Note: be sure to read the chapter on "Passing Parameters to Standard Library Routines" (parmpassing.rtf) before reading this chapter.

A Note About the FPU: The Standard Library code makes occasional use of the FPU, particularly when converting between real and string formats and when computing certain mathematical functions. You should exercise caution when using MMX instructions in a program that makes use of the Standard Library. In particular, you should ensure that you are always in FPU mode (by executing an EMMS instruction) after you are finished using MMX instructions. Better yet, you should avoid the MMX instruction set altogether and use the improved SSE instruction set that accomplishes the same tasks (and doesn’t disturb the FPU).

A note about thread safety: Each blob object maintains its own critical section variable. Therefore, blobs are protected from multi-threaded access on a per-blob level. Because blobs create this critical section object whenever a blob is initialized or allocated, you must ensure that you free or destroy the blob when you are done using it (and before your program quits) in order to free up system resources.

Conversion Format Control

The blob output functions that convert numeric values to hexadecimal, unsigned decimal, and signed decimal output provide the ability to inject underscores between groups of three (decimal) or four (hexadecimal) digits to make it easier to read large numbers. You enable and disable underscore output using the conv.setUnderscores and conv.getUnderscores functions. Please refer to their documentation in the conv.rtf file for more details.

When reading numeric data from a blob, the blob functions use an internal delimiters character set to determine which characters may legally end a sequence of numeric digits. You can change the complexion of this character set using the conv.getDelimiters and conv.setDelimiters functions. Please refer to their documentation in the conv.rtf file for more details.

When converting numeric values to string form for output, the blob routines call the conversion functions found in the conv (conversions) module. For detailed information on the actual conversions, please consult the conv.rtf document.

Blob Synopsis

The best way to describe a blob (Binary Large Object) is by calling it a "memory-based file." You generally read and write data to a blob using a file I/O paradigm, that is, by using function calls similar to the HLA standard library's fileio functions. The advantage of blobs over files is that blobs are much faster than files; the disadvantage of blobs is that blob data is volatile (meaning that you lose all data written to a blob when the program quits or if there is a power outage). Another pair of blob disadvantages is that blobs' sizes are limited by the amount of available system RAM (files are limited by the amount of free disk space, which is usually much larger) and that you have to predeclare a blob's size before using it. By contrast, files can grow to any size up to the amount of free disk space you having to specify the maximum size prior to creating the file. Despite these limitations, blobs are quite useful because blob manipulation is much faster than file manipulation (often three orders of magnitude faster).

Blobs are very similar to strings. The main differences between strings and blobs are

Blob Internal Representation

The following is the internal representation of a blob object (these declarations are inside the blob namespace):

type

blobRec:

record := -20;

allocPtr :dword;

criticalSection :dword

rcursor :dword;

wcursor :dword;

maxlen :dword;

length :dword;

blobData :byte[16]; // Minimum is 16 bytes

endrecord;

t :pointer to blobRec; // blob.t outside blob namespace

blob_t :t; // blob.blob_t outside blob namespace

blob :t; // blob.blob outside blob namespace

Note that the fieldnames in blobRec are generally considered private to the blobRec record declaration and are intended for internal use (by the blobs package) only. Applications should only reference or modify these fields using the blob accessor functions (described a little later in this document).

The allocPtr field contains the address of the start of the memory block allocated for the blob. Blobs can be allocated to any address (that is a power of two and greater than or equal to 4). Because the stdlib's mem.alloc function only guarantees 8-byte alignment, certain calls that allocate storage for a blob might need to allocate extra storage in order to align the blob's data (the blobData field) at an appropriate address in memory. This might insert (an arbitrary amount of) padding bytes before the allocPtr field in the blob data structure. The blob data structure needs to save this allocation address (in allocPtr ) so that the application can free the storage consumed by the blob object. Much of the time, the allocPtr field will contain it's own address; for unaligned (the most common) blobs, or blobs aligned on 8-byte or less addresses, no extra padding is necessary. If the blob object is allocated in static memory rather than on the heap (or in memory that was not allocated by the blob library functions), the allocPtr field will contain NULL.

The criticalSection field contains the object that controls access to the blob’s critical section in multi-threaded applications. Note that this field is still present (though unused) in single-threaded applications. This is a private field and should never be accessed by code outside the blob library.

The rcursor (read cursor) field contains the offset into the blob's data where the next value will be read from the blob using one of the blob.get* functions or any of the other blob input functions that read data from the read cursor position.

The wcursor (write cursor) field contains the offset into the blob's data where the next value will be written to in the blob using one of the blob.put* functions or any of the other blob output functions that write data to the write cursor position in the blob.

The maxlen (maximum length) field contains the maximum size of the blob. If an application attempts to read or write data beyond this point in the blob, the library routines will generate an exception.

The length field is the current (dynamic) length of the blob. This field's value is always less than or equal to maxlen's value.

The blobData field is where the blob's data appears in memory. This is an array of bytes containing at least maxlen bytes (it may contain more, depending on the alignment of the blob allocation). This field always contains at least 16 bytes (hence the declared size of 16 in the blobRec record).

Declaring Blob Variables

HLA stdlib blob variables are pointers. When declaring blob objects, you should use the blob.blob , blob.blob_t , or blob.t data types. These three names are all synonyms, you can use whatever form you choose. Whenever this documentation refers to a "blob variable", it is actually talking about a pointer to a blob object, that is, a variable of type blob.blob , blob.blob_t , or blob.t . For example:

var

blobVar : blob.t;

blobVar2 : blob.blob_t;

blobVar3 : blob.blob;

Note that these declarations all reserve exactly four bytes for the blob variables ( blobVar1 , blobVar2 , and blobVar3 ). Blob variables always hold pointers to the actual blob data. Of course, like strings and other pointers, these declarations do not actually allocate storage for the blob object itself. You will have to call a function such as blob.alloc to allocate the actual storage for a blob object.

You can also use the blob.init function to initialize a block of memory you’ve allocated for use as a blob.

See the description of this function for more details.

Initializing and Allocating Blob Variables

The HLA Blobs module provides several functions that let you allocate storage for a blob on the heap or associate other storage with the blob. Because you must allocate storage for a blob before using it, these functions will generally be the first functions you call before using a blob.

blob.init( var b:var; numBytes:dword ); @returns( "eax" );

The blob.init routine initializes a block of memory so that it contains a blob object. This function returns a pointer to the blob object in EAX, which you should store into a blob.t variable. The b argument is the address of a block of bytes that you want to use as the blob object. The numBytes argument is the total size of the block of memory pointed at by the b parameter. Note that at least 20 bytes of the object pointed at by b will be used to hold the blob's metadata (internal data structure). That is, numBytes does not specify the maximum size of the blob; the maximum size will actually be slightly smaller than this value. This function will raise an exception if numBytes is too small (insufficient space to allocate the blob metadata and at least 16 bytes of blob data.). Warning: that this function sets the allocPtr field of the blobRec data structure to NULL. If you have allocated this storage on the heap, it is your responsibility to call mem.free with the original allocation address to return the storage to the heap. You must never call blob.free to free the blob storage that you've initialized with a blob.init call.

Important: this function creates a new critical section object and initializes the criticalSection field of the blob data object.

You must call blob.destroy when you are done with the blob to delete the critical section object and return its resources to the operating system.

blob.init16( var b:var; numBytes:dword ); @returns( "eax" );

Just like blob.init except that this function always returns an address that is aligned to a 16-byte boundary. Note that if the memory block pointed at by b isn't at an appropriate address, blob.init16 will use up to 15 bytes at the beginning of this block as padding bytes to guarantee that the address this function returns in eax is aligned to a 16-byte boundary. Therefore, you should ensure that the block of memory whose address you pass is at least 16 bytes larger than you need to ensure you have enough space after padding is removed from the total. The warnings above apply to blob.init16 as well as blob.init .

blob.alloc( size:dword); @returns( "eax" );

The blob.alloc routine allocates storage for a blob object that contains at least size bytes of blob data. It initializes the blob header ( blobRec ) data structure and returns a pointer to the blob in EAX (that you should store into a blob variable). This function will raise an exception if it cannot allocate the specified amount of storage on the heap for the blob object. Note that this function also allocates storage for the blob header and pads the size of the blob (typically to a multiple of 16 bytes) so this call allocates a little bit more storage than size bytes on the heap. Note that blobs created with the blob.alloc function are always aligned on a 16-byte boundary and they always initialize the blobRec allocPtr field with the address of the storage allocated by an (internal) mem.alloc call.

Important: this function creates a new critical section object and initializes the criticalSection field of the blob data object.

You must call blob.free when you are done with the blob to delete the critical section object and return its resources to the operating system.

blob.allocAligned( size:dword; alignment:dword); @returns( "eax" );

The blob.allocAligned routine also allocates storage for a blob object on the heap. The alignment argument is an integer in the range 0..16 specifying the alignment on a 2** alignment byte boundary. That is, the alignment value is interpreted as follows:

 

alignment

Blob data aligned to

0

16 bytes

1

16 bytes

2

16 bytes

3

16 bytes

4

16 bytes

5

32 bytes

6

64 bytes

7

128 bytes

8

256 bytes

9

512 bytes

10

1024 bytes

11

2048 bytes

12

4096 bytes

13

8192 bytes

14

16384 bytes

15

32768 bytes

16

65536 bytes

Note that the minimum alignment is always 16 bytes because this is the alignment that blob.alloc guarantees. Note that the blob.allocAligned function might have to add as many as 2*alignment+32 bytes to the size of the storage allocated on the heap in order to guarantee alignment on the desired address boundary. As for the blob.alloc call, this function initializes the internal allocPtr field with the address of the block of storage allocated on the heap.

Important: this function creates a new critical section object and initializes the criticalSection field of the blob data object.

You must call blob.free when you are done with the blob to delete the critical section object and return its resources to the operating system.

blob.realloc( theBlob:blob.t; size:dword ); @returns( "eax" );

The blob.realloc routine resizes an existing blob. The first argument, theBlob , is the address of the blob object to resize and the second argument is the new maximum size of the resultant blob. This function works by allocating a new blob of the specified size, copying the blob data from the original blob to the new blob, and then freeing the storage associated with the original blob if the allocPtr field contains a non-NULL value.

It will also destroy the critical section object held by the original blob.

Important: this function creates a new critical section object and initializes the criticalSection field of the blob data object.

You must call blob.free when you are done with the blob to delete the critical section object and return its resources to the operating system.

Note that there is no blob.reallocAligned function call, but you can easily write your own using the following code:

blob.allocAligned( newsize, desiredAlignment );

push( eax );

blob.cpy( originalBlob, eax );

blob.free( originalBlob );

pop( eax );

mov( eax, originalBlob );

blob.free( theBlob:blob.t);

The blob.free routine frees the storage associated with a blob object if that blob object was allocated on the heap with a call to blob.alloc or blob.allocAligned (specifically, if the allocPtr field in the blobRec data structure contains a non-NULL value). If the allocPtr field contains NULL, then this function returns without raising an exception. If theBlob is NULL, contains a bad address, or if allocPtr isn't pointing at an object on the heap, then this routine will raise an appropriate exception.

This function will also delete the critical section object held b the blob.

blob.destroy( theBlob:blob.t);

The blob.destroy routine deletes the critical section held by the blob. If the allocPtr

field is non-NULL, this will also deallocate the storage held by the blob. Functionally, blob.free and blob.destroy are equivalent; though blob.free is intended for blobs allocated via some blob.allooc* or blob.a_* function and blob.destroy is intended for blobs initialized via the blob.init* functions.

Blob Accessor Functions

The following functions in the HLA blobs unit provide access to the blobRec data structure. Programs should use these accessor functions rather than directly accessing the blobRect fields.

blob.length( b:blob.t); @returns( "eax" );

The blobl.length routine returns the value of the blobRec.length field, that is, the current size (actual data) of the blob.

HLA high-level calling sequence example:

blob.length( b );

mov( eax, blobLength );

HLA low-level calling sequence example:

push( b );

call blob.length;

mov( eax, blobLength );

blob.setLength( b:blob.t; newLen:dword );

The blobl.setLength routine sets the value of the blobRec.length field to the value specified by the newLen parameter. You should exercise extreme caution when using this function. The blob.setLength function does not check the value of the newLen argument to determine if it is valid. You could supply a value larger than the actual amount of data currently in the blob (meaning you've just added garbage to the end of the blob) or you could even supply a value that is beyond the allocated size of the blob (creating memory access problems down the road).

HLA high-level calling sequence example:

blob.setLength( b, 32768 );

HLA low-level calling sequence example:

push( b );

pushd( 32768 );

call blob.setLength;

blob.maxlen( b:blob.t); @returns( "eax" );

The blob.maxlen routine returns the value of the blobRec.maxlen field, that is, the current maximum size of the blob.

HLA high-level calling sequence example:

blob.maxlen( b );

mov( eax, blobMaxLen );

HLA low-level calling sequence example:

push( b );

call blob.maxlen;

mov( eax, blobMaxLen );

blob.setMaxlen( b:blob.t; newLen:dword );

The blobl.setMaxlen routine sets the value of the blobRec.maxlen field to the value specified by the newLen parameter. You should exercise extreme caution when using this function. The blob.setMaxlen function does not check the value of the newLen argument to determine if it is valid. You should only set the value of this field when allocating storage for a new blob or if you are shrinking the size of a blob in-place. Expanding the maxlen value does not allocate any more storage for the blob and such action will probably lead to a memory fault later on in your program. Note that this function does not change the value of the blobRec.length field, even if the new value for maxlen is less than the existing length.

HLA high-level calling sequence example:

blob.setMaxlen( b, 32768 );

HLA low-level calling sequence example:

push( b );

pushd( 32768 );

call blob.setMaxlen;

blob.rcursor( b:blob.t); @returns( "eax" );

The blobl.rcursor routine returns the value of the blobRec.rcursor field in the EAX register.

HLA high-level calling sequence example:

blob.rcursor( b );

mov( eax, blobReadPosition );

HLA low-level calling sequence example:

push( b );

call blob.rcursor;

mov( eax, blobReadPosition );

blob.setrCursor( b:blob.t; newCursor:dword );

The blobl.setrCursor routine sets the value of the blobRec.rcursor field to the value specified by the newCursor parameter. You should exercise extreme caution when using this function. The blob. setrCursor function does not check the value of the newCursor argument to determine if it is valid. If you point the rcursor field beyond the end of the blob's length you could get a memory fault error or read garbage data.

HLA high-level calling sequence example:

blob.setrCursor( b, 0 );

HLA low-level calling sequence example:

push( b );

pushd( 0 );

call blob.setrCursor;

blob.wcursor( b:blob.t); @returns( "eax" );

The blobl.wcursor routine returns the value of the blobRec.wcursor field in the EAX register.

HLA high-level calling sequence example:

blob.wcursor( b );

mov( eax, blobWritePosition );

HLA low-level calling sequence example:

push( b );

call blob.wcursor;

mov( eax, blobWritePosition );

blob.setwCursor( b:blob.t; newCursor:dword );

The blobl.setwCursor routine sets the value of the blobRec.rcursor field to the value specified by the newCursor parameter. You should exercise extreme caution when using this function. The blob. setwCursor function does not check the value of the newCursor argument to determine if it is valid. If you point the wcursor field beyond the end of the blob's length you could get a memory fault error or leave garbage data in the middle of the blob.

HLA high-level calling sequence example:

blob.setwCursor( b, 0 );

HLA low-level calling sequence example:

push( b );

pushd( 0 );

call blob.setwCursor;

blob.reset;

The blobl.reset routine sets the blob's rcursor , wcursor , and length fields to zero. This effectively restores the blob to its original state when it was created. Note that this does not change any actual data appearing the the blob's memory storage, but since blob.reset sets the length field to zero, this effectively erases any data present in the blob.

HLA high-level calling sequence example:

blob.reset();

HLA low-level calling sequence example:

call blob.reset;

blob.eof( b:blob.t ); @returns( "@c" );

This function returns true (1) in EAX and the carry flag if the read cursor is at the end of the blob's data. It returns false (0) otherwise. Note that this function actually returns true/false in EAX even though the "returns" value is "@c". It also returns the EOF state in the carry flag (c=1 if EOF, c=0 if not at EOF).

HLA high-level calling sequence example:

while( !(blob.eof( blobPointer )) do

<< something while not at EOF>>

endwhile;

 

HLA low-level calling sequence example:

whileNotEOF:

push( blobPointer );

call blob.eof;

cmp( al, true );

jne atEOF;

<< something while not at EOF>>

jmp whileNotEOF;

atEOF:

Blob Assignment Functions

These functions copy blobs and fill a blob with a single byte, word, or double-word value.

blob.a_cpy( b:blob.t ); @returns( "eax" );

This function creates a new blob on the heap that is a copy of the blob pointed at by the b parameter. It returns a pointer to the new blob in the EAX register. When you are done using this new blob, you should free the storage associated with it (and delete the criticalsection it creates) by calling the blob.free function.

HLA high-level calling sequence example:

blob.a_cpy( someBlob );

mov( eax, newBlob );

 

HLA low-level calling sequence example:

push( someBlob );

call blob.a_cpy;

mov( eax, newBlob);

blob.cpy( src:blob.t; dest:blob.t ); @returns( "eax" );

This function copies the data from the source blob (pointed at by src ) to the destination blob (pointed at by dest ) and returns a pointer to the destination blob in EAX. This function raises an ex.BlobOverflow exception if the destination blob isn’t large enough to hold the data copied from the source blob.

HLA high-level calling sequence example:

blob.cpy( someBlob, destBlob );

 

HLA low-level calling sequence example:

push( someBlob );

push( destBlob );

call blob.cpy;

blob.fillb( theValue:byte; numBytes:dword; dest:blob );

This function fills an existing blob with numBytes copies of the byte value theValue . This function resets the rcursor field to zero and the wcursor and length fields to numBytes . This function raises an ex.BlobOverflow exception if the destination blob (pointed at by dest ) is not large enough to hold numBytes bytes.

HLA high-level calling sequence example:

blob.fillb( 0, 1024, destBlob );

 

HLA low-level calling sequence example:

pushd( 0 );

pushd( 1024 );

push( destBlob );

call blob.fillb;

blob.fillw( theValue:word; numWords:dword; dest:blob );

This function fills an existing blob with numWords copies of the word value theValue . This function resets the rcursor field to zero and the wcursor and length fields to numWords*2 . This function raises an ex.BlobOverflow exception if the destination blob (pointed at by dest ) is not large enough to hold numWords*2 bytes.

HLA high-level calling sequence example:

blob.fillw( 1000, 1024, destBlob );

 

HLA low-level calling sequence example:

pushd( 1000 );

pushd( 1024 );

push( destBlob );

call blob.fillw;

blob.filld( theValue:word; numDwords:dword; dest:blob );

This function fills an existing blob with numDwords copies of the dword value theValue . This function resets the rcursor field to zero and the wcursor and length fields to numDwords*4 . This function raises an ex.BlobOverflow exception if the destination blob (pointed at by dest ) is not large enough to hold numDwords*4 bytes.

HLA high-level calling sequence example:

blob.filld( 1_000_000, 1024, destBlob );

 

HLA low-level calling sequence example:

pushd( 1_000_000 );

pushd( 1024 );

push( destBlob );

call blob.filld;

Blob Extraction Functions

These funcions extract subranges (slices) of a blob.

blob.a_subBlob( src:blob; start:dword; len:dword ); @returns( "eax" );

This function creates a new blob on the heap and initializes it with data from an existing blob (pointed at by src ). The new blob is initialized with len bytes starting at offset start in blob src . This function returns a pointer to the new blob on the heap in the EAX register. It is the callers responsibility to call blob.free to free the storage associated with this new blob (and delete the critical section that this function creates).

HLA high-level calling sequence example:

blob.a_subBlob( someBlob, 1024, 256 );

mov( eax, newBlob );

 

HLA low-level calling sequence example:

push( someBlob );

pushd( 1024 );

pushd( 256 );

call blob.a_subBlob;

mov( eax, newBlob );

 

blob.subBlob( src:blob; start:dword; len:dword; dest:blob.t );

This function copies a sequence from a source blob ( src ) to a destination blob ( dest ). It copies len bytes starting at offset start in src to dest . This function raises an ex.BlobOverflow exception if d is too small to receive the blob data.

HLA high-level calling sequence example:

blob.subBlob( someBlob, 1024, 256, destBlob );

 

HLA low-level calling sequence example:

push( someBlob );

pushd( 1024 );

pushd( 256 );

push( destBlob

call blob.subBlob;

 

 

Blob Comparison Functions

These funcions compare two blobs for equality or inequality.

blob.eq( left:blob; right:blob.t ); @returns( "@c" );

This function compares two blobs and returns true in the carry flag and the AL register if the two blobs are equal to one another.

HLA high-level calling sequence example:

if( blob.eq( someBlob, anotherBlob ) ) then

 

// Do something if blobs are equal

 

endif;

 

HLA low-level calling sequence example:

push( someBlob );

push( anotherBlob );

call blob.eq;

jnc blobsNotEqual;

 

// Do something if blobs are equal

 

blobsNotEqual:

 

blob.ne( left:blob; right:blob.t ); @returns( "@c" );

This function compares two blobs and returns true in the carry flag and the AL register if the two blobs are not equal to one another.

HLA high-level calling sequence example:

if( blob.ne( someBlob, anotherBlob ) ) then

 

// Do something if blobs are not equal

 

endif;

 

HLA low-level calling sequence example:

push( someBlob );

push( anotherBlob );

call blob.eq;

jnc blobsEqual;

 

// Do something if blobs are not equal

 

blobsEqual:

 

 

Blob Scanning Functions

These funcions convert memory buffers (ranges of bytes) to blob objects

blob.index( src1:blob; src2:blob.t ); @returns( "@c" );

blob.index( src1:blob; offs:dword src2:blob.t ); @returns( "@c" );

blob.index2( src1:blob; src2:blob.t ); @returns( "@c" );

blob.index3( src1:blob; offs:dword src2:blob.t ); @returns( "@c" );

blob.indexStr( src1:blob; src2:string ); @returns( "@c" );

blob.indexStr( src1:blob; offs:dword src2:string ); @returns( "@c" );

blob.indexStr2( src1:blob; src2:string ); @returns( "@c" );

blob.indexStr3( src1:blob; offs:dword src2:string ); @returns( "@c" );

 

These functions search for the presence of a string or blob within some blob. They return the carry flag set if they find the src2 blob or string within the src1 blob and clear if the src2 string or blob is not found within the src1 blob. The variants with the offs parameter begin searching for the blob or string at offset offs within the blob src1 . If the carry comes back set (meaning src2 was found), then the EAX register will contain the offset into src1 where src1 is first found. These functions raise an ex.ValueOutOfRange exception if the offs value is greater than the current length of src1 .

HLA high-level calling sequence example:

if( blob.index( someBlob, subBlob ) ) then

 

// Do something if subBlob is a subblob of someBlob

 

endif;

 

HLA low-level calling sequence example:

push( someBlob );

push( subBlob );

call blob.index;

jnc notPresent;

 

// Do something if subBlob is a subblob of someBlob

 

notPresent:

 

blob.rindex( src1:blob; src2:blob.t ); @returns( "@c" );

blob.rindex( src1:blob; offs:dword src2:blob.t ); @returns( "@c" );

blob.rindex2( src1:blob; src2:blob.t ); @returns( "@c" );

blob.rindex3( src1:blob; offs:dword src2:blob.t ); @returns( "@c" );

blob.rindexStr( src1:blob; src2:string ); @returns( "@c" );

blob.rindexStr( src1:blob; offs:dword src2:string ); @returns( "@c" );

blob.rindexStr2( src1:blob; src2:string ); @returns( "@c" );

blob.rindexStr3( src1:blob; offs:dword src2:string ); @returns( "@c" );

 

These functions search backwards for the presence of a string or blob within some blob starting at the end of that blob. They return the carry flag set if they find the src2 blob or string within the src1 blob and clear if the src2 string or blob is not found within the src1 blob. The variants with the offs parameter begin searching for the blob or string at offset length-offs within the blob src1 . If the carry comes back set (meaning src2 was found), then the EAX register will contain the offset into src1 where src1 is last found. These functions raise an ex.ValueOutOfRange exception if the offs value is greater than the current length of src1 .

HLA high-level calling sequence example:

if( blob.rindex( someBlob, subBlob ) ) then

 

// Do something if subBlob is a subblob of someBlob

 

endif;

 

HLA low-level calling sequence example:

push( someBlob );

push( subBlob );

call blob.rindex;

jnc notPresent;

 

// Do something if subBlob is a subblob of someBlob

 

notPresent:

 

blob.chpos( src1:blob; src2:char ); @returns( "@c" );

blob.chpos( src1:blob; offs:dword src2:char ); @returns( "@c" );

blob.chpos2( src1:blob; src2:char ); @returns( "@c" );

blob.chpos3( src1:blob; offs:dword src2:char ); @returns( "@c" );

 

These functions search for the presence of a character within some blob. They return the carry flag set if they find the src2 character within the src1 blob and clear if the src2 character is not found within the src1 blob. The variants with the offs parameter begin searching for the character at offset offs within the blob src1 . If the carry comes back set (meaning src2 was found), then the EAX register will contain the offset into src1 where src2 is first found. These functions raise an ex.ValueOutOfRange exception if the offs value is greater than the current length of src1 .

HLA high-level calling sequence example:

if( blob.chpos( someBlob, someChar ) ) then

 

// Do something if someChar is within someBlob

 

endif;

 

HLA low-level calling sequence example:

push( someBlob );

mov( someChar, al );

push( eax );

call blob.chpos;

jnc notPresent;

 

// Do something if someChar is within someBlob

 

notPresent:

 

 

blob.rchpos( src1:blob; src2:char ); @returns( "@c" );

blob.rchpos( src1:blob; offs:dword src2:char ); @returns( "@c" );

blob.rchpos2( src1:blob; src2:char ); @returns( "@c" );

blob.rchpos3( src1:blob; offs:dword src2:char ); @returns( "@c" );

 

These functions search for the presence of a character within some blob, searching from the end of the blob. They return the carry flag set if they find the src2 character within the src1 blob and clear if the src2 character is not found within the src1 blob. The variants with the offs parameter begin searching for the character at offset offs within the blob src1 . If the carry comes back set (meaning src2 was found), then the EAX register will contain the offset into src1 where src2 is first found. These functions raise an ex.ValueOutOfRange exception if the offs value is greater than the current length of src1 .

HLA high-level calling sequence example:

if( blob.rchpos( someBlob, someChar ) ) then

 

// Do something if someChar is within someBlob

 

endif;

 

HLA low-level calling sequence example:

push( someBlob );

mov( someChar, al );

push( eax );

call blob.rchpos;

jnc notPresent;

 

// Do something if someChar is within someBlob

 

notPresent:

 

 

Blob Concatenation Functions

There are two major types of blob concatentation functions. The first group (consisting of the blob.cat macro and the blob.cat2 , blob.cat3 , and blob.a_cat functions) take all the data from one blob and concatenates that data to the end (that is, at the length offset) of a second blob. These functions set the length and wcursor fields to point at the end of the new blob and reset the rcursor position of the result to zero.

The second group of concatenation functions take data from a string, a buffer, or a blob and append this to the end of some destination blob starting at the wcursor position in the destination blob.

 

blob.a_cat( src1:blob; src2:char ); @returns( "@eax" );

This function creates a new blob on the heap whose size is equivalent to the current lengths of the src1 and src2 blobs whose pointers are passed as parameters. This function then copies the data from src1 to the new blob and then appends the data from src2 to the end of this. This function returns a pointer to the new blob in EAX and the length and wcursor fields are set to the new blob’s length and the rcursor field is set to zero. Note that it is the caller’s responsibility to call blob.free in order to return the allocated storage to the heap and delete the newly created critical section object for the blob.

HLA high-level calling sequence example:

blob.a_cat( blob1, blob2 );

mov( eax, newBlob );

 

HLA low-level calling sequence example:

push( blob1 );

push( blob2 );

call blob.a_cat;

mov( eax, newBlob );

 

blob.cat( src:blob; dest:blob );

blob.cat2( src:blob; dest:blob );

These functions concatenate the data from the src blob to the end of the data in the dest blob. They raise an ex.BlobOverflow exception if the result will not fit in the destination blob.

HLA high-level calling sequence example:

blob.cat2( srcblob, destblob );

 

HLA low-level calling sequence example:

push( srcblob );

push( destblob );

call blob.cat2;

mov( eax, newBlob );

 

blob.cat( src1:blob; src2:blob; dest:blob );

blob.cat3( src1:blob; src2:blob; dest:blob );

These functions concatenate the data from two blobs ( src1 and src2 ) and store the concatenated result into the dest operand. They raise an ex.BlobOverflow exception if the result will not fit in the destination blob.

HLA high-level calling sequence example:

blob.cat3( blob1, blob2, dest );

 

HLA low-level calling sequence example:

push( blob1 );

push( blob2 );

push( dest );

call blob.cat3;

 

blob.catsub( src:blob; start:dword; len:dword; dest:blob );

blob.catsub4( src:string; start:dword; len:dword; dest:blob );

blob.catsub( src2:blob; start:dword; len:dword; src1:string; dest:blob );

blob.catsub5( src2:string; start:dword; len:dword; src1:string; dest:blob );

The blob.catsub functions are actually overloaded procedures that map to the blob.catsub4 and blob.catsub5 functions, depending on the call signature.

These functions concatenate the data froma string ( src ) or pair of strings ( src1 and src2 ) and store the concatenated result into the dest blob operand. They raise an ex.BlobOverflow exception if the result will not fit in the destination blob. These functions raise an ex.StringIndexError exception if the start index value is greater than the current length of the string.

The first two functions extract a substring of length len , starting at character position start , from src and concatenate this string to the end of the dest blob.

The second two functions copy the src1 string to the blob and then copy the substring of src2 (specified by start and len ) to the end of this blob.

All of these functions concatenate their strings to the blob starting at the wcursor position in the blob. They will leave wcursor pointing beyond the data just concatenated and will update the length field of the blob if the concatenated data extends the length. These functions do not affect the rcursor field of the blob.

HLA high-level calling sequence example:

blob.catsub4( strVar, 0, 24, dest );

blob.catsub5( strVar2, 0, 24, strVar1, dest );

 

HLA low-level calling sequence example:

push( strVar );

pushd( 0 );

pushd( 24 );

push( dest );

call blob.catsub4;

push( strVar2 );

pushd( 0 );

pushd( 24 );

push( strVar1 );

push( dest );

call blob.catsub5;

 

blob.a_catsub( src:blob; start:dword; len:dword; dest:blob );

This function extracts a substring ( src , from start of length len ) and creates a new blob on the heap from the substring data. This function returns a pointer to the new blob in EAX This function sets the rcursor field of the blob to zero and the wcursor and length fields of the blob to len . It is the caller’s responsibility to free the storage allocated by the function (and the critical section it creates) by calling blob.free when the caller is done with this blob.

HLA high-level calling sequence example:

blob.a_catsub( strVar, 0, 24 );

mov( eax, newBlob );

 

HLA low-level calling sequence example:

push( strVar );

pushd( 0 );

pushd( 24 );

call blob.a_catsub;

mov( eax, newBlob );

 

 

blob.catbuf2( src:buf_t; dest:blob );

blob.catbuf3a( startBuf:dword; endBuf:dword; dest:blob );

blob.catbuf2 is a synonym for blob.catbuf3a . As it turns out, a buf_t object passed on the stack is the same as passing the start Buf and endBuf dwords on the stack.

This function concatenates the bytes from a buffer (specified by the src or startBuf / endBuf variables) to the end of an existing blob ( dest ). This function stores the buffer data starting at the offset specified by the wcursor field of the blob. The bytes from memory locations startBuf to endBuf-1 are concatenated to the blob. If the new wcursor field value is greater than length , this function also extends the value of the length field. This function raises an ex. BlobOverflow exception if the new blob size would be greater than the maxlen field value.

HLA high-level calling sequence example:

blob.catbuf3a( startAddress, endAddressPlus1, destBlob );

 

HLA low-level calling sequence example:

push( startAddress );

push( endAddressPlus1 );

pushd( destBlob );

call blob.catbuf3a;

 

 

blob.catbuf3b( src2:buf_t; src1:string; dest:blob );

blob.catbuf4( startBuf:dword; endBuf:dword; strSrc:string; dest:blob );

blob.catbufe3b is a synonym for blob.catbuf4 . As it turns out, a buf_t object passed on the stack is the same as passing the startBuf and endBuf dwords on the stack.

This function concatenates the bytes from a buffer (specified by the src2 or startBuf / endBuf variables) to the end of a string ( strSrc ) and concatenate this data to the end of an existing blob ( dest ). This function stores the buffer data starting at the offset specified by the wcursor field of the blob. The bytes from the string ( strSrc ) and then memory locations startBuf to endBuf-1 are concatenated to the blob. If the new wcursor field value is greater than length , this function also extends the value of the length field. This function raises an ex. BlobOverflow exception if the new blob size would be greater than the maxlen field value.

HLA high-level calling sequence example:

blob.catbuf4( startAddress, endAddressPlus1, someStr, destBlob );

 

HLA low-level calling sequence example:

push( startAddress );

push( endAddressPlus1 );

push( someStr );

pushd( destBlob );

call blob.catbuf4;

 

 

Blob Conversion Functions

These funcions convert memory buffers (ranges of bytes) to blob objects

and strings to blob objects.

blob.bufToBlob2( buf:@global:buf_t; b:blob.t );

blob.bufToBlob3( startBuf:dword; endBuf:dword; b:blob.t );

These functions convert a

range of bytes (specified by a starting and ending address) into a blob object. The blob data is stored into the (previously allocated) blob object pointed at by the b parameter.

Note that these function names are actually synonyms for the same function. As it turns out, passing a buf_t object on the stack produces the exact same stack frame as passing a starting and ending buffer address.

The startBuf parameter is the address of the first byte of the memory block to convert; the endBuf parameter supplies the last address of the buffer plus one.

The b parameter must point at a previously allocated and initialized blob object. This blob must be large enough ( maxlen ) to hold the range of bytes specified by the buffer parameter(s).

HLA low-level calling sequence examples:

blob.bufToBlob3( startingAddress, endingAddress, blobPtr1 );

blob.bufToBlob3( ebx, ecx, blobPtr2 );

blob.bufToBlob2( buf_t_Variable, blobPtr3 );

HLA low-level calling sequence examples:

push( startingAddress );

push( endingAddress );

push( blobPtr1 );

call blob.bufToBlob3;

push( ebx );

push( ecx );

push( blobPtr2 );

call blob.a_bufToBlob2;

push( (type dword buf_t_Variable[0]) );

push( (type dword buf_t_Variable[4]) );

push( blobPtr3 );

call blob.a_bufToBlob1;

 

blob.a_bufToBlob1( buf:@global:buf_t ); @returns( "@eax" );

blob.a_bufToBlob2( startBuf:dword; endBuf:dword ); @returns( "@eax" );

These functions convert a range of bytes (specified by a starting and ending address) into a blob object. The blob’s data is allocated on the heap and these functions return a pointer to the blob data in the EAX register.

Note that these function names are actually synonyms for the same function. As it turns out, passing a buf_t object on the stack produces the exact same stack frame as passing a starting and ending buffer address.

The startBuf parameter is the address of the first byte of the memory block to convert; the endBuf parameter supplies the last address of the buffer plus one.

It is the caller’s responsibility to call blob.free to free up the allocated storage and release the critical section object when the caller is done using the blob these function create.

HLA low-level calling sequence examples:

blob.a_bufToBlob2( startingAddress, endingAddress );

mov( eax, blobPtr1);

blob.a_bufToBlob2( ebx, ecx );

mov( eax, blobPtr2);

blob.a_bufToBlob1( buf_t_Variable );

mov( eax, blobPtr3);

HLA low-level calling sequence examples:

push( startingAddress );

push( endingAddress );

call blob.a_bufToBlob2;

mov( eax, blobPtr1);

push( ebx );

push( ecx );

call blob.a_bufToBlob2;

mov( eax, blobPtr2);

push( (type dword buf_t_Variable[0]) );

push( (type dword buf_t_Variable[4]) );

call blob.a_bufToBlob1;

mov( eax, blobPtr3);

blob.strToBlob( src:string; dest:blob );

blob.zstrToBlob( src:string; dest:blob );

 

General Blob I/O Functions

Here are the blob file I/O routines provided by the HLA blobs unit:

blob.a_load( FileName: string ); @returns( "eax" );

The blobl.a_load routine opens the file by the specified name, allocates sufficient storage to hold all the data in the file, reads the file's data into the blob, and then closes the file. This function returns a pointer to the initialized blob in the EAX register. You should call blob.free to return this storage to the heap when you are done using the blob.

This function initializes the read cursor so that it points at the beginning of the blob data read from the file. It initializes the write cursor to point at the end of the blob's data; note, however, that there is no additional space allocated at the end of the blob, so any attempt to write data to the blob (without resetting the write cursor to some other point in the blob) will produce a blob overflow exception.

blob.a_loadExtended( FileName: string; extend:dword ); @returns( "eax" );

The blobl.a_loadExtended routine opens the file by the specified name, allocates sufficient storage to hold all the data in the file plus the number of bytes specified by the extend argument, reads the file's data into the blob, and then closes the file. This function returns a pointer to the initialized blob in the EAX register. You should call blob.free to return this storage to the heap when you are done using the blob.

This function initializes the read cursor so that it points at the beginning of the blob data read from the file. It initializes the write cursor to point at the end of the blob's data. Because the blob's size has been extended by the value of the second parameter in the call, you can write that many additional bytes to the file.

blob.load( filename:string; b:blob.t );

The blob.load routine opens the file by the specified name and reads the file's data into the blob specified by the b parameter. 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 will read the file's data into the blob (raising an exception if the file's data is too large to fit in the blob or if there is an error reading the file).

HLA high-level calling sequence examples:

blob.load( filenameStr, b );

blob.a_load( "myfile2.txt" );

mov( eax, b2 );

blob.a_loadExtended( "myfile3.txt", 8192 );

mov( eax, b3 );

HLA low-level calling sequence examples:

push( filenameStr );

push( b );

call blob.open;

// Note: If you want to use a string literal for the filename, the best

// solution is to create a string object in the readonly section, e.g.,

//

// readonly

// filenameStr2 :string := "myfile2.txt";

//

// and just use the "filenameStr2" object you’ve created.

push( filenameStr2 );

call blob.a_open;

mov( eax, b2 );

// You may also do the following if you have a register available:

lea( eax, "myfile3.txt" );

push( eax );

pushd( 8192 );

call blob.a_loadExtended;

mov( eax, b3 );

blob.appendFile( filename:string; b:blob.t.blob );

This function opens a file, reads its data, and appends that data to the end of an existing blob. It raises the ex.BlobOverflow exception if the file is to large to append to the end of the blob specified by the b parameter. This call sets the write cursor to the end of the file appended to the blob in memory; it does not affect the value of the read cursor.

HLA high-level calling sequence example:

blob.appendFile( fileNameStr, b );

HLA low-level calling sequence example:

push( filenameStr );

push( b );

call blob.appendFile;

blob.a_appendFile( filename:string; b:blob.t.blob ); @returns( "eax" );

This function creates a new blob on the heap that is the size of the data in the b blob plus the size of the file specified by filename . It copies the data from the b blob to the new blob and then reads the file's data and appends that data to the end of the new blob. It returns a pointer to the new blob in the EAX register (the caller should ultimately call blob.free to return this storage to the heap). This call sets the write cursor to the end of the file appended to the blob in memory; the value of the read cursor will be the same value found in the b blob.

HLA high-level calling sequence example:

blob.a_appendFile( fileNameStr, b );

mov( eax, b2 );

HLA low-level calling sequence example:

push( filenameStr );

push( b );

call blob.a_appendFile;

mov( eax, b2 );

blob.a_appendFileExtended( filename:string; b:blob.t.blob; extend:dword )
{@returns( "eax" )};

This function allocates storage for a new blob that is the size of the existing blob plus the size of the file and the extend value. It then copies the blob specified by the b parameter to the newly allocated blob and appends the file's data to the end of this blob. Finally, it returns a pointer to the new blob in the EAX register. Note that the original blob (specified by the b parameter) is unaffected by this operation. This call sets the write cursor to the end of the file appended to the blob in memory; it sets the value of the read cursor to the same value of the original blob (passed in b ).

HLA high-level calling sequence example:

blob.a_appendFileExtend( fileNameStr, b, 16384 );

HLA low-level calling sequence example:

push( filenameStr );

push( b );

pushd( 16384 )

call blob.a_appendFileExtend;

blob.save( filename:string; b:blob.t );

This function writes the blob's data (specified by the b parameter) to the file specified by the filename parameter. This function will overwrite any existing file.

HLA high-level calling sequence examples:

blob.save( fileNameStr, blobVar );

HLA low-level calling sequence examples:

push( fileNameStr );

push( blobVar );

call blob.save;

Blob Binary I/O Routines

blob.write( b:blob.t; var src:var; len:dword ); @returns( "eax" );

This procedure writes the number of bytes specified by the len variable to the blob specified by the b parameter (at offset wcursor in the blob). The bytes starting at the address of the src object are written to the blob. No range checking is done on the src address value. It is your responsibility to ensure that the buffer contains at least len valid data bytes. Note that src is an untyped reference parameter. This means that blob.write will take the address of whatever object you provide as this parameter (including pointer variables, which may not be what you want). If you want to pass the value of a pointer variable as the buffer address (rather than the address of the pointer variable) when using the high-level style calling syntax, use the VAL keyword as a prefix to the parameter (see the following examples). This function returns the number of bytes written to the blob in the EAX register. If this operation writes bytes beyond the previous length of the blob, it will increment the blobRec.length field of the blob appropriately.

HLA high-level calling sequence examples:

blob.write( blobPointer, buffer, count );

// If bufPtr is a dword object containing the

// address of the buffer whose data you wish to

// write to the blob:

blob.write( blobPointer, val bufPtr, count );

// The following writes the four-byte value of

// the bufPtr variable to the blob (an unusual

// operation):

blob.write( blobPointer, bufPtr, 4 );

HLA low-level calling sequence examples:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

pushd( &buffer );

push( count );

call blob.write;

// If a 32-bit register is available and buffer

// isn’t at a fixed, static, address:

push( blobPointer );

lea( eax, buffer );

push( eax );

push( count );

call blob.write;

// If a 32-bit register is not available and buffer

// isn’t at a fixed, static, address:

push( blobPointer );

sub( 4, esp );

push( eax );

lea( eax, buffer );

mov( eax, [esp+4] );

pop( eax );

push( count );

call blob.write;

// If bufPtr points at the buffer to write,

// then use code like this:

push( blobPointer );

push( bufPtr);

push( count );

call blob.write;

// To write the 4 bytes at bufPtr to

// the file (unusual), you could use

// code like this:

push( blobPointer );

lea( eax, bufPtr );

push( eax );

pushd( 4 );

call blob.write;

blob.writeAt( b:blob.t; var src:var; index:dword; len:dword );
@returns( "eax" );

This procedure writes the number of bytes specified by the len variable to the blob specified by the b parameter at the offset specified by the index parameter. This procedure does not use nor does it modify the blobRec.wcursor value. The bytes starting at the address of the src object are written to the blob. No range checking is done on the src address value. It is your responsibility to ensure that the buffer contains at least len valid data bytes. Note that src is an untyped reference parameter. This means that blob.writeAt will take the address of whatever object you provide as this parameter (including pointer variables, which may not be what you want). If you want to pass the value of a pointer variable as the buffer address (rather than the address of the pointer variable) when using the high-level style calling syntax, use the VAL keyword as a prefix to the parameter (see the following examples). This function returns the number of bytes written to the blob in the EAX register. If the sum of index+len is greater than the previous length of the blob, then this function will extend the length of the blob. If the value of index is greater than the original length of the blob, then this function will return zero in EAX and will not transfer any data to the blob.

HLA high-level calling sequence example:

blob.writeAt( blobPointer, buffer, writeOffset, count );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

pushd( &buffer );

push( writeOffset );

push( count );

call blob.writeAt;

blob.putByte( b:blob.t; byteVal:byte );

This procedure writes a single byte value ( byteVal , which is a single-byte binary value) to the blob specified by the b parameter (at offset wcursor in the blob). This function call advances the value of wcursor by 1. This call is effectively equivalent to blob.write( b, byteVal, 1); except that it does not return the number of bytes written in EAX (which is always 1, assuming there are no exceptions).

HLA high-level calling sequence example:

blob.putByte( blobPointer, ByteValue );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory and EAX is available for use:

push( blobPointer );

movzx( ByteValue, eax );

push( eax );

call blob.putByte;

blob.putWord( b:blob.t; wordVal:word );

This procedure writes a single word value ( wordVal , which is a two-byte binary value) to the blob specified by the b parameter (at offset wcursor in the blob). This function call advances the value of wcursor by 2. This call is effectively equivalent to blob.write( b, wordVal, 2); except that it does not return the number of bytes written in EAX (which is always 2, assuming there are no exceptions).

HLA high-level calling sequence example:

blob.putWord( blobPointer, WordValue );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

pushw( 0 );

push( WordValue );

call blob.putWord;

blob.putDword( b:blob.t; dwordVal:dword );

This procedure writes a single dword value ( dwordVal , which is a four-byte binary value) to the blob specified by the b parameter (at offset wcursor in the blob). This function call advances the value of wcursor by 4. This call is effectively equivalent to blob.write( b, dwordVal, 4); except that it does not return the number of bytes written in EAX (which is always 4, assuming there are no exceptions).

HLA high-level calling sequence example:

blob.putDword( blobPointer, DwordValue );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

push( DwordValue );

call blob.putDword;

blob.putQword( b:blob.t; QwordVal:qword );

This procedure writes a single qword value ( qwordVal , which is an eight-byte binary value) to the blob specified by the b parameter (at offset wcursor in the blob). This function call advances the value of wcursor by 8. This call is effectively equivalent to blob.write( b, qwordVal, 8); except that it does not return the number of bytes written in EAX (which is always 8, assuming there are no exceptions).

HLA high-level calling sequence example:

blob.putQword( blobPointer, QwordValue );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

push( (type dword QwordValue[4]) );

push( (type dword QwordValue[0]) );

call blob.putQword;

blob.putTbyte( b:blob.t; tbyteVal:tbyte );

This procedure writes a single tbyte value ( tbyteVal , which is a 10-byte binary value) to the blob specified by the b parameter (at offset wcursor in the blob). This function call advances the value of wcursor by 10. This call is effectively equivalent to blob.write( b, tbyteVal, 10); except that it does not return the number of bytes written in EAX (which is always 10, assuming there are no exceptions).

HLA high-level calling sequence example:

blob.putTbyte( blobPointer, TByteValue );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

pushw( 0 );

push( (type word TByteValue [8]) );

push( (type dword TByteValue [4]) );

push( (type dword TByteValue [0]) );

call blob.putTbyte;

blob.putLword( b:blob.t; LwordVal:lword );

This procedure writes a single lword value ( lwordVal , which is a 16-byte binary value) to the blob specified by the b parameter (at offset wcursor in the blob). This function call advances the value of wcursor by 16. This call is effectively equivalent to blob.write( b, lwordVal, 16); except that it does not return the number of bytes written in EAX (which is always 16, assuming there are no exceptions).

HLA high-level calling sequence example:

blob.putLword( blobPointer, LwordValue );

HLA low-level calling sequence example:

// Assumes buffer is a static object at a fixed

// address in memory:

push( blobPointer );

push( (type dword LwordValue[12]) );

push( (type dword LwordValue[8 ]) );

push( (type dword LwordValue[4 ]) );

push( (type dword LwordValue[0 ]) );

call blob.putLword;

blob.read( b:blob.t; var buffer:byte; count:uns32 ); @returns( "eax" );

This routine reads a sequence of count bytes from the specified blob starting at the rcursor position in the blob. It stores the bytes into memory at the address specified by buffer .

It returns the number of bytes actually read from the blob in the EAX register (this is usually equal to the count value, unless the read operation attempts to read beyond the current length of the blob, in which case the actual number of bytes is returned in EAX).

HLA high-level calling sequence examples:

blob.read( blobPointer, buffer, count );

blob.read( blobPointer, [eax], 1024 );

HLA low-level calling sequence examples:

// If buffer is a static variable:

push( blobPointer );

pushd( &buffer );

push( count );

call blob.read;

blob.readAt( b:blob.t; var buffer:byte; index:dword; len:uns32 )

This routine reads a sequence of len bytes from the specified blob starting at offset index into the blob. It stores the bytes into memory at the address specified by buffer .

This function call ignores the initial value in the rcursor variable and it does not change this value. This function returns the actual number of bytes read in the EAX register (which is usually equal to len ). If len plus index is greater than the current blob length, this this function returns the actual number of bytes read (which will be less than len ) in the EAX register.

HLA high-level calling sequence examples:

blob.readAt( blobPointer, buffer, index, count );

blob.readAt( blobPointer, [eax], 500, 1024 );

HLA low-level calling sequence examples:

push( blobPointer );

pushd( &buffer );

push( index );

push( count );

call blob.readAt;

blob.getByte( b:blob.t ); @returns( "al" );

This procedure reads a single byte value from the blob specified by the b parameter (at offset rcursor in the blob) and returns this byte in the AL register. This function call advances the value of rcursor by 1. This call is effectively equivalent to blob.read( b, byteVal, 1); except that it does not return the number of bytes read in EAX (which is always 1, assuming there are no exceptions).

It will raise an ex.BlobOverflow exception if the value of rcursor is greater than or equal to the current blob length.

HLA high-level calling sequence example:

blob.getByte( blobPointer );

mov( al, ByteValue );

HLA low-level calling sequence example:

push( blobPointer );

call blob.getByte;

mov( al, ByteValue );

blob.getWord( b:blob.t ); @returns( "ax" );

This procedure reads a single word value from the blob specified by the b parameter (at offset rcursor in the blob) and returns this word in the AX register. This function call advances the value of rcursor by 2. This call is effectively equivalent to blob.read( b, wordVal, 2); except that it does not return the number of bytes read in EAX (which is always 2, assuming there are no exceptions).

It will raise an ex.BlobOverflow exception if the value of rcursor is greater than or equal to the current blob length.

HLA high-level calling sequence example:

blob.getWord( blobPointer );

mov( ax, WordValue );

HLA low-level calling sequence example:

push( blobPointer );

call blob.getWord;

mov( ax, WordValue );

blob.getDword( b:blob.t ); @returns( "eax" );

This procedure reads a single dword value from the blob specified by the b parameter (at offset rcursor in the blob) and returns this dword in the EAX register. This function call advances the value of rcursor by 4. This call is effectively equivalent to blob.read( b, dwordVal, 4); except that it does not return the number of bytes read in EAX (which is always 4, assuming there are no exceptions).

It will raise an ex.BlobOverflow exception if the value of rcursor is greater than or equal to the current blob length.

HLA high-level calling sequence example:

blob.getDword( blobPointer );

mov( eax, DwordValue );

HLA low-level calling sequence example:

push( blobPointer );

call blob.getDword;

mov( ax, DwordValue );

blob.getQword( b:blob.t ); @returns( "edx:eax" );

This procedure reads a single qword value from the blob specified by the b parameter (at offset rcursor in the blob) and returns this qword in the EDX:EAX register pair. This function call advances the value of rcursor by 8. This call is effectively equivalent to blob.read( b, qwordVal, 8); except that it does not return the number of bytes read in EAX (which is always 8, assuming there are no exceptions).

It will raise an ex.BlobOverflow exception if the value of rcursor is greater than or equal to the current blob length.

HLA high-level calling sequence example:

blob.getQword( blobPointer );

mov( eax, (type dword QwordValue[0]) );

mov( edx, (type dword QwordValue[4]) );

HLA low-level calling sequence example:

push( blobPointer );

call blob.getQword;

mov( eax, (type dword QwordValue[0]) );

mov( edx, (type dword QwordValue[4]) );

blob.getTbyte( b:blob.t; tbyteVal:tbyte );

This procedure reads a single tbyte value from the blob specified by the b parameter (at offset rcursor in the blob) and stores this tbyte via the tbyteVal reference parameter. This function call advances the value of rcursor by 10. This call is effectively equivalent to blob.read( b, tbyteVal, 10); except that it does not return the number of bytes read in EAX (which is always 10, assuming there are no exceptions).

It will raise an ex.BlobOverflow exception if the value of rcursor is greater than or equal to the current blob length.

HLA high-level calling sequence example:

blob.getTbyte( blobPointer, tByteVar );

HLA low-level calling sequence example:

push( blobPointer );

pushd( &tbyteVar );

call blob.getTbyte;

blob.getLword( b:blob.t; lwordVal:lword );

This procedure reads a single lword value from the blob specified by the b parameter (at offset rcursor in the blob) and stores this lword via the lwordVal reference parameter. This function call advances the value of rcursor by 16. This call is effectively equivalent to blob.read( b, lwordVal, 16); except that it does not return the number of bytes read in EAX (which is always 16, assuming there are no exceptions).

It will raise an ex.BlobOverflow exception if the value of rcursor is greater than or equal to the current blob length.

HLA high-level calling sequence example:

blob.getLword( blobPointer, lwordVar );

HLA low-level calling sequence example:

push( blobPointer );

pushd( &lwordVar );

call blob.getLword;

Blob Output Routines

The output routines in the blobs module are very similar to the file output routines in the fileio module as well as the output routines in the stdout library module. In general, these routines require (at least) two parameters; the first is the (pointer to the) blob object, the second parameter is usually the value to write to the blob. Some functions contain additional parameters that provide formatting information.

All output is written to the blob at the wcursor offset into the blob's data. For each byte written, wcursor is incremented by 1. If wcursor’s value becomes greater than or equal to the blob’s length value, then length is adjusted as well (that is, length and wcursor will have the same value). If wcursor exceeds the value in the maxlen field, then these functions raise an ex.BlobOverflow exception.

See the descriptions of the corresponding functions in the fileio module for more details.

Note that function names of the form blob.cat* are synonyms for the blob.put* functions.

blob.newln( b:blob.t )

blob.putbool( b:blob.t; b:boolean )

blob.putc( b:blob.t; c:char )

blob.putcSize( b:blob.t; c:char; width:int32; fill:char )

blob.putcset( b:blob.t; cst:cset )

blob.puts( b:blob.t; s:string )

blob.putsSize( b:blob.t; s:string; width:int32; fill:char )

blob.putb( b:blob.t; b:byte )

blob.puth8( b:blob.t; b:byte )

blob.puth8Size( b:blob.t; b:byte; size:dword; fill:char )

blob.putw( b:blob.t; w:word )

blob.puth16( b:blob.t; w:word )

blob.puth16Size( b:blob.t; w:word; size:dword; fill:char )

blob.putd( b:blob.t; d:dword )

blob.puth32( b:blob.t; d:dword )

blob.puth32Size( b:blob.t; d:dword; size:dword; fill:char )

blob.putq( b:blob.t; q:qword )

blob.puth64( b:blob.t; q:qword )

blob.puth64Size( b:blob.t; q:qword; size:dword; fill:char )

blob.puttb( b:blob.t; tb:tbyte )

blob.puth80( b:blob.t; tb:tbyte )

blob.puth80Size( b:blob.t; tb:tbyte; size:dword; fill:char )

blob.putl( b:blob.t; l:lword )

blob.puth128( b:blob.t; l:lword )

blob.puti8 ( b:blob.t; b:byte )

blob.puti8Size ( b:blob.t; b:byte; width:int32; fill:char )

blob.puti16( b:blob.t; w:word )

blob.puti16Size( b:blob.t; w:word; width:int32; fill:char )

blob.puti32( b:blob.t; d:dword )

blob.puti32Size( b:blob.t; d:dword; width:int32; fill:char )

blob.puti64( b:blob.t; q:qword )

blob.puti64Size( b:blob.t; q:qword; width:int32; fill:char )

blob.puti128( b:blob.t; l:lword )

blob.puti128Size( b:blob.t; l:lword; width:int32; fill:char )

blob.putu8 ( b:blob.t; b:byte )

blob.putu8Size( b:blob.t; b:byte; width:int32; fill:char )

blob.putu16( b:blob.t; w:word )

blob.putu16Size( b:blob.t; w:word; width:int32; fill:char )

blob.putu32( b:blob.t; d:dword )

blob.putu32Size( b:blob.t; d:dword; width:int32; fill:char )

blob.putu64( b:blob.t; q:qword )

blob.putu64Size( b:blob.t; q:qword; width:int32; fill:char )

blob.putu128( b:blob.t; l:lword )

blob.putu128Size( b:blob.t; l:lword; width:int32; fill:char )

blob.pute32( b:blob.t; r:real32; width:uns32 )

blob.pute64( b:blob.t; r:real64; width:uns32 )

blob.pute80( b:blob.t; r:real80; width:uns32 )

blob.putr32( b:blob.t; r:real32; width:uns32; decpts:uns32; pad:char )

blob.putr64( b:blob.t; r:real64; width:uns32; decpts:uns32; pad:char )

blob.putr80( b:blob.t; r:real80; width:uns32; decpts:uns32; pad:char )

blob.put( list_of_items )

Blob Input Routines

The input routines in the blobs module are very similar to the file input routines in the fileio module as well as the input routines in the stdin library module. In general, these routines require one parameter: the pointer to the blob object.

All input is read from the blob starting at the rcursor offset into the blob's data. For each byte read, rcursor is incremented by 1. If rcursor’s value becomes greater than or equal to the blob’s length value, then these functions raise an ex.EndOfFile exception.

See the descriptions of the corresponding functions in the fileio module for more details.

blob.readLn( b:blob.t );

blob.eoln( b:blob.t ); @returns( "al" );

blob.getc( b:blob.t ); @returns( "al" );

blob.gets( b:blob.t; s:string );

blob.a_gets( b:blob.t ); @returns( "eax" );

blob.geth8( b:blob.t ); @returns( "al" );

blob.geth16( b:blob.t ); @returns( "ax" );

blob.geth32( b:blob.t ); @returns( "eax" );

blob.geth64( b:blob.t );@returns( "edx:eax" );

blob.geth80( b:blob.t; var dest:tbyte );

blob.geth128( b:blob.t; var dest:lword );

blob.geti8( b:blob.t ); @returns( "al" );

blob.geti16( b:blob.t ); @returns( "ax" );

blob.geti32( b:blob.t ); @returns( "eax" );

blob.geti64( b:blob.t );@returns( "edx:eax" );

blob.geti128( b:blob.t; var dest:lword );

blob.getu8( b:blob.t ); @returns( "al" );

blob.getu16( b:blob.t ); @returns( "ax" );

blob.getu32( b:blob.t ); @returns( "eax" );

blob.getu64( b:blob.t );@returns( "edx:eax" );

blob.getu128( b:blob.t; var dest:lword );

blob.getf( b:blob.t ); @returns( "st0" );

blob.get( List_of_items_to_read );

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. This module works with ASCII characters in the range #0..#$7F only. Though the functions accept 8-bit character values, non-ASCII characters generally do not get translated by the conversion routines and the predicate routines almost always return false for non-ASCII characters.

A Note About Thread Safety: The routines in this module are all thread safe.

Note about stack diagrams: To conserve space, this documentation does not include a stack diagram for any functions because none of the current "chars" functions pass data on the stack (that is, only a return address appears on the stack).

Conversion Functions

The conversion functions in the chars module convert (alphabetic) characters to lowercase and to uppercase.

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. Character values in the range #128..#255 are returned as-is; no conversion is done on those characters even if in some language they could be interpreted as alphabetic characters.

HLA high-level calling sequence examples:

chars.toUpper( charVar );

mov( al, uppercaseCharVar );

chars.toUpper( al );

// AL now contains the uppercase version.

mov( chars.toUpper( ch ), uppercaseCharVar ); // Char is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.toUpper;

mov( al, uppercaseCharVar );

// Char to convert is in AL

call chars.toUpper;

// Result char is left in AL

mov( ch, al );

call chars.toUpper;

mov( al, uppercaseCharVar );

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. Character values in the range #128..#255 are returned as-is; no conversion is done on those characters even if in some language they could be interpreted as alphabetic characters.

HLA high-level calling sequence examples:

chars.toLower( charVar );

mov( al, lowercaseCharVar );

chars.toLower( al );

// AL now contains the lowercase version.

mov( chars.toLower( ch ), lowercaseCharVar ); // Char is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.toLower;

mov( al, lowercaseCharVar );

// Char to convert is in AL

call chars.toLower;

// Result char is left in AL

mov( ch, al );

call chars.toLower;

mov( al, lowercaseCharVar );

Predicates (Tests)

The following functions test characters in the seven-bit ASCII character set. These functions produce undefined results for other character sets. Note: Although the "returns" value for each of these functions is "AL", in reality these functions all return the Boolean result zero-extended to 32 bits in EAX.

chars.isAlpha( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isAlpha( charVar );

mov( al, booleanVar );

chars.isAlpha( al );

// AL now contains the Boolean result.

mov( chars.isAlpha( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isAlpha;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isAlpha;

// Result boolean is left in AL

mov( ch, al );

call chars.isAlpha;

mov( al, booleanVar );

chars.isUpper( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isUpper( charVar );

mov( al, booleanVar );

chars.isUpper( al );

// AL now contains the Boolean result.

mov( chars.isUpper( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isUpper;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isUpper;

// Result boolean is left in AL

mov( ch, al );

call chars.isUpper;

mov( al, booleanVar );

chars.isLower( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isLower( charVar );

mov( al, booleanVar );

chars.isLower( al );

// AL now contains the Boolean result.

mov( chars.isLower( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isLower;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isLower;

// Result boolean is left in AL

mov( ch, al );

call chars.isLower;

mov( al, booleanVar );

chars.isAlphaNum( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isAlphaNum( charVar );

mov( al, booleanVar );

chars.isAlphaNum( al );

// AL now contains the Boolean result.

mov( chars.isAlphaNum( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isAlphaNum;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isAlphaNum;

// Result boolean is left in AL

mov( ch, al );

call chars.isAlphaNum;

mov( al, booleanVar );

chars.isDigit( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isDigit( charVar );

mov( al, booleanVar );

chars.isDigit( al );

// AL now contains the Boolean result.

mov( chars.isDigit( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isDigit;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isDigit;

// Result boolean is left in AL

mov( ch, al );

call chars.isDigit;

mov( al, booleanVar );

chars.isXDigit( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isXDigit( charVar );

mov( al, booleanVar );

chars.isXDigit( al );

// AL now contains the Boolean result.

mov( chars.isXDigit( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isXDigit;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isXDigit;

// Result boolean is left in AL

mov( ch, al );

call chars.isXDigit;

mov( al, booleanVar );

chars.isGraphic( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isGraphic( charVar );

mov( al, booleanVar );

chars.isGraphic( al );

// AL now contains the Boolean result.

mov( chars.isGraphic( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isGraphic;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isGraphic;

// Result boolean is left in AL

mov( ch, al );

call chars.isGraphic;

mov( al, booleanVar );

chars.isSpace( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isSpace( charVar );

mov( al, booleanVar );

chars.isSpace( al );

// AL now contains the Boolean result.

mov( chars.isSpace( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isSpace;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isSpace;

// Result boolean is left in AL

mov( ch, al );

call chars.isSpace;

mov( al, booleanVar );

chars.isASCII( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isASCII( charVar );

mov( al, booleanVar );

chars.isASCII( al );

// AL now contains the Boolean result.

mov( chars.isASCII( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isASCII;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isASCII;

// Result boolean is left in AL

mov( ch, al );

call chars.isASCII;

mov( al, booleanVar );

chars.isCtrl( c:byte ); @returns( "AL" );

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

HLA high-level calling sequence examples:

chars.isCtrl( charVar );

mov( al, booleanVar );

chars.isCtrl( al );

// AL now contains the Boolean result.

mov( chars.isCtrl( ch ), booleanVar ); // Result is left in AL.

HLA low-level calling sequence examples:

mov( charVar, al );

call chars.isCtrl;

mov( al, booleanVar );

// Char to convert is in AL

call chars.isCtrl;

// Result boolean is left in AL

mov( ch, al );

call chars.isCtrl;

mov( al, booleanVar );

Console Display Control (console.hhf)

The HLA console module provides a reasonably portable way to control the console display under different operating systems. The routines in this module let you write "really-smart-terminal" console applications that behave in a similar fashion under different operating. The HLA console module routines take advantage of the Windows console API when running under Windows, they use the VT100/ANSI terminal control code sequences for other operating systems that use ANSI terminal control codes for console control. The routines in this module let you control the cursor position, erase selected portions of data from the screen, insert and delete characters and lines of text, scroll the screen, select display colors, and so on.

Note: this console module replaces the older HLA Standard Library console module that was Win32-specific. That earlier console module provided many features that are not present in the current console module because the Win32 console capabilities are quite a bit more sophisiticated than is possible with an ANSI terminal emulation. Older code that took advantage of these extra features will not be able to compile properly with this new console module. The original console module is still available in the HLA distribution under the "Examples" directory; new code, however, should not use that module; if you want to take advantage of the Win32 console capabilities, you should call the Win32 API routines directly.

Note: be sure to read the chapter on "Passing Parameters to Standard Library Routines" (parmpassing.rtf) before reading this chapter.

A Note About Thread Safety: The args module maintains a couple of static global variables that maintain the command-line values. Currently, these values apply to all threads in a process. You should take care when changing these values in threads. The command-line is a resource that must be shared amongst all threads in an application. If you write multi-threaded applications, it is your responsibility to serialize access to the command-line functions.

The Console Module Module

To use the date functions in your application, you will need to include one of the following statements at the beginning of your HLA application:

 

#include( "console.hhf" )

or

#include( "stdlib.hhf" )

Cursor Positioning Functions

The functions in this category reposition the cursor on the display

procedure console.gotoxy( x:dword; y:dword ); @pascal
procedure console.gotorc( r:dword; c:dword ); @stdcall

console.gotoxy and console.gotorc are actually the same function. The only difference between the two calls is that they (internally) swap their parameters before making the call to the function. Note that the "rc" in gotorc stands for "row/column" which is equivalent to saying "gotoyx". The console.gotorc function was provided because people intuitively prefer to specify the row (y) value as the first argument and the column (x) value as the second argument.

These functions position the cursor at the specified (x,y)/(c,r) coordinate on the screen.

HLA high-level calling sequence examples:

console.gotoxy( 0, 10 );

stdout.put( "Print this on line 10, column 0" nl );

console.gotoxy( 15, 0 );

stdout.put( "Print this on line 15, column 0" nl );

HLA low-level calling sequence examples:

pushd( 0 );

pushd( 10 );

call console.gotoxy; // row = 10, column = 0

// Note that console.gotorc uses the @stdcall calling convention, so

// it’s arguments are reversed from the declaration, that is, you

// push the same exact arguments you push for gotoxy (which makes

// sense, as both functions are actually the same code).

pushd( 0 );

pushd( 15 );

call console.gotorc;

procedure console.up();

console.up moves the cursor up one line. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure when the cursor is on the first line of the display. Some consoles scroll the screen down one line, others ignore the request.

HLA high-level calling sequence example:

console.up();

HLA low-level calling sequence example:

call console.up;

procedure console.nup( n:uns32);

console.nup moves the cursor up n lines. Because of the variation in terminal emulations out there, the results are undefined if this procedure attempts to move above the top line on the display. Some consoles scroll the screen down one line, others ignore the request.

HLA high-level calling sequence example:

console.nup( 5 );

HLA low-level calling sequence example:

pushd( 5 );

call console.nup;

procedure console.down();

console.down moves the cursor down one line. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure when the cursor is on the last line of the display. Some consoles scroll the screen up one line, others ignore the request..

HLA high-level calling sequence example:

console.down();

HLA low-level calling sequence example:

call console.down;

procedure console.ndown( n:uns32);

console.ndown moves the cursor down one line. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure when the cursor is on the last line of the display. Some consoles scroll the screen up one line, others ignore the request.

HLA high-level calling sequence example:

console.ndown( 5 );

HLA low-level calling sequence example:

pushd( 5 );

call console.ndown;

procedure console.left();

console.left moves the cursor up n lines. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure and it attempts to move the cursor before the first column on the line. Some consoles move the cursor to the end of the previous line, others ignore the request.

HLA high-level calling sequence example:

console.left();

HLA low-level calling sequence example:

call console.left;

procedure console.nleft( n:uns32);

console.nleft moves the cursor down one line. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure when the cursor is on the last line of the display. Some consoles scroll the screen up one line, others ignore the request.

HLA high-level calling sequence example:

console.nleft( 5 );

HLA low-level calling sequence example:

pushd( 5 );

call console.nleft;

procedure console.right();

console.right moves the cursor to the right one character position. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure when the cursor is at the last column of the display. Some consoles move the cursor to the beginning of the next line, others ignore the request.

HLA high-level calling sequence example:

console.right();

HLA low-level calling sequence example:

call console.right;

procedure console.nright( n:uns32);

console.nright moves the cursor down one line. Because of the variation in terminal emulations out there, the results are undefined if you execute this procedure and it attempts to move the cursor beyond the last column of the display. Some consoles move the cursor to the beginning of the next line, others ignore the request.

HLA high-level calling sequence example:

console.nright( 5 );

HLA low-level calling sequence example:

pushd( 5 );

call console.nright;

 

 

procedure console.saveCursor();

console.saveCursor saves the current cursor position in an internal variable. You can restore the cursor position via the console.restoreCursor call. Note that there is only one level of save available.

HLA high-level calling sequence example:

console.saveCursor();

HLA low-level calling sequence example:

call console.saveCursor;

 

procedure console.restoreCursor();

console.saveCursor restores the cursor to the position previously saved by console.saveCursor. Note that there is only one level of "save" available.

HLA high-level calling sequence example:

console.restoreCursor();

HLA low-level calling sequence example:

call console.restoreCursor;

 

Console Clearing Functions

The functions in this category clear portions (or all) of the display.

procedure console.cls();
procedure console.home();

console.cls and console.home are actually the same function. These procedures clear the screen and move the cursor to the home (0,0) position.

HLA high-level calling sequence examples:

console.cls();

console.home();

HLA low-level calling sequence examples:

call console.cls;

call console.home;

procedure console.clrToEOLN();

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

HLA high-level calling sequence examples:

console.clrToEOLN();

HLA low-level calling sequence examples:

call console.clrToEOLN;

procedure console.clrToBOLN();

console.clrToBOLN clears the text (by writing spaces) from the current cursor position to the beginning of the line that the cursor is on.

HLA high-level calling sequence examples:

console.clrToBOLN();

HLA low-level calling sequence examples:

call console.clrToBOLN;

procedure console.clrLn();

console.clrLn clears the line that the cursor is on by writing spaces to that line. This does not delete the line from the screen, it only clears the characters from the line.

HLA high-level calling sequence examples:

console.clrLn();

HLA low-level calling sequence examples:

call console.clrLn;

procedure console.clrToEOScrn();

console.clrToEOScrn clears the text (by writing spaces) from the current cursor position to the end of the screen.

HLA high-level calling sequence examples:

console.clrToEOScrn();

HLA low-level calling sequence examples:

call console.clrToEOScrn;

procedure console.clrToBOScrn();

console.clrToBOScrn clears the text (by writing spaces) from the current cursor position to the beginning of the screen.

HLA high-level calling sequence examples:

console.clrToBOScrn();

HLA low-level calling sequence examples:

call console.clrToBOScrn;

Character Insertion/Removal Functions

The functions in this category insert and delete characters on the console display.

procedure console.insertChar();

console.insertChar inserts room for a single character by shifting the characters under the cursor and to the right of the cursor to the right one position. The vacated position is filled with a space. The last character on the line is lost.

HLA high-level calling sequence examples:

console.insertChar();

HLA low-level calling sequence examples:

call console.insertChar;

procedure console.insertChars( n:dword );

console.insertChars 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.

HLA high-level calling sequence examples:

console.insertChars( n );

HLA low-level calling sequence examples:

pushd( n );

call console.insertChars;

procedure console.insertLine();

console.insertLine 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.

HLA high-level calling sequence examples:

console.insertLine();

HLA low-level calling sequence examples:

call console.insertLine;

procedure console.insertLines( n:dword );

console.insertLines 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.

HLA high-level calling sequence examples:

console.insertLines( 5 );

HLA low-level calling sequence examples:

pushd( 5 );

call console.insertLines;

procedure console.deleteChar();

console.deleteChar deletes the character under the cursor by shifting the characters after the cursor one position to the left. The last character position at the end of the line is filled with a blank.

HLA high-level calling sequence examples:

console.deleteChar();

HLA low-level calling sequence examples:

call console.deleteChar;

procedure console.deleteChars( n:dword );

console.deleteChars 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.

HLA high-level calling sequence examples:

console.deleteChars( n );

HLA low-level calling sequence examples:

pushd( n );

call console.deleteChars;

procedure console.deleteLine();

console.deleteLine 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.

HLA high-level calling sequence examples:

console.deleteLine();

HLA low-level calling sequence examples:

call console.deleteLine;

procedure console.deleteLines( n:dword );

console.deleteLines procedure deletes n lines at and below the current cursor position. The vacated lines at the bottom of the screen are filled with blanks.

HLA high-level calling sequence examples:

console.deleteLines( 5 );

HLA low-level calling sequence examples:

pushd( 5 );

call console.deleteLines;

Console Scrolling

The functions in this category scroll the screen up and down.

procedure console.scrollUp( );

console.scrollUp scrolls the entire screen up one line.

HLA high-level calling sequence examples:

console.scrollUp();

HLA low-level calling sequence examples:


call console.scrollUp;

procedure console.scrollDown( );

console.scrollDown scrolls the entire screen down one line.

HLA high-level calling sequence examples:

console.scrollDown();

HLA low-level calling sequence examples:


call console.scrollDown;

Console Output Colors

The functions in this category control the color of the characters printed on the display.

procedure console.setAttrs( foreground:uns32; background:uns32 );

console.setAttrs 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. The foreground parameter sets the color for the text characters, the background parameter sets the color of the background area of each character cell.

The console module defines the following constants that represent the corresponding colors:

console.black := 0;

console.red := 1;

console.green := 2;

console.yellow := 3;

console.blue := 4;

console.magenta := 5;

console.cyan := 6;

console.white := 7;

HLA high-level calling sequence examples:

console.setAttrs( console.yellow, console.blue );

HLA low-level calling sequence examples:


pushd( console.yellow );

pushd( console.blue );

call console.setAttrs;

Conversions (conv.hhf)

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

Note: be sure to read the chapter on "Passing Parameters to Standard Library Routines" (parmpassing.rtf) before reading this chapter.

Most string conversion routines take two forms: one version that writes data to an existing (preallocated) string and one that automatically allocates storage for a new string on the heap. Those functions that automatically allocate storage generally have a name that begins with "a_" (for allocate) whereas the functions that use a preallocated string do not have such a prefix. For example, the conv.h8ToStr function converts an 8-bit integer to a string using hexadecimal representation and stores the result in a preallocated string object. The conv.a_h8ToStr function converts an 8-bit value to a (hexadecimal) string that it allocates on the heap; conv.a_h8ToStr returns a pointer to that string in the EAX register.

An important point to keep in mind is that string variables are pointers. Unless you call a function that allocates storage for a string (i.e., one of the "a_..." functions), you must ensure that you’ve allocated sufficient storage to hold any string result the function produces. Failure to do so will produce a memory access error, null pointer reference error, or string overflow error. Remember, simply declaring a string variable does not automatically allocate storage for any string data; the declaration only allocates storage for the string pointer. You must call a function such as str.alloc to actually allocate the string data.

Note about stack diagrams: this documentation includes stack diagrams for those functions that pass parameters on the stack. To conserve space, this documentation does not include a stack diagram for any function that does not pass data on the stack (that is, only a return address appears on the stack).

A Note About the FPU: The Standard Library code makes occasional use of the FPU, particularly when converting between real and string formats and when computing certain mathematical functions. You should exercise caution when using MMX instructions in a program that makes use of the Standard Library. In particular, you should ensure that you are always in FPU mode (by executing an EMMS instruction) after you are finished using MMX instructions. Better yet, you should avoid the MMX instruction set altogether and use the improved SSE instruction set that accomplishes the same tasks (and doesn’t disturb the FPU).

Buffer vs. String Conversions

The Standard Library supports two generic types of numeric-to-string conversions – output to a string variable (an "HLA string" object) and output to a memory buffer. The string conversion routines are the safest to use, but the buffer conversion routines are a bit more general.

If you’re working with HLA-style string objects, then using the conversion-to-string functions make the most sense because you get to take full advantage of range checking and other facilities that are possible with the string format. Furthermore, you can use the Standard Library string manipulation functions to process such strings once the conversion is complete.

There are two drawbacks to the string conversion routines (versus the buffer conversion routines):

Perhaps the most common example of a non-HLA-string data type you’ll encounter is the simple zero-terminated string (the word "simple" appears here because HLA strings are zero-terminated and you can often use them whenever you need a zero-terminated string). Consider the conv.i32ToBuf routine that converts a 32-bit signed integer to the corresponding sequence of characters. This function stores that characters at the memory address passed in EDI and upon return EDI points at the first byte beyond the converted sequence, e.g.,

// Stores the characters "12345" at [edi]

conv.i32ToBuf( 12345, [edi] );

Upon return from this function, EDI will contain a value that is 5 greater than it was upon entry, and the five memory locations that EDI has skipped over will contain the characters "12345". Note that this string is not zero-terminated, but you can easily zero-terminate it by storing a zero byte at the location where EDI points upon return from the function:

// Stores the characters "12345" at [edi] and zero

// terminates the string.

conv.i32ToBuf( 12345, [edi] );

mov( 0, (type byte [edi]));

As a final example, suppose you want to build up an HLA style string by concatenating two converted strings together. You could do something like the following:

// Produces " 12345 67890" in fullStr

conv.i32ToStr( 12345, 6, ‘ ‘, leftStr );

conv.i32ToStr( 67890, 7, ‘ ‘, rightStr

str.cat( leftStr, rightStr, fullStr );

The only problem with this approach is that there is unnecessary string processing (e.g., data copying) taking place. If efficiency is paramount, and you don’t need the intermediate conversions (leftStr and rightStr), then you can do this sequence a little bit faster by generating the two strings in place as follows:

mov( fullStr, edi ); // Point EDI at start of string data

mov( edi, ebx ); // Save to compute length

conv.i32ToBuf( 12345, 6, ‘ ‘ ); // Store " 12345" at fullStr

conv.i32ToBuf( 67890, 7, ‘ ‘ ); // Store " 67890" at fullStr+6

mov( 0, (type byte [edi])); // HLA strings must be zero terminated

sub( fullStr, edi ); // Compute string length

mov( edi, (type str.strRec [ebx]).length ); // Save new length.

As the number of objects you append to the string increases, this scheme becomes even more efficient than using the str.cat approach. The code above, of course, assumes that you’ve already allocated a sufficient amount of string storage for the leftStr, rightStr, and fullStr string variables.

Conversion Format Control

The following functions control the numeric conversion process.

Underscore Control

When converting numeric data types to strings, the standard library offers the option of inserting underscores at appropriate places in the numbers (i.e., where you would normally expect a digit separator to appear, such as a comma [U.S.] or period [Europe]). The conv.setUnderscores and conv.getUnderscores functions control the operation of this feature.

The standard library conversion functions will inject underscores into hexadecimal, unsigned integers, and signed integers if the feature is enabled. For hexadecimal output the standard library conversion routines will emit an underscore between every fourth and fifth digit, starting with the L.O. digit (e.g., 1234_5678). For decimal integers (signed or unsigned), the conversion routines emit an underscore between each third and fourth digit starting with the L.O. digit (e.g., 123_456_789).

Note that the conversion routines do not emit underscores into conversions of floating-point/real values.

Thread Issues: Because the standard library maintains the internal underscore flag as a static object there will be some problems if you attempt to read and set the underscore flag in multiple threads running in the same address space. In particular, if you read the underscores flag and save it, set it to a different value, do some conversions, and then restore the underscores flag its original value, it is quite possible that another thread could do some conversions between those two points and produce incorrect output. Indeed, it would even be possible for half the number to contain underscores and the other half not contain underscores, depending on where the system interrupts the second thread. The current library code does not address this issue because the cost is very high to solve a problem that almost never occurs (most assembly applications are single-threaded). However, if you are writing a multi-threaded application, you should note that constantly changing the underscores flag is not a good idea – you should try to set the flag once, at the beginning of your program, and leave it alone throughout the program’s execution. If you must change the underscore flag setting on a regular basis within a multi-threaded application, you should put appropriate locks around all calls to conversion routines (and those routines, such as the I/O routines, that call the conversion code) to protect the settings.

Current plans are to make the Standard Library thread-safe when the threads module is added to the library.

conv.setUnderscores( onOff: boolean );

The conv.setUnderscores function lets you enable or disable the emission of underscores in numeric values. Passing true enables underscore emission, passing false disables it.

For efficiency reasons, the standard library routines always pass all parameters as a multiple of four bytes. The onOff Boolean parameter consumes the L.O. byte of the double word passed on the stack. The conv.setUnderscore routine ignores the H.O. three bytes of the value passed for this parameter, though by convention (to make debugging a little easier) you should try to pass zeros in the H.O. three bytes if it is not inconvenient to do so.

When passing a Boolean constant, you should simply push the dword containing the value true (1) or false (0), e.g,

 

pushd( true );

call conv.setUnderscores;

.

.

.

pushd( false );

call conv.setUnderscores;

When passing the Boolean value in one of the 8-bit registers AL, BL, CL or DL, you should simply push the 32-bit register that holds the 8-bit register, e.g.,

 

push( eax ); // Pushes AL onto the stack

call conv.setUnderscores;

push( ebx ); // Pushes BL onto the stack

call conv.setUnderscores;

Note that this trick does not apply to the AH, BH, CH, or DH registers. The best code to use when you need to push these registers is to drop the stack down by four bytes and then move the desired register into the memory location you’ve just created on the stack, e.g.,

 

sub( 4, esp );

mov( AH, [esp] ); // Pushes AH onto the stack

call conv.setUnderscores;

.

.

.

sub( 4, esp );

mov( BH, [esp] ); // Pushes BH onto the stack

call conv.setUnderscores;

Here’s another way you can accomplish this (a little slower, but leaves zeros in the H.O. three bytes):

 

pushd( 0 );

mov( CH, [esp] ); // Pushes CH onto the stack

call conv.setUnderscores;

When passing a Boolean variable, you should try to push the Boolean value and the following three bytes, using code like the following (HLA syntax):

 

pushd( (type dword boolVar ));

call conv.setUnderscores;

There is one drawback to the approach above. In three very rare cases the code above could cause a segmentation fault. If the Boolean variable is located on the last three bytes of a page in memory (4,096 bytes) and the next memory page is not readable, the system will generate a fault if you attempt to push all four bytes. In such a case, the next best solution, if a register is available, is to move the Boolean value into AL, BL, CL, or DL and push the corresponding 32-bit register. If no registers are available, then you can write code like the following:

 

push( eax );

push( eax );

mov( boolVar, al );

mov( al, [esp+4] );

pop( eax );

call conv.setUnderscores;

 

This code is ugly and slightly inefficient, but it will always work (assuming, of course, you don’t get a stack overflow).

The HLA compiler will generate code similar to this last example if you pass a boolean variable as the actual parameter to conv.setUnderscores:

 

conv.setUnderscores( boolVar );

Therefore, if efficiency is a concern to you, you should try to load the Boolean variable (boolVar in this example) into AL, BL, CL, or DL prior to calling conv.setUnderscores, e.g.,

 

mov( boolVar, al );

conv.setUnderscores( al );


 

conv.getUnderscores; @returns( "eax" );

You can test the current state of the underscore conversion by calling conv.getUnderscores. This function call returns the boolean result in EAX (true means underscores will be output); AL will contain the actual Boolean value and the H.O. three bytes of EAX will all contain zero. This routine does not have any parameters.

The following example demonstrates how to preserve the value of the internal underscores flag across some section of code:

 

conv.getUnderscores();

mov( al, saveUnderscores );

conv.setUnderscores( true );

.

.

.

mov( saveUnderscores, al );

conv.setUnderscores( al );

Note: Do not try to access the internal underscores flag directly in your program. Always use the conv.setUnderscores and conv.getUnderscores accessor functions. In a future version of the Standard Library, the internal representation of this flag will change and any code that accesses it directly will break at that point. However, if you call conv.setUnderscores and conv.getUnderscores, you’re guaranteed that the internal implementation will be hidden from you and your code will not fail when the internal representation changes.

Delimiter Control

During the conversion from string to a numeric form, the conversion routines will skip over zero or more delimiter characters and then process all numeric digits (including hexadecimal digits, if doing a hexadecimal conversion) up to the end of string or the first delimiter character it finds. If a conversion function encounters a value that is not a valid digit or delimiter character, it will raise a conversion exception or an illegal character exception By default, the delimiter characters are members of the following set:

Delimiters: cset :=

{

#0, // End of string

#9, // Tab

#10, // Line feed

#13, // Carriage return

' ' , // Space

',', // Comma

';', // Semicolon

':' // Colon

};

While this default delimiters character set is probably appropriate for most applications, some programmers may want to add or remove characters from this set based on their application requirements. The standard library provides two routines that provide access to this internal character set object: conv.getDelimiters and conv.setDelimiters. You should always use these routines to access this character set object rather than accessing it directly (as an external object).

Thread Issues: Because the standard library maintains the internal delimiters character set as a static object there will be some problems if you attempt to read and set the delimiters in multiple threads running in the same address space. In particular, if you read the delimiters character set and save it, set it to a different value, do some conversions, and then restore the delimiters to the original value, it is quite possible that another thread could do some conversions between those two points and produce incorrect. The current library code does not address this issue because the cost is very high to solve a problem that almost never occurs (most assembly applications are single-threaded). However, if you are writing a multi-threaded application, you should note that constantly changing the delimiters character set is not a good idea – you should try to set the delimiters once, at the beginning of your program, and leave them alone throughout the program’s execution. If you must change the delimiters character set on a regular basis within a multi-threaded application, you should put appropriate locks around all calls to conversion routines (and those routines, such as the I/O routines, that call the conversion code) to protect the settings.

Current plans are to make the delimiters character set object thread-safe when the processes module is added to the library.

Note: Do not try to access the internal delimiters character set directly in your program. Always use the conv.setDelimiters and conv.getDelimiters accessor functions. In a future version of the Standard Library, the internal representation of this character set will change and any code that accesses it directly will break at that point. However, if you call conv.setDelimiters and conv.getDelimiters , you’re guaranteed that the internal implementation will be hidden from you and your code will not fail when the internal representation changes.

conv.getDelimiters( var Delims: cset );

The conv.getDelimiters routine returns the current delimiters character set in the variable you pass by reference as the parameter. The Delims parameter is passed by reference (that is, you pass the address of the actual cset variable to receive the result). The following are examples of typical HLA high-level invocations of this routine looks like this:

conv.getDelimiters( saveDelims );

// EDI points at the delimiter cset:

conv.getDelimiters( [edi] );

// ptrtoDelims is a dword/pointer variable that contains

// the address of a cset object:

conv.getDelimiters( val ptrToDelims );

To call conv.getDelimiters using a low-level assembly syntax, you must push the address of a cset variable object onto the stack and then call the conv.getDelimiters function:

// saveDelims_s is a variable declared in the static/storage section:

pushd( &saveDelims_s );

call conv.getDelimiters;

// saveDelims_v is a variable declared in the var section or

// is a parameter:

lea( eax, saveDelims_v );

push( eax );

call conv.getDelimiters;

// Alternative call passing saveDelims_v if no 32-bit registers

// are available (this code assumes that EBP points at the current

// activation record/stack frame that contains saveDelims_v):

push( ebp );

add( @offset( saveDelims_v ), (type dword [esp] ));

call conv.getDelimiters;

// Low-level call assuming a 32-bit register (esi in this case)

// contains the address of the cset:

push( esi );

call conv.getDelimiters;

// Low-level call assuming a dword or pointer variable contains the

// address of the cset that will receive the delimiter character set:

push( ptrToDelims );

call conv.getDelimiters;

conv.setDelimiters( Delims: cset )

The conv.setDelimiters function lets you change the value of the internal delimiter character set. It requires a 16-byte character set parameter (passed by value) and will copy the value of this parameter to the internal character set variable. Note that this routine makes a copy of the actual parameter you pass it. If you pass an character set variable as the actual parameter, future changes to that character set variable (without a corresponding call to conv.setDelimiters) will have no effect on the internal delimiters character set that the standard library routines use. The following examples are typical HLL style calls to this function:

conv.setDelimiters( {‘ ‘, ‘,’} ); // Pass in a literal constant

conv.setDelimiters( csetVar ); // Pass in a cset variable’s value

conv.setDelimiters( [edx] ); // EDX points at a cset variable

To call conv.setDelimiters using a low-level calling sequence, you’d first push the 16 bytes associated with the character set object (H.O. dword first down to the L.O. dword) and then call the conv.setDelimiters function. Here are some examples:

// Push the literal cset constant {‘ ‘, ‘,’} onto the stack:

pushd( 0 ); // Must manually convert cset to a sequence of

pushd( $1001 ); // four dwords (ugh!). Note: ORD( ‘ ‘ ) = $20

pushd( 0 ); // and ORD(‘,’) = $2C so bit positions $20 and

pushd( 0 ); // $2c must contain ‘1’s, zeros everywhere else.

call conv.setDelimiters;

// Push the cset variable "saveDelims" onto the stack and

// call conv.setDelimiters:

push( (type dword saveDelims[12]));

push( (type dword saveDelims[8]) );

push( (type dword saveDelims[4]) );

push( (type dword saveDelims[0]) );

call conv.setDelimiters.

// If manually converting a literal cset constant to the equivalent

// numeric values isn’t your thing, you can also do the following

// (though this is slightly less efficient):

readonly

spaceAndComma :cset := {‘ ‘, ‘,’ };

endreadonly

push( (type dword spaceAndComma [12]));

push( (type dword spaceAndComma [8]) );

push( (type dword spaceAndComma [4]) );

push( (type dword spaceAndComma [0]) );

call conv.setDelimiters.

If you insist on using low-level calling sequences to call the conv.setDelimiters routine, you might want to consider writing a macro that will automatically push a literal cset constant for you. Here is a set of HLA macros that will do this task:

program t;

// dword_n extracts the nth dword (0, 1, 2, 3) from a

// 16-byte object such as a character set. cst must be

// a cset constant value (or an lword), n must be an

// integer constant in the range 0..3.

#macro dword_n( cst, n );

(

(@byte( @lword(cst), n*4+3) << 24)

| (@byte( @lword(cst), n*4+2) << 16)

| (@byte( @lword(cst), n*4+1) << 8)

| (@byte( @lword(cst), n*4+0) << 0)

)

#endmacro

// pushcset pushes the cset constant passed as an argument

// onto the CPU’s stack. H.O. dword is pushed first, L.O.

// dword is pushed last.

#macro pushcset( cst );

// Push the four dwords that make up a cset constant:

pushd( dword_n( cst, 3 ) );

pushd( dword_n( cst, 2 ) );

pushd( dword_n( cst, 1 ) );

pushd( dword_n( cst, 0 ) );

#endmacro

begin t;

// Example of pushcset invocation:

pushcset( {' ', ','} );

end t;

Here is an example using conv.getDelimiters and conv.setDelimiters that demonstrates how to temporarily change the delimiters character set and the restore its value:

var

saveDelims :cset;

.

.

.

conv.getDelimiters( saveDelims );

conv.setDelimiters( { ‘!’, ‘@’ } )l

.

.

.

conv.setDelimiters( saveDelims );

Hexadecimal Conversions

The standard library hexadecimal routines convert numeric values of varying sizes (8, 16, 32, 64, 80, and 128 bits) into a string of characters holding the hexadecimal representation of those values. The hexadecimal output routines can be broken down into the following categories:

Output type (string or sequence of characters to a buffer)

Fill type (no fill; fill with zeros to

a standardized length, based on data type; fill with a caller-specified character to a caller-specified length).

Internal Routines

The conversions module in the standard library contains several routines that are intended for internal use only. Generally, you should not call these routines directly from your application programs. These routines all have names that begin with an underscore. The internal hexadecimal conversion routines include: _hexTobuf64Size, _hexTobuf80, _hexTobuf80Size, _hexTobuf128, _hexTobuf128Size, _hexTobuf32, _hexTobuf32Size, and _hexTobuf64.

Hexadecimal Numeric Size Functions

The hexadecimal conversion size functions return the number of digit print positions required by the conversion of a numeric value to a hexadecimal string. There are two sets of six routines that compute the output size: one set computes the fixed-size width and the other set computes a varying-sized width.

Fixed Size Hexadecimal Size Functions

It is common practice to display hexadecimal values using exactly one digit for each nibble of the corresponding data type, including leading zeros, as necessary. The common fixed sizes are byte=2, word=4, dword=8, qword=16, tbyte=20, and lword=32. With underscore output enabled (see conv.setUnderscores) these values are byte=2, word=4, dword=9, qword=19, tb=24, and lword=39. Because these numbers are fixed (at least, for a given underscores flag setting) there are only three reasons for calling these functions:

  • You don’t know the underscores flag setting when executing a particular section of code (which can affect the output size of dword, qword, tbyte, and lword objects), or,
  • You’re generating a call to these functions via some macro that is given a function name like "putb" or "puth8" and you’re manually constructing the size function to call via the assembler’s compile-time language. or,
  • You’re writing generic code and you want to make it easy to modify the code in the future.

procedure conv.bSize( b:byte in al ); @returns( "eax" );

This function always returns 2 because the fixed output size of a byte is two hexadecimal digits (two 4-bit nibbles).

HLA high-level calling sequence examples:

conv.bSize( byteVariable );

conv.bSize( <byte register> ); // al, ah, bl, bh, cl, ch, dl, dh

conv.bSize( <constant> ); // Must fit into eight bits

Because conv.bSize passes its input parameter in the AL register, any form of the high-level calling sequence except "conv.bSize( al );" will automatically generate an instruction of the form "mov(<operand>,al);". Therefore, if at all possible, you should try to have the value whose size you wish to compute already sitting in the AL register and pass AL as the parameter to conv.bSize.

HLA low-level calling sequence examples:

mov( byteVariable, al );

call conv.bSize;

mov( <byte register>, al ); // ah, bl, bh, cl, ch, dl, dh

call conv.bSize;

mov( <constant>, al );

call conv.bSize;

procedure conv.wSize( w:word in ax ); @returns( "eax" );

This function always returns 4 because the "natural" size of a word is four hexadecimal digits (four 4-bit nibbles).

HLA high-level calling sequence examples:

conv.wSize( wordVariable );

conv.wSize( <word register> ); // ax, bx, cx, dx, bp, sp, si, di

conv.wSize( <constant> ); // Must fit into 16 bits

Because conv.wSize passes its input parameter in the AX register, any form of the high-level calling sequence except "conv.wSize( ax );" will automatically generate an instruction of the form "mov(<operand>,ax);". Therefore, if at all possible, you should try to have the value whose size you wish to compute already sitting in the AX register and pass AX as the parameter to conv.wSize.

HLA low-level calling sequence examples:

mov( wordVariable, ax );

call conv.wSize;

mov( <word register>, ax ); // bx, cx, dx, bp, sp, si, di

call conv.wSize;

mov( <constant>, ax );

call conv.wSize;

procedure conv.dSize( d:dword in eax ); @returns( "eax" );

This function returns 8 if the internal underscores flag is false, 9 if it is true, because the "natural" size of a double word is eight hexadecimal digits (eight 4-bit nibbles).

HLA high-level calling sequence examples:

conv.dSize( dwordVariable );

conv.dSize( <dword register> ); // eax, ebx, ecx, edx,

//ebp, esp, esi, edi

conv.dSize( <constant> ); // Must fit into 32 bits

Because conv.dSize passes its input parameter in the EAX register, any form of the high-level calling sequence except "conv.dSize( eax );" will automatically generate an instruction of the form "mov(<operand>,eax);". Therefore, if at all possible, you should try to have the value whose size you wish to compute already sitting in the EAX register and pass EAX as the parameter to conv.dSize.

HLA low-level calling sequence examples:

mov( dwordVariable, eax );

call conv.dSize;

mov( <dword register>, eax ); // ebx, ecx, edx, ebp, esp, esi, edi

call conv.dSize;

mov( <constant>, eax );

call conv.dSize;

procedure conv.qSize( q:qword ); @returns( "eax" );

This function returns 16 if the internal underscores flag is false, 19 if it is true, because the "natural" size of a quad word is 16 hexadecimal digits (16 4-bit nibbles) and there are four groups of four digits with underscores between them (if the underscores flag contains true).

HLA high-level calling sequence examples:

conv.qSize( qwordVariable );

conv.qSize( <constant> ); // Must fit into 64 bits

HLA low-level calling sequence examples:

// Passing a qword variable

push( (type dword qwordVar[4]) ); // Push H.O. dword first

push( (type dword qwordVar[0]) ); // Push L.O. dword second

call conv.qSize;

// Example where 64-bit value is held in EDX:EAX

push( edx ); // Push H.O. dword first

push( eax ); // Push L.O. dword second

call conv.qSize;

// Passing a qword constant:

pushd( <qword constant> >> 32 ); // Push H.O. dword

pushd( <qword constant> & $FFFF_FFFF ); // Push L.O. dword

call conv.qSize;

procedure conv.tbSize( tb:tbyte ); @returns( "eax" );

This function returns 20 if the internal underscores flag is false, 24 if it is true, because the "natural" size of a ten-byte word is 20 hexadecimal digits (20 4-bit nibbles) and there are five groups of four digits with underscores between them (if the underscores flag contains true).

HLA high-level calling sequence examples:

conv.tbSize( tbyteVariable );

conv.tbSize( <constant> ); // Must fit into 80 bits

HLA low-level calling sequence examples:

// Passing a tbyte variable

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVar[8]) ); // Push H.O. word

push( (type dword tbyteVar[4]) ); // Push middle dword second

push( (type dword tbyteVar[0]) ); // Push L.O. dword third

call conv.tbSize;

// Passing a tbyte constant:

pushw( 0 ); // Must pad to 12 bytes.

pushw( <tbyte constant> >> 64 ); // Push H.O. word

pushd( (<tbyte constant> >> 32) & $FFFF_FFFF ); // Push middle dword

pushd( <tbyte constant> & $FFFF_FFFF ); // Push L.O. dword

call conv.tbSize;

procedure conv.lSize( l:lword ); @returns( "eax" );

This function returns 32 if the internal underscores flag is false, 39 if it is true, because the "natural" size of an lword is 32 hexadecimal digits (32 4-bit nibbles) and there are eight groups of four digits with underscores between them (if the underscores flag contains true).

HLA high-level calling sequence examples:

conv.lSize( lwordVariable );

conv.lSize( <constant> ); // Must fit into 128 bits

HLA low-level calling sequence examples:

// Passing an lword variable

push( (type dword lwordVar[12])); // Push H.O. dword first

push( (type dword tbyteVar[8]) );

push( (type dword tbyteVar[4]) );

push( (type dword tbyteVar[0]) ); // Push L.O. dword last

call conv.lSize;

// Passing a lword constant:

pushd( <lword constant> >> 96 ); // Push H.O. dword first

pushw( (<lword constant> >> 64) & $FFFF_FFFF );

pushd( (<lword constant> >> 32) & $FFFF_FFFF );

pushd( <lword constant> & $FFFF_FFFF ); // Push L.O. dword last

call conv.lSize;

Standard Hexadecimal Size Functions

The h8Size, h16Size, h32Size, h64Size, h80Size, and h128Size routines compute the minimum number of output hexadecimal digits (with no leading zeros). These functions return a count that includes space for underscores if the internal underscores flag contains true (see conv.setUnderscores for details).

procedure conv.h8Size( b:byte in al ); @returns( "eax" );

This function returns the number of print positions required by the conversion of the value in AL to a string of hexadecimal digits. The return value is always 1 or 2 (as a single byte never consumes more than two hexadecimal digits). Note that the internal underscores flag setting does not affect the return result because the conversion routines never inject an underscore into a hexadecimal conversion unless there are at least five output digits.

HLA high-level calling sequence examples:

conv.h8Size( byteVariable );

conv.h8Size( <byte register> ); // al, ah, bl, bh, cl, ch, dl, dh

conv.h8Size( <constant> ); // Must fit into eight bits

Because conv.h8Size passes its input parameter in the AL register, any form of the high-level calling sequence except "conv.h8Size( al );" will automatically generate an instruction of the form "mov(<operand>,al);". Therefore, if at all possible, you should try to have the value whose size you wish to compute already sitting in the AL register and pass AL as the parameter to conv.h8Size.

HLA low-level calling sequence examples:

mov( byteVariable, al );

call conv.h8Size;

mov( <byte register>, al ); // ah, bl, bh, cl, ch, dl, dh

call conv.h8Size;

mov( <constant>, al );

call conv.h8Size;

procedure conv.h16Size( w:word in ax ); @returns( "eax" );

This function returns the number of print positions required by the conversion of the value in AX to a string of hexadecimal digits. The return value is always 1, 2, 3, or 4 (as a single word never requires more than four hexadecimal digits). Note that the internal underscores flag setting does not affect the return result because the conversion routines never inject an underscore into a hexadecimal conversion unless there are at least five output digits.

HLA high-level calling sequence examples:

conv.h16Size( wordVariable );

conv.h16Size( <word register> ); // ax, bx, cx, dx, bp, sp, si, di

conv.h16Size( <constant> ); // Must fit into 16 bits

Because conv.h16Size passes its input parameter in the AX register, any form of the high-level calling sequence except "conv.h16Size( ax );" will automatically generate an instruction of the form "mov(<operand>,ax);". Therefore, if at all possible, you should try to have the value whose size you wish to compute already sitting in the AX register and pass AX as the parameter to conv.h16Size.

HLA low-level calling sequence examples:

mov( wordVariable, ax );

call conv.h16Size;

mov( <word register>, ax ); // bx, cx, dx, bp, sp, si, di

call conv.h16Size;

mov( <constant>, ax );

call conv.h16Size;

procedure conv.h32Size( s:dword in eax ); @returns( "eax" );

This function returns the number of print positions required by the conversion of the value in EAX to a string of hexadecimal digits. The return value is always in the range 1-8 if the internal underscores flag is false, it is in the range 1-9 if the internal underscores flag is true (see the discussion of conv.setUnderscores for details).

HLA high-level calling sequence examples:

conv.h32Size( dwordVariable );

conv.h32Size( <dword register> ); // eax, ebx, ecx, edx,

//ebp, esp, esi, edi

conv.h32Size( <constant> ); // Must fit into 32 bits

Because conv.h32Size passes its input parameter in the EAX register, any form of the high-level calling sequence except "conv.h32Size( eax );" will automatically generate an instruction of the form "mov(<operand>,eax);". Therefore, if at all possible, you should try to have the value whose size you wish to compute already sitting in the EAX register and pass EAX as the parameter to conv.h32Size.

HLA low-level calling sequence examples:

mov( dwordVariable, eax );

call conv.h32Size;

mov( <dword register>, eax ); // ebx, ecx, edx, ebp, esp, esi, edi

call conv.h32Size;

mov( <constant>, eax );

call conv.h32Size;

procedure conv.h64Size( q:qword ); @returns( "eax" );

This function returns the number of print positions required by the conversion of the value passed in q to a string of hexadecimal digits. The return value is always in the range 1-16 if the internal underscores flag is false, it is in the range 1-19 if the internal underscores flag is true (see the discussion of conv.setUnderscores for details).

HLA high-level calling sequence examples:

conv.h64Size( qwordVariable );

conv.h64Size( <constant> ); // Must fit into 64 bits

HLA low-level calling sequence examples:

// Passing a qword variable

push( (type dword qwordVar[4]) ); // Push H.O. dword first

push( (type dword qwordVar[0]) ); // Push L.O. dword second

call conv.h64Size;

// Example where 64-bit value is held in EDX:EAX

push( edx ); // Push H.O. dword first

push( eax ); // Push L.O. dword second

call conv.h64Size;

// Passing a qword constant:

pushd( <qword constant> >> 32 ); // Push H.O. dword

pushd( <qword constant> & $FFFF_FFFF ); // Push L.O. dword

call conv.h64Size;

procedure conv.h80Size( tb:tbyte ); @returns( "eax" );

This function returns the number of print positions required by the conversion of the value passed in tb to a string of hexadecimal digits. The return value is always in the range 1-20 if the internal underscores flag is false, it is in the range 1-24 if the internal underscores flag is true (see the discussion of conv.setUnderscores for details).

HLA high-level calling sequence examples:

conv.h80Size( tbyteVariable );

conv.h80Size( <constant> ); // Must fit into 80 bits

HLA low-level calling sequence examples:

// Passing a tbyte variable

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVar[8]) ); // Push H.O. word

push( (type dword tbyteVar[4]) ); // Push middle dword second

push( (type dword tbyteVar[0]) ); // Push L.O. dword third

call conv.h80Size;

// Passing a tbyte constant:

pushw( 0 ); // Must pad to 12 bytes.

pushw( <tbyte constant> >> 64 ); // Push H.O. word

pushd( (<tbyte constant> >> 32) & $FFFF_FFFF ); // Push middle dword

pushd( <tbyte constant> & $FFFF_FFFF ); // Push L.O. dword

call conv.h80Size;

 

procedure conv.h128Size( l:lword ); @returns( "eax" );

This function returns the number of print positions required by the conversion of the value passed in l to a string of hexadecimal digits. The return value is always in the range 1-32 if the internal underscores flag is false, it is in the range 1-39 if the internal underscores flag is true (see the discussion of conv.setUnderscores for details).

HLA high-level calling sequence examples:

conv.h128Size( lwordVariable );

conv.h128Size( <constant> ); // Must fit into 128 bits

HLA low-level calling sequence examples:

// Passing an lword variable

push( (type dword lwordVar[12])); // Push H.O. dword first

push( (type dword tbyteVar[8]) );

push( (type dword tbyteVar[4]) );

push( (type dword tbyteVar[0]) ); // Push L.O. dword last

call conv.h128Size;

// Passing a lword constant:

pushd( <lword constant> >> 96 ); // Push H.O. dword first

pushw( (<lword constant> >> 64) & $FFFF_FFFF );

pushd( (<lword constant> >> 32) & $FFFF_FFFF );

pushd( <lword constant> & $FFFF_FFFF ); // Push L.O. dword last

call conv.h128Size;

Hexadecimal Numeric to Buffer Conversions

The hexadecimal numeric to buffer conversion routines translate a numeric value to a sequence of hexadecimal characters and store those characters into memory starting at the address pointed at by the EDI register. After the conversion, the EDI register points at the first byte in memory beyond the sequence of characters these routines produce. With successive calls to these routines (or any routine that emits characters to the buffer at which EDI points) you can build up larger strings.

If the internal underscores flag is true, these routines will emit an underscore between each group of four hexadecimal digits.

Fixed Length Hexadecimal Numeric to Buffer Conversions

The fixed length hexadecimal to buffer conversion routines translate an input numeric value to a fixed-length string (depending on the data type size and the setting of the internal underscores flag). These functions emit the characters of the string (including leading zeros, as necessary) to sequential locations starting at the address held in EDI.

procedure conv.bToBuf( b:byte in al; var buffer:var in edi );

Converts the numeric value in AL to a sequence of exactly two hexadecimal digits (including a leading zero if the value is in the range $0-$f) and stores these two characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. Because the conversion is always less than five digits, the internal underscores flag does not affect the output this function produces.

HLA high-level calling sequence examples:

// The following will load "byteVariable" into AL and

// the address of "charArrayVariable" into EDI and then

// call conv.bToBuf:

conv.bToBuf( byteVariable, charArrayVariable );

// The following call will copy BH into AL and

// EDX into EDI prior to calling conv.bToBuf:

conv.bToBuf( bh, [edx] );

// The following just calls conv.bToBuf as AL and EDI

// already hold the parameter values:

conv.bToBuf( al, [edi] );

// The following loads the constant in AL and calls

// conv.bToBuf:

conv.bToBuf( <constant>, [edi] ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AL and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. byte of EAX (i.e., AL) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AL and [EDI].

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable:

mov( byteVariable, al );

lea( edi, charArrayVariable );

call conv.bToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( byteVariable, al );

mov( &charArrayVariable, edi );

call conv.bToBuf;

// Passing a pair of registers (that are not

// AL and EDI):

mov( bh, al );

mov( edx, edi );

call conv.bToBuf;

// Passing a constant:

mov( <constant>, al );

call conv.bToBuf; // Assume EDI already contains buffer address.

procedure conv.wToBuf( w:word in ax; var buffer:var in edi );

Converts the numeric value in AX to a sequence of exactly four hexadecimal digits (including leading zeros, as necessary) and stores these four characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. Because the conversion is always less than five digits, the internal underscores flag does not affect the output this function produces.

HLA high-level calling sequence examples:

// The following will load "wordVariable" into AX and

// the address of "charArrayVariable" into EDI and then

// call conv.wToBuf:

conv.wToBuf( wordVariable, charArrayVariable );

// The following call will copy BX into AX and

// EDX into EDI prior to calling conv.wToBuf:

conv.wToBuf( bx, [edx] );

// The following just calls conv.wToBuf as AX and EDI

// already hold the parameter values:

conv.wToBuf( ax, [edi] );

// The following loads the constant in AX and calls

// conv.wToBuf:

conv.wToBuf( <constant>, [edi] ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. word of EAX (i.e., AX) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AX and [EDI].

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable:

mov( wordVariable, ax );

lea( edi, charArrayVariable );

call conv.wToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( wordVariable, ax );

mov( &charArrayVariable, edi );

call conv.wToBuf;

// Passing a pair of registers (that are not

// AX and EDI):

mov( bx, ax );

mov( edx, edi );

call conv.wToBuf;

// Passing a constant:

mov( <constant>, ax );

call conv.wToBuf; // Assume EDI already contains buffer address.

procedure conv.dToBuf( d:dword in eax; var buffer:var in edi );

Converts the numeric value in EAX to a sequence of exactly eight hexadecimal digits (including leading zeros, as necessary) and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. If the internal underscores flag contains true, this function will emit a nine-character string with a single underscore between the fourth and fifth digits.

HLA high-level calling sequence examples:

// The following will load "dwordVariable" into EAX and

// the address of "charArrayVariable" into EDI and then

// call conv.dToBuf:

conv.dToBuf( dwordVariable, charArrayVariable );

// The following call will copy EBX into EAX and

// EDX into EDI prior to calling conv.dToBuf:

conv.dToBuf( ebx, [edx] );

// The following just calls conv.dToBuf as EAX and EDI

// already hold the parameter values:

conv.dToBuf( eax, [edi] );

// The following loads the constant in EAX and calls

// conv.dToBuf:

conv.dToBuf( <constant>, [edi] ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EAX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite EAX before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not EAX and [EDI].

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

mov( dwordVariable, eax );

lea( edi, charArrayVariable );

call conv.dToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( dwordVariable, eax );

mov( &charArrayVariable, edi );

call conv.dToBuf;

// Passing a pair of registers (that are not

// EAX and EDI):

mov( ebx, eax );

mov( edx, edi );

call conv.dToBuf;

// Passing a constant:

mov( <constant>, eax );

call conv.dToBuf; // Assume EDI already contains buffer address.

procedure conv.qToBuf( q:qword; var buffer:var in edi );

Converts the numeric value passed in q to a sequence of exactly 16 hexadecimal digits (including leading zeros, as necessary) and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. If the internal underscores flag contains true, this function will emit a 19-character string with underscores between the 4th and 5th, 8th and 9th, and 12th and 13th digits.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.qToBuf:

conv.qToBuf( qwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.qToBuf:

conv.qToBuf( <constant>, [edi] ); // <constant> must fit in 64 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.qToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.qToBuf;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.qToBuf; // Assume EDI already contains buffer address.

procedure conv.tbToBuf( tb:tbyte; var buffer:var in edi );

Converts the numeric value passed in tb to a sequence of exactly 20 hexadecimal digits (including leading zeros, as necessary) and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. If the internal underscores flag contains true, this function will emit a 24-character string with underscores between the 4th and 5th, 8th and 9th, 12th and 13th , and 16th and 17th digits.

HLA high-level calling sequence examples:

// The following will push the value of "tbyteVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.tbToBuf:

conv.tbToBuf( tbyteVariable, charArrayVariable );

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing a tbyte variable and a buffer variable:

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.tbToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.tbToBuf;

// Passing a constant:

pushd( <constant> >> 64 ); // Push H.O. word as dword, first

pushd( (<constant> >> 32) & $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.tbToBuf; // Assume EDI already contains buffer address.

procedure conv.lToBuf( l:lword; var buffer:var in edi );

Converts the numeric value passed in l to a sequence of exactly 32 hexadecimal digits (including leading zeros, as necessary) and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. If the internal underscores flag contains true, this function will emit a 39 character string with underscores between the 4th and 5th, 8th and 9th, 12th and 13th , 16th and 17th, 20th and 21st, 24th and 25th, and 28th and 29th digits.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.lToBuf:

conv.lToBuf( lwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.lToBuf:

conv.lToBuf( <constant>, [edi] ); // <constant> must fit in 128 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing an lword variable and a buffer variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.lToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.lToBuf;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.lToBuf; // Assume EDI already contains buffer address.

Variable Length Hexadecimal Numeric to Buffer Conversions

The variable length hexadecimal to buffer conversion routines translate an input numeric value to a variable-length string (depending on the value, data type size, and the setting of the internal underscores flag). These functions emit the characters of the string (without leading zeros) to sequential locations starting at the address held in EDI.

procedure conv.h8ToBuf( b:byte in al; var buffer:var in edi );

Converts the numeric value in AL to a sequence of one or two hexadecimal digits and stores these two characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. Because the conversion is always less than five digits, the internal underscores flag does not affect the output this function produces.

HLA high-level calling sequence examples:

// The following will load "byteVariable" into AL and

// the address of "charArrayVariable" into EDI and then

// call conv.h8ToBuf:

conv.h8ToBuf( byteVariable, charArrayVariable );

// The following call will copy BH into AL and

// EDX into EDI prior to calling conv.h8ToBuf:

conv.h8ToBuf( bh, [edx] );

// The following just calls conv.h8ToBuf as AL and EDI

// already hold the parameter values:

conv.h8ToBuf( al, [edi] );

// The following loads the constant in AL and calls

// conv.h8ToBuf:

conv.h8ToBuf( <constant>, [edi] ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AL and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. byte of EAX (i.e., AL) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AL and [EDI].

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable:

mov( byteVariable, al );

lea( edi, charArrayVariable );

call conv.h8ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( byteVariable, al );

mov( &charArrayVariable, edi );

call conv.h8ToBuf;

// Passing a pair of registers (that are not

// AL and EDI):

mov( bh, al );

mov( edx, edi );

call conv.h8ToBuf;

// Passing a constant:

mov( <constant>, al );

call conv.h8ToBuf; // Assume EDI already contains buffer address.

procedure conv.h16ToBuf( w:word in ax; var buffer:var in edi );

Converts the numeric value in AX to a sequence of one to four hexadecimal digits and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. Because the conversion is always less than five digits, the internal underscores flag does not affect the output this function produces.

HLA high-level calling sequence examples:

// The following will load "wordVariable" into AX and

// the address of "charArrayVariable" into EDI and then

// call conv.h16ToBuf:

conv.h16ToBuf( wordVariable, charArrayVariable );

// The following call will copy BX into AX and

// EDX into EDI prior to calling conv.h16ToBuf:

conv.h16ToBuf( bx, [edx] );

// The following just calls conv.h16ToBuf as AX and EDI

// already hold the parameter values:

conv.h16ToBuf( ax, [edi] );

// The following loads the constant in AX and calls

// conv.h16ToBuf:

conv.h16ToBuf( <constant>, [edi] ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. word of EAX (i.e., AX) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AX and [EDI].

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable:

mov( wordVariable, ax );

lea( edi, charArrayVariable );

call conv.h16ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( wordVariable, ax );

mov( &charArrayVariable, edi );

call conv.h16ToBuf;

// Passing a pair of registers (that are not

// AX and EDI):

mov( bx, ax );

mov( edx, edi );

call conv.h16ToBuf;

// Passing a constant:

mov( <constant>, ax );

call conv.h16ToBuf; // Assume EDI already contains buffer address.

procedure conv.h32ToBuf( d:dword in eax; var buffer:var in edi );

Converts the numeric value in EAX to a sequence of one to eight hexadecimal digits and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence. If the internal underscores flag contains true and the value is $1_0000 or greater, then this function will emit an underscore between the fourth and fifth digits in the output string.

HLA high-level calling sequence examples:

// The following will load "dwordVariable" into EAX and

// the address of "charArrayVariable" into EDI and then

// call conv.h32ToBuf:

conv.h32ToBuf( dwordVariable, charArrayVariable );

// The following call will copy EBX into EAX and

// EDX into EDI prior to calling conv.h32ToBuf:

conv.h32ToBuf( ebx, [edx] );

// The following just calls conv.h32ToBuf as EAX and EDI

// already hold the parameter values:

conv.h32ToBuf( eax, [edi] );

// The following loads the constant in EAX and calls

// conv.h32ToBuf:

conv.h32ToBuf( <constant>, [edi] ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EAX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite EAX before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not EAX and [EDI].

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

mov( dwordVariable, eax );

lea( edi, charArrayVariable );

call conv.h32ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( dwordVariable, eax );

mov( &charArrayVariable, edi );

call conv.h32ToBuf;

// Passing a pair of registers (that are not

// EAX and EDI):

mov( ebx, eax );

mov( edx, edi );

call conv.h32ToBuf;

// Passing a constant:

mov( <constant>, eax );

call conv.h32ToBuf; // Assume EDI already contains buffer address.

procedure conv.h64ToBuf( q:qword; var buffer:var in edi );

Converts the numeric value in q to a sequence of 1 to 16 hexadecimal digits and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence.

If the internal underscores flag contains true and the value is $1_0000 or greater, then this function will emit an underscore between the 4th and 5th digits in the output string.

If the value is $1_0000 or greater, then this function will emit an underscore between the 4th and 5th digits in the output string.

If the value is $1_0000_0000 or greater, then this function will emit an underscore between the 8th and 9th digits.

If the value is $1_0000_0000_0000 or greater, then this function will emit an underscore between the 12th and 13th digits.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.h64ToBuf:

conv.h64ToBuf( qwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.h64ToBuf:

conv.h64ToBuf( <constant>, [edi] ); // <constant> must fit in 64 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.h64ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.h64ToBuf;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.h64ToBuf; // Assume EDI already contains buffer address.

procedure conv.h80ToBuf( tb:tbyte; var buffer:var in edi );

Converts the numeric value in tb to a sequence of 1 to 20 hexadecimal digits and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence.

If the internal underscores flag contains true, and:

If the value is $1_0000 or greater, then this function will emit an underscore between the 4th and 5th digits in the output string.

If the value is $1_0000_0000 or greater, then this function will emit an underscore between the 8th and 9th digits.

If the value is $1_0000_0000_0000 or greater, then this function will emit an underscore between the 12th and 13th digits.

If the value is $1_0000_0000_0000_0000 or greater, then this function will emit an underscore between the 16th and 17th digits.

HLA high-level calling sequence examples:

// The following will push the value of "tbyteVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.h80ToBuf:

conv.h80ToBuf( tbyteVariable, charArrayVariable );

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing a tbyte variable and a buffer variable:

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.h80ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.h80ToBuf;

// Passing a constant:

pushd( <constant> >> 64 ); // Push H.O. word as dword, first

pushd( (<constant> >> 32) & $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.tbToBuf; // Assume EDI already contains buffer address.

procedure conv.h128ToBuf( l:lword; var buffer:var in edi );

Converts the numeric value in l to a sequence of 1 to 32 hexadecimal digits and stores these characters at the location pointed at by EDI. This function returns EDI pointing at the first byte after the sequence.

If the internal underscores flag contains true, and:

If the value is $1_0000 or greater, then this function will emit an underscore between the 4th and 5th digits in the output string.

If the value is $1_0000_0000 or greater, then this function will emit an underscore between the 8th and 9th digits.

If the value is $1_0000_0000_0000 or greater, then this function will emit an underscore between the 12th and 13th digits.

If the value is $1_0000_0000_0000_0000 or greater, then this function will emit an underscore between the 16th and 17th digits.

If the value is $1_0000_0000_0000_0000_0000 or greater, then this function will emit an underscore between the 20th and 21st digits.

If the value is $1_0000_0000_0000_0000_0000_0000 or greater, then this function will emit an underscore between the 24th and 25th digits.

If the value is $1_0000_0000_0000_0000_0000_0000_0000 or greater, then this function will emit an underscore between the 28th and 29th digits.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.h128ToBuf:

conv.h128ToBuf( lwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.h128ToBuf:

conv.h128ToBuf( <constant>, [edi] ); // <constant> must fit in 128 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing an lword variable and a buffer variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.h128ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.h128ToBuf;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.h128ToBuf; // Assume EDI already contains buffer address.

Hexadecimal Numeric to String Conversions

The hexadecimal numeric to string conversion routines are the general-purpose hexadecimal string conversion routines. There are two versions: one set of these routines store their string data into a preallocated string object (the unadorned versions), the other set (adorned with an "a_" prefix in their name) allocates storage for a new string on the heap and returns a pointer to that new string in the EAX register.

As for the buffer routines (e.g., conv.bToBuf and conv.h8ToBuf) there are two categories of routines based on whether the routines emit a mimimum length string or pad the string with leading zeros to the data type’s natural size. The conv.hXToStr routines emit the minimum number of hexadecimal digits in the string they create.

The conv.XToStr (X= b, w, d, q, tb, or l) functions always create a fixed length string with an appropriate number of leading zeros (b=2 digits, w=4 digits, d=8 digits, q=16 digits, tb=20 digits, and l=32 digits). If the internal underscores flag contains true, then these functions will emit an underscore between each group of four hexadecimal digits.

The conv.hXToStr functions (X=8, 16, 32,64, 80, or 128) let you specify a minimum field width and a padding character (because the conv.XToBuf routines always emit fixed-length strings, there is no need to specify a minimum field width or padding character for those strings). The absolute value of the width parameter specifies the minimum string length for the conversion. The conversion will always produce a string at least abs(width) characters long. If the conversion would require more than abs(width) print positions, then the conversion will produce a larger string, as necessary.

If the string conversion requires fewer than abs(width) characters and the width parameter is a non-negative value, these routines right justify the value in the string and pad the remaining positions with the fill character. If the conversion requires fewer than abs(width) characters and the width parameter is a negative number, then these functions will left-justify the value in the output field and pad the end of the string with the fill character.

For the unadorned functions, the destination string’s maximum length must be large enough to hold the full result (including any extra print positions needed beyond the value specified by abs(width)) or these functions will raise a string overflow exception.

If the internal underscore flag is true, then the 32-bit and larger hex to string conversion functions will emit an underscore between each set of four hexadecimal digits, starting from the least significant digit. This is true for both the conv.hXToStr and conv.XToStr routines. See the descriptions of the conv.setUnderscores and conv.getUnderscores functions for more details. Note that the hexadecimal to numeric conversion functions do not inject underscores into sequences of padding characters, only into the actual digits the conversion produces. This is true even if you specify a numeric character (such as ‘0’) as the padding character.

Fixed-Length Numeric to Hexadecimal String Conversions

The functions in this category convert an 8-bit, 16-bit, 32-bit, 64-bit, 80-bit, or 128-bit numeric value to fixed-length strings (one hexadecimal digit for each four bits, including leading zeros). If the internal underscores flag contains true, then these functions will also insert an underscore between each group of four hexadecimal digits.

conv.bToStr( b:byte; dest:string );

This function converts the 8-bit value of the b parameter to a two-byte string containing b’s hexadecimal representation. Because this conversion requires exactly two digits, the internal underscores flag setting does not affect the operation of this function. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.bToStr:

conv.bToStr( byteVariable, destStr );

// The following call will BH’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.bToStr:

conv.bToStr( bh, edx );

// The following pushes the constant and destStr and calls

// conv.bToStr:

conv.bToBuf( <constant>, destStr ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.bToStr;

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

push( destStr );

call conv.bToStr;

// Passing a byte variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

push( destStr );

call conv.bToStr;

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

push( edx );

call conv.bToStr;

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

push( edx );

call conv.bToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.bToStr;

conv.a_bToStr( b:byte ); @returns( "eax" );

This function converts the value of the b parameter to a two-byte string containing b’s hexadecimal representation. Because this conversion requires exactly two digits, the internal underscores flag setting does not affect the operation of this function. This function allocates storage for the string on the heap and returns a pointer to that string in the EAX register.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and then call conv.a_bToStr:

conv.a_bToStr( byteVariable );

mov( eax, byteStr );

// The following call will BH’s value onto the stack

// before calling conv.a_bToStr:

conv.a_bToStr( bh );

mov( eax, byteStr );

// The following pushes the constant and calls

// conv.a_bToStr:

conv.bToBuf( <constant> ); // <constant> must fit in 8 bits

mov( eax, byteStr );

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

call conv.a_bToStr;

mov( eax, destStr );

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

call conv.a_bToStr;

mov( eax, destStr );

// Passing a byte variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

call conv.a_bToStr;

mov( eax, destStr );

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

call conv.a_bToStr;

mov( eax, byteStr );

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

call conv.a_bToStr;

mov( eax, byteStr );

// Passing a constant:

pushd( <constant> );

call conv.a_bToStr;

mov( eax, byteStr );

conv.wToStr( w:word; dest:string );

Converts the 16-bit value of the w parameter to a four-byte string that is the hexadecimal representation of this value. Because this conversions requires exactly four digits, the internal underscores flag setting does not affect the operation of this function. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push "wordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.wToStr:

conv.wToStr( wordVariable, destStr );

// The following call will BX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.wToStr:

conv.wToStr( bx, edx );

// The following pushes the constant and destStr and calls

// conv.wToStr:

conv.wToStr( <constant>, destStr ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.wToStr;

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

push( destStr );

call conv.wToStr;

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

push( destStr );

call conv.wToStr;

// Passing a pair of registers:

// BX = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BX

push( edx );

call conv.wToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.wToStr;

conv.a_wToStr( w:word; dest:string ); @returns( "eax" );

Converts the 16-bit value of the w parameter to a four-byte string that is the hexadecimal representation of this value. Because this conversions requires exactly four digits, the internal underscores flag setting does not affect the operation of this function. This function allocates storage for the string on the heap and returns a pointer to that string in the EAX register.

HLA high-level calling sequence examples:

// The following will push "wordVariable" and then call conv.wToStr:

conv.a_wToStr( wordVariable );

mov( eax, destStr );

// The following call will BX’s value onto the stack

// before calling conv.a_wToStr:

conv.a_wToStr( bx );

// The following pushes the constant and calls

// conv.a_wToStr:

conv.a_wToStr( <constant>, destStr ); // <constant> must fit in 16 bits

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( wordVariable, eax );

push( eax );

call conv.a_wToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

call conv.wToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

call conv.wToStr;

mov( eax, destStr );

// Passing a pair of registers:

// BX = value to print.

push( ebx ); // Pushes BX

call conv.wToStr;

mov( eax, wordStr );

// Passing a constant:

pushd( <constant> );

call conv.wToStr;

mov( eax, destStr );

conv.dToStr( d:dword; dest:string );

This function converts the 32-bit value of the d parameter to an eight- or nine-byte string that is the hexadecimal representation of this value. If d’s value is greater than $FFFF and the underscores flag is true, then this function emits a nine-character string with an underscore between the fourth and fifth digits (counting from the least signficant digit). This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.dToStr:

conv.dToStr( dwordVariable, destStr );

// The following call will EBX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.dToStr:

conv.dToStr( ebx, edx );

// The following pushes the constant and destStr and calls

// conv.dToStr:

conv.dToStr( <constant>, destStr ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

push( dwordVariable );

push( destStr );

call conv.dToStr;

// Passing a pair of registers:

// EBX = value to print, EDX = pointer to string object.

push( ebx );

push( edx );

call conv.dToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.dToStr;

conv.a_dToStr( d:dword; dest:string ); @returns( "eax" );

Converts the 32-bit value of the d parameter to an eight- or nine-byte string that is the hexadecimal representation of this value. If d’s value is greater than $FFFF and the underscores flag is true, then this function emits a nine-character string with an underscore between the fourth and fifth digits (counting from the least signficant digit). This function allocates storage for the string on the heap and returns a pointer to that string in the EAX register.

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and then call conv.a_dToStr:

conv.a_dToStr( dwordVariable );

mov( eax, destStr );

// The following call will push EBX’s value onto the stack

// before calling conv.a_dToStr:

conv.a_dToStr( ebx );

// The following pushes the constant and calls

// conv.a_dToStr:

conv.a_dToStr( <constant>, destStr ); // <constant> must fit in 32 bits

HLA low-level calling sequence examples:

// Passing a dword variable:

push( dwordVariable );

call conv.a_dToStr;

mov( eax, destStr );

// Passing a register:

// EBX = value to print.

push( ebx );

call conv.a_dToStr;

mov( eax, dwordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_dToStr;

mov( eax, destStr );

conv.qToStr( q:qword; dest:string );

Converts the 64-bit value of the q parameter to 16- or 19-byte string (based on the setting of the underscores flag) that is the hexadecimal representation of this value. If the internal underscores flag is true, then this function emits an underscore between each group of four hexadecimal digits (resulting in a 19-character string). This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// and the value of the destStr string variable

// onto the stack and then call conv.qToStr:

conv.qToStr( qwordVariable, destStr );

// The following pushes the constant onto the stack along with

// the value held in the destStr variable and calls

// conv.qToStr:

conv.qToStr( <constant>, destStr ); // <constant> must fit in 64 bits

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

push( destStr );

call conv.qToStr;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

push( destStr );

call conv.qToStr;

conv.a_qToStr( q:qword; dest:string ); @returns( "eax" );

Converts the 64-bit value of the q parameter to 16- or 19-byte string (based on the setting of the underscores flag) that is the hexadecimal representation of this value. If the internal underscores flag is true, then this function emits an underscore between each group of four hexadecimal digits (resulting in a 19-character string). This function allocates storage for the string on the heap and returns a pointer to that string in the EAX register.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack and then call conv.a_qToStr:

conv.a_qToStr( qwordVariable );

mov( eax, destStr );

// The following pushes the constant onto the stack and calls

// conv.a_qToStr:

conv.a_qToStr( <constant> ); // <constant> must fit in 64 bits

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing a qword variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

call conv.a_qToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.a_qToStr;

mov( eax, destStr );

conv.tbToStr( tb:tbyte; dest:string );

Converts the 80-bit value of the d parameter to 20- or 24-byte string (based on the setting of the underscores flag) that is the hexadecimal representation of this value. If the internal underscores flag is true, then this function emits an underscore between each group of four hexadecimal digits (resulting in a 24-character string). This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push the value of "tbyteVariable"

// and the value of destStr onto the stack

// and then call conv.tbToStr:

conv.tbToStr( tbyteVariable, destStr );

HLA low-level calling sequence examples:

// Passing a tbyte variable:

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

push( destStr );

call conv.tbToStr;

// Passing a constant:

pushd( <constant> >> 64 ); // Push H.O. word as dword, first

pushd( (<constant> >> 32) & $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

push( destStr );

call conv.tbToStr;

conv.a_tbToStr( tb:tbyte; dest:string ); @returns( "eax" );

Converts the 80-bit value of the d parameter to 20- or 24-byte string (based on the setting of the underscores flag) that is the hexadecimal representation of this value. If the internal underscores flag is true, then this function emits an underscore between each group of four hexadecimal digits (resulting in a 24-character string). This function allocates storage for the string on the heap and returns a pointer to that string in the EAX register.

HLA high-level calling sequence examples:

// The following will push the value of "tbyteVariable"

// onto the stack and then call conv.a_tbToStr:

conv.a_tbToStr( tbyteVariable );

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing a tbyte variable:

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

call conv.a_tbToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 64 ); // Push H.O. word as dword, first

pushd( (<constant> >> 32) & $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.a_tbToStr;

mov( eax, destStr );

conv.lToStr( l:lword; dest:string );

Converts the 128-bit value of the l parameter to 32- or 19-byte string (based on the setting of the underscores flag) that is the hexadecimal representation of this value. If the internal underscores flag is true, then this function emits an underscore between each group of four hexadecimal digits (resulting in a 39-character string). This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// and destStr onto the stack, and then call conv.lToStr:

conv.lToStr( lwordVariable, destStr );

// The following pushes the constant onto the stack and calls

// conv.lToStr:

conv.lToStr( <constant>, edx ); // EDX contains string pointer value.

HLA low-level calling sequence examples:

// Passing an lword variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

push( destStr );

call conv.lToStr;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

push( edx ); // EDX contains string pointer value.

call conv.lToStr;

conv.a_lToStr( l:lword; dest:string ); @returns( "eax" );

Converts the 128-bit value of the l parameter to 32- or 19-byte string (based on the setting of the underscores flag) that is the hexadecimal representation of this value. If the internal underscores flag is true, then this function emits an underscore between each group of four hexadecimal digits (resulting in a 39-character string). This function allocates storage for the string on the heap and returns a pointer to that string in the EAX register.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, and then call conv.a_lToStr:

conv.a_lToStr( lwordVariable );

mov( eax, destStr );

// The following pushes the constant onto the stack and calls

// conv.a_lToStr:

conv.a_lToStr( <constant> );

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing an lword variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

call conv.a_lToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.a_lToStr;

mov( eax, destStr );

Variable-Length Numeric to Hexadecimal String Conversions

The functions in this category convert a numeric value (8, 16, 32, 64, 80, or 128 bits) to a variable-length hexadecimal string (with no leading zeros). The string will contain the minimum number of digits needed to represent the value (plus underscores between each group of four digits if the internal underscores flag contains true).

procedure conv.h8ToStr ( b:byte; width:int32; fill:char; buffer:string );

Converts the 8-bit value of the b parameter to a one or two-byte string that is the hexadecimal representation of this value. Because 8-bit hexadecimal conversions require no more than two digits, the internal underscores flag setting does not affect the operation of this function. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.h8ToStr:

conv.h8ToStr( byteVariable, destStr );

// The following call will BH’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.h8ToStr:

conv.h8ToStr( bh, edx );

// The following pushes the constant and destStr and calls

// conv.h8ToStr:

conv.h8ToBuf( <constant>, destStr ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.h8ToStr;

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

push( destStr );

call conv.h8ToStr;

// Passing a byte variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

push( destStr );

call conv.h8ToStr;

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

push( edx );

call conv.h8ToStr;

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

push( edx );

call conv.h8ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.h8ToStr;

procedure conv.a_h8ToStr( b:byte; width:int32; fill:char );
@returns( "eax" );

Allocates an appropriate amount of string storage on the heap and then converts the 8-bit value of the b parameter to a 1..2 byte string that is the hexadecimal representation of this value. Returns a pointer to the newly allocated string in EAX. Because the number of digits is always two or less, the internal underscores flag does not affect the string this function produces.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and then call conv.a_h8ToStr:

conv.a_h8ToStr( byteVariable );

mov( eax, byteStr );

// The following call will BH’s value onto the stack

// before calling conv.a_h8ToStr:

conv.a_h8ToStr( bh );

mov( eax, byteStr );

// The following pushes the constant and calls

// conv.a_h8ToStr:

conv.a_h8ToStr( <constant> ); // <constant> must fit in 8 bits

mov( eax, byteStr );

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

call conv.a_h8ToStr;

mov( eax, destStr );

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

call conv.a_h8ToStr;

mov( eax, destStr );

// Passing a byte variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

call conv.a_h8ToStr;

mov( eax, destStr );

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

call conv.a_h8ToStr;

mov( eax, byteStr );

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

call conv.a_h8ToStr;

mov( eax, byteStr );

// Passing a constant:

pushd( <constant> );

call conv.a_h8ToStr;

mov( eax, byteStr );

procedure conv.h16ToStr ( w:word; width:int32; fill:char; buffer:string );

Converts the 16-bit value of the w parameter to a 1..4 byte string that is the hexadecimal representation of this value. Because 16-bit hexadecimal conversions require no more than four digits, the internal underscores flag setting does not affect the operation of this function. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push "wordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.h16ToStr:

conv.h16ToStr( wordVariable, destStr );

// The following call will BX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.h16ToStr:

conv.h16ToStr( bx, edx );

// The following pushes the constant and destStr and calls

// conv.h16ToStr:

conv.h16ToStr( <constant>, destStr ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.h16ToStr;

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

push( destStr );

call conv.h16ToStr;

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

push( destStr );

call conv.h16ToStr;

// Passing a pair of registers:

// BX = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BX

push( edx );

call conv.h16ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.h16ToStr;

procedure conv.a_h16ToStr( w:word; width:int32; fill:char );
@returns( "eax" );

Allocates an appropriate amount of string storage on the heap and then converts the 16-bit value of the w parameter to a 1..4 byte string that is the hexadecimal representation of this value. Returns a pointer to the newly allocated string in EAX. Because the number of digits is always four or less, the internal underscores flag does not affect the string this function produces.

HLA high-level calling sequence examples:

// The following will push "wordVariable" and call conv.a_h16ToStr:

conv.a_h16ToStr( wordVariable );

mov( eax, destStr );

// The following call will BX’s value onto the stack

// before calling conv.a_h16ToStr:

conv.a_h16ToStr( bx );

// The following pushes the constant and calls

// conv.a_h16ToStr:

conv.a_h16ToStr( <const>, destStr ); // <const> must fit in 16 bits

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( wordVariable, eax );

push( eax );

call conv.a_h16ToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

call conv.a_h16ToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

call conv.a_h16ToStr;

mov( eax, destStr );

// Passing a pair of registers:

// BX = value to print.

push( ebx ); // Pushes BX

call conv.a_h16ToStr;

mov( eax, wordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_h16ToStr;

mov( eax, destStr );

procedure conv.h32ToStr ( d:dword; width:int32; fill:char; buffer:string );

Converts the 32-bit value of the d parameter to a 1..8 byte string (1..9 bytes if the underscores flag is set) that is the hexadecimal representation of this value. If the 32-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert an underscore between the 4th and 5th digits of the string it produces. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.h32ToStr:

conv.h32ToStr( dwordVariable, destStr );

// The following call will push EBX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.h32ToStr:

conv.h32ToStr( ebx, edx );

// The following pushes the constant and destStr and calls

// conv.h32ToStr:

conv.h32ToStr( <constant>, destStr ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

push( dwordVariable );

push( destStr );

call conv.h32ToStr;

// Passing a pair of registers:

// EBX = value to print, EDX = pointer to string object.

push( ebx );

push( edx );

call conv.h32ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.h32ToStr;

procedure conv.a_h32ToStr( d:dword; width:int32; fill:char );
@returns( "eax" );

Allocates an appropriate amount of string storage on the heap and then converts the 32-bit value of the d parameter to a 1..8 byte string (1..9 bytes if the underscores flag is set) that is the hexadecimal representation of this value. Returns a pointer to the newly allocated string in EAX. If the 32-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces.

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and then call conv.a_h32ToStr:

conv.a_h32ToStr( dwordVariable );

mov( eax, destStr );

// The following call will push EBX’s value onto the stack

// before calling conv.a_h32ToStr:

conv.a_h32ToStr( ebx );

// The following pushes the constant and calls

// conv.a_h32ToStr:

conv.a_h32ToStr( <constant>, destStr ); // <constant> must fit in 32 bits

HLA low-level calling sequence examples:

// Passing a dword variable:

push( dwordVariable );

call conv.a_h32ToStr;

mov( eax, destStr );

// Passing a register:

// EBX = value to print.

push( ebx );

call conv.a_h32ToStr;

mov( eax, dwordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_h32ToStr;

mov( eax, destStr );

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

Converts the 64-bit value of the q parameter to a 1..16 byte string (1..19 bytes if the underscores flag is set) that is the hexadecimal representation of this value. If the 64-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// and the value of the destStr string variable

// onto the stack and then call conv.h64ToStr:

conv.h64ToStr( qwordVariable, destStr );

// The following pushes the constant onto the stack along with

// the value held in the destStr variable and calls

// conv.h64ToStr:

conv.h64ToStr( <constant>, destStr ); // <constant> must fit in 64 bits

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

push( destStr );

call conv.h64ToStr;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

push( destStr );

call conv.h64ToStr;

procedure conv.a_h64ToStr( q:qword; width:int32; fill:char );
@returns( "eax" );

Allocates an appropriate amount of string storage on the heap and then converts the 64-bit value of the q parameter to a 1..16 byte string (1..19 bytes if the underscores flag is set) that is the hexadecimal representation of this value. Returns a pointer to the newly allocated string in EAX. If the 64-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack and then call conv.a_h64ToStr:

conv.a_h64ToStr( qwordVariable );

mov( eax, destStr );

// The following pushes the constant onto the stack and calls

// conv.a_h64ToStr:

conv.a_h64ToStr( <constant> ); // <constant> must fit in 64 bits

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing a qword variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

call conv.a_h64ToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.a_h64ToStr;

mov( eax, destStr );

procedure conv.h80ToStr( tb:tbyte; width:int32; fill:char; buffer:string );

Converts the 80-bit value of the tb parameter to a 1..20 byte string (1..24 bytes if the underscores flag is set) that is the hexadecimal representation of this value. If the 80-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push the value of "tbyteVariable"

// and the value of destStr onto the stack

// and then call conv.h80ToStr:

conv.h80ToStr( tbyteVariable, destStr );

HLA low-level calling sequence examples:

// Passing a tbyte variable:

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

push( destStr );

call conv.h80ToStr;

// Passing a constant:

pushd( <constant> >> 64 ); // Push H.O. word as dword, first

pushd( (<constant> >> 32) & $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

push( destStr );

call conv.h80ToStr;

procedure conv.a_h80ToStr( tb:tbyte; width:int32; fill:char );
@returns( "eax" );

Allocates an appropriate amount of string storage on the heap and then converts the 80-bit value of the tb parameter to a 1..20 byte string (1..24 bytes if the underscores flag is set) that is the hexadecimal representation of this value. Returns a pointer to the newly allocated string in EAX. If the 80-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces.

HLA high-level calling sequence examples:

// The following will push the value of "tbyteVariable"

// onto the stack and then call conv.a_h80ToStr:

conv.a_h80ToStr( tbyteVariable );

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing a tbyte variable:

pushw( 0 ); // Must pad parameter to 12 bytes

push( (type word tbyteVariable[8])); // H.O. word first

push( (type dword tbyteVariable[4]));

push( (type dword qwordVariable[0])); // L.O. dword last

call conv.a_h80ToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 64 ); // Push H.O. word as dword, first

pushd( (<constant> >> 32) & $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.a_h80ToStr;

mov( eax, destStr );

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

Converts the 128-bit value of the l parameter to a 1..32 byte string (1..39 bytes if the underscores flag is set) that is the hexadecimal representation of this value. If the 128-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces. This function raises a string overflow error if the destination string is not large enough to hold the converted string.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// and destStr onto the stack, and then call conv.h128ToStr:

conv.h128ToStr( lwordVariable, destStr );

// The following pushes the constant onto the stack and calls

// conv.h128ToStr:

conv.h128ToStr( <constant>, edx ); // EDX contains string pointer value.

HLA low-level calling sequence examples:

// Passing an lword variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

push( destStr );

call conv.h128ToStr;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

push( edx ); // EDX contains string pointer value.

call conv.h128ToStr;

conv.a_h128ToStr( l:lword; width:int32; fill:char );
@returns( "eax" );

Allocates an appropriate amount of string storage on the heap and then converts the 128-bit value of the l parameter to a 1..32 byte string (1..39 bytes if the underscores flag is set) that is the hexadecimal representation of this value. Returns a pointer to the newly allocated string in EAX. If the 128-bit value is greater than $FFFF and the internal underscores flag is true, then this function will insert underscores between each set of four digits to the left of the least significant digit in the string it produces.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, and then call conv.a_h128ToStr:

conv.a_h128ToStr( lwordVariable );

mov( eax, destStr );

// The following pushes the constant onto the stack and calls

// conv.a_h128ToStr:

conv.a_h128ToStr( <constant> );

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing an lword variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

call conv.a_h128ToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.a_h128ToStr;

mov( eax, destStr );

Hexadecimal Buffer to Numeric Conversions

The hexadecimal buffer to numeric conversions ("ASCII to hexadecimal") convert a sequence of characters at some address in memory to numeric form.

These routines will skip over any leading underscore and delimiter characters (specified by the internal delimiters character set, see the discussion of conv.setDelimiters and conv.getDelimiters for details). These functions will convert all characters in the sequence until encountering a non-hexadecimal digit or underscore. If the first non-hex character is not the end of string or a delimiter character, these functions will raise a conversion exception. If the character is not a valid 7-bit ASCII character, these functions will raise an illegal character exception.

procedure conv.atoh8( var buffer:var in esi ); @returns( "eax" );

This function converts the hexadecimal character sequence beginning at character position held in ESI to an 8-bit numeric value. It raises an overflow exception if the value is outside the range $00..$FF. This function returns the result in AL, zero extended into EAX. This function returns ESI pointing at the first character after the sequence of hexadecimal characters.

HLA high-level calling sequence examples:

// The following will convert the hexadecimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in AL:

conv.atoh8( [esi] );

mov( al, hexNumericResult );

// The following loads ESI with the address of

// a sequence of hexadecimal characters (held in an HLA

// string) and converts them to an 8-bit number:

conv.atoh8( sourceStr ); // Loads "sourceStr" into ESI

mov( al, byteVariable );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoh8;

mov( al, hexNumericResult );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoh8;

mov( al, hex12 );

procedure conv.atoh16( var buffer:var in esi ); @returns( "eax" );

This function converts the hexadecimal character sequence beginning at character position held in ESI to an 8-bit numeric value. It raises an overflow exception if the value is outside the range $0000..$FFFF. This function returns the result in AX, zero extended into EAX. This function returns ESI pointing at the first character after the sequence of hexadecimal characters.

HLA high-level calling sequence examples:

// The following will convert the hexadecimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in AX:

conv.atoh16( [esi] );

mov( ax, hex16NumericResult );

// The following loads ESI with the address of

// a sequence of hexadecimal characters (held in an HLA

// string) and converts them to a 16-bit number:

conv.atoh16( sourceStr ); // Loads "sourceStr" into ESI

mov( ax, wordVariable );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoh16;

mov( ax, hex16NumericResult );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoh16;

mov( ax, hex12 );

procedure conv.atoh32( var buffer:var in esi ); @returns( "eax" );

This function converts the hexadecimal character sequence beginning at character position held in ESI to an 8-bit numeric value. It raises an overflow exception if the value is outside the range $0000_0000..$FFFF_FFFF. This function returns the result in EAX. This function returns ESI pointing at the first character after the sequence of hexadecimal characters.

HLA high-level calling sequence examples:

// The following will convert the hexadecimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in EAX:

conv.atoh32( [esi] );

mov( eax, hex32NumericResult );

// The following loads ESI with the address of

// a sequence of hexadecimal characters (held in an HLA

// string) and converts them to a 32-bit number:

conv.atoh32( sourceStr ); // Loads "sourceStr" into ESI

mov( eax, dwordVariable );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoh32;

mov( eax, hex32NumericResult );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoh32;

mov( eax, hex12 );

procedure conv.atoh64( var buffer:var in esi ); @returns( "edx:eax" );

This function converts the hexadecimal character sequence beginning at character position held in ESI to an 8-bit numeric value. It raises an overflow exception if the value is outside the range $0000_0000_0000_0000..$FFFF_FFFF_FFFF_FFFF. This function returns the result in EDX:EAX (H.O. double word in EDX). This function returns ESI pointing at the first character after the sequence of hexadecimal characters.

HLA high-level calling sequence examples:

// The following will convert the hexadecimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in EDX:EAX:

conv.atoh64( [esi] );

mov( eax, (type dword hex64NumericResult[0]) );

mov( edx, (type dword hex64NumericResult[4]) );

// The following loads ESI with the address of

// a sequence of hexadecimal characters (held in an HLA

// string) and converts them to a 64-bit number:

conv.atoh64( sourceStr ); // Loads "sourceStr" into ESI

mov( eax, (type dword qwordVariable[0]) );

mov( edx, (type dword qwordVariable[4]) );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoh64;

mov( eax, (type dword hex64NumericResult[0]) );

mov( edx, (type dword hex64NumericResult[4]) );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoh64;

mov( eax, (type dword qwordVariable[0]) );

mov( edx, (type dword qwordVariable[4]) );

procedure conv.atoh128( var buffer:var in esi; var dest:lword );

This function converts the hexadecimal character sequence beginning at character position in the string to a 128-bit numeric value. It raises an overflow exception if the value is outside the range $0000_0000_0000_0000_0000_0000_0000_0000..$FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF. This function returns the result in the variable specified by the dest parameter.

HLA high-level calling sequence examples:

// The following will convert the hexadecimal characters in memory

// at the address specified by [esi] into a numeric value

// and stores that value in lwordDest (passed by reference):

conv.atoh128( [esi], lwordDest );

// The following loads ESI with the address of

// a sequence of hexadecimal characters (held in an HLA

// string) and converts them to a 128-bit number that it

// stores in lwordDest:

conv.atoh128( sourceStr, lwordDest ); // Loads "sourceStr" into ESI

HLA low-level calling sequence examples:

// Option 1: lwordDest is a static object declared in a

// HLA STATIC, READONLY, or STORAGE section:

// As with the first example above, assume ESI already

// contains the address of the string to convert:

pushd( &lwordDest ); // Pass address of lwordDest as reference parm.

call conv.atoh128;

// Option 2: lwordDest is a simple automatic variable (no indexing)

// declared in a VAR section (or as a parameter). Assume that

// no 32-bit registers can be disturbed by this code.

// As with the first example above, assume ESI already

// contains the address of the string to convert:

push( ebp );

add( @offset( lwordDest ), (type dword [esp]));

call conv.atoh128;

// Option 3: lwordDest is a complex addressing mode and at least

// one 32-bit register is available for use by this code.

// As with the first example above, assume ESI already

// contains the address of the string to convert:

lea( eax, lwordDest ); // Assume EAX is the available register

push( eax );

call conv.atoh128;

// Same as second high-level example above. Assumes that

// lwordDest is a static object.

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

pushd( &lwordDest );

call conv.atoh128;

Hexadecimal String to Numeric Conversions

This functions convert a string value, that contain the hexadecimal representation of a number, into the numeric form. These functions have two parameters: a string object and an index into that string. Numeric conversion begins at the zero-based character position specified by the index parameter. For example, the invocation

conv.strToh8( someStr, 5 );

begins the conversion starting with the sixth character (index 5) in someStr. These functions will raise an "index out of range" exception if the supplied index is greater than the size of the string the first parameter specifies. They will return a null pointer reference exception if the string parameter is NULL (they will return an illegal memory access exception if the first parameter is not a valid pointer and references unpaged memory).

These routines will skip over any leading underscore and delimiter characters (specified by the internal delimiters character set, see the discussion of conv.setDelimiters and conv.getDelimiters for details). These functions will convert all characters in the sequence until encountering a non-hexadecimal digit or underscore. If the first non-hex character is not the end of string or a delimiter character, these functions will raise a conversion exception. If the character is not a valid 7-bit ASCII character, these functions will raise an illegal character exception.

procedure conv.strToh8( s:string; index:dword ); @returns( "eax" );

This function converts the hexadecimal character sequence beginning at the indexth character position in the string to an 8-bit numeric value. It raises an overflow exception if the value is outside the range $00..$FF. This function returns the result in AL, zero extended into EAX.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "hexValueStr" to numeric form:

conv.strToh8( hexValueStr, 0 ); // Index=0 starts at beginning

mov( al, hexNumericResult );

// The following demonstrates using a non-zero index:

conv.strToh8( "abc12", 3 ); // "12" begins at offset 3

mov( al, hex12 );

HLA low-level calling sequence examples:

push( hexValueStr ); // Same as first example above

pushd( 0 );

call conv.strToh8;

mov( al, hexNumericResult );

// Same as second example above

static

str12 :string := "abc12";

.

.

.

push( str12 ); // Note that str12 points at "abc12".

pushd( 3 ); // Index to "12" in "abc12".

call conv.strToh8;

mov( al, hex12 );

procedure conv.strToh16( s:string; index:dword ); @returns( "eax" );

This function converts the hexadecimal character sequence beginning at the indexth character position in the string to a 16-bit numeric value. It raises an overflow exception if the value is outside the range $0000..$FFFF. This function returns the result in AX, zero extended into EAX.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "hexValueStr" to numeric form:

conv.strToh16( hexValueStr, 0 ); // Index=0 starts at beginning

mov( ax, wordVar );

// The following demonstrates using a non-zero index:

conv.strToh16( "abc12FF", 3 ); // "12FF" begins at offset 3

mov( ax, wordVar );

HLA low-level calling sequence examples:

push( hexValueStr ); // Same as first example above

pushd( 0 );

call conv.strToh16;

mov( ax, wordVar );

// Same as second example above

static

str12FF :string := "abc12FF";

.

.

.

push( str12FF ); // Note that str12FF points at "abc12FF".

pushd( 3 ); // Index to "12FF" in "abc12FF".

call conv.strToh16;

mov( ax, wordVar );

procedure conv.strToh32( s:string; index:dword ); @returns( "eax" );

This function converts the hexadecimal character sequence beginning at the indexth character position in the string to a 32-bit numeric value. It raises an overflow exception if the value is outside the range $0000_0000..$FFFF_FFFF. This function returns the result in EAX.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "hexValueStr" to numeric form:

conv.strToh32( hexValueStr, 0 ); // Index=0 starts at beginning

mov( eax, dwordVar );

// The following demonstrates using a non-zero index:

conv.strToh32( "abc12_FF00", 3 ); // "12_FF00" begins at offset 3

mov( eax, dwordVar );

HLA low-level calling sequence examples:

push( hexValueStr ); // Same as first example above

pushd( 0 );

call conv.strToh32;

mov( eax, dwordVar );

// Same as second example above

static

str12FF00 :string := "abc12_FF00";

.

.

.

push( str12FF00 ); // Note that str12FF00 points at "abc12_FF00".

pushd( 3 ); // Index to "12_FF00" in "abc12_FF00".

call conv.strToh32;

mov( eax, dwordVar ); // dwordVar now contains $12_FF00.

procedure conv.strToh64( s:string; index:dword ); @returns( "edx:eax" );

This function converts the hexadecimal character sequence beginning at the indexth character position in the string to a 64-bit numeric value. It raises an overflow exception if the value is outside the range $0000_0000_0000_0000..$FFFF_FFFF_FFFF_FFFF. This function returns the result in EDX:EAX (H.O. double word in EDX).

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "hexValueStr" to numeric form:

conv.strToh64( hexValueStr, 0 ); // Index=0 starts at beginning

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

// The following demonstrates using a non-zero index:

conv.strToh64( "abc12", 1 ); // "bc12" begins at offset 1

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

HLA low-level calling sequence examples:

push( hexValueStr ); // Same as first example above

pushd( 0 );

call conv.strToh64;

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

// Same as second example above

static

strabc12 :string := "abc12";

.

.

.

push( strabc12 ); // Note that strabc12 points at "abc12".

pushd( 1 ); // Index to "bc12" in "abc12".

call conv.strToh64;

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

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

This function converts the hexadecimal character sequence beginning at the indexth character position in the string to a 128-bit numeric value. It raises an overflow exception if the value is outside the range $0000_0000_0000_0000_0000_0000_0000_0000..$FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF. This function returns the result in the variable specified by the dest parameter.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "hexValueStr" (index=0) to numeric form and store the

// 128-bit result into the 1wordDest variable:

conv.strToh128( hexValueStr, 0, 1wordDest );

// The following demonstrates using a non-zero index:

conv.strToh128( "abc1234567890abcdef", 3, 1wordDest );

HLA low-level calling sequence examples:

// Option #1: lwordDest is a STATIC/READONLY/STORAGE

// variable:

push( hexValueStr ); // Same as first example above

pushd( 0 );

pushd( &lwordDest );

call conv.strToh128;

// Option #2: lwordDest is not a static object and

// a 32-bit register is available for use:

push( hexValueStr ); // Same as first example above

pushd( 0 );

lea( eax, lwordDest ); // Assuming EAX is available

push( eax );

call conv.strToh128;

// Option #3: lwordDest is an automatic (var) object and

// no 32-bit registers are available for use:

push( hexValueStr ); // Same as first example above

pushd( 0 );

push( ebp );

add( @offset( lwordDest ), (type dword [esp]) );

call conv.strToh128;

// Option #4: lwordDest is a complex addressing mode object and

// no 32-bit registers are available for use:

push( hexValueStr ); // Same as first example above

pushd( 0 );

sub( 4, esp );

push( eax );

lea( eax, lwordDest );

mov( eax, [esp+4] );

pop( eax );

call conv.strToh128;

Signed Integer Conversions

The integer conversion functions process signed integer values that are 8, 16, 32, 64, or 128 bits long. Functions in this category compute the output size (in print positions) of an integer, convert an integer to a sequence of characters, and convert a sequence of characters to an integer value.

Internal Functions

These functions are for internal use by the standard library. You should not call these functions in your own code. The internal functions in this category are conv._intToBuf32, conv._intToBuf32Size, conv._intToBuf64, conv._intToBuf64Size, conv._intToBuf128, conv._intToBuf128Size


Integer Size Calculations

These routines return the size, in screen print positions, it would take to print the signed integer passed in the specified parameter. They return their value in the EAX register (the value always fits in AL and AX, if you’d prefer to use these registes as the return value). The count includes room for a minus sign if the number is negative. Note that these routines include print positions required by underscores if you’ve enabled underscore output in values (see conv.setUnderscores and conv.getUnderscores for details).

procedure conv.i8Size( b:byte in al ); @returns( "eax" );

This function computes the number of print positions required by the 8-bit signed integer passed in the AL register. Because the number of decimal positions is always three or less, the internal underscores flag does not affect the value this function returns. This function will always return a value in the range 1..4 (e.g., four positions for a value like "-128").

HLA high-level calling sequence examples:

conv.i8Size( byteVariable );

mov( eax, numSize );

conv.i8Size( <byte register> ); // al, ah, bl, bh, cl, ch, dl, dh

mov( eax, int8Size );

conv.i8Size( <constant> ); // Must fit into eight bits

mov( al, constantsSize );

Because conv.i8Size passes its input parameter in the AL register, any form of the high-level calling sequence except "conv.i8Size( al );" will automatically generate an instruction of the form "mov(<operand>,al);". Therefore, if possible, you should try to have the value whose size you wish to compute already sitting in the AL register and pass AL as the parameter to conv.i8Size.

HLA low-level calling sequence examples:

mov( byteVariable, al );

call conv.i8Size;

mov( eax, numSize );

mov( <byte register>, al ); // ah, bl, bh, cl, ch, dl, dh

call conv.i8Size;

mov( ax, wordVariable );

// Explicit Examples:

mov( bh, al );

call conv.i8Size;

mov( al, bhSize );

call conv.i8Size; // Assume value is already in AL

mov( al, alSize );

mov( 123, al ); // Example of computing the size of a constant

call conv.i8Size;

mov( eax, constsSize );

It might seem silly to compute the size of a constant as this last example is doing, as the constant’s print width is known at compile time. Note, however, that this sequence could appear as part of a macro expansion and the literal constant "123" could actually be the result of expanding a macro parameter.

procedure conv.i16Size( w:word in ax ); @returns( "eax" );

This function computes the number of print positions required by the 16-bit signed integer passed in the AX register. If the internal underscores flag is set and the integer value is greater than 999 (or less than -999) then this function will account for the underscores injected by the integer to string conversion routines. This function will always return a value in the range 1..6 if the underscores flag is not set (e.g., "-12345") or a value in the range 1…7 if the internal underscores flag is set (e.g., "-12_345").

HLA high-level calling sequence examples:

conv.i16Size( wordVariable );

mov( eax, numSize );

conv.i16Size( <word register> ); // ax, bx, cx, dx, bp, sp, si, di

mov( eax, int16Size );

conv.i16Size( <constant> ); // Must fit into 16 bits

mov( al, constantsSize );

Because conv.i16Size passes its input parameter in the AX register, any form of the high-level calling sequence except "conv.i16Size( ax );" will automatically generate an instruction of the form "mov(<operand>,ax);". Therefore, if possible, you should try to have the value whose size you wish to compute already sitting in the AX register and pass AX as the parameter to conv.i16Size.

HLA low-level calling sequence examples:

mov( wordVariable, ax );

call conv.i16Size;

mov( eax, numSize );

mov( <word register>, ax ); // bx, cx, dx, bp, sp, si, or di

call conv.i16Size;

mov( ax, wordVariable );

// Explicit Examples:

mov( bx, ax );

call conv.i16Size;

mov( al, bxSize );

call conv.i16Size; // Assume value is already in AX

mov( al, axSize );

mov( 12345, ax ); // Example of computing the size of a constant

call conv.i16Size;

mov( eax, constsSize );

See the comment at the end of conv.i8Size about passing constants to these functions.

procedure conv.i32Size( d:dword in eax ); @returns( "eax" );

This function computes the number of print positions required by the 32-bit signed integer passed in the EAX register. If the internal underscores flag is set this function will include print positions for the underscores that the conversion routines will inject into the string. This function will always return a value in the range 1..11 if the underscores flag is not set (e.g., "-1000000000") or a value in the range 1…14 if the internal underscores flag is set (e.g., "-1_000_000_000").

HLA high-level calling sequence examples:

conv.i32Size( wordVariable );

mov( eax, numSize );

conv.i32Size( <dword register> ); // eax, ebx, ecx, edx,

mov( eax, int16Size )l // ebp, esp, esi, or edi

conv.i32Size( <constant> ); // Must fit into 32 bits

mov( al, constantsSize );

Because conv.i32Size passes its input parameter in the EAX register, any form of the high-level calling sequence except "conv.i32Size( eax );" will automatically generate an instruction of the form "mov(<operand>,eax);". Therefore, if possible, you should try to have the value whose size you wish to compute already sitting in the EAX register and pass EAX as the parameter to conv.i32Size.

HLA low-level calling sequence examples:

mov( dwordVariable, eax );

call conv.i32Size;

mov( eax, numSize );

mov( <dword register>, eax ); // ebx, ecx, edx,

call conv.i32Size; // ebp, esp, esi, or edi

mov( ax, wordVariable );

// Explicit Examples:

mov( ebx, eax );

call conv.i32Size;

mov( al, bxSize );

call conv.i32Size; // Assume value is already in AX

mov( al, axSize );

mov( 1234567890, eax ); // Example of computing

call conv.i32Size; // the size of a constant.

mov( eax, constsSize );

See the comment at the end of conv.i8Size about passing constants to these functions.

procedure conv.i64Size( q:qword ); @returns( "eax" );

This function computes the number of print positions required by the 64-bit signed integer passed on the stack. If the internal underscores flag is set this function will include print positions for the underscores that the conversion routines will inject into the string. This function will always return a value in the range 1..20 if the underscores flag is not set (e.g., "-9223372036854775807") or a value in the range 1..26 if the internal underscores flag is set (e.g., "-9_223_372_036_854_775_808").

HLA high-level calling sequence examples:

conv.i64Size( qwordVariable );

mov( eax, numSize );

conv.i64Size( <constant> ); // Must fit into 64 bits

mov( al, constantsSize );

HLA low-level calling sequence examples:

push( (type dword qwordVariable[4])); // Push H.O. dword first

push( (type dword qwordVariable[0])); // Push L.O. dword second

call conv.i64Size;

mov( eax, numSize );

// Compute the size of a 64-bit constant:

pushd( 12345 >> 32 ); // Push H.O. dword first

pushd( 12345 & $FFFF_FFFF ); // Push L.O. dword second

call conv.i64Size;

mov( eax, constsSize );

See the comment at the end of conv.i8Size about passing constants to these functions. If you make a habit of explicitly passing 64-bit constants to this function, you might consider writing a macro to push the 64-bit constant for you (see the chapter on "Passing Parameters to Standard Library Routines" for more details).

procedure conv.i128Size( l:lword ); @returns( "eax" );

This function computes the number of print positions required by the 128-bit signed integer passed in the EAX register. If the internal underscores flag is set this function will include print positions for the underscores that the conversion routines will inject into the string. This function will always return a value in the range 1..40 if the underscores flag is not set (e.g., "-170141183460469231731687303715884105727") or a value in the range 1…52 if the internal underscores flag is set (e.g., "-170_141_183_460_469_231_731_687_303_715_884_105_728").

HLA high-level calling sequence examples:

conv.i128Size( lwordVariable );

mov( eax, numSize );

conv.i128Size( <constant> ); // Must fit into 128 bits

mov( al, constantsSize );

HLA low-level calling sequence examples:

push( (type dword lwordVariable[12])); // Push H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // Push L.O. dword last

call conv.i64Size;

mov( eax, numSize );

// Compute the size of a 128-bit constant:

pushd( 12345 >> 96 ); // Push H.O. dword first

pushd( (12345 >> 64) & $FFFF_FFFF );

pushd( (12345 >> 32 ) & $FFFF_FFFF );

pushd( 12345 & $FFFF_FFFF ); // Push L.O. dword last

call conv.i128Size;

mov( eax, constsSize );

See the comment at the end of conv.i8Size about passing constants to these functions. If you make a habit of explicitly passing 128-bit constants to this function, you might consider writing a macro to push the 128-bit constant for you (see the chapter on "Passing Parameters to Standard Library Routines" for more details).

Signed Integer Numeric to Buffer Conversions

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. Note that these functions do not zero terminate the string; if you want a zero-terminated string, then store a zero at the byte pointed at by EDI upon return from these functions.

If the internal underscores flag is set (see conv.getUnderscores and conv.setUnderscores for details), then these functions will insert an underscore between each group of three digits starting with the least significant digit.

procedure conv.i8ToBuf( i8 :int8 in al; var buf:var in edi );

This function converts the 8-bit signed integer passed in AL to a sequence of 1..4 characters. The string this function produces is always in the range -128..127. Note that because this string always contains three or fewer digits, the internal underscores flag setting does not affect this function’s output.

HLA high-level calling sequence examples:

// The following will load "byteVariable" into AL and

// the address of "charArrayVariable" into EDI and then

// call conv.i8ToBuf:

conv.i8ToBuf( byteVariable, charArrayVariable );

// The following call will copy BH into AL and

// EDX into EDI prior to calling conv.i8ToBuf:

conv.i8ToBuf( bh, [edx] );

// The following just calls conv.i8ToBuf as AL and EDI

// already hold the parameter values:

conv.i8ToBuf( al, [edi] );

// The following loads the constant in AL and calls

// conv.i8ToBuf:

conv.i8ToBuf( <constant>, [edi] ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AL and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. byte of EAX (i.e., AL) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AL and [EDI].

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable:

mov( byteVariable, al );

lea( edi, charArrayVariable );

call conv.i8ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( byteVariable, al );

mov( &charArrayVariable, edi );

call conv.i8ToBuf;

// Passing a pair of registers (that are not

// AL and EDI):

mov( bh, al );

mov( edx, edi );

call conv.i8ToBuf;

// Passing a constant:

mov( <constant>, al );

call conv.i8ToBuf; // Assume EDI already contains buffer address.

procedure conv.i16ToBuf( i16 :int16 in ax; var buf:var in edi )

This function converts the 16-bit signed integer passed in AX to a sequence of 1..6 characters if the internal underscores flag is false, 1..7 characters if the underscores flag contains true. The string this function produces is always in the range -32768..32767. If the internal underscores flag contains true and the value is greater than 999 or less than -999, then this function emits an underscore between the third and fourth digits (from the right) in the string.

HLA high-level calling sequence examples:

// The following will load "wordVariable" into AX and

// the address of "charArrayVariable" into EDI and then

// call conv.i16ToBuf:

conv.i16ToBuf( wordVariable, charArrayVariable );

// The following call will copy BX into AX and

// EDX into EDI prior to calling conv.i16ToBuf:

conv.i16ToBuf( bx, [edx] );

// The following just calls conv.i16ToBuf as AX and EDI

// already hold the parameter values:

conv.i16ToBuf( ax, [edi] );

// The following loads the constant in AX and calls

// conv.i16ToBuf:

conv.i16ToBuf( <constant>, [edi] ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. word of EAX (i.e., AX) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AX and [EDI].

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable:

mov( wordVariable, ax );

lea( edi, charArrayVariable );

call conv.i16ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( wordVariable, ax );

mov( &charArrayVariable, edi );

call conv.i16ToBuf;

// Passing a pair of registers (that are not

// AX and EDI):

mov( bx, ax );

mov( edx, edi );

call conv.i16ToBuf;

// Passing a constant:

mov( <constant>, ax );

call conv.i16ToBuf; // Assume EDI already contains buffer address.

procedure conv.i32ToBuf( i32 :int32 in eax; var buf:var in edi )

This function converts the 32-bit signed integer passed in EAX to a sequence of 1..11 characters if the internal underscores flag is false, 1..14 characters if the underscores flag contains true. The string this function produces is always in the range -2147483648..2147483647. If the internal underscores flag contains true and the value is greater than 999 or less than -999, then this function emits an underscore between the third and fourth digits (from the right) in the string.

HLA high-level calling sequence examples:

// The following will load "dwordVariable" into EAX and

// the address of "charArrayVariable" into EDI and then

// call conv.i32ToBuf:

conv.i32ToBuf( dwordVariable, charArrayVariable );

// The following call will copy EBX into EAX and

// EDX into EDI prior to calling conv.i32ToBuf:

conv.i32ToBuf( ebx, [edx] );

// The following just calls conv.i32ToBuf as EAX and EDI

// already hold the parameter values:

conv.i32ToBuf( eax, [edi] );

// The following loads the constant in EAX and calls

// conv.i32ToBuf:

conv.i32ToBuf( <constant>, [edi] ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EAX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite EAX before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not EAX and [EDI].

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

mov( dwordVariable, eax );

lea( edi, charArrayVariable );

call conv.i32ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( dwordVariable, eax );

mov( &charArrayVariable, edi );

call conv.i32ToBuf;

// Passing a pair of registers (that are not

// EAX and EDI):

mov( ebx, eax );

mov( edx, edi );

call conv.i32ToBuf;

// Passing a constant:

mov( <constant>, eax );

call conv.i32ToBuf; // Assume EDI already contains buffer address.

procedure conv.i64ToBuf( q :qword; var buf:var in edi )

This function converts the 64-bit signed integer passed in q to a sequence of 1..20 characters if the internal underscores flag is false, 1..26 characters if the underscores flag contains true. The string this function produces is always in the range -9223372036854775808 .. 9223372036854775807. If the internal underscores flag contains true and the value is greater than 999 or less than -999, then this function emits an underscore between each group of three digits starting with the least significant digit.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.i64ToBuf:

conv.i64ToBuf( qwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.i64ToBuf:

conv.i64ToBuf( <constant>, [edi] ); // <constant> must fit in 64 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.i64ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.i64ToBuf;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.i64ToBuf; // Assume EDI already contains buffer address.

procedure conv.i128ToBuf( l :lword; var buf:var in edi )

This function converts the 128-bit signed integer passed in l to a sequence of 1..40 characters if the internal underscores flag is false, 1..53 characters if the underscores flag contains true. The string this function produces is always in the range -170141183460469231731687303715884105728 .. 170141183460469231731687303715884105727. If the internal underscores flag contains true and the value is greater than 999 or less than -999, then this function emits an underscore between each group of three digits starting with the least significant digit.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.i128ToBuf:

conv.i128ToBuf( lwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.i128ToBuf:

conv.i128ToBuf( <constant>, [edi] ); // <constant> must fit in 128 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing an lword variable and a buffer variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.i128ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.i128ToBuf;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.i128ToBuf; // Assume EDI already contains buffer address.

Integer Numeric to String Conversions

These routines convert a signed integer value ( 8, 16, 32, 64, or 128 bits) to a string. The standard ("unadorned") functions store the string data into a string object that you pass as a parameter to the function. That string object must be preallocated and large enough to receive the string result (else a string overflow occurs). The "adorned" functions, whose names begin with "a_" automatically allocate storage on the heap, store the converted string into that heap object, and then return a pointer to the newly allocated string in the EAX register (it is the caller’s responsibility to free the storage when it is no longer needed).

These functions let you specify a minimum field width and a fill character. 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.

Here are the maximum number of print positions these routines will produce for each data type before considering the minimum field width:

Underscores flag is false:

8 bits: 4 (-128..127)

16 bits: 6 (-32768..32767)

32 bits: 11 (-2147483648..2147483647)

64 bits: 20 (-9223372036854775808..9223372036854775807)

128 bits: 40 (-170141183460469231731687303715884105728 ..

170141183460469231731687303715884105728)

Underscores flag is true:

8 bits: 4 (-128..127)

16 bits: 7 (-32_768..32_767)

32 bits: 14 (-2_147_483_648..2_147_483_647)

64 bits: 26 (-9_223_372_036_854_775_808..9_223_372_036_854_775_807)

128 bits: 52 (-170_141_183_460_469_231_731_687_303_715_884_105_728 ..

170_141_183_460_469_231_731_687_303_715_884_105_728)

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

This function converts an 8-bit signed integer to the decimal string representation of that integer and stores the string in the preallocated string object specified by the dest paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. Note that the internal underscores flag will not affect the output because 8-bit integers are always three digits or smaller.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.i8ToStr:

conv.i8ToStr( byteVariable, destStr );

// The following call will BH’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.i8ToStr:

conv.i8ToStr( bh, edx );

// The following pushes the constant and destStr and calls

// conv.i8ToStr:

conv.bToBuf( <constant>, destStr ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.i8ToStr;

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

push( destStr );

call conv.i8ToStr;

// Passing a byte variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

push( destStr );

call conv.i8ToStr;

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

push( edx );

call conv.i8ToStr;

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

push( edx );

call conv.i8ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.i8ToStr;

procedure conv.a_i8ToStr ( b:int8; width:int32; fill:char );
@returns( "eax" );

This function converts an 8-bit signed integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. Note that the internal underscores flag will not affect the output because 8-bit integers are always three digits or smaller.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and then call conv.a_i8ToStr:

conv.a_i8ToStr( byteVariable );

mov( eax, byteStr );

// The following call will BH’s value onto the stack

// before calling conv.a_i8ToStr:

conv.a_i8ToStr( bh );

mov( eax, byteStr );

// The following pushes the constant and calls

// conv.a_i8ToStr:

conv.a_i8ToStr( <constant> ); // <constant> must fit in 8 bits

mov( eax, byteStr );

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

call conv.a_i8ToStr;

mov( eax, destStr );

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

call conv.a_i8ToStr;

mov( eax, destStr );

// Passing a byte variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

call conv.a_i8ToStr;

mov( eax, destStr );

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

call conv.a_i8ToStr;

mov( eax, byteStr );

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

call conv.a_i8ToStr;

mov( eax, byteStr );

// Passing a constant:

pushd( <constant> );

call conv.a_i8ToStr;

mov( eax, byteStr );

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

This function converts a 16-bit signed integer to its decimal string representation and stores the string in the preallocated string object specified by the dest paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "wordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.i16ToStr:

conv.i16ToStr( wordVariable, destStr );

// The following call will BX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.i16ToStr:

conv.i16ToStr( bx, edx );

// The following pushes the constant and destStr and calls

// conv.i16ToStr:

conv.i16ToStr( <constant>, destStr ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.i16ToStr;

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

push( destStr );

call conv.i16ToStr;

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

push( destStr );

call conv.i16ToStr;

// Passing a pair of registers:

// BX = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BX

push( edx );

call conv.i16ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.i16ToStr;

procedure conv.a_i16ToStr( w:int16; width:int32; fill:char );
@returns( "eax" );

This function converts a 16-bit signed integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "wordVariable" and call conv.a_i16ToStr:

conv.a_i16ToStr( wordVariable );

mov( eax, destStr );

// The following call will BX’s value onto the stack

// before calling conv.a_i16ToStr:

conv.a_i16ToStr( bx );

// The following pushes the constant and calls

// conv.a_i16ToStr:

conv.a_i16ToStr( <const>, destStr ); // <const> must fit in 16 bits

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( wordVariable, eax );

push( eax );

call conv.a_i16ToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

call conv.a_i16ToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

call conv.a_i16ToStr;

mov( eax, destStr );

// Passing a pair of registers:

// BX = value to print.

push( ebx ); // Pushes BX

call conv.a_i16ToStr;

mov( eax, wordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_i16ToStr;

mov( eax, destStr );

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

This function converts a 32-bit signed integer to its decimal string representation and stores the string in the preallocated string object specified by the buffer paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.i32ToStr:

conv.i32ToStr( dwordVariable, destStr );

// The following call will push EBX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.i32ToStr:

conv.i32ToStr( ebx, edx );

// The following pushes the constant and destStr and calls

// conv.i32ToStr:

conv.i32ToStr( <constant>, destStr ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data. In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string data.

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

push( dwordVariable );

push( destStr );

call conv.i32ToStr;

// Passing a pair of registers:

// EBX = value to print, EDX = pointer to string object.

push( ebx );

push( edx );

call conv.i32ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.i32ToStr;

procedure conv.a_i32ToStr( d:int32; width:int32; fill:char );
@returns( "eax" );

This function converts a 32-bit signed integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and then call conv.a_i32ToStr:

conv.a_i32ToStr( dwordVariable );

mov( eax, destStr );

// The following call will push EBX’s value onto the stack

// before calling conv.a_i32ToStr:

conv.a_i32ToStr( ebx );

// The following pushes the constant and calls

// conv.a_i32ToStr:

conv.a_i32ToStr( <constant>, destStr ); // <constant> must fit in 32 bits

HLA low-level calling sequence examples:

// Passing a dword variable:

push( dwordVariable );

call conv.a_i32ToStr;

mov( eax, destStr );

// Passing a register:

// EBX = value to print.

push( ebx );

call conv.a_i32ToStr;

mov( eax, dwordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_i32ToStr;

mov( eax, destStr );

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

This function converts a 64-bit signed integer to its decimal string representation and stores the string in the preallocated string object specified by the buffer paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// and the value of the destStr string variable

// onto the stack and then call conv.i64ToStr:

conv.i64ToStr( qwordVariable, destStr );

// The following pushes the constant onto the stack along with

// the value held in the destStr variable and calls

// conv.i64ToStr:

conv.i64ToStr( <constant>, destStr ); // <constant> must fit in 64 bits

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

push( destStr );

call conv.i64ToStr;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

push( destStr );

call conv.i64ToStr;

procedure conv.a_i64ToStr( q:qword; width:int32; fill:char );
@returns( "eax" );

This function converts a 64-bit signed integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack and then call conv.a_i64ToStr:

conv.a_i64ToStr( qwordVariable );

mov( eax, destStr );

// The following pushes the constant onto the stack and calls

// conv.a_i64ToStr:

conv.a_i64ToStr( <constant> ); // <constant> must fit in 64 bits

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing a qword variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

call conv.a_i64ToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.a_i64ToStr;

mov( eax, destStr );

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

This function converts a 128-bit signed integer to its decimal string representation and stores the string in the preallocated string object specified by the buffer paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// and destStr onto the stack, and then call conv.i128ToStr:

conv.i128ToStr( lwordVariable, destStr );

// The following pushes the constant onto the stack and calls

// conv.i128ToStr:

conv.i128ToStr( <constant>, edx ); // EDX contains string pointer value.

HLA low-level calling sequence examples:

// Passing an lword variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

push( destStr );

call conv.i128ToStr;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

push( edx ); // EDX contains string pointer value.

call conv.i128ToStr;

 

procedure conv.a_i128ToStr( l:lword; width:int32; fill:char );
@returns( "eax" );

This function converts a 128-bit signed integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, and then call conv.a_i128ToStr:

conv.a_i128ToStr( lwordVariable );

mov( eax, destStr );

// The following pushes the constant onto the stack and calls

// conv.a_i128ToStr:

conv.a_i128ToStr( <constant> );

mov( eax, destStr );

HLA low-level calling sequence examples:

// Passing an lword variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

call conv.a_i128ToStr;

mov( eax, destStr );

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.a_i128ToStr;

mov( eax, destStr );

Signed Integer String to Numeric Conversions

The standard library string to integer conversion routines convert a sequence of digits, possibly prefaced by a minus sign, into the corresponding signed integer value. These routines begin by skipping over any leading delimiter characters (see the conv.getDelimiters and conv.setDelimiters functions for details), handling an optional minus sign, followed by any number of decimal digits and underscores (these routines ignore the underscores). Conversion stops at the end of the string or upon encountering a delimiter character.

These routines will raise a conversion error exception if they encounter a 7-bit ASCII character that is not a decimal digit, an underscore, or a delimiter character during the translation. These routines will raise an illegal character exception if they encounter a non-ASCII character (one with its H.O. bit set). These routines will raise a value out of range exception if the converted value will not fit in the destination data object.

There are two basic sets of string to numeric conversion routines: the conv.atoi* routines and the conv.strToi* routines. The atoi* routines process the characters pointed at by the ESI register. The strToi* routines process data in a string object, starting at an offset specified by a second parameter. For example, "conv.strToi8( "12345", 3);" returns the value 45 because it begins processing the string at (zero-based) offset 3 in the string.

procedure conv.atoi8 ( buffer:var in esi ); @returns( "al" );

This function converts the sequence of characters starting at the memory address held in ESI to an 8-bit signed integer. It returns the value (in the range -128..+127) in AL. Note that this function actually returns the sign-extended value in EAX, so you may use EAX if it is more convenient to do so. Upon successful return, ESI is pointing at the delimiter character at the end of the sequence of digits.

HLA high-level calling sequence examples:

// The following will convert the decimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in AL:

conv.atoi8( [esi] );

mov( al, numericResult );

// The following loads ESI with the address of

// a sequence of decimal characters (held in an HLA

// string) and converts them to an 8-bit number:

conv.atoi8( sourceStr ); // Loads "sourceStr" into ESI

mov( al, byteVariable );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoi8;

mov( al, numericResult );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoi8;

mov( al, num12 );

procedure conv.atoi16 ( buffer:var in esi ); @returns( "ax" );

This function converts the sequence of characters starting at the memory address held in ESI to a 16-bit signed integer. It returns the value (in the range -32768..+32767) in AX. Note that this function actually returns the sign-extended value in EAX, so you may use EAX if it is more convenient to do so. Upon successful return, ESI is pointing at the delimiter character at the end of the sequence of digits.

HLA high-level calling sequence examples:

// The following will convert the decimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in AX:

conv.atoi16( [esi] );

mov( ax, numericResult );

// The following loads ESI with the address of

// a sequence of decimal characters (held in an HLA

// string) and converts them to a 16-bit number:

conv.atoi16( sourceStr ); // Loads "sourceStr" into ESI

mov( ax, wordVariable );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoi16;

mov( ax, numericResult );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoi16;

mov( ax, num12 );

procedure conv.atoi32 ( buffer:var in esi ); @returns( "eax" );

This function converts the sequence of characters starting at the memory address held in ESI to a 32-bit signed integer. It returns the value (in the range -2147483648..+2147483647) in EAX. ESI is pointing at the delimiter character at the end of the sequence of digits.

HLA high-level calling sequence examples:

// The following will convert the hexadecimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in EAX:

conv.atoi32( [esi] );

mov( eax, numericResult );

// The following loads ESI with the address of

// a sequence of decimal characters (held in an HLA

// string) and converts them to a 32-bit number:

conv.atoi32( sourceStr ); // Loads "sourceStr" into ESI

mov( eax, dwordVariable );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoi32;

mov( eax, numericResult );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoi32;

mov( eax, num12 );

procedure conv.atoi64 ( buffer:var in esi ); @returns( "edx:eax" );

This function converts the sequence of characters starting at the memory address held in ESI to a 64-bit signed integer. It returns the value (in the range -9223372036854775808..+9223372036854775807) in EDX:EAX (EDX contains the H.O. dword). ESI is pointing at the delimiter character at the end of the sequence of digits.

HLA high-level calling sequence examples:

// The following will convert the decimal characters in memory

// at the address specified by [esi] into a numeric value

// and return that value in EDX:EAX:

conv.atoi64( [esi] );

mov( eax, (type dword hex64NumericResult[0]) );

mov( edx, (type dword hex64NumericResult[4]) );

// The following loads ESI with the address of

// a sequence of decimal characters (held in an HLA

// string) and converts them to a 64-bit number:

conv.atoi64( sourceStr ); // Loads "sourceStr" into ESI

mov( eax, (type dword qwordVariable[0]) );

mov( edx, (type dword qwordVariable[4]) );

HLA low-level calling sequence examples:

// Same as first example above – ESI already contains

// the address of the first character to convert:

call conv.atoi64;

mov( eax, (type dword numericResult[0]) );

mov( edx, (type dword numericResult[4]) );

// Same as second example above

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

call conv.atoi64;

mov( eax, (type dword qwordVariable[0]) );

mov( edx, (type dword qwordVariable[4]) );

procedure conv.atoi128( buffer:var in esi; var l:lword );

This function converts the sequence of characters starting at the memory address held in ESI to a 128-bit signed integer. It returns the value (in the range -170141183460469231731687303715884105728..+170141183460469231731687303715884105727) in the l parameter that is passed by reference to this function. ESI is pointing at the delimiter character at the end of the sequence of digits.

HLA high-level calling sequence examples:

// The following will convert the decimal characters in memory

// at the address specified by [esi] into a numeric value

// and stores that value in lwordDest (passed by reference):

conv.atoi128( [esi], lwordDest );

// The following loads ESI with the address of

// a sequence of decimal characters (held in an HLA

// string) and converts them to a 128-bit number that it

// stores in lwordDest:

conv.atoi128( sourceStr, lwordDest ); // Loads "sourceStr" into ESI

HLA low-level calling sequence examples:

// Option 1: lwordDest is a static object declared in a

// HLA STATIC, READONLY, or STORAGE section:

// As with the first example above, assume ESI already

// contains the address of the string to convert:

pushd( &lwordDest ); // Pass address of lwordDest as reference parm.

call conv.atoi128;

// Option 2: lwordDest is a simple automatic variable (no indexing)

// declared in a VAR section (or as a parameter). Assume that

// no 32-bit registers can be disturbed by this code.

// As with the first example above, assume ESI already

// contains the address of the string to convert:

push( ebp );

add( @offset( lwordDest ), (type dword [esp]));

call conv.atoi128;

// Option 3: lwordDest is a complex addressing mode and at least

// one 32-bit register is available for use by this code.

// As with the first example above, assume ESI already

// contains the address of the string to convert:

lea( eax, lwordDest ); // Assume EAX is the available register

push( eax );

call conv.atoi128;

// Same as second high-level example above. Assumes that

// lwordDest is a static object.

static

sourceStr :string := "12";

.

.

.
mov( sourceStr, esi );

pushd( &lwordDest );

call conv.atoi128;

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

This function converts the sequence of characters starting at zero-based offset index within the string parameter s to an 8-bit signed integer. It returns the value (in the range -128..+127) in AL. Note that this function actually returns the sign-extended value in EAX, so you may use EAX if it is more convenient to do so.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "decValueStr" to numeric form:

conv.strToi8( decValueStr, 0 ); // Index=0 starts at beginning

mov( al, numericResult );

// The following demonstrates using a non-zero index:

conv.strToi8( "abc12", 3 ); // "12" begins at offset 3

mov( al, hex12 );

HLA low-level calling sequence examples:

push( decValueStr ); // Same as first example above

pushd( 0 );

call conv.strToi8;

mov( al, decNumericResult );

// Same as second example above

static

str12 :string := "abc12";

.

.

.

push( str12 ); // Note that str12 points at "abc12".

pushd( 3 ); // Index to "12" in "abc12".

call conv.strToi8;

mov( al, dec12 );

procedure conv.strToi16( s:string; index:dword )

This function converts the sequence of characters starting at zero-based offset index within the string parameter s to a 16-bit signed integer. It returns the value (in the range -32768..+32767) in AX. Note that this function actually returns the sign-extended value in EAX, so you may use EAX if it is more convenient to do so.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "decValueStr" to numeric form:

conv.strToi16( hexValueStr, 0 ); // Index=0 starts at beginning

mov( ax, wordVar );

// The following demonstrates using a non-zero index:

conv.strToi16( "abc1234", 3 ); // "1234" begins at offset 3

mov( ax, wordVar );

HLA low-level calling sequence examples:

push( decValueStr ); // Same as first example above

pushd( 0 );

call conv.strToi16;

mov( ax, wordVar );

// Same as second example above

static

str1200 :string := "abc1200";

.

.

.

push( str1200 ); // Note that str1200 points at "abc1200".

pushd( 3 ); // Index to "1200" in "abc1200".

call conv.strToi16;

mov( ax, wordVar );

procedure conv.strToi32( s:string; index:dword )

This function converts the sequence of characters starting at zero-based offset index within the string parameter s to a 32-bit signed integer. It returns the value (in the range -2147483648..+2147483647) in EAX.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "decValueStr" to numeric form:

conv.strToi32( decValueStr, 0 ); // Index=0 starts at beginning

mov( eax, dwordVar );

// The following demonstrates using a non-zero index:

conv.strToi32( "abc12_345", 3 ); // "12_345" begins at offset 3

mov( eax, dwordVar );

HLA low-level calling sequence examples:

push( decValueStr ); // Same as first example above

pushd( 0 );

call conv.strToi32;

mov( eax, dwordVar );

// Same as second example above

static

str12345 :string := "abc-12_345";

.

.

.

push( str12345 ); // Note that str12345 points at "abc-12_345".

pushd( 3 ); // Index to "-12_345" in "abc-12_345".

call conv.strToi32;

mov( eax, dwordVar ); // dwordVar now contains -12,345.

procedure conv.strToi64( s:string; index:dword )

This function converts the sequence of characters starting at zero-based offset index within the string parameter s to a 64-bit signed integer. It returns the value (in the range -9223372036854775808 .. +9223372036854775807) in EDX:EAX (EDX contains the H.O. dword).

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "decValueStr" to numeric form:

conv.strToi64( decValueStr, 0 ); // Index=0 starts at beginning

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

// The following demonstrates using a non-zero index:

conv.strToi64( "a-123", 1 ); // "-123" begins at offset 1

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

HLA low-level calling sequence examples:

push( decValueStr ); // Same as first example above

pushd( 0 );

call conv.strToi64;

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

// Same as second example above

static

strabc12 :string := "a-123";

.

.

.

push( strabc12 ); // Note that strabc12 points at "a-123".

pushd( 1 ); // Index to "-123" in "a-123".

call conv.strToi64;

mov( eax, (type dword qwordVar[0]) );

mov( edx, (type dword qwordVar[4]) );

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

This function converts the sequence of characters starting at zero-based offset index within the string parameter s to a 128-bit signed integer. It returns the value (in the range -170141183460469231731687303715884105728.. +170141183460469231731687303715884105727) in the parameter l that you pass by reference to this function.

HLA high-level calling sequence examples:

// The following will convert the characters at the beginning

// of "decValueStr" (index=0) to numeric form and store the

// 128-bit result into the 1wordDest variable:

conv.strToi128( decValueStr, 0, 1wordDest );

// The following demonstrates using a non-zero index:

conv.strToi128( "abc1234567890123456789", 3, 1wordDest );

HLA low-level calling sequence examples:

// Option #1: lwordDest is a STATIC/READONLY/STORAGE

// variable:

push( decValueStr ); // Same as first example above

pushd( 0 );

pushd( &lwordDest );

call conv.strToi128;

// Option #2: lwordDest is not a static object and

// a 32-bit register is available for use:

push( decValueStr ); // Same as first example above

pushd( 0 );

lea( eax, lwordDest ); // Assuming EAX is available

push( eax );

call conv.strToi128;

// Option #3: lwordDest is an automatic (var) object and

// no 32-bit registers are available for use:

push( decValueStr ); // Same as first example above

pushd( 0 );

push( ebp );

add( @offset( lwordDest ), (type dword [esp]) );

call conv.strToi128;

// Option #4: lwordDest is a complex addressing mode object and

// no 32-bit registers are available for use:

push( decValueStr ); // Same as first example above

pushd( 0 );

sub( 4, esp );

push( eax );

lea( eax, lwordDest );

mov( eax, [esp+4] );

pop( eax );

call conv.strToi128;

Unsigned Integer Conversions

The integer conversion function process signed integer values that are 8, 16, 32, 64, or 128 bits long. Functions in this category compute the output size (in print positions) of an integer, convert an integer to a sequence of characters, and convert a sequence of characters to an integer value.

Internal Routines

The following routines are used internally by the standard library unsigned integer code and you should not directly call them: conv._u8Size, conv._u16Size, and conv._u32Size.

Unsigned Integer Size Calculations

These routines return the size, in screen print positions, it would take to print the unsigned integer passed in the specified parameter. They return their value in the EAX register. Note that these routines include print positions required by underscores if you’ve enabled underscore output in values (see conv.setUnderscores and conv.getUnderscores for details).

It should go without saying that if you compute the size of an unsigned integer and then change the value of the internal underscores flag, the size you’ve computed may be invalid.

procedure conv.u8Size( b:byte in al ); @returns( "eax" );

Computes the output size of an 8-bit unsigned integer (passed in AL) and returns this value in EAX. The return result will always be a value in the range 1..3. The internal underscores flag does not affect the result this function returns.

HLA high-level calling sequence examples:

conv.u8Size( byteVariable );

mov( eax, numSize );

conv.u8Size( <byte register> ); // al, ah, bl, bh, cl, ch, dl, dh

mov( eax, int8Size );

conv.u8Size( <constant> ); // Must fit into eight bits

mov( al, constantsSize );

Because conv.u8Size passes its input parameter in the AL register, any form of the high-level calling sequence except "conv.u8Size( al );" will automatically generate an instruction of the form "mov(<operand>,al);". Therefore, if possible, you should try to have the value whose size you wish to compute already sitting in the AL register and pass AL as the parameter to conv.u8Size.

HLA low-level calling sequence examples:

mov( byteVariable, al );

call conv.u8Size;

mov( eax, numSize );

mov( <byte register>, al ); // ah, bl, bh, cl, ch, dl, dh

call conv.u8Size;

mov( ax, wordVariable );

// Explicit Examples:

mov( bh, al );

call conv.u8Size;

mov( al, bhSize );

call conv.u8Size; // Assume value is already in AL

mov( al, alSize );

mov( 123, al ); // Example of computing the size of a constant

call conv.u8Size;

mov( eax, constsSize );

It might seem silly to compute the size of a constant as this last example is doing, as the constant’s print width is known at compile time. Note, however, that this sequence could appear as part of a macro expansion and the literal constant "123" could actually be the result of expanding a macro parameter.

procedure conv.u16Size( w:word in ax )

Computes the output size of a 16-bit unsigned integer (passed in AX) and returns this value in EAX. The return result will always be a value in the range 1..5 if the internal underscores flag contains false, 1..6 if the underscores flag contains true.

HLA high-level calling sequence examples:

conv.u16Size( wordVariable );

mov( eax, numSize );

conv.u16Size( <word register> ); // ax, bx, cx, dx, bp, sp, si, di

mov( eax, int16Size );

conv.u16Size( <constant> ); // Must fit into 16 bits

mov( al, constantsSize );

Because conv.u16Size passes its input parameter in the AX register, any form of the high-level calling sequence except "conv.u16Size( ax );" will automatically generate an instruction of the form "mov(<operand>,ax);". Therefore, if possible, you should try to have the value whose size you wish to compute already sitting in the AX register and pass AX as the parameter to conv.u16Size.

HLA low-level calling sequence examples:

mov( wordVariable, ax );

call conv.u16Size;

mov( eax, numSize );

mov( <word register>, ax ); // bx, cx, dx, bp, sp, si, or di

call conv.u16Size;

mov( ax, wordVariable );

// Explicit Examples:

mov( bx, ax );

call conv.u16Size;

mov( al, bxSize );

call conv.u16Size; // Assume value is already in AX

mov( al, axSize );

mov( 12345, ax ); // Example of computing the size of a constant

call conv.u16Size;

mov( eax, constsSize );

See the comment at the end of conv.i8Size about passing constants to these functions.

procedure conv.u32Size( d:dword in eax )

Computes the output size of a 32-bit unsigned integer (passed in EAX) and returns this value in EAX. The return result will always be a value in the range 1..10 if the internal underscores flag contains false, 1..11 if the underscores flag contains true.

HLA high-level calling sequence examples:

conv.u32Size( wordVariable );

mov( eax, numSize );

conv.u32Size( <dword register> ); // eax, ebx, ecx, edx,

mov( eax, int16Size )l // ebp, esp, esi, or edi

conv.u32Size( <constant> ); // Must fit into 32 bits

mov( al, constantsSize );

Because conv.u32Size passes its input parameter in the EAX register, any form of the high-level calling sequence except "conv.u32Size( eax );" will automatically generate an instruction of the form "mov(<operand>,eax);". Therefore, if possible, you should try to have the value whose size you wish to compute already sitting in the EAX register and pass EAX as the parameter to conv.u32Size.

HLA low-level calling sequence examples:

mov( dwordVariable, eax );

call conv.u32Size;

mov( eax, numSize );

mov( <dword register>, eax ); // ebx, ecx, edx,

call conv.u32Size; // ebp, esp, esi, or edi

mov( ax, wordVariable );

// Explicit Examples:

mov( ebx, eax );

call conv.u32Size;

mov( al, bxSize );

call conv.u32Size; // Assume value is already in AX

mov( al, axSize );

mov( 1234567890, eax ); // Example of computing

call conv.u32Size; // the size of a constant.

mov( eax, constsSize );

See the comment at the end of conv.u8Size about passing constants to these functions.

procedure conv.u64Size( q:qword )

Computes the output size of a 64-bit unsigned integer (passed in in q parameter) and returns this value in EAX. The return result will always be a value in the range 1..20 (e.g., "18446744073709551615") if the internal underscores flag contains false, 1..26 if the underscores flag contains true (e.g., "18_446_744_073_709_551_615").

HLA high-level calling sequence examples:

conv.u64Size( qwordVariable );

mov( eax, numSize );

conv.u64Size( <constant> ); // Must fit into 64 bits

mov( al, constantsSize );

HLA low-level calling sequence examples:

push( (type dword qwordVariable[4])); // Push H.O. dword first

push( (type dword qwordVariable[0])); // Push L.O. dword second

call conv.u64Size;

mov( eax, numSize );

// Compute the size of a 64-bit constant:

pushd( 12345 >> 32 ); // Push H.O. dword first

pushd( 12345 & $FFFF_FFFF ); // Push L.O. dword second

call conv.u64Size;

mov( eax, constsSize );

See the comment at the end of conv.u8Size about passing constants to these functions. If you make a habit of explicitly passing 64-bit constants to this function, you might consider writing a macro to push the 64-bit constant for you (see the chapter on "Passing Parameters to Standard Library Routines" for more details).

procedure conv.u128Size( l:lword )

Computes the output size of a 128-bit unsigned integer (passed in the l parameter) and returns this value in EAX. The return result will always be a value in the range 1..39 (e.g., "340282366920938463463374607431768211455") if the internal underscores flag contains false, 1..51 if the underscores flag contains true (e.g., "340_282_366_920_938_463_463_374_607_431_768_211_455").

HLA high-level calling sequence examples:

conv.u128Size( lwordVariable );

mov( eax, numSize );

conv.u128Size( <constant> ); // Must fit into 128 bits

mov( al, constantsSize );

HLA low-level calling sequence examples:

push( (type dword lwordVariable[12])); // Push H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // Push L.O. dword last

call conv.u64Size;

mov( eax, numSize );

// Compute the size of a 128-bit constant:

pushd( 12345 >> 96 ); // Push H.O. dword first

pushd( (12345 >> 64) & $FFFF_FFFF );

pushd( (12345 >> 32 ) & $FFFF_FFFF );

pushd( 12345 & $FFFF_FFFF ); // Push L.O. dword last

call conv.u128Size;

mov( eax, constsSize );

See the comment at the end of conv.u8Size about passing constants to these functions. If you make a habit of explicitly passing 128-bit constants to this function, you might consider writing a macro to push the 128-bit constant for you (see the chapter on "Passing Parameters to Standard Library Routines" for more details).

Unsigned Integer Numeric to Buffer Conversions

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. Note that these functions do not zero terminate the string; if you want a zero-terminated string, then store a zero at the byte pointed at by EDI upon return from these functions.

If the internal underscores flag is set (see conv.getUnderscores and conv.setUnderscores for details), then these functions will insert an underscore between each group of three digits starting with the least significant digit.

procedure conv.u8ToBuf( u8: uns8 in al )

This function converts the 8-bit unsigned integer passed in AL to a sequence of 1..3 characters. The string this function produces is always in the range 0..255. Note that because this string always contains three or fewer digits, the internal underscores flag setting does not affect this function’s output.

HLA high-level calling sequence examples:

// The following will load "byteVariable" into AL and

// the address of "charArrayVariable" into EDI and then

// call conv.u8ToBuf:

conv.u8ToBuf( byteVariable, charArrayVariable );

// The following call will copy BH into AL and

// EDX into EDI prior to calling conv.u8ToBuf:

conv.u8ToBuf( bh, [edx] );

// The following just calls conv.u8ToBuf as AL and EDI

// already hold the parameter values:

conv.u8ToBuf( al, [edi] );

// The following loads the constant in AL and calls

// conv.u8ToBuf:

conv.u8ToBuf( <constant>, [edi] ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AL and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. byte of EAX (i.e., AL) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AL and [EDI].

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable:

mov( byteVariable, al );

lea( edi, charArrayVariable );

call conv.u8ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( byteVariable, al );

mov( &charArrayVariable, edi );

call conv.u8ToBuf;

// Passing a pair of registers (that are not

// AL and EDI):

mov( bh, al );

mov( edx, edi );

call conv.u8ToBuf;

// Passing a constant:

mov( <constant>, al );

call conv.u8ToBuf; // Assume EDI already contains buffer address.

procedure conv.u16ToBuf( u16: uns16 in ax )

This function converts the 16-bit unsigned integer passed in AX to a sequence of 1..5 characters if the internal underscores flag is false, 1..6 characters if the underscores flag contains true. The string this function produces is always in the range 0..65535. If the internal underscores flag contains true and the value is greater than 999, then this function emits an underscore between the third and fourth digits (from the right) in the string.

HLA high-level calling sequence examples:

// The following will load "wordVariable" into AX and

// the address of "charArrayVariable" into EDI and then

// call conv.u16ToBuf:

conv.u16ToBuf( wordVariable, charArrayVariable );

// The following call will copy BX into AX and

// EDX into EDI prior to calling conv.u16ToBuf:

conv.u16ToBuf( bx, [edx] );

// The following just calls conv.u16ToBuf as AX and EDI

// already hold the parameter values:

conv.u16ToBuf( ax, [edi] );

// The following loads the constant in AX and calls

// conv.u16ToBuf:

conv.u16ToBuf( <constant>, [edi] ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, always keep in mind that these statements load the AX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite the L.O. word of EAX (i.e., AX) before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not AX and [EDI].

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable:

mov( wordVariable, ax );

lea( edi, charArrayVariable );

call conv.u16ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( wordVariable, ax );

mov( &charArrayVariable, edi );

call conv.u16ToBuf;

// Passing a pair of registers (that are not

// AX and EDI):

mov( bx, ax );

mov( edx, edi );

call conv.u16ToBuf;

// Passing a constant:

mov( <constant>, ax );

call conv.u16ToBuf; // Assume EDI already contains buffer address.

procedure conv.u32ToBuf( u32: uns32 in eax)

This function converts the 32-bit unsigned integer passed in EAX to a sequence of 1..10 characters if the internal underscores flag is false, 1..11 characters if the underscores flag contains true. The string this function produces is always in the range 0..4294967295. If the internal underscores flag contains true and the value is greater than 999, then this function emits an underscore between the third and fourth digits (from the right) in the string.

HLA high-level calling sequence examples:

// The following will load "dwordVariable" into EAX and

// the address of "charArrayVariable" into EDI and then

// call conv.u32ToBuf:

conv.u32ToBuf( dwordVariable, charArrayVariable );

// The following call will copy EBX into EAX and

// EDX into EDI prior to calling conv.u32ToBuf:

conv.u32ToBuf( ebx, [edx] );

// The following just calls conv.u32ToBuf as EAX and EDI

// already hold the parameter values:

conv.u32ToBuf( eax, [edi] );

// The following loads the constant in EAX and calls

// conv.u32ToBuf:

conv.u32ToBuf( <constant>, [edi] ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EAX and EDI registers with their respective parameter values. In particular, you should not specify [EAX] as the buffer address because the code that HLA generates can overwrite EAX before it copies the address to the EDI register. It goes without saying that this function will overwrite the values of EAX and EDI if the original parameters are not EAX and [EDI].

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

mov( dwordVariable, eax );

lea( edi, charArrayVariable );

call conv.u32ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

mov( dwordVariable, eax );

mov( &charArrayVariable, edi );

call conv.u32ToBuf;

// Passing a pair of registers (that are not

// EAX and EDI):

mov( ebx, eax );

mov( edx, edi );

call conv.u32ToBuf;

// Passing a constant:

mov( <constant>, eax );

call conv.u32ToBuf; // Assume EDI already contains buffer address.

procedure conv.u64ToBuf( q:qword )

This function converts the 64-bit unsigned integer passed in q to a sequence of 1..20 characters if the internal underscores flag is false, 1..26 characters if the underscores flag contains true. The string this function produces is always in the range 0 .. 18446744073709551615. If the internal underscores flag contains true and the value is greater than 999, then this function emits an underscore between each group of three digits starting with the least significant digit.

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.u64ToBuf:

conv.u64ToBuf( qwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.u64ToBuf:

conv.u64ToBuf( <constant>, [edi] ); // <constant> must fit in 64 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.u64ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.u64ToBuf;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

call conv.u64ToBuf; // Assume EDI already contains buffer address.

procedure conv.u128ToBuf( l:lword )

This function converts the 128-bit unsigned integer passed in l to a sequence of 1..39 characters if the internal underscores flag is false, 1..52 characters if the underscores flag contains true. The string this function produces is always in the range 0 .. 340282366920938463463374607431768211455. If the internal underscores flag contains true and the value is greater than 999, then this function emits an underscore between each group of three digits starting with the least significant digit.

HLA high-level calling sequence examples:

// The following will push the value of "lwordVariable"

// onto the stack, load the address of "charArrayVariable"

// into EDI and then call conv.u128ToBuf:

conv.u128ToBuf( lwordVariable, charArrayVariable );

// The following pushes the constant onto the stack and calls

// conv.u128ToBuf:

conv.u128ToBuf( <constant>, [edi] ); // <constant> must fit in 128 bits

When using the HLA high-level calling form, always keep in mind that these statements load the EDI register with the respective parameter value. It goes without saying that this function will overwrite the value of EDI if the original parameter is not [EDI].

HLA low-level calling sequence examples:

// Passing an lword variable and a buffer variable:

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

lea( edi, charArrayVariable );

call conv.u128ToBuf;

// Alternate form of above if charArrayVariable is

// a static object (STATIC, READONLY, or STORAGE):

push( (type dword lwordVariable[12])); // H.O. dword first

push( (type dword lwordVariable[8]));

push( (type dword lwordVariable[4]));

push( (type dword lwordVariable[0])); // L.O. dword last

mov( &charArrayVariable, edi );

call conv.u128ToBuf;

// Passing a constant:

pushd( <constant> >> 96 ); // Push H.O. dword of constant first.

pushd( (<constant> >> 64)& $FFFF_FFFF );

pushd( (<constant> >> 32)& $FFFF_FFFF );

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword last.

call conv.u128ToBuf; // Assume EDI already contains buffer address.

Unsigned Integer Numeric to String Conversions

These routines convert an unsigned integer value ( 8, 16, 32, 64, or 128 bits) to a string. The standard ("unadorned") functions store the string data into a string object that you pass as a parameter to the function. That string object must be preallocated and large enough to receive the string result (else a string overflow occurs). The "adorned" functions, whose names begin with "a_" automatically allocate storage on the heap, store the converted string into that heap object, and then return a pointer to the newly allocated string in the EAX register (it is the caller’s responsibility to free the storage when it is no longer needed).

These functions let you specify a minimum field width and a fill character. 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.

Here are the maximum number of print positions these routines will produce for each data type before considering the minimum field width:

Underscores flag is false:

8 bits: 3 (0..255)

16 bits: 5 (0..65535)

32 bits: 10 (0..4294967295)

64 bits: 20 (0..18446744073709551615)

128 bits: 39 (0 .. 340282366920938463463374607431768211455)

Underscores flag is true:

8 bits: 3 (0..255)

16 bits: 6 (0..65_535)

32 bits: 13 (0..4_294_967_295)

64 bits: 26 (0..18_446_744_073_709_551_615)

128 bits: 51 (0..340_282_366_920_938_463_463_374_607_431_768_211_455)

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

This function converts an 8-bit unsigned integer to the decimal string representation of that integer and stores the string in the preallocated string object specified by the dest paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. Note that the internal underscores flag will not affect the output because 8-bit integers are always three digits or smaller.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.u8ToStr:

conv.u8ToStr( byteVariable, destStr );

// The following call will BH’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.u8ToStr:

conv.u8ToStr( bh, edx );

// The following pushes the constant and destStr and calls

// conv.u8ToStr:

conv.bToBuf( <constant>, destStr ); // <constant> must fit in 8 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.u8ToStr;

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

push( destStr );

call conv.u8ToStr;

// Passing a byte variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

push( destStr );

call conv.u8ToStr;

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

push( edx );

call conv.u8ToStr;

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

push( edx );

call conv.u8ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.u8ToStr;

procedure conv.a_u8ToStr ( b:uns8; width:int32; fill:char );
@returns( "eax" );

This function converts an 8-bit unsigned integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. Note that the internal underscores flag will not affect the output because 8-bit integers are always three digits or smaller.

HLA high-level calling sequence examples:

// The following will push "byteVariable" and then call conv.a_u8ToStr:

conv.a_u8ToStr( byteVariable );

mov( eax, byteStr );

// The following call will BH’s value onto the stack

// before calling conv.a_u8ToStr:

conv.a_u8ToStr( bh );

mov( eax, byteStr );

// The following pushes the constant and calls

// conv.a_u8ToStr:

conv.a_u8ToStr( <constant> ); // <constant> must fit in 8 bits

mov( eax, byteStr );

HLA low-level calling sequence examples:

// Passing a byte variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

call conv.a_u8ToStr;

mov( eax, destStr );

// Passing a byte variable and a buffer variable, option 2

// (if byteVariable isn’t the last byte in mapped memory):

push( (type dword byteVariable));

call conv.a_u8ToStr;

mov( eax, destStr );

// Passing a byte variable, option 3

// No registers are available and we can’t guarantee that

// the three bytes following byteVariable are present in

// mapped memory:

sub( 4, esp );

push( eax );

movzx( byteVariable, eax );

mov( eax, [esp+4] );

pop( eax );

call conv.a_u8ToStr;

mov( eax, destStr );

// Passing a pair of registers (hex value in AL, BL, CL, or DL):

// BL = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BL

call conv.a_u8ToStr;

mov( eax, byteStr );

// Passing a pair of registers (hex value in AH, BH, CH, or DH):

// BH = value to print, EDX = pointer to string object.

pushd( 0 );

mov( bh, [esp] );

call conv.a_u8ToStr;

mov( eax, byteStr );

// Passing a constant:

pushd( <constant> );

call conv.a_u8ToStr;

mov( eax, byteStr );

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

This function converts a 16-bit unsigned integer to its decimal string representation and stores the string in the preallocated string object specified by the dest paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "wordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.u16ToStr:

conv.u16ToStr( wordVariable, destStr );

// The following call will BX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.u16ToStr:

conv.u16ToStr( bx, edx );

// The following pushes the constant and destStr and calls

// conv.u16ToStr:

conv.u16ToStr( <constant>, destStr ); // <constant> must fit in 16 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data (confusing, isn’t it?). In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string.

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( byteVariable, eax );

push( eax );

push( destStr );

call conv.u16ToStr;

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

push( destStr );

call conv.u16ToStr;

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

push( destStr );

call conv.u16ToStr;

// Passing a pair of registers:

// BX = value to print, EDX = pointer to string object.

push( ebx ); // Pushes BX

push( edx );

call conv.u16ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.u16ToStr;

procedure conv.a_u16ToStr( w:uns16; width:int32; fill:char );
@returns( "eax" );

This function converts a 16-bit unsigned integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "wordVariable" and call conv.a_u16ToStr:

conv.a_u16ToStr( wordVariable );

mov( eax, destStr );

// The following call will BX’s value onto the stack

// before calling conv.a_u16ToStr:

conv.a_u16ToStr( bx );

// The following pushes the constant and calls

// conv.a_u16ToStr:

conv.a_u16ToStr( <const>, destStr ); // <const> must fit in 16 bits

HLA low-level calling sequence examples:

// Passing a word variable and a buffer variable, option 1

// (if a 32-bit register is available):

movzx( wordVariable, eax );

push( eax );

call conv.a_u16ToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 2

// (if wordVariable isn’t the last byte in mapped memory):

push( (type dword wordVariable));

call conv.a_u16ToStr;

mov( eax, destStr );

// Passing a word variable and a buffer variable, option 3

// No registers are available and we can’t guarantee that

// the two bytes following wordVariable are present in

// mapped memory:

pushw( 0 );

push( wordVariable );

call conv.a_u16ToStr;

mov( eax, destStr );

// Passing a pair of registers:

// BX = value to print.

push( ebx ); // Pushes BX

call conv.a_u16ToStr;

mov( eax, wordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_u16ToStr;

mov( eax, destStr );

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

This function converts a 32-bit unsigned integer to its decimal string representation and stores the string in the preallocated string object specified by the buffer paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and "destStr" (which

// is a pointer to a string object) and then call conv.u32ToStr:

conv.u32ToStr( dwordVariable, destStr );

// The following call will push EBX’s value onto the stack and then

// push EDX’s value (which is the address of a string object)

// before calling conv.u32ToStr:

conv.u32ToStr( ebx, edx );

// The following pushes the constant and destStr and calls

// conv.u32ToStr:

conv.u32ToStr( <constant>, destStr ); // <constant> must fit in 32 bits

When using the HLA high-level calling form, remember that string variables are dword pointers that contain the address of a string object. The destination string parameter is passed by value, not by reference; it just turns out that the value of a string is a pointer to the actual string data. In any case, this is why you can pass a register value as the destination string location rather than having to pass something like "[edx]". A construct like "[edx]" would imply that EDX contains the address of the string variable, that is, a pointer to the pointer to the string data.

HLA low-level calling sequence examples:

// Passing a dword variable and a buffer variable:

push( dwordVariable );

push( destStr );

call conv.u32ToStr;

// Passing a pair of registers:

// EBX = value to print, EDX = pointer to string object.

push( ebx );

push( edx );

call conv.u32ToStr;

// Passing a constant:

pushd( <constant> );

push( destStr );

call conv.u32ToStr;

procedure conv.a_u32ToStr( d:uns32; width:int32; fill:char );
@returns( "eax" );

This function converts a 32-bit unsigned integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push "dwordVariable" and then call conv.a_u32ToStr:

conv.a_u32ToStr( dwordVariable );

mov( eax, destStr );

// The following call will push EBX’s value onto the stack

// before calling conv.a_u32ToStr:

conv.a_u32ToStr( ebx );

// The following pushes the constant and calls

// conv.a_u32ToStr:

conv.a_u32ToStr( <constant>, destStr ); // <constant> must fit in 32 bits

HLA low-level calling sequence examples:

// Passing a dword variable:

push( dwordVariable );

call conv.a_u32ToStr;

mov( eax, destStr );

// Passing a register:

// EBX = value to print.

push( ebx );

call conv.a_u32ToStr;

mov( eax, dwordStr );

// Passing a constant:

pushd( <constant> );

call conv.a_u32ToStr;

mov( eax, destStr );

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

This function converts a 64-bit unsigned integer to its decimal string representation and stores the string in the preallocated string object specified by the buffer paramenter. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). This function will raise a string overflow exception if the destination string is not large enough to hold the conversion. If the conversion requires more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).

HLA high-level calling sequence examples:

// The following will push the value of "qwordVariable"

// and the value of the destStr string variable

// onto the stack and then call conv.u64ToStr:

conv.u64ToStr( qwordVariable, destStr );

// The following pushes the constant onto the stack along with

// the value held in the destStr variable and calls

// conv.u64ToStr:

conv.u64ToStr( <constant>, destStr ); // <constant> must fit in 64 bits

HLA low-level calling sequence examples:

// Passing a qword variable and a buffer variable:

push( (type dword qwordVariable[4])); // H.O. dword first

push( (type dword qwordVariable[0])); // L.O. dword last

push( destStr );

call conv.u64ToStr;

// Passing a constant:

pushd( <constant> >> 32 ); // Push H.O. dword of constant first.

pushd( <constant> & $FFFF_FFFF ); // Push L.O. dword second.

push( destStr );

call conv.u64ToStr;

 

procedure conv.a_u64ToStr( q:qword; width:int32; fill:char );
@returns( "eax" );

This function converts a 64-bit unsigned integer to the decimal string representation of that integer and stores the string in storage it allocates on the heap. The width and fill parameters specify the minimum field width and padding character (if the minimum field width is greater than the number of output characters needed for the string). The caller is responsible for freeing the storage when it is no longer needed. If the conversion produces more than three digits and the internal underscores flag is true, then this function will insert an underscore between each group of three digits, starting with the least signficant digit (see conv.getUnderscores and conv.setUnderscores for more details).