Modular Software Design: Principles and Assembly Process

Design Methodology for Software Development

Modular Programming Principles

When developing a small program, any methodology can be sufficient. However, the problem arises as the program grows, increasing the number of statements and making it difficult to maintain readability and logically follow the implementation. Another factor to consider is that the person who reads code is not necessarily the same as the one who wrote it. Furthermore, different programmers may be involved in developing a single application.

It seems clear that the solution lies in developing complex programs from short sequences, or modules, each performing a well-defined task. This approach is known as modular programming.

Modularity should not only be present in the code but also throughout the entire development process, encompassing the following tasks:

  • Precise specification of what the program needs to do.
  • Division of the overall problem into smaller, manageable tasks.
  • Precise definition of what each task should do and how they should communicate.
  • Writing tasks and connecting assembly language modules to form the complete program.
  • Debugging and testing of the program.
  • Documentation of the program.

The primary tool used for dividing programs into modules is the **tree diagram**. This block-oriented figure summarizes the relationships between modules (tasks) and sub-modules (subtasks). A tree diagram clearly shows the chain of subordination between modules and submodules, which is helpful to both the programmer and anyone else who wants to understand or discuss the program, providing a clear view of its functional structure.

The reasons for dividing a program into smaller parts include:

  • Modules are easier to understand and manage.
  • Different modules can be assigned to different programmers, facilitating parallel development.
  • Debugging and testing can be conducted more systematically.
  • Documentation can be understood more easily.
  • Changes can be localized, reducing the risk of unintended side effects.
  • Frequently used tasks can be programmed as modules and stored in libraries for reuse across various programs.

When designing program modules, one should consider how data enters and leaves them, control flow, and how information is communicated between them (known as data coupling). In general, the selection and design of modules should aim to simplify control coupling and minimize data coupling.

Most assemblers support the modularization process in three primary ways:

  • Allowing data to be structured for access by multiple modules.
  • Providing mechanisms such as procedures or subroutines.
  • Allowing the insertion of code sections, known as macros, using simple statements.

The Assembly Process Explained

An **assembler** is a program that converts a source code listing into *object code*, i.e., machine language (which the processor ultimately interprets). Object code typically only lacks references to external routines. The source code is a file containing the sequence of assembly language instructions forming the program, as well as certain directives or commands for the assembly process itself, usually created using a text editor. Object code consists of machine language code and information required for linking with other object modules.

Linking and Relocation in Software Development

When building a program, some of its modules can be placed in the same source module and assembled together, while others may reside in different modules to be assembled separately. If assembled separately, the main module (which contains the first instruction to be executed) should end with an END statement to indicate the program’s entry point. Each of the other modules must also end with an END statement, but without any operation. In any case, the resulting object modules (some of which can be grouped into libraries) must be *linked* to form the *load module* before the program can be executed.

In addition to the output load module, the **linker** prints a memory map indicating where the object modules are loaded in memory. After building the load module, it is *loaded* by the **loader** into the computer’s memory, and execution begins.