program print_message;
#include( "stdlib.hhf" )
begin print_message;
stdout.put( "Hello World!" );
end print_message;
program HelloWorld;
#include( "stdlib.hhf" )
static
// nl is concated to end of string at compile time
hwString : string := "Hello World ... low_level" nl;
prsEnter : string := "Press 'Enter' to continue/exit ... ";
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;
push( prsEnter );
call stdout.puts;
call stdin.readLn; // keep window open until the enter key is pressed
end HelloWorld;
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
// declare first_name, last_name as strings in static memory ...
// and reserve (i.e. allocate) space for 64 byte-size characters
first_name: str.strvar(64);
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;
program get_input2;
#include( "stdlib.hhf" )
// after two front slash characters, 'comments' may be inserted into HLA programs
// declare firstname; lastname as string variables in var (dynamic) memory ...
var
firstname: string;
lastname: string;
begin get_input2;
stdout.put( "Please enter your first name: " );
stdin.a_gets();
mov( eax, firstname );
stdout.put( "Now enter your last name: " );
stdin.a_gets();
mov( eax, lastname );
stdout.put( "Are you really ", lastname, ", ", firstname, "?" nl );
str.free( lastname );
str.free( firstname );
end get_input2;
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 adjacent strings are all concatenated together
stdout.put( nl "Enter a second integer " );
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 );
stdout.newln(); // output an appropriate carriage return/newline character(s)
end add_nums;
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 'var' 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 an 8 bit register,
// the lowest 8 bit byte of 'eax', eax is 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;
program repeat_proc2;
#include( "stdlib.hhf" )
procedure get_nums_find_sum;
begin get_nums_find_sum;
stdout.puts( "Enter an integer: " );
stdin.flushInput();
stdin.geti32();
mov( eax, ebx );
stdout.puts( "Enter a second integer: " );
stdin.flushInput();
stdin.geti32();
stdout.put( "The sum of ", (type int32 ebx), " and ", (type int32 eax), " is " );
add( ebx, eax );
stdout.puti32( eax );
end get_nums_find_sum;
procedure done; @returns( "al" );
begin done;
stdout.puts( nl "Done (y/n) ? " );
stdin.flushInput();
stdin.getc();
if( al == 'y' || al == 'Y' ) then
mov( 1, eax);
else
stdout.puts( "Ok ... Here we go again." nl );
mov( 0, eax );
endif;
end done;
begin repeat_proc2;
repeat
get_nums_find_sum();
until( done() );
end repeat_proc2;
program repeat_proc3;
#include( "stdlib.hhf" )
procedure getValidInt( msg:string ); @returns( "eax" );
begin getValidInt;
forever
try
stdout.put( msg );
stdin.flushInput();
stdin.geti32(); // May raise an exception.
unprotected
break;
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;
end getValidInt;
procedure get_nums_find_sum;
begin get_nums_find_sum;
mov( getValidInt( "Enter a first integer: " ), ebx );
getValidInt( "Enter a second integer: " );
stdout.put( "The sum of ", (type int32 ebx), " and ", (type int32 eax), " is " );
add( ebx, eax );
stdout.puti32( eax );
end get_nums_find_sum;
procedure done; @returns( "al" );
begin done;
stdout.puts( nl "Done (y/n) ? " );
stdin.flushInput();
stdin.getc();
if( al == 'y' || al == 'Y' ) then
mov( 1, eax);
else
stdout.puts( "Ok ... Here we go again." nl );
mov( 0, eax );
endif;
end done;
begin repeat_proc3;
repeat
get_nums_find_sum();
until( done() );
end repeat_proc3;
program someProgramName; // The program starts here
begin someProgramName; // program execution starts here, when program is run
end someProgramName; // The program ends here
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;
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 the syntax for passing a string and number to the following procedure ...
// (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;
forever
// << Code to execute (at least once)
// prior to the termination test >>
breakif( termination_expression );
// << Code to execute after the loop-termination test >>
endfor;
// 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:
// 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
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, nl );
end lowlevel2;
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;
// 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;
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;
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;
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;
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 );
stdin.readLn();
end testRandom;
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? "
nl nl "Press 'Enter' to continue/exit ... "
);
stdin.readLn();
bot:
end memoryFlowLab;
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 );
stdout.put( nl nl "Press 'Enter' to continue/exit ... " );
stdin.readLn();
end memoryFlowLab2;
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 ); // overflow example ...
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 ]) ... means to go to the address held in eax ...
// and then to 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 int32 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 int32 the 4 bytes in eax
end memoryFlowLab3;
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();
stdin.readLn();
end types;
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;
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 );
tdout.put( "Illegal numeric value, please re-enter", nl );
exception( ex.ValueOutOfRange );
tdout.put( "Value is out of range, please re-enter", nl );
endtry;
until( goodInteger );
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;
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;
// NOTE: empty char is in every set ... so IS IN { 'Y', 'N' }
until( !( al not 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;
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 )
stdout.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;
program records; // demo using NO global var's //
#include( "stdlib.hhf" )
type
MyContact: record
theName:string;
thePhone:string;
endrecord;
pMyContact: pointer to MyContact;
// tot.num of contacts returned in ecx
procedure getBook( pC: pMyContact in ebx; size: int32 ); @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 < size ) do
mov( @size( MyContact ), edx );
intmul( ecx, edx ); // edx := ecx*@size( MyContact )
// recall takeIn( .. ) returns a pointer to the string in eax
mov( takeIn( "Enter name " ), ( type string [ebx+edx] ) );
add( @size(MyContact.theName), edx );
mov( takeIn( "Enter phone " ), ( type string [ebx+edx] ) );
inc( ecx );
stdout.puts( "More y/n ? " );
stdin.flushInput();
stdin.getc();
breakif ( al == 'n' || al == 'N' );
endwhile;
end getBook;
procedure printBook( pC: pMyContact in ebx; size: 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 < size ; inc( ecx ) ) do
mov( @size( MyContact ), edx );
intmul( ecx, edx ); // edx := ecx*@size( MyContact )
stdout.puts( nlTab2 ); stdout.puts( ( type string [ebx+edx] ) );
add( @size(MyContact.theName), edx );
stdout.puts( nlTab2 ); stdout.puts( ( type string [ebx+edx] ) );
stdout.newln();
endfor;
end printBook;
procedure mainProc; @nodisplay;
const
MaxCount: int32:= 3; // to keep testing short/simple
static
MyBBook: MyContact[ MaxCount ];
size: int32;
begin mainProc;
getBook( &MyBBook, MaxCount ); // returns number of contacts in ecx
mov( ecx, size );
stdout.puts( nl "Your 'book' :" );
printBook( &MyBBook, size );
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 mainProc;
begin records;
mainProc();
end records;
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;
student =
record
Name: string [64];
Major: integer;
SSN: string[11];
Midterm1: integer;
Midterm2: integer;
Final: integer;
Homework: integer;
Projects: integer;
end;
type
student: record
Name: char[65];
Major: int16;
SSN: char[12];
Midterm1: int16;
Midterm2: int16;
Final: int16;
Homework: int16;
Projects: int16;
endrecord;
program studentRec;
#include( "stdlib.hhf" )
type
student: record
// NOTE! 'Name' is a reserved word, so used Fullname
Fullname: char[68];
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;
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;
program readFile;
#include( "stdlib.hhf" )
const
MaxCount: int32 := 100; // to allow for a sufficient? number of MyContacts
FNAME: text := """contacts.dat"""; // each """ includes one " in output
type
MyContacts: record
theName: string;
thePhone: string;
endrecord;
static
MyBBook: MyContacts[ MaxCount ]; // get array space for MaxCount MyContacts
// tot.num of contacts returned in ecx
procedure newBook( 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 );
stdin.getc();
if( al == 'y' || al == 'Y' ) then
mov( s, eax );
break;
else str.free( s );
endif;
endfor;
end takeIn;
begin newBook;
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( nl "More y/n ? " );
stdin.flushInput();
stdin.getc();
breakif( al == 'n' || al == 'N' );
endwhile;
end newBook;
// returns count of records written to file in ecx ...
procedure fileBook( numRecs:int32 ); @nodisplay; @returns( "ecx" );
var
outFileHandle: dword;
begin fileBook;
try
fileio.openNew( FNAME ); // overwrites any existing file
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 );
exception( ex.FileOpenFailure )
stdout.put( "There was a problem opening file ", FNAME, " for output." nl );
endtry;
end fileBook;
// returns count of records read from file in ecx ...
procedure inputBook( maxSize:int32 ); @nodisplay; @returns( "ecx" );
var
inFileHandle: dword;
begin inputBook;
try
fileio.open( FNAME, fileio.r ); // open file for reading
mov( eax, inFileHandle );
mov( 0, ecx ); // initialize counter ...
while( !fileio.eof( inFileHandle) ) do
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContacts )
// allocate space for new strings and
// move pointers into array (of pointers)
fileio.a_gets( inFileHandle); // returns new string in eax
mov( eax, MyBBook.theName[ebx] ); // store in array of pointers
fileio.a_gets( inFileHandle);
mov( eax, MyBBook.thePhone[ebx] );
inc( ecx ); // increment contact count
endwhile;
fileio.close( inFileHandle );
exception( ex.FileOpenFailure )
stdout.puts
(
nl "There was some problem reading your file. "
"Perhaps it doesn't exist?"
nl "Do want to start a new contact book (y/n) ? "
);
stdin.getc();
if( al = 'y' || al = 'Y' ) then
newBook( maxSize ); // recall ... returns size in ecx
fileBook( ecx );
else
mov( 0, ecx );
endif;
endtry;
end inputBook;
procedure printBook( numRecs:int32 ); @nodisplay;
begin printBook;
// a little different formatting ...
stdout.puts
(
nl "Your 'book' : __________________________________________________" nl
);
for( mov( 0, ecx ); ecx < numRecs; nop() ) do // nop() as inc(ecx) inside loop
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContacts )\
inc( ecx );
stdout.puts( nl stdio.tab stdio.tab );
stdout.putu32Size( ecx, 3, '0' ); // 3 spaces and front pad with zeros
stdout.put( ": ", MyBBook.theName[ebx]:-20 );
stdout.puts( " ---> " );
stdout.put( MyBBook.thePhone[ebx] );
endfor;
end printBook;
begin readFile;
inputBook( MaxCount ); // recall ... returns the record count in ecx
printBook( ecx );
stdout.put( nl nl "Total contacts now in memory = ", (type int32 ecx),
". Press 'Enter' to exit ... " );
stdin.readLn();
end readFile;
// store next string from file-with-named-handle in newly allocated memory
fileio.a_gets( inFileHandle); // and return pointer to that memory in eax
// ebx holds previously calculated offset into next record in array myBBook
mov( eax, myBBook.theName[ebx] ); // store new address in array of addresses
MyOutputFile.create();
file.create();
mov( eax, filePtr );
// checkerBoard.hla //
// Modified from a program found on the web mid 2007
// A good illustration of NESTED LOOPs ...
// Also illustrates HLA MODular division.
// But here, dividing by an even number, we can simply
// and faster CHECK if the last BIT(s) = 0 to see if
// the number itself divides, (with a zero remainder),
// by 2 ... or an other even number.
// Adapted to HLA and using HLA console COLOURS
program cb;
#include( "stdlib.hhf" )
static
c: char := 'x';
i: uns32;
j: uns32;
k: uns32;
begin cb;
stdout.puts( nl stdio.tab "Checkers ?" nl nl );
for( mov(1, i); i <= 16; inc(i) ) do
for( mov(0, j); j < 8; inc(j) ) do
for( mov(0, k); k < 3; inc(k) ) do
// Change foreground colour to red,
// background to black.
if( c = 'x' ) then
console.setAttrs( console.red, console.black );
else
console.setAttrs( console.white, console.black );
endif;
stdout.put(c);
endfor;
if( c = 'x') then
mov( 'o', c );
else
mov( 'x', c );
endif;
endfor;
stdout.newln();
// ----------------------------------
// an example of HLA MODular DIVision ...
// ----------------------------------
mov( 0, edx ); // initialize to zero
mov( i, eax );
imod( 2, edx:eax ); // returns "edx"
if( edx = 0 ) then // if( (i mod 2) = 0 ) then
if( c = 'x' ) then
mov( 'o', c );
else
mov( 'x', c );
endif;
endif;
endfor;
// Change foreground colour to cyan, background to blue
console.setAttrs( console.cyan, console.blue );
stdout.puts( "Press 'Enter' to exit ... " );
stdin.readLn();
console.cls();
stdout.puts( nl stdio.tab "Checkers ?" nl nl );
for( mov(1, i); i <= 16; inc(i) ) do // start count at one so 2nd / 2 = 0
for( mov(0, j); j < 8; inc(j) ) do
for( mov(0, k); k <3; inc(k) ) do
// Change foreground colour to red,
// background to black.
if( c = 'x' ) then
console.setAttrs( console.red, console.black );
else
console.setAttrs( console.white, console.black );
endif;
stdout.put(c);
endfor;
if( c = 'x') then mov( 'o', c ); else mov( 'x', c ); endif;
endfor;
stdout.newln();
// --------------------------------------------------------
// demo HLA MODular DIVision by an even number USING A MASK
// --------------------------------------------------------
mov( i, eax );
and( 1, eax ); // MASKs out all bits except lowest in eax
if( eax = 0 ) then // i.e. if( (i mod 2) = 0 ) then
if( c = 'x') then mov( 'o', c ); else mov( 'x', c ); endif;
endif;
endfor;
// Change foreground colour to cyan, background to blue
console.setAttrs( console.cyan, console.blue );
stdout.puts( "Press 'Enter' to exit ... " );
stdin.readLn();
end cb;
program addToContacts;
// A MENU function 'showMenu' added ... also a name change of the
// 'newBook' function ... changed to 'updateBook' to make it more generic
// Note: If recCount=0 the first time called ... it is a 'new book' ...
// but later it is called with recCount=at_present_num to updateBook by
// adding more contacts
// Name of 'inputBook' function changed to 'inputBook_fromFile' to make it
// more descriptive of what it does. Also fewer passed parameters in
// this ver. (Used global variable 'recCount' instead, for simplicity here.)
#include( "stdlib.hhf" )
const
maxCount: int32:= 100; // to allow for a sufficient? number of 'myContacts'
cFileName: text:= """contacts.dat"""; // each """ includes one " in output
type
myContacts: record
theName: string;
thePhone: string;
endrecord;
static
// special global variables utilized in program
myBBook: myContacts[ maxCount ];
recCount: int32 := 0; // initalized to 0 before contacts.dat file read
done: boolean := false;
// tot.num of contacts returned in ecx
procedure updateBook; @nodisplay; @returns("ecx");
var
more: boolean;
// nested procedures are allowed in HLA
procedure takeIn( message:string ); @nodisplay; @returns("eax");
var
s: string;
c: char;
begin takeIn;
repeat
stdout.put( message, ": " );
stdin.flushInput();
stdin.a_gets();
stdout.put("You input ", (type string eax), " ok (y/n) ? " );
mov( eax, s );
stdin.get( c );
if( c = 'y' ) then mov( s, eax );
else str.free( s );
endif;
until( c='y' );
end takeIn;
begin updateBook;
mov( recCount, ecx );
mov( true, more );
while( ( ecx < maxCount ) && more ) 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] );
stdout.puts( nl "More y/n ? " );
stdin.flushInput();
stdin.getc();
if ( al='n' ) then mov( false, more ); endif;
inc( ecx );
endwhile;
end updateBook;
procedure fileBook; @nodisplay;
var
outFileHandle: dword;
begin fileBook;
fileio.openNew( cFileName ); // overwrites any existing file
mov( eax, outFileHandle );
for( mov( 0, ecx ); ecx < recCount; 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 );
end fileBook;
// returns count of records in file in ecx
procedure inputBook_fromFile; @nodisplay; @returns( "ecx" );
var
inFileHandle: dword;
begin inputBook_fromFile;
try
fileio.open( cFileName, fileio.r ); // open file for reading
mov( eax, inFileHandle );
mov( 0, ecx ); // initialize counter to zero
while( !fileio.eof( inFileHandle) ) do
mov( @size( myContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myContacts )
// allocate space for new string and
// mov pointer into array (of pointers)
fileio.a_gets( inFileHandle);
// returns new string in eax
// mov pointer into array of pointers
mov( eax, myBBook.theName[ebx] );
fileio.a_gets( inFileHandle);
mov( eax, myBBook.thePhone[ebx] );
inc( ecx ); // increment count of contacts
endwhile;
fileio.close( inFileHandle );
exception( ex.FileOpenFailure )
stdout.puts
(
nl "There was some problem reading your file. "
"Perhaps it dosen't exist?"
nl "Do want to start a new contact book (y/n) ? "
);
stdin.getc();
if( al = 'y' || al = 'Y' ) then
updateBook();
mov( ecx, recCount ); // update global variable recCount
fileBook();
else
mov( 0, ecx );
endif;
endtry;
end inputBook_fromFile;
procedure showBook; @nodisplay;
begin showBook;
// a little different formatting ...
console.cls();
stdout.puts
(
nl "Your 'book' : __________________________________________________" nl
);
for( mov( 0, ecx ); ecx < recCount; inc( ecx ) ) do
mov( @size( myContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myContacts )\
stdout.puts( nl stdio.tab stdio.tab );
inc( ecx ); // show 1,2,3... and not 0,1,2 ...
stdout.putu32Size( ecx, 3, '0' );
dec (ecx ); // restore to 0,1,2,...
stdout.put( ": ", myBBook.theName[ebx]:-24 );
stdout.puts( " ---> " );
stdout.put( myBBook.thePhone[ebx]:15 );
endfor;
end showBook;
procedure showMenu; @nodisplay;
begin showMenu;
showBook();
stdout.put
(
nl nl "Total contacts now in memory = ", recCount, "."
nl nl "Add new contacts (y/n) ? "
);
stdin.flushInput();
stdin.getc();
if( al = 'y' ) then
updateBook();
mov( ecx, recCount ); // update the global variable recCount
fileBook();
else
mov( true, done ); // done is also a global variable
endif;
end showMenu;
begin addToContacts;
inputBook_fromFile(); // recall ... returns the record count in ecx
mov( ecx, recCount ); // update global variable recCount
repeat
showMenu();
until( done );
end addToContacts;
program editContacts;
// 'editContact' function added
// The 'nested' function 'takeIn' was made global to allow it to be
// re-used ... NOW it is called from more then just that one procedure.
// Recall that procedures, (variables, etc.), must be defined, (in
// appropriate scope), BEFORE they can be used in a program.
#include( "stdlib.hhf" )
const
maxCount: int32 := 100; // to allow for a sufficient? number of 'myContacts'
cFileName: text := """contacts.dat"""; // each """ includes one " in output
type
myContacts: record
theName: string;
thePhone: string;
endrecord;
static
// special global variables utilized in program (to keep it simple here)
myBBook: myContacts[ maxCount ];
recCount: int32:= 0; // initalized to zero before contacts.dat file is read
done: boolean:= false;
procedure takeIn( message:string ); @nodisplay; @returns("eax");
var
s: string;
c: char;
begin takeIn;
repeat
stdout.put( message, ": " );
stdin.flushInput();
stdin.a_gets();
stdout.put("You input ", (type string eax), " ok (y/n) ? " );
mov( eax, s );
stdin.get( c );
if( c = 'y' ) then mov( s, eax );
else str.free( s );
endif;
until( c='y' );
end takeIn;
// tot.num of contacts returned in ecx
procedure updateBook; @nodisplay; @returns("ecx");
var
more: boolean;
begin updateBook;
mov( recCount, ecx );
mov( true, more );
while( ( ecx < maxCount ) && more ) 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] );
stdout.puts( nl "More y/n ? " );
stdin.flushInput();
stdin.getc();
if ( al='n' ) then mov( false, more ); endif;
inc( ecx );
endwhile;
end updateBook;
procedure fileBook; @nodisplay;
var
outFileHandle: dword;
begin fileBook;
fileio.openNew( cFileName ); // overwrites any existing file
mov( eax, outFileHandle );
for( mov( 0, ecx ); ecx < recCount; 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 );
end fileBook;
// returns count of records in file in ecx
procedure inputBook_fromFile; @nodisplay; @returns( "ecx" );
var
inFileHandle: dword;
begin inputBook_fromFile;
try
fileio.open( cFileName, fileio.r ); // open file for reading
mov( eax, inFileHandle );
mov( 0, ecx ); // initialize counter to zero
while( !fileio.eof( inFileHandle) ) do
mov( @size( myContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myContacts )
// allocate space for new string and
// mov pointer into array (of pointers)
fileio.a_gets( inFileHandle);
// returns new string in eax
// mov pointer into array of pointers
mov( eax, myBBook.theName[ebx] );
fileio.a_gets( inFileHandle);
mov( eax, myBBook.thePhone[ebx] );
inc( ecx ); // increment count of contacts
endwhile;
fileio.close( inFileHandle );
exception( ex.FileOpenFailure )
stdout.puts
(
nl "There was some problem reading your file. "
"Perhaps it dosen't exist?"
nl "Do want to start a new contact book (y/n) ? "
);
stdin.getc();
if( al = 'y' || al = 'Y' ) then
updateBook();
mov( ecx, recCount ); // update global variable recCount
fileBook();
else
mov( 0, ecx );
endif;
endtry;
end inputBook_fromFile;
procedure showBook; @nodisplay;
begin showBook;
// a little different formatting ...
console.cls();
stdout.puts
(
nl
"Your 'book' : __________________________________________________"
nl
);
for( mov( 0, ecx ); ecx < recCount; inc( ecx ) ) do
mov( @size( myContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myContacts )\
stdout.puts( nl stdio.tab stdio.tab );
inc( ecx ); // to show 1,2,3 ... and not 0,1,2 ...
stdout.putu32Size( ecx, 3, '0' );
dec (ecx ); // now restore to 0,1,2 ...
stdout.put( ": ", myBBook.theName[ebx]:-24 );
stdout.puts( " ---> " );
stdout.put( myBBook.thePhone[ebx]:15 );
endfor;
end showBook;
procedure editContact; @nodisplay;
var
contactNum: int32;
goodNum: boolean;
begin editContact;
mov( false, goodNum );
repeat
try
stdout.put( nl, "Which contact number (1..", recCount, ") ? " );
stdin.flushInput();
stdin.get( contactNum );
mov( contactNum, eax );
if( (type int32 eax) < 1 || (type int32 eax) > recCount ) then
raise( ex.ValueOutOfRange );
else
mov( true, goodNum );
endif;
exception( ex.ValueOutOfRange )
stdout.puts( nl "Not in valid range. Please re-enter." );
exception( ex.ConversionError )
stdout.puts( nl "Not legal numeric input. Please re-enter." );
endtry;
until( goodNum );
dec( contactNum ); //
mov( @size( myContacts ), ebx );
intmul( contactNum, ebx ); // ebx := contactNumIndex*@size( myContacts )
mov( takeIn( "Enter name " ), myBBook.theName[ebx] );
mov( takeIn( "Enter phone " ), myBBook.thePhone[ebx] );
end editContact;
procedure showMenu; @nodisplay;
begin showMenu;
showBook();
stdout.put
(
nl nl "Total contacts now in memory = ", recCount, "."
nl nl "Add contacts, Edit contacts, or Quit: (a, e, q) ? "
);
stdin.flushInput();
stdin.getc();
if( al = 'a' ) then
updateBook();
mov( ecx, recCount ); // update the global variable recCount
fileBook();
elseif( al = 'e' ) then
editContact();
fileBook();
elseif( al = 'q' ) then
mov( true, done ); // done is also a global variable
endif;
end showMenu;
begin editContacts;
inputBook_fromFile(); // recall ... returns the record count in ecx
mov( ecx, recCount ); // update global variable recCount
repeat
showMenu();
until( done );
end editContacts;
program editContacts2;
// 'deleteContact' function added ...
// 'sortContacts' function added ...
// 'xDupilcates' function added ... Also, the ability to
// update the file is added directly to the 'menu'.
// Note: 'memory leaks' NOT fully handled in this short program ... since here
// the little? amount of allocated memory gets set free when the program quits.
// Note: fixing the ‘memory leaks’ here would make a GOOD project! :)
// (Global variables still used here to 'keep it simple'.)
// Note (one) way to preserve register values across procedure calls that
// use those registers and so would change the values in the registers.
// Do some research on the use of a 'stack' in memory ...
// (Another good project.)
#include( "stdlib.hhf" )
const
maxCount: int32 := 100; // to allow for a sufficient? number of 'myContacts'
cFileName: text := """contacts.dat"""; // each """ includes one " in output
type
myContacts: record
theName: string;
thePhone: string;
endrecord;
static
// global variables utilized in program (to keep it simple here)
myBBook: myContacts[ maxCount ];
recCount: int32:= 0; // initalized to zero before contacts.dat file is read
done: boolean:= false;
// An example of an HLA library function slightly modified here for this demo ...
procedure stricmp; @nodisplay; @noframe; @align(4);
begin stricmp;
push( ebx );
// Compare the two strings until we encounter a zero byte
// or until the corresonding characters are different.
cmpLoop:
mov( [esi], al );
mov( [edi], ah );
add( 1, esi );
add( 1, edi );
cmp( al, 0 );
je notAlpha;
cmp( al, ah );
je cmpLoop;
mov( ax, bx );
// convert to upper case IFF in range 'a..z'
// if( bl >= 'a' && bl <= 'z' ) then and( $5f, bl ); endif
cmp( bl, 'a' );
jb excludeBL;
cmp( bl, 'z' );
ja excludeBL;
and( $5f, bl);
excludeBL:
// IF bl NOT in A..Z THEN 'jmp notAlpha'
cmp( bl, 'A' );
jb notAlpha;
cmp( bl, 'Z' );
ja notAlpha;
// if( bh >= 'a' && bh <= 'z' ) then and( $5f, bh ); endif;
cmp( bh, 'a' );
jb excludeBH;
cmp( bh, 'z' );
ja excludeBH;
and( $5f, bh );
excludeBH:
// IF bh NOT in A..Z THEN 'jmp notAlpha'
cmp( bh, 'A' );
jb notAlpha;
cmp( bh, 'Z' );
ja notAlpha;
// If we arrive here, bl AND bh are in the range A..Z
cmp( bl, bh );
je cmpLoop;
// so ... NOW exit here!
// Note: we do NOT want to fall through to the final cmp(al,ah)
pop( ebx );
ret();
notAlpha:
// At this point, we've either encountered a zero byte in the source
// string or we've encountered two bytes that are not the same in
// the two strings. In either case, return the result of the
// comparison in the flags.
cmp( al, ah );
pop( ebx );
ret();
end stricmp;
// An example of an HLA library function slightly modified here for this demo ...
procedure ilt( src1:string; src2:string ); @nodisplay; @noalignstack;
begin ilt;
push( esi );
push( edi );
mov( src1, esi );
mov( src2, edi );
stricmp();
mov( 0, eax );
setb( al );
pop( edi );
pop( esi );
end ilt;
procedure takeIn( message:string ); @nodisplay; @returns("eax");
var
s: string;
c: char;
begin takeIn;
repeat
stdout.put( message, ": " );
stdin.flushInput();
stdin.a_gets();
stdout.put("You input ", (type string eax), " ok (y/n) ? " );
mov( eax, s );
stdin.get( c );
if( c = 'y' ) then mov( s, eax );
else str.free( s );
endif;
until( c = 'y' );
end takeIn;
// returns valid NUMBER in range (1..recCount) in 'eax'...
procedure takeInValidNum; @nodisplay; @returns( "eax" );
var
goodNum: boolean;
begin takeInValidNum;
mov( false, goodNum );
repeat
try
stdout.put( nl, "Which contact number (1..", recCount, ") ? " );
stdin.flushInput();
stdin.geti32(); // returns number in eax
if( (type int32 eax) < 1 || (type int32 eax) > recCount ) then
raise( ex.ValueOutOfRange );
else
mov( true, goodNum );
endif;
exception( ex.ValueOutOfRange )
stdout.puts( nl "Not in valid range. Please re-enter." );
exception( ex.ConversionError )
stdout.puts( nl "Not legal numeric input. Please re-enter." );
endtry;
until( goodNum );
end takeInValidNum;
// tot.num of contacts returned in ecx
procedure updateBook; @nodisplay; @returns("ecx");
var
more: boolean;
begin updateBook;
push( ebx );
mov( recCount, ecx );
mov( true, more );
while( ( ecx < maxCount ) && more ) 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] );
stdout.puts( nl "More y/n ? " );
stdin.flushInput();
stdin.getc();
if ( al='n' ) then mov( false, more ); endif;
inc( ecx );
endwhile;
pop( ebx);
end updateBook;
procedure fileBook; @nodisplay;
var
outFileHandle: dword;
begin fileBook;
push( eax );
push( ebx );
push( ecx );
fileio.openNew( cFileName ); // overwrites any existing file
mov( eax, outFileHandle );
for( mov( 0, ecx ); ecx < recCount; 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 );
pop( ecx );
pop( ebx );
pop( eax );
end fileBook;
// returns count of records in file in ecx
procedure inputBook_fromFile; @nodisplay; @returns( "ecx" );
var
inFileHandle: dword;
begin inputBook_fromFile;
push( eax );
push( ebx );
try
fileio.open( cFileName, fileio.r ); // open file for reading
mov( eax, inFileHandle );
mov( 0, ecx ); // initialize counter to zero
while( !fileio.eof( inFileHandle) ) do
mov( @size( myContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myContacts )
// allocate space for new string and
// mov pointer into array (of pointers)
fileio.a_gets( inFileHandle);
// returns new string in eax
// mov pointer into array of pointers
mov( eax, myBBook.theName[ebx] );
fileio.a_gets( inFileHandle);
mov( eax, myBBook.thePhone[ebx] );
inc( ecx ); // increment count of contacts
endwhile;
fileio.close( inFileHandle );
exception( ex.FileOpenFailure )
stdout.puts
(
nl "There was some problem reading your file. "
"Perhaps it dosen't exist?"
nl "Do want to start a new contact book (y/n) ? "
);
stdin.getc();
if( al = 'y' || al = 'Y' ) then
updateBook();
mov( ecx, recCount ); // update global variable recCount
fileBook();
else
mov( 0, ecx );
endif;
endtry;
pop( ebx );
pop( eax );
end inputBook_fromFile;
procedure showBook; @nodisplay;
var
lineCount: int32;
begin showBook;
push( ebx );
push( ecx );
// a little different formatting ...
console.cls();
stdout.puts
(
nl
"Your 'book' : __________________________________________________"
nl
);
mov( 2, lineCount );
for( mov( 0, ecx ); ecx < recCount; inc( ecx ) ) do
inc( lineCount );
mov( @size( myContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myContacts )\
stdout.puts( nl stdio.tab stdio.tab );
inc( ecx ); // to show 1,2,3 ... and not 0,1,2 ...
stdout.putu32Size( ecx, 3, '0' );
dec (ecx ); // now restore to 0,1,2 ...
stdout.put( ": ", myBBook.theName[ebx]:-24 );
stdout.puts( " ---> " );
stdout.put( myBBook.thePhone[ebx]:15 );
// pause after 22 lines printed ... (add if desrired)
/*
if( lineCount = 22 && ecx <= recCount) then
stdout.puts( nl nl "Press 'Enter' to continue ... " );
stdin.readLn(); // wait for 'Enter' key to be pressed
mov( 0, lineCount ); // set to zero lines re. next screen full
endif;
*/
endfor;
pop( ecx );
pop( ebx );
end showBook;
procedure editContact; @nodisplay;
var
contactNum: int32;
begin editContact;
push( eax );
push( ebx );
takeInValidNum(); // returns num in eax
mov( eax, contactNum );
dec( contactNum ); // so = indexes go 0,1,2... (and NOT 1,2,3...)
mov( @size( myContacts ), ebx );
intmul( contactNum, ebx ); // ebx := contactNumIndex*@size( myContacts )
mov( takeIn( "Enter name " ), myBBook.theName[ebx] );
mov( takeIn( "Enter phone " ), myBBook.thePhone[ebx] );
pop( ebx );
pop( eax );
end editContact;
procedure deleteContact; @nodisplay;
begin deleteContact;
push( eax );
push( ebx );
if( recCount > 0 ) then // can't delete 0 records
takeInValidNum(); // returns num in eax
while( eax < recCount ) do
mov( @size( myContacts ), ebx );// equals 8 here ...
intmul( eax, ebx );
mov( myBBook.theName[ebx], myBBook.theName[ebx-8] );
mov( myBBook.thePhone[ebx], myBBook.thePhone[ebx-8] );
inc( eax );
endwhile;
dec( recCount );
endif;
pop( ebx );
pop( eax );
end deleteContact;
// a 'bubblesort' ...
procedure sortBook; @nodisplay;
var
topCount: int32;
noSwap: boolean;
#macro swap( a, b );
mov( a, edx );
xchg( edx, b );
mov( edx, a );
#endmacro
begin sortBook;
push( eax );
push( ebx );
push( edx );
push( esi );
if( recCount > 1 ) then // can't sort 1 record
mov( recCount, topCount );
repeat
mov( true, noSwap );
for( mov( 1, esi ); esi < topCount; inc( esi ) ) do
mov( @size( myContacts ), ebx ); // equals 8 here ...
intmul( esi, ebx ); // ebx := contactNumIndex*@size( myContacts )
// returns 'true' result in 'al' register if string1 'less than' string2
// and ... IGNORES CASE i.e. (converts to Caps to compare)
ilt( myBBook.theName[ebx], myBBook.theName[ebx-8] );
if( al ) then // swap pointers ...
swap( myBBook.theName[ebx], myBBook.theName[ebx-8] );
swap( myBBook.thePhone[ebx], myBBook.thePhone[ebx-8] );
mov( false, noSwap );
endif;
endfor;
dec( topCount ); // since highest value already in last position
until( noSwap );
endif;
pop( esi );
pop( edx );
pop( ebx );
pop( eax );
end sortBook;
/*
int xDuplicates(int a[], int n) // a is an array of int;
{ // n is the number of int's
int i, k = 0;
for( i = 1; i < n; i++ )
{
if( a[k] != a[i] ) // starts with a[0] compared with a[1] ...
{
a[k+1] = a[i]; // a lot of copying if few or no duplicates
k++;
}
// if equal goto next 'i' ( but still same old 'k' though )
}
return (k+1); // number of unique elements in array
}
*/
// Adapted from the above 'C' language algorithm found on the web.
procedure xDuplicates; @nodisplay;
var
k: int32;
begin xDuplicates;
push( eax );
push( ebx );
push( ecx );
push( esi );
mov( 0, k );
for( mov( 1, esi ); esi < recCount; inc( esi) ) do
mov( @size( myContacts ), ebx );
intmul( esi, ebx );
mov( @size( myContacts ), ecx );
intmul( k, ecx );
//if (a[k] != a[i]) then
if( str.ine( myBBook.theName[ecx], myBBook.theName[ebx] ) ) then
//a[k+1] = a[i];
add( @size( myContacts ), ecx );// now ecx holds (k+1)
mov( myBBook.theName[ebx], myBBook.theName[ecx] );
mov( myBBook.thePhone[ebx], myBBook.thePhone[ecx] );
inc( k );
endif;
endfor;
inc(k);
mov( k, recCount ); // return ( k+1 )
pop( esi );
pop( ecx );
pop( ebx );
pop( eax );
end xDuplicates;
procedure showMenu; @nodisplay;
begin showMenu;
push( eax );
showBook();
stdout.put
(
nl nl "Total contacts now in memory = ", recCount, "."
nl nl "Add, Delete, Edit, Sort, XDuplicates, File or Quit (a, d, e, s, x, f, q) ? "
);
stdin.flushInput();
stdin.getc();
if( al = 'a' ) then
updateBook();
mov( ecx, recCount ); // update the global variable recCount
fileBook();
elseif( al = 'd' ) then
deleteContact();
fileBook();
elseif( al = 'e' ) then
editContact();
fileBook();
elseif( al = 's' ) then
sortBook();
elseif( al = 'x' ) then
//sortBook(); // should call sort first (if not already sorted)
xDuplicates(); // 'chop' duplicates works on a pre-sorted array
elseif( al = 'f' ) then
fileBook();
os.system( "notepad " + cFileName ); // load 'contacts.dat' file into
// text editor to view or edit
elseif( al = 'q' ) then
mov( true, done ); // 'done' is also a global variable
endif;
pop( eax );
end showMenu;
begin editContacts2;
inputBook_fromFile(); // recall ... returns the record count in ecx
mov( ecx, recCount ); // update global variable recCount
repeat
showMenu();
until( done );
end editContacts2;
program editContacts2free;
// Note: The 'memory leaks' are handled now ...
// i.e. all deleted/duplicate (allocated) strings are now sent to str.free( stringToDelete )
// Static global variable 'RecCount' is used exclusively to keep 'count'.
// Updating the 'contacts.dat' file is Done automatically if records changed.
// New global boolean variable 'Sorted' added to keep track if Sorted, (or needs to be Sorted).
// Sorting is Done automatically if xDuplicates called, (if not already Sorted)
// or if contacts added or edited.
// Also ... see the example of a type of debugging used here.
#include( "stdlib.hhf" )
// set to 'true' to turn on debugging code/info to be included in the .exe file
? myDebug:= false;
? @nodisplay:= true; // All procedures have @nodisplay 'as default' now.
const
MaxSize: int32 := 100; // to allow for a sufficient? number of 'MyContacts'
ContactsFNAME: text := """contacts.dat"""; // each """ includes one " in output
type
MyContacts: record
theName: string;
thePhone: string;
endrecord;
static
// global variables utilized in program (to keep it simple here)
MyBBook: MyContacts[ MaxSize ];
RecCount: int32:= 0; // initalized to zero before contacts.dat file is read
Sorted: boolean:= false;
Done: boolean:= false;
// An example of an HLA library function slightly modified here for this demo ...
procedure stricmp; @noframe; @align(4);
begin stricmp;
push( ebx );
// Compare the two strings until we encounter a zero byte
// or until the corresonding characters are different.
cmpLoop:
mov( [esi], al );
mov( [edi], ah );
add( 1, esi );
add( 1, edi );
cmp( al, 0 );
je notAlpha;
cmp( al, ah );
je cmpLoop;
mov( ax, bx );
// convert to upper case IFF in range 'a..z'
// if( bl >= 'a' && bl <= 'z' ) then and( $5f, bl ); endif;
cmp( bl, 'a' );
jb excludeBL;
cmp( bl, 'z' );
ja excludeBL;
and( $5f, bl);
excludeBL:
// IF bl NOT in A..Z THEN 'jmp notAlpha'
cmp( bl, 'A' );
jb notAlpha;
cmp( bl, 'Z' );
ja notAlpha;
// if( bh >= 'a' && bh <= 'z' ) then and( $5f, bh ); endif;
cmp( bh, 'a' );
jb excludeBH;
cmp( bh, 'z' );
ja excludeBH;
and( $5f, bh );
excludeBH:
// IF bh NOT in A..Z THEN 'jmp notAlpha'
cmp( bh, 'A' );
jb notAlpha;
cmp( bh, 'Z' );
ja notAlpha;
// If we arrive here, bl AND bh are in the range A..Z
cmp( bl, bh );
je cmpLoop;
// so ... NOW exit here!
// Note: we do NOT want to fall through to the final cmp(al, ah)
pop( ebx );
ret();
notAlpha:
// At this point, we've either encountered a zero byte in the source
// string or we've encountered two bytes that are not the same in
// the two strings. In either case, return the result of the
// comparison in the flags.
cmp( al, ah );
pop( ebx );
ret();
end stricmp;
// An example of an HLA library function slightly modified here for this demo ...
procedure ilt( src1:string; src2:string ); @noalignstack;
begin ilt;
push( esi );
push( edi );
mov( src1, esi );
mov( src2, edi );
stricmp();
mov( 0, eax );
setb( al );
pop( edi );
pop( esi );
end ilt;
// An example of an HLA library function slightly modified here for this demo ...
procedure ieq( src1:string; src2:string ); @noalignstack;
begin ieq;
push( esi );
push( edi );
mov( src1, esi );
mov( src2, edi );
stricmp();
mov( 0, eax );
sete( al );
pop( edi );
pop( esi );
end ieq;
procedure takeIn( message:string; value:string ); @returns("eax");
var
s: string;
begin takeIn;
forever;
stdout.puts( message );
if( str.length( value ) > 0 ) then
stdout.put( "(", value, "): " );
else
stdout.puts( ": " );
endif;
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;
// returns VALID NUMBER in range (1..RecCount) in 'eax'...
procedure takeInValidNum; @returns( "eax" );
begin takeInValidNum;
forever;
try
stdout.put( nl, "Which contact number (1..", RecCount, ") ? " );
stdin.flushInput();
stdin.geti32(); // returns number in eax
if( (type int32 eax) < 1 || (type int32 eax) > RecCount ) then
raise( ex.ValueOutOfRange );
endif;
unprotected
break;
exception( ex.ValueOutOfRange )
stdout.puts( nl "Not in valid range. Please re-enter." );
exception( ex.ConversionError )
stdout.puts( nl "Not legal numeric input. Please re-enter." );
endtry;
endfor;
end takeInValidNum;
procedure fileBook;
var
outFileHandle: dword;
begin fileBook;
push( eax );
push( ebx );
push( ecx );
try
fileio.openNew( ContactsFNAME ); // overwrites any existing file
mov( eax, outFileHandle );
for( mov( 0, ecx ); ecx < RecCount; 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 a problem opening file ", ContactsFNAME,
" for output." nl );
endtry;
pop( ecx );
pop( ebx );
pop( eax );
end fileBook;
// a 'bubblesort' ... by NAMES ...
procedure sortBook;
var
topCount: int32;
noSwap: boolean;
#macro swap( a, b );
mov( a, edx );
xchg( edx, b );
mov( edx, a );
#endmacro
#macro myCmp;
if( str.eq( MyBBook.thePhone[ebx], MyBBook.thePhone[ebx-8] ) ) then
ilt( MyBBook.thePhone[ebx], MyBBook.thePhone[ebx-8] );
else
ilt( MyBBook.thePhone[ebx], MyBBook.thePhone[ebx-8] );
endif;
#endmacro
begin sortBook;
if( RecCount > 1 && !Sorted ) then // can't sort 1 record
push( eax );
push( ebx );
push( edx );
push( esi );
mov( mov( RecCount, eax ), topCount );
repeat
mov( true, noSwap );
for( mov( 1, esi ); esi < topCount; inc( esi ) ) do
mov( @size( MyContacts ), ebx ); // equals 8 here ...
intmul( esi, ebx ); // ebx := contactNumIndex*@size( MyContacts )
// returns 'true' result in 'al' register if string1 'less than' string2
// and ... IGNORES CASE i.e. converts to Caps to compare
myCmp; // cmp value in al
if( al ) then // swap pointers ...
str.eq( MyBBook.thePhone[ebx], MyBBook.thePhone[ebx-8] );
if( al) then
swap( MyBBook.theName[ebx], MyBBook.theName[ebx-8] );
swap( MyBBook.thePhone[ebx], MyBBook.thePhone[ebx-8] );
mov( false, noSwap );
endif;
endif;
endfor;
dec( topCount ); // since highest value already in last position
until( noSwap );
mov( true, Sorted ); // update global variable 'Sorted'
fileBook(); // update contacts.dat file
pop( esi );
pop( edx );
pop( ebx );
pop( eax );
endif;
end sortBook;
// Total number of contacts is stored in global variable RecCount
procedure addContacts;
begin addContacts;
push( eax );
push( ebx );
push( ecx );
mov( RecCount, 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( nl "More y/n ? " );
stdin.flushInput();
stdin.getc();
breakif( al == 'n' || al == 'N' );
if( ecx == MaxSize ) then
stdout.put( "You have reached the max array size of ", MaxSize, nl );
endif;
endwhile;
if( ecx > RecCount ) then // Contacts were added ...
mov( ecx, RecCount ); // So, update the global variable 'RecCount'
mov( false, Sorted ); // 'authorize' a sort
sortBook();
endif;
pop( ecx );
pop( ebx);
pop( eax );
end addContacts;
// returns count of records in file in global variable RecCount
procedure inputBook_fromFile;
var
inFileHandle: dword;
begin inputBook_fromFile;
push( eax );
push( ebx );
try
fileio.open( ContactsFNAME, fileio.r ); // open file for reading
mov( eax, inFileHandle );
// RecCount was initialized to zero in static memory
while( !fileio.eof( inFileHandle) ) do
mov( @size( MyContacts ), ebx );
intmul( RecCount, ebx );
// allocate space for new string and
// mov pointer into array (of pointers)
fileio.a_gets( inFileHandle); // returns new string in eax
mov( eax, MyBBook.theName[ebx] ); // mov pointer into array of pointers
fileio.a_gets( inFileHandle);
mov( eax, MyBBook.thePhone[ebx] );
inc( RecCount ); // increment count of contacts
endwhile;
fileio.close( inFileHandle );
exception( ex.FileOpenFailure )
stdout.puts
(
nl "There was some problem reading your file. "
"Perhaps it dosen't exist?"
nl "Do want to start a new contact book (y/n) ? "
);
stdin.getc();
if( al = 'y' || al = 'Y' ) then
mov( 0, RecCount );
addContacts();
endif;
endtry;
pop( ebx );
pop( eax );
end inputBook_fromFile;
procedure editContact;
var
contactNum: int32;
s: string;
begin editContact;
if( RecCount > 0 ) then // need at least 1 record to edit ...
push( eax );
push( ebx );
mov( takeInValidNum(), contactNum ); // returns num in eax
dec( contactNum ); // so = indexes go 0,1,2... (and NOT 1,2,3...)
mov( @size( MyContacts ), ebx );
intmul( contactNum, ebx ); // ebx := contactNumIndex*@size( MyContacts )
// free old allocated strings and overwite with new ...
mov( MyBBook.theName[ebx], eax );
mov( eax, s ); // get copy of pointer into s ...
mov( takeIn( "Enter name ", s ), MyBBook.theName[ebx] );
str.free( s );
mov( MyBBook.thePhone[ebx], eax );
mov( eax, s ); // get copy into s ...
mov( takeIn( "Enter phone ", s ), MyBBook.thePhone[ebx] );
str.free( s );
mov( false, Sorted ); // 'authorize' a sort
sortBook();
pop( ebx );
pop( eax );
endif;
end editContact;
procedure deleteContact;
begin deleteContact;
if( RecCount > 0 ) then // can't delete 0 records
push( eax );
push( ebx );
takeInValidNum();
// returns valid num 1..RecCount in eax
dec( eax ); // to get to proper record index
str.free( MyBBook.thePhone[eax*8] ); // ok ... free these strings
str.free( MyBBook.theName[eax*8] );
inc( eax ); // to get back to record number
while( eax < RecCount && RecCount > 1 ) do // need at least 2 rec's to do this loop
mov( @size( MyContacts ), ebx );// equals 8 here ...
intmul( eax, ebx ); // ebx := contactNumIndex*@size( MyContacts )
mov( MyBBook.theName[ebx], MyBBook.theName[ebx-8] );
mov( MyBBook.thePhone[ebx], MyBBook.thePhone[ebx-8] );
inc( eax );
endwhile;
dec( RecCount );
fileBook();
pop( ebx );
pop( eax );
endif;
end deleteContact;
/*
int xDuplicates(int a[], int n) // a is an array of int;
{ // n is the number of int's
int i, k=0;
for (i = 1; i < n; i++) //
{
if (a[k] != a[i]) // starts with a[0] compared with a[1] ...
{
// a[k+1] = a[i]; <==> a lot of copying IF NO duplicates
if( k+1 != i ) a[k+1] = a[i]; // ... but fixed here so it does NOT copy in this case
k++;
}
// if equal goto next 'i' ( but still same old 'k' though )
}
return (k+1); // number of unique elements in array
}
*/
// Adapted from the above 'C' language algorithim found on the web.
procedure xDuplicates;
var
k: int32;
begin xDuplicates;
if( RecCount > 1 ) then // Note: need RecCount > 1 to enter this block ...
push( eax );
push( ebx );
push( ecx );
push( esi );
if( !Sorted ) then
sortBook();
endif;
mov( 0, k );
for( mov( 1, esi ); esi < RecCount; inc( esi) ) do
mov( @size( MyContacts ), ecx );
intmul( k, ecx ); // ecx holds offset into LAST LOWER UNIQUE record compared
mov( @size( MyContacts ), ebx );
intmul( esi, ebx ); // ebx now holds offset into this 'i'
//if (a[k] != a[i])
if( !( str.ieq( MyBBook.theName[ecx], MyBBook.theName[ebx] ) &&
str.eq( MyBBook.thePhone[ecx], MyBBook.thePhone[ebx] )) ) then
inc( k ); // k = k+1; get ready for next pair of '(k, i)' to compare ...
add( @size( MyContacts ), ecx ); // now ecx holds offset for (k+1)
if( k <> esi ) then // i.e. if ( (k+1) <> i ) then ...
#if(myDebug)
stdout.put( "k=", k, nl nl ); // myDebug <--------------------<<<<
#endif
mov( MyBBook.theName[ebx], MyBBook.theName[ecx] ); // copy this new
mov( MyBBook.thePhone[ebx], MyBBook.thePhone[ecx] );
endif;
else // a[k] = a[i], SO THEN goto next 'i', (i.e. esi), but still same old 'k' though.
// ok to free this 'i' record since = the 'k' record below
#if(myDebug)
stdout.put( "i=", (type uns32 esi), " " ); // myDebug <----------------<<<<
#endif
str.free( MyBBook.thePhone[ebx] ); // ok to free these strings
str.free( MyBBook.theName[ebx] );
endif;
endfor;
inc( k );
mov( k, ecx );
if( ecx < RecCount ) then // i.e. if there were duplicate records then update ...
mov( ecx, RecCount ); // so ... return ( k+1 ) in RecCount
fileBook(); // and ... update 'contacts.dat' file
endif;
pop( esi );
pop( ecx );
pop( ebx );
pop( eax );
endif;
#if(myDebug)
stdout.puts( nl "Press 'Enter' to continue ... " ); // myDebug <-------------------<<<<
stdin.readLn();
#endif
end xDuplicates;
procedure showBook;
var
lineCount: int32;
begin showBook;
push( ebx );
push( ecx );
// a little different formatting ...
console.cls();
stdout.puts
(
nl "Your 'book' : __________________________________________________" nl
);
mov( 2, lineCount );
for( mov( 0, ecx ); ecx < RecCount; inc( ecx ) ) do
inc( lineCount );
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( MyContacts )
stdout.puts( nl stdio.tab stdio.tab );
inc( ecx ); // to show 1,2,3 ... and not 0,1,2 ...
stdout.putu32Size( ecx, 3, '0' );
dec( ecx ); // now restore to 0,1,2 ...
stdout.put( ": ", MyBBook.theName[ebx]:-24 );
stdout.puts( " ---> " );
stdout.put( MyBBook.thePhone[ebx]:15 );
// pause after 22 lines printed ... (add if desrired)
/*
if( lineCount = 22 && ecx <= RecCount) then
stdout.puts( nl nl "Press 'Enter' to continue ... " );
stdin.readLn(); // wait for 'Enter' key to be pressed
mov( 0, lineCount ); // set to zero lines re. next screen full
endif;
*/
endfor;
pop( ecx );
pop( ebx );
end showBook;
procedure showMenuDoChoice;
begin showMenuDoChoice;
push( eax );
showBook();
stdout.put
(
nl nl "Total contacts now in memory = ", RecCount, "."
nl nl "Add, Delete, Edit, Sort, XDuplicates or Quit (a, d, e, s, x, q) ? "
);
stdin.flushInput();
stdin.getc();
if( al = 'a' ) then
addContacts();
elseif( al = 'd' ) then
deleteContact();
elseif( al = 'e' ) then
editContact();
elseif( al = 's' ) then
sortBook();
elseif( al = 'x' ) then
xDuplicates(); // 'chop' duplicates works on a pre-Sorted array.
elseif( al = 'q' ) then
mov( true, Done ); // 'Done' is also a global variable
endif;
pop( eax );
end showMenuDoChoice;
procedure clearAllContacts;
begin clearAllContacts;
for( mov( RecCount, ecx ); ecx > 0; nop() ) do
dec( ecx );
mov( @size( MyContacts ), ebx );
intmul( ecx, ebx );
str.free( MyBBook.theName[ebx] );
str.free( MyBBook.thePhone[ebx] );
endfor;
mov( 0, RecCount );
end clearAllContacts;
begin editContacts2free;
inputBook_fromFile(); // returns the record count in global variable RecCount
repeat
showMenuDoChoice();
until( Done );
clearAllContacts();
end editContacts2free;
program getManna;
#includeonce("w.hhf")
#include( "stdlib.hhf" )
#include("PSmessage.hhf") // PSmessage: string:=
const
maxDays: int32 := 366; // to allow for a leap year of days
cFileName: text := """dailyManna.txt"""; // each """ includes one " in output
type
myManna: record
english: string;
french: string;
endrecord;
static
myFood: myManna[ maxDays ];
dateStr :str.strvar(16);
topLine :str.strvar(128);
verseStr :str.strvar(1024*8); // to be able to hold long strings
today :date.daterec;
// returns count of records in file in ecx
procedure inputManna; @nodisplay; @returns( "ecx" );
var
inFileHandle: dword;
dummyStr: string;
recCount: int32; // to 'backup' ecx in case ecx gets corrupted
// if an exception occurs inside try..endtry
begin inputManna;
str.alloc( 128 );
mov( eax, dummyStr );
mov( 0, recCount );
try
fileio.open( cFileName, fileio.r ); // open file for reading
mov( eax, inFileHandle );
mov( 0, ecx ); // initialize counter to zero
while( !fileio.eof( inFileHandle) && ecx < maxDays ) do
mov( @size( myManna ), ebx );
intmul( ecx, ebx ); // ebx := ecx*@size( myManna )
// allocate space for new strings and
// mov pointers into array (of pointers)
fileio.a_gets( inFileHandle ); // returns new string in eax
mov(eax,myFood.english[ebx]); // mov pointer into array of pointers
fileio.gets( inFileHandle, dummyStr );
fileio.a_gets( inFileHandle );
mov( eax, myFood.french[ebx] );
fileio.gets( inFileHandle, dummyStr );
fileio.gets( inFileHandle, dummyStr );
inc( ecx ); // increment count of contacts
mov( ecx, recCount );
endwhile;
fileio.close( inFileHandle );
anyexception
str.put
(
verseStr,
nl, "There was some problem reading your file.", nl
nl, "Perhaps it dosen't exist? ...", nl
nl, "Or the record structure is corrupted?", nl
nl nl
);
endtry;
str.free( dummyStr );
mov( recCount, ecx );
end inputManna;
begin getManna;
// MAKE TOP LINE MESSAGE STRING WITH TODAY'S DATE ...
date.today( today );
date.setFormat( date.yyyymmdd );
date.setSeparator( '-' );
date.toString( today, dateStr );
date.dayNumber( today ); // returns dayNumber in eax 1..366
str.put
(
topLine,
//"123456789012345678901234567890123456789012345"//
"Food for your heart, Matthew and Bonnie, for ", dateStr,
", day number ", (type int32 eax), " . . . "
);
// Get todays MANNA and STORE INTO array of records ...
inputManna(); // returns the record count in ecx
if( ecx > 0 ) then
date.dayNumber( today );
mov( 0, edx );
imod( ecx, edx:eax ); // edx = 0,1,2,..recCount-1
mov( edx, ebx );
// stdout.put( "ecx=", ecx, " edx=",edx ); // debugging ..........
if( ebx = 0 ) then // i.e. show last record ...
mov( ecx, ebx );
endif;
dec( ebx ); // now at last index ...
intmul( @size( myManna ), ebx ); // ebx now holds offset
str.cat( myFood.english[ebx], verseStr );
str.cat( nl, verseStr );
str.cat( nl, verseStr );
str.cat( myFood.french[ebx], verseStr );
endif;
w.MessageBox( NULL, verseStr, topLine, w.MB_OK );
w.MessageBox( NULL, PSmessage, topLine, w.MB_OK );
end getManna;
// -------- Save this next block as a text file with the name PSmessage.hhf ------- //
const
n: text := "#10";
static
PSmessage: string :=
"P.S. ... 'NOTYOU'" n n
"But ye, brethren, are not in darkness, that that day should overtake you as a thief. "
"Ye are all the children of light, and the children of the day: we are not of the night, "
"nor of darkness." n n
"Therefore let us not sleep, as [do] others; but let us watch and be sober. "
"For they that sleep sleep in the night; and they that be drunken are drunken in the night. "
"But let us, who are of the day, be sober, putting on the breastplate of faith and love; "
"and for an helmet, the hope of salvation. For God hath not appointed us to wrath, "
"but to obtain salvation by our Lord Jesus Christ, Who died for us, that, "
"whether we wake or sleep, we should live together with him." n n
"Wherefore comfort yourselves together, and edify one another, even as also ye do. "
"(1 Thessalonians 5:1-11)" n n
"Seeing [it is] a righteous thing with God to recompense tribulation to them that trouble you; "
"And to you who are troubled rest with us, when the Lord Jesus shall be revealed from heaven "
"with his mighty angels, In flaming fire taking vengeance on them that know not God, and that "
"obey not the gospel of our Lord Jesus Christ: Who shall be punished with everlasting destruction "
"from the presence of the Lord, and from the glory of his power; When he shall come to be glorified "
"in his saints, and to be admired in all them that believe (because our testimony among you was "
"believed) in that day. (2 Thessalonians 1:6-10)" n n
"Knowing this first, that there shall come in the last days scoffers, walking after their own lusts, "
"And saying, Where is the promise of his coming? for since the fathers fell asleep, "
"all things continue as [they were] from the beginning of the creation. "
"For this they willingly are ignorant of, that by the word of God the heavens were of old, "
"and the earth standing out of the water and in the water: Whereby the world that then was, "
"being overflowed with water, perished:" n n
"But the heavens and the earth, which are now, by the same word are kept in store, "
"reserved unto fire against the day of judgment and perdition of ungodly men. "
"But, beloved, be not ignorant of this one thing, that one day [is] with the Lord as a thousand years, "
"and a thousand years as one day. "
"The Lord is not slack concerning his promise, as some men count slackness; "
"but is longsuffering to us-ward, "
"not willing that any should perish, but that all should come to repentance." n n
"But the day of the Lord will come as a thief in the night; "
"in the which the heavens shall pass away with a great noise, "
"and the elements shall melt with fervent heat, "
"the earth also and the works that are therein shall be burned up. "
"[Seeing] then [that] all these things shall be dissolved, what manner [of persons] ought ye to be "
"in [all] holy conversation and godliness, Looking for and hasting unto the coming of the day of God, "
"wherein the heavens being on fire shall be dissolved, and the elements shall melt with fervent heat?" n n
"Nevertheless we, according to his promise, look for new heavens and a new earth, "
"wherein dwelleth righteousness." n n
"Wherefore, beloved, seeing that ye look for such things, "
"be diligent that ye may be found of him in peace, without spot, and blameless. "
"And account [that] the longsuffering of our Lord [is] salvation; even as our beloved brother Paul "
"also according to the wisdom given unto him hath written unto you; As also in all [his] epistles, "
"speaking in them of these things; in which are some things hard to be understood, "
"which they that are unlearned and unstable wrest, as [they do] also the other scriptures, "
"unto their own destruction." n n
"Ye therefore, beloved, seeing ye know [these things] before, beware lest ye also, "
"being led away with the error of the wicked, fall from your own stedfastness. "
"But grow in grace, and [in] the knowledge of our Lord and Saviour Jesus Christ. "
"To him [be] glory both now and for ever. Amen. (2 Peter 3:3-18)" n n
"Remember therefore how thou hast received and heard, and hold fast, and repent. "
"If therefore thou shalt not watch, I will come on thee as a thief, and thou shalt not know "
"what hour I will come upon thee. (Revelation 3:3)" n ;
/*
"Behold, I come as a thief. Blessed [is] he that watcheth, and keepeth his garments, "
"lest he walk naked, and they see his shame. (Revelation 16:15)" n ;
*/
// -------------------------------- end of PSmessage.hhf -------------------------- //
program distanceAndPoints;
// To find the distance between 2 points, we may
// use the fact that the square on the hypotenuse,
// of any right angled triangle, is equal to the sum
// of the squares on the other two sides ...
// See: The hypotenuse of a right triangle
// http://en.wikipedia.org/wiki/Hypotenuse
#include( "stdlib.hhf" )
// We can store our points (on the 2-D XY plane) like this ...
type
point:
record
x: real32;
y: real32;
endrecord;
static
p1: point;
p2: point;
// to hold the distance between two points
d: real32;
// This macro returns the distance between the two
// points a and b ... on the top of the FPU stack.
#macro findDistance( a, b );
fld( b.x ); // load the x part, of the point b, onto the top of the FPU
fld( a.x );
fsubp(); // now have dX in st0, i.e. b.x - a.x in st0
/*
// uncomment these and prove it for your self ...
fst( d );
stdout.put( nl nl, "Verify ... Does x2-x1 = ", d, " ?" nl nl );
*/
fld( st0 ); // Duplicate dX; i.e. dX now in st0 and in st1
fmulp(); // Compute dX*dX; pop st0 and st1 and store product in st0
fld( b.y );
fld( a.y );
fsubp(); // now have dY in st0
fld( st0 ); // Duplicate dY.
fmulp(); // Compute dY*dY.
faddp(); // Compute dX*dX + dY*dY
fsqrt(); // Compute sqrt( dX*dX + dY*dY ).
#endmacro
begin distanceAndPoints;
finit(); // initialize the FPU
stdout.puts( "Enter 1st point x,y : " );
// stdin.get( p1.x, p1.y ); // <== This line is the easy way ...
// or the following block works too: (Recall 4 bytes for each real32.)
mov( &p1, ecx ); // put address of p1 into ecx
stdin.get( (type real32 [ecx]), (type real32 [ecx+4]) ); // ... compare this (1.)
stdout.puts( "Enter 2nd point x,y : " );
stdin.get( p2.x, p2.y ); // ... with this (2.)
findDistance( p1, p2 ); // after execution, the distance is on the top of the FPU
fstp( d ); // pop off top of stack and store into d, a real32 variable.
stdout.put( "The distance is: ", d );
end distanceAndPoints;
fld( y 0; // now have y in st0
fld( x // now have x in st0, and y in st1
fpatan(); // take arctan(st1/st0) and store in st1; pop stack
fmulp( radTodeg ); // convert to degrees ... declared as: radTodeg: real32:= 360 / ( 2 * 3.14159 );
program vectors1;
// Note the changes from the previous program that had
// 2 points ... to this one, with one point w.r.t. to the origin;
// (0, 0) is the reference point for the head of vectors.
#include( "stdlib.hhf" )
type
vector:
record
x: real32;
y: real32;
endrecord;
static
v1: vector;
v2: vector;
// to hold the length of a vector
len: real32;
// Returns the length of a vector
// on the top of the FPU stack.
#macro findLength( a );
fld( a.x ); // now have a.x in st0
fld( st0 ); // Duplicate a.x on TOS.
fmulp(); // Compute a.x*a.x.
fld( a.y ); // now have a.y in st0
fld( st0 ); // Duplicate a.y.
fmulp(); // Compute a.y*a.y.
faddp(); // Compute a.x*a.x + a.y*a.y
fsqrt(); // Compute sqrt( a.x*a.x + a.y*a.y ).
#endmacro
begin vectors1;
finit(); // initialize the FPU
stdout.puts( "Enter 1st vector x, y : " );
stdin.get( v1.x, v1.y );
findLength( v1);
fstp( len );
stdout.put( "The length of v1 is: ", len );
stdout.puts( nl nl "Enter 2nd vector x, y : " );
stdin.get( v2.x, v2.y );
findLength( v2 );
fstp( len );
stdout.put( "The length of v2 is: ", len );
end vectors1;
program vectors2;
// We will add a function here to find the vector direction angle ...
// and add some logic/validation procedures to assist that function.
#include( "stdlib.hhf" )
type
vector:
record
x: real32;
y: real32;
endrecord;
const ZeroVector:dword := 1024; // 1024..65535 are numbers ok to use for user exceptions
static
v1: vector;
v2: vector;
// to hold the length of a vector
len: real32;
angle: real32;
radTodeg: real32 := 360 / ( 2 * 3.14159 );
// this function returns 'true' if the 'real'
// value passed to it is zero, otherwise
// ... it returns 'false'
procedure isZero( r:real32 ); @nodisplay; @returns( "al" );
begin isZero;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
sete( al ); // Set AL to 1 if TOS = 0.0
fstp( st0 ); // NOW ... pop ST0 off the top of the stack.
end isZero;
// this function returns 'true' if the 'real' passed to it is positive
// ... otherwise, it returns 'false'
procedure isPos( r:real32 ); @nodisplay; @returns( "al" );
begin isPos;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
seta( al ); // Set AL to 1 if TOS > 0.0
fstp( st0 ); // NOW ... pop ST0 off the top of the stack.
end isPos;
// Returns the length of a vector
// on the top of the FPU stack.
#macro findLength( a );
fld( a.x ); // now have a.x in st0
fld( st0 ); // Duplicate a.x on TOS.
fmulp(); // Compute a.x*a.x.
fld( a.y ); // now have a.y in st0
fld( st0 ); // Duplicate a.y.
fmulp(); // Compute a.y*a.y.
faddp(); // Compute a.x*a.x + a.y*a.y
fsqrt(); // Compute sqrt( a.x*a.x + a.y*a.y ).
#endmacro
// Returns the angle of a vector in degrees cc, w.r.t. the x-axis
// being zero degrees ... (returned on the top of the FPU stack)
#macro findAngle( a );
// The logic here ... Note: we don't want to
// divide by 0! But, if x is 0, then the angle
// is either 90 deg. if y if positive, or 270 (i.e. -90 )
// deg. if y if neg. If both x and y are 0, then
// the vector direction angle is NOT defined.
// See logic below for 0 deg and 180 deg angles
if( isZero( a.y ) ) then
if( isZero( a.x ) ) then raise( ZeroVector ); // raise exception
elseif( isPos( a.x ) ) then fld( 0.0 );
else fld( 180.0 );
endif;
elseif( isZero( a.x ) ) then
if( isPos( a.y ) ) then fld( 90.0 );
else fld( -90.0 );
endif;
else
fld( a.y ); // now have a.y in st0
fld( a.x ); // now have a.x st0, a.y in st1
fpatan(); // take arctan(st1/st0) and store in st1; pop stack
fld( radTodeg );
fmulp(); // convert to degrees
endif;
#endmacro
procedure more; @nodisplay; @returns( "al" );
begin more;
stdout.puts( "More (y/n) ? " );
stdin.getc();
stdin.flushInput();
if( al == 'n' || al == 'N' ) then mov( 0, al );
else mov( 1, al );
endif;
end more;
begin vectors2;
finit(); // initialize the FPU
repeat
try
stdout.puts( "Enter 1st vector x, y : " );
stdin.get( v1.x, v1.y );
findLength( v1);
fstp( len );
stdout.put( "The length of v1 is: ", len:8:2 );
findAngle( v1 );
fstp( angle );
stdout.put( nl "The angle of v1 is: ", angle:8:2 );
stdin.flushInput();
stdout.puts( nl nl "Enter 2nd vector x, y : " );
stdin.get( v2.x, v2.y );
findLength( v2 );
fstp( len );
stdout.put( "The length of v2 is: ", len:8:2 );
findAngle( v2 );
fstp( angle );
stdout.put( nl "The angle of v2 is: ", angle:8:2, nl nl );
exception( ZeroVector );
stdout.puts( nl nl "Zero vector has no defined direction. " nl );
anyexception
stdout.puts( nl nl "Some exception occurred ... perhaps invalid data?" nl );
endtry;
stdin.flushInput();
until( !more() );
end vectors2;
program vectors3;
// Here, we convert our macros to procedures, to get us closer to being ready,
// (to encapsulate all our vector object related stuff in one VECTOR CLASS),
// for our next program ... after this one.
#include( "stdlib.hhf" )
type
vector:
record
x: real32;
y: real32;
endrecord;
const ZeroVector:dword := 1024; // 1024..65535 ok num's for user exceptions
static
v1: vector;
v2: vector;
// to hold the length of a vector
len: real32;
angle: real32;
radTodeg: real32 := 360 / ( 2 * 3.1415926535897932385 );
// pi = 3.1415926535897932385 // rounded to 20 decimals //
// 3.14159265358979323846264338327950288419... //
// this function returns 'true' if the 'real'
// value passed to it is zero, otherwise
// ... it returns 'false'
procedure isZero( r:real32 ); @nodisplay; @returns( "al" );
begin isZero;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
sete( al ); // Set AL to 1 if TOS = 0.0
fstp( st0 ); // NOW ... pop ST0 off the top of the stack.
end isZero;
// this function returns 'true' if the 'real' passed to it is positive
// ... otherwise, it returns 'false'
procedure isPos( r:real32 ); @nodisplay; @returns( "al" );
begin isPos;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
seta( al ); // Set AL to 1 if TOS > 0.0
fstp( st0 ); // NOW ... pop ST0 off the top of the stack.
end isPos;
// Returns the length of a vector
// on the top of the FPU stack.
procedure findLength( a:vector ); @nodisplay; @returns( "st0" );
begin findLength;
fld( a.x ); // now have a.x in st0
fld( st0 ); // Duplicate a.x on TOS.
fmulp(); // Compute a.x*a.x.
fld( a.y ); // now have a.y in st0
fld( st0 ); // Duplicate a.y.
fmulp(); // Compute a.y*a.y.
faddp(); // Compute a.x*a.x + a.y*a.y
fsqrt(); // Compute sqrt( a.x*a.x + a.y*a.y ).
end findLength;
// Returns the angle of a (2D) vector in degrees cc, w.r.t. the x-axis
// being zero degrees ... (returned on the top of the FPU stack)
procedure findAngle( a:vector ); @nodisplay; @returns( "st0" );
begin findAngle;
// Note logic here: we don't want to divide by 0!
// But, if x is 0, then the angle is either 90 deg.,
// if y if positive, or 270 (i.e. -90 ) deg., if y if neg.
// If both x and y are 0, then the vector direction angle is NOT
// defined. See logic below for 0 deg and 180 deg angles
if( isZero( a.y ) ) then
if( isZero( a.x ) ) then raise( ZeroVector );
elseif( isPos( a.x ) ) then fld( 0.0 );
else fld( 180.0 );
endif;
elseif( isZero( a.x ) ) then
if( isPos( a.y ) ) then fld( 90.0 );
else fld( -90.0 );
endif;
else
fld( a.y ); // now have a.y in st0
fld( a.x ); // now have a.x st0, a.y in st1
fpatan(); // take arctan(st1/st0) and store in st1 pop stack
fld( radTodeg );
fmulp(); // convert to degrees
endif;
end findAngle;
procedure more; @nodisplay; @returns( "al" );
begin more;
stdout.puts( "More (y/n) ? " );
stdin.getc();
stdin.flushInput();
if( al == 'n' || al == 'N' ) then mov( 0, al );
else mov( 1, al );
endif;
end more;
begin vectors3;
finit(); // initialize the FPU
repeat
try
stdout.puts( "Enter 1st vector x, y : " );
stdin.get( v1.x, v1.y );
findLength( v1);
fstp( len );
stdout.put( "The length of v1 is: ", len:8:2 );
findAngle( v1 );
fstp( angle );
stdout.put( nl "The angle of v1 is: ", angle:8:2 );
stdin.flushInput();
stdout.puts( nl nl "Enter 2nd vector x, y : " );
stdin.get( v2.x, v2.y );
findLength( v2 );
fstp( len );
stdout.put( "The length of v2 is: ", len:8:2 );
findAngle( v2 );
fstp( angle );
stdout.put( nl "The angle of v2 is: ", angle:8:2, nl nl );
exception( ZeroVector );
stdout.puts( nl nl "Zero vector has no defined direction. " nl );
anyexception
stdout.puts( nl nl "Some exception occurred ... perhaps invalid data?" nl );
endtry;
stdin.flushInput();
until( ! more() );
end vectors3;
program vectorClass1;
#include( "stdlib.hhf" )
type
Vector:
class
var
x: real32;
y: real32;
procedure create;
procedure destroy;
procedure get_x; @returns( "st0" );
procedure get_y; @returns( "st0" );
method set_x( r:real32 );
method set_y( r:real32 );
method takeIn;
procedure display;
procedure findLength; @returns( "st0" );
procedure findAngle; @returns( "st0" );
endclass;
const
RadToDeg: real32 := 360 / ( 2 * 3.1415926535897932384626433833 );
ZeroVector: dword := 1024; // 1024..65535 are ok for user exceptions
static
VMT( Vector );
v1: Vector;
v2: Vector;
pVec: pointer to Vector;
procedure Vector.create; @nodisplay; @noframe;
begin create;
push( eax );
if( esi = 0 ) then // get new memory IFF NOT already allocated for this obj.
mem.alloc( @size( Vector ) );
mov( eax, esi );
endif;
mov( &Vector._VMT_, this._pVMT_ ); // update virtual memory table
pop( eax );
ret();
end create;
procedure Vector.destroy; @nodisplay;
begin destroy;
push( eax ); // isInHeap uses this
// Place any other clean up code here.
// The code to free dynamic objects should always appear last
// in the destructor.
// The following code assumes that ESI still contains the address
// of the object.
if( isInHeap( esi )) then
free( esi );
endif;
pop( eax );
end destroy;
procedure Vector.get_x; @nodisplay; @noframe; //@returns( "st0" );
begin get_x;
fld( this.x );
ret();
end get_x;
method Vector.set_x( r:real32 ); @nodisplay;
begin set_x;
fld( r );
fstp( this.x );
end set_x;
procedure Vector.get_y; @nodisplay; @noframe; //@returns( "st0" );
begin get_y;
fld( this.y );
ret();
end get_y;
method Vector.set_y( r:real32 ); @nodisplay;
begin set_y;
fld( r );
fstp( this.y );
end set_y;
method Vector.takeIn; @nodisplay;
begin takeIn;
stdout.puts( "Enter vector x, y : " );
stdin.get( this.x, this.y );
end takeIn;
procedure Vector.display; @nodisplay;
var
r32: real32;
begin display;
stdout.put( "For (", this.x:8:2, ",", this.y:8:2, ")" );
this.findLength();
fstp( r32 );
stdout.put( ", length =", r32:8:2 );
this.findAngle();
fstp( r32 );
stdout.put( ", angle =", r32:8:2, nl );
end display;
// Returns the length of a vector
// on the top of the FPU stack.
procedure Vector.findLength; @nodisplay; //@returns( "st0" );
begin findLength;
fld( this.x ); // now have x in st0
fld( st0 ); // Duplicate x on TOS.
fmulp(); // Compute x*x.
fld( this.y ); // now have y in st0
fld( st0 ); // Duplicate y.
fmulp(); // Compute y*y.
faddp(); // Compute x*x + y*y
fsqrt(); // Compute sqrt( x*x + y*y ).
end findLength;
// Returns the angle of a vector in degrees cc, w.r.t.
// x --> zero degrees ... on the top of the FPU stack.
procedure Vector.findAngle; @nodisplay; //@returns( "st0" );
procedure isZero( r:real32 ); @nodisplay; @returns( "al" );
begin isZero;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
sete( al ); // Set AL to 1 if TOS = 0.0
/*
if( !al ) then // consider ALSO as zero if (r < 1.0e-10)
fabs(); // take positive value
fld( 1.0e-10 );
fcompp();
fstsw( ax );
sahf();
setb( al ); // AL = true if ST1 < ST0.
endif;
*/
fstp( st0 ); // NOW ... pop ST0=r off the top of the stack.
end isZero;
procedure isPos( r:real32 ); @nodisplay; @returns( "al" );
begin isPos;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
seta( al ); // Set AL to 1 if TOS > 0.0
fstp( st0 ); // NOW ... pop ST0 off the top of the stack.
end isPos;
begin findAngle;
// Note logic here: we don't want to divide by 0!
// But, if x is 0, then the angle is either 90 deg.,
// if y if positive, or 270 (i.e. -90 ) deg., if y if neg.
// If both x and y are 0, then the vector direction angle is NOT
// defined. See logic below for 0 deg and 180 deg angles
if( isZero( this.y ) ) then
if( isZero( this.x ) ) then raise( ZeroVector );
elseif( isPos( this.x ) ) then fld( 0.0 );
else fld( 180.0 );
endif;
elseif( isZero( this.x ) ) then
if( isPos( this.y ) ) then fld( 90.0 );
else fld( -90.0 );
endif;
else
fld( this.y ); // now have this.y in st0
fld( this.x ); // now have this.x st0, rhis.y in st1
fpatan(); // take arctan(st1/st0) and store in st1 pop stack
fld( RadToDeg );
fmulp(); // convert to degrees
endif;
end findAngle;
procedure more; @nodisplay; @returns( "al" );
begin more;
stdout.puts( "More (y/n) ? " );
stdin.getc();
stdin.flushInput();
if( al == 'n' || al == 'N' ) then mov( 0, al );
else mov( 1, al );
endif;
end more;
begin vectorClass1;
finit(); // initialize the FPU
repeat
try
stdout.newln();
v1.create(); // get new memory IFF NOT already allocated for this obj.
v1.takeIn();
stdin.flushInput();
v1.display();
//v1.destroy();
stdout.newln();
mov( &v2, pVec );
pVec.create();
pVec.takeIn();
pVec.display();
//pVec.destroy();
exception( ZeroVector );
stdout.puts( nl nl "Zero vector has no defined direction. " nl );
anyexception
stdout.puts( nl nl "Some exception occurred ... perhaps invalid data?" nl );
endtry;
stdin.flushInput();
stdout.newln();
until( ! more() );
v1.destroy();
pVec.destroy();
end vectorClass1;
program test_vectorClass;
#includeonce( "vectorClass.hhf" )
static
// VMT( Vector ); // moved to near the top of the file vectorClass.hla
// *** See ALSO the comments there!!! ***
v1: Vector; // used here in test program below ...
v2: Vector;
pVec: pointer to Vector;
procedure more; @nodisplay; @returns( "al" );
begin more;
stdout.puts( "More (y/n) ? " );
stdin.getc();
stdin.flushInput();
if( al == 'n' || al == 'N' ) then mov( 0, al );
else mov( 1, al );
endif;
end more;
begin test_vectorClass;
finit(); // initialize the FPU
repeat
try
v1.create(); // get dynamic memory to hold object v1
// (IFF memory NOT exists already)
stdout.newln();
v1.takeIn(); // set values in object v1
stdin.flushInput();
v1.display(); // show to stdout the stuff ordered up by 'display procedure'
stdout.newln();
mov( &v2, pVec );
pVec.create();
pVec.takeIn();
pVec.display();
exception( Vector.ZeroVector );
stdout.puts( nl nl "Zero vector has no defined direction. " nl );
anyexception
stdout.puts( nl nl "Some exception occured ... perhaps invalid data?" nl );
endtry;
stdout.newln();
stdin.flushInput();
until( ! more() );
v1.destroy();
pVec.destroy();
end test_vectorClass;
#include ( "stdlib.hhf" )
type
Vector:
class
const
RadToDeg: real32 := 360 / ( 2 * 3.1415926535897932384626433833 );
ZeroVector: uns32 := 1024; // 1024..65535 are ok for user exceptions
var
x: real32;
y: real32;
procedure create; @external;
procedure destroy; @external;
procedure get_x; @returns( "st0" ); @external;
procedure get_y; @returns( "st0" ); @external;
method set_x( r:real32 ); @external;
method set_y( r:real32 ); @external;
method takeIn; @external;
procedure display; @external;
procedure findLength; @returns( "st0" ); @external;
procedure findAngle; @returns( "st0" ); @external;
endclass;
unit vectorClass;
#includeonce( "vectorClass.hhf" )
static
VMT( Vector ); // MUST go here, ( and NOT in header file )
// so as NOT to be defined twice when the
// "vectorClass.hhf" is ALSO included
// in the main program.
procedure Vector.create; @nodisplay; @noframe;
begin create;
push( eax );
if( esi = 0 ) then
mem.alloc( @size( Vector ) );
mov( eax, esi );
endif;
mov( &Vector._VMT_, this._pVMT_ );
pop( eax );
ret();
end create;
procedure Vector.destroy; @nodisplay;
begin destroy;
push( eax ); // isInHeap uses this
// Place any other clean up code here.
// The code to free dynamic objects should always appear last
// in the destructor.
// The following code assumes that ESI still contains the address
// of the object.
if( isInHeap( esi )) then
free( esi );
endif;
pop( eax );
end destroy;
procedure Vector.get_x; @nodisplay; @noframe; // @returns( "st0" );
begin get_x;
fld( this.x );
ret();
end get_x;
method Vector.set_x( r:real32 ); @nodisplay;
begin set_x;
fld( r );
fstp( this.x );
end set_x;
procedure Vector.get_y; @nodisplay; @noframe; // @returns( "st0" );
begin get_y;
fld( this.y );
ret();
end get_y;
method Vector.set_y( r:real32 ); @nodisplay;
begin set_y;
fld( r );
fstp( this.y );
end set_y;
method Vector.takeIn; @nodisplay;
begin takeIn;
stdout.puts( "Enter vector x, y : " );
stdin.get( this.x, this.y );
end takeIn;
procedure Vector.display; @nodisplay;
var
r32: real32;
begin display;
stdout.put( "For (", this.x:8:2, ",", this.y:8:2, ")" );
this.findLength();
fstp( r32 );
stdout.put( ", length =", r32:8:2 );
this.findAngle();
fstp( r32 );
stdout.put( ", angle =", r32:8:2, nl );
end display;
// Returns the length of a vector
// on the top of the FPU stack.
procedure Vector.findLength; @nodisplay; // @returns( "st0" );
begin findLength;
fld( this.x ); // now have x in st0
fld( st0 ); // Duplicate x on TOS.
fmulp(); // Compute x*x.
fld( this.y ); // now have y in st0
fld( st0 ); // Duplicate y.
fmulp(); // Compute y*y.
faddp(); // Compute x*x + y*y
fsqrt(); // Compute sqrt( x*x + y*y ).
end findLength;
// Returns the angle of a vector in degrees cc, w.r.t.
// x --> zero degrees ... on the top of the FPU stack.
procedure Vector.findAngle; @nodisplay; // @returns( "st0" );
procedure isZero( r:real32 ); @nodisplay; @returns( "al" );
begin isZero;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
sete( al ); // Set AL to 1 if TOS = 0.0
//
//if( !al ) then // consider ALSO as zero if (r < 1.0e-10)
//
// fabs(); // take positive value
// fld( 1.0e-10 );
// fcompp();
// fstsw( ax );
// sahf();
// setb( al ); // AL = true if ST1 < ST0.
//endif;
//
fstp( st0 ); // NOW ... pop ST0=r off the top of the stack.
end isZero;
procedure isPos( r:real32 ); @nodisplay; @returns( "al" );
begin isPos;
fld( r );
// The FTST instruction compares the value in ST0 against 0.0
// Note that this instruction does not differentiate -0.0 from +0.0
// If the value in ST0 is either of these values, ftst will set C 3
// to denote equality. Note that this instruction does *NOT POP* st(0)
// off the stack ...
ftst();
fstsw( ax );
sahf();
seta( al ); // Set AL to 1 if TOS > 0.0
fstp( st0 ); // NOW ... pop ST0 off the top of the stack.
end isPos;
begin findAngle;
if( isZero( this.y ) ) then
//assert( !isZero( this.x ) );
if( isZero( this.x ) ) then raise( Vector.ZeroVector );
elseif( isPos( this.x ) ) then fld( 0.0 );
else fld( 180.0 );
endif;
elseif( isZero( this.x ) ) then
if( isPos( this.y ) ) then fld( 90.0 );
else fld( -90.0 );
endif;
else
fld( this.y ); // now have y in st0
fld( this.x ); // now have x in st0, y in st1
fpatan(); // take arctan(st1/st0) and store in st1; pop stack
fld( Vector.RadToDeg );
fmulp(); // convert to degrees
endif;
end findAngle;
end vectorClass;