349 lines
9.4 KiB
NASM
349 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
|