-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProj6_robinss3.asm
368 lines (316 loc) · 8.71 KB
/
Proj6_robinss3.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
TITLE PROJECT 6 - Low-level I/O procedures (Proj6_robinss3.asm)
; Author: Shawn Robinson
; Last Modified: 2021/12/05
; OSU email address: [email protected]
; Course number/section: CS271 Section 400
; Project Number: 6 Due Date: 2021/12/05
; Description: This program defines two procedures: 1) Takes ASCII user input and stores it as an integer value, and 2) Takes an integer value and writes its ASCII representation to the console.
INCLUDE Irvine32.inc
; ---------------------------------------------------------------------------------
; Name: mGetString
;
; Displays a prompt for and gets user input.
;
; Preconditions: do not use eax, ecx, edx as arguments
;
; Receives:
; promptAddr = Address of prompt displayed to the user before reading input
; outputSize = Size of buffer in bytes for storing user input
;
; Returns:
; outputAddr = Address of buffer to store user input
; bytesRead = Length in bytes of user input
; -----------------------------------------------------------------------------
mGetString MACRO promptAddr, outputAddr, outputSize, bytesRead
; Preservation
PUSH EDX
PUSH ECX
PUSH EAX
; Display prompt
MOV EDX, promptAddr
CALL WriteString
; Read user input
MOV EDX, outputAddr
MOV ECX, outputSize
CALL ReadString
; Move ReadString outputs to the macro's parameters
MOV bytesRead, EAX
; Preservation
POP EAX
POP ECX
POP EDX
ENDM
; ---------------------------------------------------------------------------------
; Name: mDisplayString
;
; Writes a string to the console.
;
; Preconditions: do not use edx as arguments
;
; Receives:
; stringAddr = Address of string to write
; -----------------------------------------------------------------------------
mDisplayString MACRO stringAddr
; Preservation
PUSH EDX
; Display string
MOV EDX, stringAddr
CALL WriteString
; Preservation
POP EDX
ENDM
BUFFERSIZE = 40 ; Size of any local arrays created in procedures.
BASE = 10 ; Base that WriteVal displays numbers in.
.data
prompt BYTE "Please enter an integer: ", 0
userInput SDWORD ?
invalidInputMsg BYTE "ERROR: You did not enter a signed number or your number was too big. Please try again.", 0 ; String used by the ReadVal procedure when the user input is invalid.
programIntro BYTE "Computer Architecture and Assembly Project #6 : Low Level I/O",10,13
BYTE "Written by: Shawn Robinson",10,13,10,13
BYTE "Please provide 10 signed 32-bit decimal integers (Range: [-2147483648, 2147483647]).",10,13,10,13,0
intArray SDWORD 11 DUP(0)
arrayMsg BYTE "The numbers you provided: ",0
intSum SDWORD 0
sumMsg BYTE "The sum of your numbers (assuming it didn't overflow, 'cause I'm not handling that): ",0
intAvg SDWORD 0
AvgMsg BYTE "The (truncated) average of your numbers (assuming the sum didn't overflow): ",0
goodbye BYTE "Thanks for running the IO procedures test.",10,13, "I hope your day is as wonderful for you as the moment I fixed all the bugs in this thing was for me.",0
.code
main PROC
MOV EDX, OFFSET programIntro
CALL WriteString
; Get 10 numbers for input
MOV ECX, 10
MOV EDI, OFFSET intArray
_getInput:
PUSH OFFSET invalidInputMsg
PUSH OFFSET userInput
PUSH OFFSET prompt
CALL ReadVal
MOV EAX, userInput
STOSD
LOOP _getInput
CALL CRLF
; Display the 10 input numbers
MOV EDX, OFFSET arrayMsg
CALL WriteString
MOV ECX, 10
MOV ESI, OFFSET intArray
_displayInput:
LODSD
PUSH EAX
CALL WriteVal
MOV AL, ','
CALL WriteChar
MOV AL, ' '
CALL WriteChar
LOOP _displayInput
CALL CRLF
CALL CRLF
; Calc sum
MOV ECX, 10
MOV ESI, OFFSET intArray
_addNext:
LODSD
ADD intSum, EAX
LOOP _addNext
; Display sum
MOV EDX, OFFSET sumMsg
CALL WriteString
PUSH intSum
CALL WriteVal
CALL CRLF
CALL CRLF
; Sign extend intSum into EDX so IDIV works properly
CMP intSum, 0
JS _negSignExtend
_zeroExtend:
MOV EDX, 0
JMP _divide
_negSignExtend:
MOV EDX, -1
_divide:
MOV EAX, intSum
MOV EBX, 10
IDIV EBX
MOV intAvg, EAX
; Display sum and average
MOV EDX, OFFSET avgMsg
CALL WriteString
PUSH intAvg
CALL WriteVal
CALL CRLF
CALL CRLF
; Display goodbye message :)
MOV EDX, OFFSET goodbye
CALL WriteString
CALL CRLF
CALL CRLF
Invoke ExitProcess,0 ; exit to operating system
main ENDP
; ---------------------------------------------------------------------------------
; Name: ReadVal
;
; Prompt the user for a signed 32-bit integer, retries if the user input isn't valid, and returns the 32-bit integer.
;
; Preconditions: none
;
; Postconditions: none
;
; Receives:
; [ebp+16] = address of invalidInputMsg
; [ebp+12] = address of SDWORD to store user input
; [ebp+8] = address of string to prompt user for input
;
; Returns:
; [ebp+12] = address of stored user input
; ---------------------------------------------------------------------------------
ReadVal PROC
; Preservation + Set base pointer
PUSH EBP
MOV EBP, ESP
PUSHAD
PUSHFD
; Create buffer for mGetString output
SUB ESP, BUFFERSIZE
MOV EDI, ESP
_getUserInput:
; Get user input via mGetString
mGetString [EBP+8], EDI, BUFFERSIZE, EBX
; Check for empty input
CMP EBX, 0
JE _invalidInputError
; Convert and validate
; Move ESI to final index of string, set direction flag to move backwards
MOV ESI, EDI
ADD ESI, EBX
SUB ESI, 1
STD
MOV EAX, 0 ; Will hold value after its pulled from the string and converted
MOV ECX, EBX ; Will hold array length
MOV EBX, 0 ; Will hold sums of (each digit in string * the digit's place value)
MOV EDX, 1 ; Will hold place value multiplier
_nextChar:
MOV EAX, 0 ; Empty EAX out entirely as we'll be doing 32bit multiplication with it eventually
LODSB
; Check if current char is a sign (43d or 45d -- '+' or '-')
CMP AL, 43
JE _signChar
CMP AL, 45
JE _signChar
; Check if current char is a number ([48d, 57d])
CMP AL, 57
JA _invalidInputError
CMP AL, 48
JB _invalidInputError
_numberChar:
SUB AL, 48 ; Convert ASCII digit to int
PUSH EDX
MUL EDX ; Multiply by its place value -- Save and restore EDX since its overwritten
POP EDX
SUB EBX, EAX ; Add calc'd value to running sum (Sub instead of add so min_int can be valid, since otherwise we'd have issues with abs(min_int) > abs(max_int))
JNS _invalidInputError
; Increase place value multiplier
MOV EAX, 10
MUL EDX
MOV EDX, EAX
CMP ECX, 1
JE _invert ; If at end (start) of string, make sure to invert back to positive since there was no sign char
JMP _endOfCycle
_signChar:
; Check that the sign is at the start of the string -- If there's stuff before it, the input is invalid
CMP ECX, 1
JNZ _invalidInputError
; If '-', ignore since EBX is already negative
CMP AL, 45
JZ _endOfCycle
_invert:
; If '+', mult by -1 to make positive (and check for overflow since abs(min_int) > abs(max_int))
NEG EBX
JO _invalidInputError
_endOfCycle:
LOOP _nextChar
JMP _storeOutput
_invalidInputError:
; Display error message for invalid input
MOV EDX, [EBP+16]
CALL WriteString
CALL CRLF
JMP _getUserInput
_storeOutput:
; Store in output variable
MOV EAX, [EBP+12]
MOV [EAX], EBX
; Preservation + Dereference local variables
ADD ESP, BUFFERSIZE
POPFD
POPAD
POP EBP
RET 8
ReadVal ENDP
; ---------------------------------------------------------------------------------
; Name: WriteVal
;
; Takes an SDWORD value and writes its ASCII symbols to the output.
;
; Preconditions: none
;
; Postconditions: A message will be written to the console.
;
; Receives:
; [ebp+8] = address of the SDWORD to write
;
; Returns:
; none
; ---------------------------------------------------------------------------------
WriteVal PROC
; Preservation + Set EBP
PUSH EBP
MOV EBP, ESP
PUSHAD
PUSHFD
; Repeatedly divide the int value by 10 and store the remainders.
MOV EAX, [EBP+8]; Int value to repeatedly divide
MOV EBX, BASE ; Divisor
MOV ECX, 0 ; Current digit
SUB ESP, BUFFERSIZE
MOV EDI, ESP ; Set EDI to start of array for reading back later
CLD
; Check for negative
CMP EAX, 0
JNS _divide ; If positive, skip writing a '-'
NEG EAX
PUSH EAX
MOV AL, '-'
CALL WriteChar
POP EAX
MOV EDI, ESP
_divide:
MOV EDX, 0 ; Zero out the high 32 bits of division
DIV EBX
MOV EBX, EAX ; Preserve EAX -- EBX was a constant so it doesn't matter
MOV EAX, EDX ; Prep to store remainders
STOSB ; Store the remainders
MOV EAX, EBX ; Restore EAX
MOV EBX, BASE ; Restore EBX
INC ECX ; Increment current digit count
CMP EAX, 0
JNZ _divide
; Read back the ints, add 48 (since 0d is 48 ascii), write that as char
MOV ESI, EDI ; Set up to read from the array backwards. Using the stack would have made more sense here, but the assignment requires STO/LOD
DEC ESI
STD
MOV EBX, 48 ; Ascii offset
_writeNextChar:
LODSB
ADD EAX, EBX
CALL WriteChar
LOOP _writeNextChar
; Preservation + Dereference local variables
ADD ESP, BUFFERSIZE
POPFD
POPAD
POP EBP
RET 4
WriteVal ENDP
END main