# Programs management --- ## Summary - [Creating a simple program](#creating-a-simple-program) - [Generating code](#generating-code) - [An object-oriented approach](#an-object-oriented-approach) - [Program parts](#program-parts) ## Creating a simple program First, we will create a very simple program. The goal is to show how to set up programs. \ Let's start by importing the `Program` class. ```py from libpyosdev.program import Program ``` Next, we need to create an instance of it. `Program` constructor takes **2 required parameters** and 1 is facultative. ```py Program( name: str, arch: ArchitectureInfos, output: str="output.asm" ) ``` The `name` parameter does not need to contain a file extension: it is added automatically at the code generation phase. \ The `arch` parameter tells which architecture is used by the program. You need to assign a specific architecture instance to it. \ The `output` parameter contains the name of the output file and is set by default to `"output.asm"`. But you can modify it, for example to `"output/myprog.asm"`. In our example, we will keep the default value of `output`. ```py prog = Program("first_program", None) # output = "output.asm" ``` As you can see, the `arch` parameter is set to `None`. It mustn't, because the program will not be able to know what architecture instruction set and registers it is supposed to use. Let's use the **Intel x86 8086** architecture. ```py from libpyosdev.x86_8086.infos import info8086 prog = Program("first_program", info8086) ``` The `info8086` is an instance of the `ArchitectureInfos` class and contains all the required informations about the 8086 architecture. ## Generating code Now, let's generate the code of the program. We will use the `write()` function of `Program`, which creates a file and writes code inside. \ If we simply call it right after our `prog` definition, we will get an output file with the content below: ```asm ; This program has been generated by the pyosdevlib Python library ; and is not intended to be edited ; ``` These comments are generated by the `Program.generate_preamble()` function, which is called when `Program` instances are initialized. To add some assembly, we can use `Program.generate_line()`, used to generate a raw line of Assembly code. ```py generate_line( line: str ) ``` We can use it to generate some instructions: ```py prog.generate_line("mov ax, 0x1234") prog.generate_line("mov bx, ax") ``` If we call `write()`, we will get this: ```asm ; This program has been generated by the pyosdevlib Python library ; and is not intended to be edited ; mov ax, 0x1234 mov bx, ax ``` ## An object-oriented approach For big projects, it is better to use OOP (Object-Oriented Programming) to organize the code. It works very well with LibPyOSDev. \ We must define a class that heritates from `Program`. For example: ```py class MyProgram(Program): def __init__(self): super().__init__("my_program", info8086) ``` Next, let's add a main function to this class, which will be used to generate the code. ```py def run(self): self.generate_line("mov ax, 0x1234") self.generate_line("mov bx, ax") self.write() ``` To generate the code, we just need to create an instance of our program class and call the `run()` method defined above. ## Program parts By using this approach, it is much better to separate your program into several parts, represented by Python functions. There are two main ways to do it. The first way is to define our parts and call them in `run()`. ```py def run(self): self.part1() self.part2() self.part3() # ... self.write() ``` It's okay for small programs, but when you make very big ones, it becomes annoying. This is why the second method is more recommended. It consists in using the **program parts definition convention** of LibPyOSDev. The name of each part must start with `_X_`, where `X` is a number. Then, every functions matching this pattern will be called in order by `write()`. Here is an example on how it works: ```py def _1_first_step(self): self.generate_line("start:") self.generate_line(" mov ax, 10") self.generate_line(" mov bx, 5") self.generate_line(" add ax, bx") self.generate_line(" jmp hang") def _2_second_step(self): self.generate_line("hang:") self.generate_line(" jmp hang") def run(self): self.write() ``` You can also define the second step before the first one, they will always be called in order. Here is the generated code: ```asm start: mov ax, 10 mov bx, 5 add ax, bx jmp hang hang: jmp hang ```