Let's say you want to do something interesting. Like call a TSC event using assembly code. How would you do that?
Well, no fears. Pixel already has "pre-built" functions that are easy to use via the CALL instruction. Functions work like this:
Let's look at a function. These 3 instructions will call a TSC event on the map: Instruction Comments PUSH (event number) ;The number of the TSC event you want to call. THIS MUST BE CONVERTED TO HEX FIRST. CALL 00421990 ;This is the specific CALL that will cause the TSC event to be run. ADD ESP,4 ;Fix the stack.Pixel already wrote the code that's been compiled and now located at address 421990, so we can just use the CALL immediately. Instruction Comments PUSH 11 ;11 (hex) = 17 (dec). This will run event 17. CALL 00421990 ADD ESP,4Event 17 is the "refill health and missiles" event by default. So if you run this piece of code, Quote's health/missiles will be refilled along with a dialog box to tell you that it happened. There are many more functions that do useful things. Like the following: Add some weapon energy: Instruction Comments PUSH (amount of weapon energy) ;Numbers must be in hex. CALL 004196F0 ADD ESP,4Play a song: Instruction Comments PUSH (music ID number) ;This is the TSC number of the song. Translate it to hex first. CALL 00420EE0 ADD ESP,4You can find more of these functions in the Assembly Compendium. Fix the Stack? This has probably confused you more than it has helped you. The "fix the stack" part is just the ADD ESP,(number) command that's at the bottom of any call to a function. The ADD ESP,(number) part removes the need for POP after calling a function.
Here's some more info: Instruction Comments PUSH (music ID number) CALL 00420EE0 ADD ESP,4 ;We PUSHed only once, so we use ADD ESP,4Play a sound effect: Instruction Comments PUSH (channel number) PUSH (Sound ID number) CALL 00420640 ADD ESP,8 ;We PUSHed twice, so we use ADD ESP,8Notice that for playing a sound, we need to PUSH two numbers instead of one. These numbers tell the function two things: the channel number, and the # of the sound to play. So, back to the "fix the stack part". In order to fix the stack after a function, you multiply the (number of times you PUSHed) by 4, then use ADD, ESP (that number). If you pushed one time before CALLing the function, it's ADD ESP,4. If you push 2 times, it's ADD ESP,8. Also, be careful when multiplying by 4. It's obvious that 1*4 = 4, and 2*4 = 8. However, 4*4 is not 16. We are using hexadecimal, so 4*4 = 10 in hex. (Hopefully studying assembly will not cause you to fail at basic multiplication in real life) Here's a super-sized function that requires 8 PUSHes! Spawn an NPC:[1] Instruction Comments PUSH 0 PUSH 0 PUSH (direction) ;direction of NPC. 0 = left, 2 = right. PUSH (Y velocity) ;y-velocity of NPC PUSH (X velocity) ;x-velocity of NPC PUSH (Y) ;y-position of NPC PUSH (X) ;x-position of NPC PUSH (NPC ID) ;TSC NPC number, in hex CALL 0046EFD0 ;perform create NPC function. ADD ESP,20 ;4*8 = 32 (dec) = 20 (hex)Also, don't be under the impression that EAX, ECX, and EDX are somehow magically saved before and after you use a function. Keep in mind that registers are not supposed to be permanent storage locations. If you call a function, that function will probably use EDX, EAX, and ECX during its calculations. Therefore, the values of the registers will be different after you call a function (unless you made the function yourself and you know exactly what it does). Saving Register Values Preserving the value of a register during a function CALL is easy. Just PUSH whichever register you want to save and then do POP (register) after the function is done. Here I save and load the number located in EAX so that it is preserved after we CALL the "play a sound" function. Address Instruction 004937F4 MOV EAX,1300 004937F9 PUSH EAX ;Save the value of EAX. 004937FA PUSH 1 ;Push 1 (channel number) 004937FC PUSH 1D ;Push 1D (hex) = 29 (dec) 004937FE CALL 00420640 ;Play a sound (29 is the teleport sound) 00493803 ADD ESP,8 ;Fix the stack. 00493806 POP EAX ;Load the value of EAX. Now EAX holds 1300 just like before.It seems as if CALLing a function would screw up the stack because of all the PUSHes and then the CALL and the modifying ESP part. In reality - the structure of an ASM function is carefully designed so that it doesn't screw up the stack. The PUSH EAX and POP EAX parts (highlighted in green) actually work correctly. What if you wanted to save the value of multiple registers instead of just one? This is also easy. Just do: PUSH EAX PUSH ECX PUSH EDXright before the function. After the function, just do: POP EDX POP ECX POP EAXAnd that's it. Previous Lesson: Math Instructions Next Lesson: Valentine Nemesis Table of Contents [1]For the Spawn NPC function, the first two PUSHes do not have to be PUSH 0. The first PUSH actually determines which entity slot the game starts checking at for this NPC. There are 0x200 slots total. The next PUSH is more complicated. You're supposed to do PUSH DWORD [EBP+8] if you want to create a parent/child system between NPCs. The parent can communicate information to its children and vice versa. These are both more complex topics, so if you feel overwhelmed, just know that using those two PUSH 0 commands will work just fine. |