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
|
# CONVERT AN INPUT FILE TO AN OUTPUT FILE WITH ALL
# LETTERS CONVERTED TO UPPERCASE
.SECTION .DATA
# CONSTANTS
.EQU SYS_OPEN, 2
.EQU SYS_WRITE, 1
.EQU SYS_READ, 0
.EQU SYS_CLOSE, 3
.EQU SYS_EXIT, 60
# WE HAVE NO ACCESS TO HEADERS, SO, LET'S DEFINE OUR FLAGS
.EQU O_RDONLY, 0
.EQU O_CREAT_WRONLY_TRUNC, 03101
# ADD SOME OTHER NAMING TO MAKE MY LIFE EASIER
.EQU STDIN, 0
.EQU STDOUT, 1
.EQU STDERR, 2
.EQU END_OF_FILE, 0
.EQU NUMBER_ARGUMENTS, 2
################################################################
.SECTION .BSS
.EQU BUFFER_SIZE, 500
.LCOMM BUFFER_DATA, BUFFER_SIZE
################################################################
.SECTION .TEXT
# STACK POSITIONS
.EQU ST_SIZE_RESERVE, 16
.EQU ST_FD_IN, -8
.EQU ST_FD_OUT, -16
.EQU ST_ARGC, 0 # NUMBER OF ARGUMENTS
.EQU ST_ARGV_0, 8 # PROGRAM'S NAME
.EQU ST_ARGV_1, 16 # INPUT FILE NAME
.EQU ST_ARGV_2, 24 # OUTPUT FILE NAME
.GLOBL _START
.GLOBAL CONVERT_TO_UPPER # DECLARATION ONLY
### START OF PROGRAM ###
_START:
# SAVE STACK POINTER
MOVQ %RSP, %RBP
# ALLOC SPACE FOR FDS ON THE STACK
SUBQ $ST_SIZE_RESERVE, %RSP
# LABELS ARE HERE JUST TO MAKE MY LIFE EASIER IN SPLITTING THE SECTIONS
# I WON'T BE JUMPING AROUND...
# INT OPEN(CONST CHAR *PATHNAME, INT FLAGS, MODE_T MODE)
# ARGUMENT'S ORDER: %RDI, %RSI, %RDX
OPEN_FILES:
OPEN_FD_IN:
MOVQ $SYS_OPEN, %RAX # SETUP SYSCALL NUMBER
MOVQ ST_ARGV_1(%RBP), %RDI # INPUT FILENAME INTO %RDI
MOVQ $O_RDONLY, %RSI # OPEN IN O_RDONLY
MOVQ $0666, %RDX # REGULAR POSIX PERMS
SYSCALL # INVOKE KERNEL TO CALL SYS_OPEN()
STORE_FD_IN:
MOVQ %RAX, ST_FD_IN(%RBP) # SAVE FILE'S FD
OPEN_FD_OUT:
MOVQ $SYS_OPEN, %RAX # SETUP SYSCALL NUMBER
MOVQ ST_ARGV_2(%RBP), %RDI # OUTPUT FILE NAME LOCATION TO %EBX
MOVQ $O_CREAT_WRONLY_TRUNC, %RSI# SETUP WRITING FLAGS
MOVQ $0666, %RDX # POSIX MODE
SYSCALL # INVOKE KERNEL (SYS_OPEN())
STORE_FD_OUT:
MOVQ %RAX, ST_FD_OUT(%RBP) # SAVE OUTPUT FD IN THE STACK
################################################################
# SSIZE_T READ(INT FD, VOID *BUF, SIZE_T COUNT);
# SSIZE_T WRITE(INT FD, CONST VOID *BUF, SIZE_T COUNT);
READ_LOOP_BEGIN:
MOVQ $SYS_READ, %RAX
MOVQ ST_FD_IN(%RBP), %RDI # READ FROM THIS FD
MOVQ $BUFFER_DATA, %RSI # COPY BUFFER'S ADDRESS TO %RSI, SO
# READ() KNOWS WHERE TO PUT DATA READ
MOVQ $BUFFER_SIZE, %RDX # SET BUFFER SIZE FOR READ()
SYSCALL # INVOKE KERNEL
# NUMBER OF BYTES READ SHOULD BE IN %EAX
# LEAVE LOOP IF WE ARE AT EOF
CMPQ $END_OF_FILE, %RAX
JLE END_LOOP
CONTINUE_READ_LOOP:
# CALL CONVERT FUNCTION FOR THE CURRENT BUFFER
PUSHQ $BUFFER_DATA # BUFFER LOCATION - 2ND ARGUMENT
PUSHQ %RAX # BUFFER SIZE - 1ST ARGUMENT
# (RETURNED FROM READ() SYSCALL)
CALL CONVERT_TO_UPPER
POPQ %RAX # GET SIZE BACK
ADDQ $8, %RSP # CLEANUP STACK USED FOR BUFFER_DATA LOCATION
WRITE_FILE:
MOVQ %RAX, %RDX # USE NUMBER OF BYTES READ TO WRITE TO
# OUTPUT FILE
MOVQ $SYS_WRITE, %RAX
MOVQ ST_FD_OUT(%RBP), %RDI # WRITE TO THIS FD
MOVQ $BUFFER_DATA, %RSI # THIS BUFFER
SYSCALL # INVOKE KERNEL
JMP READ_LOOP_BEGIN # KEEP READING THE FILE
END_LOOP:
MOVQ $SYS_CLOSE, %RAX
MOVQ ST_FD_OUT(%RBP), %RDI
SYSCALL
MOVQ $SYS_CLOSE, %RAX
MOVQ ST_FD_IN(%RBP), %RDI
SYSCALL
MOVQ $SYS_EXIT, %RAX
MOVQ $0, %RDI
SYSCALL
# CONVERT_TO_UPPER:
#
# CONVERT ALL ASCII CHARACTERS IN $BUFFER
# TO UPPERCASE
#
# - 1ST ARGUMENT: BUFFER SIZE
# - 2ND ARGUMENT: BUFFER ADDRESS
#
# REMEMBER: ARGUMENTS MUST BE PUSHED IN THE REVERSE ORDER AS THEY ARE DOCUMENTED
#
# - %EAX - BEGINNING OF BUFFER
# - %RDI - BUFFER LENGTH
# - %RSI - CURRENT BUFFER OFFSET
# - %CL - BYTE BEING EXAMINED (LOWER BYTE OF %RCX)
## CONSTANTS
.EQU LOWERCASE_A, 'A' # LOWER BUNDARY
.EQU LOWERCASE_Z, 'Z' # UPPER BOUNDARY
# CONVERSION
.EQU UPPER_CONVERSION, 'A' - 'A'
## STACK STUFF
.EQU ST_BUFFER_LEN, 16 # BUFFER LENGTH
.EQU ST_BUFFER, 24 # ACTUAL BUFFER
.TYPE CONVERT_TO_UPPER, @FUNCTION
CONVERT_TO_UPPER:
PUSHQ %RBP # SETUP FUNCTION'S STACK FRAME. SAVE CURRENT %EBP
MOVQ %RSP, %RBP # AND SET IT TO CURRENT STACK POINTER
MOVQ ST_BUFFER(%RBP), %RAX
MOVQ ST_BUFFER_LEN(%RBP), %RDI
MOVQ $0, %RSI
# IF A ZERO LENGTH BUFFER WAS GIVEN, JUST LEAVE
CMPQ $0, %RAX
JE END_CONVERT_LOOP
CONVERT_LOOP:
MOVB (%RAX, %RSI, 1), %CL # GET CURRENT BYTE
# CHECK IF CURRENT BYTE IS ALREADY IN LOW CASE
CMPB $LOWERCASE_A, %CL
JL NEXT_BYTE
CMPB $LOWERCASE_Z, %CL
JG NEXT_BYTE
# CONVERT IF WE ARE STILL HERE
ADDB $UPPER_CONVERSION, %CL
MOVB %CL, (%RAX, %RSI, 1) # REPLACE OLD VALUE WITH THE NEW ONE
NEXT_BYTE:
INCQ %RSI
CMPQ %RSI, %RDI # CONTINUE CONVERSION UNLESS WE REACHED
JNE CONVERT_LOOP # THE END OF THE BUFFER
END_CONVERT_LOOP:
# NO RETURN VALUE, JUST RESTORE STACK POINTER
# AND BASE POINTER AND RETURN.
MOVQ %RBP, %RSP
POPQ %RBP
RET
|