I'm working on an assembly language program using MASM and the Irvine32 library for a class project. The program is supposed to:
However, when I run the program, the output is incorrect. Instead of displaying the reversed list of temperatures, it only outputs +67,
and then terminates.
Output I'm Getting:
Enter the name of the file to be read: C:/CS271/Project6/Temps090124.txt
Here's the corrected temperature order!
+67,
Hope that helps resolve the issue, goodbye!
Press any key to continue...
Expected Output:
I expect the program to display all the temperatures from the file in reverse order, something like:
Enter the name of the file to be read: C:/CS271/Project6/Temps090124.txt
Here's the corrected temperature order!
-1,+2,+5,+10,+15,+20,+25,+30,+35,+40,+45,+42,+38,+34,+30,+25,+20,+15,+10,+7,+3,+0,-2,-3,
Hope that helps resolve the issue, goodbye!
Press any key to continue...
What I've Tried:
Str_Trim
Calls: Ensured that the parameters for Str_Trim
are pushed onto the stack in the correct order (character to remove first, then the address of the string).\
) instead of forward slashes (/
) in the file path.userFilePath
, fileHandle
, bytesRead
, readBuffer
, numTempsParsed
, and the contents of tempArray
.Temps090124.txt
to ensure it contains the correct comma-separated temperature values without any extra characters or whitespace.Irvine32.inc
and associated library files are correctly set up in my development environment.ParseTempsFromString
procedure to confirm that it correctly parses the temperatures.Despite these efforts, the program still outputs only +67,
. I suspect there might be an issue with how the file is being read or how the data is being parsed and stored.
My Code:
Here is the full code of my program:
TITLE String Primitives and Macros (Proj6_brooksc3.asm)
; Author: Cameron Brooks
; Last Modified: 11/24/2024
; Description: This program reads temperature measurements from a specified
; text file, parses the temperatures, reverses their order,
; and displays them with the correct ordering.
INCLUDE Irvine32.inc
; Macro definitions...
; Constants
DELIMITER EQU ',' ; Default delimiter
TEMPS_PER_DAY EQU 24 ; Number of temperature readings per day
; Data Section
.DATA
; File-related variables
userFilePath BYTE 256 DUP(0) ; Buffer to store user input filename
fileHandle DWORD ? ; To store the file handle
bufferSize EQU 1024 ; Size of the read buffer
readBuffer BYTE bufferSize DUP(0) ; Buffer to store file contents
bytesRead DWORD ? ; Number of bytes read
; Temperature array
tempArray SDWORD TEMPS_PER_DAY DUP(0) ; Array to store temperature values
; Number of temperatures parsed
numTempsParsed DWORD ? ; To store the actual number of temperatures parsed
; Messages
successMsg BYTE "Here's the corrected temperature order!", 0
promptFile BYTE "Enter the name of the file to be read: ", 0
fileOpenError BYTE "Error: Unable to open the file.", 0
fileReadError BYTE "Error: Unable to read from the file.", 0
fileCloseError BYTE "Error: Unable to close the file.", 0
exitMsg BYTE "Hope that helps resolve the issue, goodbye!", 0
.CODE
main PROC
; Initialize Program
CALL ClrScr
; Step 1: Get Filename from User
mGetString OFFSET promptFile, OFFSET userFilePath, 255, OFFSET bytesRead
; Null-terminate the userFilePath string
MOV EAX, [bytesRead]
MOV EBX, OFFSET userFilePath
ADD EBX, EAX
MOV BYTE PTR [EBX], 0
; Trim any trailing whitespace or newline characters from userFilePath
PUSH ' '
PUSH OFFSET userFilePath
CALL Str_Trim
PUSH 0Dh
PUSH OFFSET userFilePath
CALL Str_Trim
PUSH 0Ah
PUSH OFFSET userFilePath
CALL Str_Trim
; Step 2: Open the Text File
MOV EDX, OFFSET userFilePath
CALL OpenInputFile
CMP EAX, INVALID_HANDLE_VALUE
JE FileOpenErrorHandler
MOV fileHandle, EAX
; Step 3: Read from the File
MOV EAX, fileHandle
MOV EDX, OFFSET readBuffer
MOV ECX, bufferSize
CALL ReadFromFile
JC FileReadErrorHandler
MOV bytesRead, EAX
; Null-terminate the readBuffer
MOV EAX, [bytesRead]
MOV EBX, OFFSET readBuffer
ADD EBX, EAX
MOV BYTE PTR [EBX], 0
; Step 4: Parse Temperatures from String
PUSH OFFSET readBuffer
PUSH OFFSET tempArray
CALL ParseTempsFromString
ADD ESP, 8
; Step 5: Write Temperatures in Reverse Order
mDisplayString OFFSET successMsg
CALL CrLf
PUSH OFFSET tempArray
CALL WriteTempsReverse
ADD ESP, 4
; Step 6: Close the File
MOV EAX, fileHandle
CALL CloseFile
CMP EAX, 0
JE FileCloseErrorHandler
; Program Completion
mDisplayString OFFSET exitMsg
CALL CrLf
CALL WaitMsg
EXIT
FileOpenErrorHandler:
mDisplayString OFFSET fileOpenError
CALL CrLf
JMP ExitProgram
FileReadErrorHandler:
mDisplayString OFFSET fileReadError
CALL CrLf
; Attempt to close the file before exiting
MOV EAX, fileHandle
CALL CloseFile
JMP ExitProgram
FileCloseErrorHandler:
mDisplayString OFFSET fileCloseError
CALL CrLf
JMP ExitProgram
ExitProgram:
CALL WaitMsg
EXIT
main ENDP
; Procedure: ParseTempsFromString
; Description: Parses temperature values from a comma-delimited string.
ParseTempsFromString PROC STDCALL
; Prologue
PUSH EBP
MOV EBP, ESP
PUSH ESI
PUSH EDI
PUSH EBX
; Retrieve parameters
MOV ESI, [EBP + 8] ; ESI = fileBuffer
MOV EDI, [EBP + 12] ; EDI = tempArray
; Initialize index
XOR ECX, ECX ; ECX = 0 (index into tempArray)
ParseLoop:
; Skip delimiters and whitespace
SkipDelimiters:
CMP BYTE PTR [ESI], 0
JE ParseEnd
MOV AL, [ESI]
CMP AL, DELIMITER
JE SkipChar
CMP AL, ' '
JE SkipChar
CMP AL, 0Dh ; Carriage return
JE SkipChar
CMP AL, 0Ah ; Line feed
JE SkipChar
JMP CheckSign
SkipChar:
INC ESI
JMP SkipDelimiters
CheckSign:
; Check for sign
MOV EDX, 0 ; EDX = 0 (positive)
CMP BYTE PTR [ESI], '-'
JNE CheckPlus
MOV EDX, 1 ; EDX = 1 (negative)
INC ESI ; Skip '-'
JMP ParseNumber
CheckPlus:
CMP BYTE PTR [ESI], '+'
JNE ParseNumber
INC ESI ; Skip '+'
ParseNumber:
; Initialize number accumulator
XOR EBX, EBX
ParseDigits:
CMP BYTE PTR [ESI], 0
JE StoreNumber
MOV AL, [ESI]
CALL IsDigit
JZ IsADigit
; Not a digit
JMP StoreNumber
IsADigit:
MOV AL, [ESI]
SUB AL, '0'
MOVZX EAX, AL
IMUL EBX, EBX, 10
ADD EBX, EAX
INC ESI
JMP ParseDigits
StoreNumber:
; Apply sign
MOV EAX, EBX
CMP EDX, 1
JNE StorePositive
NEG EAX
StorePositive:
; Store EAX in tempArray[ECX]
MOV [EDI + ECX*4], EAX
; Increment ECX
INC ECX
CMP ECX, TEMPS_PER_DAY
JL ContinueParsing
JMP ParseEnd
ContinueParsing:
JMP ParseLoop
ParseEnd:
MOV [numTempsParsed], ECX ; Store the number of temperatures parsed
; Epilogue
POP EBX
POP EDI
POP ESI
POP EBP
RET 8
ParseTempsFromString ENDP
; Procedure: WriteTempsReverse
; Description: Writes the temperature values from the provided array in reverse order.
WriteTempsReverse PROC STDCALL
; Prologue: Save registers that will be used
PUSH EBP
MOV EBP, ESP
PUSH EBX
PUSH ESI
PUSH EDI
; Retrieve parameter from stack
MOV ESI, [EBP + 8] ; ESI = tempArray
; Initialize loop counter
MOV ECX, [numTempsParsed] ; ECX = number of temperatures parsed
DEC ECX ; ECX = numTempsParsed - 1
WriteLoop:
CMP ECX, -1 ; If ECX < 0, exit loop
JL WriteEnd
; Load temperature value
MOV EAX, [ESI + ECX*4] ; EAX = tempArray[ECX]
; Display the temperature with sign
CALL WriteInt ; Write the integer in EAX with sign
; Display the delimiter
mDisplayChar DELIMITER ; Display the delimiter
; Decrement counter
DEC ECX
JMP WriteLoop
WriteEnd:
; Display a newline for readability
CALL CrLf
; Epilogue: Restore registers and return
POP EDI
POP ESI
POP EBX
POP EBP
RET 4 ; Clean up 1 parameter (4 bytes)
WriteTempsReverse ENDP
END main
Additional Information:
File Contents (Temps090124.txt
):
The file contains the following temperatures:
-3,-2,0,3,7,10,15,20,25,30,35,40,45,42,38,34,30,25,20,15,10,5,2,-1,
Development Environment:
Questions:
+67,
instead of the reversed list of temperatures?What I've Tried So Far:
Str_Trim
calls.I'm stuck at this point and would appreciate any guidance on what might be causing this issue.
I'm thrilled to report that after applying the suggested fixes, my assembly language program is now working perfectly! It's incredibly satisfying to see the temperatures being parsed and displayed correctly without any unexpected values. I'd like to share how I resolved the issues, in case others encounter similar problems.
RECAP:
+67
at the end of the output.Solution:
After some debugging and with the helpful hints from @Jester, I think I've figured out and fixed the issues.
ParseTempsFromString
Issue:
I realized that the arguments passed to the ParseTempsFromString
procedure were swapped. The procedure expected the parameters in a specific order, but I had pushed them onto the stack incorrectly.
Incorrect Code:
; Original (incorrect) parameter order
PUSH OFFSET readBuffer ; Should be second parameter
PUSH OFFSET tempArray ; Should be first parameter
CALL ParseTempsFromString
Fix:
By swapping the order of the parameters when pushing them onto the stack, I believe the procedure receives the correct arguments.
Corrected Code:
; Correct parameter order
PUSH OFFSET tempArray ; First parameter (output array)
PUSH OFFSET readBuffer ; Second parameter (input string)
CALL ParseTempsFromString
Explanation:
From what I understand, in assembly language, when using the stdcall
calling convention, parameters are pushed onto the stack in reverse order. The callee retrieves them based on this order. By swapping the parameters, ParseTempsFromString
gets the correct arguments, and the temperatures are parsed correctly.
IsDigit
MacroIssue:
I was incorrectly using CALL IsDigit
in the ParseTempsFromString
procedure. Since IsDigit
is a macro in the Irvine32 library and not a function, calling it like this was causing problems.
Incorrect Code:
CALL IsDigit
JZ IsADigit
; Not a digit
JMP StoreNumber
Fix:
I replaced the CALL IsDigit
with direct character comparisons to check if a character is a digit.
Corrected Code:
ParseDigits:
CMP BYTE PTR [ESI], 0
JE StoreNumber
MOV AL, [ESI]
CMP AL, '0'
JL StoreNumber ; Not a digit
CMP AL, '9'
JG StoreNumber ; Not a digit
SUB AL, '0' ; Convert ASCII to numeric value
MOVZX EAX, AL
IMUL EBX, EBX, 10
ADD EBX, EAX
INC ESI
JMP ParseDigits
Explanation:
By directly comparing the character in AL
to '0'
and '9'
, I can determine if it's a digit. This prevents non-digit characters from being misinterpreted, which was causing the unexpected +67
in the output.
WriteTempsReverse
Issue:
In the WriteTempsReverse
procedure, the loop condition wasn't properly exiting when ECX
reached -1
, which led to an out-of-bounds access and the +67
issue.
Incorrect Code:
CMP ECX, -1
JL WriteEnd
Fix:
I changed the comparison to use JLE
(Jump if Less or Equal) to ensure the loop exits when ECX
is -1
.
Corrected Code:
CMP ECX, -1
JLE WriteEnd
Explanation:
Using JLE
ensures that when ECX
is less than or equal to -1
, the loop exits, preventing invalid memory access.
Issue:
There was an extra comma displayed after the last temperature.
Fix:
I modified the WriteTempsReverse
procedure to add the delimiter only between temperatures, not after the last one.
Corrected Code:
; After displaying the temperature
CMP ECX, 0
JG AddDelimiter ; If more temperatures, add delimiter
JMP WriteLoop ; If last temperature, skip adding delimiter
AddDelimiter:
mDisplayChar DELIMITER
JMP WriteLoop
Explanation:
By checking if there are more temperatures to display, I avoided adding a comma after the last one.
Issue:
There was inconsistency in how the stack was being cleaned up. I was using RET x
in my procedures but also adjusting ESP
in the caller.
Fix:
I decided to let the callee clean up the stack and removed the ADD ESP, x
from the caller.
Corrected Code:
; No need to adjust ESP after the call since the callee cleans up the stack
CALL ParseTempsFromString
Explanation:
By letting the callee handle the stack cleanup, I ensured that the stack remains balanced, preventing potential issues.
Jester's Hint:
"Hint: If ECX < 0, exit loop ... but that's not what you have coded."
Impact:
Jester's hint was really helpful. It made me realize that my loop condition in WriteTempsReverse
wasn't correct. Changing JL
to JLE
fixed the loop, and the unexpected +67
disappeared.
Final Working Code:
After applying all these fixes, the program now works correctly. Here's the output:
Enter the name of the file to be read: C:/CS271/Project6/Temps090124.txt
Here's the corrected temperature order!
-1,+2,+5,+10,+15,+20,+25,+30,+35,+40,+45,+42,+38,+34,+30,+25,+20,+15,+10,+7,+3,+0,-2,-3
Hope that helps resolve the issue, goodbye!
Press any key to continue...
Conclusion:
I'm really happy that the program is now functioning as expected. The process taught me a lot about the importance of parameter order, proper use of macros, and correct loop conditions in assembly language. Thanks to Jester for the helpful hint!
Takeaway:
Small mistakes in assembly can cause big problems, so paying attention to details is crucial. Also, community help can be invaluable when debugging.