News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

PART I - Doubly Linked List

Started by Tight_Coder_Ex, May 09, 2011, 03:31:04 AM

Previous topic - Next topic

Tight_Coder_Ex

This segment describes the two associate structures and the first routine that initialzed parameter table

   LITEM STRUCTURE

    00  Status       dw            -1 If never used, NULL otherwise
    02  Tstamp       dd            Timestamp when entry was created
    06  Prev         dw            Pointer to node previous to this one alpha-numerically
    08  Next         dw               "     "   "  next      "   "   "     "      "
    0A  Chars        dw            Length of entry excluding null
    0C  Offset       dd            This entries beginning position in TxtPntr
   
  = 10  <16> Bytes

  Reserved parts of structure for future expansion and to padd structure to 128 bytes
 
   LPARAM STRUCTURE

     00  Status      dw            Bit 0 = 1 Intialized ready for use
     02  Tstamp      dd            TimeStamp of lists creation

     06  Reserved    db (10)

     10  MaxItems    dw            Maximum allowable entries (expands dynamically in LPOST)
     12  Items       dw            Current number of items
     14  CurPntr     dw            Item currently being added, deleted or edited.
     16  ItemPntr    dd            Pointer to array of ITEMS

     1A  Reserved    db (06)

     20  MaxText     dd            Maximum size of ASCII buffer
     24  TxtSize     dd            Space already used in buffer (Next avaliable position)
     28  TxtPntr     dd            Pointer to ASCII buffer

     2C  Reserved    dd

     30  BasePntr    dw (28)       Array of pointer to first entry of that character
                                   (00) First entry less than letter "A"
                                   (01) - (26) "A" - "Z"
                                   (27) First entry greater than letter "Z"

     68  Reserved    dd (04)

  When bit 2 of Status is on, these values are pointers to parent (FileNme) or decendant (FileHandle)
 
     78  FileNme     dd            ASCII pointer to filename associated with list
     7C  FileHandle  dd            Handle to file that was opened
     
   = 80  <128> Bytes


LCREATE    ENTRY   EAX Bits 31 - 08 = Initial number of elements required
                            07 - 00 = Average size of each entry
                   EBX = Pointer LPARAM structure
                   EDX = Pointer to ASCII description of string
                   
          RETURN   EAX = NULL if error, otherwise datestamp list was created


                CPU     P4

        LMEM_FIXED              equ      0h
        LMEM_ZEROINIT           equ     40h

        NULL                    equ     0
        MB_OK                   equ     0
        MB_APPLMODAL            equ     0

        extern _GetSystemTime@4, _SystemTimeToFileTime@8, _LocalAlloc@8, _MessageBoxA@16
        extern _LocalFree@4

        global  LCreate

        extern  TStamp

                section .data

  ErrCaption    db      'MEMORY ALLOCATION', 0
  ErrText       db      'Cannot allocate memory in LCreate', 0

                section .text
; ==========================================================================================================
;       ENTRY:  ECX Bits 00 - 07 = Average size of each entry
;                        24 - 08 = Initial number of nodes
;               EDX = Pointer to lists description string

;      RETURN:  EAX = NULL if process failed, otherwise time stamp returned by TStamp
; ----------------------------------------------------------------------------------------------------------

LCreate         push    esi
                push    edi

                push    ebp
                mov     ebp, esp                        ; Create empty procedure frame

        ; Setup stack with the 4 parameters required for error reporting

                push    MB_OK | MB_APPLMODAL
                push    ErrText
                push    ErrCaption
                push    NULL

        ; Populate stack with local variables passed by caller

                push    edx                             ; Arg3: Pointer to lists description
                mov     eax, ecx
                mov      al,  cl                        ; Avg size of each entry
                shr     eax, 8
                push    eax                             ; Arg2: Initial number of nodes in list
                imul    ecx
                push    eax                             ; Arg1: Initial size of Ascii buffer

        ; As parameter table can be in stack, it needs be initialized to known values

                xor     eax, eax
                mov     ecx, eax
                mov      cl, 48                         ; Size of LParam structure
                mov     edi, ebx
                rep     stosb                           ; Nulls to LParam.BasePntr
                dec     eax
                mov      cl, 56
                rep     stosb                           ; Fill LParam.BasePntr's with -1's
                inc     eax
                mov      cl, 24
                rep     stosb                           ; Fill remainder of table

        ; Alloc memory for ASCII buffer.  Arg2 is already on stack to LocalAlloc

                push    LMEM_ZEROINIT | LMEM_FIXED
                call    _LocalAlloc@8
                test    eax, eax
                jnz     .LC01

                add     esp, 8                          ; Align stack appropriately
                jmp     .Error

        .LC01   mov     [ebx+40], eax                   ; Save TxtPntr
                sub     esp, 4
                pop     dword [ebx+32]                  ; Save MaxText

                mov     eax, [esp]                      ; Get initial number of nodes
                mov     [ebx+16], ax                    ; Store in LParam.MaxItems
                shl     eax, 4                          ; Shift as each nodes parameters are 16 bytes
                push    eax
                push    LMEM_ZEROINIT | LMEM_FIXED
                call    _LocalAlloc@8                   ; Get pointer
                test    eax, eax
                pop     ecx                             ; Restore max item count
                jnz     .LC02

                push    dword [ebx+40]                  ; Get pointer to ASCII buffer
                call    _LocalFree@4
                pop     edx                             ; Waste last argument left on stack
                jmp     .Error

        .LC02   mov     [ebx+22], eax                   ; Store LParam.ItemPntr
                mov     edi, eax                        ; Set pointer for block moves
                xor     eax, eax
                dec     eax                             ; Set fill pattern

        .LC03   stosw                                   ; Set nodes status bits, (all on = never used]
                add     edi, 4                          ; Bump to Prev & Next positions
                stosd
                add     edi, 6                          ; Point to beginning of next node
                loop    .LC03 

                pop     edi                             ; Restore pointer to lists description
                mov     esi, edi
                mov     eax, ecx
                dec     ecx
                repnz   scasb                           ; Determine strings length
                mov     edi, [ebx+40]                   ; Get pointer to list ASCII buffer again
                inc     ecx
                neg     ecx                             ; ECX = Strings length include terminator
                mov     [ebx+36], ecx                   ; Store LParam.TxtSize
                rep     movsb                           ; Copy string to destination
                call    TStamp
                mov     dword [ebx+2], eax              ; Save date stamp of lists creation 
                inc     byte [ebx]                      ; Bump status bit 0 = 1 Table initialized and ready
                jmp     .Done

        .Error  call    _MessageBoxA@16
                xor     eax, eax

        .Done   leave                                   ; Kill procedure frame
                pop     edi                             ; Restore index registers
                pop     esi 
                ret


ABH = 171 bytes

hutch--

Looks good TC, it would be helpful to many folks if you can post a working example a bit later.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Tight_Coder_Ex

As a request from some one, they would like a copy of this they can maintain in ML and it's design be STD call compliant so procedures can be called from "C".


; ==========================================================================================================
LL_ITEMS        STRUCT
        Status          dw      ?               ; FF = Node never used
                                                ; When Bit3 of LL_PARAM status, then this associate node #
        Posted          dd      ?               ; Elapsed time in seconds since 1970-01-01
          Prev          dw      ?               ; Pointer to previous node number
          Next          dw      ?               ; Pointer to next node
         Chars          dw      ?               ; Number of characters in string
          Pntr          dd      ?               ; Offset in LL_PARAM.TxtPntr
LL_ITEMS        ENDS

; ===========================================================================================================
LL_PARAM        STRUCT

            Status      dw      ?               ; All bits ON = Empty, never used
                                                ; Bit 01 = List is active, ok to use
                                                ; Bit 02 = Data has changed since last load
           Created      dd      ?               ; Number of seconds since 1970-01-01

                        align 16
          MaxItems      dw      ?               ; Current space allocated to nodes
             Items      dw      ?               ; Number of items already in list
           CurPntr      dw      ?               ; Pointer to which is active node
          ItemPntr      dd      ?               ; Pointer to table

                        align 16
           MaxText      dd      ?               ; Size of ASCII buffer
           TxtSize      dd      ?               ; How much of it is already used
           TxtPntr      dd      ?               ; Pointer to buffer

                        align 16

        ; Base Pointer is an array of 28 words that points to the first element matching that letter

          BasePntr      dw      28      dup (?) ; Index  0 = Anything less than the letter 'A'
                                                ;        1 <> 26  'A' through 'Z'
                                                ;       27 = Anything greater than 'Z'

        ; Whenever one or the other is specified it is the pointer to that lists LL_PARAM
        ; When A parent record is deleted, it will delete all decendants
        ;      Only when all decendants are deleted, will the parent be deleted.

          Parent        dd      ?               ; Many of these are related to one of parent
           Child        dd      ?               ; One of these is related to many of those

            Padd        dd      ?, ?            ; Res'vd for future use and to padd structure to 128 bytes

           FileNme      dd      ?               ; Pointer to ASCII filename
        FileHandle      dd      ?               ; Handle that is currently opened to this list

LL_PARAM ENDS
; ----------------------------------------------------------------------------------------------------------

        DELIMITER       equ     '|'
        CHAR_MASK       equ     5FH
             NULL       equ     0


One of the major changes I made here was to remove error reporting.  It will be the responsibility of the calling process to do whatever is required as even if this fails
it doesn't necessarily necessitate termination of application, whether it be CONSOLE or GUI.


                .586P
                .model flat, stdcall
                 option casemap:none

        include         LStructs.inc

                .nolist
        include         windows.inc
        include         kernel32.inc
        include         user32.inc
        include         msvcrt.inc
                .list

        includelib      kernel32.lib
        includelib      user32.lib
        includelib      msvcrt.lib

                .code
; ==========================================================================================================

;       ENTRY:  Arg1 > AL = Average number of bytes / entry
;                    > Bits 31 - 08 Initial number of nodes
;               Arg2 > Pointer to ASCII string that describes lists contents
;               Arg3 > Pointer to LL_PARAM

;     RETURNS:  EAX = NULL if either memory region could not be created
;                   = TimeStamp if procedure succeeded, in otherwords !NULL.

; ----------------------------------------------------------------------------------------------------------

  LCreate       proc    uses    esi edi ebx             Sizes:DWORD, ListDesc:DWORD, Param_Table:DWORD

        ; Initialize LPARAM buffer to initial values

                xor     ecx, ecx
                mov      cl, sizeof LL_PARAM            ; Size of structure
                xor     eax, eax                        ; Fill pattern
                mov     edi, ebx                        ; Pointer to buffer
                shr     ecx, 2                          ; Adjust size to represent number of dwords
                rep     stosd
                lea     edi, [ebx+LL_PARAM.BasePntr]
                mov      cl, sizeof LL_PARAM.BasePntr
                shr     ecx, 2                          ; Adjust for number of dwords
                dec     eax                             ; Fill pattern to -1's
                rep     stosd

        ; Calculate buffer sizes based on values passed in Sizes

                mov     eax, Sizes                      ; Get values passed by caller
                xor     ecx, ecx
                mov      cl,  al                        ; ECX = Average size of each entry
                shr     eax, 8                          ; Shift initial node counter
                push    eax                             ; Will need this when nodes are created

        ; ASCII buffer is the larger of the two most often, so we will create it first.

                imul    ecx                             ; Determine intial number of bytes
                push    eax
                push    LMEM_FIXED or LMEM_ZEROINIT
                call    LocalAlloc                      ; Get pointer to memory

        ASSUME  ebx : PTR LL_PARAM

                or      eax, eax                        ; Did API return a pointer
                jz      Err

                mov     ebx, Param_Table                ; Establish pointer to LL_PARAM
                mov     [ebx].TxtPntr, eax              ; Save pointer
                mov     edi, eax
                mov     eax, [esp - 4]                  ; Get size of buffer
                mov     [ebx].MaxText, eax              ; Save size of buffer

        ; Now we can move description string to destination.  lstrlen & lstrcpy could have been used
        ; here, but after cleaning up stack, this uses just a little more space

                push    edi                             ; Save destination pointer
                xor     ecx, ecx
                mov      al,  cl
                mov     edi, ListDesc                   ; Pointer to description passed by caller
                dec     ecx                             ; ECX = -1 so str length can be calculated
                push    edi                             ; Weill need this pointer again
                repnz   scasb                           ; Find EOS
                not     ecx                             ; String length including terminator
                mov     [ebx].TxtSize, ecx              ; Save in talbe
                pop     esi
                pop     edi
                dec     ecx                             ; Restore pointers and transfer string
                rep     movsb

        ; Nodes table can be created now

                mov     eax, [esp]                      ; Get initial number of nodes calculated earlier
                mov     [ebx].MaxItems, ax
                imul    eax, sizeof LL_ITEMS
                push    eax
                push    LMEM_FIXED or LMEM_ZEROINIT
                call    LocalAlloc

        .if     eax
                mov     [ebx].ItemPntr, eax             ; Save pointer
                mov     edi, eax
                pop     ecx                             ; Get number of nodes again
                xor     eax, eax
                mov     edx, eax
                dec     eax                             ; Establish fill pattern
                mov      dl, sizeof LL_ITEMS

        ; Loop through each of the nodes, initializing status, prev & Next values to -1's

        @@:     mov     [edi], ax                       ; Save status
                mov     dword ptr [edi+LL_ITEMS.Prev], eax
                add     edi, edx
                loop    @B

        ; Only two fields left to initialize and that is datestamp & status

                inc     byte ptr [ebx]                  ; Set status to table active
                lea     eax, [ebx+LL_ITEMS.Posted]
                push    eax
                call    crt_time                        ; Number of seconds since 1970-01-01

        ; Allocating space for nodes failed, so we need to recover memory from ASCII buffer

        .else
                push    dword ptr [ebx].TxtPntr         ; Get address of ASCII buffer
                call    LocalFree                       ; recover memory
        Err:    xor     eax, eax                        ; return NULL status
        .endif

        ASSUME  ebx : NOTHING

                ret

  LCreate       endp
                end

Tight_Coder_Ex

The way I do things in NASM just got me in trouble


                inc     byte ptr [ebx]                  ; Set status to table active
                lea     eax, [ebx+LL_ITEMS.Posted]
                push    eax
                call    crt_time                        ; Number of seconds since 1970-01-01

Needs           add     esp, 4                          ; So register are restore properly in epilogue



Note how in NASM code I preserve registers before procedure frame that way stack need not be aligned upon completion.