Tuesday, December 23, 2014

Recursion Surgeon

Hilo,Halo,Howdayado! On this post I'm going to teach you about recursion. What is recursion you might ask? Well I'll tell you. You know how in our last program we used a loop? Well we're going to replace that loop with recursion,which is when a procedure calls itself instead of doing some sort of jump,which takes us back to the beginning of the procedure,so since we don't choose where we jump back to,we're going to have to make a few changes to our program.

Speaking of which, here's the program:

.586
.model flat, c
.stack 100h
.data

; gcd(10, 4), gcd(28, 21), gcd(12, 8),
left dword 11, 28, 12
right dword 4, 21, 8

.code

doit proc
push eax
push ebx
push ecx
xor eax,eax
xor ecx,ecx
doagain:
sub esp,4
push left[ecx]
push right[ecx]
call gcd
pop ebx
cmp ecx,12
jl doagain
pop ecx
pop ebx
pop eax
ret

doit endp

gcd proc
push eax
push ebx
push edx
mov eax,[esp + 20]       ; eax is left
mov ebx,[esp + 16]       ; ebx is right
cmp ebx, 0 ; We are done when right is zero
je weAreDone
xor edx,edx
div ebx
mov eax,ebx
mov ebx,edx
sub esp,4
push eax
push ebx
call gcd
pop eax
weAreDone:
; eax has the greatest common divisor
mov [esp + 24],eax
pop edx
pop ebx
pop eax

ret 8

gcd endp

end

We don't change anything in doit so let's just talk about gcd and what we change in there. Once we get to gcd our stack will look like this:

I'll explain the stack from top to bottom: At the bottom of the stack we have the return address that takes us back to main because that's the very first thing we do when we go into our program,next we have the 3 saved registers for our no trace coding,but just because I called them rand doesn't mean they're just a bunch of random numbers,they are those numbers for a reason,I just called them rand because they're always going to be a different number so I can't confirm them with a fixed value. Next we have our little slot for our return value,and our inputs left and right,and at the top we have the return address back from gcd to doit.

The first time through gcd we have our saves which still aren't a fixed value for our procedure,our return value slot,our inputs,and our return address:

The second time we have our saves which are procedure values this time around,our slot,inputs,and address:

The third time we have our saves,slot,inputs,and address:

The fourth time we only have our saves because we're at the base case which is when right is 0 and since we do the compare right after the saves we jump down to where we unwind the stack:

The first thing we change in gcd is we get rid of our jump address,because we don't need it anymore,and the next thing we change is before gcd calls itself we do the same thing that we do in doit just before we call gcd,we subtract 4 from the esp to make a slot for our return value,and push the inputs onto the stack so that we can access them through the esp. Another thing I'd like to point out is that since we call gcd from inside itself when we hit the return we return to the instruction right after the call so then we hit the return again and again,meanwhile we're popping a ton of values off the stack. You might be thinking: "Wait a minute,if we do that aren't we going to get a ton of random values?" well the answer is no because since we do the same thing that we do in doit before the call and since gcd calls itself and we go back to the beginning we do all those pushes,while doing these instructions we are doing something called unwinding the stack and eventually we get to the return that takes us back to doit and then we switch to the next set of numbers and call gcd. Well ya that's all cool,but the true beauty of recursion is that (especially with gcd) when you go back to the beginning if you've set up the code right the inputs are ready,the stack's ready,everything's ready to go around again,take the gcd of 11,and 4 for instance,which is the one I'm showing you in the pictures.

1st time gcd(11,4) = gcd(4,11 % 4) = gcd(4,3)
2nd time gcd(4,3) = gcd(3,4 % 3) = gcd(3,1)
3rd time gcd(3,1) = gcd(1,3 % 1) = gcd(1,0)
4th time gcd(1,0) = 1

The first time around 11,and 4 are on the stack,so we take them and mod them together which turns the 11 into a 3,then we push our numbers onto the stack again and go to the second time and keep changing the inputs on the stack etc.etc.etc.,and that's the beauty of it: we have our inputs,mod them together,switch them go again.

WELL TTFN, TA TA FOR NOW!!!!!!!!!!!!!!!!!!!

Thursday, December 4, 2014

On this post I changed the program so that it brings the gcd of every set of numbers and adds them together,and also performs no-trace-coding which means we save the registers and restore them so it's like the procedure that changed them was never called.

Here's my program:

.586
.model flat, c
.stack 100h
.data

; gcd(10, 4), gcd(28, 21), gcd(12, 8),
left dword 10, 28, 12
right dword 4, 21, 8

.code

doit proc
push eax
push ebx
push ecx
xor eax,eax
xor ecx,ecx
doagain:
sub esp,4
push left[ecx]
push right[ecx]
call gcd
pop ebx
cmp ecx,12
jl doagain
pop ecx
pop ebx
pop eax
ret

doit endp

gcd proc
push eax
push ebx
push edx
mov eax,[esp + 20]       ; eax is left
mov ebx,[esp + 16]       ; ebx is right
again:
xor edx,edx                   ; zeroing out edx to prevent Integer overflow
div ebx                          ; divides left by right
cmp edx, 0       ; We are done when remainder is zero
je weAreDone

mov eax,ebx       ; we have a new left
mov ebx,edx                 ; our remainder is our new right
jmp again                      ; repeat

weAreDone:
; ebx has the greatest common divisor
mov [esp + 24],ebx
pop edx
pop ebx
pop eax

ret

gcd endp

end

You might notice something that wasn't there before,a subtraction by four from the esp. I put that in because when we subtract 4 from the esp it goes up four bytes which is one space up which opens up a little spot,and before that we push eax,ebx,and ecx to the top of the stack. When we do that we are saving the values that those registers have when we first get to doit so that we can restore them later for main. Then we take eax and xor it because we want to use it to add up the gcd's and ecx is our indexer. Then we push left and right to the top of the stack and call gcd placing the return address at the top of the stack. Once we get to gcd we save the registers that we change in gcd for doit and then place left and right in eax and ebx. Once we get to weAreDone after the other parts of the procedure we place the gcd (which is currently in ebx) into that empty slot we created with the subtraction and pop the rest of the registers values in doit back into them,and that's very important because for example: even though eax is used in gcd it's also used in doit as our sum so we have to restore it to the amount that it was in doit or else we're stomping on our sum,and then return to doit leaving right at the top of the stack. Once we get back to doit at the instruction after the call we add 8 to the esp which leaves the return value (our gcd) at the top of the stack. Then we pop it into ebx and add that to eax which in doit is the sum of all of the gcd's. Then when ecx (our indexer) reaches 12 we go down to the pops and restore eax,ebx,and ecx to their original values before we even called doit.

WELL TTFN,TA TA FOR NOW!!!!!!!!!!!!!!!!!!!!