Author Topic: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)  (Read 56833 times)

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #15 on: May 17, 2013, 05:13:08 PM »
Chapter 14: The Computer reads its Files


 
1.  Create (md) a sub-directory in the project folder.  Give it the name Chapt14_readFile

2.  Go (cd) to that new directory.

3.  Using your text editor, create the following program file in that folder.  Give the program file the name readFile.hla

4.  Compile the program and run it. Study the comments that are in the hla program, as well as the output that appears on the screen when you run the program.


Notice that we are again starting out with the same program with which we ended in the previous chapter. For a simple start ... we will just add to it.  We will add another procedure, inputBook(), to read these records into an array of records, when we first run our program, (if there is an existing file, that is ... with records to read.)  Then we will just show them, with a little fancier formatting then before. (Check it out.)

BUT … if the records.dat file does NOT exist in the default directory, i.e. our working folder, we will just report that possibility and then ask if the user wishes to start a new one.  If so, we can just call our previously defined procedure getBook() ... Let’s give it a different name of newBook().  However we must not forget to then, right away, write these new records to the disk.  To do so … we just call the procedure we made in the last chapter: fileBook( .. ).  Of course, we must pass to it the number of elements to file.  No problem.  We just pass along the value in the ecx register from the previous function call of getBook(), which returned the number of contacts input from the keyboard in the ecx register.

NOTE:  In our programs we have NOT been specifying any path to the file that we write to or read from the disk. Thus, the .exe file just then reads/writes its data files from/to the default working directory ... which is the same as the folder that we have just made ... the folder where we have been putting each chapter’s files.


Here then, is our next step in this mini data base type program:
 
Code: [Select]
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;


Some things to note regarding the above program:

 •  If you were to open up the contacts.dat file with a text editor like MS Notepad.exe, you would notice that each field is on a new line, since that is the way we asked the program to write each string to the file in our procedure fileBook( recCount )  … as per the nl at the end of each fileio.put( outFileHandle, myBBook.thePhone[ebx], nl );

•  So when we read back from the file, we can read each field into a newly allocated string and then store the pointer to that new string, (the new string that on the fly was allocated with sufficient bytes to hold the characters in the field just read from the file), in the appropriate offset in our array of records, (which we, before compiling), set to a maximum of 100 …


Code: [Select]
// 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

•  So with the fileio.a_gets( inFileHandle) call, the strings are individually allocated in memory as input, and the new address to each new chunk of memory that holds the characters in that string, is then stored into the next spot in our array.  Since presently, (in 2007), HLA addresses are 32 bits, (i.e. 4 bytes), all we need to reserve ahead of compile time, is 4 bytes for each string in our array, even though while the program is running, we could conceivable allocate 100’s of bytes further to hold the characters of each string then input.

Thus, not so much computer memory space is wasted, just reserving 4 bytes for each pointer ahead of time, if we don’t actually input all those strings allowed by our maxCount of 100 in our program.

•  We could also mem.alloc( numBytes) space for our array of records, (here, of pointers), on the fly, i.e. dynamically, while the program is executing.

•  Note: the older HLA style of mem.alloc was malloc, ... of str.alloc was stralloc and of mem.free( pointer ) was free( pointer ), ... of str.free( p ) was strfree( p)


Note that the older malloc and stralloc types are used in this link:

http://216.92.238.133/Webster/www.artofasm.com/Windows/HTML/ConstsVarsAndDataTypes2.html

 

Copied here for reference ...(original link no longer available.)

16 The File I/O Module (fileio.hhf)

--------------------------------------------------------------------------------

The file I/O functions are quite similar to the file class functions except that you explicitly pass a file handle to these routines rather than invoking a method via a file class variable. In fact, the file class methods call the corresponding fileio functions whenever you invoke a file object's methods. Therefore, if you want your programs to be a little smaller, you should use the fileio functions rather than the file class package.

16.1 General File I/O Functions

Here are the file output routines provided by the HLA fileio unit:

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


The fileio.open routine opens the file by the specified name. The Access parameter is one of the following:

•  fileio.r

•  fileio.w

•  fileio.rw

•  fileio.a


The fileio.r constant tells HLA to open the file for read-only access. The fileio.w constant tells HLA to open the file for writing. Using the fileio.rw constant tells fileio.open to open the file for reading and writing. The fileio.a option tells the fileio.open function to open the file for writing and append all written data to the end of the file.

This routine raise an exception if there is a problem opening the file (e.g., the file does not exist). If the file is successfully opened, this function returns the file handle in the EAX register.

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


This function opens a new file for writing. The single parameter specifies the file's (path) name. This function raises an exception if there is an error opening the file. If the file is opened successfully, this function returns the file handle in the EAX register. If the file already exists, this function will successfully open the file and delete any existing data in the file.

fileio.close( Handle:dword );


14 Exceptions (excepts.hhf)

--------------------------------------------------------------------------------

The exceptions units contains several things of interest. First, it defines the ExceptionValues enumerated data type that lists out all the standard exceptions in the HLA Standard Library. The second thing provided in the excepts unit is the ex.PrintExceptionError procedure which prints a string associated with the exception number in EAX. Next, the excepts.hhf header file defines the "assert( expr )" macro. Finally,  …

14.1.7 ex.BadFileHandle (6)

The file class and fileio library modules raise this exception if you attempt to read from or write to a file with an illegal file handle (i.e., the file has not been opened or has already been closed).

14.1.8 ex.FileOpenFailure (7)

The HLA file open routines raise this error if there was a catastrophic error opening a file.

14.1.9 ex.FileCloseError ( 8 )

The HLA file close routines raise this error if there was an error closing a file.

14.1.10 ex.FileWriteError (9)

The HLA Standard Library file output routines raise this exception if there is an error while attempting to write data to a file. This is usually a catastrophic error such as file I/O or some hardware error.

14.1.11 ex.FileReadError (10)

The HLA Standard Library file output routines raise this exception if there is an error while attempting to read data from a file. This is usually a catastrophic error such as file I/O or some hardware error.

14.1.12 ex.DiskFullError (11)

The HLA Standard Library raises this exception if you attempt to write data to a disk that is full.

14.1.13 ex.EndOfFile (12)

The HLA Standard Library file I/O routines raise this exception if you attempt to read data from a file after you've reached the end of file. Note that HLA does not raise this exception upon reaching the EOF. You must actually attempt to read beyond the end of the file. …

 
15 File Class (fileclass.hhf)

--------------------------------------------------------------------------------

The HLA Standard Library provides a file class that simplifies file I/O. The name of this class is file and you should declare all objects to be of this type or a pointer to this type, e.g.,

var
        MyOuputFile: file;
        filePtr:     pointer to file;


Once you declare a file variable, you access the remaining methods, procedures, and fields in the file class by specifying the file variable name and a period as a prefix to the field name.

Note: HLA also provides a fileio library module that does file I/O using traditional procedures rather than class objects. If you're more comfortable using such a programming paradigm, or you prefer your code to be a bit more efficient, you should use the fileio module.

Warning: Don't forget that HLA objects modify the values in the ESI and EDI registers whenever you call a class procedure, method, or iterator. Do not leave any important values in either of these register when making calls to the following routines. If the use of ESI and EDI is a problem for you, you might consider using the fileio module that does not suffer from this problem.

Note: Although the file class is convenient to use and provides some nice features to object-oriented programming, the way that the classes work pretty much means that you will be linking in the entire file class if you use only a single method from the class. If you're trying to write a small program, you should use the fileio module rather than the file class.

15.1 General File Operations

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

file.create; @returns( "esi" );  [for dynamic objects]


The file class provides a file.create constructor which you should always call before making use of a file variable. For file variables (as opposed to file pointer variables), you should call this routine specifying the name of the file variable. For file pointer variables, you should call this routine using the class name and store the pointer returned in EAX into your file variable. For example, to initialize the two files in the previous example, you would use code like the following:

Code: [Select]
    MyOutputFile.create();

    file.create();

    mov( eax, filePtr );


Note that the file.create constructor simply initializes the virtual method table pointer and does other necessary internal initialization. The constructor does not open a file or perform other file-related activities.


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

This method returns the file handle in the EAX register. The returned value is invalid if you have not opened the file. You can pass this handle value to any of the Standard Library file routines (e.g., fileio.putc) that expect a handle. You may also pass this value to Windows or Linux API functions that expect a file handle.


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

This method opens an existing file. The filename parameter is a string specifying the name of the file you wish to open. The access parameter is one of the following:

•  fileio.r

•  fileio.w

•  fileio.rw

•  fileio.a


The fileio.r constant tells filevar.open to open the file for read-only access. The fileio.w constant tells filevar.open to open the file for writing. Using the fileio.rw constant tells fileio.open to open the file for reading and writing. The fileio.a option tells the filevar.open function to open the file for writing and append all written data to the end of the file.

Before accessing the data in a file, you must open the file (which initializes the file handle). The filevar.open and filevar.openNew methods are excellent tools for this purpose. You may also open the file using direct calls to the Windows or Linux API, but you must initialize the filevar.fileHandle field of the class variable before making any other method calls in the file class.


filevar.openNew( filename:string )


This function opens a new file for writing (if the file already exists, it is first deleted and then a new file is opened for writing). The file is given the "normal" attribute.

Before accessing the data in a file, you must open the file (which initializes the file handle). The filevar.open and filevar.openNew methods are excellent tools for this purpose. You may also open the file using direct calls to the Windows or Linux API, but you must initialize the filevar.fileHandle field of the class variable before making any other method calls in the file class.

filevar.close;

This method closes a file opened via file.Open or file.OpenNew and flushes any buffered data to the disk.

« Last Edit: May 19, 2013, 09:43:53 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #16 on: May 17, 2013, 06:16:48 PM »
Now for a little educational fun to finish off this chapter ...

Code: [Select]
// 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;
« Last Edit: May 19, 2013, 10:30:38 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #17 on: May 17, 2013, 06:19:54 PM »
Chapter 15: The Computer updates (edits, deletes, sorts, chops-duplicates-in) its files

 
 
1.   Create (md) a sub-directory in the project folder.  Give it the name Chapt15_updateFile

2.   Go (cd) to that new directory.

3.   Using your text editor, create the following program file in that folder. 
        Give the first program file here the name addToContacts.hla

4.   Compile the program and run it. Study the comments that are in the HLA program,
        as well as the output that appears on the screen when you run the program.

5.   We will we then add functions and make changes …
   

Notice that we are again starting out with the same program with which we ended in the previous chapter. For a simple start … we will just add to it.  We will add another procedure, showMenu() … and make a few other changes … (see comments in program)
 
Code: [Select]
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;

 
 
Some things to note:

* The menu here is just a start … it will be expanded as we add more functions.

* We need to watch that the values of the registers outside a procedure call don’t get messed up inside a procedure call … (if we need to keep that register value for future use. The last program in this set will show one way to do that so you don’t have to keep a complete mental picture of all the registers you are using for each purpose.)
« Last Edit: May 19, 2013, 11:16:02 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #18 on: May 19, 2013, 10:53:51 AM »
Here is the next step ...



Code: [Select]
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;


We now have a small program working  and debugged and tested with some real data input to check the validity of the design …

Do we get the expected output with some extreme data? 

Then … we can add functions, (i.e. HLA procedures or macros), to that … and test again. 

Add … test.  Add … test …

That way, you can narrow down most of the source of any coding errors to the latest ‘chunk’ of code added.
« Last Edit: May 19, 2013, 11:33:31 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #19 on: May 19, 2013, 11:39:07 AM »
Here now, are several functions added ...

Each one was added, one at a time … and tested.

Some took a lot more time to get working … than others. 


See the comments and questions in the comments …

What happens when you run the program with various real data of you own?

Code: [Select]
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;


Have you been noticing how you can divide things up into smaller tasks and put them into HLA procedures or macros that you can test out and get working and then use again and again in your program or in other programs?
« Last Edit: May 19, 2013, 12:15:45 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #20 on: May 19, 2013, 12:16:39 PM »
This versions frees up dynamically allocated memory when it is finished being used.



Code: [Select]
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;



To be continued ...
« Last Edit: May 19, 2013, 03:04:46 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #21 on: May 19, 2013, 03:22:36 PM »
Chapter 16: The Computer … vast OS’s … and the garbage collects?
 
 
 
This chapter is ONE BIG RESEARCH PROJECT... firstly on operating systems ...  and on managing memory and other bits inside the ‘box’.
 
From the early day when there were NO OS’s …
 

1.   OS's have been growing bigger and very complex ...
2.   So what did OS's start out as ...
3.   What are some present BIG risks / benefits ... (because of this complexity?)
4.   What is their future ... if any?
5.   What do these mean: 'memory leaks' and 'garbage collection' and is there a need for real time computing still? (Give examples ... Try to google for these.)
6.   What is the relationship between the executable file that an assembler/compiler like HLA produces and an OS like Windows XP or LINUX?
7.   What do these signify:  direct memory access and protected memory? In the old MS DOS days, the OS did not forebide the programmer to poke values directly into memory. What are these dreaded fatal protected memory faults about anyways?
8.   Note that not all computers need to have disks, or screens, or keyboards, or mouses, etc ... Find examples of some very dedicated computing usage, including ones that may not even require an OS.



The following was taken from:

http://www.plantation-productions.com/Webster/Win32Asm/index.html

Win32 programs run in protected mode which is available since 80286. But 80286 is now history. So we only have to concern ourselves with 80386 and its descendants. Windows runs each Win32 program in separated virtual space. That means each Win32 program will have its own 4 GB address space. However, this doesn't mean every win32 program has 4GB of physical memory, only that the program can address any address in that range. Windows will do anything necessary to make the memory the program references valid. Of course, the program must adhere to the rules set by Windows, else it will cause the dreaded General Protection Fault. Each program is alone in its address space. This is in contrast to the situation in Win16. All Win16 programs can *see* each other. Not so under Win32. This feature helps reduce the chance of one program writing over other program's code/data.
Memory model is also drastically different from the old days ofthe 16-bit world. Under Win32, we need not be concerned with memory model or segments anymore! There's only one memory model: Flat memory model. There's no more 64K segments. The memory is a large continuous space of 4 GB. That also means you don't have to play with segment registers. You can use any segment register to address any point in the memory space. That's a GREAT help to programmers. This is what makes Win32 assembly programming as easy as C.
When you program under Win32, you must know some important rules. One such rule is that, Windows uses esi, edi, ebp and ebx internally and it doesn't expect the values in those registers to change. So remember this rule first: if you use any of those four registers in your callback function, don't ever forget to restore them before returning control to Windows. A callback function is your own function which is called by Windows. The obvious example is the windows procedure. This doesn't mean that you cannot use those four registers, you can. Just be sure to restore them back before passing control back to Windows.

Also, take a look at:
If you've ever been confronted by the dreaded Blue Screen Of Death, suffered random reboots or faced the frustration of inexplicable PC crashes, read on for some preventative measures...
http://www.soundonsound.com/sos/jan06/articles/pcmusician.htm?print=yes
http://www.extremetech.com/article2/0,1558,10430,00.asp


Here are some links to help get stated on this VERY BIG project:
 
http://courses.cs.vt.edu/~csonline/OS/Lessons/Introduction/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/Resources/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/Processes/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/Synchronization/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/Deadlock/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/MemoryAllocation/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/VirtualMemory/index.html
http://courses.cs.vt.edu/~csonline/OS/Lessons/FileManagement/index.htm
http://courses.cs.vt.edu/~csonline/OS/Lessons/Summary/index.html
   
Let's take a quick review of all that we have learned about operating systems in these lessons.
 
   1. In the introduction, we saw that software can be roughly divided into two groups: application software and system software. Operating systems are a type of system software that allow applications to interface with computer hardware. Four major categories of operating systems are batch, timesharing, personal computing, and dedicated.
 
   2. Resources are any objects that can be allocated within a system, and the operating system is responsible for managing them. Some resources such as primary memory can be space-multiplexed while other resources such as the CPU must be time-multiplexed.
 
   3. A process is an executing program. Since most computers allow multiple processes to execute simultaneously, the operating system must manage the order in which they execute. Three examples of process scheduling algorithms are First Come First Serve, Round Robin, and Shortest Process Next.
 
   4. Synchronization involves the orderly sharing of system resources by processes. The operating system must ensure that mutually exclusive access is granted to a processes using a critical resource. One way to do this is through the use of semaphores, software flags which indicate whether a critical resource is available.
 
   5. Three conditions are necessary for deadlock to occur in a system: (1) mutual exclusion, (2) hold-and-wait, and (3) no preemption. By attacking one of these three conditions, modern operating systems handle deadlock problems.
 
   6. Three common strategies for primary memory allocation are Best fit, Worst fit, and First fit.
 
   7. Virtual memory allows systems to execute programs which exceed the size of primary memory by dividing programs into sections called pages which can be loaded into sections of memory called page frames. Policies for determining which pages to load and remove from memory include Random Replacement, First In First Out, Second Chance, Least Recently Used, and Least Frequently Used.
 
   8. The file manager is responsible for maintaining secondary storage. Storage blocks in secondary storage can be managed using one of at least three methods: Contiguous Allocation, Linked Allocation, and Indexed Allocation.
« Last Edit: May 19, 2013, 03:37:38 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #22 on: May 19, 2013, 03:23:37 PM »
Chapter 16: The Computer … vast OS’s … and the garbage collects?  (continued ...)



http://www.mohanraj.info/josh.jsp
 
 
Operating System Design & Implementation Tutorial - JOSH
 
    * Introduction
    * Boot-strap basics
    * Considerations for the boot-strap loader
    * Designing and assembling the boot-loader
    * Rudimentary kernel
    * Interrupt Service
    * Shell - Overview
    * Shell - Implementation
    * Other chapters
 
Introduction
 
I was fascinated by computers right from the moment I got an opportunity to lay my hands on one. I learned BASIC programming in school when I was in grade eight. Those were the days of the 80286 with BASICA on MS-DOS. I went on to complete my degree in Medicine. After completion my interest in computers returned as I got the opportunity of developing a small database system for the hospital I was working. I went on to learn C++, Visual Basic, and ultimately Assembler. I always had a fantasy of developing my own operating system and started reading more and more about that. I ultimately set down to develop a small operating system, which will help me understand the PC better. I then realised the difficulties I underwent to get the needed help and so decided to write the OS as a tutorial as well. I am not sure if the OS will ultimately be completed. But I am sure the reader will be able to complete his/her own version as he/she works with it. Developing the system (which is still under process) was great fun and I am sure the reader will enjoy developing his/her own system.
Pre-requisites
 
A basic/working knowledge of Assembler with an unrelenting desire to explore and know more and to develop something new will be needed to start the project. My own knowledge of the PC is derived from books ('Under the IBM PC' by Peter Norton is an excellent text and I would suggest readers to get a copy of it and read it cover to cover) as a result of which the project will be very much limited in expertise. A copy of the excellent and free NASM (Netwide assembler) is needed for developing the system. You can download your free copy at NASM. All development is done on the Windows operating system. It is also possible to do the same under Unix/Linux system. Differences will be hinted whenever needed.
Design considerations
 
JOSH, as I named it to signify a sense of excitement/satisfaction, will be a real-mode operating system to make learning faster and easier (and my own understanding of the protected mode has only just started). Like MS-DOS it will be interrupt driven. JOSH will be a single tasking operating system. ...
 

 
 
http://64.233.167.104/search?q=cache:Yh9w1R0Q9QEJ:www.cs.vu.nl/~bal/ProposalWritingClass/minix.ps+a+tiny+operating+system&hl=en&ct=clnk&cd=35&gl=ca
   
1. Title: A Small Simple Secure Operating System
 
2. Summary
Current operating systems are very insecure. In theory they all have elaborate security features, but in reality, they are subject to all manner of attacks from hackers, viruses, worms, and so on. The core of the problem is that no current operating system was designed with security in mind. In addi- tion, they are all huge and monolithic, which means that they contain vast numbers of bugs and that each bug is capable of bringing down the entire system. In this research we propose to investigate a new class of operating systems designed from the ground up with security as the primary focus. Our approach is to start with a microkernel, a tiny piece of code that runs in kernel mode and forms the true operating system. This code is small enough that it can probably be made (almost) correct. The rest of the system runs as small protected modules in user mode. In addition, the proposed system is based on the principle of least authority, a well-known technique for containing damage due to software bugs or other causes. Specifically, the use of capabilities will be used to implement this
principle. We believe that by combining the concepts of microkernel and capabilities it should be possible to design an operating system far more secure than any existing one. One OiO is requested and an excellent candidate is available. The research also includes building a prototype to test the results, so a scientific programmer is also requested.
 
3. Description of the Proposed Research
Introduction
Current operating systems have extremely poor security. Practically every week one reads in the newspaper about another security hole in Windows. No sooner has Microsoft posted a patch to its website (which only a tiny fraction of Windows users download and install) than another hole turns up, sometimes in the patch itself. Attacks by viruses and worms are rampant. It is no understatement to say that Windows has very serious security issues.  Variants of UNIX such as Solaris, BSD (including MacOS X and Linux) are not in the press as much. In part it is because they are slightly more secure, but in reality because hackers tend to focus on hitting Windows, where they can get the most publicity for their attacks. If cars, television sets, and other appliances broke down as often as computer software, the public would not stand for it. With computers and especially operating systems, people seem to assume it is only natural that they do not work correctly. Nothing could be further from the truth.  Most of the security problems with current operating systems are due to a fundamental design flaw that they all have inherited from their ancestor operating systems of the 1960s. In the rest of this section we will discuss the nature of the flaw and our proposal for doing research into much more secure operating systems.
 
Why Are Computer Systems Insecure?
Most security problems ultimately come down to the fact that programmers are human beings and as such are not perfect. While no one disputes this statement, no current systems are designed to deal with the consequences of it. Various studies have shown the number of bugs in commercially available software ranges from about 1 bug per 1000 lines of code to 20 bugs per 1000 lines of code, with large and complex software systems having more bugs per 1000 lines of code due to the larger number of modules and their more complex interactions. Windows XP consists of 50 million lines of code. From this it follows that Windows XP has somewhere between 50,000 and 1 million bugs in it. Even if Microsoft’s quality control is the best is the business, there are probably in excess of 50,000 bugs in XP. In this light, it is not surprising that a few of these come to light every week and that Microsoft is constantly sticking its fingers in the software dike. Linux, which consists of a few million lines of code, probably has only a few tens of thousands of bugs. Whether open source software has more bugs than commercial software (due to less stringent quality control) or fewer bugs (due to more eyes looking at the source code) is a highly contentious subject. ...
 
 
 
http://www.personal.kent.edu/~rmuhamma/OpSystems/os.html
 
Operating Systems Lecture Notes
 
   1. Introduction
   2. History of Operating Systems
   3. Operating Systems Structure
          * System Component
          * Operating System Services
          * System Calls and System Programs
          * Layered Approach System Design
          * Mechanism and Policy
   4. Process
          * Definition of Process
          * Process State
          * Process Operations
          * Process Control Block
   5. Threads
   6. Solaris-2 Operating Systems
   7. CPU/Process Scheduling
   8. Schedule Algorithm
          * FCFS Scheduling
          * Round Robin Scheduling
          * SJF Scheduling
          * SRT Scheduling
          * Priority Scheduling
          * Multilevel Queue Scheduling
          * Multilevel Feedback Queue Scheduling
   9. Interprocess Communication
          * Critical Section
          * Mutual Exclusion
          * Achieving Mutual Exclusion
          * Semaphores
  10. Deadlock
          * Necessary and Sufficient Deadlock Conditions
          * Dealing with Deadlock Problem
                o Deadlock Prevention
                o Deadlock Avoidance
                o Deadlock Detection
  11. Absolutely Important UNIX Commands
  12. References
 
 
http://www.howstuffworks.com/operating-system.htm
 
Inside This Article
1. Introduction to How Operating Systems Work
2. The Bare Bones
3. What Does It Do?
4. What Kinds Are There?
5. Wake-Up Call
6. Processor Management
7. Memory Storage and Management
8. Device Management ...
 
If you have a computer, then you have heard about operating systems. Any desktop or laptop PC that you buy normally comes pre-loaded with Windows XP. Macintosh computers come pre-loaded with OS X. Many corporate servers use the Linux or UNIX operating systems. The operating system (OS) is the first thing loaded onto the computer -- without the operating system, a computer is useless.
 
More recently, operating systems have started to pop up in smaller computers as well. If you like to tinker with electronic devices, you are probably pleased that operating systems can now be found on many of the devices we use every day, from cell phones to wireless access points. The computers used in these little devices have gotten so powerful that they can now actually run an operating system and applications. The computer in a typical modern cell phone is now more powerful than a desktop computer from 20 years ago, so this progression makes sense and is a natural development. In any device that has an operating system, there's usually a way to make changes to how the device works. This is far from a happy accident; one of the reasons operating systems are made out of portable code rather than permanent physical circuits is so that they can be changed or modified without having to scrap the whole device.
 
For a desktop computer user, this means you can add a new security update, system patch, new application or often even a new operating system entirely rather than junk your computer and start again with a new one when you need to make a change. As long as you understand how an operating system works and know how to get at it, you can in many cases change some of the ways it behaves. And, it's as true of your cell phone as it is of your computer.
 
The purpose of an operating system is to organize and control hardware and software so that the device it lives in behaves in a flexible but predictable way. In this article, we'll tell you what a piece of software must do to be called an operating system, show you how the operating system in your desktop computer works and give you some examples of how to take control of the other operating systems around you.
 
 
NEXT ... (see above link)
 
 
   
http://en.wikipedia.org/wiki/Disk_Operating_System
 
Disk Operating System (specifically) and disk operating system (generically), most often abbreviated as DOS (not to be confused with the DOS family of disk operating systems for the IBM PC compatible platform), refer to operating system software used in most computers that provides the abstraction and management of secondary storage devices and the information on them (e.g., file systems for organizing files of all sorts). Such software is referred to as a disk operating system when the storage devices it manages are made of rotating platters (such as hard disks or floppy disks).
 
In the early days of microcomputing, memory space was often limited, so the disk operating system was an extension of the operating system. This component was only loaded if needed. Otherwise, disk-access would be limited to low-level operations such as reading and writing disks at the sector-level.
 
In some cases, the disk operating system component (or even the operating system) was known as DOS.
 
Sometimes, a disk operating system can refer to the entire operating system if it is loaded off a disk and supports the abstraction and management of disk devices. Examples include DOS/360 and FreeDOS. On the PC compatible platform, an entire family of operating systems was called DOS.
 
History
 
In the early days of computers, there were no disk drives; delay lines, punched cards, paper tape, magnetic tape, magnetic drums, were used instead. And in the early days of microcomputers, paper tape or audio cassette tape (see Kansas City standard) or nothing were used instead. In the latter case, program and data entry was done at front panel switches directly into memory or through a computer terminal / keyboard, sometimes controlled by a ROM BASIC interpreter; when power was turned off after running the program, the information so entered vanished.
 
Both hard disks and floppy disk drives require software to manage rapid access to block storage of sequential and other data. When microcomputers rarely had expensive disk drives of any kind, the necessity to have software to manage such devices (ie, the 'disk's) carried much status. To have one or the other was a mark of distinction and prestige, and so was having the Disk sort of an Operating System. As prices for both disk hardware and operating system software decreased, there were many such microcomputer systems.
 
 
http://www.ft.com/cms/s/0/9dba9ba2-5a3b-11dc-9bcd-0000779fd2ac.html
 
Chinese military hacked into Pentagon
 
By Demetri Sevastopulo in Washington
 
Published: September 3 2007 19:00 | Last updated: September 3 2007 20:53
 
The Chinese military hacked into a Pentagon computer network in June in the most successful cyber attack on the US defence department, say American ­officials.
 
The Pentagon acknowledged shutting down part of a computer system serving the office of Robert Gates, defence secretary, but declined to say who it believed was behind the attack.
 
Current and former officials have told the Financial Times an internal investigation has revealed that the incursion came from the People’s Liberation Army.
 
One senior US official said the Pentagon had pinpointed the exact origins of the attack. Another person familiar with the event said there was a “very high level of confidence...trending towards total certainty” that the PLA was responsible. The defence ministry in Beijing declined to comment on Monday.
 
Angela Merkel, Germany’s chancellor, raised reports of Chinese infiltration of German government computers with Wen Jiabao, China’s premier, in a visit to Beijing, after which the Chinese foreign ministry said the government opposed and forbade “any criminal acts undermining computer systems, including hacking”. ...
 
 
 
http://www.computerworld.com/governmenttopics/government/story/0,10801,78935,00.html
 
China next to get access to Microsoft source code
Joris Evers, IDG News Service 
 
February 28, 2003 (IDG News Service) -- China has signed up to participate in Microsoft Corp.'s new Government Security Program (GSP), giving the Chinese government access to the source code of Windows operating system software, Microsoft said in a statement today.
The agreement was signed by Microsoft and the China Information Technology Security Certification Center. Chinese President Jiang Zemin was briefed on the GSP today by Microsoft Chairman and Chief Software Architect Bill Gates, who is visiting China, Microsoft said.
The company announced its GSP initiative last month in a bid to allay concerns about the security of its software (see story). The GSP is open to governments and international organizations. GSP participants receive access to the source code and technical information of several versions of Windows software needed to conduct security reviews of the products, the software maker said.
Russia, the U.K. and the North Atlantic Treaty Organization have already signed up to participate in the GSP, and Microsoft is in talks with more than 30 countries, territories and organizations about the program.
Governments signing up for the security program will be able to build systems that offer the high levels of security required for national security, Microsoft has said. However, government users won't be allowed to make modifications to the code or compile the source code into Windows programs themselves, according to Microsoft.
The GSP is also seen as a move in Microsoft's battle against open-source software, primarily the Linux operating system, edging into government administration all around the globe. An open-source license allows users to access and modify the source code. Government users in Finland, Germany, France, Taiwan and the Philippines, among other countries, have already adopted open-source software or are looking into doing so.
Participation in the GSP is free. The program covers current versions, service packs and beta releases of Windows 2000, Windows XP, Windows Server 2003 and Windows CE. In addition, government IT professionals can visit Microsoft headquarters to review Windows development and meet with Microsoft staff. Online access to the source code is secured by smart cards.
Microsoft sees the GSP as an important part of its Trustworthy Computing initiative, an effort announced by Gates last year to improve the security of its software.
Microsoft shares Windows code with governments, companies and educational institutions under various programs that are part of its Shared Source Initiative announced in 2001. The GSP is different in that Microsoft sees it as a partnership with a country or international organization and that it does not require a country or organization to be a Microsoft customer in order to participate. ...

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #23 on: May 19, 2013, 03:46:04 PM »
Chapter 17: The Computer delivers daily Manna ...



Here is a fun little project that uses a similar record structure to our previous readFile in Chapter 14.  We will need to find today's date and the dayNumber in the year ... January 1 is day 1.  We can add more records to our file dailyManna.txt if we like. (We will need to keep the same record structure, though.)

We start off by reading the records in dailyManna.txt into an array of records set to a maxDays of 366 ... to allow for leap years.  Recall, this is an array of pointers. (HLA strings are pointers.) So ... it only needs 4 x 2 x 366 bytes of memory at compile time. Later, as we read in the strings from the file dailyManna.txt, we will allocate sufficient memory for each string using fileio.a_gets( inFileHandle );  // which returns the address to each new string in eax

We also will count the records so we will know how may we have ... and also, so we do not exceed the maxDays size of the array.

We will use modular division to find the offset into the array for todays date.

Take a look and see if you can figure out what does what.  You can google on unknown words like this example of key words: HLA day number in year. 

Oh yes, the program uses two data files: dailyManna.txt and PSmessage.hhf.   (Links to these two files follow.)

These need to be in the same folder as the getManna.hla file. 

Link to file to dailyManna.txt to download ...

http://www.4shared.com/office/7hQJKozZ/dailyManna.html

Link to file to PSmessage.hhf to download ...

http://www.4shared.com/file/rSSo6-Z4/PSmessage.html


When you are ready to compile/assemble the getManna.hla file use this command:  hla -w getManna
.

Yes ... this is a Windows example!  Cool French too ... eh!


Now let's get some rich nourishment:

Code: [Select]
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;


link to file to dailyManna.txt to download ...

http://www.4shared.com/office/7hQJKozZ/dailyManna.html

link to file to PSmessage.hhf to download ...

http://www.4shared.com/file/rSSo6-Z4/PSmessage.html

« Last Edit: May 19, 2013, 05:29:57 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #24 on: May 19, 2013, 04:18:54 PM »
Code: [Select]
// -------- 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 -------------------------- //

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #25 on: May 19, 2013, 05:39:17 PM »
Chapter 18: OOP ... goes the computer



1.   Create (md) a sub-directory in the project folder. Give it the name Chapt18_OOP
2.   Go (cd) to that new directory.
3.   Using your text editor, create the following program file in that folder.
      Give the program file the name distanceAndPoints.hla
4.   Compile the program and run it. Study the comments that are in the hla program,
      as well as the output that appears on the screen when you run the program.

You may want to review Chapter 11: The Computer does some Algebra with real numbers ...
that introduces the FPU and real numbers ... up to Chapter 15, and especially Chapter 13:
The Computer files its Records before you dig into the following.

We are about to venture into some very useful stuff that will also help your math insights and skills.

Recall that a straight line is the shortest distance between two points.  If you were to plot, on some graph paper, two points in Euclidean 2 space, calling them A and B with co-ordinates (x1, y1) and (x2, y2) respectively, then the distance between A and B is given by the formula sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ).

This is sometimes abbreviated as sqrt( dX*dX + dY*dY ) …
or spelled out in words …
the square_root_of ( (the difference in the x’s squared) + (the difference in the y’s squared) ).

Our first little program will do just this. We will use real numbers, (HLA type real32’s), for each value of x and y.

You may recall that a real number is just a positive or negative or zero decimal number. 

This includes all fractions, (rational numbers) ...
and all numbers (irrational) like the square root of 2 and PI that never have a repeating pattern to their decimal part …

Thus, we always just approximate reals, (i.e. just the irrational reals), to some number of decimal places.

You may have seen somewhere that 100.001 can be represented by 1.00001e2.

This means the same thing as 1.00001 * 100 which we know is equal to 100.001.

The HLA default output for real numbers is in a format like 1.00001e2.


So let’s dig in.  Don’t be afraid to make changes and see the output.  You can un-comment the line that I commented out … to verify that the output there is what is expected.

Code: [Select]
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;



If you entered 2, 3 and 0, 0  or the other way around ... either way you should have the answer ...

The distance is:  3.605551242e+0

If you entered  0, 0 and  3, -4  or the other way around ... either way you should have the answer ...

The distance is:  5.000000000e+0


So what does this have to do with OOP?  Well, we have just defined an OBJECT, a point ... and described one property of points ... they have a distance between them.  With Objected Oriented Programming, we will keep together, i.e. encapsulate, all the data and all the functions that relate to some object.

Another way that we can think of a point object is ... as a vector.  We can imagine the vector, for now, as having its tail at the origin, i.e. at (0, 0) ... and its head at the point (x, y).  A vector object can also be described by an unique length and an unique direction angle (in the range 0..365.999999999999... degrees) from some line, say the x-axis, (or, -179.9999999999999... to +180 degrees.)

If we have some point or vector then, represented by (x, y) ... we can think of it

●   as the 2 real numbers of the co-ordinates x and y ...

OR ...

●   as a vector with length = sqrt( x*x + y*y ) and direction angle theta = arctan( y/x ).

NOTE!  Either way, TWO unique real numbers are sufficient to describe this OBJECT!  ( i.e a 2-Dimensional vector - has just TWO DIMENSIONS! )

For this example, we will choose the two real numbers that uniquely describe our point, the head of the vector.  And we will also associate functions with this encapsulation of information, concerning this point/vector object ...  i.e.procedures and methods that readily let us calculate / access or get and report this info ... or that set / reset / change / manipulate the numbers that are the data (point) of this object.  To encapsulate all this ... we (will eventually) use a CLASS and give that class a class name.  And all members of that class will use that class name ... similar to what we did with the data inside a record.  Recall that we accessed parts of a record with the dot operator. ... We will likewise use this same dot operator to get to the members of the Class Object ... whether they be data ... or functions that act on that data.

You may not have heard yet ... that the tangent or tan of some angle is just the rise over the run in the right angled triangle described by the points (0, 0), (x, 0),  and ( x, y) ... In this case ... y/x.

And if we know x and y, we can calculate y/x = tan( theta ).  Theta is the angle measured cc (counterclockwise) from the x-axis to the point ( x, y).

Once we know the value of tan( theta ) ... we can look up that value, on a graph or in tables, or use the inverse tan of that value, (same thing as arctan), to find theta.

We will get the FPU unit to do our conversions for us.  If we feed into the FPU function fpatan() ... y and x ... the function will find y/x and then the inverse tan (i.e. the arctan) of the fraction ... i.e. fpatan() outputs theta if we input y and then x ... IT IS IN RADIANS so we will multiply by 360/( 2*pi ) to convert to degrees, where the degrees are counted cc from the positive X-axis. (Recall that in one complete revolution of 360 degrees there are 2pi radians. And ... for a circle with radius1, the circumference is 2*r*pi  = 2*1*pi  = 2*pi ).

See below for the rest of  program vectors2;
Code: [Select]
   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 );

So here ... where we know y, the rise ... and x, the run ...  then the rise/run = y/x = tan( theta ). Thus we find the direction angle for our vector by plugging this into the formula ...  theta = arctan( y/x )

But if we instead knew the direction angle theta and the length of some vector ... we could then find the unique(x, y) that represents the head of that vector, if we place its tail at the origin.  (We  would use x = lengthOfVector * cos( theta )  and  y = lengthOfVector* sin( theta );  Note that sin(theta)/cos(theta) = tan(theta) = y/x for all theta.)

For further quick reference see:

http://answermath.com/sin.htm

 http://mathworld.wolfram.com/x-Axis.html

http://www.physics.uoguelph.ca/tutorials/vectors/vectors.html
« Last Edit: May 19, 2013, 06:22:15 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #26 on: May 19, 2013, 06:28:55 PM »
Try these next three programs out for size ...



Don't forget to see the comments in the programs. 

We will encapsulate these into an OOP Vector Class ... right after these.

Code: [Select]
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;



Code: [Select]
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;



Code: [Select]
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;
« Last Edit: May 23, 2013, 07:12:06 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #27 on: May 21, 2013, 11:14:24 AM »
Chapter 19: OOP 2



1.   Create (md) a sub-directory in the project folder. Give it the name Chapt19_OOP2
2.   Go (cd) to that new directory.
3.   Using your text editor, create the following program file in that folder. Give the program file the name vectorClass1.hla
4.   Compile the program and run it. Study the comments that are in the hla program, as well as the output that appears
      on the screen when you run the program.

 
Our first CLASS ACT ...

and a lot of behind the scenes work ...

to get the (little) facility (here) we desire ...

but then we have gained considerable power for the next class act to use as a spring board ... and so on ... 

i.e. if we have designed our class a little classy, thinking ahead about future objects that might build on this class?

Code: [Select]
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;


We commonly divide the above program up into three parts or modules, each in its own file:

( For simplicity here, we may put all 3 of these new files into our working directory ...
  so the compiler will know where to find them when it is compiling.)

1.   Our main program file, (let's rename it as test_vectorClass.hla). It will also have this line near its top ...
      #include( "vectorClass.hhf" )
2.   The header file itself, that includes any header files, any constants, macros, etc. that the class will use ...
       and declares the class prototypes ... we'll call it vectorClass.hhf
3.   The class definition itself, (let's name this file vectorClass.hla).  This file will ALSO have a line like this near its top:
       #include( "vectorClass.hhf" )

●   And we can THEN compile/assemble these files into one exeucutable file with this command line command:
     HLA   test_vectorClass   vectorClass
●   If all goes well, we will have the desired executable file with the name test_vectorClass.exe in the working folder.
     ( Also ... the files test_vectorClass.obj and vectorClass.obj )
●   If we make any changes to our test file, (or make a new program to use our vectorClass), but ...
     have not made any changes to our vectorClass, we don't need to assemble the vectorClass.hla file again into an obj file.
●   This is how we would make our new executable, if we just changed our test program and so need to re-compile it:
     HLA   test_vectorClass   vectorClass.obj
●   Note the ending of obj on the last file above.  It hasn't changed ... so HLA can just link it in ...
     in the new executable of the new test file.

Well ... there are a few more fine details also needed ... like adding @external; after all the procedure / method prototypes in the header file ...
« Last Edit: May 24, 2013, 06:51:53 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #28 on: May 23, 2013, 06:58:08 AM »
Chapter 19: OOP 2 ... Continued ...  So here are these 3 FILES:



1. // File name is: test_vectorClass.hla //

Code: [Select]
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;



2. // File name is: vectorClass.hhf //

Code: [Select]
#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;


3. // file name is: vectorClass.hla //

Code: [Select]
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;


Remember to compile/assemble like this (on the command line):

HLA    test_vectorClass   vectorClass

That way, the unit file gets compiled to an obj file and then both of the new .obj files ... test_vectorClass.obj   and   vectorClass.obj ... are liked together into the one executable file test_vectorClass.exe

Now we have three modules ... the main program file, the Class header file, and the Class definition file.  If we were to add another class to our program, we would then probably add two more files ... say ... otherClass.hhf ... otherClass.hla.  Of course, we would then have to #include( "otherClass.hhf" ) in our main program along with any other changes required TO USE this 2nd class. (Remember to also #include( "otherClass.hhf" ) inside and near the top of otherClass.hla.)

If there were no changes to our 1st class definition ... we could use a command line command to compile the project like this: (If there were, we could just add it to the top line,  and remove the -c ... to produce the exe file all in one step. )

HLA   -c   test_vectorClass   otherClass

Followed by this

HLA   test_vectorClass.obj   vectorClass.obj   otherClass.obj

Get the drift ...?


P.S.

You may find a batch file like the following to be useful ...


@echo off
rem to compile one hla file ...
SET fname=
SET /P fname=Enter the name of the file to be compiled/run:
rem hla -w %fname%
hla %fname%
dir %fname%.exe
pause
del %fname%.obj
del %fname%.link
%fname%
pause


or ... to link in an UNIT project FILE with a main FILE ...


@echo off
rem to compile and link a main program with a project unit file (see below)
SET fname=
SET fname2=
SET /P fname=Enter the name of the main file to be compiled/run:
SET /P fname2=Enter the name of the project file to be linked/run:

rem hla -w %fname%

hla %fname% %fname2%
dir %fname%.exe
pause

del %fname%.obj
del %fname2%.obj
del %fname%.link

%fname%
pause
« Last Edit: May 24, 2013, 07:04:03 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Re: BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)
« Reply #29 on: May 24, 2013, 07:38:03 PM »
Chapter 20 ... Moving OOP with HLA   ... a CLASS ACT !



Now ... for a little advanced project ...

You may like to convert the HLA code at this next link to an HLA class act :)

http://developers-heaven.net/forum/index.php?topic=2599.msg2966#msg2966


Viva la HLA !


But even more ...


Baruch HaShem !!!


Shalom shalom,


David Wayne Zavitz

2013-05-24   
(My mom was born 105 years ago today.  Dear Father in Heaven, thank YOU SO MUCH for such a wonderful dad and mom and family.)




P.S.


For your ease of ref...


2013-05-24

Hi All,

Just realized (via a few student e-mails) that my old Google Doc's were no longer easily accessible to public ... so updated some and copied everything over to a new hosting site (New site has much better format to show and for students to easily copy/paste example programs)


BEGINNING COMPUTER PROGRAMMING WITH HLA (High Level Assembly)

http://developers-heaven.net/forum/index.php?topic=2607.0



BEGINNING COMPUTER PROGRAMMING (using HLA and Python 3.1 and C++) 

http://developers-heaven.net/forum/index.php?topic=46.0



Six Fast Steps to Programming in High Level Assembly (HLA)

http://developers-heaven.net/forum/index.php?topic=2599.0


Everything HLA ...

http://www.plantation-productions.com/Webster/


HLA CLASSES ...

http://www.plantation-productions.com/Webster/www.artofasm.com/Windows/HTML/ClassesAndObjects.html#998258

http://www.plantation-productions.com/Webster/www.artofasm.com/index.html


Or see ...

http://www.plantation-productions.com/Webster/
« Last Edit: May 24, 2013, 08:02:23 PM by David »