Unit 7
Unit 7
section .bss
stack resb 100 ; Stack for storing opening parentheses
top resb 1 ; Top pointer for the stack
section .text
global _start
_start:
mov esi, string ; Point ESI to the string
mov byte [top], 0 ; Initialize the top of the stack to 0
check_parentheses:
mov al, [esi] ; Load the current character
cmp al, 0 ; Check if it's the end of the string
je check_end ; If end, jump to check_end
push_stack:
mov al, '(' ; Push '(' onto the stack
mov bl, [top] ; Get the current top of the stack
mov [stack+ebx], al ; Store '(' at the top
inc byte [top] ; Increase the top of the stack
jmp next_char
pop_stack:
cmp byte [top], 0 ; Check if the stack is empty
je unbalanced ; If empty, it's unbalanced
dec byte [top] ; Pop the stack
jmp next_char
next_char:
inc esi ; Move to the next character
jmp check_parentheses
check_end:
cmp byte [top], 0 ; If stack is empty, parentheses are balanced
je balanced
jmp unbalanced
balanced:
mov edx, 19 ; Length of balanced message
mov ecx, msg_balanced
jmp print_result
unbalanced:
mov edx, 20 ; Length of unbalanced message
mov ecx, msg_unbalanced
print_result:
; Linux sys_write example (or other suitable output)
mov eax, 4
mov ebx, 1
int 0x80
Explanation:
1. Design and Development Process
In this assignment, I designed a program to check if a sequence of parentheses (e.g.,
(()())) is balanced using a stack. A stack is a simple and highly effective data structure to
solve problems involving pairs of symbols, such as parentheses. The stack operates on
a Last-In-First-Out (LIFO) basis, meaning the last item pushed onto the stack is the first
one to be removed, making it ideal for keeping track of paired symbols in a sequence.
The process of solving the problem can be broken down into the following steps:
Step 1: String Traversal
The program starts by reading each character in the string, which contains parentheses
like (()()). It processes one character at a time, determining if it's an opening parenthesis
( or a closing parenthesis ).
Step 2: Push Operation (for opening parentheses)
When the program encounters an opening parenthesis (, it pushes it onto the stack.
This means that the program "remembers" the presence of an unmatched opening
parenthesis. The position of this opening parenthesis is stored in memory by adding it to
the stack.
Step 3: Pop Operation (for closing parentheses)
When the program encounters a closing parenthesis ), it checks the stack. If the stack is
not empty, it means there's an unmatched opening parenthesis that can pair with the
closing parenthesis. The program removes the last pushed opening parenthesis from
the stack (this operation is called "popping" from the stack). If the stack is empty at this
point, it indicates that there's no matching opening parenthesis for the closing
parenthesis, meaning the sequence is unbalanced.
Step 4: End of the String Check
Once the entire string has been processed, the program checks the stack one last time.
If the stack is empty, it means all opening parentheses had corresponding closing
parentheses, and the sequence is balanced. However, if there are still unmatched
opening parentheses left in the stack, the sequence is unbalanced.
2. Understanding Gained from Low-Level Programming
This project reinforced my understanding of several important low-level programming
concepts:
Memory Management: Unlike high-level languages where data structures such as
stacks are abstracted away, assembly programming forces the programmer to manually
manage memory. The stack in this project is implemented by directly manipulating
memory locations and tracking the "top" of the stack.
Control Over Execution: Assembly language offers direct control over how data is
processed and stored. This level of control helps build a deeper understanding of how
high-level operations, like pushing and popping from a stack, are implemented at the
hardware level.
Efficient Use of Resources: Writing the program in assembly requires careful
management of CPU registers and memory, leading to more efficient programs. While
high-level languages handle memory allocation automatically, assembly programming
forces the programmer to think about how much memory is being used and how to
optimize the code.
By writing this program, I gained a clearer understanding of how data structures like
stacks operate at a low level and how these structures can be manually controlled to
solve real-world problems.
3. Benefits of Using Assembly Language for High-Level Data Structures
Using assembly language for high-level data structures, like a stack, offers several
advantages:
Efficiency
Assembly language provides direct access to memory and CPU registers, allowing the
programmer to write highly efficient code. This is especially important in performance-
critical applications, such as embedded systems or real-time applications, where even
small inefficiencies can lead to slower execution times.
For example, pushing an element onto the stack or popping it off the stack in assembly
language is done by directly manipulating memory addresses, avoiding the overhead
associated with function calls or dynamic memory management in high-level languages.
This makes assembly language suitable for scenarios where efficiency is paramount.
Understanding of System Architecture
Programming in assembly gives a deeper understanding of the underlying hardware. It
makes the programmer more aware of how memory is allocated and managed and how
the CPU processes instructions. This knowledge is valuable not only for assembly
language programming but also when writing high-performance code in higher-level
languages like C or C++.
By implementing a stack manually, I learned how to manage memory manually and
gained an appreciation for how high-level languages abstract away these details.
Fine-Grained Control
In assembly language, the programmer has complete control over how data is stored,
processed, and retrieved. This control allows for optimizations that would not be
possible in higher-level languages. For instance, managing the exact position of the
stack pointer and controlling when data is pushed or popped from the stack gives the
programmer the ability to optimize memory usage.
For a problem like checking balanced parentheses, the control over how memory is
used and how data flows through the system can lead to highly optimized, efficient
code.
Relevance in Embedded Systems
In embedded systems, where resources are limited and performance is critical,
assembly language is often used for tasks that require fine control over the hardware.
Implementing a stack in assembly would be a common task in embedded programming
for managing interrupt routines or tracking nested function calls.
Conclusion
This project provided a valuable learning experience in low-level programming by
demonstrating how high-level data structures like stacks can be implemented directly
using assembly language. It helped me understand how to manage memory, control the
flow of data, and optimize performance. While assembly language is more complex and
time-consuming compared to higher-level languages, the efficiency, control, and
understanding it provides make it a valuable tool, especially in performance-critical and
resource-constrained environments.