Powers (Exponents) POWER A,B = Takes register A to the power of register B, which is written as A^{B}. Stores result to A. POWER is a great instruction. Now we know how to add, subtract, multiply, divide, and take powers in ASM. Guess what? The instruction POWER does not exist. I made it up. *mua ha ha ha ha* All right, all right. I will say that ADD, SUB, IMUL, IDIV, LEA, and all those other ones I've mentioned before are real. But POWER is not. So, if we want to use exponents, we can't do it at all? Wrong. We can totally use powers, but we have to calculate them in terms of IMUL. A power or exponent is just multiplying a number by itself a bunch of times. So by using IMUL a bunch of times, we can do the same thing as POWER would if it were a real instruction. In this case, we want to create our own function that takes in some PUSHes and calculates the result of a power operation. The Simple Power Function Here's a simple function that will calculate power operations for us. It will take EAX to the power of ECX, which is written as EAX^{ECX}. The result will be stored to EAX. Address Instruction Comments 00493A20 CMP ECX,0 ;Is the power 0? 00493A23 JE 493A32 ;If yes, then jump to MOV EAX,1. 00493A25 MOV EDX,EAX ;Otherwise, save the original value of EAX. 00493A27 DEC ECX ;Beginning of the cycle. Decrease ECX by 1. 00493A28 CMP ECX,0 ;Now is ECX less than or equal to 0? 00493A2B JLE 493A37 ;If yes, then break the cycle by jumping to RETN. 00493A2D IMUL EAX,EDX ;Otherwise, multiply EAX by its original value. 00493A30 JMP 493A27 ;Repeat the cycle. 00493A32 MOV EAX,1 ;EAX to the power of 0 must be 1. 00493A37 RETN ;Return to the address after the CALL.Now we can take powers. MOV EAX,9 MOV ECX,5 CALL 493A20The above code will calculate 9^{5} = 59049, which is E6A9 in hex. So E6A9 is stored to EAX. Also notice that ECX and EDX are used when calculating the power. If we want to save the values of ECX and EDX, we just do this: MOV EAX,9 MOV ECX,5 PUSH ECX PUSH EDX CALL 493A20 POP EDX POP ECXThe Complex Power Function The simple power function is quite useful, but it has limitations. You're forced into using EAX and ECX. You can't take EDX to the power of EAX unless you MOV the values of the registers around a lot. This is our new goal: The Better Power Function: PUSH (registerA ID number) PUSH (registerB ID number) CALL 493A40 ;Calculates registerA to the power of registerB. Stores result to registerA. ADD ESP,8This new power function should let us choose which registers we want to use instead of telling us that we have to use EAX and ECX. To make this function, we have to invent identification numbers for each register:
Address Instruction 00493A40 PUSH EBP ;--- These two lines of 00493A41 MOV EBP,ESP ;--- code start the function. 00493A43 CMP DWORD [EBP+C],1 ;Does registerA have ID number = 1? 00493A47 JNE 493A4C ;If not, go check for ID number = 2. 00493A49 PUSH EAX ;If yes, then EAX is the 1st register we want. 00493A4A JMP 493A56 ;Go check for registerB. 00493A4C CMP DWORD [EBP+C],2 ;Does registerA have ID number = 2? 00493A50 JNE 493A55 ;If not, the ID must be 3. 00493A52 PUSH ECX ;If yes, then ECX is the 1st register we want. 00493A53 JMP 493A56 ;Go check for registerB. 00493A55 PUSH EDX ;We know the ID is 3, so EDX is the 1st register we want. 00493A56 CMP DWORD [EBP+8],1 ;Does registerB have ID number = 1? 00493A5A JNE 493A5F ;If not, check for ID number = 2. 00493A5C PUSH EAX ;If yes, then EAX is the 2nd register we want. 00493A5D JMP 493A69 ;Go POP the previously PUSHed values into the right registers. 00493A5F CMP DWORD [EBP+8],2 ;Does registerB have ID number = 2? 00493A63 JNE 493A68 ;If not, the second ID number must be 3. 00493A65 PUSH ECX ;If yes, then ECX is the 2nd register we want. 00493A66 JMP 493A69 ;Go POP the previously PUSHed values into the right registers. 00493A68 PUSH EDX ;We know the second ID is 3, so EDX is the 2nd register we want. 00493A69 POP ECX 00493A6A POP EAX 00493A6B CALL 493A20 ;Call the simple power function and calculate EAX to the power of ECX. 00493A70 CMP DWORD [EBP+C],1 00493A74 JE 493A82 ;If the first ID number = 1, exit the function since EAX already holds the answer. 00493A76 CMP DWORD [EBP+C],2 00493A7A JNE 493A80 00493A7C MOV ECX,EAX ;If the first ID number = 2, store answer into ECX. 00493A7E JMP 493A82 00493A80 MOV EDX,EAX ;If the first ID number = 3, store answer into EDX. 00493A82 MOV ESP,EBP ;--- These three lines of 00493A84 POP EBP ;--- code will exit the 00493A85 RETN ;--- function.Now, the above block of code is quite complex and long. At the very least, it's one of the longest pieces of handwritten ASM code we have seen so far. One thing that you should notice is that we accessed the arguments that were PUSHed right before the CALL to the function. In this case, the last argument PUSHed before the CALL is registerB, so registerB's ID number will be inside DWORD [EBP+8]. registerA is the second to last argument PUSHed, so registerA's ID number will be inside DWORD [EBP+C]. To make the job of understanding this function easier, I'll translate the ASM to psuedocode: begin the "Complex Power Function" if (register A has ID number of 1) then PUSH EAX otherwise if (register A has ID number of 2) then PUSH ECX otherwise PUSH EDX afterwards if (register B has ID number of 1) then PUSH EAX otherwise if (register B has ID number of 2) then PUSH ECX otherwise PUSH EDX afterwards POP ECX ;The value of register B now goes into ECX. POP EAX ;The value of register A now goes into EAX. CALL (simple power function) ;Simple power function will calculate EAX to the power of ECX. if (register A has ID number of 1) then exit this function otherwise if (register A has ID number of 2) then store EAX to ECX exit this function otherwise store EAX to EDX exit this functionNow, if you look at the complex power function, you'll realize that it doesn't do the "calculating" part by itself. In fact, the complex power function just shuffles around the registers until the value of register A gets stored into EAX, and the value of register B gets stored into ECX. Then it shuffles around the registers some more until the final answer gets stored to register A, whatever register that may be. In order to do the job of calculating, the complex power function will CALL the simple power function! This is one of the key points of function design: a function is allowed to CALL other functions. We put the simple power function at 493A20, and we put the complex power function at 493A40. The two functions are far enough from each other that they don't overlap. Here's a quote from a famous writer:
Jonathan Swift is thinking about the possibility that a big flea may have smaller fleas that feed upon that big flea, and those small fleas have even smaller fleas, and so on until we encounter fleas that are almost impossibly small.^{[1]} Even though big fleas don't really have miniature fleas that live on them, we can easily apply a similar idea to Cave Story assembly hacking:
Now we put this function to work. Instructions PUSH 2 ;ECX has ID = 2. PUSH 3 ;EDX has ID = 3. CALL 493A40 ;Calculates ECX to the power of EDX, stores to ECX. ADD ESP,8 Instructions PUSH EAX PUSH ECX ;Save the values of EAX and ECX. PUSH 3 PUSH 3 CALL 493A40 ;Calculates EDX to the power of itself, stores to EDX. ADD ESP,8 POP ECX POP EAX ;Load the values of EAX and ECX.Notice that the first function call is equivalent to the fake command POWER ECX,EDX. The second function call is the same as the fake command POWER EDX,EDX, and it doesn't mess up the values of EAX and ECX because we remembered to preserve them. Previous Lesson: Structure of Functions Next Lesson: Object Oriented Programming Table of Contents ^{[1]}Actually, Mr. Swift was mocking mathematicians and he knew it was ridiculous for big fleas to contain small fleas. Go figure. ^{[2]}I cannot say "ad infinitum" at the end, because your computer does not have an infinite amount of memory and therefore an infinite number of function CALLs don't actually happen while Cave Story is running. There are a lot of CALLs that occur, just not an infinite number of them. Also, a function doesn't necessarily need to call other functions... it just might call other functions. |