A similar program to the above coded in HLA ....
program studentList_test;
// A mini demo of student record keeping
// using an HLA 'list' type student record structure.
// Each new student is inserted in order of unique
// student ID number (an HLA int32).
#include( "stdlib.hhf" )
?@nodisplay:=true;
const
fName: string:="studentInfo.txt";
max: int32:= 3; // using a small number here to keep this demo simple
type
Student:
record
id: int32;
sname: string;
marks: int32[max];
next: pointer to Student;
endrecord;
pStudent: pointer to Student;
static
pHead: pStudent:= NULL;
pTail: pStudent:= NULL; // used by append( pS:pStudent );
fileFlag: boolean:= false;
cnt: int32:= 0;
const
header1: string:= "STUDENT GRADES MANAGEMENT SYSTEM" nl;
header2: string:= "1. (I)nitialize student record list in RAM" nl
"2. (A)dd i.e. insert in order, by ID, a NEW student record" nl
"3. view/(D)elete a student record" nl
"4. view/(E)dit a student record" nl
"5. (S)how all student records (as inserted BY ID)" nl
"6. show all student records sorted by (N)ame" nl
"7. (F)ile all student records presently in RAM" nl
"8. (R)ead all student records on file into RAM" nl
"9. e(X)it" nl;
// Return a pointer to the Student if ID found in list; else return NULL
procedure pID( n:int32 ); @returns("eax");
begin pID;
push( ecx );
mov( pHead, eax ); // get address of pHead into eax
mov( n, ecx ); // using ecx to hold ID number to check in 'n'
//while( eax != NULL ) do
while1:
cmp( eax, NULL );
je endwhile1;
cmp( (type Student[eax]).id, ecx );
je endit; // return address eax, since id number entered was already use
// else ... get next pointer to compare
mov( (type Student[eax]).next, eax );
//endwhile;
jmp while1;
endwhile1:
// If reach here ... then id number not in list so ...
mov( NULL, eax ); // return NULL;
endit:
pop( ecx );
end pID;
// A function to compare two Student records to permit sorting by ID number ...
procedure studentCmp( pS1:pStudent; pS2:pStudent ); @returns("eax");
begin studentCmp;
push( ebx ); push( ecx );
mov( pS1, ebx );
mov( pS2, ecx );
mov( (type Student[ecx]).id, ecx );
if( (type Student[ebx]).id < ecx ) then
mov( 1, eax );
else
mov( 0, eax );
endif;
pop( ecx ); pop( ebx );
end studentCmp;
// insert new Student in list with id's in proper order
procedure insert( pS:pStudent );
begin insert;
push( eax ); push( ebx ); push( ecx );
mov( pS, ebx );
mov( pHead, ecx ); // Get a working copy of pHead in ecx
// Firstly, we handle the case where 'this' should be the first element.
if( pHead == NULL || studentCmp(pS, pHead) ) then
// So ... it now becomes the first element ...
// mov( pHead, ecx );
mov( ecx, (type Student[ebx]).next ); // old pHead becomes 2nd in list ...
mov( ebx, pHead); // pHead = pS; ... this pS becomes the head of the list
else // If here ... search the linked list for the right location ...
// mov( pHead, ecx ); // Get a working copy of pHead in ecx
// Start comparing with element AFTER 'ecx' ... i.e. the 'next in' ...
while( (type Student[ecx]).next != NULL && studentCmp((type Student[ecx]).next, pS) ) do
mov( (type Student[ecx]).next, ecx ); // Traverse the list ...
endwhile;
// Ok now to insert after 'working copy' by relinking the pointers (similar to above)
// ( Includes inserting at end position ... where (type Student[ecx]).next == NULL )
mov( (type Student[ecx]).next, eax );
mov( eax, (type Student[ebx]).next ); // Inserted 'pS' is linked to 'upstream element'
mov( ebx, (type Student[ecx]).next ); // Now 'ecx' is updated to link to the new 'pS' element
endif;
// Update global variables.
inc( cnt );
mov( true, fileFlag );
pop( ecx ); pop( ebx ); pop( eax );
end insert;
procedure get_i( message:string ); @returns( "eax" );
begin get_i;
forever
try
stdout.put( message, ": " );
stdin.flushInput();
stdin.geti32();
unprotected
break;
exception( ex.ValueOutOfRange )
stdout.puts( "Out of range ... " );
exception( ex.ConversionError );
stdout.puts( "Illegal numeric value ... " );
anyexception
stdout.puts( "Illegal entry, integers only ... " );
endtry;
endfor;
end get_i;
procedure addInsertNew; // ... and insert in the proper place in the list.
var
tmpStr: string;
good: boolean;
begin addInsertNew;
push( eax ); push( ebx ); push( ecx );
get_i( "Enter ID (0 to abort)" );
cmp( eax, 0 );
je endit;
mov( eax, ebx ); // get a copy of ID into ebx
if( pID(ebx) ) then // i.e. if pointer returned is NOT NULL, the id IS used
stdout.put( nl "ID ", (type int32 ebx), " is already taken.", nl );
jmp endit; // Exit right now ...
endif;
mov( mem.alloc(@size(Student)), ecx );
mov( ebx, (type Student[ecx]).id ); // pS->id = ID;
stdout.puts( "Enter Name (Last, First): " );
stdin.flushInput();
stdin.a_gets();
mov( eax, (type Student[ecx]).sname );
for( mov( 0, ebx ); ebx<max; inc( ebx ) ) do
push( ebx ); push( ecx ); // preserve across exception handling
mov( str.alloc( 32 ), tmpStr ); // get a tmp string to hold 'input prompt message'
inc( ebx ); // for 1,2,3... instead of 0,1,2....
str.put( tmpStr, "Enter mark ", (type int32 ebx):3 );
//dec( ebx ); // restore ebx to 0,1,2... // see pop( ebx ) ... below
get_i( tmpStr ); // returns int32 in eax
pop( ecx ); pop( ebx ); // preserved across exception handling
mov( eax, (type Student[ecx]).marks[ebx*4] ); // update marks with this mark
str.free( tmpStr );
endfor;
insert( ecx ); // Set up links to this node
endit:
pop( ecx ); pop( ebx ); pop( eax );
end addInsertNew;
procedure view( pS:pStudent );
begin view;
push( eax ); push( ebx ); push( ecx );
mov( pS, ecx );
stdout.put
(
"ID number: ", (type Student[ecx]).id:-4,
" Name: ", (type Student[ecx]).sname:-20, " Marks: "
);
for( mov( 0, eax ); eax < max; inc( eax ) ) do
mov( eax, ebx );
shl( 2, ebx ); // ebx*4
stdout.put( (type Student[ecx]).marks[ebx], " " );
endfor;
pop( ecx ); pop( ebx ); pop( eax );
end view;
procedure showAll;
begin showAll;
if( pHead == NULL ) then
stdout.puts( nl "No records in memory at present." nl );
else
push( ecx );
mov( pHead, ecx );
while( ecx != NULL ) do
view( ecx );
stdout.newln();
mov( (type Student[ecx]).next, ecx );
endwhile;
stdout.newln();
pop( ecx );
endif;
end showAll;
procedure del( pS:pStudent );
begin del;
push( eax ); push( ebx ); push( ecx ); push( edx );
/* Handle special case of 'first in list' */
mov( pS, ecx );
if( ecx == pHead ) then
mov( (type Student[ecx]).next, pHead );//phead = pS->next;
//str.free( (type Student[ecx]).sname ); //free( pS->first );
//mem.free( ecx );//free( pS );
jmp endit; //return;
endif;
/* Else not first in list, so ... */
mov( pHead, ebx );/* set p to this initial value to start loop */
/* Now find the pointer to the previous record. */
while( ebx != pS ) do
mov( ebx, edx );/* pp holds previous pointer value ... */
mov( (type Student[ebx]).next, ebx );/* set to next pointer in link list chain */
endwhile;
/*
Now we have a pointer to the previous student record, so ...
we can now point that previous record to one past this pS record
*/
mov( (type Student[ecx]).next, eax );
mov( eax, (type Student[edx]).next );
/* Now free the memory for this record and update the globals ... */
endit:
str.free( (type Student[ecx]).sname );
mem.free( ecx );
dec( cnt );
if( cnt>0 ) then
mov( true, fileFlag );
else
mov( false, fileFlag );
endif;
pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end del;
procedure viewDel;
begin viewDel;
push( eax ); push( ebx ); push( ecx );
get_i( "Enter the id number to View/Delete" );
mov( eax, ebx ); // ID in ebx
mov( pID( ebx ), ecx ); /* See if pointer exists; i.e. get value or NULL */
if( cl ) then /* i.e. if pointer returned above was not NULL ... */
view( ecx );
stdout.puts( nl "Delete (y/n) ? ");
stdin.flushInput();
stdin.getc();
if( al=='y' || al=='Y' ) then
del( ecx );
endif;
else
stdout.put( nl, "Student with ID number ", (type int32 ebx), " not found.", nl );
endif;
pop( ecx ); pop( ebx ); pop( eax );
end viewDel;
procedure delAll;
begin delAll;
push( ebx ); push( ecx );
mov( pHead, ecx );
while( ecx != NULL ) do
mov( (type Student[ecx]).next, ebx ); // Get a pointer to the next record.
str.free( (type Student[ecx]).sname );
mem.free( ecx );
mov( ebx, ecx ); // Update ecx ...
endwhile;
// Update globals ...
mov( NULL, pHead );
mov( 0, cnt );
mov( false, fileFlag );
pop( ecx ); pop( ebx );
end delAll;
/* Note: calling delAll() here ... will re-set globals as above ... */
procedure init;
begin init;
push( eax );
if( pHead != NULL ) then
stdout.puts( nl "Do you wish to overwrite the records in memory y/n ? " );
stdin.flushInput();
stdin.getc();
if( al=='y' || al=='Y' ) then
delAll(); /* re-sets globals ... */
else
if( cnt==1 ) then stdout.puts( "1 student record was left intact in memory." );
else stdout.put( cnt, " student records were left intact in memory.", nl );
endif;
endif;
else
stdout.puts( nl "There were no records in memory to clear." nl );
endif;
pop( eax );
end init;
procedure viewEdit;
begin viewEdit;
push( eax ); push( ebx ); push( ecx );
get_i( "Enter the id number to View/Edit" );
mov( eax, ebx ); // ID in ebx
mov( pID( ebx ), ecx ); /* See if pointer exists; i.e. get value or NULL */
if( ecx ) then /* i.e. if pointer returned above was not NULL ... */
view( ecx );
stdout.puts( nl "Edit (y/n) ? ");
stdin.flushInput();
stdin.getc();
if( al=='y' || al=='Y' ) then
del( ecx );
mov( true, fileFlag );
addInsertNew();
endif;
else
stdout.put( nl, "Student with ID number ", (type int32 ebx), " not found.", nl );
endif;
pop( ecx ); pop( ebx ); pop( eax );
end viewEdit;
procedure writeFile; @returns( "eax" );
var
fp: dword;
begin writeFile;
push( ebx ); push( ecx ); push( edx );
mov( pHead, ecx );
if( ecx==NULL ) then
stdout.puts( nl "No records available ... so NO records written to file." nl );
mov( 0, eax );
jmp endit;
endif;
fileio.openNew( fName );
mov( eax, fp );
mov( 0, eax ); /* to count the records actually written to the file */
while( ecx!=NULL ) do
fileio.put
(
fp,
(type Student[ecx]).id, nl,
(type Student[ecx]).sname, nl
);
for( mov( 0, edx ); edx < max; inc( edx ) ) do
mov( edx, ebx );
shl( 2, ebx ); // ebx*4
fileio.put( fp, (type Student[ecx]).marks[ebx], nl );
endfor;
mov( (type Student[ecx]).next, ecx );
inc( eax );
endwhile;
fileio.close( fp );
mov( false, fileFlag );
endit:
pop( edx ); pop( ecx ); pop( ebx );
end writeFile;
// uses globals pHead and pTail ...
procedure append( pS:pStudent );
begin append;
push( ebx ); push( ecx );
mov( pS, ecx );
if( pHead != NULL ) then
mov( pTail, ebx ); // get tail address into ebx
mov( ecx, (type Student[ebx]).next ); // point old tail to next
mov( NULL, (type Student[ecx]).next ); // point new tail next to NULL
mov( ecx, pTail ); // update address for tail
else // is first record ... so
mov( ecx, pHead ); // get new address for pHead
mov( NULL, (type Student[ecx]).next ); // point next to NULL
mov( ecx, pTail ); // update address for tail
endif;
pop( ecx ); pop( ebx );
end append;
procedure readFile; @returns( "eax" );
var
fp: dword;
count: int32;
exists: boolean;
tmpStr: string;
begin readFile;
push( ebx ); push( ecx ); push( edx );
str.alloc( 32 );
mov( eax, tmpStr );
mov( 0, count );
try
fileio.open( fName, fileio.r );
mov( eax, fp );
mov( true, exists );
anyexception
mov( false, exists );
stdout.put
(
nl "Error opening file ", fName, "." nl
"Perhaps it hasn't been created yet?" nl
"Press 'Enter' to continue ... "
);
stdin.readLn();
endtry;
cmp( exists, false );
je endit;
init(); // BUT!!! may need to delete any records in memory first.
/*
NOTE: we need pHead to be equal to NULL in the following ...
to be permitted to set pHead ...
to point to the first student record in memory (read in from the file).
*/
if( pHead != NULL ) then // then exit with message ...
if( cnt==1 ) then
stdout.puts( nl "The 1 former student record was left in memory." nl );
else
stdout.put( nl "The ", cnt, " former student records were left in memory." nl );
//mov( 0, count );// done above ... So old count of student records will NOT be updated.
jmp endit;
endif;
endif;
// If the program reaches here ... then ...
while( !fileio.eof( fp ) ) do
mem.alloc(@size(Student));
mov( eax, ecx );
fileio.get( fp, tmpStr );
conv.strToi32( tmpStr, 0 );
mov( eax, (type Student[ecx]).id );
fileio.a_gets( fp );
mov( eax, (type Student[ecx]).sname );
for( mov( 0, edx ); edx < max; inc( edx ) ) do
mov( edx, ebx );
shl( 2, ebx ); // ebx*4
fileio.get( fp, tmpStr );
conv.strToi32( tmpStr, 0 );
mov( eax, (type Student[ecx]).marks[ebx] );
endfor;
//insert( ecx );
append( ecx ); // ... since file already in order ... sorted by ID
inc( count );
endwhile;
fileio.close( fp );
if( count==1 ) then
stdout.puts( nl "1 record was read into memory from the disk file." nl );
else
stdout.put( nl, count, " records were read into memory from the disk file." nl );
endif;
endit:
str.free( tmpStr );
if( count>0 ) then mov( false, fileFlag ); endif;
mov( count, eax ); // return count of student records found in the file.
pop( edx ); pop( ecx ); pop( ebx );
end readFile;
// sort an array of pointers
// sort in ascending order of the names pointed to ...
procedure bubbleSort( a:dword );
var
noswap: boolean;
begin bubbleSort;
push( eax ); push( ebx ); push( ecx ); push( edx ); push( esi ); push( edi );
mov( a, ebx );
// repeat
repeatTop:
mov( true, noswap );
//for( mov( 1, edx ); edx < cnt; inc( edx ) ) do
mov( 1, edx );
beginLoop:
mov( edx, ecx );
sub( 1, ecx );
mov( [ebx+ecx*4], esi ); // get 1st pointer into esi
mov( [ebx+edx*4], edi ); // get 2nd pointer into edi
if( str.gt( (type Student[esi]).sname, (type Student[edi]).sname ) ) then
mov( esi, [ebx+edx*4] );
mov( edi, [ebx+ecx*4] );
mov( false, noswap );
endif;
//endfor;
inc( edx );
cmp( edx, cnt );
jb beginLoop;
// until( noSwap );
cmp( noswap, true );
jne repeatTop;
pop( edi ); pop( esi ); pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end bubbleSort;
#macro showByName( a );
if( cnt==0 ) then
stdout.puts( nl "No records in memory at present." nl );
else
push( ecx ); push( edx );
for( mov( 0, ecx ); ecx<cnt; inc( ecx ) ) do
mov( a, edx );
mov( [edx+ecx*4], edx ); // get next pointer into edx
view( edx );
stdout.newln();
endfor;
stdout.newln();
pop( edx ); pop( ecx );
endif;
#endmacro
procedure showAllByName;
var
ary: dword;
begin showAllByName;
push( eax ); push( ecx );
// create an array of pointers to hold pointers in list
mov( cnt, eax );
shl( 2, eax ); // eax*4
mem.alloc( eax );
mov( eax, ary );
// copy list pointers to ary
mov( pHead, ecx );
while( ecx != NULL ) do
mov( ecx, [eax] ); // copy pointer into array
add( 4 , eax ); // get next ary address
mov( (type Student[ecx]).next, ecx ); // get next pointer in list
endwhile;
//showByName( ary ); // cnt records
// sort ary of pointers, with cnt linked names, by name
bubbleSort( ary ); // cnt records
// show cnt records sorted by name, used sorted array of pointers
showByName( ary ); // cnt records
mem.free( ary );
pop( ecx ); pop( eax );
end showAllByName;
procedure menu; @returns( "al" );
begin menu;
stdout.puts( header1 );
if( cnt == 1 ) then stdout.puts( "Presently there is 1 record in RAM." nl nl );
else stdout.put( "Presently there are ", cnt, " records.", nl, nl );
endif;
stdout.puts( header2 );
stdout.puts( nl "Please enter your selection: ");
stdin.flushInput();
stdin.getc();
end menu;
begin studentList_test;
mov( readFile(), cnt );
showAll();
forever
menu();
if ( al=='1' || al=='i' || al=='I' ) then init();
elseif ( al=='2' || al=='a' || al=='A' ) then addInsertNew();
elseif ( al=='3' || al=='d' || al=='D' ) then viewDel();
elseif ( al=='4' || al=='e' || al=='E' ) then viewEdit();
elseif ( al=='5' || al=='s' || al=='S' ) then showAll();
elseif ( al=='6' || al=='n' || al=='N' ) then showAllByName();
elseif ( al=='7' || al=='f' || al=='f' ) then
if( fileFlag ) then
mov( writeFile(), cnt);
stdout.put( cnt, " records were filed." nl );
endif;
elseif ( al=='8' || al=='r' || al=='R' ) then
// If any were read ... will update Global variable pHead
if( readFile() > 0 ) then
mov( eax, cnt ); // update Global cnt
endif;
elseif ( al=='9' || al=='x' || al=='X' ) then
if( fileFlag ) then
mov( writeFile(), cnt );
stdout.put( cnt, " records were filed." nl );
endif;
break;
else stdout.puts( nl "Not implemented yet ..." nl);
endif;
endfor;
end studentList_test;