Lab 5: Subprograms

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.

Subroutine template
1
2
3
4
5
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

Subroutines

When is it a subroutine or a subprogram? It depends on the scope and size of the task to accomplish. Our 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.

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

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

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.

    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
    
  2. Run the code and verify that the registers contain the expected values

    Expected Results
    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
    
  3. 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.

    1. Sum each register

    2. Divide by 4

    3. Dump the registers verify that values

  4. Call the subprogram and then print the results.

    Expected Results: 256+512+1024+2048 / 4 = 960 (0x03C0)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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

Lab 5 Solutions