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.

asked May 31, 2018 at 23:40
\$\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"
answered Jun 3, 2018 at 14:31
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.