MASM-Low-Level-IO/MASM-Low_Level_IO.asm

348 lines
9.4 KiB
NASM

TITLE Project 6 - String Primitives and Macros (MASM_Low-Level-IO.asm)
; Author: Andrew Scott
; Last Modified: 2022-03-13
; Course number/section: CS271 Section 400
; Project Number: 6 Due Date: 2022-03-13
; Description: Program asks for 10 signed integers from the user as strings.
; The strings are converted from ASCII to SDWORDs and stored
; in an array. Then, their sum, and the truncated average are
; calculated. After the calculations all numbers entered by
; the user as well as the results are converted back to their
; ASCII representations and printed as strings.
INCLUDE Irvine32.inc
; name: mGetString
; Gets a value from the user
; preconditions: global variables are passed by reference
; receives: global variables userPrompt, userInput, userInputLen
; returns: stores user input in userInput
mGetString MACRO prompt:REQ, inString:REQ, stringLen:REQ
push EAX
push ECX
push EDX
push EDI
mov EDX, prompt
call WriteString
mov EDX, inString
mov ECX, MAXSIZE
call ReadString
mov EDI, stringLen
mov [EDI], EAX
pop EDI
pop EDX
pop ECX
pop EAX
ENDM
; name: mDisplayString
; Prints a string to the output
; preconditions: strings must be passed by reference
; receives: outString - offset of string to be written to output
; returns: prints outString
mDisplayString MACRO outString:REQ
push EDX
mov EDX, outString
call WriteString
pop EDX
ENDM
MAXSIZE = 500
.data
projectTitle BYTE "Project 6 - String Primitives and Macros, by Andrew Scott",13,10,0
description1 BYTE "You will be prompted to enter 10 signed decimal integers. Each integer must fit into a 32-bit register.",13,10,0
description2 BYTE "After you've entered 10 valid integers this program will display the integers, their sum, and their truncated average",13,10,13,10,0
userPrompt BYTE ". Please enter a signed number: ",0
invalidInput BYTE "ERROR: You did not enter a signed number or your number was too big. Please Try again.",13,10,0
listLabel BYTE 13,10,"You entered the following numbers:",13,10,0
sumLabel BYTE 13,10,"The sum of these numbers is: ",0
avgLabel BYTE 13,10,"The truncated average of these numbers is: ",0
retryPrompt BYTE "ERROR: Input was not a signed number or was too big. Please try again.",13,10,13,10,0
space BYTE " ",0
ecOption BYTE "**EC1: Number each line of user input and display a running subtotal of the valid numbers.",13,10,13,10,0
userInput BYTE MAXSIZE DUP(?)
userOutput BYTE MAXSIZE DUP(?)
userInputLen DWORD 0
userInputNum SDWORD 0
inputIsValid DWORD 0
userArray SDWORD 10 DUP(0)
userArrayLen DWORD LENGTHOF userArray
userArraySum SDWORD 0
userArrayAvg SDWORD 0
inputCount DWORD 1
.code
main PROC
; introduce the program
push OFFSET projectTitle
push OFFSET ecOption
push OFFSET description1
push OFFSET description2
call introduction
; prepare to get values from user
mov ECX, userArrayLen
mov EDI, OFFSET userArray
; get signed integers from user and store them
_fillArray:
push OFFSET space
push OFFSET userOutput
push OFFSET inputCount
push OFFSET retryPrompt
push OFFSET userPrompt
push OFFSET userInput
push OFFSET userInputLen
push OFFSET userInputNum
call ReadVal ; get user input
inc inputCount
mov EAX, userInputNum
mov [EDI], EAX ; store user input
mov EAX, 0
add EDI, 4
loop _fillArray
; calculate the sum
mov ECX, userArrayLen ; prepare registers
mov ESI, OFFSET userArray
mov userArraySum, 0
_calcSum:
mov EAX, userArraySum ; begin addition
mov EBX, [ESI]
add EAX, EBX
mov userArraySum, EAX
add ESI, 4
mov EAX, 0
loop _calcSum
; find average
mov EAX, userArraySum
cdq
idiv userArrayLen
mov userArrayAvg, EAX
; display all nums entered by user
mov ECX, userArrayLen ; prepare registers and display label
mov EDI, OFFSET userArray
mov EDX, OFFSET listLabel
mDisplayString EDX
_displayNums: ; begin loop to print nums in userArray
push OFFSET space
push OFFSET userOutput
push EDI
call WriteVal
add EDI, 4
loop _displayNums
call CrLf
; display sum
push OFFSET sumLabel
push OFFSET userOutput
push OFFSET userArraySum
call WriteVal
call CrLf
; display truncated average
push OFFSET avgLabel
push OFFSET userOutput
push OFFSET userArrayAvg
call WriteVal
call CrLf
Invoke ExitProcess,0 ; exit to operating system
main ENDP
; name: introduction
; Introduces the program
; preconditions: global variables projectTitle, description1, and description2 are strings
; postconditions: none
; receives: global variables projectTitle, description1, and description2 from the stack
; returns: prints the program intro
introduction PROC
push EBP
mov EBP, ESP
mDisplayString [EBP+20]
mDisplayString [EBP+16]
mDisplayString [EBP+12]
mDisplayString [EBP+8]
pop EBP
ret 16
introduction ENDP
; name: ReadVal
; Prompts the user to input a string of digits, validates the input, converts it
; to a signed int, then stores it. Uses local variable isNegative as custom
; sign flag and convertedInt as temporary storage for the integer after conversion.
; preconditions: retryPrompt, userPrompt, userInput are strings and global variables
; userInputNum is a DWORD global variable
; postconditions: none
; receives: global variables retryPrompt, userPrompt, userInput, userInputNum passed on stack by reference
; returns: signed integer stored in userInputNum
ReadVal PROC
;push EBP
;mov EBP, ESP
local isNegative:DWORD, convertedInt:SDWORD
push EAX ; preserve registers
push EBX
push ECX
push EDX
push ESI
_tryAgain:
; prepend line number to prompt
mov EDX, [EBP+36]
mov EBX, [EBP+32]
mov EAX, [EBP+28]
push EDX
push EBX
push EAX
call WriteVal
; get input from user
mGetString [EBP+20], [EBP+16], [EBP+12]
mov ESI, [EBP+16] ; prepare isNegative, ESI and ECX for _isNumeric loop
mov ECX, [EBP+12]
mov ECX, [ECX]
mov isNegative, 0
cld
_isNumeric:
; begin loop to check sign and validate input
lodsb
cmp AL, 43
je _positive ; check sign
cmp AL, 45
je _negative
cmp AL, 48 ; check input is numeric
jl _invalidNum
cmp AL, 57
jg _invalidNum
jmp _validNum
_positive:
mov isNegative, 0 ; make sure isNegative is clear
jmp _validNum
_negative:
mov isNegative, 1 ; set isNegative
_validNum:
loop _isNumeric
jmp _continue ; continue to conversion after confirming input is numeric
_invalidNum:
mDisplayString [EBP+24] ; display error if input is invalid and jump to re-prompt
jmp _tryAgain
_continue:
; prepare registers, local, and counter (ECX) for conversion loop
mov convertedInt, 0
mov ESI, [EBP+16]
mov EAX, 0
mov EBX, 10
mov EDX, 0
mov ECX, [EBP+12]
mov ECX, [ECX]
cld
_convert:
; begin convert loop
lodsb
cmp AL, 43 ; compare first character to +/- and skip if present
je _skipSign
cmp AL, 45
je _skipSign
sub AL, 48 ; begin conversion to SDWORD
push EAX
mov EAX, convertedInt
imul EBX
mov convertedInt, EAX
pop EAX
jo _invalidNum ; check for overflow before proceeding, jump to error and re-prompt if OV flag = 1
cmp isNegative, 1 ; negate if isNegative is set
je _convertToNeg
_add:
add convertedInt, EAX ; add current value to convertedInt
jo _invalidNum
mov EAX, 0 ; reset EAX to 0
_skipSign:
loop _convert
jmp _storeValue ; jump to saving value to global variable after convert loop complete
_convertToNeg:
neg EAX
jmp _add
_storeValue: ; store value in variable userInputNum by reference
mov EDX, [EBP+8]
mov EAX, convertedInt
mov [EDX], EAX
pop ESI ; restore registers and return
pop EDX
pop ECX
pop EBX
pop EAX
ret 32
ReadVal ENDP
; name: WriteVal
; Converts SDWORD input to ASCII and prints the data with it's corresponding label
; preconditions: global variables have been declared for the data label and SDWORD.
; Uses local variables remainder and quotient to hold results of division.
; postconditions: none
; receives: label and data global variables passed on stack
; returns: prints the data and label to the output
WriteVal PROC
local remainder:DWORD, quotient:DWORD
push EAX ; preserve registers
push EBX
push ECX
push EDX
push ESI
push EDI
; prepare local vars and registers for conversion and array filling
mov remainder, 0
mov quotient, 0
mov EBX, 10
mov ECX, 0
mov ESI, [EBP+8]
mov EAX, [ESI]
mov EDI, [EBP+12]
cld
cmp EAX, 0
jl _negative ; check for negative input
_convert:
; begin conversion from SDWORD to string
cdq
idiv EBX
mov quotient, EAX
mov remainder, EDX
add remainder, 48
mov EAX, remainder
push EAX ; save value to add later
inc ECX ; increment counter for adding to byte array later
mov EAX, quotient
cmp EAX, 0
jne _convert ; keep looping until quotient = 0
jmp _fillByteArray
_negative:
mov AL, 45 ; add '-' (ASCII 45) as first byte if negative
stosb
mov EAX, [ESI]
neg EAX ; reverse sign before continuing to _convert
jmp _convert
_fillByteArray:
; begin filling array
pop EAX ; pop values pushed during conversion
stosb ; store popped values in array
loop _fillByteArray
mov EAX, 0 ; terminate string
mov [EDI], EAX
; invoke mDisplayString to print byte array
mDisplayString [EBP+16]
mDisplayString [EBP+12]
; restore registers and return
pop EDI
pop ESI
pop EDX
pop ECX
pop EBX
pop EAX
ret 12
WriteVal ENDP
END main