HLA vector and list function library ... also readLine and readWord ...

Started by David, August 16, 2012, 08:48:57 PM

Previous topic - Next topic

David

This space is designated to hold a library of HLA vector and HLA list functions ... similar to the Cvec/Cvec2 and Clist/Clist2 library of functions available here...

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


For some example programs that demo the use of these HLA vector and list containers with their push_back, (and list push_front, and vector reserve) ... and merge sort, insert sort, find, erase, and unique functions ... see this next link ...

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


The first set of vector and list functions, permit just one type of vector or list in a program.  These pairs of include files are named:


The first pair is:

"vector_func's.hhf"
"vector.hhf"

If you include file "vector_func's.hhf" to get access to the sort, etc... functions, that file FIRSTLY includes file "vector.hhf", which firstly includes file "stdlib.hhf"




The next pair is:

"sllist_func's.hhf"
"sllist.hhf"


If you include file "sllist_func's.hhf" to get access to the sort, etc... functions, that file FIRSTLY includes file "sllist.hhf", which firstly includes file "stdlib.hhf"



Note: see the vector2/sllist2 .hhf files at the bottom also ... linked here ...

http://developers-heaven.net/forum/index.php?topic=2600.msg2978#msg2978

David

The vector pair:

"vector.hhf"

// vector.hhf // 2012-08-21 //


#includeOnce( "stdlib.hhf" )


// NB!!! the record Rec must be defined before including this file
// ALSO  the type pRec: pointer to Rec;

// ALSO ... the procedure freeRec( pr: pRec );
/* // for example ... //
    procedure freeRec( pr: pRec in eax ); @nodisplay;
    begin freeRec;
        free( (type Rec [eax]).str );
    end freeRec;
*/

? @nodisplay:= true; // All procedures have @nodisplay 'as default' now.


#if( !@defined( InitCap ) )
const
    InitCap: uns32 := 8;
#endif

type   
    Vec:    record
                ary:        pRec;
                capacity:   uns32;
                size:       uns32;   
            endrecord;
 
 
procedure initVec( var v: Vec );
begin initVec;
    push( ebx );
    mov( v, ebx );
    mov( 0, (type Vec [ebx]).ary );
    mov( 0, (type Vec [ebx]).capacity );
    mov( 0, (type Vec [ebx]).size );
    pop( ebx );
end initVec;


// double capacity ...
procedure enlargeVec( var v: Vec );
begin enlargeVec;
    push( eax ); push( ebx ); push( ecx );

    mov( v, ebx );   
    if( (type Vec [ebx]).capacity != 0 ) then   // i.e. if capacity not zero
        shl( 1, (type Vec [ebx]).capacity );    // double capacity
    else;  // set initial capacity
        mov( mov( InitCap, eax ), (type Vec [ebx]).capacity );   
    endif;
   
    //mov( (type Vec [ebx]).elen, ecx );
    intmul( @size(Rec), (type Vec [ebx]).capacity, ecx );

    if( (type dword (type Vec [ebx]).ary) ) then mem.realloc( (type Vec [ebx]).ary, ecx );
    else mem.alloc( ecx );
    endif;
   
    mov( eax, (type Vec [ebx]).ary );

    pop( ecx ); pop( ebx ); pop( eax );
end enlargeVec;


// if size == capacity then enlarge ...
procedure push_backVec( var v: Vec; var r: Rec );
begin push_backVec;
    //push( ebx ); push( ecx ); push( esi ); push( edi ); // caller must preserve if needed
   
    // if size == capacity then enlarge/double capacity ...
    mov( v, ebx );
    mov( (type Vec [ebx]).size, ecx );
    if( ecx == (type Vec [ebx]).capacity ) then
        enlargeVec( (type Vec [ebx]) );
    endif;
   
   
    // ok ... now append (copy) of new rec ...
   
    mov( r, esi ); // get address of new Rec into esi ...
    //mov( (type Vec [ebx]).size, edi );
    intmul( @size(Rec), (type Vec [ebx]).size, edi );
    add( (type Vec [ebx]).ary, edi ); // get offset into edi

    // get num bytes to copy in ecx ...
    mov( @size(Rec), ecx );
    rep.movsb();
   
    inc( (type Vec [ebx]).size ); // update size
 
    //pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end push_backVec;


procedure reserveVec( var v: Vec; newCap: uns32  );
begin reserveVec;
    push( eax ); push( ebx );

    mov( newCap, eax );
    mov( v, ebx );
    if(  eax > (type Vec [ebx]).capacity ) then
   
        mov( eax, (type Vec [ebx]).capacity );
        intmul( @size(Rec), eax );          // num new bytes in eax
        if( (type dword (type Vec [ebx]).ary) ) then
            mem.realloc( (type Vec [ebx]).ary, eax );
        else
            mem.alloc( eax );
        endif;
        mov( eax, (type Vec [ebx]).ary );   // update new ary pointer
       
    endif;

    pop( ebx ); pop( eax );
end reserveVec;


procedure clearVec( var v: Vec );
begin clearVec;
    push( eax ); push( ebx ); push( ecx );
   
    mov( v, ebx );
    mov( (type Vec[ebx]).size, ecx );
    for( dec( ecx ); (type int32 ecx) >= 0; dec( ecx ) ) do
        intmul( @size(Rec), ecx, eax );
        add( (type Vec [ebx]).ary, eax );
        freeRec( eax ); // recall this procedure 'freeRec' must be defined
                        // along with type Rec, BEFORE including this file
    endfor;
   
    mem.free( (type Vec [ebx]).ary );
   
    initVec( (type Vec [ebx]) );
 
    pop( ecx ); pop( ebx ); pop( eax );
end clearVec;

David

"vector_func's.hhf"

// vector_func's.hhf // 2012-08-21 //

#includeOnce ( "vector.hhf" )


type
    pVec: pointer to Vec;


#if( !@defined( pMyCmp ) )
static
    pMyCmp: procedure( a: dword in esi; b: dword in edi ); @returns( "eax" );
#endif

 

// re. isort ... note: cmp func is a < b
// re. msort ... note: cmp func is a <= b

procedure isortVec( var v: Vec );
static
    rcmp:   Rec;
    tmpReg: dword;
begin isortVec;
    push( eax ); push( ebx ); push( ecx );  push( edx ); push( esi ); push( edi );
   
     //int i, j; // i in eax, j in edx
    // start with an array of just the first 2 elements (if exists) ...
    // for( i = 1; i < cv->size; ++i )
    cld();
    mov( v, ebx );
    for( mov( 1, eax ); eax < (type Vec [ebx]).size; inc( eax ) ) do
   
        // update this cmp Rec on each outer loop
        //memcpy( &cmp, &cv->ary[i], sizeof(Rec) );
        mov( &rcmp, edi );
        intmul( @size(Rec), eax, esi );
        add( (type Vec [ebx]).ary, esi );
        mov( @size(Rec), ecx ); // get # bytes to copy in a Rec ... into ecx
        rep.movsb(); // ok ... got copy into rcmp ...

        // get index of element just to the left of the above 'cmp' to start comparisons
        mov( eax, edx );    // j = i
        dec( edx );         // --j // i.e. j = i-1; //
        while( (type int32 edx) >= 0 ) do   // while( j >= 0 && myCmp(&cmp, &cv->ary[j]) < 0 )
       
            intmul( @size(Rec), edx, edi ); // get offset into edi ..
            add( (type Vec [ebx]).ary, edi );
            mov( &rcmp, esi );
       
            //breakif( !pMyCmp(esi, edi) ); // 'copy up' // but first ...
            mov( eax, tmpReg ); // get copy of loop counter in eax
            pMyCmp( esi, edi );
            if( eax ) then
                mov( tmpReg, eax );
            else
                mov( tmpReg, eax );
                break;
            endif;
           
            // Now ...  'copy up' ...
            //memcpy( &cv->ary[j+1], &cv->ary[j], sizeof(Rec) );
            intmul( @size(Rec), edx, esi );
            add( (type Vec [ebx]).ary, esi );
            mov( esi, edi );
            add( @size(Rec), edi ); // to inc( j ) add size  //
            mov( @size(Rec), ecx ); // get # bytes to copy in a Rec into ecx
            rep.movsb();       
       
            dec( edx ); //--j; // --j to prepare for next inner loop
           
        endwhile;
       
        // insert pointer at index j+1 (since j was decremented above ...
        //memcpy( &cv->ary[j+1], &cmp, sizeof(Rec) );
        inc( edx ); // ++j
        mov( &rcmp, esi );
        intmul( @size(Rec), edx, edi );
        add( (type Vec [ebx]).ary, edi );
        mov( @size(Rec), ecx ); // get # bytes to copy in a Rec into ecx
        rep.movsb();
 
    endfor;
   
    pop( edi ); pop( esi ); pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end isortVec; // size hasn't changed ...



////////////////////////////////////////////////////////////////////////////
// re. msort ... note: cmp func is a <= b

procedure mergeVec( pv: pVec in ebx; bot: int32; top: int32; ptmp: pVec in edx );
var
    mid:    int32; // set to (bot+top)/2
    sz:     int32; // set to top-bot+1    // size of the range to be merged
    j:      int32;
    tmpReg: dword;
begin mergeVec;
    //push( eax ); push( ecx ); push( esi ); push( edi );
   
    //int mid = (bot+top)/2;
    mov( top, eax );
    add( bot, eax );
    shr( 1, eax ); // divide by 2
    mov( eax, mid );
    //int sz = top - bot + 1;   // size of the range to be merged
    mov( top, eax );
    sub( bot, eax );
    inc( eax );
    mov( eax, sz );   
    mov( 0, j );        // int j = 0;        // next open index in tmp
    mov( bot, eax );    // int h1 = bot;     // next index to consider in 1st half
    mov( mid, ecx );    // int h2 = mid + 1; // next index to consider in 2nd half
    inc( ecx );
   
    // while indexes h1 and h2 are not past their ends ...
    cld();
    while( eax <= mid && ecx <= top ) do // while( h1 <= mid && h2 <= top )
       
        // move the smaller element into the tmp vec 'next' ...
        // if( myCmp( &(cv->ary[h1]), &(cv->ary[h2]) ) <= 0  )
        intmul( @size(Rec), eax, esi );
        add( (type Vec [ebx]).ary, esi );
       
        intmul( @size(Rec), ecx, edi );
        add( (type Vec [ebx]).ary, edi );
       
        mov( eax, tmpReg );
        pMyCmp( esi, edi ); // if( myCmp(...) <= 0  )
       
        if( eax ) then // if( myCmp(...) <= 0 )
            //tmp->ary[j++] = cv->ary[h1++];
            mov( tmpReg, eax );                     // restore eax
            intmul( @size(Rec), eax, esi );
            add( (type Vec [ebx]).ary, esi );

            intmul( @size(Rec), j, edi );
            add( (type Vec [edx]).ary, edi );

            mov( ecx, tmpReg );
            mov( @size(Rec), ecx );
            rep.movsb(); // tmp->ary[j++] = cv->ary[h1++];
            mov( tmpReg, ecx );

            inc( eax ); // ++h1
            inc( j );

        else
            //tmp->ary[j++] = cv->ary[h2++];
            mov( tmpReg, eax );                     // restore eax

            intmul( @size(Rec), ecx, esi );         // ecx holds h2
            add( (type Vec [ebx]).ary, esi );

            intmul( @size(Rec), j, edi );
            add( (type Vec [edx]).ary, edi );

            mov( ecx, tmpReg );
            mov( @size(Rec), ecx );
            rep.movsb(); // tmp->ary[j++] = cv->ary[h2++];
            mov( tmpReg, ecx );

            inc( ecx ); // ++h2
            inc( j );
           
        endif;

    endwhile;

   
    // Note: only one (at most) of the two 'while's' below is executed ...
   
    //while( h1 <= mid ) // copy any remaining entries (1st half)
        //tmp->ary[j++] = cv->ary[h1++];
    while( eax <= mid ) do
       
        intmul( @size(Rec), eax, esi );
        add( (type Vec [ebx]).ary, esi );

        intmul( @size(Rec), j, edi );
        add( (type Vec [edx]).ary, edi );

        mov( ecx, tmpReg );
        mov( @size(Rec), ecx );
        rep.movsb(); // tmp->ary[j++] = cv->ary[h1++];
        mov( tmpReg, ecx );

        inc( eax ); // ++h1
        inc( j );
   
    endwhile;
           
         
    //while( h2 <= top ) // copy any remaining entries (2nd half)
        //tmp->ary[j++] = cv->ary[h2++];
       
    while( ecx <= top ) do 
     
        intmul( @size(Rec), ecx, esi );
        add( (type Vec [ebx]).ary, esi );

        intmul( @size(Rec), j, edi );
        add( (type Vec [edx]).ary, edi );

        mov( ecx, tmpReg );
        mov( @size(Rec), ecx );
        rep.movsb(); // tmp->ary[j++] = cv->ary[h2++];
        mov( tmpReg, ecx );

        inc( ecx ); // ++h2
        inc( j );
       
    endwhile;
         

    //for( j = 0; j < sz; ++j ) // copy back sorted tmp vec...
        //cv->ary[bot+j] = tmp->ary[j];
       
    mov( (type Vec [edx]).ary, esi );
    //mov( bot, edi );
    intmul( @size(Rec), bot, edi );
    add( (type Vec [ebx]).ary, edi );
    //mov( sz, ecx );
    intmul( @size(Rec), sz, ecx );
    rep.movsb();
   
    /*
    for( mov( 0, ecx ); ecx < sz; inc( ecx ) ) do

        intmul( @size(Rec), ecx, esi );
        add( (type Vec [edx]).ary, esi );
       
        mov( bot, eax );
        add( ecx, eax );
        intmul( @size(Rec), eax, edi );
        add( (type Vec [ebx]).ary, edi );
       
        mov( ecx, tmpReg );
        mov( @size(Rec), ecx );
        rep.movsb(); // cv->ary[bot+j] = tmp->ary[j];
        mov( tmpReg, ecx );
   
    endfor;
    */

    //pop( edi ); pop( esi ); pop( ecx ); pop( eax );
end mergeVec;


procedure my_msortVec( pv: pVec in ebx; bot: int32; top: int32; ptmp: pVec in edx );
var
    mid:    int32; 
begin my_msortVec;
    mov( top, eax );
    if( bot < eax ) then
   
        add( bot, eax );    // NOW EAX HOLDS top + bot
        shr( 1, eax );      // divide by 2 ...
        mov( eax, mid );    // mid = ( bot + top ) / 2;
       
        my_msortVec( ebx, bot, mid, edx );  // sort the first half ...
        inc( mid );                         // now mid = mid +1
       
        my_msortVec( ebx, mid, top, edx );  // and the second half
       
        mergeVec( ebx, bot, top, edx );     // now merge 2 sorted chunks

    endif;
end my_msortVec;


procedure msortVec( var v: Vec );
var
    Rega:   dword;
    Regb:   dword;
    Regc:   dword;
    Regd:   dword;
    Regesi: dword;
    Regedi: dword;
    tmp:    Vec;
begin msortVec;
    mov( eax, Rega ); mov( ebx, Regb ); mov( ecx, Regc ); mov( edx, Regd );
    mov( esi, Regesi ); mov( edi, Regedi );
   
    mov( v, ebx );
    mov( (type Vec [ebx]).size, eax );
    if( eax > 1 ) then
   
        initVec( tmp );
        reserveVec( tmp, eax ); // Note: resets cap... BUT tmp size still 0
        dec( eax );
        lea( edx, tmp );        // get address of new tmp Vec into edx ...
        my_msortVec( ebx, 0, eax, edx );
       
        // NOW done ... BUT only free ary mem that held copies of pointers
        mem.free( tmp.ary );
       
    endif;
   
    mov( Regedi, edi ); mov( Regesi, esi );
    mov( Regd, edx ); mov( Regc, ecx ); mov( Regb, ebx ); mov( Rega, eax );
   
end msortVec;



// returns index if string present, else -1 ...
procedure findVec( var v: Vec; var r: Rec ); @returns( "eax" );
begin findVec;
    push( ebx ); push( ecx ); push( esi ); push( edi );
   
    mov( v, ebx );
    for( mov( 0, ecx ); ecx < (type Vec [ebx]).size; inc( ecx ) ) do
        //if( myCmp(&cv->ary[i], r) == 0 )
        intmul( @size(Rec), ecx, esi );
        add( (type Vec [ebx]).ary, esi );
        mov( r, edi );
        pMyCmp( esi, edi ); // need an 'eq' cmp here ...
        breakif( eax );     // i.e. break if equal ...
    endfor;
   
    // now check loop end condition ...
    if( ecx != (type Vec [ebx]).size ) then
        mov( ecx, eax ); // found
    else
        mov( -1, eax ); // NOT found
    endif;
   
    pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end findVec;


// if int index valid, erases element there ...
procedure eraseVec( var v: Vec; index: uns32 in eax );
var
    tmpReg: dword;
begin eraseVec;
    push( ebx ); push( ecx ); push( esi ); push( edi );
   
    mov( v, ebx );
    if( (type int32 eax) >= 0 && (type int32 eax) < (type Vec [ebx]).size ) then
       
        mov( eax, tmpReg );
        intmul( @size(Rec), eax );
        add( (type Vec [ebx]).ary, eax );
        //freeVrec( &(cv->ary[index]) );
        freeRec( eax ); // must pass in eax, needed here if dynamic memory
        mov( tmpReg, eax  );
     

        //if( index < cv->size-1 )
            //memcpy(&cv->ary[index], &cv->ary[index+1], (cv->size-1-index)*sizeof(Rec));
       
        mov( (type Vec [ebx]).size, ecx );
        dec( ecx ); // ecx holds size-1
        if( eax < ecx ) then
            sub( eax, ecx ); // ecx now holds (cv->size-1-index)
            intmul( @size(Rec), ecx ); // ecx now holds bytes to copy ...
           
            intmul( @size(Rec), eax, edi );
            add( (type Vec [ebx]).ary, edi );
            mov( edi, esi );
            add( @size(Rec), esi );
            rep.movsb();
        endif;

        // now update size and return by reference (since address passed in)
        dec( (type Vec [ebx]).size );
           
    else
        mov( (type Vec [ebx]).size, ecx );
        dec( ecx ); // ecx holds size-1
        stdout.put( nl, "ERROR! Index ", index, " out of range 0..", (type int32 ecx), nl);
    endif;
   
    pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end eraseVec;




#if ( false )

// isort ///////////////////////////////////////////////////////////////////

void isortCvec( Cvec* cv, int (*myCmp) (const Rec* a, const Rec* b) )
{
    int i, j;
    Rec cmp;
    for( i = 1; i < cv->size; ++i ) /* start with an array of just the first 2 elements (if exists) */
    {
        memcpy( &cmp, &cv->ary[i], sizeof(Rec) ); /* get new cmp on each outer loop */
        j = i-1; /* get index of element just to the left of 'cmp' to start comparisons */
        while( j >= 0 && myCmp(&cmp, &cv->ary[j]) < 0 )
        {
            memcpy( &cv->ary[j+1], &cv->ary[j], sizeof(Rec) ); /* 'copy up' */
            --j; /* decrement j in preparation for next inner loop ... */
        }
        memcpy( &cv->ary[j+1], &cmp, sizeof(Rec) ); /* insert cmp at index j+1
                                                       (since j was decremented above) */
    }
    cv->isSorted = 1;
} /* size hasn't changed ... */


// msort ///////////////////////////////////////////////////////////////////

void mergeCvec( Cvec* cv, int bot, int top, Cvec* tmp,
            int (*myCmp) (const Rec* a, const Rec* b) )
{
    int mid = (bot+top)/2;
    int sz = top - bot + 1;   // size of the range to be merged
    int j = 0;                // next open index in tmp
    int h1 = bot;             // next index to consider in the 1st half
    int h2 = mid + 1;         // next index to consider in the 2nd half

    // while indexes h1 and h2 are not past their ends ...
    while( h1 <= mid && h2 <= top )
    {   // move the smaller element into the tmp vec next ...
        if( myCmp( &(cv->ary[h1]), &(cv->ary[h2]) ) <= 0  )
            tmp->ary[j++] = cv->ary[h1++];
        else
            tmp->ary[j++] = cv->ary[h2++];
    }
    // Note: only one of the two 'while's' below is executed ...
    while( h1 <= mid ) // copy any remaining entries (1st half)
        tmp->ary[j++] = cv->ary[h1++];
    while( h2 <= top ) // copy any remaining entries (2nd half)
        tmp->ary[j++] = cv->ary[h2++];

    for( j = 0; j < sz; ++j ) // copy back sorted tmp vec...
        cv->ary[bot+j] = tmp->ary[j];
}
void my_msortCvec( Cvec* cv, int bot, int top, Cvec* tmp,
               int (*myCmp) (const Rec* a, const Rec* b) )
{
    if( bot == top ) return;
    else
    {
        int mid = ( bot + top ) / 2;
        my_msortCvec( cv, bot, mid, tmp, myCmp );   // sort the first ...
        my_msortCvec( cv, mid+1, top, tmp, myCmp ); // and the second half
        mergeCvec( cv, bot, top, tmp, myCmp );      // now merge 2 sorted chunks
    }
}
/* *NOTE* Calling msortCvec sets the returned by ref Cvec to *** isSorted *** */
void msortCvec( Cvec* cv, int (*myCmp) (const Rec* a, const Rec* b) )
{
    if( cv->size > 1 )
    {
        Cvec tmp;
        initCvec( &tmp );
        reserveCvec( &tmp, cv->size ); /* Note: resets cap, BUT size still 0 */
        my_msortCvec( cv, 0, cv->size-1, &tmp, myCmp );
        cv->isSorted = 1;
        free( tmp.ary ); /* only free ary mem that held copies of pointers */
    }
}


// find ////////////////////////////////////////////////////////////////////

/* returns index if string present, else -1 */
int findCvec( Cvec* cv, const Rec* r, int (*myCmp) (const Rec* a, const Rec* b) )
{
    int i;
    for( i = 0; i < cv->size; ++i )
        if( myCmp(&cv->ary[i], r) == 0 ) return i;
    /* else if reach here ... */
    return -1;
}

// erase ///////////////////////////////////////////////////////////////////

/* if int index valid, erases element there */
void eraseCvec( Cvec* cv, int index )
{
    //int i;
    if( index < 0 || index >= cv->size )
        { printf( "\nERROR! Index %d out of range 0..%d\n", index, cv->size-1 );
          return; }

    freeVrec( &(cv->ary[index]) ); /* needed here for dynamic memory types */

    /* copy each (set of pointers above) down one index */
    //for( i = index; i < cv->size-1; ++i )
        //memcpy(&cv->ary[i], &cv->ary[i+1], sizeof(Rec));
    if( index < cv->size-1 )
        memcpy(&cv->ary[index], &cv->ary[index+1], (cv->size-1-index)*sizeof(Rec));

    /* now update size and return (by reference, since address passed in) */
    -- cv->size;
}

#endif


David

The list pair:

"sllist.hhf"

// sllist.hhf // 2012-08-16 //

// using NNode to avoid conflict with Node
// using SLList to avoid conflict with List

#includeOnce( "stdlib.hhf" )


// NB!!! the record NNode must be defined before including this file
// ALSO  the type pNNode: pointer to NNode;


? @nodisplay:= true; // all procedures have @nodisplay 'as default' now


type   
    SLList: record
                head:   pNNode;
                tail:   pNNode;
                size:   uns32;   
            endrecord;
 
 
procedure initSLList( var lst: SLList );
begin initSLList;
    push( ebx );
    mov( lst, ebx );
    mov( 0, (type SLList [ebx]).head );
    mov( 0, (type SLList [ebx]).tail );
    mov( 0, (type SLList [ebx]).size );
    pop( ebx );
end initSLList;


procedure push_frontSLList( var lst: SLList; var n: NNode );
begin push_frontSLList;
    //push( ebx ); push( ecx ); push( esi ); push( edi ); // caller must preserve as needed
   
    cld();
    mem.alloc( @size(NNode) );
   
    mov( eax, edi );
    mov( n, esi ); // get address of new NNode into esi ...

    // get num bytes to copy in ecx ...
    mov( @size(NNode), ecx );
    rep.movsb();
   
    // ok ... now prepend (copy) of new NNode ...
    mov( lst, ebx );
    if( (type SLList [ebx]).size != 0 ) then // not empty
        mov( mov( (type SLList [ebx]).head, ecx ), (type NNode [eax]).next );
        mov( eax, (type SLList [ebx]).head );
    else
        mov( 0, (type NNode [eax]).next );
        mov( eax, (type SLList [ebx]).tail );
        mov( eax, (type SLList [ebx]).head );
    endif;
   
    inc( (type SLList [ebx]).size ); // update size
 
    //pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end push_frontSLList;

procedure push_backSLList( var lst: SLList; var n: NNode );
begin push_backSLList;
    // push( ebx ); push( ecx ); push( esi ); push( edi ); // caller must preserve as needed
   
    cld();
    mem.alloc( @size(NNode) );
   
    mov( eax, edi );
    mov( n, esi ); // get address of new NNode into esi ...

    // get num bytes to copy in ecx ...
    mov( @size(NNode), ecx );
    rep.movsb();
   
    mov( 0, (type NNode [eax]).next );
   
    // ok ... now append (copy) of new rec ...
    mov( lst, ebx );
    if( (type SLList [ebx]).size != 0 ) then // not empty
        mov( (type SLList [ebx]).tail, ecx );
        mov( eax, (type NNode [ecx]).next ); // now 'next' updated
        mov( eax, (type SLList [ebx]).tail );
    else
        mov( eax, (type SLList [ebx]).tail );
        mov( eax, (type SLList [ebx]).head );
    endif;
   
    inc( (type SLList [ebx]).size ); // update size
 
    // pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end push_backSLList;


procedure clearSLList( var v: SLList );
begin clearSLList;
    push( eax ); push( ebx ); push( ecx );
   
    mov( v, ebx );
    mov( (type SLList[ebx]).head, ecx );
    while(  ecx != 0 ) do
        mov( ecx, eax );
        mov( (type NNode [ecx]).next, ecx );
        freeNNode( eax ); // recall this procedure 'freeNNode' must be defined
                        // along with type NNode, BEFORE including this file
                       
        mem.free( eax );
    endwhile;
   
   
    initSLList( (type SLList [ebx]) );
 
    pop( ecx ); pop( ebx ); pop( eax );
end clearSLList;


David

"sllist_func's.hhf"

// sllist_func's.hhf // // 2012-08-16 //


#includeOnce( "sllist.hhf" )


type
    pSLList: pointer to SLList;


#if( !@defined( pMyCmp ) )
static
    pMyCmp: procedure( a: dword in esi; b: dword in edi ); @returns( "eax" );
#endif


// re. isort ... note: cmp func is a <= b (for list, ALSO same as for msort)
// re. msort ... note: cmp func is a <= b


// pass in the address of the SLList and the new Node to insert ...
procedure insert_sortedSLList( var lst: SLList; pn: pNNode in eax );
//var
    //head: pNNode; // esi
var
    reg32:  dword;
begin insert_sortedSLList;
    push( ebx ); push( esi ); push( edi );
   
    //pList head;
    //if( !isSortedSLList( list, myCmp )) msortSLList( list, myCmp );
    //head = list->start;
    mov( lst, ebx );
    mov( (type SLList [ebx]).head, esi );
    mov( eax , edi );
   
    // firstly, we handle most common case where 'n' is NOT the first Node
    //if( head != NULL && myCmp( n, head ) >= 0 ) // NEED >=
    if( esi && pMyCmp( esi, edi ) ) then
        // so search the linked list for the right location
        //pList cur = head;
        //while( cur->next != NULL && myCmp( cur->next, n ) <= 0 )
        mov( esi, reg32 );
        mov( (type NNode [esi]).next, esi );
        //while( esi && pmyCmp( esi, edi ) ) do
        forever
            if( !esi || !pMyCmp( esi, edi ) ) then
                //cur = cur->next;
                mov( reg32, esi );
                break;
            else
                mov( esi, reg32 );
                mov( (type NNode [esi]).next, esi );
            endif;
        endfor;
       
        //if( cur == list->end ) list->end = n;
        if( esi == (type SLList [ebx]).tail ) then
            mov( edi, (type SLList [ebx]).tail );
        endif;
       
        //n->next = cur->next;
        mov( (type NNode [esi]).next, (type NNode [edi]).next );
       
        //cur->next = n;
        mov( edi, (type NNode [esi]).next );
       
        // list->start = head; // unchanged //
        //++ list->size;
        inc( (type SLList [ebx]).size );
    else // if we reach here, this IS the first node ...
        //n->next = head; // so set this node in first position
        mov( (type SLList [ebx]).head, (type NNode [edi]).next );
        //list->start = n;
        mov( edi, (type SLList [ebx]).head );
        //++ list->size;
        inc( (type SLList [ebx]).size );
        if( (type SLList [ebx]).size == 1 ) then
            // list->end = n;
            mov( edi, (type SLList [ebx]).tail );
        endif;
    endif;
   
    pop( edi ); pop( esi ); pop( ebx );
end insert_sortedSLList;

//void isortSLList( SLList* list, int (*myCmp) (const pList r1, const pList r2) )
procedure isortSLList( var lst: SLList );
var
    nSLList:    SLList;
    //cur:        pNNode; // eax
    //nextNode:   pNNode; // ecx
begin isortSLList;
    push( eax ); push( ebx ); push( ecx );
   
    initSLList( nSLList );
    mov( lst, ebx );
    //for( cur = list->start; cur != NULL; cur = nextNode )
    for( mov( (type SLList [ebx]).head, eax ); eax; mov( ecx, eax ) ) do
        //nextNode = cur->next; // get copy here before next is changed below
        mov( (type NNode [eax]).next, ecx );
        //insert_sortedSLList( &nSLList, cur, myCmp ); // 'cur' already is a Node pointer
        insert_sortedSLList( nSLList, eax );
    endfor;
     
    // now ... update old list with new front and back ...
    //list->start = nSLList.start;
    mov( nSLList.head, (type SLList [ebx]).head );
    //list->end = nSLList.end;
    mov( nSLList.tail, (type SLList [ebx]).tail );
   
    // size hasn't changed 

    pop( ecx ); pop( ebx ); pop( eax );
end isortSLList;


// merge two sorted SLLists with heads 'a' and 'b' ... in sorted order
procedure mergeSLLists(var a: SLList; var b: SLList ); @returns( "eax" ); // new head
var
    newMergedHead:  dword;
    //sorted:         dword; // use ebx // using edx now ..
begin mergeSLLists;
    //push( ebx ); push( esi ); push( edi ); // using edx instead of ebx here now

    //pList sorted, new_merged_head;
    mov( a, eax );
    mov( (type SLList [eax]).head, esi );
    mov( b, eax  );
    mov( (type SLList [eax]).head, edi );
   
    //if( a->start == NULL ) return b->start;
    //if( b->start == NULL ) return a->start;
    if( !esi ) then mov( edi, newMergedHead );
    elseif( !edi ) then mov( esi, newMergedHead );
   
    else
        //if( myCmp(a->start, b->start) <= 0 )
        if( pMyCmp( esi, edi ) ) then
            //sorted = a->start;
            //a->start = a->start->next;
            mov( esi, edx );
            mov( (type NNode [esi]).next, esi );
        else
            //sorted = b->start;
            //b->start = b->start->next;
            mov( edi, edx );
            mov( (type NNode [edi]).next, edi );
        endif;
       
        //new_merged_head = sorted;
        mov( edx, newMergedHead );

        // now ...
        //while( a->start != NULL && b->start != NULL )
        while( esi && edi ) do
            //if( myCmp(a->start, b->start) <= 0 )
            if( pMyCmp( esi, edi ) ) then
                //sorted->next = a->start;
                mov( esi, (type NNode [edx]).next );
                //sorted = a->start;
                mov( esi, edx );
                //a->start = a->start->next;
                mov( (type NNode [esi]).next, esi );   
            else
                //sorted->next = b->start;
                mov( edi, (type NNode [edx]).next );
                //sorted = b->start;
                mov( edi, edx );
                //b->start = b->start->next;
                mov( (type NNode [edi]).next, edi );
            endif;
        endwhile;

        // and finally ...
        //if( a->start != NULL )
        if( esi ) then
            //sorted->next = a->start;
            mov( esi, (type NNode [edx]).next );
        //else if( b->start != NULL )
        elseif( edi ) then
            //sorted->next = b->start;
            mov( edi, (type NNode [edx]).next );
        endif;
   
    endif;

    mov( newMergedHead, eax );
   
    //pop( edi ); pop( esi ); pop( ebx );
end mergeSLLists;


// a recursive mergesort ...
procedure mergesortSLList( var lst: SLList );
var
    // cur:    pNNode; // ecx
    a:      SLList;
    b:      SLList;
begin mergesortSLList;
    push( ebx );
    //push( ecx ); push( edx );
    //pList cur = list->start;
    mov( lst, ebx );
    mov( (type SLList [ebx]).head, ecx );
    mov( ecx, a.head );
    //SLList a, b;

    // base case is a SLList of length 0 or 1 ...
    //if ((cur == NULL) || (cur->next == NULL))  return;
   
    if( a.head != 0 /*&& b.head != 0*/ ) then
        mov( (type NNode [ecx]).next, b.head );
        if( b.head != 0 ) then
            // split SLList into 'a' and 'b' sublists ...
            //a.start = cur;
            //b.start = cur->next; // both these lines done above
            while( b.head != 0 /*&& ( b.start->next != NULL)*/  ) do
                mov( b.head, edx );
                mov( (type NNode [edx]).next, edx );
                breakif( !edx );
               
                //cur = cur->next;
                mov( (type NNode [ecx]).next, ecx );
                //b.start = b.start->next->next;
                mov( (type NNode [edx]).next, b.head );
            endwhile;

            //b.start = cur->next;
            mov( (type NNode [ecx]).next, b.head );
           
            //cur->next = NULL; // SLList divided into 2 roughly equal parts now
            mov( 0, (type NNode [ecx]).next );

            // recursively sort the sublists ...
            mergesortSLList( a );
            mergesortSLList( b );

            // merge the two sorted SLLists together ...
            //list->start = mergeSLLists(&a, &b, myCmp);
            mergeSLLists( a, b ); // returns new merged head in eax
            mov( eax, (type SLList [ebx]).head );
        endif;;
    endif;
    //pop( edx ); pop( ecx );
    pop( ebx );
end mergesortSLList;


procedure update_endSLList( var lst: SLList ); // after sort
begin update_endSLList;
    //push( ebx ); push( ecx );
   
    mov( lst, ebx );
    if( (type SLList [ebx]).size > 1 ) then
        //pList cur;
        //for( cur = list->start; cur->next != NULL; cur = cur->next ) ;
        //list->end = cur;
        //list->end->next = NULL; // this line NOT-needed/redundant
        mov( (type SLList [ebx]).head, ecx );
        while( (type NNode [ecx]).next != 0 ) do mov( (type NNode [ecx]).next, ecx ); endwhile;
        mov( ecx, (type SLList [ebx]).tail );
    endif;
   
    //pop( ecx ); pop( ebx );
end update_endSLList;


procedure msortSLList( var lst: SLList );
var
    Rega:   dword;
    Regb:   dword;
    Regc:   dword;
    Regd:   dword;
    Regesi: dword;
    Regedi: dword;
begin msortSLList;
    mov( eax, Rega ); mov( ebx, Regb ); mov( ecx, Regc ); mov( edx, Regd );
    mov( esi, Regesi ); mov( edi, Regedi );
   
    mov( lst, ebx );
    mergesortSLList( (type SLList [ebx]) );
    //mov( lst, ebx );
    update_endSLList( (type SLList [ebx]) );
   
    mov( Regedi, edi ); mov( Regesi, esi );
    mov( Regd, edx ); mov( Regc, ecx ); mov( Regb, ebx ); mov( Rega, eax );
end msortSLList;



// returns pointer if present, else returns 0
procedure findSLList( var lst: SLList; var nn: NNode ); @returns( "eax" );
begin findSLList;
    push( ebx ); push( esi ); push( edi );
   
   
    //pList p;
    mov( lst, ebx );
    mov( nn, edi );
    //for( p = cl->start; p != NULL; p = p->next )
    for( mov( (type SLList [ebx]).head, esi ); esi; mov( (type NNode [esi]).next, esi ) ) do
        //if( myCmp(p, pr) == 0 ) return p;
        pMyCmp( esi, edi ); // returns eax = 1 if found, else 0
        if( eax ) then
            mov( esi, eax );
            break;
        endif;
    endfor;
    // if not found above, eax is already 0
   
    pop( edi ); push( esi ); push( ebx );
end findSLList;


procedure eraseSLList( var lst: SLList;  pn: pNNode in eax ); // if pn valid, erase element there
//var
    //prev:   pNNode; // use edx
begin eraseSLList;
    push( ebx ); push( ecx ); push( edx );
   
    //pList prev = NULL, cur = cl->start;
    mov( 0, edx );
    mov( lst, ebx );
   
    for( mov( (type SLList [ebx]).head, ecx ); ecx && ecx != eax; mov( (type NNode [ecx]).next, ecx ) ) do
        //prev = cur;
        mov( ecx, edx );
    endfor;
   
    if( ecx ) then
       
        if( edx ) then // case of NOT FIRST element
            //prev->next = cur->next;
            mov( (type NNode [ecx]).next, (type NNode [edx]).next );
        else // case of IS FIRST element
            //cl->start = cl->start->next;
            mov( (type NNode [ecx]).next, (type SLList [ebx]).head );
        endif;
       
        //if( cur == cl->end ) cl->end = prev; /* case of IS LAST element*/
        if( ecx == (type SLList [ebx]).tail ) then
            mov( edx, (type SLList [ebx]).tail );
        endif;

        freeNNode( ecx ); // needed here for types like dynamic C strings, etc
        mem.free( ecx ); // free memory for dynamic Node ...

        // now update size and return (by reference, since address passed in)
        //-- cl->size;
        dec(  (type SLList [ebx]).size );
       
    else
        stdout.puts( nl "eraseSLList ERROR! Pointer NOT found ..." );
    endif;
   
    pop( edx ); pop( ecx ); pop( ebx );
end eraseSLList;



#if( !@defined( chrIsInStr ) )
    // returns *POSITION*  1..n  (POS = INDEX+1)  or  0 if NOT found //
    procedure chrIsInStr( c: char in al; s: string ); @returns( "eax" );
    begin chrIsInStr;
        str.chpos( s, al );
        inc( eax );
    end chrIsInStr;
#endif


procedure splitSLList( var sll: SLList; s: string; delims: string );
var
    myNNode:    NNode;
    reg32:      dword;
    i:          uns32;
begin splitSLList;
    push( eax ); push( ebx ); push( ecx ); push( edx );
   
    mov( sll, ebx );
    mov( s, ecx ); // ecx holds start address ...
   
    forever
   
        while( chrIsInStr( (type char [ecx]), delims ) ) do
            inc( ecx );
        endwhile;
       
        breakif( (type char [ecx]) == 0 ); // since 'empty' string
       
        mov( ecx, edx );
        inc( edx ); // edx holds address one past end of 'word' ...
       
        mov( (type char [edx]), al );
        while( al && !chrIsInStr( al, delims ) ) do
            inc( edx );
            mov( (type char [edx]), al );
        endwhile;
       
        mov( edx, reg32 );  // save a copy of 'end' in reg32 ...
        sub( ecx, edx );    // edx holds 'len of next word'
       
        mov( ecx, i );
        mov( s, eax );
        sub( eax, i );      // i is index to start
       
        str.a_substr( s, i, edx ); // start, len
        mov( eax, myNNode.nval );
       
        push_backSLList( (type SLList [ebx]), myNNode );
       
        mov( reg32, ecx );  // update (next) start pointer ecx ...
   
    endfor;
 
    pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end splitSLList;


procedure uniqueSLList( var lst: SLList );
begin uniqueSLList;
    push( eax ); push( ebx ); push( esi ); push( edi );

    //pList p = c->start;
    mov( lst, ebx );
    mov( (type SLList [ebx]).head, esi ); // esi holds cur NNode address
   
    if( esi ) then
        //while( p->next != NULL )
        while( (type NNode [esi]).next != 0 ) do
            //if( myCmp(p, p->next) == 0 )
            mov( (type NNode [esi]).next, edi );
            if( pMyCmp( esi, edi ) ) then // if equal then ...
                //pList tmp = p->next;
                mov( edi, eax ); // eax is tmp copy ...
               
                //p->next = p->next->next;
                mov( (type NNode [edi]).next, edi ); // now advance edi ...
                mov( edi, (type NNode [esi]).next );
               
                freeNNode( eax ); // needed here for types like dynamic Cstrings, etc
                mem.free( eax );
                //-- c->size;
                dec( (type SLList [ebx]).size );
            else
                //p = p->next;
                mov( edi, esi ); // mov esi up since not equal ...
            endif;
        endwhile;
   
        update_endSLList( (type SLList [ebx]) ) ; // update end ...
    endif;
   
    pop( edi ); pop( esi ); pop( ebx ); pop( eax );
end uniqueSLList;

David

Now the FILE read functions, that permit reading in an HLA string of any length, restricted only by the amount of remaining available memory ...

These include files are named:

"readLine.hhf"

Called like this: (similar to getline in C++)

while( readLine( fin, lineStr ) ) do

   // process lineStr

endwhile;

and

"readWord.hhf"

Called like this:

while( readWord( fin, wordStr, delimitersStr ) ) do

   // process wordStr

endwhile;

// readLine.hhf // 2012-08-11 //

#includeOnce( "stdlib.hhf" )

#if( !@defined( InitStrCap ) )
const
    InitStrCap: uns32 := 1024;
#endif


// expects an unitialized string to be passed in ...
procedure readLine( fin:dword; var line:string ); @nodisplay; @returns("eax");
var
    cap:    dword;
    sTmp:   string;
   
begin readLine;
    push( ebx ); push( ecx );
   
    mov( InitStrCap, eax );
    mov( eax, cap );
    str.alloc( eax );
    mov( eax, ebx );
   
    mov( 0, ecx );      // and ... ecx tracks size
    if( !fileio.eof( fin ) ) then

        while( !fileio.eof( fin ) ) do
       
            fileio.getc( fin );
           
            if( al == stdio.cr ) then // Windows #13 #10
                fileio.getc( fin );
                break; // eat #10
            elseif( al == stdio.eoln ) then // Linux, etc ?
                break;
            endif;
           
            if( ecx == cap ) then   // size == cap, so realloc into cap x2 memory
                shl( 1, cap );      // cap gets x2 (doubled)...
                mov( ecx, (type str.strRec [ebx]).length );
                mov( 0, (type char [ebx+ecx+1]) );
                push( eax );        // preserve char in al ...
                str.realloc( ebx, cap );
                mov( eax, ebx );    // Ok ... now NEW string in ebx ..
                pop( eax );         // restore char in al in eax
            endif;
           
            mov( al, [ebx + ecx] );
            inc( ecx );

        endwhile;
       
        mov( ecx, (type str.strRec [ebx]).length );
        mov( 0, (type char [ebx+ecx]) );
       
        mov( ebx, sTmp );   // get a copy of address ...
        str.a_cpy( ebx );   // new 'right sized' string now in eax
       
        mov( line, ebx );
        mov( eax, [ebx] );  // so can return by ref new string
        str.free( sTmp );
    else
        str.free( ebx );    // prevent 'memory leak'
        mov( 0, eax );      // AT eof NOW, so return 'zero'
    endif;

    pop( ecx ); pop( ebx );
end readLine;



And ...

// readLineConsole.hhf // 2012-08-22  //

#includeOnce( "stdlib.hhf" )

#if( !@defined( InitCap ) )
const
    InitCap: uns32 := 256;
#endif

procedure readLineConsole; @nodisplay; @returns( "eax" );
var
    cap:    uns32;
begin readLineConsole;
    push( ebx ); push( ecx );
 
    mov( 0, ecx );      // ecx tracks size ...
    mov( ecx, cap );
    forever
       
        if( ecx == cap ) then
            if( ecx != 0 ) then
                shl( 1, cap ); // double cap
                str.realloc( ebx, cap );
            else
                mov( InitCap, cap );
                str.alloc( cap );
            endif;
            mov( eax, ebx );
        endif;
       
        stdin.getc();
        breakif( al == stdio.eoln );
       
        mov( al, [ebx+ecx] );
        inc( ecx );
       
    endfor;
   
    mov( 0, (type byte [ebx+ecx]) );            // 0 terminate
    mov( ecx, (type str.strRec [ebx]).length ); // update size

    str.a_cpy( ebx );   // returns 'right size capacity' string in eax ...
    str.free( ebx );    // can eliminate this 'tmp' string now ...
   
    pop( ecx ); pop( ebx );
end readLineConsole;

David

"readWord.hhf"

// readWord.hhf // 2012-08-11 //

#includeOnce( "stdlib.hhf" )

#if( !@defined( InitStrCap ) )
const
    InitStrCap: uns32 := 32;
#endif

/*
#if( !@defined( chrIsInStr ) ) 
    // returns *POSITION*  1..n  (POS = INDEX+1)  or  0 if NOT found //
    procedure chrIsInStr( c: char in al; s: string ); @returns( "eax" );
    begin chrIsInStr;
        str.chpos( s, al );
        inc( eax );
    end chrIsInStr;
#endif
*/

// expects an unitialized string to be passed in ...
procedure readWord( fin:dword; var line:string; delims: string ); @nodisplay; @returns("eax");
var
    cap:    dword;
    sTmp:   string;
    c:      char;
begin readWord;
    push( ebx ); push( ecx );
   
    mov( 0, ecx );      // using ecx to track size ...
    mov( InitStrCap, eax );
    mov( eax, cap );
   
    str.alloc( eax );
    mov( eax, ebx );
   
    // firstly ... skip over any leading delimit char's ...
    while( !fileio.eof( fin ) ) do
        fileio.getc( fin );
        mov( al, c );
        //breakif( !chrIsInStr( al, delims ) );
        str.chpos( delims, al );
        if( eax == -1 ) then
            // ok ... we have a good char in c ...
            mov( c, al ); // restore good char value to al ...
            mov( al, [ebx + ecx] );
            inc( ecx );
            break;
        endif;
    endwhile;
   
    // now ... get rest of word ... (if any)
    while( !fileio.eof( fin ) ) do
       
        fileio.getc( fin );
        mov( al, c );
       
        //breakif( chrIsInStr( al, delims ) );
        str.chpos( delims, al );
        breakif( eax != -1 );
       
        if( ecx == cap ) then   // size == cap, so realloc into cap x2 memory
            shl( 1, cap );      // cap gets x2 (doubled)...
            mov( ecx, (type str.strRec [ebx]).length );
            mov( 0, (type char [ebx+ecx+1]) );
            str.realloc( ebx, cap );
            mov( eax, ebx );    // Ok ... now NEW string in ebx ..
        endif;
           
        mov( c, al ); // restore good char value to al ...
        mov( al, [ebx + ecx] );
        inc( ecx );
       
    endwhile;
   
    if( !ecx && fileio.eof( fin ) ) then // if eof and empty line ...
        str.free( ebx );
        mov( 0, eax );
    else
        mov( ecx, (type str.strRec [ebx]).length );
        mov( 0, (type char [ebx+ecx]) );
       
        mov( ebx, sTmp );   // get a copy of address ...
        str.a_cpy( ebx );   // new 'right sized' string now in eax
       
        mov( line, ebx );
        mov( eax, [ebx] );  // so can return by ref new string
        str.free( sTmp );
    endif;

    pop( ecx ); pop( ebx );
end readWord;



And ...

// readWordConsole.hhf // 2012-08-27 //

#includeOnce( "stdlib.hhf" )

#if( !@defined( InitStrCap ) )
const
    InitStrCap: uns32 := 32;
#endif

/*
#if( !@defined( chrIsInStr ) ) 
    // returns *POSITION*  1..n  (POS = INDEX+1)  or  0 if NOT found //
    procedure chrIsInStr( c: char in al; s: string ); @returns( "eax" );
    begin chrIsInStr;
        str.chpos( s, al );
        inc( eax );
    end chrIsInStr;
#endif
*/

// expects an unitialized string to be passed in ...
procedure readWordConsole( var line:string; delims: string ); @nodisplay; @returns("eax");
var
    cap:    dword;
    sTmp:   string;
    c:      char;
begin readWordConsole;
    push( ebx ); push( ecx );
   
    mov( 0, ecx );      // using ecx to track size ...
    mov( InitStrCap, eax );
    mov( eax, cap );
   
    str.alloc( eax );
    mov( eax, ebx );
   
    // firstly ... skip over any leading delimit char's ...
    forever
        stdin.getc();
        mov( al, c );
        if( al != stdio.eoln ) then
            str.chpos( delims, al );
            if( eax == -1 ) then
                // ok ... we have a good char in c ...
                mov( c, al ); // restore good char value to al ...
                mov( al, [ebx + ecx] );
                inc( ecx );
                break;
            endif;
        endif;
    endfor;
   
    // now ... get rest of word ... (if any)
    forever
       
        stdin.getc();
        breakif( al == stdio.eoln );
        mov( al, c );
       
        str.chpos( delims, al );
        breakif( eax != -1 );
       
        if( ecx == cap ) then   // size == cap, so realloc into cap x2 memory
            shl( 1, cap );      // cap gets x2 (doubled)...
            mov( ecx, (type str.strRec [ebx]).length );
            mov( 0, (type char [ebx+ecx+1]) );
            str.realloc( ebx, cap );
            mov( eax, ebx );    // Ok ... now NEW string in ebx ..
        endif;
           
        mov( c, al ); // restore good char value to al ...
        mov( al, [ebx + ecx] );
        inc( ecx );
       
    endfor;

    mov( ecx, (type str.strRec [ebx]).length );
    mov( 0, (type char [ebx+ecx]) );
   
    mov( ebx, sTmp );   // get a copy of address ...
    str.a_cpy( ebx );   // new 'right sized' string now in eax
   
    mov( line, ebx );
    mov( eax, [ebx] );  // so can return by ref new string
    str.free( sTmp );

    pop( ecx ); pop( ebx );
end readWordConsole;

David

This second set of vector and list functions, permit any number of types of vector or list in a program.  These pairs of include files are named:


The first pair is:

"vector2_func's.hhf"
"vector2.hhf"

If you include file "vector2_func's.hhf" to get access to the sort, etc... functions, that file FIRSTLY includes file "vector2.hhf", which firstly includes file "stdlib.hhf"




The next pair is:

"sllist2_func's.hhf"
"sllist2.hhf"


If you include file "sllist2_func's.hhf" to get access to the sort, etc... functions, that file FIRSTLY includes file "sllist2.hhf", which firstly includes file "stdlib.hhf"

David

The vector2 pair:

"vector2.hhf"

// vector2.hhf // 2012-08-15 //

#includeOnce( "stdlib.hhf" )

? @nodisplay:= true; // All procedures have @nodisplay 'as default' now.


#if( !@defined( InitCap ) )
const
    InitCap: uns32 := 8;
#endif

// Recall needs a procedure like freeRec( pr: dword in eax );
// so can pass proc address to pFreeRec used below ...
/* // for example ... //
    procedure freeRec( pr: dword in eax ); @nodisplay; @noframes;
    begin freeRec;
        free( (type Rec eax).str );
        ret();
    end freeRec;
*/

// get a (static) procedure pointer
static
    pFreeRec: procedure( a: dword in eax );
   
type   
    Vec:    record
                ary:        dword;
                capacity:   uns32;
                size:       uns32;
                elen:       uns32;
            endrecord;
 
 
procedure initVec( var v: Vec; eln: uns32 );
begin initVec;
    push( ebx );
    mov( v, ebx );
    mov( 0, (type Vec [ebx]).ary );
    mov( 0, (type Vec [ebx]).capacity );
    mov( 0, (type Vec [ebx]).size );
    mov( eln, (type Vec [ebx]).elen );
    pop( ebx );
end initVec;


// double capacity ...
procedure enlargeVec( var v: Vec );
begin enlargeVec;
    push( eax ); push( ebx ); push( ecx );

    mov( v, ebx );   
    if( (type Vec [ebx]).capacity != 0 ) then   // i.e. if capacity not zero
        shl( 1, (type Vec [ebx]).capacity );    // double capacity
    else;  // set initial capacity
        mov( mov( InitCap, eax ), (type Vec [ebx]).capacity );   
    endif;
   
    mov( (type Vec [ebx]).elen, ecx );
    intmul( (type Vec [ebx]).capacity, ecx );

    if( (type Vec [ebx]).ary ) then mem.realloc( (type Vec [ebx]).ary, ecx );
    else mem.alloc( ecx );
    endif;
   
    mov( eax, (type Vec [ebx]).ary );

    pop( ecx ); pop( ebx ); pop( eax );
end enlargeVec;


// if size == capacity then enlarge ...
procedure push_backVec( var v: Vec; pr: dword );
begin push_backVec;
    //push( ebx ); push( ecx ); push( esi ); push( edi ); // caller must preserve if needed
   
    // if size == capacity then enlarge/double capacity ...
    mov( v, ebx );
    mov( (type Vec [ebx]).size, ecx );
    if( ecx == (type Vec [ebx]).capacity ) then
        enlargeVec( (type Vec [ebx]) );
    endif;
   
   
    // ok ... now append (copy) of new rec ...
   
    mov( pr, esi ); // get address of new Rec into esi ...
    mov( (type Vec [ebx]).size, edi );
    intmul( (type Vec [ebx]).elen, edi );
    add( (type Vec [ebx]).ary, edi ); // get offset into edi

    // get num bytes to copy in ecx ...
    mov( (type Vec [ebx]).elen,  ecx );
    rep.movsb();
   
    inc( (type Vec [ebx]).size ); // update size
 
    //pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end push_backVec;


procedure reserveVec( var v: Vec; newCap: uns32  );
begin reserveVec;
    push( eax ); push( ebx );

    mov( newCap, eax );
    mov( v, ebx );
    if(  eax > (type Vec [ebx]).capacity ) then
   
        mov( eax, (type Vec [ebx]).capacity );
        intmul( (type Vec [ebx]).elen, eax );       // num new bytes in eax
        if( (type Vec [ebx]).ary ) then
            mem.realloc( (type Vec [ebx]).ary, eax );
        else
            mem.alloc( eax );
        endif;
        mov( eax, (type Vec [ebx]).ary );           // update new ary pointer
       
    endif;

    pop( ebx ); pop( eax );
end reserveVec;


procedure clearVec( var v: Vec );
static
    rsize: uns32;
begin clearVec;
    push( eax ); push( ebx ); push( ecx );
   
    mov( v, ebx );
    mov( (type Vec [ebx]).elen, rsize );
    mov( (type Vec[ebx]).size, ecx );
    if( (type Vec [ebx]).ary ) then // if was allocated some memory ...
   
        for( dec( ecx ); (type int32 ecx) >= 0; dec( ecx ) ) do
            mov( ecx, eax );
            intmul( rsize, eax );
            add( (type Vec [ebx]).ary, eax );
            pFreeRec( eax ); // recall some procedure 'freeRec' must be defined
                            // along with some type Rec, BEFORE including this file
        endfor;
        mem.free( (type Vec [ebx]).ary );
       
    endif;
   
   
   
    initVec( (type Vec [ebx]), 0 );
 
    pop( ecx ); pop( ebx ); pop( eax );
end clearVec;


David

"vector2_func's.hhf"

// vector2_func's.hhf // 2012-08-15 //

#includeOnce ( "vector2.hhf" )


type
    pVec: pointer to Vec;


#if( !@defined( pMyCmp ) )
static
    pMyCmp: procedure( a: dword in esi; b: dword in edi ); @returns( "eax" );
#endif



// re. isort ... note: cmp func is a < b (for vector)
// re. msort ... note: cmp func is a <= b

procedure isortVec( var v: Vec );
static
    tmpReg: dword;
var
    rsize:  uns32;
    prcmp:  dword;
begin isortVec;
    push( eax ); push( ebx ); push( ecx ); push( edx ); push( esi ); push( edi );
   
     //int i, j; // i in eax, j in edx
    // start with an array of just the first 2 elements (if exists) ...
    // for( i = 1; i < cv->size; ++i )
    cld();
    mov( v, ebx );
    mov( (type Vec [ebx]).elen, rsize );
    mem.alloc( rsize );
    mov( eax, prcmp );
    for( mov( 1, eax ); eax < (type Vec [ebx]).size; inc( eax ) ) do
   
        // update this cmp Rec on each outer loop
        //memcpy( &cmp, &cv->ary[i], sizeof(Rec) );
        mov( prcmp, edi );
        mov( eax, esi );
        intmul( rsize, esi );
        add( (type Vec [ebx]).ary, esi );
        mov( rsize, ecx ); // get # bytes to copy in a Rec ... into ecx
        rep.movsb(); // ok ... got copy into rcmp ...

        // get index of element just to the left of the above 'cmp' to start comparisons
        mov( eax, edx );    // j = i
        dec( edx );         // --j // i.e. j = i-1; //
        while( (type int32 edx) >= 0 ) do   // while( j >= 0 && myCmp(&cmp, &cv->ary[j]) < 0 )
       
            mov( edx, edi );
            intmul( rsize, edi ); // get offset into edi ..
            add( (type Vec [ebx]).ary, edi );
            mov( prcmp, esi );
       
            //breakif( !pMyCmp(esi, edi) ); // 'copy up' // but first ...
            mov( eax, tmpReg ); // get copy of loop counter in eax
            pMyCmp( esi, edi );
            if( eax ) then
                mov( tmpReg, eax );
            else
                mov( tmpReg, eax );
                break;
            endif;
           
            // Now ...  'copy up' ...
            //memcpy( &cv->ary[j+1], &cv->ary[j], sizeof(Rec) );
            mov( edx, esi );
            intmul( rsize, esi );
            add( (type Vec [ebx]).ary, esi );
            mov( esi, edi );
            add( rsize, edi ); // to inc( j ) add size  //
            mov( rsize, ecx ); // get # bytes to copy in a Rec into ecx
            rep.movsb();       
       
            dec( edx ); //--j; // --j to prepare for next inner loop
           
        endwhile;
       
        // insert pointer at index j+1 (since j was decremented above ...
        //memcpy( &cv->ary[j+1], &cmp, sizeof(Rec) );
        inc( edx ); // ++j
        mov( prcmp, esi );
        mov( edx, edi );
        intmul( rsize, edi );
        add( (type Vec [ebx]).ary, edi );
        mov( rsize, ecx ); // get # bytes to copy in a Rec into ecx
        rep.movsb();
 
    endfor;
   
    pop( edi ); pop( esi ); pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end isortVec; // size hasn't changed ...



////////////////////////////////////////////////////////////////////////////
// re. msort ... note: cmp func is a <= b

procedure mergeVec( pv: pVec in ebx; bot: int32; top: int32; ptmp: pVec in edx );
var
    mid:    int32; // set to (bot+top)/2
    sz:     int32; // set to top-bot+1    // size of the range to be merged
    j:      int32;
    tmpReg: dword;
    rsize:  uns32;
begin mergeVec;
    //push( eax ); push( ecx ); push( esi ); push( edi ); // NOTE: all 6 reg's preserved by msortVec
   
    //int mid = (bot+top)/2;
    mov( top, eax );
    add( bot, eax );
    shr( 1, eax ); // divide by 2
    mov( eax, mid );
    //int sz = top - bot + 1;   // size of the range to be merged
    mov( top, eax );
    sub( bot, eax );
    inc( eax );
    mov( eax, sz );   
    mov( 0, j );        // int j = 0;        // next open index in tmp
    mov( bot, eax );    // int h1 = bot;     // next index to consider in 1st half
    mov( mid, ecx );    // int h2 = mid + 1; // next index to consider in 2nd half
    inc( ecx );
   
    mov( (type Vec [ebx]).elen, rsize );
   
    // while indexes h1 and h2 are not past their ends ...
    cld();
    while( eax <= mid && ecx <= top ) do // while( h1 <= mid && h2 <= top )
       
        // move the smaller element into the tmp vec 'next' ...
        // if( myCmp( &(cv->ary[h1]), &(cv->ary[h2]) ) <= 0  )
        mov( eax, esi );
        intmul( rsize, esi );
        add( (type Vec [ebx]).ary, esi );
       
        mov( ecx, edi );
        intmul( rsize, edi );
        add( (type Vec [ebx]).ary, edi );
       
        mov( eax, tmpReg );
        pMyCmp( esi, edi ); // if( myCmp(...) <= 0  )
       
        if( eax ) then // if( myCmp(...) <= 0 )
            //tmp->ary[j++] = cv->ary[h1++];
            mov( tmpReg, eax );                     // restore eax
            mov( eax, esi );
            intmul( rsize, esi );
            add( (type Vec [ebx]).ary, esi );

            mov( j, edi );
            intmul( rsize, edi );
            add( (type Vec [edx]).ary, edi );

            mov( ecx, tmpReg );
            mov( rsize, ecx );
            rep.movsb(); // tmp->ary[j++] = cv->ary[h1++];
            mov( tmpReg, ecx );

            inc( eax ); // ++h1
            inc( j );

        else
            //tmp->ary[j++] = cv->ary[h2++];
            mov( tmpReg, eax );                     // restore eax

            mov( ecx, esi );
            intmul( rsize, esi );         // ecx holds h2
            add( (type Vec [ebx]).ary, esi );

            mov( j, edi );
            intmul( rsize, edi );
            add( (type Vec [edx]).ary, edi );

            mov( ecx, tmpReg );
            mov( rsize, ecx );
            rep.movsb(); // tmp->ary[j++] = cv->ary[h2++];
            mov( tmpReg, ecx );

            inc( ecx ); // ++h2
            inc( j );
           
        endif;

    endwhile;

   
    // Note: only one (at most) of the two 'while's' below is executed ...
   
    //while( h1 <= mid ) // copy any remaining entries (1st half)
        //tmp->ary[j++] = cv->ary[h1++];
    while( eax <= mid ) do
       
        mov( eax, esi );
        intmul( rsize, esi );
        add( (type Vec [ebx]).ary, esi );

        mov( j, edi );
        intmul( rsize, edi );
        add( (type Vec [edx]).ary, edi );

        mov( ecx, tmpReg );
        mov( rsize, ecx );
        rep.movsb(); // tmp->ary[j++] = cv->ary[h1++];
        mov( tmpReg, ecx );

        inc( eax ); // ++h1
        inc( j );
   
    endwhile;
           
         
    //while( h2 <= top ) // copy any remaining entries (2nd half)
        //tmp->ary[j++] = cv->ary[h2++];
       
    while( ecx <= top ) do 
     
        mov( ecx, esi );
        intmul( rsize, esi );
        add( (type Vec [ebx]).ary, esi );

        mov( j, edi );
        intmul( rsize, edi );
        add( (type Vec [edx]).ary, edi );

        mov( ecx, tmpReg );
        mov( rsize, ecx );
        rep.movsb(); // tmp->ary[j++] = cv->ary[h2++];
        mov( tmpReg, ecx );

        inc( ecx ); // ++h2
        inc( j );
       
    endwhile;
         

    //for( j = 0; j < sz; ++j ) // copy back sorted tmp vec...
        //cv->ary[bot+j] = tmp->ary[j];
 
    mov( (type Vec [edx]).ary, esi );
    mov( bot, edi );
    intmul( rsize, edi );
    add( (type Vec [ebx]).ary, edi );
    mov( sz, ecx );
    intmul( rsize, ecx );
    rep.movsb();
       
/*   
    for( mov( 0, ecx ); ecx < sz; inc( ecx ) ) do

        mov( ecx, esi );
        intmul( rsize, esi );
        add( (type Vec [edx]).ary, esi );
       
        mov( bot, eax );
        add( ecx, eax );
   
        mov( eax, edi );
        intmul( rsize, edi );
        add( (type Vec [ebx]).ary, edi );
       
        mov( ecx, tmpReg );
        mov( rsize, ecx );
        rep.movsb(); // cv->ary[bot+j] = tmp->ary[j];
        mov( tmpReg, ecx );
   
    endfor;
*/   

    //pop( edi ); pop( esi ); pop( ecx ); pop( eax );
end mergeVec;


procedure my_msortVec( pv: pVec in ebx; bot: int32; top: int32; ptmp: pVec in edx );
var
    mid:    int32; 
begin my_msortVec;
    mov( top, eax );
    if( bot < eax ) then
   
        add( bot, eax );    // NOW EAX HOLDS top + bot
        shr( 1, eax );      // divide by 2 ...
        mov( eax, mid );    // mid = ( bot + top ) / 2;
       
        my_msortVec( ebx, bot, mid, edx );  // sort the first half ...
        inc( mid );                         // now mid = mid +1
       
        my_msortVec( ebx, mid, top, edx );  // and the second half
       
        mergeVec( ebx, bot, top, edx );     // now merge 2 sorted chunks

    endif;
end my_msortVec;


procedure msortVec( var v: Vec );
var
    Rega:   dword;
    Regb:   dword;
    Regc:   dword;
    Regd:   dword;
    Regesi: dword;
    Regedi: dword;
    tmp:    Vec;
begin msortVec;
    mov( eax, Rega ); mov( ebx, Regb ); mov( ecx, Regc ); mov( edx, Regd );
    mov( esi, Regesi ); mov( edi, Regedi );
   
    mov( v, ebx );
    mov( (type Vec [ebx]).size, eax );
    if( eax > 1 ) then
   
        initVec( tmp, (type Vec [ebx]).elen );
        reserveVec( tmp, eax ); // Note: resets cap... BUT tmp size still 0
        dec( eax );
        lea( edx, tmp );        // get address of new tmp Vec into edx ...
        my_msortVec( ebx, 0, eax, edx );
       
        // NOW done ... BUT only free ary mem that held copies of pointers
        mem.free( tmp.ary );
       
    endif;
   
    mov( Regedi, edi ); mov( Regesi, esi );
    mov( Regd, edx ); mov( Regc, ecx ); mov( Regb, ebx ); mov( Rega, eax );
   
end msortVec;



// returns index if string present, else -1 ...
procedure findVec( var v: Vec; pr: dword ); @returns( "eax" );
begin findVec;
    push( ebx ); push( ecx ); push( esi ); push( edi );
   
    mov( v, ebx );
    for( mov( 0, ecx ); ecx < (type Vec [ebx]).size; inc( ecx ) ) do
        //if( myCmp(&cv->ary[i], r) == 0 )
        mov( ecx, esi );
        intmul( (type Vec [ebx]).elen, esi );
        add( (type Vec [ebx]).ary, esi );
        mov( pr, edi );
        pMyCmp( esi, edi ); // need an 'eq' cmp here ...
        breakif( eax );     // i.e. break if equal ...
    endfor;
   
    // now check loop end condition ...
    if( ecx != (type Vec [ebx]).size ) then
        mov( ecx, eax ); // found
    else
        mov( -1, eax ); // NOT found
    endif;
   
    pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end findVec;


// if int index valid, erases element there ...
procedure eraseVec( var v: Vec; index: int32 in eax );
var
    tmpReg: dword;
    rsize:  uns32;
begin eraseVec;
    push( ebx ); push( ecx ); push( esi ); push( edi );
   
    mov( v, ebx );
    mov( (type Vec [ebx]).elen, rsize );
    if( (type int32 eax) >= 0 && (type int32 eax) < (type Vec [ebx]).size ) then
       
        mov( eax, tmpReg );
        intmul( rsize, eax );
        add( (type Vec [ebx]).ary, eax );
        pFreeRec( eax ); // must pass in eax, needed here if dynamic memory
        mov( tmpReg, eax  );
     

        //if( index < cv->size-1 )
            //memcpy(&cv->ary[index], &cv->ary[index+1], (cv->size-1-index)*sizeof(Rec));
       
        mov( (type Vec [ebx]).size, ecx );
        dec( ecx ); // ecx holds size-1
        if( eax < ecx ) then
            sub( eax, ecx ); // ecx now holds (cv->size-1-index)
            intmul( rsize, ecx ); // ecx now holds bytes to copy ...
           
            mov( eax, edi );
            intmul( rsize, edi );
            add( (type Vec [ebx]).ary, edi );
            mov( edi, esi );
            add( rsize, esi );
            rep.movsb();
        endif;
       
        // now update size and return by reference (since address passed in)
        dec( (type Vec [ebx]).size );
           
    else
        mov( (type Vec [ebx]).size, ecx );
        dec( ecx ); // ecx holds size-1
        stdout.put( nl, "ERROR! Index ", index, " out of range 0..", (type int32 ecx), nl);
    endif;
   
    pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end eraseVec;


/*
void uniqueCvec( Cvec* cv, int (*myCmp) (const void* a, const void* b),
                 void (*freeVrec) (void* el) )
{
    int i = 0, j = 1;
    if( !isSortedCvec( cv, myCmp ) )
        msortCvec( cv, myCmp, freeVrec ); // first make sure, sorted order

    for( ; j < cv->size ; ++j )
    {
        if( myCmp( (cv->ary + i*cv->elen), (cv->ary + j*cv->elen) ) != 0 )
        {
            ++i;
            if( i != j ) memcpy( (cv->ary + cv->elen*i), (cv->ary + cv->elen*j),
                                  cv->elen );
        }
        else freeVrec( cv->ary + cv->elen*j );
    }

    cv->size = ++i;
}
*/


// Adapted from the above 'C' language algorithim ...
procedure xDuplicates( var v: Vec ); 
var
    k: uns32;
begin xDuplicates;
    push( ebx );
   
    mov( v, ebx );
    if( (type Vec [ebx]).size > 1 ) then // Note: need size > 1 to enter this block ...

        push( eax ); push( ecx ); push( esi ); push( edi );
       
        cld(); // re. rep.movsb() ...

        mov( 0, k );
        for( mov( 1, ecx ); ecx < (type Vec [ebx]).size; inc( ecx) ) do

            //mov( @size(MyContact), (type Vec [ebx]).elen ); // ......................debugging //

            mov( (type Vec [ebx]).elen, edi );
            intmul( k, edi ); 
            add( (type Vec [ebx]).ary, edi );
            // edi holds offset into LAST LOWER UNIQUE record compared

            mov( (type Vec [ebx]).elen, esi );
            stdout.put( "esi = ", esi, ", edi = ", edi, nl );
            intmul( ecx, esi );
            stdout.put( "esi = ", esi, ", edi = ", edi, nl );
            add( (type Vec [ebx]).ary, esi );
            // esi now holds offset into this 'i'

            //if (a[k] != a[i])
            //if( str.ine( MyBBook.theName[edi], MyBBook.theName[esi] ) ) then
           
            stdout.put( "esi = ", esi, ", edi = ", edi, nl );
            pMyCmp( esi, edi );
            if( !eax ) then // uses eax ...
                               
                inc( k ); // k=k+1; get ready for next pair of '(k,i)' to compare
                add( (type Vec [ebx]).elen, edi ); // now edi holds offset for (k+1)

                if( k != ecx ) then // i.e. if ( (k+1) != i ) then ...
                   
                    push( ecx ); // preserve ecx counter ...
                    //mov( MyBBook.theName[esi], MyBBook.theName[edi] ); // copy this new
                    //mov( MyBBook.thePhone[esi], MyBBook.thePhone[edi] );
                   
                    mov( (type Vec [ebx]).elen, ecx );
                    rep.movsb();
                   
                    pop( ecx );

                endif;           

            else    // a[k]=a[i], SO THEN goto next 'i', (i.e. ecx), but still same old 'k' though.
                    // ok to free this 'i' record since = the 'k' record below

                //str.free( MyBBook.thePhone[esi] ); // ok to free these strings               
                //str.free( MyBBook.theName[esi] );
                mov( esi, eax );
                pFreeRec( eax );

            endif;

        endfor;

        inc( k );
        mov( k, (type Vec [ebx]).size );

        pop( edi ); pop( esi ); pop( ecx ); pop( eax );

    endif;

    pop( ebx );
end xDuplicates;

David

The list2 pair:

"sllist2.hhf"

// sllist2.hhf // 2012-08-15 //

// using NNode to avoid conflict with Node
// using SLList to avoid conflict with List

#includeOnce( "stdlib.hhf" )


// NB!!! the record NNode must be defined before including this file
// ALSO  the type pNNode: pointer to NNode;


? @nodisplay:= true; // all procedures have @nodisplay 'as default' now

type
    NNode:  record
                nval:    dword;
                next:    pointer to NNode;
            endrecord;
           
    pNNode:   pointer to NNode;

type   
    SLList: record
                head:   pNNode;
                tail:   pNNode;
                size:   uns32;   
            endrecord;
static
    pFreeNNode: procedure( pn: pNNode in eax );
 
procedure initSLList( var lst: SLList );
begin initSLList;
    push( eax ); push( ebx );
    mov( lst, ebx );
    mov( 0, (type SLList [ebx]).head );
    mov( 0, (type SLList [ebx]).tail );
    mov( 0, (type SLList [ebx]).size );
    pop( ebx ); pop( eax );
end initSLList;


procedure push_frontSLList( var lst: SLList; var n: NNode );
begin push_frontSLList;
    //push( ebx ); push( ecx ); push( esi ); push( edi ); // caller must preserve if needed
   
    cld();
    mem.alloc( @size(NNode) );
   
    mov( eax, edi );
    mov( n, esi ); // get address of new NNode into esi ...

    // get num bytes to copy in ecx ...
    mov( @size(NNode), ecx );
    rep.movsb();
   
    // ok ... now prepend (copy) of new rec ...
    mov( lst, ebx );
    if( (type SLList [ebx]).size != 0 ) then // not empty
        mov( mov( (type SLList [ebx]).head, ecx ), (type NNode [eax]).next );
        mov( eax, (type SLList [ebx]).head );
    else
        mov( 0, (type NNode [eax]).next );
        mov( eax, (type SLList [ebx]).tail );
        mov( eax, (type SLList [ebx]).head );
    endif;
   
    inc( (type SLList [ebx]).size ); // update size
 
    //pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end push_frontSLList;

procedure push_backSLList( var lst: SLList; var n: NNode );
begin push_backSLList;
    //push( ebx ); push( ecx ); push( esi ); push( edi ); // caller must preserve if needed
   
    cld();
    mem.alloc( @size(NNode) );
   
    mov( eax, edi );
    mov( n, esi ); // get address of new Rec into esi ...

    // get num bytes to copy in ecx ...
    mov( @size(NNode), ecx );
    rep.movsb();
   
    mov( 0, (type NNode [eax]).next );
   
    // ok ... now append (copy) of new rec ...
    mov( lst, ebx );
    if( (type SLList [ebx]).size != 0 ) then // not empty
        mov( (type SLList [ebx]).tail, ecx );
        mov( eax, (type NNode [ecx]).next ); // now 'next' updated
        mov( eax, (type SLList [ebx]).tail );
    else
        mov( eax, (type SLList [ebx]).tail );
        mov( eax, (type SLList [ebx]).head );
    endif;
   
    inc( (type SLList [ebx]).size ); // update size
 
    //pop( edi ); pop( esi ); pop( ecx ); pop( ebx );
end push_backSLList;


procedure clearSLList( var v: SLList );
begin clearSLList;
    push( eax ); push( ebx ); push( ecx );
   
    mov( v, ebx );
    mov( (type SLList[ebx]).head, ecx );
    while(  ecx != 0 ) do
        mov( ecx, eax );
        mov( (type NNode [ecx]).next, ecx );
        pFreeNNode( eax );             
        mem.free( eax );
    endwhile;
   
   
    initSLList( (type SLList [ebx]) );
 
    pop( ecx ); pop( ebx ); pop( eax );
end clearSLList;



David

"sllist2_func's.hhf"

// sllist2_func's.hhf // // 2012-08-16 //


#includeOnce( "sllist2.hhf" )


type
    pSLList: pointer to SLList;


#if( !@defined( pMyCmp ) )
static
    pMyCmp: procedure( a: dword in esi; b: dword in edi ); @returns( "eax" );
#endif


// re. isort ... note: cmp func is a <= b (for list, ALSO same as for msort)
// re. msort ... note: cmp func is a <= b


// pass in the address of the SLList and the new Node to insert ...
procedure insert_sortedSLList( var lst: SLList; pn: pNNode in eax );
//var
    //head: pNNode; // esi
var
    reg32:  dword;
begin insert_sortedSLList;
    push( ebx ); push( esi ); push( edi );
   
    //pList head;
    //if( !isSortedSLList( list, myCmp )) msortSLList( list, myCmp );
    //head = list->start;
    mov( lst, ebx );
    mov( (type SLList [ebx]).head, esi );
    mov( eax , edi );
   
    // firstly, we handle most common case where 'n' is NOT the first Node
    //if( head != NULL && myCmp( n, head ) >= 0 ) // NEED >=
    if( esi && pMyCmp( esi, edi ) ) then
        // so search the linked list for the right location
        //pList cur = head;
        //while( cur->next != NULL && myCmp( cur->next, n ) <= 0 )
        mov( esi, reg32 );
        mov( (type NNode [esi]).next, esi );
        //while( esi && pmyCmp( esi, edi ) ) do
        forever
            if( !esi || !pMyCmp( esi, edi ) ) then
                //cur = cur->next;
                mov( reg32, esi );
                break;
            else
                mov( esi, reg32 );
                mov( (type NNode [esi]).next, esi );
            endif;
        endfor;
       
        //if( cur == list->end ) list->end = n;
        if( esi == (type SLList [ebx]).tail ) then
            mov( edi, (type SLList [ebx]).tail );
        endif;
       
        //n->next = cur->next;
        mov( (type NNode [esi]).next, (type NNode [edi]).next );
       
        //cur->next = n;
        mov( edi, (type NNode [esi]).next );
       
        // list->start = head; // unchanged //
        //++ list->size;
        inc( (type SLList [ebx]).size );
    else // if we reach here, this IS the first node ...
        //n->next = head; // so set this node in first position
        mov( (type SLList [ebx]).head, (type NNode [edi]).next );
        //list->start = n;
        mov( edi, (type SLList [ebx]).head );
        //++ list->size;
        inc( (type SLList [ebx]).size );
        if( (type SLList [ebx]).size == 1 ) then
            // list->end = n;
            mov( edi, (type SLList [ebx]).tail );
        endif;
    endif;
   
    pop( edi ); pop( esi ); pop( ebx );
end insert_sortedSLList;

//void isortSLList( SLList* list, int (*myCmp) (const pList r1, const pList r2) )
procedure isortSLList( var lst: SLList );
var
    nSLList:    SLList;
    //cur:        pNNode; // eax
    //nextNode:   pNNode; // ecx
begin isortSLList;
    push( eax ); push( ebx ); push( ecx );
   
    initSLList( nSLList );
    mov( lst, ebx );
    //for( cur = list->start; cur != NULL; cur = nextNode )
    for( mov( (type SLList [ebx]).head, eax ); eax; mov( ecx, eax ) ) do
        //nextNode = cur->next; // get copy here before next is changed below
        mov( (type NNode [eax]).next, ecx );
        //insert_sortedSLList( &nSLList, cur, myCmp ); // 'cur' already is a Node pointer
        insert_sortedSLList( nSLList, eax );
    endfor;
     
    // now ... update old list with new front and back ...
    //list->start = nSLList.start;
    mov( nSLList.head, (type SLList [ebx]).head );
    //list->end = nSLList.end;
    mov( nSLList.tail, (type SLList [ebx]).tail );
   
    // size hasn't changed 

    pop( ecx ); pop( ebx ); pop( eax );
end isortSLList;


// merge two sorted SLLists with heads 'a' and 'b' ... in sorted order
procedure mergeSLLists(var a: SLList; var b: SLList ); @returns( "eax" ); // new head
var
    newMergedHead:  dword;
    //sorted:         dword; // use ebx // using edx now ..
begin mergeSLLists;
    //push( ebx ); // using edx instead of ebx here now ...
    //push( esi ); push( edi );

    //pList sorted, new_merged_head;
    mov( a, eax );
    mov( (type SLList [eax]).head, esi );
    mov( b, eax  );
    mov( (type SLList [eax]).head, edi );
   
    //if( a->start == NULL ) return b->start;
    //if( b->start == NULL ) return a->start;
    if( !esi ) then mov( edi, newMergedHead );
    elseif( !edi ) then mov( esi, newMergedHead );
   
    else
        //if( myCmp(a->start, b->start) <= 0 )
        if( pMyCmp( esi, edi ) ) then
            //sorted = a->start;
            //a->start = a->start->next;
            mov( esi, edx );
            mov( (type NNode [esi]).next, esi );
        else
            //sorted = b->start;
            //b->start = b->start->next;
            mov( edi, edx );
            mov( (type NNode [edi]).next, edi );
        endif;
       
        //new_merged_head = sorted;
        mov( edx, newMergedHead );

        // now ...
        //while( a->start != NULL && b->start != NULL )
        while( esi && edi ) do
            //if( myCmp(a->start, b->start) <= 0 )
            if( pMyCmp( esi, edi ) ) then
                //sorted->next = a->start;
                mov( esi, (type NNode [edx]).next );
                //sorted = a->start;
                mov( esi, edx );
                //a->start = a->start->next;
                mov( (type NNode [esi]).next, esi );   
            else
                //sorted->next = b->start;
                mov( edi, (type NNode [edx]).next );
                //sorted = b->start;
                mov( edi, edx );
                //b->start = b->start->next;
                mov( (type NNode [edi]).next, edi );
            endif;
        endwhile;

        // and finally ...
        //if( a->start != NULL )
        if( esi ) then
            //sorted->next = a->start;
            mov( esi, (type NNode [edx]).next );
        //else if( b->start != NULL )
        elseif( edi ) then
            //sorted->next = b->start;
            mov( edi, (type NNode [edx]).next );
        endif;
   
    endif;

    mov( newMergedHead, eax );
   
    //pop( edi ); pop( esi );
    //pop( ebx );
end mergeSLLists;


// a recursive mergesort ...
procedure mergesortSLList( var lst: SLList );
var
    // cur:    pNNode; // ecx
    a:      SLList;
    b:      SLList;
begin mergesortSLList;
    push( ebx );
    //push( ecx ); push( edx );
    //pList cur = list->start;
    mov( lst, ebx );
    mov( (type SLList [ebx]).head, ecx );
    mov( ecx, a.head );
    //SLList a, b;

    // base case is a SLList of length 0 or 1 ...
    //if ((cur == NULL) || (cur->next == NULL))  return;
   
    if( a.head != 0 /*&& b.head != 0*/ ) then
        mov( (type NNode [ecx]).next, b.head );
        if( b.head != 0 ) then
            // split SLList into 'a' and 'b' sublists ...
            //a.start = cur;
            //b.start = cur->next; // both these lines done above
            while( b.head != 0 /*&& ( b.start->next != NULL)*/  ) do
                mov( b.head, edx );
                mov( (type NNode [edx]).next, edx );
                breakif( !edx );
               
                //cur = cur->next;
                mov( (type NNode [ecx]).next, ecx );
                //b.start = b.start->next->next;
                mov( (type NNode [edx]).next, b.head );
            endwhile;

            //b.start = cur->next;
            mov( (type NNode [ecx]).next, b.head );
           
            //cur->next = NULL; // SLList divided into 2 roughly equal parts now
            mov( 0, (type NNode [ecx]).next );

            // recursively sort the sublists ...
            mergesortSLList( a );
            mergesortSLList( b );

            // merge the two sorted SLLists together ...
            //list->start = mergeSLLists(&a, &b, myCmp);
            mergeSLLists( a, b ); // returns new merged head in eax
            mov( eax, (type SLList [ebx]).head );
        endif;
    endif;
    //pop( edx ); pop( ecx );
    pop( ebx );
end mergesortSLList;


procedure update_endSLList( var lst: SLList ); // after sort
begin update_endSLList;
    //push( ebx ); push( ecx );
   
    mov( lst, ebx );
    if( (type SLList [ebx]).size > 1 ) then
        //pList cur;
        //for( cur = list->start; cur->next != NULL; cur = cur->next ) ;
        //list->end = cur;
        //list->end->next = NULL; // this line NOT-needed/redundant
        mov( (type SLList [ebx]).head, ecx );
        while( (type NNode [ecx]).next != 0 ) do mov( (type NNode [ecx]).next, ecx ); endwhile;
        mov( ecx, (type SLList [ebx]).tail );
    endif;
   
    //pop( ecx ); pop( ebx );
end update_endSLList;


procedure msortSLList( var lst: SLList );
var
    Rega:   dword;
    Regb:   dword;
    Regc:   dword;
    Regd:   dword;
    Regesi: dword;
    Regedi: dword;
begin msortSLList;
    mov( eax, Rega ); mov( ebx, Regb ); mov( ecx, Regc ); mov( edx, Regd );
    mov( esi, Regesi ); mov( edi, Regedi );
   
    mov( lst, ebx );
    mergesortSLList( (type SLList [ebx]) );
    //mov( lst, ebx );
    update_endSLList( (type SLList [ebx]) );

    mov( Regedi, edi ); mov( Regesi, esi );
    mov( Regd, edx ); mov( Regc, ecx ); mov( Regb, ebx ); mov( Rega, eax );
end msortSLList;



// returns pointer if present, else returns 0
procedure findSLList( var lst: SLList; var nn: NNode ); @returns( "eax" );
begin findSLList;
    push( ebx ); push( esi ); push( edi );
   
   
    //pList p;
    mov( lst, ebx );
    mov( nn, edi );
    //for( p = cl->start; p != NULL; p = p->next )
    for( mov( (type SLList [ebx]).head, esi ); esi; mov( (type NNode [esi]).next, esi ) ) do
        //if( myCmp(p, pr) == 0 ) return p;
        pMyCmp( esi, edi ); // returns eax = 1 if found, else 0
        if( eax ) then
            mov( esi, eax );
            break;
        endif;
    endfor;
    // if not found above, eax is already 0
   
    pop( edi ); push( esi ); push( ebx );
end findSLList;


procedure eraseSLList( var lst: SLList;  pn: pNNode in eax ); // if pn valid, erase element there
//var
    //prev:   pNNode; // use edx
begin eraseSLList;
    push( ebx ); push( ecx ); push( edx );
   
    //pList prev = NULL, cur = cl->start;
    mov( 0, edx );
    mov( lst, ebx );
   
    for( mov( (type SLList [ebx]).head, ecx ); ecx && ecx != eax; mov( (type NNode [ecx]).next, ecx ) ) do
        //prev = cur;
        mov( ecx, edx );
    endfor;
   
    if( ecx ) then
       
        if( edx ) then // case of NOT FIRST element
            //prev->next = cur->next;
            mov( (type NNode [ecx]).next, (type NNode [edx]).next );
        else // case of IS FIRST element
            //cl->start = cl->start->next;
            mov( (type NNode [ecx]).next, (type SLList [ebx]).head );
        endif;
       
        //if( cur == cl->end ) cl->end = prev; /* case of IS LAST element*/
        if( ecx == (type SLList [ebx]).tail ) then
            mov( edx, (type SLList [ebx]).tail );
        endif;

        pFreeNNode( ecx ); // needed here for types like dynamic C strings, etc
        mem.free( ecx ); // free memory for dynamic Node ...

        // now update size and return (by reference, since address passed in)
        //-- cl->size;
        dec(  (type SLList [ebx]).size );
       
    else
        stdout.puts( nl "eraseSLList ERROR! Pointer NOT found ..." );
    endif;
   
    pop( edx ); pop( ecx ); pop( ebx );
end eraseSLList;



#if( !@defined( chrIsInStr ) )
    // returns *POSITION*  1..n  (POS = INDEX+1)  or  0 if NOT found //
    procedure chrIsInStr( c: char in al; s: string ); @returns( "eax" );
    begin chrIsInStr;
        str.chpos( s, al );
        inc( eax );
    end chrIsInStr;
#endif


procedure splitSLList( var sll: SLList; s: string; delims: string );
var
    myNNode:    NNode;
    reg32:      dword;
    i:          uns32;
begin splitSLList;
    push( eax ); push( ebx ); push( ecx ); push( edx );
   
    mov( sll, ebx );
    mov( s, ecx ); // ecx holds start address ...
   
    forever
   
        while( chrIsInStr( (type char [ecx]), delims ) ) do
            inc( ecx );
        endwhile;
       
        breakif( (type char [ecx]) == 0 ); // since 'empty' string
       
        mov( ecx, edx );
        inc( edx ); // edx holds address one past end of 'word' ...
       
        mov( (type char [edx]), al );
        while( al && !chrIsInStr( al, delims ) ) do
            inc( edx );
            mov( (type char [edx]), al );
        endwhile;
       
        mov( edx, reg32 );  // save a copy of 'end' in reg32 ...
        sub( ecx, edx );    // edx holds 'len of next word'
       
        mov( ecx, i );
        mov( s, eax );
        sub( eax, i );      // i is index to start
       
        str.a_substr( s, i, edx ); // start, len
        mov( eax, myNNode.nval );
       
        push_backSLList( (type SLList [ebx]), myNNode );
       
        mov( reg32, ecx );  // update (next) start pointer ecx ...
   
    endfor;
 
    pop( edx ); pop( ecx ); pop( ebx ); pop( eax );
end splitSLList;


procedure uniqueSLList( var lst: SLList );
begin uniqueSLList;
    push( eax ); push( ebx ); push( esi ); push( edi );

    //pList p = c->start;
    mov( lst, ebx );
    mov( (type SLList [ebx]).head, esi ); // esi holds cur NNode address
   
    if( esi ) then
        //while( p->next != NULL )
        while( (type NNode [esi]).next != 0 ) do
            //if( myCmp(p, p->next) == 0 )
            mov( (type NNode [esi]).next, edi );
            if( pMyCmp( esi, edi ) ) then // if equal then ...
                //pList tmp = p->next;
                mov( edi, eax ); // eax is tmp copy ...
               
                //p->next = p->next->next;
                mov( (type NNode [edi]).next, edi ); // now advance edi ...
                mov( edi, (type NNode [esi]).next );
               
                pFreeNNode( eax ); // needed here for types like dynamic Cstrings, etc
                mem.free( eax );
                //-- c->size;
                dec( (type SLList [ebx]).size );
            else
                //p = p->next;
                mov( edi, esi ); // mov esi up since not equal ...
            endif;
        endwhile;
   
        update_endSLList( (type SLList [ebx]) ) ; // update end ...
    endif;
   
    pop( edi ); pop( esi ); pop( ebx ); pop( eax );
end uniqueSLList;