BEGINNING COMPUTER PROGRAMMING
(Using a Try it and See it approach)
(AND ... a Computer Student's/Teacher's DREAM Introductory Computer Language - HLA)
Introduction:
To facilitate the solid insights provided in learning about computing by some exposure to Assembly Language, but also to provide a friendly and facile start for new programmers, HLA provides all the facility and power of Assembly Language, but with a user friendly syntax, yet offers many of the elegant structures of an HLL (High Level Language) like Pascal or C or even the OOP of a language like C++. And so, going on to C, or C++, or any other modern HLL, will be more readily enabled, and with some real appreciation of what’s going on under the hood and inside the machine.
The author has successfully taught Computer Programming at a Canadian grade 7/8 Junior High and 9 to 12 High School level when PCs first became available using varied approaches, including using a personally authored simulated SIMPLE SIMON compiler running on a PC. But his final approach, after which this course is being patterned, was the most fun, and also the most successful, for both students ... and teacher.
Please enjoy and profit from your work. Don't be afraid to make changes and see what happens. Now dig in ... You may be surprised how much you will learn.
Shalom shalom,
David W. Zavitz
Toronto, Ontario, CANADA
dwzavitz@gmail.com
For arrangements to use this text beyond individual personal use, please contact the author at the above e-mail.
© (C) 2007-08-17
Acknowledgements:
This course and text would not be possible without the very gifted insights, teaching experiences, and programming skills of Randall Hyde,
http://en.wikipedia.org/wiki/Randall_Hyde
the Author, Creator, and so far, the Sustainer of HLA. I also would like to acknowledge the tremendous encouragement given by my youngest son Matthew, who's recent Engineering need to know a little C++, and then Assembly ... provided the incentive for a little more then that, from dad, (which led to HLA and now this attempt to benefit beginning computer students in general.) Also, I would like to thank my students at Scarborough Christian High School, who besides being very promising young ladies and gentlemen, provided exemplary feedback as we went along into some not yet fully charted territory.
Table of Contents:
Chapter 01: The Computer prints a message
Chapter 02: The Computer gets some input
Chapter 03: The Computer adds some numbers
Chapter 04: The Computer repeats a procedure
Chapter 05: The Basic Elements of computer programming … and low level example(s)
Chapter 06: The Computer rolls the dice … a first simulation
Chapter 07: First Memory Flow Lab
Chapter 08: Memory Flow Lab2
Chapter 09: Memory Flow Lab3
Chapter 10: HLA Data Types
Chapter 11: The Computer does some Algebra with real numbers ... and Input Data Validation
Chapter 12: Pointers, Strings, your own Types, Records, Arrays, and dynamic things on the fly!
Chapter 13: The Computer files its Records
NOTE: For Chapter 14, and those beyond, continue at http://docs.google.com/View?docid=d674v6b_23gp6pnz&revision=_latest
Chapter 14: The Computer reads its Files
Chapter 15: The Computer updates (edits, deletes, sorts, chops-duplicates-in) its Files
Chapter 16: The Computer … vast OS’s … and the garbage collects? (Operating Systems ... what is DOS?)
Chapter 17: The Computer delivers daily Manna ... (gui Canadien ... eh?)
NOTE: For Chapter 18, and those that follow, goto http://docs.google.com/View?docID=d674v6b_26c6rrkw&revision=_latest
Chapter 18: OOP ... goes the computer?
Chapter 19: OOP 2
Chapter 20: Shaping OOP
More to follow … ?
Chapter 01: The Computer prints a message
installs and configures HLA files in the directory c:\hla\
For finding everything regarding HLA:
http://216.92.238.133/Webster/
If you are a first-time HLA user, the best thing to do is download and run the HLA setup program hlasetup.exe. This version replaces both the HLA and freeHLA (fhla) versions from previous releases.
http://216.92.238.133/Webster/HighLevelAsm/HLADoc/HLARef/HLARef_pdf/03_InstallingHLA.pdf
Teacher … also, as needed, to show how to use Windows, some simple OS commands, from the command line like cd to a directory, or md or dir i.e. showing the contents of the current directory. And also, as needed, to discuss folders, and folders inside folders, and the path to a file in some folder (i.e. the tree structure in organizing files on the disk.)
click start
click run
enter cmd ( i.e. type in cmd and then press the Enter key)
enter (the following lines on the command line, but not rem or what comes after):
md projects rem md projects means make a directory called projects
cd projects rem cd projects means change to the directory projects
md chapt01_print_message
cd chapt01_print_message
3. Using a text editor program (already installed on your computer) like notepad.exe enter this next line (on the command line):
notepad.exe print_message.hla
and then select yes when asked Do you want to create a new file?
Now enter these next five lines into the text editor workspace, (i.e. copy and paste the lines into the text editor workspace):
program print_message;
#include( "stdlib.hhf" )
begin print_message;
stdout.put( "Hello World!" );
end print_message;
4. Now save this as a file with the name print_message.hla
(Make sure notepad doesn't save it as print_message.hla.txt)
(Enter dir to see the files in that DIRectory. If it does have a .txt on the end of it, then rename it by entering the command below … (note: ren means rename)
ren print_message.hla.txt print_message.hla
or
ren print_message.txt *.hla
Note: the file needs to end in .hla to let it be compiled by HLA
hla print_message.hla
print_message.exe
See also: http://en.wikipedia.org/wiki/High_Level_Assembly
program HelloWorld;
#include( "stdlib.hhf" )
static
hwString :string := "Hello World" nl;
begin HelloWorld;
// Push the address of the "Hello World" string
push( hwString );
// Call an HLA Standard Library function that
// will print the string whose address has
// been pushed on the stack.
call stdout.puts;
end HelloWorld;
There is absolutely nothing stopping a programmer from writing the "Hello World" program in low-level assembly language, should they really want to do this. However, for the beginner who is experiencing their first hour with assembly language, the former code is far more approachable than this latter code (i.e., explaining the stack and how parameters are passed to a procedure via the stack is a relatively advanced subject).
Addendum:
A user, "Kocmotex" ...
http://developers-heaven.net/forum/index.php/topic,50.msg100.html#new
pointed out a typo error in the "Hello Word", that is now fixed : "(Was) ... the use of a colon to terminate the third line of chapter 1's program ... employed as a device to demonstrate to the reader the operation of HLA's error reporting system" ?
It may be a good, even at this point, to try out making that "typo error" in your program. See what error message the HLA compiler/assembler presents to you, to help you find errors in your HLA program, so that you might fix them ... and then successfully make ... in this case ... the "print_message.exe" file.
Chapter 02: The Computer gets some input
click start
click run
enter cmd
enter (the following on the command line):
cd c:\hla\projects
md chapt02_get_input
cd chapt02_get_input
notepad.exe get_input.hla (or just enter ... notepad get_input.hla)
and then select yes when asked Do you want to create a new file?
Now enter these lines into the text editor workspace, (i.e. copy and paste the lines into the text editor workspace):
program get_input;
#include( "stdlib.hhf" )
// after two front slash characters, 'comments' may be inserted
// into HLA programs
// declare first_name as a string variable in static memory and
// also reserve (i.e. allocate) space for 64 (byte-size) characters there
static
first_name: str.strvar(64);
// declare last_name as a string in static memory and also
// reserve (i.e. allocate) space for 64 byte-size characters
last_name: str.strvar(64);
begin get_input;
stdout.put( "Please enter your first name: " );
stdin.get( first_name );
stdout.put( "Now enter your last name: " );
stdin.get( last_name );
stdout.put( "Are you really ", last_name, ", ", first_name, "?" );
end get_input;
hla get_input.hla (or just enter the command line command: hla get_input)
get_input.exe (or just enter: get_input)
Chapter 03: The Computer adds some numbers
click start
click run
enter cmd
then enter (on the command line):
cd c:\hla\projects
md chapt03_add_nums
cd chapt03_add_nums
notepad add_nums.hla
and then select yes when asked Do you want to create a new file?
Now enter these lines into the text editor workspace, (i.e. copy and paste the lines into the text editor workspace):
program add_nums;
#include( "stdlib.hhf" )
// declare three 32 bit integer variables in the HLA static memory space
static
num1: int32:= 23; // initialize num1 to 23
num2: int32:= 77; // initialize num2 to 77
sum: int32; // sum is NOT initialized
begin add_nums;
stdout.put( "The sum of ", num1, " and ", num2, " is " );
// eax is a 32 bit register in the microprocessor
mov( num1, eax );// move the (value of the) integer num1 into eax
add( num2, eax );// add the (value of the) integer num2 to (what's in) eax
mov( eax, sum ); // move the 32 bit value now in the eax register into the
// 32 bits of static memory that we reserved and
// called 'sum'
stdout.put( sum )
stdout.newln(); // output an appropriate carriage
// return/newline character(s)
stdout.put( nl, "Enter an integer " ); // nl will output a
// newline character(s) as above
stdin.get( num1 );
// Note: ok to leave off comma here, as
stdout.put( nl "Enter a second integer " ); // adjacent strings are all
// concatenated together
stdin.get( num2 );
stdout.put( nl "The sum of ", num1, " and ", num2, " is " );
mov( num1, eax );
add( num2, eax );
mov( eax, sum );
stdout.put( sum );
end add_nums;
hla add_nums.hla (or just enter: hla add_nums)
add_nums.exe (or just enter the now executable command: add_nums)
Chapter 04: The Computer repeats a procedure
click start
click run
enter cmd
then enter (on the command line):
cd c:\hla\projects
md chapt04_repeat_proc
cd chapt04_repeat_proc
notepad repeat_proc.hla
and then select yes when asked Do you want to create a new file?
Now enter these lines into the text editor workspace, (i.e. copy and paste the lines into the text editor workspace):
program repeat_proc;
#include( "stdlib.hhf" )
static
done: boolean:= false;
// The above reserves a byte of static memory and those 8 bits of
// memory space are now referred to (by the name) 'done' .
// The value is initialized to 'false' i.e. 0000_0000
// Note: HLA can presently address 2^32 bytes of memory, so it takes a 32 bit
// variable to be able to address each byte of memory available. The highest
// address addressable, 1111_1111_1111_1111_1111_1111_1111_1111,
// has all 32 binary bits set.
// Note: the address to get to the memory location 'done' is a 32 bit address,
// but the memory space reserved for the boolean variable 'done' is just one
// byte
// The following procedure asks for two integers to be entered and then
// finds their sum.
// It then prints out the two integers just input, and also their sum.
procedure get_nums_find_sum;
// Now declare three 'automatic' variables to hold 32 bit integers
// This is done in this area AFTER the top 'procedure ...' line
// but BEFORE the 'begin ...' line
var
num1: int32; // These 'automatic' variables, inside the procedure's
num2: int32; // declaration section, are created EACH time 'called'.
sum: int32; // And when the procedure is EXITed, they are 'lost'
begin get_nums_find_sum;
stdout.put( "Enter an integer: " );
stdin.flushInput(); // clears the 'stdin' input buffer
stdin.get( num1 );
stdout.put( "Enter a second integer: " );
stdin.get( num2 );
stdout.put( "The sum of ", num1, " and ", num2, " is " );
mov( num1, eax );
add( num2, eax );
mov( eax, sum ); // the int32 (in static memory) with label 'sum'
// now holds the 32 bit value of (num1 + num2)
stdout.put( sum );
end get_nums_find_sum;
begin repeat_proc;// Ok now ... start the 'main' section of this program.
// This section is where the 'repeat_proc.exe' program
// file begins to execute, when it first starts to run,
// after it is loaded into memory.
repeat // This marks the top of the repeat loop structure.
get_nums_find_sum(); // now execute this procedure we defined above
// We could call the 'macro' stdout.put which passes two nl
// OS appropriate codes and the string "Again (y/n) ? " ... each to
// appropriate procedure calls of the stdout library 'put' function …
// but here, ok to use stdout.puts( someString ); since the ...
stdout.puts( nl nl "More (y/n) ? " ); // strings here all get
// concatenated together by
// the compiler
// flushInput() is a library procedure and is in
// the 'stdin' namespace in HLA
stdin.flushInput();// this flushes any characters like 'nl' that were
// left in the stdin buffered input stream
stdin.getc();// puts the first character entered into 'al' (al is
// the lowest 8 bit byte of 'eax', a 32 bit register)
if( al = 'n' ) then // if the 8 bits in 'al' match the eight bits in
// the ASCII representation of the character
// 'n' ... then
mov( true, done );
// move the 8 bit value for 'true' into the 8 bits
// of memory the computer refers to by (the name of)
// 'done'
elseif( al = 'N' ) then // now check, if need to,
// if a capital 'N' character was entered
mov( true, done );
else // if, and only if, neither of above two checks were true,
// then do this section ...
// output a string (that gets all concatenated together
// before assembled by HLA)
stdout.puts( nl "Ok ... Here we go again." nl );
endif; // This marks the 'end' of the if..elseif..
// ..elseif..else..endif structure.
until( done ); // This marks the bottom of the 'repeat/until' structure.
// This 'repeat..until' structure is always traversed at least once,
// since the 'condition' to exit is NOT tested UNTIL the end.
// If the value of the boolean variable is 'true'
// the loop is exited. Otherwise, it begins again
// right after the top 'repeat' (address in the memory).
end repeat_proc;
Now save this text file with the name repeat_proc.hla
hla repeat_proc.hla (or just enter this command: hla repeat_proc)
repeat_proc.exe (or just enter: repeat_proc)
Chapter 05 : The Basic Elements of a program & low level examples of branching
So far … we have
You may be surprised that all programs are mostly just various combinations of the above.
We have also learned some rudimentary house keeping:
program someProgramName; // The program starts here
begin someProgramName;
end someProgramName; // The program ends here
Notice the semi-colon characters that tell the compiler that we have finished an instruction, (similar to the period at the end of a sentence.)
See: http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/HelloWorlda2.html#1011925
program testing123;
#include( "stdlib.hhf" ) // include the file stdlib.hhf from the HLA include folder
begin testing123;
stdout.put("Ok. Testing ", 1234567890 ); // just to give some output …
end testing123;
Notice the # character. It instructs the compiler here to include the standard library header file. Also notice that a semi-colon, at the end of that instruction to the compiler, is NOT used.
If you wanted to break up a long program file into sections, or store some procedure in a file to be reused later, for example, if you had the declaration/definition of say, procedure myDoIt, saved in a text file in your working directory, or in the HLA include directory, with the name myDoIt.hhf, you could then ask the compiler to include that file like this:
#include ( "myDoIt.hhf" )
You could insert this line in your .hla file in the include section, (like thus):
#include( "stdlib.hhf")
#include ( "myDoIt.hhf" )
Then, at compile time, the HLA compiler/assembler will insert the text of that file into the program(s) then being compiled, just as if you had copied and pasted in that text - at that location, (and then saved your thus modified someName.hla program, before compiling.)
program compNamNum;
#include( "stdlib.hhf" )
static
cName: string:= "Dumb Computer"; // the 32 bit address in static
// memory of the 14+ byte constant
// character string in readonly
// memory
cNumb: int32:= 1234567890; // the 32 bit space in static
// memory for an integer
begin compNamNum;
// stdout.put( ... ) is a function in the standard library
// in the ‘stdout’ namespace, ‘nl’ is the HLA for ‘new line’ for all OS’s
stdout.put( "The name of this computer is ", cName, nl );
stdout.put( "The number of this computer is ", cNumb, nl nl );
end compNamNum;
program test_showNameNumb;
#include( "stdlib.hhf" )
// Note syntax of how string and number get passed to the following procedure
// Note: A procedure must be declared/defined before it can be called by a program.
// 1st variable passed is a string; 2nd is an int32; NOTE! ‘;’ separator
procedure printout( cNam: string; cNum: int32 );
begin printout;
stdout.put( "The name.number of this computer is ", cNam, ".", cNum, nl );
end printout;
begin test_showNameNumb;
// Note that 1st passed is a constant string, 2nd passed is a constant int
// Note the ‘,’ separator used between parameters
// when procedures are called …
printout( "1_Dumb_Computer", 123456789 );
printout( "2_Dumb_Computer", 234567890 );
printout( "3_Dumb_Computer", 345678901 );
end test_showNameNumb;
For an assignment, create a suitable folder like c:\hla\projects\chapt05_myWork … Then, in it:
http://safari.oreilly.com/1593270658/ns1593270658-CHP-15-SECT-3
The following two code fragments demonstrate an HLA forever..endfor loop (along with a breakif) and the corresponding "pure" assembly code: ... |
forever
<< Code to execute (at least once)
prior to the termination test >>
breakif( termination_expression );
<< Code to execute after the loop-termination test >>
endfor;
Converting a forever..endfor loop into pure assembly language is a trivial matter. All you need is a single jmp instruction that can transfer control from the bottom of the loop back to the top of the loop. The implementation of the break statement is just as trivial, it's just a jump (or conditional jump) to the first statement following the loop. The following two code fragments demonstrate an HLA forever..endfor loop (along with a break..if) and the corresponding "pure" assembly code:
// High-level forever statement in HLA:
forever
stdout.put("Enter an unsigned integer less "
"than or equal to five: ");
stdin.get( u );
breakif( u <= 5 );
stdout.put("Error: the value must be in "
"range zero to five." nl);
endfor;
// Low-level coding of the forever loop in HLA:
foreverLabel:
stdout.put("Enter an unsigned integer "
"less than five: ");
stdin.get( u );
cmp( u, 5 ); // i.e. compare the value of u with 5
jbe endForeverLabel; // jump to the bottom label if u <= 5
stdout.put("Error: the value must be in range "
"zero to five" nl);
jmp foreverLabel; // jump to the top label
endForeverLabel:
Of course, you can also rotate this code to create a slightly more efficient version:
// Low-level coding of the forever loop in HLA using code rotation:
jmp foreverEnter;
foreverLabel:
stdout.put("Error: the value must be in "
"range zero to five." nl);
foreverEnter:
stdout.put("Enter an unsigned integer less "
"than five: ");
stdin.get( u );
cmp( u, 5 );
ja foreverLabel; // jump to top label if u > 5
And yes … for more than you may ever need to know about HLA ... try this link:
And in particular, to see MUCH MORE about loops follow these links at:
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/HelloWorlda3.html
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/LowLevelControlStructsa4.html
A test program, containing the code snippet from the previous page (slightly enhanced) follows:
program lowlevel2; // my test program (of the code snippet-further optimized/commented)
#include( "stdlib.hhf" )
static
u: uns32;
begin lowlevel2;
// Low-level coding of the forever loop in HLA
// using code rotation:
jmp foreverEnter;
foreverLabel:
stdout.puts("Error: the value must be in the range 0..5" nl);
foreverEnter:
stdout.puts("Enter an unsigned integer less than or equal to five: ");
stdin.getu32(); // returns uns32 number in eax register
// mov( eax, u );
// cmp( u, 5 );
cmp( eax, 5 );
ja foreverLabel; // jump to top label if u > 5
mov( eax, u ); // since good data now ...
stdout.put( nl "Ok ... good data ... you entered: ", u );
end lowlevel2;
Chapter 06: The Computer rolls the dice … a first simulation
Ok … here is an interactive project with a little Google research to find out about the random function in the HLA library.
I. Announce what the program is about.
II. Ask the user to input a guess in the range 2-12.
III. Get the computer to select randomly a number in that range.
IV. See if the number guessed matches the total on the die that the computer rolled
V. Report the results
VI. Ask if user wants to try again … or quit
Ok … let's get a shell started …
program guessIt;
#include( "stdlib.hhf" )
// Note: procedure playgame is the main game …
procedure playgame;
var
userNum: uns32;
die1: uns32;
die2: uns32;
diceSum: uns32;
begin playgame;
stdout.puts
(
nl
"THE COMPUTER will roll the dice AFTER" nl
"YOU ENTER a number in the RANGE 2..12" nl
"Ok ... what is your guess: "
);
stdin.flushInput();
stdin.get( userNum );
// The following tasks are yet to be coded:
// 1. get two random numbers 1..6 for each die
// 2. see if sum of die matches the number the user guessed
// 3. report success or falure
stdout.put( "You entered ", userNum, nl );
end playgame;
begin guessIt;
repeat // since we presume that the user wants to play at least
// one time, we can use the repeat..until structure
playgame();
stdout.puts( nl "Play again (y/n) ? " );
stdin.flushInput();
stdin.getc();// recall first character returned in lowest 8 bits of eax
// i.e. the al register
until( al = 'n' || al = 'N' ); // i.e. until n or N entered
end guessIt;
Ok … it compiles and runs ok so far, but did you notice that the user can enter numbers that are way out of range?
Let’s fix that this way: (We will pass the value to a function to validate it.)
// this 'function' returns true in al if x <= 12, otherwise it returns false
procedure isValid( x:uns32 ); @returns("al");
begin isValid;
if(x <=12) then
mov( true, al );
else
mov( false, al);
endif
endisvalid;
Ok … let’s plug that in and check it out so far …
program guessIt2;
#include( "stdlib.hhf" )
// this 'function' returns true in al if x <= 12, otherwise it returns false
procedure isValid( x:int32 ); @returns("al");
begin isValid;
if(x <= 12 ) then
mov( true, al );
else
mov( false, al);
endif;
end isValid;
// Note: procedure playgame is the main game …
procedure playgame;
var
userNum: int32;
die1: int32;
die2: int32;
diceSum: int32;
begin playgame;
stdout.puts
(
nl "THE COMPUTER will roll the dice AFTER" nl
"YOU ENTER a number in the RANGE 2 to 12 ..." nl
);
repeat
stdout.puts( "Your guess 2..12 : " );
stdin.flushInput();
stdin.get( userNum );
until( isValid( userNum ) );
// the following is to do yet …
// get two random numbers 1..6 for each die
// see if sum matches then … report success or falure
stdout.put( "You entered ", userNum, nl );
end playgame;
begin guessIt2;
repeat // since we presume that the user wants to play at least
// one time, we can use the repeat..until structure
playgame();
stdout.puts( nl "Play again (y/n) ? " );
stdin.flushInput();
stdin.getc();// recall first character returned in lowest 8 bits of eax // i.e. the al register
until( al = 'n' || al = 'N' );
end guessIt2;
So far … so good. But let’s do that research on the HLA random function, (if HLA has one?)
Ok … google HLA random and here is what came up on top just now (2007-08-19):
rand.randomize(); // or call rand.randomize; (but this form used here)
This function "randomizes" the seed used by the random number generators. If you call rand.randomize, the random number generators should begin generating a sequence starting at a random point in the normal sequence put out by the random number generator. The randomization function is based on the number of CPU clock cycles that have occurred since the CPU was last powered up. This function uses the Pentium's RDTSC instruction, hence you should only call this function on machines that have this instruction available (Intel Pentium and later as well as other manufacturer's CPUs that have this instruction). Because of the nature of the RDTSC instruction, you should not call rand.randomize frequently or you will compromise the quality of the random numbers (indeed, it's generally not a good idea to "randomize" a random number generator more than once per program invocation).
rand.range( startRange:int32; endRange:int32 ); @returns( "eax" );
This function generates a uniformly distributed random number in the range "startRange..endRange" (inclusive). This function generates its random numbers using the rand.random function. This function returns the value in the EAX register.
Or try this link:
Random Number Generators (rand.hhf)
Now let’s see if we can plug it in and get it working in our program … (and notice some changes? / deletions? / improvements? … that were made also.)
program guessIt3;
#include( "stdlib.hhf" )
// This 'function' returns true in al if x is in the range 2..12,
// otherwise it returns false. The @display is turned off since it is
// not needed here. This makes this procedure call more efficient.
procedure isValid( x:int32 ); @nodisplay; @returns("al");
begin isValid;
if( x >= 2 && x <= 12 ) then // i.e. if x in range 2..12
mov( true, al );
else
mov( false, al);
endif;
end isValid;
// Note: procedure playgame is the main game …
procedure playgame; @nodisplay;
var
userNum: int32;
die1: int32;
die2: int32;
begin playgame;
stdout.puts
(
nl
"THE COMPUTER will roll the dice AFTER" nl
"YOU ENTER a number in the RANGE 2 to12." nl nl
);
repeat
stdout.puts( "Your guess 2..12 : " );
stdin.flushInput();
stdin.get( userNum );
until( isValid( userNum ) ); // Note: userNum is first passed to isValid
// Then isValid returns true or false.
// get two random numbers 1..6 for each die
rand.range( 1, 6 ); // returns random num 1..6 in eax
mov( eax, die1 ); // and move value into die1
rand.range( 1, 6 ); // returns random num 1..6 in eax
mov( eax, die2 ); // and move value into die2
add( die1, eax ); // eax now holds sum
// see if sum matches
if( eax = userNum ) then
stdout.puts( "MATCH!" nl );// report success
else
stdout.puts( "NO match!" nl ); // report failure
endif;
stdout.put
(
"You entered ", userNum, nl
"And the computer rolled ", die1, " & ", die2,
" or ", (type int32 eax), "." nl
);
end playgame;
begin guessIt3;
rand.randomize(); // We just want to call this once in our program,
// as per above info.
repeat // Since we presume that the user wants to play at least
// one time, we can use the repeat..until structure.
playgame(); // We pick two unique random numbers in range 1..6
// in each call.
stdout.puts( nl "Play again (y/n) ? " );
stdin.flushInput();
stdin.getc(); // Recall, the first character is returned in
// lowest 8 bits of eax
// i.e. the al register
until( al = 'n' || al = 'N' );
end guessIt3;
Well, these functions are working ok … but let’s make some more enhancements:
Let’s add a counter to keep track of the number of games. And suppose that you started out with so many cents, say 100, at the beginning and win the number of cents, (the value you guessed), when there is a match … but lose the number (the same number of cents) you guess for the sum of the dice if there is NO match … (Let’s use global variable’s for these to keep the design simple, and also we can use registers, so we won’t have to pass these values back and forth to/from the procedures or to use pass by reference syntax, since this is supposed to be a simple simulation?)
The game will be over when all your starting cents are used up, … or … if you choose to quit, (or if by some long shot, the computer loses all its cents!)
Make a final report of the success or failure. (What you started out with. How much you were up/down. Your new total and the computers new total. The number of games played.)
Are you ready to start programming?
program guessIt4;
#include( "stdlib.hhf" )
static // below are used as global variables
cCents: int32;
pCents: int32;
cWins: int32:= 0;
pWins: int32:= 0;
numGames: int32:= 0;
procedure getValidInt( msg:string; low:int32; high:int32 );@nodisplay;@returns( "eax" );
begin getValidInt;
push( ebx );
push( edx );
forever
try
stdout.puts( msg );
stdin.flushInput();
stdin.geti32(); // May raise an exception.
unprotected
breakif( eax >= low && eax <= high );
stdout.put( "Value was out of range ", low, "..", high, ", "
"re-enter please ... " nl );
exception( ex.ValueOutOfRange )
stdout.put( "Value was out of range, re-enter please ... " nl );
exception( ex.ConversionError )
stdout.put( "Value contained illegal char's, re-enter please ..." nl );
endtry;
endfor;
pop( edx );
pop( ebx );
end getValidInt;
procedure playgame; @nodisplay; // Note: procedure playgame is the main game ...
var
userNum: int32;
die1: int32;
die2: int32;
begin playgame;
stdout.puts
(
nl "THE COMPUTER will roll the dice AFTER "
"YOU ENTER a number in the RANGE 2 to 12." nl nl
);
mov( getValidInt( "Your guess 2..12 : ", 2, 12 ), userNum );
// get two random numbers 1..6 for each die
rand.range( 1, 6 ); // returns random num 1..6 in eax
mov( eax, die1 ); // and move value into die1
rand.range( 1, 6 ); // returns random num 1..6 in eax
mov( eax, die2 ); // and move value into die2
add( die1, eax ); // eax now holds sum
// see if sum matches ...
if( eax == userNum ) then
// note that edx and ebx were given initial values in the 'main' program section
stdout.puts( "MATCH!" nl ); // report
add( userNum, edx ); // player wins
sub( userNum, ebx ); // computer loses
else
stdout.puts( "NO match!" nl ); // report failure
sub( userNum, edx ); // player loses
add( userNum, ebx ); // computer wins
endif;
stdout.put
(
"You entered ", userNum, nl
"And the computer rolled ", die1, " & ", die2,
" or ", (type int32 eax), "." nl
);
end playgame;
begin guessIt4;
rand.randomize(); // we just want to call this once in our program,
// as per above info
mov( getValidInt( nl "Computer stakes 100..1000 (cents) : ",100,1000 ), cCents );
mov( cCents, ebx ); // working copy in ebx
mov( getValidInt( nl "Player stakes 100..1000 (cents) : ", 100, 1000 ), pCents );
mov( pCents, edx ); // working copy in edx
// since we presume that the user wants to play at least
// one time, we can use the repeat..until structure
repeat
inc( numGames ); // here we go again so increment numGames
playgame(); // we pick two unique random numbers
// in range 1..6 in each call
stdout.puts( nl "Play again (y/n) ? " );
stdin.flushInput();
stdin.getc(); // recall first character returned in the
// lowest 8 bits of eax (i.e. the al register)
until
(
al == 'n' || al == 'N' || // i.e. until n or N selected ... or
(type int32 ebx) <= 0 || // the value, taken as an integer, in ebx <= 0 ... or
(type int32 edx) <= 0 // the value, taken as an integer, in edx <= 0
);
// don't allow, (i.e. correct for), 'in-hole' stuations ...
if( (type int32 edx) < 0 ) then // player in hole
add( edx, ebx ); // i.e. decrease computer balance
mov( 0, edx ); // so now can up balance of player to zero
elseif( (type int32 ebx) < 0 ) then // computer in hole
add( ebx, edx ); // decrease player balance
mov( 0, ebx ); // so now can up balance of computer to zero
endif;
stdout.put
(
nl "After ", numGames, " games, the computer has ",
(type int32 ebx), " cents", nl
"and the player has ", (type int32 edx), " cents", nl
);
// Recall cCents, and pCents hold the starting staked values
mov( ebx, eax ); // calculate the computer winnings
sub( cCents, eax );
mov( eax, cWins );
mov( edx, eax ); // calculate the player winnings
sub( pCents, eax );
mov( eax, pWins );
if( cWins > 0 ) then // computer wins
neg( pWins ); // take the negative of ( )
stdout.put
(
"The computer wins ", cWins,
" and the player loses ", pWins, nl
);
else // computer loses
neg( cWins );
stdout.put( "The player wins ", pWins, " and the computer loses ", cWins, nl );
endif;
stdout.puts( nl "Press 'Enter' to continue/exit ... " );
stdin.flushInput();
stdin.getc();
end guessIt4;
Here is a fun program using random numbers that I modified a little …
program testRandom;
// adapted from ...
// http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/IntegerArithmetic3.html
#include( "stdlib.hhf" );
/*
Note:
console.black := 0;
console.red := 1;
console.green := 2;
console.yellow := 3;
console.blue := 4;
console.magenta := 5;
console.cyan := 6;
console.white := 7;
*/
begin testRandom;
console.cls();
mov( 10_000, ecx );
call rand.randomize; // or rand.randomize();
repeat
// Generate a random X-coordinate
// using rand.range.
rand.range( 1, 78 );
mov( eax, ebx ); // Save the X-coordinate for now.
// Generate a random Y-coordinate
// using rand.urange
rand.urange( 1, 23 );
mov( eax, edx );
// Print an asterisk at
// the specified coordinate on the screen.
console.gotoxy( ebx, edx );
rand.range( 0, 6 ); // no white foreground
console.setAttrs( eax, 7 ); // all white background
stdout.puts( "*" );
// Repeat this 10,000 times to get
// a good distribution of values.
dec( ecx );
until( @z ); // zero flag set when ecx becomes zero
// Position the cursor at the bottom of the
// screen so we can observe the results.
console.gotoxy( 0, 23 );
end testRandom;
Chapter 07: Memory Flow Lab
program memoryFlowLab;
/*
Also see:
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/HelloWorlda3.html
*/
#include("stdlib.hhf")
static
myCharArray:char[255]; // reserve 255 bytes of memory for 255 char's
myChr: char; // reserve 1 byte of memory for 1 character
myInt: int32:= 12345; // reserve 4 bytes, i.e. 32 bits, for an int
// the following shows how to declare a 'pointer' type variable ...
myP: pointer to int32:= &myInt; // now myP holds address of myInt
begin memoryFlowLab;
// Note below that the ':' character notifies the compiler that
// 'top' IS to be known, from here on, (in the program), as A LABEL
// So now the running program HAS a label, (i.e. a name), to
// refer to, if it needs to go to THAT ADDRESS.
top:
// Note below, the 'no op', i.e. nop() means 'no operation'
// 1. How many bytes of program execution
// memory does this nop(); take?
// 2. How many bytes of program execution memory
// do all these comments in the source file take?
// 3. How many bytes of program execution memory
// are BETWEEN the addresses given by 'top' and 'bot' ?
top2:
nop();
next:
add( myInt, eax );
next2:
mov( & top, eax ); // '&' means 'take the address of'
stdout.put( nl "The address of label top is ", eax );
mov( &top2, eax );
stdout.put( nl "The address of label top2 is ", eax );
stdout.puts( nl "So how much space do 'comments' take in the '.exe' file?" );
mov( &next, eax );
stdout.put( nl nl "The address of label next is ", eax );
stdout.puts( nl "How many bytes does the instruction nop() take in memory?" );
mov( &next2, eax );
stdout.put( nl nl "The address of label next2 is ", eax );
stdout.puts( nl "How many bytes are used by the instruction 'add( myInt, eax )' ? " );
sub( &next, eax );
stdout.put( nl "(The answer, in hexidecimal ... ", eax, ")" );
mov( &myCharArray, eax );
stdout.put( nl nl "The address of variable myCharArray is ", eax );
mov( &myChr, eax );
stdout.put( nl "The address of variable myChr is ", eax );;
mov( &myInt, eax );
stdout.put( nl nl "The address of variable myInt is ", eax );
mov( myP, eax );
stdout.put( nl "And ALSO, value of variable myP= ", eax );
mov( &top, eax ); // eax holds address of 'top' label
mov( &bot, ebx ); // ebx holds address of 'bot' label
mov( ebx, ecx ); // now ecx ALSO holds the address of bot2
sub( eax, ecx ); // i.e. subtract eax from ecx and
// store result in ecx
// ecx now holds the number of bytes
// from top to bot in the programs
// memory (while the program is runing
// i.e. executing in the computers 'ram')
stdout.put
(
nl nl
"What to learn:" nl nl // Note: the compiler concatenates ADJACENT strings ... ok:)
"The address of label 'top' is ", EAX, nl
"The address of label 'bot' is ",EBX, nl,
ebx, " - ", eax, " = ", ecx, " = ",
(type int32 ecx) , nl nl
"So there are ", ecx, " (in hex) bytes inside the running program,"
nl "from the label 'top' to the label 'bot'." nl
nl "As the program flows, (i.e. executes), as we have written it "
"from "
nl "the address labelled 'top' to the address labelled 'bot' ... "
nl "do the values of the addresses in memory decrease or increase? "
);
bot:
end memoryFlowLab;
Chapter 08: Memory Flow Lab2
program memoryFlowLab2;
#include("stdlib.hhf")
// An example of initializing at the same time as declaring variables.
// We have printed out the value of the memory addresses of some variables, so
// lets ALSO print out the (typed) values in these addresses ...
static
// reserve 256 bytes of memory for 256 char's. Note: (32*8) = 256
// Note: '32 dup' means to duplicate [ .. ] 32 times in memory
myCharArray:char[]:= 32 dup [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ];
// reserve 1 byte of memory for 1 character
myChr: char:= 'z';
// set p, a pointer to 'char', to hold the address of the first element in myCharArray
p: pointer to char:= &myCharArray;
begin memoryFlowLab2;
mov( & myCharArray, eax );
stdout.put( "The address of variable myCharArray is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myCharArray );
mov( & myCharArray[0], eax );
stdout.put( nl nl "The address of variable myCharArray[0] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myCharArray[0] );
mov( & myCharArray[1], eax );
stdout.put( nl nl "The address of variable myCharArray[1] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myCharArray[1] );
mov( & myCharArray[254], eax );
stdout.put( nl nl "The address of variable myCharArray[254] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myCharArray[254] );
mov( & myCharArray[255], eax );
stdout.put( nl nl "The address of variable myCharArray[255] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myCharArray[255] )
mov( & myCharArray[256], eax );
stdout.put( nl nl "The address of variable myCharArray[256] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myCharArray[256] )
mov( & myChr, eax );
stdout.put( nl nl "The address of variable myChr is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myChr, nl nl );
// ok here is an example using the HLA for..endfor loop structure
// let's use a register as a counter to give the maximum speed counting ...
for( mov( 0, ecx); ecx < 39; inc( ecx ) ) do
stdout.put( myCharArray[ ecx ], " " );
endfor;
stdout.puts
(
nl "Can you figure out all the above outputs? Press 'Enter' to continue ... "
);
stdin.readLn(); // wait for 'Enter' key to be pressed ...
// Now ... print out some headers, right-adjusted in 10 character wide fields.
stdout.put
(
nl nl, "address":10, "value":10, nl nl
);
for( mov( 0, ecx ); ecx < 20; inc( ecx ) ) do
lea( eax, myCharArray[ ecx ] ); // take address of each element and put it in eax
stdout.put( " ", eax, myCharArray[ ecx ]:10, nl );
endfor;
stdout.puts
(
nl "Can you figure out all these above outputs? Press 'Enter' to continue ... "
);
stdin.readLn(); // wait for 'Enter' key to be pressed ...
// Now ... print out some headers, right-adjusted in 10 character wide fields.
stdout.put
(
nl nl, "address":10, "value":10, nl
);
mov( p, eax ); // move address of first byte into eax
for( mov( 0, ecx ); ecx < 20; inc( ecx ) ) do
// (type char [ eax ]) = go to the address held in eax and ...
// intrepret the byte, at that address, as a char
stdout.put( " ", eax, (type char [ eax ]):10, nl );
inc( eax ); // go to next byte (address)
endfor;
mov( p, eax );
add( 256, eax );
stdout.put( nl " ", eax, (type char [ eax ]):10, nl );
end memoryFlowLab2;
Chapter 09: Memory Flow Lab3
program memoryFlowLab3;
#include("stdlib.hhf")
// An example of initializing at the same time as declaring variables.
// We have printed out the value of the memory addresses of some variables, so
// lets ALSO print out the (typed) values in these addresses ...
static
// reserves 80 bytes of memory for 20 int32's. Note: (2*10*4bytes) = 80bytes
// Note: '2 dup' means to duplicate [ .. ] 2 times in memory
myInt32Array: int32[]:= 2 dup [ 1,2,3,4,5,6,7,8,9,0 ];
myInt32: int32:= 987654321;
// set p, a pointer to 'int32', to hold the address of the first element in myInt32Array
p: pointer to int32:= &myInt32Array;
begin memoryFlowLab3;
mov( & myInt32Array, eax );
stdout.put( "The address of variable myInt32Array is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32Array );
mov( & myInt32Array[0], eax );
stdout.put( nl nl "The address of variable myInt32Array[0] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32Array[0] );
mov( & myInt32Array[1*4], eax );
stdout.put( nl nl "The address of variable myInt32Array[1*4] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32Array[1*4] );
mov( & myInt32Array[18*4], eax );
stdout.put( nl nl "The address of variable myInt32Array[18*4] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32Array[18*4] );
mov( & myInt32Array[19*4], eax );
stdout.put( nl nl "The address of variable myInt32Array[19*4] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32Array[19*4] )
mov( & myInt32Array[20*4], eax );
stdout.put( nl nl "The address of variable myInt32Array[20*4] is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32Array[20*4] )
mov( & myInt32, eax );
stdout.put( nl nl "The address of variable myInt32 is ", eax );
stdout.put( nl "The VALUE IN THAT ADDRESS is ", myInt32, nl nl );
// ok here is an example using the HLA for..endfor loop structure
// let's use a register as a counter to give the maximum speed counting ...
for( mov( 0, ecx); ecx < 20; inc( ecx ) ) do
stdout.put( myInt32Array[ ecx*4 ], " " );
endfor;
stdout.puts
(
nl "Can you figure out all the above outputs? Press 'Enter' to continue ... "
);
stdin.readLn(); // wait for 'Enter' key to be pressed ...
// Now ... print out some headers, right-adjusted in 10 character wide fields.
stdout.put
(
nl nl, "address":10, "value":10, nl nl
);
for( mov( 0, ecx ); ecx < 20*4; add( 4, ecx ) ) do // add 4 ... since 4 bytes used for each int32
lea( eax, myInt32Array[ ecx ] ); // take address of each element and put it in eax
stdout.put( " ", eax, myInt32Array[ ecx ]:10, nl );
endfor;
stdout.puts
(
nl "Can you figure out all these above outputs? Press 'Enter' to continue ... "
);
stdin.readLn(); // wait for 'Enter' key to be pressed ...
// Now ... print out some headers, right-adjusted in 10 character wide fields.
stdout.put
(
nl nl, "address":10, "value":10, nl
);
mov( p, eax ); // move address of first int32 into eax
for( mov( 0, ecx ); ecx < 20; inc( ecx ) ) do
// (type int32 [ eax ]) = go to the address held in eax and ...
// intrepret the 4 bytes, at that address, as an int32
stdout.put( " ", eax, (type int32 [ eax ]):10, nl );
add( 4, eax ); // go to next int32 (address)
endfor;
mov( p, eax );
add( 20*4, eax ); // note: compiler/assembler does this calulation of 20*4
// when compiling/assembling the 'xxx.hla' file into the 'xxx.exe' file
stdout.put( nl " ", eax, (type int32 [ eax ]):10, nl );
// recall '[eax]' means to goto the address held in eax
// (type int32 [ eax ]) means to interpret as an int 32 the 4 bytes at the address (pointed to by the value) in eax
// eax, without square brackets, means take the value in eax
// (type int32 eax ) would mean to interpret as an int 32 the 4 bytes in eax
end memoryFlowLab3;
Chapter 10: HLA Data Types
program types; // some HLA data types
#include( "stdlib.hhf" )
static
b: byte:= 65;
u8: uns8:= $ff;
i8: int8:= 127;
ca: char:= 'a';
cz: char:= 'z';
ccA: char:= 'A';
ccZ: char:= 'Z';
w: word:= $ffff;
u16: uns16:= $FFFF;
i16: int16:= $7fff;
dw: dword:= $ffff_ffff;
u32: uns32:= $ffffffff;
i32: int32:= $7fff_ffff;
r32: real32:= 1234567890.123e-47;
r64: real64:= 1234567890.123e-316;
s: string:= "Pausing now. Press 'Enter' to continue ...";
len_s: int32;
begin types;
str.length( s); // library function returns string length in eax
mov( eax, len_s );
stdout.put
(
"b=", b, " as byte (i.e. hex)"
" =", (type uns8 b), " as uns8"
" =", (type char b), " as char" nl
"u8=", u8, nl
"i8=", i8, nl nl
"ca=", ca,
" =", (type byte ca), " as byte"
" =", (type uns8 ca), " as uns8" nl
"cz=", cz, nl
"ccA=", ccA,
" =", (type byte ccA), " as byte"
" =", (type uns8 ccA), " as uns8" nl
"ccZ=", ccZ, nl nl
"w=", w, nl
"u16=", u16, " (max uns16)" nl
"i16=", i16, " (max int16)" nl nl
"dw=", dw, nl
"u32=", u32, " (max uns32)" nl
"i32=", i32, " (max int32)" nl nl
"r32=", r32, nl
"r64=", r64, nl nl,
"s=", s, " (The string length of s =", len_s, ") "
);
stdin.readLn(); stdout.newln();
for( mov( '0', cl ); cl <= '9'; inc( cl ) ) do
stdout.put( (type char cl), " " );
endfor;
stdout.newln();
for( mov( '0', cl ); cl <= '9'; inc( cl ) ) do
stdout.put( cl, " " );
endfor;
stdout.newln(); stdout.newln();
for( mov( 'A', cl ); cl <= 'Z'; inc( cl ) ) do
stdout.put( (type char cl), " " );
endfor;
stdout.newln();
for( mov( 'A', cl ); cl <= 'Z'; inc( cl ) ) do
stdout.put( cl, " " );
endfor;
stdout.newln();
for( mov( 'a', cl ); cl <= 'z'; inc( cl ) ) do
stdout.put( (type char cl), " " );
endfor;
stdout.newln();
for( mov( 'a', cl ); cl <= 'z'; inc( cl ) ) do
stdout.put( cl, " " );
endfor;
stdout.newln();
end types;
See also:
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/MoreDataRepresentationa2.html#1002164
Characters
Perhaps the most important data type on a personal computer is the character data type. The term "character" refers to a human or machine readable symbol that is typically a non-numeric entity. In general, the term "character" refers to any symbol that you can normally type on a keyboard (including some symbols that may require multiple key presses to produce) or display on a video display. Many beginners often confuse the terms "character" and "alphabetic character." These terms are not the same. Punctuation symbols, numeric digits, spaces, tabs, carriage returns (enter), other control characters, and other special symbols are also characters. When this text uses the term "character" it refers to any of these characters, not just the alphabetic characters. When this text refers to alphabetic characters, it will use phrases like "alphabetic characters," "upper case characters," or "lower case characters.".
Another common problem beginners have when they first encounter the character data type is differentiating between numeric characters and numbers. The character '1' is distinct and different from the value one. The computer (generally) uses two different internal, binary, representations for numeric characters ('0', '1', ..., '9') versus the numeric values zero through nine. You must take care not to confuse the two.
Most computer systems use a one or two byte sequence to encode the various characters in binary form. Windows and Linux certainly fall into this category, using either the ASCII or Unicode encodings for characters. This section will discuss the ASCII character set and the character declaration facilities that HLA provides.
The ASCII Character Encoding
The ASCII (American Standard Code for Information Interchange) Character set maps 128 textual characters to the unsigned integer values 0..127 ($0..$7F). Internally, of course, the computer represents everything using binary numbers; so it should come as no surprise that the computer also uses binary values to represent non-numeric entities such as characters. ...
Also, take a look at this little program, slightly modified from AoA. This will help remind you that all data is stored as bits and that it is up to the programmer to decode those bits according to the encoding originally used in storing those bits in memory, in order to make sense of those chunks of bits in the computer's memory. (Of equal importance is to know where each 'chunk' of bits begins and ends in memory.)
program dataInterpretation;
#include( "stdlib.hhf" );
static
myRealNum: real32 := -1.0; // now occupies 32 bits in memory;
// can use mov( & myRealNum, eax);
// to find address of this 'chunk' of bits in static memory
begin dataInterpretation;
// myRealNum:10 means to right justify in a field 10 spaces wide, with 4 decimal places
stdout.put( "'myRealNum' interpreted as a real32 value: ", myRealNum:10:4, " or " );
stdout.put( nl "'myRealNum' interpreted as an uns32 value: " );
mov( myRealNum, eax ); // move the bits in myRealNum into eax
stdout.putu32( eax );
stdout.newln();
stdout.put( "'myRealNum' interpreted as an int32 value: " );
mov( myRealNum, eax );
stdout.puti32( eax );
stdout.newln();
stdout.put( "'myRealNum' interpreted as a dword value: $" );
mov( myRealNum, eax );
stdout.putd( eax );
stdout.newln();
// or ... could use this style for the above
stdout.put( "'myRealNum' interpreted as a dword value: $" );
mov( myRealNum, eax );
stdout.put( (type dword eax) );
stdout.newln();
stdout.puts( "Press 'Enter' to continue ... " );
stdin.readLn();
end dataInterpretation;
Chapter 11: The Computer does some Algebra with real numbers ... and Input Data Validation
program realMath; // finds the running average of test scores
#include( "stdlib.hhf" )
static
score: int32;
outOf: int32;
totScores: int32:= 0;
totOutOf: int32:= 0;
average: real64;
count: int32:= 0;
begin realMath;
// The FINIT instruction initializes the FPU for proper operation. Your applications
// should execute this instruction before executing any other FPU instructions.
finit(); // initialize floating point (math) unit
stdout.puts
(
"The following program asks for input of test scores" nl
"and then finds the average of the scores todate:"
nl nl
);
repeat
inc(count);
stdout.puts( "Enter score: " );
stdin.flushInput();
stdin.get( score );
mov( score, eax );
add( eax, totScores );
stdout.puts( "Enter outOf: " );
stdin.get( outOf );
mov( outOf, eax );
add( eax, totOutOf );
// The FILD (integer load) instruction converts a 16, 32, or 64 bit two's
// complement integer to the 80 bit extended precision format and pushes the
// result onto the stack. This instruction always expects a single operand. This
// operand must be the address of a word, double word, or quad word integer
// variable.
fild( totScores ); // float integer load
fild( totOutOf );
// With no operands, the FDIVP instruction pops ST0 and ST1,
// computes ST1/ST0, and pushes the result back onto the stack.
fdivp(); // find totScores / totOutOf and leave result on top of FPU stack (in st0)
// The FSTP instruction POPS and copies the value on the top of the floating
// point register stack to another floating point register or to a 32, 64, or 80 bit
// memory variable. When copying data to a 32 or 64 bit memory variable, the
// 80 bit extended precision value on the top of stack is rounded to the smaller
// format as specified by the rounding control bits in the FPU control register.
fstp( average ); // POP and store sto, the top of the FPU stack, into average
stdout.put
(
"The average of ", count, " test(s) with ",
totScores, " marks out of ", totOutOf,
" is: ", average:8:3, nl nl
);
stdout.puts( "Another (y/n) ? " );
stdin.flushInput();
until( stdin.getc() = 'n' );
end realMath;
216.92.238.133/Webster/www.artofasm.com/Windows/HTML/RealArithmetic.html#998833
Let’s add some enhancements to the above program that finds a running average. Did you notice that if you enter a non-number, when a number is expected for input, the program stops with an error message? With HLA, we can easily trap those errors. The following demonstrates how:
See also:
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/HelloWorlda3.html#998851
The TRY..EXCEPTION..ENDTRY Statement
The HLA TRY..EXCEPTION..ENDTRY statement provides very powerful exception handling capabilities. The syntax for this statement is the following: ...
Here is a short example:
repeat
// initialize to false at start of repeat loop, 'goodInteger' is assumed
// to have been declared previously as a boolean variable
mov( false, goodInteger );
try
stdout.put( "Enter an integer: " );
// i is presumed to have been declared previously as an integer
stdin.get( i );
mov( true, goodInteger );
exception( ex.ConversionError );
stdout.put( "Illegal numeric value, please re-enter", nl );
exception( ex.ValueOutOfRange );
stdout.put( "Value is out of range, please re-enter", nl );
endtry;
until( goodInteger );
Now let’s put this into our program:
program realMath2;
// finds the running average of test scores
// this version checks for/and handles non-numeric input
#include( "stdlib.hhf" )
static
score: int32;
outOf: int32;
totScores: int32:= 0;
totOutOf: int32:= 0;
average: real64;
count: int32:= 0;
good: boolean;
// In the following macro, 'message' is presumed to be the input message-string passed to the
// macro, and 'num' is some previously declared type of number that is passed to the macro,
// and back again. Note: A macro, is just as if that code was inserted into the place from where
// the macro is called. Thus, this macro behaves similar to an 'inline' function call in C++ ...
// NOTE! It is also an 'overloaded' function, in that the type of number validated, will be the
// type of number passed in.
#macro getNumber( message, num );
repeat
// initialize to false at start of repeat loop, 'good' is assumed
// to have been declared previously as a boolean (global) variable
mov( false, good );
try
stdout.puts( message ); // or more generally, could use stdout.put( )
stdin.flushInput();
// (the value passed to) num is presumed to have been declared
// previously with some type
stdin.get( num );
mov( true, good ); // if no exception jumped to … then accept data
exception( ex.ConversionError );
stdout.put( "Illegal numeric value, please re-enter", nl );
exception( ex.ValueOutOfRange );
stdout.put( "Value is out of range, please re-enter", nl );
endtry;
until( good );
#endmacro
begin realMath2;
// The FINIT instruction initializes the FPU for proper operation. Your applications
// should execute this instruction before executing any other FPU instructions.
finit(); // initialize floating point (math) unit
stdout.puts
(
"The following program asks for input of test scores" nl
"and then finds the average of the scores todate:"
nl nl
);
repeat
inc(count); // increment count by one each time a new score is obtained
getNumber( "Enter score: ", score );
mov( score, eax );
add( eax, totScores );
getNumber( "Enter outOf: ", outOf );
mov( outOf, eax );
add( eax, totOutOf );
// The FILD (integer load) instruction converts a 16, 32, or 64 bit two's
// complement integer to the 80 bit extended precision format and pushes the
// result onto the stack. This instruction always expects a single operand. This
// operand must be the address of a word, double word, or quad word integer
// variable.
fild( totScores ); // float integer load
fild( totOutOf );
// With no operands, the FDIVP instruction pops ST0 and ST1,
// computes ST1/ST0, and pushes the result back onto the stack.
fdivp(); // find totScores/totoutOf and leave result on top of FPU stack (in st0)
// The FSTP instruction POPS and copies the value on the top of the floating
// point register stack to another floating point register or to a 32, 64, or 80 bit
// memory variable. When copying data to a 32 or 64 bit memory variable, the
// 80 bit extended precision value on the top of stack is rounded to the smaller
// format as specified by the rounding control bits in the FPU control register.
fstp( average ); // POP and store sto, the top of the FPU stack, into average
stdout.put
(
"The average of ", count, " test(s) with ",
totScores, " marks out of ", totOutOf,
" is: ", average:8:3, nl nl
);
stdout.puts( "Another (y/n) ? " );
stdin.flushInput();
until( stdin.getc() = 'n' );
end realMath2;
The following version also limits the range of user input values and applies some logic, (like the test score input can't be greater than the total score.) It also illustrates some low-level coding for error-checking and looping.
And note how we have been using a macro to make the code more modular. The macro is called twice in the main program. Note where the macro definition is placed. (The same place as procedures or global variables ... Note also that the macro must be defined before it can be called.)
program realMath3;
// finds the running average of test scores
// This version checks for/and handles non-numeric input, and ...
// also limits the range of user input to proscribed values/logic
#include( "stdlib.hhf" )
static
score: int32;
outOf: int32;
totScores: int32:= 0;
totOutOf: int32:= 0;
average: real64;
count: int32:= 0;
one00: int32:= 100;
// In the following macro, 'message' is presumed to be the input message-
// string passed to the macro, and 'num' is some previously declared type of
// number that is passed to the macro,and back again. Note: A macro, is just
// as if that code was inserted into the place from where the macro is
// called. Thus, this macro behaves similarly to an 'inline' function call
// in C++ ... NOTE! It is also an 'overloaded' function, in that the type of
// number validated, will be the same type of number as the type of the
// number that was passed in. (Here it is an int32)
#macro getNumber( message, num, low, high );
forever
try
stdout.puts( message ); // or more generally, could use stdout.put( )
stdin.flushInput();
stdin.get( num ); // the value passed to num is expected to be
// a type int32 just as num was declared
// don't accept numbers less than low or more than high
if( num < low || num > high ) then
raise( ex.ValueOutOfRange );
endif;
unprotected;
break; // IF we arrive here, NO exception was raised above,
// so break out of loop right now ...
exception( ex.ConversionError );
stdout.put( "Illegal numeric value, please re-enter", nl );
exception( ex.ValueOutOfRange );
stdout.put( "Value is out of range, please re-enter", nl );
endtry;
endfor;
#endmacro
begin realMath3;
// The FINIT instruction initializes the FPU for proper operation. Your applications
// should execute this instruction before executing any other FPU instructions
finit(); // initialize floating point (math) unit
console.setAttrs( console.blue, console.cyan );
console.cls();
stdout.puts
(
"The following program asks for input of test scores" nl
"and then finds the average of the scores todate ..." nl nl
);
repeat
inc(count); // increment count by one each time a new score is obtained
// An example of low level coding follows ... in the outer loop
// ... but for the inner loop, the HLA 'repeat..until' is used
jmp startGetNumber;
errorGetNumber1:
stdout.put( nl "NOT valid entry ... 'out of' must be at least as big as ",
score, nl );
jmp startGetNumber;
errorGetNumber2:
stdout.puts( nl "Entry aborted ... here we go again ..." nl );
startGetNumber:
getNumber( "Enter score: ", score, 0, 100 );
// Don't allow zero entry for 'outOf'. Also, don't allow divide by zero
repeat // This 'repeat..until' loop is an example of High Level coding
getNumber( "Enter outOf: ", outOf, 0, 100 );
if( outOf <= 0 ) then
stdout.puts
(
"'0' not allowed here. Please enter valid data." nl
);
endif;
until( outOf > 0 );
// Also don't allow a score greater than outOf
mov( score, eax );
cmp( eax, outOf );
ja errorGetNumber1; // i.e. jump to top label if score > outOf
// Now one LAST check to confirm the data input was correct ...
stdout.put
(
"You entered a score of ", score, " out of ", outOf,
" <=== OK (y/n) ? "
);
stdin.flushInput();
stdin.getc();
chars.toLower( al ); // Converts 'Y'to 'y'
cmp( al, 'y' );
jne errorGetNumber2; // loop back to TOP error handling message block
// endof low level loop
mov( score, eax );
add( eax, totScores );
mov( outOf, eax );
add( eax, totOutOf );
// The FILD (integer load) instruction converts a 16, 32, or 64 bit two's
// complement integer to the 80 bit extended precision format and pushes the
// result onto the stack. This instruction always expects a single operand. This
// operand must be the address of a word, double word, or quad word integer
// variable.
fild( totScores ); // float integer load
fild( one00 ); // will multiply by 100 to convert to percent
// With no operands, the FMULP instruction pops ST0 and ST1,
// computes the product and pushes the result back onto the stack
fmulp();
fild( totOutOf ); // now in STO ... and totScores*100 now in ST1
// With no operands, the FDIVP instruction pops ST0 and ST1,
// computes ST1/ST0, and pushes the result back onto the stack.
fdivp(); // find totScores*100/totoutOf; (result in stack top i.e. st0)
// The FST and FSTP instructions copy the value on the top of the floating point
// register stack to another floating point register or to a 32, 64, or 80 bit
// memory variable. When copying data to a 32 or 64 bit memory variable, the 80
// bit extended precision value on the top of stack is rounded to the smaller
// format as specified by the rounding control bits in the FPU control register.
// The FSTP instruction POPS the value off the top of stack when moving it to
// the destination location. It does this by incrementing the top of stack
// pointer in the status register after accessing the data in ST0. If the
// destination operand is a floating point register, the FPU stores the
// value at the specified register number before popping the data off
// the top of the stack.
fstp( average ); // POP and store STO, the top of the FPU stack, into average
stdout.put
(
nl "The average so far in ", count, " test(s) with ",
totScores, " marks out of ", totOutOf,
" is ", average:7:1, "%" nl nl
);
repeat
stdout.puts( "Another (y/n) ? " );
stdin.flushInput();
stdin.getc(); // recall returns char in 'al' register
chars.toUpper( al ); // Converts 'y', 'n' to 'Y', 'N"
if( al not in { 'Y', 'N' } ) then
stdout.puts( "Only 'y' or 'n' accepted here ... " );
endif;
until( al in { 'Y', 'N' } );
until( al = 'N' );
stdout.put
(
nl "The FINAL AVERAGE IS ", average:7:1, "% in ", count,
" test(s) with ", totScores, " marks out of ", totOutOf, "." nl
);
end realMath3;
For further reference see:
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/LowLevelControlStructsa2.html
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/LowLevelControlStructs.html#999101
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/Macros.html#998258s.html#998258
Chapter 12: Pointers, Strings, my own Types, Records, Arrays, and dynamic things on the fly
program records;
#include( "stdlib.hhf" )
const
MaxCount: int32:= 3; // to keep testing short/simple
type
MyContact: record
theName:string;
thePhone:string;
endrecord;
static
MyBBook: MyContact[ MaxCount ];
// tot.num of contacts returned in ecx
procedure getBook; @nodisplay; @returns("ecx");
// nested procedures are allowed in HLA
procedure takeIn( message:string ); @nodisplay; @returns("eax");
begin takeIn;
stdout.put( message, ": " );
stdin.flushInput();
stdin.a_gets(); // now eax holds the address of the string just entered
end takeIn;
begin getBook;
mov( 0, ecx );
while( ecx < MaxCount ) do
mov( @size( MyContact ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContact )
// recall takeIn( .. ) returns a pointer to the string in eax
mov( takeIn( "Enter name " ), MyBBook.theName[ebx] );
mov( takeIn( "Enter phone " ), MyBBook.thePhone[ebx] );
inc( ecx );
stdout.puts( "More y/n ? " );
stdin.flushInput();
stdin.getc();
breakif ( al == 'n' || al == 'N' );
endwhile;
end getBook;
procedure printBook; @nodisplay; // Note: assumes ecx holds size of array
const // text string composed of nl and 2 tabs
nlTab2: text:="nl stdio.tab stdio.tab";
begin printBook;
for( mov( 0, ecx ); ecx < edx ; inc( ecx ) ) do
mov( @size( MyContact ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContact )
tdout.puts( nlTab2 ); stdout.puts( MyBBook.theName[ebx] );
stdout.puts( nlTab2 ); stdout.puts( MyBBook.thePhone[ebx] );
stdout.newln();
endfor;
end printBook;
begin records;
getBook(); // returns number of contacts in ecx
mov( ecx, edx ); // edx now holds the tot.num of contacts,
stdout.puts( nl "Your 'book' :" );
printBook(); // value in edx ready for next procedure called
stdout.put( nl "Note! The size of each MyContact, @size(MyContact) = " );
stdout.putu32( @size( MyContact ) );
stdout.put
(
", since each of the" nl
"2 strings in the record 'MyContact' is really only a 4 byte pointer." nl
);
stdin.readLn();
end records;
So … what has been happening above?
Or see this ... with some data input validation:
program records3;
#include( "stdlib.hhf" )
const
MaxCount: int32 := 3; // to keep testing short/simple
type
MyContacts: record
theName: string;
thePhone: string;
endrecord;
static
MyBBook: MyContacts[ MaxCount ];
// count of contacts entered returned in ecx ...
procedure getBook( maxSize:int32 ); @nodisplay; @returns("ecx");
// nested procedures are allowed in HLA
procedure takeIn( message:string ); @nodisplay; @returns("eax");
var
s: string;
begin takeIn;
forever
stdout.put( message, ": " );
stdin.flushInput();
stdin.a_gets();
stdout.put("You input '", (type string eax), "' ... Ok (y/n) ? " );
mov( eax, s ); // get a copy of pointer into s ...
stdin.getc();
if( al == 'y' || al == 'Y' ) then
mov( s, eax );
break;
else str.free( s );
endif;
endfor;
end takeIn;
begin getBook;
mov( 0, ecx );
while( ecx < maxSize ) do
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContacts )
mov( takeIn( "Enter name " ), MyBBook.theName[ebx] );
mov( takeIn( "Enter phone " ), MyBBook.thePhone[ebx] );
inc( ecx );
stdout.puts( "More y/n ? " );
stdin.flushInput();
stdin.getc();
breakif( al == 'n' || al == 'N' );
endwhile;
end getBook;
procedure printBook( numRecs:int32 ); @nodisplay;
const // text string composed of nl and 2 tabs
nlTab2: text:="nl stdio.tab stdio.tab";
begin printBook;
for( mov( 0, ecx ); ecx < numRecs; inc( ecx ) ) do
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // now ebx := ecx*@size( MyContacts )
stdout.puts( nlTab2 ); stdout.puts( MyBBook.theName[ebx] );
stdout.puts( nlTab2 ); stdout.puts( MyBBook.thePhone[ebx] );
stdout.newln();
endfor;
end printBook;
begin records3;
getBook( MaxCount ); // recall, returns the record count in ecx
stdout.puts( nl "Your 'book' :" );
printBook( ecx ); // recall, ecx holds number of records ...
stdout.put
(
nl "Note! The size of each MyContacts, @size(MyContacts) = "
);
stdout.putu32( @size(MyContacts) );
stdout.put
(
", since each of the" nl
"2 strings in the record 'MyContacts' is really only a "
"4 byte pointer ... " nl nl
"Press 'Enter' to quit ... "
);
stdin.readLn();
end records3;
begin records3;
getBook(); // recall … returns the record count in ecx
mov( ecx, recCount );
printBook( recCount );
stdout.put
(
nl "Note! The size of each myContacts, @size(myContacts) = ",
sizeMyContacts, ", since each of the" nl
"2 strings in the record 'myContacts' is really only a 4 byte pointer." nl
);
stdin.readLn();
end records3;
For further reference on HLA strings, see … HLA strings …
http://216.92.238.133/Webster/www.artofasm.com/AoAExtra/HLAStrs.html
Also ...
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/CharacterStringsa2.html
And a little further on down the first page was ( a link to an other HLA user site that has backed up some AoA info - But .... the link is no longer available ...The info was copied here for easy reference.)
This chapter discusses how to declare and use record (structures), unions, and name spaces in your programs. After strings and arrays, records are among the most commonly used composite data types; indeed, records are the mechanism you use to create user-defined composite data types. Many assembly language programmers never bother to learn how to use records in assembly language, yet would never consider not using them in high level language programs. This is somewhat inconsistent since records (structures) are just as useful in assembly language programs as in high level language programs. Given that you use records in assembly language (and especially HLA) in a manner quite similar to high level languages, there really is no reason for excluding this important tool from your programmer's tool chest. Although you'll use unions and name spaces far less often than records, their presence in the HLA language is crucial for many advanced applications. This brief chapter provides all the information you need to successfully use records, unions, and name spaces within your HLA programs.
Another major composite data structure is the Pascal record or C/C++ structure 1 . The Pascal terminology is probably better, since it tends to avoid confusion with the more general term data structure. Since HLA uses the term "record" we'll adopt that term here.
Whereas an array is homogeneous, whose elements are all the same, the elements in a record can be of any type. Arrays let you select a particular element via an integer index. With records, you must select an element (known as a field) by name.
The whole purpose of a record is to let you encapsulate different, but logically related, data into a single package. The Pascal record declaration for a student is probably the most typical example:
student =
record
Name: string [64];
Major: integer;
SSN: string[11];
Midterm1: integer;
Midterm2: integer;
Final: integer;
Homework: integer;
Projects: integer;
end;
Most Pascal compilers allocate each field in a record to contiguous memory locations. This means that Pascal will reserve the first 65 bytes for the name 2 , the next two bytes hold the major code, the next 12 the Social Security Number, etc.
In HLA, you can also create structure types using the RECORD/ENDRECORD declaration. You would encode the above record in HLA as follows:
type
student: record
Name: char[65];
Major: int16;
SSN: char[12];
Midterm1: int16;
Midterm2: int16;
Final: int16;
Homework: int16;
Projects: int16;
endrecord;
As you can see, the HLA declaration is very similar to the Pascal declaration. Note that, to be true to the Pascal declaration, this example uses character arrays rather than strings for the Name and SSN (U.S Social Security Number) fields. In a real HLA record declaration you'd probably use a string type for at least the name (keeping in mind that a string variable is only a four byte pointer).
The field names within the record must be unique. That is, the same name may not appear two or more times in the same record. However, all field names are local to that record. Therefore, you may reuse those field names elsewhere in the program.
The RECORD/ENDRECORD type declaration may appear in a variable declaration section (e.g., STATIC or VAR) or in a TYPE declaration section. In the previous example the Student declaration appears in the TYPE section, so this does not actually allocate any storage for a Student variable. Instead, you have to explicitly declare a variable of type Student. The following example demonstrates how to do this: (Click on the above link to see graphics and the rest ...)
Also here is a good place to start looking for an answer to any HLA How do I code for ... type question:
http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/AoATOC.html
For a little fun assignment, write a short HLA program, using the above student record type ... and output the record size. ( Hint: use the HLA function @size( student ); ) Does your output agree with the bytes you added up for the record in your head? (Recall int16 uses 2 bytes.)
Here is a little demo program, but don’t look until you have tried your own.
program studentRec;
#include( "stdlib.hhf" )
type
student: record
// NOTE! 'Name' is a reserved word, so used Fullname
Fullname: char[65];
Major: int16;
SSN: char[12];
Midterm1: int16;
Midterm2: int16;
Final: int16;
Homework: int16;
Projects: int16;
endrecord;
static
stRecSize: int32:= @size ( student ); // figured out at compile time
i32: int32;
begin studentRec;
mov( @size( student ), eax );
mov( eax, i32 ); // figured out at run time
stdout.put
(
"The number of bytes in each student record here is : ", i32,
nl "stRecSize is : ", i32, nl "Yep! It is : "
);
// The HLA compiler/assembler can recognize this value here ...
// It is inside a procedure/function call & not the stdout.put(..) macro.
// '@size ( student )' is replaced by 'its value' at compile time
stdout.puti32( @size ( student ) );
end studentRec;
Chapter 13: The Computer files its Records
Notice that we are starting out with the same program with which we ended in the previous chapter. For a simple start … we will just add … and then let the program always call a procedure to file the records at the end, before exiting. (Later, we will add another procedure to read these records into an array of records, when we first run our program, if there is an existing file, that is … with records to read.)
Here is a simple ‘pattern’ (to guide us), from AoA, of the HLA functionality we wish to add:
program simpleFileOutput;
#include( "stdlib.hhf" )
static
outputHandle:dword;
begin simpleFileOutput;
fileio.openNew( "myfile.txt" );
mov( eax, outputHandle );
for( mov( 0, ebx ); ebx < 10; inc( ebx ) ) do
fileio.put( outputHandle, ( type uns32 ebx ), nl );
endfor;
fileio.close( outputHandle );
end simpleFileOutput;
program fileRecords;
#include( "stdlib.hhf" )
const
MaxCount: int32 := 3; // to keep testing short/simple
FNAME: text := """records.dat""";
type
MyContacts: record
theName: string;
thePhone: string;
endrecord;
static
MyBBook: MyContacts[ MaxCount ];
// count of contacts entered returned in ecx ...
procedure getBook( maxSize:int32 ); @nodisplay; @returns("ecx");
// nested procedures are allowed in HLA
procedure takeIn( message:string ); @nodisplay; @returns("eax");
var
s: string;
begin takeIn;
forever
stdout.put( message, ": " );
stdin.flushInput();
stdin.a_gets();
stdout.put("You input '", (type string eax), "' ... Ok (y/n) ? " );
mov( eax, s ); // get a copy of pointer into s ...
stdin.getc();
if( al == 'y' || al == 'Y' ) then
mov( s, eax );
break;
else str.free( s );
endif;
endfor;
end takeIn;
begin getBook;
mov( 0, ecx );
while( ecx < maxSize ) do
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContacts )
mov( takeIn( "Enter name " ), MyBBook.theName[ebx] );
mov( takeIn( "Enter phone " ), MyBBook.thePhone[ebx] );
inc( ecx );
stdout.puts( "More y/n ? " );
stdin.flushInput();
stdin.getc();
breakif ( al == 'n' || al == 'N' );
endwhile;
end getBook;
procedure printBook( numRecs:int32 ); @nodisplay;
begin printBook;
for( mov( 0, ecx ); ecx < numRecs; inc( ecx ) ) do
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // now ebx := ecx*@size( MyContacts )
stdout.put( stdio.tab, MyBBook.theName[ebx]:-20, " ", MyBBook.thePhone[ebx], nl )
endfor;
end printBook;
procedure fileBook( numRecs:int32 ); @nodisplay;
var
outFileHandle: dword;
begin fileBook;
try
fileio.openNew( FNAME );
mov( eax, outFileHandle );
for( mov( 0, ecx ); ecx < numRecs; inc( ecx ) ) do
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContacts )
fileio.put( outFileHandle, MyBBook.theName[ebx], nl );
fileio.put( outFileHandle, MyBBook.thePhone[ebx], nl );
endfor;
fileio.close( outFileHandle );
anyexception
stdout.put( "There was some problem opening file ", FNAME, " for output." );
endtry;
end fileBook;
begin fileRecords;
getBook( MaxCount ); // recall, returns the record count in ecx
stdout.puts( nl "Your 'book' ..." nl );
printBook( ecx );
fileBook( ecx );
stdout.put( nl, (type int32 ecx), " records were filed. Press 'Enter' to exit ... " );
stdin.readLn();
end fileRecords;
For next chapters ...
SEE
http://developers-heaven.net/forum/index.php/topic,2607.0.html