Monday, October 27, 2014

SPACESHIP!!!!!!!!!!!!!!!!!!!!!!!!!!!SPACESHIP!!!!!!!!!!!!!!!!!!!!SPACESHIP!!!!!!!!!!!!!SPACESHIP!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Hi! Long time no see huh! Well I've been working on a little something that I'm going to show you now. I've been working on a program that makes a little spaceship video game! You drive a little spaceship around a black screen and it bounces off the walls. You can adjust the speed (speed = magnitude),and direction (magnitude + direction = velocity) of the spaceship anddddddd, it will never leave the screen!





Here's my program:



#include <Core.h>
#include <Vector.h>

Vector position;
Vector velocity;

void checkin ( )
{
if(Core::Input::IsPressed(Core::Input::KEY_LEFT))
{
velocity.x = velocity.x - 1;
}
if(Core::Input::IsPressed(Core::Input::KEY_RIGHT))
{
velocity.x = velocity.x + 1;
}
if(Core::Input::IsPressed(Core::Input::KEY_UP))
{
velocity.y = velocity.y - 1;
}

if(Core::Input::IsPressed(Core::Input::KEY_DOWN))
{
velocity.y = velocity.y + 1;
}
}






bool MyUpdateFn( float dt )
{
checkin ();

position = position +  velocity * dt;



if (position.x > 800)
{
velocity.x = velocity.x * -1;
}

if (position.x < 0)
{
velocity.x = velocity.x * -1;
}

if (position.y > 800)
{
velocity.y = velocity.y * -1;
}


if (position.y < 0)
{
velocity.y = velocity.y * -1;
}
return false;
}


void drawship (Core::Graphics& graphics)
{
Vector a(-50,50);
Vector b(-50,-50);
Vector c(50,-50);
Vector d(50,50);
Vector e(0,-100);

Vector aPrime = position + a;
Vector bPrime = position + b;
Vector cPrime = position + c;
Vector dPrime = position + d;
Vector ePrime = position + e;

graphics.DrawLine(aPrime.x,aPrime.y,bPrime.x,bPrime.y);// 1
graphics.DrawLine(aPrime.x,aPrime.y,dPrime.x,dPrime.y);// 4
graphics.DrawLine(bPrime.x,bPrime.y,cPrime.x,cPrime.y);// 2
graphics.DrawLine(cPrime.x,cPrime.y,dPrime.x,dPrime.y);// 3
graphics.DrawLine(ePrime.x,ePrime.y,cPrime.x,cPrime.y);// 5
graphics.DrawLine(ePrime.x,ePrime.y,bPrime.x,bPrime.y);// 6



}


void MyDrawFn( Core::Graphics& graphics)
{
// drawship(graphics);
}


int main()
{
Core::Init("Collin", 800, 800);
Core::RegisterUpdateFn(MyUpdateFn);
Core::RegisterDrawFn(MyDrawFn);
Core::GameLoop();
}



As you can see, it's a ton of code. So I'll start explaining it so we're not here for a century or 2. At the top we make our 2 vectors: position, and velocity. Next we make our void checkin procedure. After that we make our bool MyUpdateFn .  Then we type in the void drawship procedure,and after that we type in void MyDrawFn,and finally.........last but not least...........int........main!! Once you've gotten all that typed in we'll take a peek at how it fits together to make a game.




HI THERE!! REMEMBER ME!!?? OF COURSE YOU DO!! So the reason we make our 2 vectors is because we're gonna need vectors right? The reason we call them position and velocity is because one of them represents the ships' position and the other represents the ships' velocity. The position of the ship is where it's at on the screen and the velocity of the ship is what direction the ship is going in and how fast it's going in that direction,so whenever you change the ships' velocity you indirectly change the ships position so together velocity and position make the ship move. But that  doesn't mean that you have to change velocity to change position, in fact position has to be updated every single frame or else the ship stops when you let go of the arrow keys and then you have to start again at the lowest speed. The top procedure: void checkin controls which direction the ship is going in and how fast it's going in that direction,those little bits of code each control one of the arrow keys, you see how on left we're taking away from velocity.x and on right we are adding to it? Plus when we do up, we subtract from velocity.y,and when we do down  we add to it. Then it's off to see bool MyUpdateFn! In MyUpdateFn we make the ship bounce off the "walls" of the screen,here's how we do it. The screen is 800 by 800 pixels right? Well how we make the ship bounce off the walls is this. We say "if position.x is less than 0 or bigger than 800 multiply it by -1 and the same thing with position.y. The reason we multiply them by -1 is because whenever you multiply a number by -1 if it's a positive number it becomes a negative number,and if it's a negative number it becomes a positive number which flips the direction of the ship so whenever the ship hits a wall it goes in the opposite direction at the same speed. Now we're going to talk about my drawship procedure. This procedure as it's name suggests draws the ship on our screen using vectors a,b,c,d, and e and then sets them to the x,and y positions that we want to put the lines that make up the ship between. Now, you're probably wondering, if we set the ship to one position how is it supposed to move?  How we do that is we take these vectors and add them to position so that they will move, and name those new vectors aPrime,bPrime etc. etc. Then we take those Prime vectors and build the ship with them. And everything else after that is stuff my dad put in that I don't know how to explain.


                                           WELL   TTFN TA TA FOR NOW!!!!



Wednesday, June 25, 2014

The Case of the Missing Sphere

 Hi, it's been awhile. I've been working on ray tracing, and I have something new to teach you. Have you ever looked at a really realistic movie made out of computer graphics, or a really good video game and wondered how they made it ? They might have used ray tracing. Ray tracing is using rays and vectors to make a picture.  I've been working on it and I'm going to teach you what I know so far.  We are going
to put a sphere in the middle of a picture and use rays to color it so we can see it. First off we're going to need to place the sphere, and since we need to place it in the middle first we need to figure out how big our screen is. We're not going to use the entire screen, instead we're going to use a smaller 300*300 pixels screen. That means to place the sphere in the middle of the screen we need to place it's origin at -150 y,  150 x, and -300 z, and make it's radius 150. So we have our sphere. Now we need our rays. We need to hit every single pixel with a ray. So that means we need to put a ray at every single pixel. Doing them individually would take forever, so instead we're going to use a loop.

 This loop is going to create new rays and place them at different points.

for(int row = 0 ; row < NUM_PIXELS_Y ; row++)
for(int col = 0; col < NUM_PIXELS_X ; col++)
{
Ray explorer;
explorer.origin.x = -149.5 + col;
explorer.origin.y = -149.5 + row;
explorer.direction.z =-1;
   

Notice that I didn't close it off with a curly brace. That's because this is only the 1st 1/2 of the loop.  We'll get into the second 1/2 later but for now let's focus on this.

Our ray is called explorer, and what's nice about this loop is that after it does explorer it automatically does all the other rays, so we don't have to do another loop along with it. When we say col we mean columns, and obviously row means rows. The "for" part up there is the 1st part of the loop, they're the actions we do to determine if we go into the 2nd part of the loop. So there's int row = 0; which creates row and then makes it 0, then we compare row to NUM_PIXELS_Y;, row++ means increment row(if you don't know what increment means it means add 1) and the next line is the same except col replaces row and  NUM_PIXELS_X; replaces NUM_PIXELS_Y;. Then we have all this stuff about rays' origin. Origin.x is -149.5 + col, and origin.y is -149.5 + row, so as columns goes across and rows goes up and down we end up with rays at every single pixle of our 300*300 screen. And direction.z is the direction of the rays.


glm::vec3 hipotenoose = sphere.origin - explorer.origin;
 float adjacentLength = glm::dot(explorer.direction,hipotenoose);
 glm::vec3 adjacent = explorer.direction * adjacentLength; glm::vec3 opposite = hipotenoose - adjacent; if(glm::length(opposite) < sphere.radius) { 



qimage.setPixel(col,row,qRgb(0xFF,0,0)); }

Now for the 2nd 1/2 of the loop.

We've got our sphere and our rays, and now we have to figure out what rays hit the sphere and what rays don't and what to do if the ray hits the sphere. Here's the math.
                                                       
I know, it looks ridiculous but this is what we do. See we've got our ray which we can tell just by looking at this picture is going to hit the sphere. But the computer doesn't know that, so we need to do some math to figure it out. We've got our sphere, the spheres' origin,the spheres' radius, and our point of the world ( the + ). So 1st we draw a line from the spheres' origin to the rays' origin ( hypotenuse ), a line coming off of the ray ( adjacent ), a line coming off the point of the world to the rays' origin,and a line coming off the point of the world to the spheres' origin. The reason we're drawing these lines from the point of the world is so we can figure out hypotenuse, and how we do that is we say: ray origin - sphere origin = hypotenuse. So now we've got one side but we need the others to. So on to adjacent !!! To figure out adjacent we need to do hypotenuse, dot, rays' direction. Dot sort of means multiplication but you end up with the magnitudes of the vectors you're using and cos theta. So this is what we got from dotting those 2 together: |hypotenuse| |rays ' direction| cos theta. Now we scale it off of the ray's direction and we've got our adjacent! The next part is easy since we've got our hypotenuse and our adjacent now we need to get our opposite. All we have to do is subtract one from the other and we've got our opposite. Then we compare it to the spheres' radius and if it's longer the ray didn't hit  the sphere but if it's shorter the ray did hit the sphere. And the last line of code colors the pixels and you can fiddle around with the numbers to change the color of the sphere.

Detective, case solved!

Wednesday, November 27, 2013

The battle of AbstractionMan and Evil Dr.Details

        Ahoy there mateys! This day we shall plunge ourselves down into the sea of C++! I'm serious there so get ready to type! Now you know what we've been doing so far, we've been coding up vectors. But we've had trouble with Evil Dr.Redundancy.....and a new villain Evil Dr.Details. That's why I've prepared two procedures to get rid of them called add, and subtract so that instead of  having a ton of redundant code and a ton of details we can just do the code that's in the procedures and have a lot of abstraction. And for each of those two procedures we changed the're names to something special. On add we changed the name to  operator+ and on subtract we changed the name to operator- so instead of calling the procedures we can just say resultVector=leftVector+rightVector and it will call operator+ or we can say resultVector=leftVector-rightVector and it will call operator-. And guess what, that helps to get rid of details so we fired a shot at Evil Dr.Details. Aaaaaaaannnnnnnd that also gets rid of redundancy so we hit Evil Dr.Redundancy prrrrrretty hard. And AbstractionMan is now here to help us,and you can't have abstraction without the code to produce it so we've had some help from MemoryMan too. And how MemoryMan helps us is that instead of having six floats to keep track of we have 3 vectors called leftVector,rightVector,and resultVector. Now we're on a roll!

Here is my program:

struct Vector
{
float x;
float y;
};
Vector operator- (Vector left,Vector right)
{
Vector resultMan;
resultMan.x=left.x-right.x;
    resultMan.y=left.y-right.y;
return resultMan;
}
Vector operator+ (Vector left,Vector right)
{
Vector resultMan;
resultMan.x=left.x+right.x;
resultMan.y=left.y+right.y;
    return resultMan;
}
Vector leftVector;
Vector rightVector;
Vector resultVector;

void CollinsBasicVectorEquationCallback(const BasicVectorEquationInfo& data)
{
leftVector.x=data.x1*data.scalar1;
leftVector.y=data.y1*data.scalar1;
rightVector.x=data.x2*data.scalar2;
    rightVector.y=data.y2*data.scalar2;

if(data.add)
{
resultVector= leftVector + rightVector;
}
else
{
       resultVector=leftVector - rightVector;
}
}



Thursday, November 21, 2013

Vector!!!!......is back. dendenduuuuuuuuuun!

Direction
Magnitude
X components
Y components
                 Those are what vectors have. Ever watch Despicable Me? Well the rival bad guys name is vector,now your going to learn what a vector is. A vector is an arrow with direction,which is what "direction" the arrow points in,and magnitude,which is how long or short the arrow is. When you subtract 2 vectors then there is a result vector which is a different colored vector that connects the tips of the 2 other vectors to finish the shape. Then there's adding vectors. When you add vectors the 2nd vector starts from the tip of the 1st vector.  On a graph,vectors start from the origin (Middle point) for instance: when you subtract vectors then the vectors start from the origin and the result vector connects the tips of the 2 vectors. But when your adding vectors the 1st vector starts from the origin and the 2nd vector starts from the tip of the 1st one,and the result vector starts from the origin.

     Here's what some vector addition would resemble to:

    Here's what some vector subtraction would look like:

            The blue vectors are the vectors that I've been talking about and the red vector is the result vector. Now I'm going to talk about X components and Y components. The X component is how far right or left the vector points. And the Y component is how far up or down the vector points. When you add vectors you add the individual components,for example:if my X component for one vector was 3 and my Y component was 5 then if I add that to another vector that has a X component that's 4 and a Y component that is 2 then my result vector would be X 7 Y 7. And for subtraction it's the same thing.

Now I'm going to talk about scalar multiplication. There are 2 sliders that we haven't used yet called the scalars. The scalars are multiplication sliders that are usually at 1 because 1 times anything is exactly that value but If you change that 1 to a 2 then it will double the magnitude and change the direction of the vectors.
For example: here are my vectors with scalars of 1.

And here are vectors with scalars of 2.
I bet you can see the difference huh? Well even though they aren't the same size they are the same shape. Oh and make sure that you change the scalar for both vectors not just 1.

And last but not least here is my program:

float myFloats[6];

void CollinsBasicVectorEquationCallback(const BasicVectorEquationInfo& data)
{
myFloats[0]=data.x1*data.scalar1;
myFloats[1]=data.y1*data.scalar1;
myFloats[2]=data.x2*data.scalar2;
myFloats[3]=data.y2*data.scalar2;

if(data.add)
{
myFloats[4]=myFloats[0]+myFloats[2];
myFloats[5]=myFloats[1]+myFloats[3];
}
else
{
myFloats[4]=myFloats[0]-myFloats[2];
myFloats[5]=myFloats[1]-myFloats[3];
}

}  
Catch you later! 

Friday, November 8, 2013

Who's the New Guy?

Your going to hate me when I say this but this post is not about assembly.......it's about C++. Now C++ is a type of code that is very commonly used and instead of using the CPU to build it uses the compiler and (here's the funny part) C++'s disassembly builds as assembly so you can double check that it's working right. SO now I have to talk about how C++ works. For one thing C++ is a lot shorter than assembly because 1 C++ instruction can make several assembly instructions. Plus (surprisingly) it takes care of all the hard stuff for you. But don't get to carried away with it or else your assembly skills will drop. I'm still going to have you do some assembly programs after we go over some C++. C++ procedures and assembly procedures look almost nothing alike and as I said C++ procedures are a lot shorter than assembly procedures.
Here is a C++ procedure:

int main()
{
int total=0;
int termindex=0;
while (termindex < 6)
{
// calculate the next term value
int TermValue = raiseToThePower(bases[termindex],exponents[termindex]);

total = determineOperations(operations[termindex],total,TermValue);
termindex++;
}
std::cout << total;
}


Here is an assembly procedure:

doit proc
push eax
push edx
push ebx
push ebp
push ecx
sub esp,4               ; allocating termindex so we can use it 
mov ebp,esp
mov dword ptr [ebp],0
restart:
mov eax,4 ; calculate 4-byte offset into our data
mul dword ptr [ebp] ; Multiply term index
mov edx,eax ; Saving the 4-byte offset into edx

    ; calculate the next term value
mov ecx,bases[edx]
mov ebx,exponents[edx]
call raiseToThePower

    ; do the operation
mov ecx,eax
mov ebx,total
mov eax,operations[edx]
call determineOperations
mov total,ebx

; See if we need to repeat
inc dword ptr [ebp]               
cmp dword ptr [ebp],6
jl restart
pop ecx 
pop ebp
pop ebx
pop edx
pop eax 
add esp,4 ; deallocating termindex
  ret
doit endp


You see in assembly we need to define everything to the right register and it takes so much time and dudududududududu but in C++ it doesn't take so long and you don't have to worry about the registers because  C++ takes care of  them for us. And you don't have to worry about the "Leave No Trace"rule because C++ automatically does the pushes and pops. Now, you know how we carry certain values back and forth between the different procedures? Well how we do that in C++ is we say "return"and then the name of the dword that has the value that we want to pass to a different procedure. But still don't let it get the best of you because assembly is still pretty important.

Well the previous post (Assembly Town) had a program didn't it. This is that same program except in C++.



#include <iostream>

// We go from main to raiseToThePower
// with the bases and exponents (2,7) and go
// into the loop to multiply them together
// as many times as the exponent

int determineOperations(int operation, int currentTotal, int termValue)
{  
int newtotal;
switch(operation)
{
case 0:
newtotal=currentTotal + termValue;
break;
case 1:
       newtotal=currentTotal - termValue;
break;
case 2:
newtotal=currentTotal * termValue;
break;
}
return newtotal;
}      

int raiseToThePower(int base, int exponent)
{
int count=0;
int answerToThePower=1;
while (count <exponent)
{
answerToThePower=answerToThePower * base;
count++;
}
return answerToThePower;
}

int bases      [] = {2,3,6,9,4,2};
int exponents []  = {7,5,3,2,3,3};
int operations [] = {0,0,1,2,0,2};

int main()
{
int total=0;
int termindex=0;
while (termindex < 6)
{
// calculate the next term value
int TermValue = raiseToThePower(bases[termindex],exponents[termindex]);

total = determineOperations(operations[termindex],total,TermValue);
termindex++;
}
std::cout << total;
}


Wednesday, October 30, 2013

Assembly Town

Now we're going to go farther into the "Leave no trace rule".You know my last program? Well yeah it was good but there a few spots where we aren't cleaning up after ourselves but it doesn't affect our program. So what? I'll tell you what we aren't following the "Leave no trace rule". So In this program you are gonna see a lot of pushes and pops. Why? Because we push values onto the stack to save it and then we pop the values back into the registers to restore them. Well most of them anyway. The only ones that we don't push or pop are the return values (outputs). Which basically means that if there's a register that's supposed to change like on determineOperations ebx comes in as the current total and comes out as the new total so you don't push or pop ebx on determineOperations same thing with eax on raiseToThePower. Now I have just a little comment about the pushes and pops they follow LIFO (last in,first out) so we're going to pop in the opposite order we push which means if we say push eax,and then we say push ebx then we have to say pop ebx,and then we say pop eax.

Another thing we need is documentation,documentation is when you put a semi-colon on a blank line or off to the side of a line and type what you are doing there. But when you have documentation you have to make sure you follow it or else your going to have a lot of confusion so if I were you I'd update my code to follow my documentation. But one thing I want to tell you about. Once you change one procedure you have to make sure the other procedures follow it so that the program is going crazy and throwing random numbers at you. And another thing you'll want to do is put white space (blank lines) in between the different parts of the program that do different things and then put documentation above them. Now documentation doesn't hurt your program unless you forget to put a semi-colon at the front of line.
 For example:
raiseToThePower proc
;inputs:
;    ecx->current base
;    ebx->current exponent
;output:
;    eax->answer to the power
So you see how I have the word inputs then I put ecx->current base well the arrow points to the thing that the register is carrying in if it's an input or carrying out if it's an output (get it in-put out-put).

So now we are going to talk about a new word.......abstraction. Abstraction means that we don't care about the details only the important stuff. For example: we don't need to memorize every cog (line of code) in the factory (procedure) unless we're running that factory. So now our code has turned into a business doit prepares the ingredients ships them off to raiseToThePower industries raiseToThePower works with them then sends the output to determineOperations co. determineOperations modifies it then sends it off to doit then it starts all over again.                                                      

Now one more thing I want to talk about with you. You know the push and pop thing? Well I have a question about it. Since determine operations has multiple parts should we put the pops after every one of them? To that I say HA! Because of EVIL DR. REDUNDANCY! We can't have him around here so since there's a return at the end of each part we should change it to a jump and do the pops there and put the return at the end of that.

But do you know what the craziest thing about this whole thing is?.........DENDEDEDEN WE DON'T NEED TO CHANGE THE CHART AT ALL!

Now here's my code:

.model flat, c
.stack 100h
.data

 bases dword      2,3,6,9,4,2
 exponents dword  7,5,3,2,3,3
 operations dword 0,0,1,2,0,2
 total dword 0
.code
doit proc
push eax
push edx
push ebx
push ebp
push ecx
sub esp,4               ; allocating termindex so we can use it
mov ebp,esp
mov dword ptr [ebp],0

restart:
mov eax,4 ; calculate 4-byte offset into our data
mul dword ptr [ebp] ; Multiply term index
mov edx,eax ; Saving the 4-byte offset into edx

    ; calculate the next term value
mov ecx,bases[edx]
mov ebx,exponents[edx]
call raiseToThePower

    ; do the operation
mov ecx,eax
mov ebx,total
mov eax,operations[edx]
call determineOperations
mov total,ebx

; See if we need to repeat
inc dword ptr [ebp]              
cmp dword ptr [ebp],6
jl restart
pop ecx
pop ebp
pop ebx
pop edx
pop eax
add esp,4 ; deallocating termindex
  ret
doit endp

;inputs:
;   operation -> eax
;   current total -> ebx
;   term value -> ecx
;output:
;   new total -> ebx
determineOperations proc

; See which operation we need to perform
push eax
push ecx
cmp eax,0
je Addition
cmp eax,1
je Subtraction
cmp eax,2
je Multiplication
  ret

  Multiplication:
mov eax,ebx ; Move current total to the accumulator
mul ecx     ; Multiply the term value against the current total
mov ebx,eax ; Move the new total back to the return register (ebx)
    jmp popland

  Subtraction:
sub ebx,ecx
jmp popland

  Addition:
add ebx,ecx
jmp popland

  Popland:
    pop ecx
pop eax
ret
determineOperations endp


; inputs:
; ecx -> current base
;   ebx -> current exponent
; output:
;   eax -> answer to the power
raiseToThePower proc
push ebp
push edx
push ebx
push ecx
sub esp,4             ; Allocating 4 bytes on the stack for count
mov ebp,esp
mov dword ptr [ebp],0 ; Initializing count to zero
mov eax,1             ; Start with base^0
again:
cmp dword ptr [ebp],ebx
je weAreDone
mul ecx               ; Raise to the next power
inc dword ptr [ebp]
jmp again
weAreDone:
mov ebx,eax
add esp,4

pop ecx
pop ebx
pop edx
pop ebp
  ret
raiseToThePower endp

end




Tuesday, October 15, 2013

GET YOUR HANDS OFF MY STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

On this post we're going to talk more into privacy of the separate procedures. you know count and termindex? Well raisetothepower is the only procedure that uses count and doit and determineoperations are the only ones that use termindex. So why should they be in global scope (.data)? the answer is they shouldn't. So how are we going to make count invisible to doit and determineoperations but not to raisetothepower,and termindex invisible to raisetothepower but not to doit and determineoperations? Well 1st I need to introduce you to the 3 sections of ram: .code,data,and stack. But which one will we need? The answer is................
                                                              STACK
Yes stack is the one that can help us because we have esp,and ebp and we have tons of room in stack. The way we can access the stack is with ebp because that's what it's made for accessing into other sections of memory. So it's simple to access the stack all we need to do is say mov ebp,esp. But wait little red flag coming up if it's at the top of the stack it's important right so we should swim through it not over it (hee hee hee Nemo) and how we do that is we say sub esp,4 (each spot in ram is 4 bytes apart and 4 bytes is just enough room so this is our new top of the stack.) because we don't wanna mess with those jellyfish (heehee) or stomp on our ret value that will also be held in ram because it will be located at the top of the stack and if we don't update the stack pointer to a different location the instruction pointer (eip) will have the value of termindex and eip will take us off into neverneverland when we return, thus it's critical that we update the stack pointer because each procedure should be able to assume that the stack pointer is pointing to the top of the stack,so now that we have a spot that we can do whatever we want we can get going on using it as a spot for count.
remember we're trying to make each procedure as independent from each other as possible

Now we gotta talk about what I call the "Every man for himself"rule. It's basically stating that the procedures must be as independent from each other as possible so we need to make sure that doit, determineoperations, & raiseToThePower are getting as little help from each other as possible which means that they need to manage by themselves. So how are we going to do that?Well we need to talk about the "Leave no trace"rule. It means that if raiseToThePower wants to use the stack pointer and ebp sure it can use them BUT only as long as it sets them back to where they originally were.

Here's my code:

.model flat, c
.stack 100h
.data
 total dword 0
 bases dword      2,3,6,9,4,2
 exponents dword  7,5,3,2,3,3
 operations dword 0,0,1,2,0,2
.code
doit proc
;    ecx will get the base ebx will get the exponent and the program will run all the way through
; when we get back to doit we will start over again

  sub esp,4
  mov ebp,esp
  mov dword ptr [ebp],0
  ; inc may be in the wrong spot,or be inc the wrong thing because it should be inc termindex
restart:
  mov eax,4
  mul dword ptr [ebp]
  mov ebx,eax
  mov ecx,bases[ebx]
  mov ebx,exponents[ebx]
  call raiseToThePower
  call determineOperations
  inc dword ptr [ebp]                                                                                
  cmp dword ptr [ebp],6
  jl restart
  add esp,4
  ret
doit endp
;inputs:
;     termindex
;output:
;    total will have the current total
determineOperations proc
  mov ebx,eax
  mov eax,4
  mul dword ptr [ebp]

  cmp operations[eax],0
  je Addition
  cmp operations[eax],1
  je Subtraction
  cmp operations[eax],2
  je Multiplication

  ret
  Multiplication:
 mov eax,total
 mul ebx
 mov total,eax
 ret
  Subtraction:
 sub total,ebx
 ret
  Addition:
 add total,ebx
 ret
determineOperations endp


; inputs:
; ecx -> current base
;   ebx ->current exponent
; output:
;   eax ->answer to the power
raiseToThePower proc
  push dword ptr [ebp]
  sub esp,4
  mov ebp,esp
  mov dword ptr [ebp],0
  mov eax,1
again:
  cmp dword ptr [ebp],ebx
  je weAreDone
  mul ecx
  inc dword ptr [ebp]
  jmp again
weAreDone:
  add esp,4
  pop dword ptr [ebp]
  ret
raiseToThePower endp

end


So what's different about this program and other programs that we've done so far? Well at the top of raiseToThePower we have a push to save the value of ebp to the top of the stack and a pop at the end to restore it and lots of various changes that I would advise you to look for. WELL that has been the end of this post so good-bye now see ya!