Subroutines and procedures are fundamental concepts in assembly language programming that allow you to structure your code into reusable and manageable blocks. This section will cover the following topics:

  1. What are Subroutines and Procedures?
  2. Defining and Calling Subroutines
  3. Passing Parameters to Subroutines
  4. Returning Values from Subroutines
  5. Practical Examples
  6. Exercises

  1. What are Subroutines and Procedures?

Subroutines (also known as procedures or functions) are blocks of code designed to perform a specific task. They help in:

  • Code Reusability: Write once, use multiple times.
  • Modularity: Break down complex problems into smaller, manageable parts.
  • Maintainability: Easier to debug and update.

  1. Defining and Calling Subroutines

Defining a Subroutine

In assembly language, a subroutine is defined using labels and the RET instruction to return control to the calling code.

; Example of a simple subroutine
my_subroutine:
    ; Subroutine code here
    RET

Calling a Subroutine

To call a subroutine, use the CALL instruction followed by the subroutine's label.

CALL my_subroutine

Example

section .data
    msg db 'Hello, World!', 0

section .text
    global _start

_start:
    CALL print_message
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

print_message:
    ; Print the message
    mov eax, 1          ; syscall: write
    mov edi, 1          ; file descriptor: stdout
    mov rsi, msg        ; message to write
    mov edx, 13         ; message length
    syscall
    RET

  1. Passing Parameters to Subroutines

Parameters can be passed to subroutines using registers or the stack.

Using Registers

section .text
    global _start

_start:
    mov rdi, 5          ; Pass 5 as a parameter
    CALL add_one
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

add_one:
    add rdi, 1          ; Add one to the parameter
    RET

Using the Stack

section .text
    global _start

_start:
    push 5              ; Push 5 onto the stack
    CALL add_one
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

add_one:
    pop rdi             ; Pop the parameter from the stack
    add rdi, 1          ; Add one to the parameter
    RET

  1. Returning Values from Subroutines

Values can be returned from subroutines using registers.

section .text
    global _start

_start:
    mov rdi, 5          ; Pass 5 as a parameter
    CALL add_one
    ; rax now contains the result
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

add_one:
    add rdi, 1          ; Add one to the parameter
    mov rax, rdi        ; Move the result to rax
    RET

  1. Practical Examples

Example 1: Simple Addition Subroutine

section .text
    global _start

_start:
    mov rdi, 5          ; First parameter
    mov rsi, 3          ; Second parameter
    CALL add_numbers
    ; rax now contains the result (8)
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

add_numbers:
    add rdi, rsi        ; Add the two parameters
    mov rax, rdi        ; Move the result to rax
    RET

Example 2: String Length Subroutine

section .data
    msg db 'Hello, World!', 0

section .text
    global _start

_start:
    mov rsi, msg        ; Pass the string
    CALL string_length
    ; rax now contains the length of the string (13)
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

string_length:
    xor rax, rax        ; Clear rax (length counter)
.loop:
    cmp byte [rsi + rax], 0 ; Check for null terminator
    je .done            ; If null terminator, exit loop
    inc rax             ; Increment length counter
    jmp .loop           ; Repeat loop
.done:
    RET

  1. Exercises

Exercise 1: Subtract Two Numbers

Write a subroutine that subtracts two numbers passed in rdi and rsi, and returns the result in rax.

Solution:

section .text
    global _start

_start:
    mov rdi, 10         ; First parameter
    mov rsi, 4          ; Second parameter
    CALL subtract_numbers
    ; rax now contains the result (6)
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

subtract_numbers:
    sub rdi, rsi        ; Subtract the second parameter from the first
    mov rax, rdi        ; Move the result to rax
    RET

Exercise 2: Find Maximum of Two Numbers

Write a subroutine that finds the maximum of two numbers passed in rdi and rsi, and returns the result in rax.

Solution:

section .text
    global _start

_start:
    mov rdi, 10         ; First parameter
    mov rsi, 20         ; Second parameter
    CALL max_number
    ; rax now contains the result (20)
    ; Exit the program
    mov eax, 60         ; syscall: exit
    xor edi, edi        ; status: 0
    syscall

max_number:
    cmp rdi, rsi        ; Compare the two parameters
    cmovge rax, rdi     ; If rdi >= rsi, move rdi to rax
    cmovl rax, rsi      ; If rdi < rsi, move rsi to rax
    RET

Conclusion

In this section, you learned about subroutines and procedures in assembly language, including how to define, call, and pass parameters to them. You also saw practical examples and completed exercises to reinforce your understanding. Subroutines are essential for writing modular and maintainable assembly code, and mastering them will significantly improve your programming skills.

© Copyright 2024. All rights reserved