*************************************** Lab 5: Subprograms *************************************** .. include:: /includes/prolog.inc .. include:: ../asm-urls.rst .. contents:: Table of Contents Overview =========== We saw how to use labels to jump to a specific address (or location of the code) using ``call`` and then how to return to using ``ret`` to perform some specific action that operates like a function. .. code-block:: asm :caption: Subroutine template :linenos: label: push ecx ; save register X data on stack ; do something with ECX (or other register) pop ecx ; restore data to register X ret ; return to the address that called the label We can use the same the logic to create a subprogram. - A subprogram is a piece of code that starts at some address in the text segment. - The program can jump to that address to ``call`` the subprogram. - When the subprogram is done executing, it jumps back to the instruction after the call. - The original execution resumes “as if nothing had happened”. Sources ---------- - |NASM Subprograms (Module)| - University of Hawaii - |Subprograms 101| - University of Hawaii Presentation - |Subprogram Arguments| - University of Hawaii Presentation - |Subprogram Local Variables| - University of Hawaii Presentation Subroutines -------------- When is it a *subroutine* or a *subprogram*? It depends on the scope and size of the task to accomplish. Our :ref:`nasm_template` file is nothing more than a subprogram. Here is an example subprogram that determines if the number in ``ebx`` is odd. This code block defines a new ``segment .text`` section that contains the label ``is_odd``. The subprogram could reserve memory in segments ``.bss`` or ``.data``. .. code-block:: text :caption: Sample subprogram ; FUNCTION: is_odd ; Takes one parameter: an integers (> 0) ; Destroys values of eax and ecx (eax = returned value) segment .text is_odd: push ebp ; save the value of EBP of the caller mov ebp, esp ; update the value of EBP for this subprogram mov eax, 0 ; EAX = 0 mov ecx, [ebp+8] ; EBX = integer (parameter #1) shr ecx, 1 ; Right logical shift adc eax, 0 ; EAX = EAX + carry (if even: EAX = 0, if odd: EAX = 1) pop ebp ; restore the value of EBP ret ; clean up Subprogram template --------------------- .. code-block:: text :caption: Subprogram template that returns a value ;--------------------------------------------------------------- ; FUNCTION: subprogram_name ; Takes one parameter: an integers (> 0) ; Destroys values of eax and ecx (eax = returned value) ;--------------------------------------------------------------- segment .data segment .bss returnvalue resd 1 ; place in memory for the return value segment .text subprogram_name: ; name of the subprogram push ebp ; save original EBP mov ebp, esp ; set EBP = ESP pusha ; save all (including new EBP) . . . ; subprogram code ; Place return value in EAX mov [returnvalue], eax ; save return value in memory popa ; restore all (including new EBP) mov eax, [returnvalue] ; retrieve the saves return value pop ebp ; restore original ebp ret ; return Sample Subroutines --------------------- - |count-odds.asm.txt| - |recursive-sum.asm.txt| - |fibonacci.asm.txt| Create a Simple Subprogram ============================ You will create a simple subprogram that calculates the average value in the register, and then returns the value in EAX. 1. Start with the code that reads a value into each register. .. code-block:: text :caption: Starter code enter 0,0 ; Initialize register EBP pusha ; Push register contents to the stack ; mov eax, msg_int call print_string ; Get int call read_int ; eg: EAX = 256 (0x0100) mov ebx, eax ; copy to other registers mov ecx, eax mov edx, eax shl ebx, 1 ; perform a left bit shift of 1 place shl ecx, 2 ; perform a left bit shift of 2 places shl edx, 3 ; perform a left bit shift of 3 places ; call print_nl ; ; Example ; EAX = 00000100 EBX = 00000200 ECX = 00000400 EDX = 00000800 dump_regs 0 ; verify registers ; call average ; Calculates the average, store in EAX ; ; Registers EBX, ECX, and EDX should be unchanged ; EAX contains the average: 256+512+1024+2048 / 4 = 960 (0x03C0) ; ; EAX = 00000027 EBX = 0000001F ECX = 0000002F EDX = 0000003F dump_regs 2 ; verify registers ; mov ecx, eax ; save the returned value in EAX mov eax, msg_avg ; Move string to print call print_nl call print_string mov eax, ecx ; Move avg to EAX to print call print_int call print_nl ; popa ; Restore register data from the stack mov eax, 0 ; flush EAX of data leave ; restore the stack ret ; Return from main back into C library wrapper ; -------------------------------- ; Place the subprogram here #. Run the code and verify that the registers contain the expected values .. code-block:: text :caption: Expected Results :emphasize-lines: 4 Enter an integer: 256 Register Dump # 0 EAX = 00000100 EBX = 00000200 ECX = 00000400 EDX = 00000800 ESI = F7F38000 EDI = 00000000 EBP = FFAAD888 ESP = FFAAD868 EIP = 565EE794 FLAGS = 0206 PF #. Create a subprogram called ``average`` that will calculate the average value in the registers and then return the value in EAX. .. note:: Place the template code of the subprogram at the bottom of your asm file. **Yes, you should COPY and FILL IN the comment header.** a. Sum each register #. Divide by 4 #. Dump the registers verify that values #. Call the subprogram and then print the results. .. code-block:: text :caption: Expected Results: 256+512+1024+2048 / 4 = 960 (0x03C0) :linenos: :emphasize-lines: 4, 14 Enter an integer: 256 Register Dump # 0 EAX = 00000100 EBX = 00000200 ECX = 00000400 EDX = 00000800 ESI = F7F38000 EDI = 00000000 EBP = FFAAD888 ESP = FFAAD868 EIP = 565EE794 FLAGS = 0206 PF Register Dump # 1 EAX = 000003C0 EBX = 00000200 ECX = 00000004 EDX = 00000000 ESI = F7F38000 EDI = 00000000 EBP = FFAAD860 ESP = FFAAD840 EIP = 565EE7E2 FLAGS = 0206 PF Register Dump # 2 EAX = 000003C0 EBX = 00000200 ECX = 00000400 EDX = 00000800 ESI = F7F38000 EDI = 00000000 EBP = FFAAD888 ESP = FFAAD868 EIP = 565EE7A0 FLAGS = 0206 PF The average of the registers is: 960 Solutions ============================== .. toctree:: :caption: Lab 5 Solutions solutions