1
\$\begingroup\$

Module 1:

;*******************************************************************************
; A generic Linked List ADT for any datatype.
;
; Author: William 

                .386 
                .MODEL  flat, stdcall
                .STACK  4096
                PUBLIC  push_front, print_list

; Win32 function prototypes and synonyms

GetProcessHeap  PROTO
HeapAlloc       PROTO,  hHeap:DWORD,
                        dwFlags:DWORD,
                        dwBytes:DWORD
GetStdHandle    PROTO,  nStdHandle:DWORD
WriteConsoleA   PROTO,  hConsoleOutput:DWORD,
                        lpBuffer:PTR DWORD,
                        nNumberofCharsToWrite:DWORD,
                        lpNumberOfCharsWritten:PTR DWORD,
                        lpReserved:DWORD

STD_OUTPUT_HANDLE       equ  -11
HEAP_ZERO_MEMORY        equ  00000008h  

                .CODE
;-------------------------------------------------------------------------------
; print_list(hptr, fptr)
; 
; Prints nodes of given linked list
;
; Entry:
  hptr          EQU     [ebp + 8]                       ;head ptr 
  fptr          EQU     DWORD PTR[ebp + 12]             ;std_call func ptr that 
;                                                        prints list data type
;-------------------------------------------------------------------------------
print_list:
                push    ebp
                mov     ebp, esp
                sub     esp, 12
                push    eax
                push    edx
                push    ebx

                ; Locals variables
arrow           EQU     [ebp - 4]                       ;string "-> " 
                mov     DWORD PTR arrow, '  >-' 

nstr            EQU     [ebp - 8]                       ;string "NULL"
                mov     DWORD PTR nstr, 'LLUN'  

wrtn            EQU     [ebp - 12]                      ;num of chars written
                mov     DWORD PTR wrtn, 0

                ; loop while(node != NULL)
                mov     ebx, hptr
                jmp     endL3
L3:             
                ; print (node->data)
                push    [ebx]                   
                call    fptr                                    

                ; print "-> "
                INVOKE  GetStdHandle, 
                        STD_OUTPUT_HANDLE               
                mov     edx, eax        
                INVOKE  WriteConsoleA, edx,
                                       ADDR  arrow,     
                                       3,
                                       ADDR  wrtn,
                                       0 

                ;node = node -> next    
                mov     ebx, [ebx + 4]

endL3:          test    ebx, ebx        
                jnz     L3

                ;print "NULL"
                INVOKE  GetStdHandle, 
                        STD_OUTPUT_HANDLE               
                mov     edx, eax        
                INVOKE  WriteConsoleA, edx,
                                       ADDR nstr,
                                       4,
                                       ADDR wrtn,
                                       0 

                pop     ebx
                pop     edx
                pop     eax
                mov     esp, ebp
                pop     ebp
                ret     8

;-------------------------------------------------------------------------------
; push_front(headptr, dataptr, dsiz)
; 
; Adds a new linked list node to the front of a linked list.
; 
; Entry:
  hptr          EQU     [ebp + 8]                       ;DW ptr to headptr
  dptr          EQU     [ebp + 12]                      ;DW data ptr
  dsiz          EQU     [ebp + 16]                      ;DW data size in bytes  
  node_size     EQU     00000008h
;-------------------------------------------------------------------------------
push_front:
                ; set up stack save regs
                push    ebp
                mov     ebp, esp
                push    eax
                push    ebx
                push    ecx
                push    edx
                push    esi

                ; allocate memory for new node
                call    GetProcessHeap
                INVOKE  HeapAlloc, eax,
                                   HEAP_ZERO_MEMORY, 
                                   node_size
                mov     ebx, eax                        ;save node in ebx

                ; allocate memory for new data
                call    GetProcessHeap
                INVOKE  HeapAlloc, eax,
                                   HEAP_ZERO_MEMORY, 
                                   dsiz

                ; save into new node
                mov     [ebx], eax                      ;node->data = new data
                mov     eax, hptr
                mov     eax, [eax]                      ;dereference hptr
                mov     [ebx + 4], eax                  ;node->next = hptr 

                ; loop copying data into newly allocated 
                ; memory byte per byte 
                mov     edx, dsiz
                mov     esi, dptr
                xor     ecx, ecx                        ;loop counter                   
                jmp     endL1
L1:             mov     al, [esi + ecx] 
                mov     [ebx + ecx], al
                inc     ecx
endL1:          cmp     ecx, edx
                jl      L1

                ; hptr = new node
                mov     eax, hptr
                mov     [eax], ebx

                ; restore regs clean up stack
                pop     esi
                pop     edx
                pop     ecx
                pop     ebx
                pop     eax
                pop     ebp
                ret     12

                END

Module 2

;*******************************************************************************
; LL_main.asm is the main module that demonstrates the Linked_List ADT
;
; Author: William 

                .386 
                .MODEL  flat, stdcall
                .STACK  4096
                EXTERN  push_front:near, 
                        print_list:near

; Win32 API function prototypes and synonyms
ExitProcess     PROTO,  dwExitCode:DWORD 
GetStdHandle    PROTO,  nStdHandle:DWORD
WriteConsoleA   PROTO,  hConsoleOutput:DWORD,
                        lpBuffer:PTR DWORD,
                        nNumberofCharsToWrite:DWORD,
                        lpNumberOfCharsWritten:PTR DWORD,
                        lpReserved:DWORD

STD_OUTPUT_HANDLE       EQU     -11
NULL                    EQU     0

                .CODE
;-------------------------------------------------------------------------------
; print_hex(num)
;
; prints a 32 bit unsigned hexadecimal number to the console
;
; Entry:
  num           EQU     [ebp + 8]                       ; DW unsigned hex number
;-------------------------------------------------------------------------------
print_hex:
                ; set up stack frame, save regs
                push    ebp
                mov     ebp, esp 
                sub     esp, 4
                push    eax
                push    ebx
                push    edx
                push    esi

                ; local variable for # of chars written by WriteConsoleA
nWrtn           EQU     [ebp - 4]
                mov     DWORD PTR nWrtn, 0                      

; This loop repeatedly divides the number by 10h, converts the remainder
; to its corresponding ASCII code, and then saves the char to the stack 
                mov     eax, num                
                mov     ebx, 10h                        ; the divisor, 10h
                xor     edx, edx                        ; clear edx
                xor     esi, esi                        ; esi = num of chars
L1:             div     ebx                             ; div num by 10h

                ;convert remainer to ASCII code
                ;if edx > 9, add 7
                cmp     edx, 9
                jle     endIf1
                add     edx, 7
endIf1:
                ;add char '0' to num
                add     edx, '0'

                ;put char on stack
                dec     esp                             ; allocate stack space 
                mov     [esp], dl                       ; save char to stack
                inc     esi                             ; inc num of chars              
                xor     edx, edx                        ; clear edx

                ; end of loop L1                
                test    eax, eax                        ; loop while num != 0   
                jnz     L1

                ; print out chars from the stack
                mov     edx, esp
                INVOKE  GetStdHandle, STD_OUTPUT_HANDLE 
                mov     ecx, eax
                INVOKE  WriteConsoleA, ecx,
                                       edx,
                                       esi,
                                       ADDR nWrtn,
                                       NULL

                add     esp, esi                        ; restore esp                   ; 

                ;clean up stack, restore regs
                pop     esi
                pop     edx
                pop     ebx
                pop     eax
                mov     esp, ebp
                pop     ebp
                ret     4

;-------------------------------------------------------------------------------
; main() 
;
; creates a linked list of integers and prints them to the screen.
;-------------------------------------------------------------------------------
main:
                mov     ebp, esp
                sub     esp, 8

                ; local variables
head_ptr        EQU     [ebp - 4]                       ; head of list
                mov     DWORD PTR head_ptr, 0

hex_int         EQU     [ebp - 8]                       ; a starting int
                mov     DWORD PTR hex_int, 10fd134h

                ; loop L2 adds 6 numbers to list
                xor     esi, esi                        ; loop counter
L2:                     
                ; add hex_int to front of the list
                push    4
                lea     eax, hex_int
                push    eax
                lea     eax, head_ptr
                push    eax 
                call    push_front

                inc     esi
                add     DWORD PTR hex_int, 14768h

                ; end of L2     
                cmp     esi, 6                          ; while (esi < 6)       
                jl      L2                      

                ; print out list
                push    print_hex
                push    head_ptr
                call    print_list

                mov     esp, ebp
                INVOKE  ExitProcess, 0
                END     main

Output:

116363C-> 114EED4-> 113A76C-> 1126004-> 111189C-> 10FD134-> NULL

This is a minimal implementation of a Linked List that took me a long time to figure out and get working. It only allows you to create a list, push to it, and print it out. There is no way to destroy the list, so the program leaks memory.

I'm looking for ALL criticism.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The print_hex procedure preserves almost all registers, yet you didn't preserve the ECX register that you nonetheless used in mov ecx, eax.

Maybe an idea to use pushad and popad:

num         EQU     [ebp + 36]
nWrtn       EQU     [ebp - 4]
;-----------------------------------------------
print_hex:
    ; set up stack frame, save regs
    pushad
    mov     ebp, esp
    ...
    mov     esp, ebp
    popad
    ret     4

Instead of initializing the local variable with sub esp, 4 mov DWORD PTR nWrtn, 0 you can simply write:

    xor     eax, eax
    push    eax

The hex conversion should not be done using division at all! Remember that dividing by 16 is just shifting 4 times to the right.
Next is an optimized version:

        mov     ebx, 'A' - 10
        mov     ecx, num
        xor     esi, esi      ; esi = num of chars
L1:     mov     eax, ecx
        and     eax, 15       ; Keep lowest nibble
        mov     edx, '0'
        cmp     eax, 9
        cmova   edx, ebx      ; Only for [10,15] will EDX become 55
        add     eax, edx      ; Either [0,9] + 48 or [10,15] + 55
        dec     esp           ; allocate stack space 
        mov     [esp], al     ; save char to stack
        inc     esi           ; inc num ofchars              
        shr     ecx, 4        ; loop while num != 0   
        jnz     L1

Allocating single bytes on the stack (any number of bytes from 1 to 8), leaves the stackpointer un-aligned! Always keep ESP dword aligned.

        mov     edx, esp
        and     esp, -4       ; Keep ESP dword aligned
        INVOKE  GetStdHandle, STD_OUTPUT_HANDLE

To restore ESP use EBP

        mov     esp, ebp
        popad
        ret     4

        xor     esi, esi     ; loop counter
L2:                     
        ...
        inc     esi
        cmp     esi, 6       ; while (esi < 6)       
        jl      L2

Since ESI holds a counter that will always be positive, I think using the unsigned conditional jump would be more appropriate.

        cmp     esi, 6         ; while (esi < 6)       
        jb      L2

Better still, count downward and shave off the cmp instruction. You can do this because the ESI value here isn't used for anything else than the loop count.

        mov     esi, 6
L2:
        ...
        dec     esi
        jnz     L2

The loop that copies memory a byte at a time can be simplified if the copying starts at the high end. And because it's a safe assumption that dsiz will always be positive non-zero you can code a Repeat-Until loop instead of a While loop.

; loop copying data into newly allocated memory byte per byte 
        mov     edx, dsiz      ; 4 is your example
        mov     esi, dptr
L1:     dec     edx
        mov     al, [esi + edx] 
        mov     [ebx + edx], al
        jnz     L1             ; Flags still set according to "dec edx"
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.