Working on an college - Assembly Language C course -assignment where I need to create a basic printf function using only system calls. Whenever there is a '%' I need to check the next character to determine how to implement either a character or a string.
If there is a 'c', replace it with a character, if there is a 's', replace it with a string. If there is another '%', output that. The professor stated that he knows this is a tough assignment, so it's okay if the solution is partially implemented, but i'm so close to a full solution, so I wanted to push ahead.
I've done extensive work on this code, and made comments on mostly every line, so I want to emphasize this is for purposes of me learning. I can print out one string 'woot woot' using mov eax, [abp + 12] or the second 'woot woot' using mov eax, [ebp + 16], but I cannot find a solution to print both. This is my dilemma.
Thank you for your time, and happy coding!
Here is a link to the assignment for clarification: https://imgur.com/h9tP89j
This is my sample output:
Hello world
str3 is 'woot woots', isn't that cool?
A is a char, but so is %,, s again!
Here is my code:
4 segment .data
5
6 str1 db "Hello world", 10, 0
7 str2 db "str3 is '%s', isn't that cool?", 10, 0
8 str3 db "woot woot", 0
9 str4 db "%c is a char, but so is %%, %s again!", 10, 0
10
11 segment .bss
12
13
14 segment .text
15 global asm_main
16
17 asm_main:
18 push ebp
19 mov ebp, esp
20 ; ********** CODE STARTS HERE **********
21
22 ;; EVERYTHING UP UNTIL THE PRINTF FUNCTION DOES NOT CHANGE AT ALL
23
24 ; eax (syscall-number) What do we want done? 3 is Read, 4 is Write
25 ; ebx (other-info) Usually when do you want the thing done? Or printed?
26 ; 0 is if you want to type something yourself, 1 is if you want to print something
27 ; ecx (other-info) Usually this is where you would put the string to be printed (example: str1)
28 ; edx (other-info) How long is the data that needs to be printed? You can ignore the null character
29 ; int 0x80 = Turn on the kernel and do the thing
30
31 push str1 ; push string 1 - 4 bytes
32 call printf ; call function
33 add esp, 4 ; str1 is a dword with 4 bytes
34
35 push str3 ; push string 3 - 4 bytes
36 push str2 ; push string 2 - 4 bytes
37 call printf ; call function
38 add esp, 8 ; str3 and str2 is 8 bytes total
39
40 push str3 ; push string 3 - 4 bytes
41 push 'A' ; Push A character - it's still a dword so 4 bytes
42 push str4 ; push string 4 - 4 bytes
43 call printf ; call function
44 add esp, 8 ; two arguments, 8 bytes total
45
46 ; *********** CODE ENDS HERE ***********
47 mov eax, 0
48 mov esp, ebp
49 pop ebp
50 ret
51
52 printf:
53 push ebp ; Prologue - every function starts with this
54 mov ebp, esp ; Prologue - and this
55
56 mov edx, -1 ; this is a counter to walk through each string slowly
57 mov edi, -1
58 loop:
59 inc edx ; increment counter for each loop
60 mov esi, edx ; constantly update this reserve to preserve counter, for use with offsetedx
61 mov eax, DWORD [ebp + 8] ; set eax to the dword pointer at ebp + 8
62 cmp BYTE [eax + edx], 0 ; compare the byte in the string with a null terminator
63 je loopEnd ; if there is a null terminator, jump to the end of the loop
64
65 percentCheck: ; each time we come up to a %, we want to check the next character to see how to proceed
66 cmp BYTE [eax + edx], 37 ; compare the current byte with a 37, which is is a '%' on the ascii table
67 jne continue ; if there is no percentage, we can continue walking through the string
68 inc edx ; move to the next byte
69
70 charCheck:
71 cmp BYTE [eax + edx], 99 ; compare the byte with a 99, which is 'c' on the ascii table
72 jne stringCheck ; if there is no 'c', move to the next check
73 mov eax, 4 ; syscall write operation
74 mov ebx, 1 ; syscall for printing to screen
75 lea ecx, [ebp + 12] ; pointer is possibly on the character. If not...?
76
77 offsetCheck: ; my idea is to check for the byte where ecx is pointing to see if there's an 'A'
78 je offsetEnd ; if it is, then output that bad boy!
79 add ebp, 4 ; if not, then add to the stack to adjust for the offset
80 lea ecx, [ebp] ; now point ecx to the new pointer on the stack
81 jmp offsetCheck ; run it again to make sure you are poiting to the 'A' character
82 offsetEnd:
83
84 int 0x80 ; make the kernel do the thing
85 jmp loop ; re-run the loop
86
87 stringCheck: ; this loop is a little tricky, as we need to be able to point to the correct string to output instead of the 's', but w$
88 cmp BYTE [eax + edx], 115 ; compare the byte with a 115, which is an 's' on the ascii table
89 jne continue ; if there is no 's', just let the string keep going
90 mov edx, -1 ; to calculate string length, just use the walktrhough loop again
91 offsetedx:
92 inc edx ; edx is our counter here
93 ; mov edi, edx
94 mov eax, DWORD [ebp + 8] ; set eax to the dword pointer at ebp + 8 again
95 cmp BYTE [eax + edx], 0 ; checking for a null terminator
96 je offsetedxEnd ; if there is a null terminator, assume we have reached the end of the string we wanted to drop in, and proc$
97
98 mov eax, 4 ; syscall write operation
99 mov ebx, 1 ; syscall for printing to screen
100 mov ecx, DWORD [ebp + 12] ; having trouble figuring out how to dymically set this to the right place. What to compare ecx to? $
101 cmp edi, -1
102 je continueoffset
103 inc edi ; trying to increment edi so on the next check, I can set ecx to run the second 'woot woot' output
104 mov ecx, DWORD [ebp + 4] ; this will output the sencond woot woot, but I can't get it to make the adjustment
105
106 continueoffset:
107 mov edx, 9
108 ; mov edi, ecx
109 int 0x80
110 ;; inc edi
111 ; mov edx, edi
112 ; jmp offsetedx
113 offsetedxEnd:
114
115 ; int 0x80 ; let the kernel do its thing
116 mov edx, esi ; make sure to put edx back to what it was supposed to be so the top loop isn't screwed up
117 jmp loop ; re-run the loop
118
119 continue:
120 mov eax, 4 ; SYS_write - Print the thing out
121 mov ebx, 1 ; STDOUT (terminal) - Write to screen
122 mov ecx, DWORD [ebp + 8] ; sets pointer to format string
123
124 add ecx, edx ; added counter to pointer, which ecx is pointing to
125 mov edx, 1 ;; Want edx to only output 1 character, but after the output, we need it restored to original count
126
127 int 0x80 ; kernel please grant me your strength
128 mov edx, esi ; Extra important since we need edx to be walking through the string, so it needs to be restored to where it was
129 jmp loop ; run that loop back again
130
131 loopEnd:
132
133 mov esp, ebp ; Epilogue - every function ends with this
134 pop ebp ; Epilogue - and this
135 ret ; Epilogue - also this
When using stack frames , you should not modify the EBP
register outside the function prologue/epilogue.
Since all of your registers are being used, you need a local variable on the stack which always points to the address of the next unused vararg parameter. This variable should be initialized to ebp+12
, because that is the address of the second parameter of printf, which is the first vararg parameter. After using that parameter, you should increment that local variable by 4, to make it point to that next vararg parameter.
As long as this variable always points to the next unused vararg parameter, you should have no trouble finding the next parameter.
In order to allocate space for such a local variable, you need to allocate 4 bytes on the stack. You can do this with a push
instruction or a sub esp, 4
instruction. This can be done immediately after the function prologue.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.