Today, ladies and gentlemen, I present to you the most tedious, bullshit, pain-in-the-asshole x86 assembly program that I have ever written. It's basically a string manipulation clusterfuck--complete with a bubble sort algorithm. If you don't know about the string/array opcodes like rep, scas, stos, lods, movs, etc--this might be moderately educational.
Code: Select all
TITLE proj3.asm ;File name of program
LF equ 0ah ;ASCII 10 - newline character
CR equ 0dh ;ASCII 13 - carriage return character
BEEP equ 7h ;ASCII Beep code
NEWLINE equ CR,LF ;Combine CR and LF for carriage return
DOUBLESPC equ NEWLINE,LF ;Combine NEWLINE and LF for double space
MAXSTR equ 256d ;Accept up to 256 character string
MAXMSG equ 1000d
.586 ;Allow for Pentium instrucitons
.MODEL FLAT ;Memory model is FLAT (4GB)
INCLUDE io32.inc ;Include the 32 bit I/O procedures
include debugger.inc
.STACK 4096h ;4k hex bytes for stack
.DATA ;Data segment begins here
Intro BYTE 'This program allows the user to enter a list of integers fromt he keyboard.',NEWLINE,
'The program saves the integers in an array, sortst he integers in ascending',NEWLINE,
'order, allows for the addition or deletion of integers, and deletion of the',NEWLINE,
'entire array.',0
Menu BYTE DOUBLESPC,'[1] Enter a List of Integers',NEWLINE,
'[2] Display the List of Integers',NEWLINE,
'[3] Display the List Sorted in Ascending Order',NEWLINE,
'[4] Add an Integer to the List',NEWLINE,
'[5] Delete an Integer from the List',NEWLINE,
'[6] Delete the List of Integers',NEWLINE,
'[7] Quit',DOUBLESPC,
'Enter a number to select --> ',0
Invalid BYTE NEWLINE,'That is not a "valid" choice ... try again, please.',BEEP,0
InArray BYTE DOUBLESPC,'Enter up to 10 integers or [Enter] to quit.',DOUBLESPC,0
AddNumber BYTE DOUBLESPC,'Enter a number to add --> ',0
ArrayFull BYTE DOUBLESPC, 'The list is full, you cannot add without deleting an integer first.',0
Remove BYTE DOUBLESPC, 'Enter the number to remove --> ', 0
ConData BYTE DOUBLESPC, 'The list contains data. Choose [4] to add one integer.',0
Deleted BYTE DOUBLESPC, 'The list has been deleted.', 0
Empty BYTE DOUBLESPC, 'The list is empty, so there is nothing to delete.', 0
TheNum BYTE NEWLINE, 'The number ',0
Added BYTE ' has been added.',0
Rem BYTE ' has been deleted.',0
NotFound BYTE ' could not be found.',0
InvalidIn BYTE DOUBLESPC, 'Invalid Input!',0
String BYTE MAXSTR dup(0)
Array DWORD 10d dup(?)
SortedArray DWORD 10d dup(?)
Choice DWORD ?
Number DWORD 0h
Number2 DWORD 0h
ArrayCount DWORD 0d
TmpPtr DWORD ?
NextLine2 BYTE CR,LF,0
Message BYTE MAXMSG dup(0)
Spacer BYTE ' ',0
Period BYTE '.',0
;************************** Main Body of Program *****************************
; Given : Nothing
; Process: Main Body - calls procedures to do the processing
; Return : Nothing
;*****************************************************************************
.CODE ;executable section begins here
_main:
lea esi, Intro
call PrintString
NextMenu:
lea esi, Menu
call PrintString ;Prompt user for input
lea esi, Choice
call GetChar
.if Choice == '1'
call FillArray ;Fill array if 1
.elseif Choice == '2'
lea esi, Array
mov TmpPtr, esi ;Display array if 2
call ShowArray
.elseif Choice == '3'
call BubbleSort ;Display sorted array if 3
lea esi, SortedArray
mov TmpPtr, esi
call ShowArray
.elseif Choice == '4' ;Add integer if 4
call AddInteger
.elseif Choice == '5'
call DeleteInteger ;Delete integer if 5
.elseif Choice == '6'
call DeleteArray ;Delete entire array if 6
.elseif Choice == '7'
jmp quit ;Exit program if 7
.else
lea esi, Invalid ;Invalid input
call PrintString
.endif
jmp NextMenu ;Continue displaying menu until exit
quit: INVOKE ExitProcess, 0 ; exit with return code 0
;**************** Procedure to Fill "Array" with user input ********************
; Given : Nothing
; Process : Fills ArrayCount and Array in the data segment with user input.
; : Continues prompting until no input is entered, or invalid input
; : entered. 10 integer max.
; Return : Nothing
;******************************************************************************
FillArray PROC NEAR32
pushad ;Save the contents of all registers
pushfd ;
mov eax, ArrayCount
cmp eax, 0d ;Check if the array is empty
je @@start
lea esi, ConData ;Otherwise print error and exit
call PrintString
jmp AllDone
@@start:lea esi, InArray ;Prompt user
call PrintString
lea ebx, Array
mov ArrayCount,0 ;counter for number of elements
mov ecx,10d ;loop counter
looptop:
lea esi,String
call GetString
cmp String,0 ;No input--terminate
je AllDone
call ValidateInteger ;Check if current input is valid
cmp edx, 0d
je AllDone ;Exit if not
lea edi,Number
call Ascii2Int ;Convert string to integer
mov eax,Number
mov [ebx],eax ;Store integer in Array
add ebx,4d ;Increase array length
inc ArrayCount
loop looptop
AllDone:
popfd ;Restore the registers
popad ;
ret ;Return to Calling procedure
FillArray ENDP
;**************** Procedure to Display an Array *******************************
; Given : A pointer to the array to be displayed in TmpPtr
; Process : Loops through each array entry, calculates the number of spaces to print
; : and prints each element with corresponding index right justified
; Return : Nothing
;******************************************************************************
ShowArray PROC NEAR32
pushad ;Save the contents of all registers
pushfd ;
lea esi, NextLine2
call PrintString ;Print newline
mov ecx,ArrayCount ;Exit if array is empty
cmp ecx, 0d
je endShA
cld
@@loopTop:
lea esi,NextLine2 ;Print newline
call PrintString
mov esi,TmpPtr ;Load array
lodsd ;Load integer
mov Number,eax ;Store current integer
mov TmpPtr,esi ;Store current index
mov ebx, ArrayCount ;Load array size
inc ebx
sub ebx, ecx ;Determine what index we're on
mov [Number2], ebx ;Store index temporarily
lea esi, Number2
lea edi, String
call Int2Ascii ;Convert index to string
lea esi, String
call PrintString ;Print index
jmp strlen ;Figure out strlen of current element
space: lea esi, Spacer
call PrintString
loop space
pop ecx
lea esi,String
call PrintString
loop @@loopTop
jmp endShA
strlen:
lea esi, Number ;Load current element
lea edi, String
call Int2Ascii ;Convert to string
push ecx
mov ecx, -1d
lea edi, String ;Load string
xor al, al ;Scan for Null character
repne scasb
not ecx ;2s compliment ecx counter
dec ecx ;ecx is now strlen
mov eax, ecx
mov ecx, 12d ;12-strlen = number of spaces required
sub ecx, eax
mov ebx, ecx ;temporarily store ecx
pop ecx ;Check which loop iteration we're on
cmp ecx, 1d ;last iteration
push ecx ;restore ecx
mov ecx, ebx ;Restore spacer counter
ja space ;if we're not to the last iteration, proceed
mov ebx, ArrayCount
cmp ebx, 10d ;If we're at element 10 of the last iteration
jb space ;remove a space from the spacer counter
dec ecx
jmp space
endShA: popfd ;Restore the registers
popad ;
ret ;Return to Calling procedure
ShowArray ENDP
;**************** Procedure for Bubble Sort Algorithm**************************
; Given : Nothing
; Process : Takes "Array" in the Data segment, copies it over to "SortedArray"
; : then performs bubble sort algorithm to organize in ascending order.
; Return : Nothing
;******************************************************************************
BubbleSort PROC NEAR32
pushad
pushfd
cld
;COPY ARRAY
lea esi, Array
lea edi, SortedArray
mov ecx, 10d
rep movsd ;Copy Array to SortedArray
mov ecx, ArrayCount
cmp ecx, 0d ;Exit if array is empty
je @@end
@@loopOuter:
mov eax, ArrayCount ;Inner loop counter
dec eax ;loop to ArrayCount-1
@@loopInner:
cmp eax, 0d ;Check if we've looped to all elements
je @@endInner
lea esi, SortedArray ;Reset array pointer
push ecx ;save outer loop counter
mov ecx, eax
@@PtrLoop:
add esi, 4 ;Increment pointer to current index
loop @@PtrLoop
pop ecx ;Restore outer loop counter
push eax ;Store inner loop counter
lodsd ;Load current index
mov edi, eax ;edi holds index i
pop eax
dec eax ;decrement inner loop counter
push eax
sub esi, 8d ;move pointer to next number
lodsd
mov edx, eax ;edx holds index i-1
pop eax
cmp edi, edx ;if i < i-1
jae @@loopInner ;else continue
;OTHERWISE SWAP edi, edx
sub esi, 4
mov [esi], edi ;Swap the two entries
add esi, 4
mov [esi], edx
jmp @@loopInner ;Continue inner loop
@@endInner:
loop @@loopOuter ;Continue outer loop
@@end: popfd
popad
ret
BubbleSort ENDP
;**************** Procedure to Add an Integer to the Array ********************
; Given : Nothing
; Process : Prompts user for input (assuming array isn't full) then stores the
; : new value in the last position of "Array" in the data segment
; Return : Nothing
;******************************************************************************
AddInteger PROC NEAR32
pushad
pushfd
cld
mov edx, ArrayCount
cmp edx, 10d ;Exit if array is full
jge AddIError
lea esi, AddNumber
call PrintString ;Display prompt
lea esi, String ;Get user input
call GetString ;Validate input
call ValidateInteger
cmp edx, 0d
je AddIExit ;exit if they didn't enter anything
lea edi, Number
call Ascii2Int ;convert input to integer
mov eax, [Number]
mov ecx, ArrayCount ;Load counter
lea edi, Array ;Load array index
AddILoop:
add edi, 4 ;Apply our index offset manually since stosd won't do it for us. :(
loop AddILoop
stosd ;Store integer
inc ArrayCount
lea edi, Message
mov [Message], 0d
lea esi, TheNum
call Strcat ;Concatenate success message
lea esi, String
call Strcat
lea esi, Added
call Strcat
lea esi, Message
call PrintString ;Display success message
jmp AddIExit
AddIError:
lea esi, ArrayFull
call PrintString
AddIExit:
popfd
popad
ret
AddInteger ENDP
;**************** Procedure to Delete an Integer ******************************
; Given : Nothing
; Process : Prompts for user for a number. Scans "Array" for the number, then
; : removes it. Appropriate message is displayed if the Number was not
; : found in the array.
; Return : Nothing
;******************************************************************************
DeleteInteger PROC NEAR32
pushad
pushfd
cld
mov eax, ArrayCount ;Check to see if Array is empty
cmp eax, 0d
ja @@start ;Continue if not
lea esi, Empty
call PrintString ;Display error if it is empty
jmp @@end
@@start:
lea esi, Remove ;Prompt for an integer
call PrintString
lea esi, String
call GetString
call ValidateInteger ;Get and validate integer
cmp edx, 0d ;Exit if they entered nothing
je @@end
lea edi, Number
call Ascii2Int ;convert input from string to integer
mov eax, [Number]
lea edi, Array ;load destination array
mov ecx, ArrayCount ;load counter
repne scasd ;can for integer
cmp ecx, 0d ;If we found it before the end, continue
ja @@mid
sub edi, 4 ;Check to see if last index was a match
mov ebx, [edi]
cmp ebx, eax
je @@mid ;If so, continue
lea edi, Message
mov Message, 0d
lea esi, TheNum
call Strcat ;If not, the string wasn't found
lea esi, String
call Strcat
lea esi, NotFound
call Strcat
lea esi, Message ;Concatenate error message and exit
call PrintString
jmp @@end
@@mid: mov esi, edi ;Copy string to cover blank entry
sub edi, 4d
rep movsd
dec ArrayCount ;Decrease array length
lea edi, Message
mov Message, 0d
lea esi, TheNum ;Concatenate success message
call Strcat
lea esi, String
call Strcat
lea esi, Rem
call Strcat
lea esi, Message
call PrintString ;Display success message
@@end: popfd
popad
ret
DeleteInteger ENDP
;**************** Procedure to Delete Entire Array *******************************
; Given : Nothing
; Process : Checks to make sure the array isn't already empty, then clears it
; Return : Nothing
;******************************************************************************
DeleteArray PROC NEAR32
pushad
pushfd
cld
mov eax, ArrayCount ;Load array length
cmp eax, 0d
ja @@start
lea esi, Empty
call PrintString ;If array is empty, display error and exit
jmp @@end
@@start:mov ArrayCount, 0d
lea esi, Deleted ;Otherwise set length to 0 and exit
call PrintString
@@end: popfd
popad
ret
DeleteArray ENDP
;**************** Procedure to Concatenate Arrays *****************************
; Given : Source string in ESI. Destination String in EDI
; Process : Finds terminating null character at the end of ESI. Copies EDI over
; : until its null character is found.
; Return : Nothing
;******************************************************************************
Strcat PROC NEAR32
pushad
pushfd
cld
mov ecx, -1
xor al, al
repnz scasb ;Scan until terminating null character is found
dec edi ;Back up one byte
@@loop: lodsb ;load value from source
stosb ;store value in destination
test al, al
jnz @@loop ;continue until destination's NULL is hit
popfd
popad
ret
Strcat ENDP
;**************** Procedure to Validate an Integer ****************************
; Given : Nothing
; Process : Checks to see that the "String" array would become a valid integer
; : before calling Ascii2Int
; Return : Bool in EDX - 1 is valid; 0 is invalid
;******************************************************************************
ValidateInteger PROC NEAR32
push ecx
push eax
push esi
mov edx, 1d ;Set validity to true
lea eax, String ;Load string
@@loop: mov cl, BYTE PTR [eax] ;Loop every byte
cmp cl, 0h
je @@done ;Exit when NULL is reached
inc eax
cmp cl, 48d
jb @@inv ;Check for 0-9 ascii range
cmp cl, 57d
jbe @@loop
jmp @@inv
@@inv: mov edx, 0d ;Set validity to false
lea esi, InvalidIn
call PrintString ;Display error message if outside of range
@@done: pop esi
pop eax ;Restore all registers that were screwed with except EDX flag
pop ecx
ret
ValidateInteger ENDP
Public _main
END ;Code segment ends
]