Programs management


Summary

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.

from libpyosdev.program import Program

Next, we need to create an instance of it. Program constructor takes 2 required parameters and 1 is facultative.

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.

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.

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:

; 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.

generate_line(
    line: str
)

We can use it to generate some instructions:

prog.generate_line("mov ax, 0x1234")
prog.generate_line("mov bx, ax")

If we call write(), we will get this:

; 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:

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.

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().

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:

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:

start:
    mov ax, 10
    mov bx, 5
    add ax, bx
    jmp hang
hang:
    jmp hang