diff --git a/mia_uppg3.txt b/mia_uppg3.txt
index d61dbb62df11c36dc704cb7cd0ed5c2711dc54a6..b752d1a8049af126da4c59eb188d6765e8bb78ee 100644
--- a/mia_uppg3.txt
+++ b/mia_uppg3.txt
@@ -387,22 +387,22 @@ uM:
 7f: 0000000
 
 K1:
-00: 0a ADD
-01: 0b SUB
-02: 0c MULT
-03: 0f NOP
-04: 12 TEST
-05: 15 D
-06: 1a R 
-07: 1d T
-08: 1f TT
-09: 20 TT
-0a: 22 TTT
-0b: 27 TTTT
-0c: 00 FR
-0d: 00 FH
-0e: 00 LF
-0f: 00 FFF
+00: 0a LOAD
+01: 0b STORE
+02: 0c ADD
+03: 0f SUB
+04: 12 AND
+05: 15 LSR
+06: 1a BRA
+07: 1d BNE
+08: 1f HALT
+09: 20 CMP
+0a: 22 BGE
+0b: 27 BEQ
+0c: 00
+0d: 00 
+0e: 00
+0f: 00
 
 K2:
 00: 03
diff --git a/src/simudator/cli/cli.py b/src/simudator/cli/cli.py
index a375743567c885d6d9f284ab2d6413db0828b4b2..8cece34ab1733200d85c52e342092bc5f697c8fc 100644
--- a/src/simudator/cli/cli.py
+++ b/src/simudator/cli/cli.py
@@ -6,16 +6,16 @@ from os.path import exists, isdir
 from simudator.core.processor import Processor
 
 HELP_TEXT = """Here is a list of possible commands:
-CLI control: 
+CLI control:
    - h, help : Shows this message.
 
    - q, quit : Closes the program. Nothing will be saved.
 
-Simulation control: 
+Simulation control:
    - n [x], next [x]: Simulates x ticks.
      If no x is given, simulates one tick.
 
-   - rc, run_continuously: 
+   - rc, run_continuously:
      Continuously simulate ticks until a HALT is signalled
      or a breakpoint is reached.
 
@@ -30,72 +30,78 @@ State information:
    - p, print: Prints all states of all modules.
 
    - pl, printlist [add/remove] [module name] ...:
-     Add or remove the given modules to a list of modules to print. If no 
+     Add or remove the given modules to a list of modules to print. If no
      arguments are given, i.e. "pl" or "printlist" on their own, all the modules 
      in the list will be printed.
 
-   - pp, pretty print: Prints relevant processor state information in a 
+   - pp, pretty print: Prints relevant processor state information in a
      readable and compact format.
 
-   - ppv, pretty print verbose: Prints all processor state information in a 
+   - ppv, pretty print verbose: Prints all processor state information in a
      readable and compact format.
 
-Breakpoints: 
+Breakpoints:
    - p br, print breaks: Prints all breakpoints.
 
-   - p br lam, print break lambdas: 
-     Prints all available lambdas of this processor to use for lambda 
-     breakpoints. 
+   - p br lam, print break lambdas:
+     Prints all available lambdas of this processor to use for lambda
+     breakpoints.
 
    - br state, break state [module name] [state name] [value]:
-     Add a breakpoint for the module with the given name. The breakpoint brakes 
+     Add a breakpoint for the module with the given name. The breakpoint brakes
      when the specified state of the module has the specified value.
 
-   - br mem, break memory [module name] [adress] [value]:
-     Add a breakpoint for a memory module with the given name. The breakpoint 
-     breaks when the specified adress of the memory has the specified value.
+   - br mem, break memory [module name] [address] [value]:
+     Add a breakpoint for a memory module with the given name. The breakpoint
+     breaks when the specified address of the memory has the specified value.
 
    - br lam, break lambda [lambda_name] [keyword argument] ...:
-     Add a breakpoint that uses the referenced lambda to evaluate when to 
-     break. Any module names in the keyword arguments will be substituted with 
+     Add a breakpoint that uses the referenced lambda to evaluate when to
+     break. Any module names in the keyword arguments will be substituted with
      the actual module. Strings must be enclosed with quotation marks.
 
    - rm br [id], remove break [id]:
      Remove the breakpoint with the given ID.
 
-File I/O: 
+File I/O:
    - l, load [path]:
      Loads a processors state from a given file path.
 
    - sf, save to file [path]:
-     Saves the processor state to the specified file. 
+     Saves the processor state to the specified file.
 """
 
 
 class CLI:
     """
     Terminal based program for controlling a processor.
+
     Mostly used for testing and debugging.
+
+    Parameters
+    ----------
+    processor : Processor
     """
 
     def __init__(self, processor: Processor):
-        self.running = True
-        self.processor = processor
-        self.print_list = []
+        self._running = True
+        self._processor = processor
+        self._print_list = []
 
     def run(self) -> None:
-        """
-        Runs the CLI.
-        Will put the user in an input loop where they can send commands to the program
+        """Run the CLI.
+
+        Put the user in an input loop where they can send commands to the
+        program.
         """
 
         print("Welcome to simuDAtor!")
 
-        while self.running:
+        while self._running:
             # Get user commands here
             user_input = input("> ")
 
-            # Note: Keep the order of the cases consistent with the help text 
+            # Note: Keep the order of the cases consistent with the help text
             # for convenience
             match user_input.split():
                 # CLI control -------------------------------------------------
@@ -106,7 +112,7 @@ class CLI:
                 case ["q"] | ["quit"]:
                     # Ends the loop
                     print("Goodbye")
-                    self.running = False
+                    self._running = False
 
                 # Simulation control ------------------------------------------
                 case ["n"] | ["next"]:
@@ -114,7 +120,7 @@ class CLI:
                     # this could take time for larger simulations,
                     # could be good to add loading icon and stop user input
 
-                    self.processor.do_tick()
+                    self._processor.do_tick()
 
                 case ["n", _] | ["next", _]:
                     # runs a given number of ticks
@@ -123,32 +129,32 @@ class CLI:
 
                     try:
                         for _ in range(int(user_input.split()[1])):
-                            self.processor.do_tick()
-                            if self.processor.is_stopped:
+                            self._processor.do_tick()
+                            if self._processor.is_stopped:
                                 break
-                        if self.processor.breakpoint_reached:
-                            bp = self.processor.last_breakpoint
+                        if self._processor.breakpoint_reached:
+                            bp = self._processor.last_breakpoint
                             print(f"Reached breakpoint: {bp}")
-                        if self.processor.should_halt():
+                        if self._processor.should_halt():
                             print("The processor halted")
                     except ValueError:
                         print("Invalid value")
 
                 case ["rc"] | ["run_continuously"]:
-                    self.processor.run_continuously()
-                    if self.processor.breakpoint_reached:
-                        bp = self.processor.last_breakpoint
+                    self._processor.run_continuously()
+                    if self._processor.breakpoint_reached:
+                        bp = self._processor.last_breakpoint
                         print(f"Reached breakpoint: {bp}")
 
-                    if self.processor.should_halt():
+                    if self._processor.should_halt():
                         print("The processor halted")
 
                 case ["u"] | ["undo"]:
                     # undo one clock cycles
 
-                    current_cycle = self.processor.get_clock()
+                    current_cycle = self._processor.get_clock()
                     try:
-                        self.processor.load_cycle(max(current_cycle - 1, 0))
+                        self._processor.load_cycle(max(current_cycle - 1, 0))
                     except IndexError:
                         print("Index out of range")
 
@@ -162,46 +168,46 @@ class CLI:
                         print("Invalid value")
                         continue
 
-                    current_cycle = self.processor.get_clock()
+                    current_cycle = self._processor.get_clock()
                     try:
-                        self.processor.load_cycle(max(current_cycle - number, 0))
+                        self._processor.load_cycle(max(current_cycle - number, 0))
                     except IndexError:
                         print("Index out of range")
 
                 case ["r"] | ["resets"]:
                     # self.processor.load_cycle(0)
-                    self.processor.reset()
+                    self._processor.reset()
 
                 # State information -------------------------------------------
                 case ["c"] | ["clock"]:
                     # shows current clockcycle
-                    print("Clock is at: ", str(self.processor.get_clock()))
+                    print("Clock is at: ", str(self._processor.get_clock()))
 
                 case ["p"] | ["print"]:
                     print("\n")
                     # prints all module states, is most prbably hard to read
-                    for module in self.processor.get_modules():
+                    for module in self._processor.get_modules():
                         module.print_module()
                         print("\n")
 
                 case ["pl", *_] | ["printlist", *_]:
-                    self.print_list_command(user_input.split())
+                    self._print_list_command(user_input.split())
 
                 case ["pp"] | ["pretty print"]:
                     # pretty print the modules of the processor
-                    self.processor.pretty_print()
+                    self._processor.pretty_print()
 
                 case ["ppv"] | ["pretty print verbose"]:
                     # pretty print the modules of the processor
                     # with all available information
-                    self.processor.pretty_print_verbose()
+                    self._processor.pretty_print_verbose()
 
                 # Breakpoints -------------------------------------------------
                 case ["p", "br"] | ["print", "breaks"]:
-                    self.processor.print_breakpoints()
+                    self._processor.print_breakpoints()
 
                 case ["p", "br", "lam"] | ["print", "break", "lambdas"]:
-                    lambdas = self.processor.get_breakpoint_lambdas()
+                    lambdas = self._processor.get_breakpoint_lambdas()
                     if not lambdas:
                         print("There are no lambdas for this processor.")
                     else:
@@ -212,7 +218,7 @@ class CLI:
                     state_name = user_input.split()[3]
                     val = ast.literal_eval(user_input.split()[4])
                     try:
-                        self.processor.add_state_breakpoint(
+                        self._processor.add_state_breakpoint(
                             module_name, state_name, val
                         )
                     except ValueError as e:
@@ -221,9 +227,11 @@ class CLI:
                 case ["br", "mem", _, _, _] | ["break", "memory", _, _, _]:
                     module_name = user_input.split()[2]
                     try:
-                        adress = ast.literal_eval(user_input.split()[3])
+                        address = ast.literal_eval(user_input.split()[3])
                         value = ast.literal_eval(user_input.split()[4])
-                        self.processor.add_memory_breakpoint(module_name, adress, value)
+                        self._processor.add_memory_breakpoint(
+                            module_name, address, value
+                        )
                     except ValueError as e:
                         print(e)
 
@@ -235,14 +243,14 @@ class CLI:
                         key, value = kwarg.split('=')
                         kwargs[key] = ast.literal_eval(value)
                     try:
-                        self.processor.add_lambda_breakpoint(lambda_name, **kwargs)
+                        self._processor.add_lambda_breakpoint(lambda_name, **kwargs)
                     except ValueError as e:
                         print(e)
 
                 case ["rm", "br", _] | ["remove", "break", _]:
                     try:
                         bp_id = int(user_input.split()[2])
-                        if not self.processor.remove_breakpoint(bp_id):
+                        if not self._processor.remove_breakpoint(bp_id):
                             print(f"No breakpoint with ID {bp_id}")
                     except ValueError:
                         print("Breakpoint ID must be an integer")
@@ -251,36 +259,38 @@ class CLI:
                 case ["l", _] | ["load", _]:
                     # load processor state from file
                     try:
-                        self.processor.reset()
-                        self.processor.load_state_from_file(user_input.split()[1])
+                        self._processor.reset()
+                        self._processor.load_state_from_file(user_input.split()[1])
                     except FileNotFoundError:
                         print("No file called ", user_input.split()[1], " was found")
 
                 case ["sf"] | ["save to file"]:
                     file_path = "cpu_save_file"
-                    self.processor.save_state_to_file(file_path)
+                    self._processor.save_state_to_file(file_path)
 
                 case ["sf", _] | ["save to file", _]:
                     file_path = user_input.split()[1]
-                    self.check_file_path(file_path)
+                    self._save_to_file(file_path)
 
                 # -------------------------------------------------------------
                 case _:
                     # message for unknown command
                     print("Unknown command. Write \"help\" for assistance.")
 
-    def check_file_path(self, file_path) -> None:
-        """
-        Checks the file path user gave as input. If it is a directory
-        this funciton will simply return and not write anything. If it
-        is a file that already exists it will demand a 'yes' or 'no' from
-        user to decide if it should write over the existing file.
-        If the given file does not exist it will simply create the file.
+    def _save_to_file(self, file_path: str) -> None:
+        """Save the processor state to the given file path.
+
+        No save is made if the file path is a directory. If the file already
+        exists, the user is given the choice to overwrite the file.
+
+        Parameters
+        ----------
+        file_path : str
         """
 
         # Given file does not exist, save_state_to_file will create it
         if not exists(file_path):
-            self.processor.save_state_to_file(file_path)
+            self._processor.save_state_to_file(file_path)
 
         else:
             # We dont touch directories
@@ -299,17 +309,27 @@ class CLI:
                     print("Please give an input of 'yes' or 'no'!")
 
             if overwrite == "yes":
-                self.processor.save_state_to_file(file_path)
-                return
-            else:
-                return
-
-    def print_list_command(self, user_input):
+                self._processor.save_state_to_file(file_path)
+
+    def _print_list_command(self, user_input: list[str]) -> None:
+        """Add/remove modules to internal list or print all modules in the
+        internal list.
+
+        Parameters
+        ----------
+        user_input : list[str]
+            A print list command split by spaces. Should be on the format
+            ``["printlist", "add", MODULE_NAME, ...]`` or
+            ``["printlist", "remove", MODULE_NAME, ...]`` to add or remove
+            modules, where MODULE_NAME is the name of a module as a string,
+            or ``["printlist"]`` to print all added modules.
+            (``"printlist"`` can be substituted with ``"pl"``)
+        """
         # no additional commands so only print
         if len(user_input) == 1:
             print("\n")
-            for name in self.print_list:
-                self.processor.get_module(name).print_module()
+            for name in self._print_list:
+                self._processor.get_module(name).print_module()
                 print("\n")
             return
 
@@ -319,17 +339,19 @@ class CLI:
             for name in names:
                 # check if name exists
                 try:
-                    self.processor.get_module(name)
-                    if name not in self.print_list:
-                        self.print_list.append(name)
+                    self._processor.get_module(name)
+                    if name not in self._print_list:
+                        self._print_list.append(name)
                 except KeyError:
-                    print("No module named", name,)
-
+                    print(
+                        "No module named",
+                        name,
+                    )
 
         elif user_input[1] == "remove":
             for name in names:
-                if name in self.print_list:
-                    self.print_list.remove(name)
+                if name in self._print_list:
+                    self._print_list.remove(name)
                 else:
                     print(name, "not in print list")
         else:
diff --git a/src/simudator/core/breakpoint.py b/src/simudator/core/breakpoint.py
index 85855446cfe5b97cbf7d14b0b27843f1ca75aa59..115d3b460829840050d19ceead5c55dc2b8fd3bc 100644
--- a/src/simudator/core/breakpoint.py
+++ b/src/simudator/core/breakpoint.py
@@ -1,9 +1,34 @@
 class Breakpoint:
+
+    __slots__ = "is_enabled"
+    """
+    Base class for beakpoints, intended to be subclassed.
+
+    Attributes
+    ----------
+    is_enabled : bool
+        Used to check and toggle if a breakpoint is on or off.
+    """
+
     def __init__(self):
         self.is_enabled = True
 
     def is_break(self) -> bool:
+        """Check if the breakpoint has been reached.
+
+        Returns
+        -------
+        bool
+            ``True`` if the breakpoint has been reached, ``False`` otherwise.
+        """
         return False
 
     def set_enabled(self, is_enabled: bool) -> None:
+        """Toggle the breakpoint on or off.
+
+        Parameters
+        ----------
+        is_enabled : bool
+            ``True`` to toggle on, ``False`` to toggle off.
+        """
         self.is_enabled = is_enabled
diff --git a/src/simudator/core/breakpoint_lambda.py b/src/simudator/core/breakpoint_lambda.py
index 64a3e9f581a1d8dc4385e4ee806a23c24217534b..52bff4bb3f88d631197bbc45cfd08f45f977c093 100644
--- a/src/simudator/core/breakpoint_lambda.py
+++ b/src/simudator/core/breakpoint_lambda.py
@@ -4,14 +4,32 @@ from simudator.core.breakpoint import Breakpoint
 
 
 class LambdaBreakpoint(Breakpoint):
+
+    __slots__ = ("_lambda_func", "_kwargs")
+    """
+    A breakpoint that uses a given function to decide whether the breakpoint
+    has been reached or not.
+
+    Parameters
+    ----------
+    lambda_func : Callable
+        The function used to decide if the breakpoint has been reached.
+
+        ``lambda_func(**kwargs) -> bool``
+        Should return ``True`` if the breakpoint has been reached, ``False``
+        otherwise.
+    **kwargs
+        Arguments to pass to `lambda_func`.
+    """
+
     def __init__(self, lambda_func: Callable[..., bool], **kwargs) -> None:
         super().__init__()
-        self.lambda_func = lambda_func
-        self.kwargs = kwargs
+        self._lambda_func = lambda_func
+        self._kwargs = kwargs
 
     def is_break(self):
-        return self.lambda_func(**self.kwargs)
+        return self._lambda_func(**self._kwargs)
 
     def __str__(self) -> str:
-        s = f"Lambda {self.lambda_func.__name__} with kwargs {self.kwargs}"
+        s = f"Lambda {self._lambda_func.__name__} with kwargs {self._kwargs}"
         return s
diff --git a/src/simudator/core/breakpoint_memory.py b/src/simudator/core/breakpoint_memory.py
index c62e65ce67d7e84feb29939b28eb902d0f768f41..8dbd66348eea3a2da753a1c1402bde9f638301e9 100644
--- a/src/simudator/core/breakpoint_memory.py
+++ b/src/simudator/core/breakpoint_memory.py
@@ -5,15 +5,32 @@ from simudator.core.modules import Memory
 
 
 class MemoryBreakpoint(Breakpoint):
-    def __init__(self, memory: Memory, adress: int, value: Any) -> None:
+
+    __slots__ = ("_memory", "_address", "_value")
+
+    """
+    A breakpoint class for memory modules to break when a certain address
+    of the memory has a certain value.
+
+    Parameters
+    ----------
+    memory : Memory
+        Memory module to check the contents of.
+    address : int
+        Address of the memory module to break at.
+    value : Any
+        Value of the address that triggers the breakpoint.
+    """
+
+    def __init__(self, memory: Memory, address: int, value: Any) -> None:
         super().__init__()
-        self.memory = memory
-        self.adress = adress
-        self.value = value
+        self._memory = memory
+        self._address = address
+        self._value = value
 
     def is_break(self):
-        memory_content = self.memory.get_state()["memory"]
-        return memory_content[self.adress] == self.value
+        memory_content = self._memory.get_state()["memory"]
+        return memory_content[self._address] == self._value
 
     def __str__(self) -> str:
-        return f"{self.memory.name} {self.adress}: {self.value}"
+        return f"{self._memory.name} {self._address}: {self._value}"
diff --git a/src/simudator/core/breakpoint_state.py b/src/simudator/core/breakpoint_state.py
index c7fce9723223a80814e6f40508a0c636b2ae5f9c..58b042cbabf727bfb5ec1f523617592c0035595c 100644
--- a/src/simudator/core/breakpoint_state.py
+++ b/src/simudator/core/breakpoint_state.py
@@ -5,14 +5,30 @@ from simudator.core.module import Module
 
 
 class StateBreakpoint(Breakpoint):
+
+    __slots__ = ("_module", "_state", "_value")
+    """
+    A breakpoint class to break when the state variable for a module reaches
+    a specified value.
+
+    Parameters
+    ----------
+    module : Module
+        Module to check the state of.
+    state : str
+        Name of the state variable of the module to check the value of.
+    value : Any
+        Value of the state variable to break at.
+    """
+
     def __init__(self, module: Module, state: str, value: Any) -> None:
         super().__init__()
-        self.module = module
-        self.state = state
-        self.value = value
+        self._module = module
+        self._state = state
+        self._value = value
 
     def is_break(self):
-        return self.module.get_state()[self.state] == self.value
+        return self._module.get_state()[self._state] == self._value
 
     def __str__(self) -> str:
-        return self.module.name + " " + self.state + " == " + str(self.value)
+        return self._module.name + " " + self._state + " == " + str(self._value)
diff --git a/src/simudator/core/module.py b/src/simudator/core/module.py
index d4337943e4162edb28e24683cdae30b110255d39..b84284952a8c0177ddf4756f874401a89d745fa4 100644
--- a/src/simudator/core/module.py
+++ b/src/simudator/core/module.py
@@ -1,42 +1,77 @@
 from __future__ import annotations
+from os import error
+
 from simudator.core.signal import Signal
 
 
 class Module:
     """
     This class specifies the basic functionality of all module types.
-    The modules are processor componenets in the processor,
+    The modules are processor components in the processor,
     such as ALU, busses, registers and more.
+
+    Parameters
+    ----------
+    signals : dict[str, Signal]
+        Dictionary containing all signals connected to the module. Input
+        signals should have keys beginning with 'in\_' and output signals keys
+        beginning with 'out\_'.
+    name : str
+        Name of the module.
+
+    Attributes
+    ----------
+    name : str
+        Name of the module.
+    signals : dict[str, Signal]
+        The signals connected to this module. The key for a signal can be
+        regarded as the name of a signal 'port' of the module. The keys should
+        be prefixed with 'in\_' for input signals and 'out\_' for output signals.
     """
 
-    def __init__(self, name: str = "") -> None:
+    __slots__ = ("name", "signals")
 
+    def __init__(self, signals: dict[str, Signal], name: str = "") -> None:
         self.name = name
+        self.signals = signals
+
+        for key, signal in self.signals.items():
+            if key[0:2] == "in":
+                signal.add_destination(self)
 
     def update_register(self) -> None:
+        """Simulate module behaviour for saving input into the internal state
+        during a clock tick.
+
+        This method should be used for any module that has any 'internal'
+        registers. Exact behaviour is specified in each module subclass.
         """
-        Simulates module behaviour for saving input into the internal state
-        during a clock tick. Exact behaviour is specifid in each module type.
-        """
-        raise NotImplemented
+        raise NotImplementedError
 
     def output_register(self) -> None:
+        """Simulate module behaviour for outputting data from internal
+        registers, if any, to output signals during a clock tick.
+
+        Exact behaviour is specified in each module subclass.
         """
-        Simulates module behaviour for giving data to output signals
-        during a clock tick. Exact behaviour is specifid in each module type.
-        """
-        raise NotImplemented
+        raise NotImplementedError
 
     def update_logic(self) -> None:
         """
-        Simulates behaviour for sending output using the internal state
-        during a clock tick. Exact behaviour is specifid in each module type.
+        Simulate behaviour for sending output using the internal state
+        during a clock tick. Exact behaviour is specified in each module type.
         """
-        raise NotImplemented
+        raise NotImplementedError
 
     def get_state(self) -> dict:
         """
-        Returns a dict of the module states.
+        Return the state of the module.
+
+        Returns
+        -------
+        dict[str, Any]
+            State of the module represented as a dictionary with one key for
+            each state variable.
         """
         state_dict = dict()
         state_dict["name"] = self.name
@@ -44,7 +79,12 @@ class Module:
 
     def get_gui_state(self) -> dict:
         """
-        Returns a dict of the module states that should be displayed in a GUI.
+        Return the state of the module as should be displayed in a GUI.
+
+        See Also
+        --------
+        get_state :
+            Similar method that includes state variables not related to the GUI.
         """
         state_dict = dict()
         state_dict["name"] = self.name
@@ -52,40 +92,56 @@ class Module:
 
     def set_state(self, state: dict) -> None:
         """
-        Sets the modules state to one given in dict. Dict format will be
-        diffrent for each type of module so use get_state to get correct format.
+        Set the state of the module.
+
+        Parameters
+        ----------
+        state : dict[str, Any]
+            Module state represented as a dictionary.
         """
         self.name = state["name"]
 
     def get_output_signals(self) -> list[Signal]:
+        """Return the output signals of the module.
+
+        Assumes all output signals are stored with a key beginning in 'out\_' in
+        the signals dictionary.
+
+        Returns
+        -------
+        list[Signal]
+            List of all output signals of this module.
         """
-        Returns the modules output signals. Is implemented in specific classes.
-        """
-        raise NotImplemented
+        return [signal for key, signal in self.signals.items() if key[0:3] == "out"]
 
     def get_input_signals(self) -> list[Signal]:
+        """Return the input signals of the module.
+
+        Assumes all input signals are stored with a key beginning in 'in\_' in
+        the signals dictionary.
+
+        Returns
+        -------
+        list[Signal]
+            List of all input signals of this module.
         """
-        Returns the modules input signals. Is implemented in specific classes.
-        """
-        raise NotImplemented
+        return [signal for key, signal in self.signals.items() if key[0:2] == "in"]
 
     def reset(self) -> None:
-        """
-        Resets the module to its default state.
-        """
-        raise NotImplemented
+        """Reset the module to its default state."""
+        raise NotImplementedError
 
-    def load_from_str(self, state_string):
-        """
-        Sets the moudles state according to a string
-        Each module type will decide how the string should be formated.
+    def load_from_str(self, state_string) -> None:
+        """Set the module state according to a string.
+
+        Each module subclass decides the format of the string.
         """
-        raise NotImplemented
+        raise NotImplementedError
 
     def get_longest_line_len(self, ignore_keys=None) -> int:
-        """
-        Helper function for pretty_print that returns the length of
-        the longest line to print for a module.
+        """Return the length of the longest line when printing the module.
+
+        Helper function for pretty_print.
         """
 
         if ignore_keys == None:
@@ -103,17 +159,44 @@ class Module:
         return longest_line_len
 
     def print_module(self) -> None:
-        """
-        Prints the module directly to terminal
-        """
+        """Print the module."""
         print(self.name)
 
-    def save_state_to_file(self, file_path: str) -> bool:
+    def _helper_save_state_to_file(self, file_path: str, content: str) -> bool:
+        """Save the given content to a given file.
+
+        Parameters
+        ----------
+        file_path : str
+            Path to file to save to.
+        content : str
+            Content to save to file.
+
+        Returns
+        -------
+        ``True`` on success, ``False`` otherwise.
         """
-        Tries to save the modules state to a given file.
-        Returns true on success and false if something went wrong.
+        try:
+            with open(file_path, "a") as file:
+                file.write(content)
+        except OSError:
+            return False
+        return True
+
+    def save_state_to_file(self, file_path: str) -> bool:
+        """Save the modules state to file.
+
+        Parameters
+        ----------
+        file_path : str
+            Path to the file to save to.
+
+        Returns
+        -------
+        bool
+            True if saving succeeded, False otherwise.
         """
-        raise NotImplemented
+        raise NotImplementedError
 
     def __str__(self) -> str:
         return self.name
diff --git a/src/simudator/core/modules/demux.py b/src/simudator/core/modules/demux.py
index 157d6089f53f74ffea2b4a17bef0d6da4e821e3b..2aeb731c2e414cd161b71ec38ea2743d0be6ffbf 100644
--- a/src/simudator/core/modules/demux.py
+++ b/src/simudator/core/modules/demux.py
@@ -6,70 +6,97 @@ from simudator.core.signal import Signal
 
 class Demux(Module):
     """
-    A general demux that allows an arbitrary amount of outputs to map
-    to a single input. The output side is controlled by the control
-    signal 'from_demux_s'.
+    A general demultiplexer that allows an arbitrary amount of outputs to map
+    to a single input.
+
+    Assumes the value sent from input to outputs is an integer.
+
+    Parameters
+    ----------
+    control : Signal
+        Control signal of which the value is used to index which
+        output signal to output to.
+    bit_length : int
+        Maximum number of bits for the value outputted by the demux. All extra
+        bits of the input value are discarded.
+    input : Signal
+        Input signal of which the value is forwarded to the currently
+        selected output signal.
+    outputs : list[Signal]
+        List of output signals to demultiplex the input signal value to.
+    name : str
+        Name of the demultiplexer module.
+    value : Any
+        Initial value to output to the currently selected output signal.
     """
 
-    def __init__(self, from_demux: Signal, bit_length: int,
-                 input: Signal, outputs: list[Signal] = [],
-                 name="demux", value=0)  -> None:
-        # Name
-        super().__init__(name)
+    __slots__ = ("_bit_length", "_value")
+
+    def __init__(
+        self,
+        control: Signal,
+        bit_length: int,
+        input: Signal,
+        outputs: list[Signal] = [],
+        name="demux",
+        value=0,
+    ) -> None:
 
         # Signals
-        self.from_demux_s = from_demux
-        self.outputs = outputs
-        self.input_s = input
+        signals = {
+            "in_control": control,
+            "in_input": input,
+        }
+        for i, s in enumerate(outputs):
+            signals[f"out_output_{i}"] = s
 
-        # mask and bit_length
-        self.bit_length = bit_length
-        self.mask = 2**self.bit_length -1
+        super().__init__(signals, name)
 
-        # Value to be read/written
-        self.value = value
+        self._bit_length = bit_length
 
-        # Set the demux as destination of its input signal
-        self.input_s.add_destination(self)
+        # Value to be read/written
+        self._value = value
 
     def update_register(self) -> None:
         """
-        Read the input value and update the value of the demux.
+        Read the input signal value and update the value of the demux.
         """
-        input_value = self.input_s.get_value()
-        self.value = input_value & self.mask
 
+        input_value = self.signals["in_input"].get_value()
+        mask = 2**self._bit_length - 1
+        self._value = input_value & mask
 
-    def output_register(self):
-        """
-        Read which signal to write to from the control signal
-        'from_demux_s' and forward the input to that output signal.
+    def output_register(self) -> None:
+        """Read which signal to write to from the control signal and forward
+        the input to that output signal.
         """
-        output_index = self.from_demux_s.get_value()
-        self.outputs[output_index].update_value(self.value)
+        output_index = self.signals["in_control"].get_value()
+        output_signal_key = f"out_output_{output_index}"
+        self.signals[output_signal_key].update_value(self._value)
 
-    def update_logic(self):
-        """
-        The demux has no logic
+    def update_logic(self) -> None:
+        """Do nothing.
+
+        The demux has no logic.
         """
         pass
 
-    def get_state(self) -> dict[str: Any]:
-        """
-        Returns a dict of the demux state.
-        """
+    def get_state(self) -> dict[str, Any]:
         state = super().get_state()
-        state["value"] = self.value
-        state["bit_length"] = self.bit_length
-        state["mask"] = self.mask
+        state["value"] = self._value
+        state["bit_length"] = self._bit_length
 
         return state
 
-    def set_state(self, state: dict) -> None:
-        """
-        Sets the name and value of the demux.
+    def set_state(self, state: dict[str, Any]) -> None:
+        """Set the name and value of the demux.
+
+        Parameters
+        ----------
+        state : dict[str, Any]
+            The state of the demux to load. Should contain the keys "value" and
+            "bit_length", both with values of type ``int``.
         """
         super().set_state(state)
-        self.value = state["value"]
-        self.bit_length = state["bit_length"]
-        self.mask = state["mask"]
+        self._value = state["value"]
+        self._bit_length = state["bit_length"]
diff --git a/src/simudator/core/modules/memory.py b/src/simudator/core/modules/memory.py
index d71774550dd0565f864d93d7261f4b64a7f6a6b2..4aa611c4aca4ac542b6a67c3cba48b4fd1e551d5 100644
--- a/src/simudator/core/modules/memory.py
+++ b/src/simudator/core/modules/memory.py
@@ -1,5 +1,7 @@
 from __future__ import annotations
 
+from typing import Any
+
 from simudator.core.module import Module
 from simudator.core.signal import Signal
 
@@ -7,46 +9,66 @@ from simudator.core.signal import Signal
 class Memory(Module):
     """
     A general size memory module.
+
+    Parameters
+    ----------
+    input : Signal
+        Signal of which the value may be written to the current address.
+    output : Signal
+        Signal to which the content of the current address is written.
+    control : Signal
+        Signal used to enable or disable write to the current address. Assumed
+        to output ``bool``.
+    address : Signal
+        Signal used to address the memory. Assumed to output ``int``.
+    size : int
+        Size of the address space of the memory.
+    name : str
+        Name of the module.
+    """
+
+    __slots__ = (
+        "_memory",
+        "_current_address",
+        "_is_write",
+    )
     """
+    Constants used to textually pad the values of the memory when saving to
+    file.
+    """
+    MEMORY_VALUE_PADDING = 2
+    MEMORY_ADDRESS_PADDING = 2
 
     def __init__(
         self,
-        input_signal: Signal,
-        output_signal: Signal,
-        control_signal: Signal,
-        adress_signal: Signal,
+        input: Signal,
+        output: Signal,
+        control: Signal,
+        address: Signal,
         size: int,
-        value_padding: int = 2,
-        adress_padding: int = 2,
         name="Memory",
     ) -> None:
-        super().__init__(name)
 
-        # Input signals
-        self.input_s = input_signal
-        self.control_s = control_signal
-        self.adress_s = adress_signal
+        # signals
+        signals = {
+            "in_input": input,
+            "in_control": control,
+            "in_address": address,
+            "out_content": output,
+        }
 
-        # Output signals
-        self.output_s = output_signal
+        # Init super class
+        super().__init__(signals, name)
 
         # Internal state
-        self.memory = [0 for _ in range(size)]
-        self.current_adress = 0
-        self.is_write = False
-
-        # Register self as destination of input signals
-        self.input_s.add_destination(self)
-        self.control_s.add_destination(self)
-        self.adress_s.add_destination(self)
-
-        # Values used to format the strings when saving state to file
-        self.adress_padding = adress_padding
-        self.value_padding = value_padding
+        self._memory = [0 for _ in range(size)]
+        self._current_address = 0
+        self._is_write = False
 
     def update_register(self):
-        if self.is_write:
-            self.memory[self.current_adress] = self.input_s.get_value()
+        if self._is_write:
+            value = self.signals["in_input"].get_value()
+            self._memory[self._current_address] = value
 
     def output_register(self) -> None:
         pass
@@ -54,70 +76,61 @@ class Memory(Module):
         # in case the memory was written to in update_register()
 
     def update_logic(self) -> None:
-        if (
-            self.adress_s.get_value() is not None
-            and self.control_s.get_value() is not None
-        ):
-            self.is_write = self.control_s.get_value()
-            self.current_adress = self.adress_s.get_value()
-            self.output_s.update_value(self.memory[self.current_adress])
-
-    def get_input_signal(self) -> Signal:
-        return self.input_s
-
-    def get_output_signal(self) -> Signal:
-        return self.output_s
-
-    def get_adress_signal(self) -> Signal:
-        return self.adress_s
-
-    def get_control_signal(self) -> Signal:
-        return self.control_s
+        adr_sig = self.signals["in_address"]
+        ctrl_sig = self.signals["in_control"]
+        out_sig = self.signals["out_content"]
+        if adr_sig.get_value() is not None and ctrl_sig.get_value() is not None:
+            self._is_write = ctrl_sig.get_value()
+            self._current_address = adr_sig.get_value()
+            out_sig.update_value(self._memory[self._current_address])
 
     def print_module(self) -> None:
         print("", self.name, "\n -----")
-        for adress, value in enumerate(self.memory):
+        for address, value in enumerate(self._memory):
             if value:
-                print("", str(hex(adress)), "", str(hex(value)), "\n")
+                print("", str(hex(address)), "", str(hex(value)), "\n")
 
-    def get_state(self) -> dict:
+    def get_state(self) -> dict[str, Any]:
         state = super().get_state()
-        state["is_write"] = self.is_write
-        state["current_adress"] = self.current_adress
-        state["memory"] = self.memory[:]
+        state["is_write"] = self._is_write
+        state["current_address"] = self._current_address
+        state["memory"] = self._memory[:]
         return state
 
-    def get_gui_state(self) -> dict:
+    def get_gui_state(self) -> dict[str, Any]:
         state = super().get_gui_state()
-        state["current_adress"] = self.current_adress
-        state["memory"] = self.memory[:]
+        state["current_address"] = self._current_address
+        state["memory"] = self._memory[:]
         return state
 
-    def set_state(self, state: dict) -> None:
+    def set_state(self, state: dict[str, Any]) -> None:
         super().set_state(state)
         if "is_write" in state:
-            self.is_write = state["is_write"]
-        if "current_adress" in state:
-            self.current_adress = state["current_adress"]
+            self._is_write = state["is_write"]
+        if "current_address" in state:
+            self._current_address = state["current_address"]
         if "memory" in state:
-            self.memory = state["memory"]
+            self._memory = state["memory"]
 
     def reset(self) -> None:
         """
-        Reset the memory to 0 for each adress.
+        Reset the memory to 0 for each address.
         """
-        for i in range(len(self.memory)):
-            self.memory[i] = 0
+        for i in range(len(self._memory)):
+            self._memory[i] = 0
 
-    def get_longest_line_len(self, ignore_keys=[]) -> int:
+    def get_longest_line_len(self, ignore_keys=None) -> int:
         """
         Helper function for pretty_print that returns the length of
         the longest value in the memory to print for a module.
         """
 
+        if ignore_keys is None:
+            ignore_keys = []
+
         longest_memory_line = 0
 
-        for value in self.memory:
+        for value in self._memory:
             value_len = len(str(value))
             if value_len > longest_memory_line:
                 longest_memory_line = value_len
@@ -127,22 +140,18 @@ class Memory(Module):
     def get_largest_mem_adr(self) -> int:
         """
         Helper function for pretty_print that returns the length of
-        the largest adress in the memory to print for a module.
-        """
-        return len(str(len(self.memory)))
-
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
+        the largest address in the memory to print for a module.
         """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        for index, value in enumerate(self.memory):
-            # Write the adress in hex and add ': ' to the end
-            file.write(hex(index)[2:].rjust(self.adress_padding, "0") + ": ")
-            # Write the value in hex
-            file.write(hex(value)[2:].rjust(self.value_padding, "0"))
-            file.write("\n")
-
-        file.write("\n")
-        file.close()
+        return len(str(len(self._memory)))
+
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = ""
+        content = self.name + ":\n"
+        for index, value in enumerate(self._memory):
+            content += (
+                str(hex(index)[2:].rjust(self.MEMORY_ADDRESS_PADDING, "0")) + ": "
+            )
+            content += str(hex(value)[2:].rjust(self.MEMORY_VALUE_PADDING, "0"))
+            content += "\n"
+        content += "\n"
+        return super()._helper_save_state_to_file(file_path, content)
diff --git a/src/simudator/core/modules/mux.py b/src/simudator/core/modules/mux.py
index e3a355a2774ce40ad4f2a9c6496d68b2070f65ac..cf8e06a9f48366c4dc8a269661dd0825a18e5d5d 100644
--- a/src/simudator/core/modules/mux.py
+++ b/src/simudator/core/modules/mux.py
@@ -6,84 +6,111 @@ from simudator.core.signal import Signal
 
 class Mux(Module):
     """
-    A general mux that allows an arbitrary amount of input to map
-    to a single output. The input side is controlled by the control
-    signal 'to_mux_s'.
+    A general mux that allows an arbitrary amount of input to map to a single
+    output.
+
+    Parameters
+    ----------
+    control : Signal
+        Control signal of which the value is used to index which
+        input signal to read from.
+    bit_length : int
+        Maximum number of bits for the value outputted by the mux. All extra
+        bits of the input value are discarded.
+    inputs : list[Signal]
+        List of input signals to select from to output onto the output signal.
+    output : Signal
+        Signal onto which the value of the currently selected input is
+        outputted.
+    name : str
+        Name of the multiplexer module.
+    value : Any
+        Initial value to output to the output signal.
     """
 
-    def __init__(self,
-                 to_mux: Signal,
-                 bit_length: int,
-                 output: Signal,
-                 inputs: list[Signal] = [],
-                 name="mux",
-                 value=0
-                 ) -> None:
-        print("in: ", inputs)
-        # Name
-        super().__init__(name)
+    __slots__ = ("_bit_length", "_value")
+
+    def __init__(
+        self,
+        control: Signal,
+        bit_length: int,
+        output: Signal,
+        inputs: list[Signal] = [],
+        name="mux",
+        value=0,
+    ) -> None:
 
         # Signals
-        self.to_mux_s = to_mux
-        self.output_s = output
-        self.inputs = inputs
+        signals = {
+            "in_control": control,
+            "out": output,
+        }
+        for i, s in enumerate(inputs):
+            signals[f"in_input_{i}"] = s
+
+        # Init super class
+        super().__init__(signals, name)
 
         # mask and bit_length
-        self.bit_length = bit_length
-        self.mask = 2**self.bit_length -1
+        self._bit_length = bit_length
 
         # Value to be read/written
-        self.value = value
-
-        # Set mux as destination of all input signals
-        if self.inputs: # make sure inputs is not empty
-            for signal in self.inputs:
-                signal.add_destination(self)
+        self._value = value
 
     def update_register(self) -> None:
+        """Read which signal to read from the control signal and forward that
+        signal to the output.
         """
-        Read which signal to read from the control signal
-        'to_mux_s' and forward that signal to the output.
-        """
-        input_index = self.to_mux_s.get_value()
-        input_value = self.inputs[input_index].get_value()
-        self.value = input_value & self.mask
+        input_index = self.signals["in_control"].get_value()
+        input_signal_key = f"in_input_{input_index}"
+        input_value = self.signals[input_signal_key].get_value()
+        mask = 2**self._bit_length - 1
+        self._value = input_value & mask
 
     def output_register(self):
+        """Output the value of the currently selected input signal to the
+        output signal.
         """
-        Output the value of the mux to its output.
-        """
-        self.output_s.update_value(self.value)
+        self.signals["out"].update_value(self._value)
 
     def update_logic(self):
-        """
+        """Do nothing.
+
         The mux has no logic.
         """
         pass
 
-    def get_state(self) -> dict[str: Any]:
-        """
-        Returns a dict of the mux state.
-        """
+    def get_state(self) -> dict[str, Any]:
         state = super().get_state()
-        state["value"] = self.value
-        state["bit_length"] = self.bit_length
-        state["mask"] = self.mask
+        state["value"] = self._value
+        state["bit_length"] = self._bit_length
 
         return state
 
-    def set_state(self, state: dict) -> None:
-        """
-        Sets the name and value of the mux.
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\nvalue: " + str(self._value) + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
+
+    def set_state(self, state: dict[str, Any]) -> None:
+        """Set the name, value and bit length of the demux.
+
+        Parameters
+        ----------
+        state : dict[str, Any]
+            The state of the demux to load. Should contain the keys "name",
+            "value" and "bit_length" with values of type ``str``, ``int`` and
+            ``int`` respectively.
         """
         super().set_state(state)
-        self.value = state["value"]
-        self.bit_length = state["bit_length"]
-        self.mask = state["mask"]
+        self._value = state["value"]
+        self._bit_length = state["bit_length"]
 
     def print_module(self) -> None:
-        print(self.name, "\n-----",
-              "\nvalue: ", self.value,
-              "\nbit length: ", self.bit_length,
-              "\nmask: ", self.mask,
-              )
+        print(
+            self.name,
+            "\n-----",
+            "\nvalue: ",
+            self._value,
+            "\nbit length: ",
+            self._bit_length,
+        )
diff --git a/src/simudator/core/modules/register.py b/src/simudator/core/modules/register.py
index 08ee3c1821f8b87e1fa1682124e834243137300d..723638511324f405f79ab56e456635ed597a2e00 100644
--- a/src/simudator/core/modules/register.py
+++ b/src/simudator/core/modules/register.py
@@ -8,156 +8,166 @@ from simudator.core.signal import Signal
 
 class Register(Module):
     """
-    A simple module that can store a value.
+    A simple module that can store and output a value.
+
+    Parameters
+    ----------
+    input : Signal
+        Signal from which the value is stored in the register.
+    output : Signal
+        Signal onto which the value of the register is outputted.
+    value : Any
+        Initial value of the register.
+    name : str
+        Name of the register.
     """
 
-    def __init__(self, input_signal: Signal,
-                       output_signal: Signal,
-                       value: Any = 0, name: str = "Register") -> None:
+    __slots__ = "_value"
 
-        # set the registers name
-        super().__init__(name)
+    def __init__(
+        self,
+        input_signal: Signal,
+        output_signal: Signal,
+        value: Any = 0,
+        name: str = "Register",
+    ) -> None:
 
         # signals
-        self.input_s = input_signal
-        self.output_s = output_signal
-        self.value = value
+        signals = {
+            "in_content": input_signal,
+            "out_content": output_signal,
+        }
 
-        # set the register as destination of its input signal
-        self.input_s.add_destination(self)
+        # init the instance
+        super().__init__(signals, name)
+        self._value = value
 
     def update_register(self) -> None:
+        """Update the internal value of the register with the input signal.
+
+        Any extra bits that do not fit the bit length of the register are
+        thrown away.
         """
-        Propagate the input signal to the registers internal state.
-        Throw away bits larger than the length of the register.
-        """
-        input_value = self.input_s.get_value()
+        input_value = self.signals["in_content"].get_value()
         if input_value is None:
             return
-        self.value = input_value
+        self._value = input_value
 
     def output_register(self) -> None:
-        """
-        Propagate the value of the register to the output signal.
-        It is the response of the destination to know when it should
-        read the output.
-        """
-        self.output_s.update_value(self.value)
+        """Output the value of the register onto the output signal."""
+        self.signals["out_content"].update_value(self._value)
 
     def update_logic(self):
-        """
+        """Do nothing.
+
         The register has no logic.
         """
         pass
 
-    def get_state(self) -> dict[str: Any]:
-        """
-        Returns a dict of the register state.
-        These states are changable via set_states.
-        """
+    def get_state(self) -> dict[str, Any]:
         state = super().get_state()
-        state["value"] = self.value
+        state["value"] = self._value
         return state
 
     def get_gui_state(self) -> dict:
         state = self.get_state()
         return state
 
-    def set_state(self, state: dict[str: Any]) -> None:
+    def set_state(self, state: dict[str, Any]) -> None:
         """
-        Sets the register state to one given in dict.
+        Set the state of the register.
+
+        Parameters
+        ----------
+        state : dict[str, Any]
+            The state of the register to load. Should contain the keys "name"
+            and "value" with values of type ``str`` and ``int`` respectively.
         """
         self.name = state["name"]
-        self.value = state["value"]
+        self._value = state["value"]
 
     def reset(self) -> None:
         """
-        Resets the register to 0.
+        Reset the register to 0.
         """
-        self.value = 0
-
-    def get_output_signals(self) -> []:
-        return [self.output_s]
+        self._value = 0
 
-    def get_input_signals(self) -> []:
-        return [self.input_s]
-
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
-        Returns true on success and false if something went wrong.
-        """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("value: " + str(self.value) + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\nvalue:: " + str(self._value) + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def print_module(self) -> None:
-        print("", self.name, "\n -----",
-              "\n value: ", self.value,
-              )
+        print(
+            "",
+            self.name,
+            "\n -----",
+            "\n value: ",
+            self._value,
+        )
 
 
 class IntegerRegister(Register):
     """
-    A simple module that can store an integer value with a given bit-length.
+    A register intended to store integers only.
+
+    Parameters
+    ----------
+    input : Signal
+        Signal from which the value is stored in the register.
+    output : Signal
+        Signal onto which the value of the register is outputted.
+    bit_length : int
+        Maximum number of bits of the input to store in the register. All extra
+        bits of the input value are discarded.
+    value : Any
+        Initial value of the register.
+    name : str
+        Name of the register.
     """
 
-    def __init__(self, input_signal: Signal, output_signal: Signal,
-                 bit_length: int, value: int = 0, name: str | None = None) -> None:
+    __slots__ = "_bit_length"
+
+    def __init__(
+        self,
+        input: Signal,
+        output: Signal,
+        bit_length: int,
+        value: int = 0,
+        name: str | None = None,
+    ) -> None:
 
-        # set the registers name
+        # set the name
         if name is None:
             name = f"{bit_length}-bit register"
 
-        super().__init__(input_signal, output_signal, value=value, name=name)
+        super().__init__(input, output, value=value, name=name)
 
         # set the bit length of the register
-        self.bit_length = bit_length
-
-        # set the registers mask. An 8 bit register should
-        # have the mask 1111 1111, aka one '1' for every bit
-        self.mask = 2**bit_length - 1
+        self._bit_length = bit_length
 
     def update_register(self) -> None:
-        """
-        Propagate the input signal to the registers internal state.
-        Throw away bits larger than the length of the register.
-        """
         super().update_register()
-        self.value = self.value & self.mask
+        mask = 2**self._bit_length - 1
+        self._value = self._value & mask
 
-    def get_state(self) -> dict[str: Any]:
-        """
-        Returns a dict of the register state.
-        These states are changable via set_states.
-        """
+    def get_state(self) -> dict[str, Any]:
         state = super().get_state()
-        state["bit_length"] = self.bit_length
+        state["bit_length"] = self._bit_length
         return state
 
-    def set_state(self, state: dict[str: Any]) -> None:
-        """
-        Sets the register state to one given in dict.
-        """
+    def set_state(self, state: dict[str, Any]) -> None:
         super().set_state(state)
         if "bit_length" in state:
-            self.bit_length = state["bit_length"]
-            self.mask = 2**self.bit_length -1
+            self._bit_length = state["bit_length"]
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
-        """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("value: " + hex(self.value)[2:] + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\nvalue: " + hex(self._value)[2:] + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
-    def load_from_str(self, state_string):
+    def load_from_str(self, state_string) -> None:
         string_pair = state_string.split(": ")
-        #TODO: Maybe check if it starts with value: ?
-        self.value = int(string_pair[1], 16)
+        # TODO: Maybe check if it starts with value: ?
+        self._value = int(string_pair[1], 16)
 
 
 class Flag(IntegerRegister):
@@ -166,21 +176,23 @@ class Flag(IntegerRegister):
     updated during the 'update logic' phase.
     """
 
-    def __init__(self, input_signal: Signal, output_signal: Signal,
-                 bit_length=1, value=0, name="Flag") -> None:
+    def __init__(
+        self,
+        input_signal: Signal,
+        output_signal: Signal,
+        bit_length=1,
+        value=0,
+        name="Flag",
+    ) -> None:
 
         # set the flags name
-        super().__init__(input_signal=input_signal, output_signal=output_signal,
-                         bit_length=bit_length, value=value, name=name)
-
-    def output_register(self) -> None:
-        """
-        Propagate the value of the flag to the output signal.
-
-        It is the response of the destination to know when it should
-        read the output.
-        """
-        self.output_s.update_value(self.value)
+        super().__init__(
+            input=input_signal,
+            output=output_signal,
+            bit_length=bit_length,
+            value=value,
+            name=name,
+        )
 
     def update_logic(self):
         """
@@ -189,26 +201,26 @@ class Flag(IntegerRegister):
         should update the same tick as the change that causes the flag
         to activate (ex: loop counter reaches zero -> set L flag to 1)
         """
-        input_value = self.input_s.get_value()
-        self.value = input_value & self.mask
+        input_value = self.signals["in_content"].get_value()
+        mask = 2**self._bit_length - 1
+        self._value = input_value & mask
 
     def get_gui_state(self) -> dict:
         state = super().get_gui_state()
-        state["value"] = self.value
+        state["value"] = self._value
         return state
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
-        """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("value: " + str(self.value) + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\nvalue: " + str(self._value) + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def print_module(self) -> None:
-        print("", self.name, "\n -----",
-              "\n value: ", self.value,
-              "\n bit length: ", self.bit_length,
-              "\n mask: ", self.mask,
-              )
+        print(
+            "",
+            self.name,
+            "\n -----",
+            "\n value: ",
+            self._value,
+            "\n bit length: ",
+            self._bit_length,
+        )
diff --git a/src/simudator/core/processor.py b/src/simudator/core/processor.py
index 027b0efbf95349b7315edeb8b51ff1892c50bfd5..aaff184cbdb716982080db184e756df747191554 100644
--- a/src/simudator/core/processor.py
+++ b/src/simudator/core/processor.py
@@ -17,30 +17,72 @@ from .signal import Signal
 class Processor:
     """
     Main class for controlling the processor.
+
     Uses modules and signals to simulate processor components behaviour.
     """
 
-    def __init__(self):
+    __slots__ = (
+        "modules",
+        "signals",
+        "clock",
+        "update_queue",
+        "module_history",
+        "signal_history",
+        "breakpoint_id_counter",
+        "breakpoints",
+        "breakpoint_reached",
+        "last_breakpoint",
+        "cycles_to_save",
+        "removed_cycles",
+        "max_line_len",
+        "line_separator",
+        "is_stopped",
+        "new_instruction",
+        "current_instructions",
+        "ignore_keys",
+        "lambdas",
+        "assembly_cycles",
+        "line_separator",
+    )
+
+    LINE_SEPARATOR = "-"
+    MAX_LINE_LEN = 170
+    CYCLES_TO_SAVE = 10000
+
+    def __init__(self) -> None:
+        # For simulation
         self.modules: dict[str, Module] = dict()
         self.signals = []
         self.clock = 0
         self.update_queue = []
-        self.module_history: list[dict[str, dict[str, Any]]] = []
-        self.signal_history: list[list] = []  # TODO: Is this needed?
+        self.is_stopped = False
+
+        # Breakpoint handling
         self.breakpoint_id_counter = 1
         self.breakpoints: dict[int, Breakpoint] = {}
         self.breakpoint_reached = False
         self.last_breakpoint = None
-        self.cycles_to_save = 1000
+        self.lambdas: dict[str, Callable[..., bool]] = {}
+
+        # Saving processor state for saving/loading to file and undoing ticks
         self.removed_cycles = 0
-        self.max_line_len = 170
-        self.line_seperator = "-"
-        self.is_stopped = False
+        self.assembly_cycles = [0]  # Map asm instruction to clock cycle
+        self.module_history: list[dict[str, dict[str, Any]]] = []
+        self.signal_history: list[list] = []  # TODO: Is this needed?
+
+        # For showing which instructions are being done and signalling
+        # the start of a new one
         self.new_instruction = False
-        self.current_instructions: list[str] = []
+
+        # It is the responsibility of the CPU to tell the gui:s pipeline
+        # diagram what information to be displayed and how to display it.
+        # Thus each CPU keeps track of its current instructions together with
+        # each instructions position in the pipeline diagram.
+        self.current_instructions: list[tuple[str, int, int]] = []
+
         # TODO: keeping track of what pieces of info not to show
         # show not be done at the processor level.
-        # Maybe implemenet a 'get_pretty_print_state' at module
+        # Maybe implement a 'get_pretty_print_state' at module
         # level?
         self.ignore_keys = [
             "bit_length",
@@ -51,18 +93,13 @@ class Processor:
             "decrement_by_one",
             "bus_id",
         ]
-        self.lambdas: dict[str, Callable[..., bool]] = {}
-
-        # List containing all clock cycles where a new asm instruction started
-        self.assembly_cycles = [0]
 
     def do_tick(self) -> None:
-        """
-        Simulate one clock cycle of the processor.
+        """Simulate one clock cycle of the processor.
 
         Run each module's tick function, then handle the following updates.
         Also check for breakpoints that are reached in this cycle, and save
-        clock cycles when new assemly instructions are started.
+        clock cycles when new assembly instructions are started.
         """
         if len(self.module_history) > self.clock - self.removed_cycles:
             # If a previous stored cycle has been loaded, discard
@@ -93,37 +130,80 @@ class Processor:
         # new assembly instruction
         if self.is_new_instruction():
             self.new_instruction = True
-            self.current_instruction = self.get_current_instruction()
+            self.current_instructions = self.get_current_instructions()
             self.assembly_cycles.append(self.get_clock())
         else:
             self.new_instruction = False
 
-    def get_current_instruction(self) -> str:
+    def get_current_instructions(self) -> list[tuple[str, int, int]]:
+        """Return a list of the current instructions with their positions.
+
+        Each entry in the list is a tuple of a instruction with its column and
+        row position for the pipeline diagram.
+
+        Returns
+        -------
+        list[tuple[str, int, int]]
+            A list of instructions represented as tuples. Each tuple contains
+            the instruction and its position in the pipeline.
         """
-        Return the current instruction. Useful for pipeline diagram.
+        raise NotImplementedError
+
+    def get_pipeline_dimensions(self) -> tuple[int, int]:
+        """Return the dimensions of the pipeline of the processor.
+
+        Returns
+        -------
+        width : int
+            The width of the pipeline (number of cycles to display at a time).
+        depth : int
+           Depth of the pipeline.
         """
-        raise NotImplemented
+        raise NotImplementedError
 
-    def run_asm_instruction(self, num_instructions=1) -> None:
+    def get_asm_instruction(self) -> str:
+        """Return a string containing the 'code' for the current assembly
+        instruction.
+
+        What 'code' refers to is up to each processor to define. It could be a
+        memory address, an instruction label or something else.
+
+        Returns
+        -------
+        str
+            The current assembly instruction.
         """
-        Runs assembler instructions on the processors 'num_instructions' times.
+        raise NotImplementedError
+
+    def run_asm_instruction(self, num_instructions=1) -> None:
+        """Run a number of assembly instructions.
 
-        Default argument is one, but it is possible to specify any number of instructions.
-        This should be implemented per processor and thus we raise NotImplemented.
+        Defaults to running 1 instruction unless some other number is provided.
+
+        Parameters
+        ----------
+        num_instructions : int
+            Number of assembly instructions to perform.
+
+        Notes
+        -----
+        This should be implemented per processor and thus we
+        raise NotImplementedError.
         """
 
-        raise NotImplemented
+        raise NotImplementedError
 
     def undo_asm_instruction(self, num_instructions=1) -> None:
         """
-        Undos 'num_instructions' numbers of assembler instructions on the CPU.
+        Undo a number of assembly instructions on the CPU.
 
-        Default argument is one, but it is possible to specify any number of instructions.
-        Undo assembler instrucion assumes the CPU is always at the end of the list
-        'self.assembly_cycles'.
+        Default argument is one, but it is possible to specify any number of
+        instructions.
 
-        Undos asm instructions by finding the corresponding clock cycle before
-        said number of asm intructions started and loading that clock cycle.
+        Parameters
+        ----------
+        num_instructions : int
+            Number of assembly instructions to undo.
         """
 
         current_clock_cycle = self.clock
@@ -148,7 +228,7 @@ class Processor:
         # Need +1 here since we save the start state to enable to
         # load the start state. This is done since we only append clock
         # cycles to the list self.assembly_cycles when we reach a new state
-        # that has uPC set to 0, which wont happend when we load a new file.
+        # that has uPC set to 0, which wont happen when we load a new file.
         self.assembly_cycles = self.assembly_cycles[: index + 1]
 
     def run_continuously(self) -> None:
@@ -161,48 +241,51 @@ class Processor:
 
     def should_halt(self) -> bool:
         """
-        Return True when the processor should halt, otherwise False.
+        Return whether the processor should halt this clock cycle or not.
+
+        Returns
+        -------
+        bool
+            ``True`` if the processor should halt, ``False`` otherwise.
 
-        Note
+        Notes
         ----
         Each processor should implement the condition for True in its subclass.
-        If we intead raise NotImplemented as we do in all other cases when the
-        functionality should be implemented in the subclass we lose the ability
-        to test modules on the base processor class.
+        If we instead raise NotImplementedError as we do in all other cases
+        when the functionality should be implemented in the subclass we lose
+        the ability to test modules on the base processor class.
         """
         return False
 
     def stop(self) -> None:
-        """
-        Signal to stop the execution of the processor until the processor is
+        """Signal to stop the execution of the processor until the processor is
         instructed to run continuously or do some ticks again.
         """
         self.is_stopped = True
 
     def unstop(self) -> None:
-        """
-        Reset the stop execution signal.
-        """
+        """Reset the stop execution signal."""
         self.is_stopped = False
 
     def is_new_instruction(self) -> bool:
         """
-        Return true on the same clock cycle the CPU starts a new instruciton.
+        Return true on the same clock cycle the CPU starts a new instruction.
 
-        Should be implemented per CPU, return field new_instruction. It is up to each
-        processor to set this field to True/False correctly.
+        Should be implemented per CPU, return field new_instruction. It is up
+        to each processor to set this field to True/False correctly.
         """
         return self.new_instruction
 
     def stop_at_breakpoints(self) -> None:
-        """
-        Stop the execution if any breakpoint has been reached during this cycle.
+        """Stop the execution if any breakpoint has been reached during this
+        clock cycle.
 
         Also record the breakpoint that was reached.
         """
         self.breakpoint_reached = False
         for _, bp in self.breakpoints.items():
-            # TODO: Can make this more efficient by only checking enabled breakpoints
+            # TODO: Can make this more efficient by only checking enabled
+            # breakpoints
             if bp.is_break() and bp.is_enabled:
                 self.breakpoint_reached = True
                 self.last_breakpoint = bp
@@ -211,15 +294,16 @@ class Processor:
             self.stop()
 
     def reset(self):
-        """
-        Resets all values in the processor and
-        then does one propogation to the signals.
-        Is run before running the processor to propogate values in
-        module out into signals of the system.
+        """Reset the processor to its start state.
+
+        This resets all modules and removes any saved states for undoing clock
+        cycles. A round of value propagation is done to reset signals too.
         """
         self.clock = 0
         self.module_history.clear()
         self.signal_history.clear()
+        self.removed_cycles = 0
+        self.assembly_cycles = [0]
 
         for module in self.modules.values():
             module.reset()
@@ -230,45 +314,83 @@ class Processor:
             module.update_logic()
 
     def add_modules_to_update(self, module: Module) -> None:
-        """
-        Queues module to be updated at the end of clock cycle.
+        """Queue a module to be updated at the end of clock cycle.
+
+        Parameters
+        ----------
+        module : Module
+            Module to queue for update.
+
+        See Also
+        --------
+        Module.update_logic :
+            Method for updating a module.
         """
         if module not in self.update_queue:
             self.update_queue.append(module)
 
     def add_module(self, module: Module) -> None:
-        """
-        Add module into the processor.
+        """Add module to be simulated by the processor.
+
+        Parameters
+        ----------
+        module : Module
+            Module to add.
         """
         self.modules[module.name] = module
 
     def get_module(self, name: str) -> Module:
-        """
-        Get module with specific name.
+        """Get module with specific name.
+
+        Parameters
+        ----------
+        name : str
+            Name of the module to get.
+
+        Returns
+        -------
+        Module
+            The module with the specified name.
         """
         return self.modules[name]
 
     def get_modules(self) -> list[Module]:
-        """
-        Get list of all modules.
+        """Get list of all modules.
+
+        Returns
+        -------
+        list[Module]
+            List of all modules in the processor.
         """
         return list(self.modules.values())
 
     def add_signals(self, signals: list[Signal]) -> None:
-        """
-        Add signals into the processor.
+        """Add signals to the processor.
+
+        Parameters
+        ----------
+        list[Signal]
+            List of signals to add for simulation.
         """
         self.signals += signals
 
     def get_clock(self) -> int:
-        """
-        Get current clockcycle.
+        """Get the current clockcycle number.
+
+        Returns
+        -------
+        int
+            Current clock cycle number of the processor.
         """
         return self.clock
 
     def set_clock(self, value: int) -> None:
-        """
-        Set current clockcycle.
+        """Set current clockcycle number.
+
+        Parameters
+        ----------
+        value : int
+            Cycle number to set the clock to.
         """
         self.clock = value
 
@@ -281,7 +403,7 @@ class Processor:
 
         # Only save a specified number of cycles,
         # saving every cycle can easily eat all ram
-        if len(self.module_history) > self.cycles_to_save:
+        if len(self.module_history) > Processor.CYCLES_TO_SAVE:
             self.module_history.pop(0)
             self.removed_cycles += 1
 
@@ -290,10 +412,15 @@ class Processor:
         self.module_history.append(module_states)
 
     def load_cycle(self, cycle: int) -> None:
-        """
-        Load the state of all modules as they were at the specified clock cycle.
+        """Load the state of all modules as they were at the specified clock
+        cycle.
 
         This does not erase the history of states for the later cycles.
+
+        Parameters
+        ----------
+        cycle : int
+            Number of the cycle to load.
         """
 
         cycle_index = cycle - self.removed_cycles
@@ -318,13 +445,13 @@ class Processor:
             module = self.update_queue.pop(0)
             module.update_logic()
 
-    def load_state_from_file(self, file_path) -> None:
-        """
-        Loads states for modules from a file.
+    def load_state_from_file(self, file_path: str) -> None:
+        """Load states for modules from a file.
 
-        Appends the lines from the loaded file to a long string. This string
-        is given to each module in the function 'load_from_str'. Each module
-        will thus load itself.
+        Parameters
+        ----------
+        file_path : str
+            Path to the file containing the processor state to load.
         """
         self.reset()
 
@@ -337,6 +464,9 @@ class Processor:
         else:
             raise ValueError  # Else raise error
 
+        # Appends the lines from the loaded file to a long string. This string
+        # is given to each module in the function 'load_from_str'. Each module
+        # will thus load itself.
         module_name = None
         module_str = ""
 
@@ -367,34 +497,50 @@ class Processor:
             module = self.get_module(module_name)
             module.load_from_str(module_str)
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Saves the current state of the cpu to a given file.
+    def save_state_to_file(self, file_path: str) -> bool:
+        """Save the current state of the cpu to file.
 
         Will overwrite the given file.
+
+        Parameters
+        ----------
+        file_path : str
+            Path to the file to write to.
         """
         file = open(file_path, "w")
         file.write("")
         file.close()
 
         for module in self.modules.values():
-            print(module)
-            module.save_state_to_file(file_path)
+            res =module.save_state_to_file(file_path)
+
+            if not res:
+                return False
+        
+        return True
 
     def pretty_print(self) -> None:
+        """Print the processor state in a readable and compact format."""
         self.pretty_print_verbose(self.ignore_keys)
 
     def pretty_print_verbose(self, ignore_keys=None) -> None:
         """
-        Prints the most relevant information about each module
-        compact and pretty. Fields of modules can be ignored with the
-        optional argument 'ignore_keys'.
+        Print the most relevant information about each module in a compact and
+        readable format.
+
+        State variables of modules can be ignored with the optional argument.
+
+        Parameters
+        ----------
+        ignore_keys : list[str]
+            List of names of state variables of modules to exclude when
+            printing module states.
         """
         # TODO: ignore keys per module and not for all modules
         memory_modules = []
         other_modules = []
 
-        if ignore_keys == None:
+        if ignore_keys is None:
             ignore_keys = []
 
         # TODO: remove isinstance(module, micro_memory)
@@ -418,16 +564,17 @@ class Processor:
         module_to_line_length = {}
         # get the longest line length for all other_modules
         for module in other_modules:
-            line_len = module.get_longest_line_len(ignore_keys) + 3  # +3 for padding
+            # +3 for padding
+            line_len = module.get_longest_line_len(ignore_keys) + 3
 
             # TODO: what to do if two or more modules has the same name
             module_to_line_length[module] = line_len
 
         groups = self.group_pp_modules(module_to_line_length)
 
-        print(self.line_seperator * self.max_line_len)
+        print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN)
 
-        # print each group seperate
+        # print each group separate
         for group in groups:
             self.pretty_print_names(group)
 
@@ -456,40 +603,46 @@ class Processor:
                             real_keys[row] + ": " + str(module_state[real_keys[row]])
                         )
 
-                    # padd the string so each string has the same
-                    # length
+                    # pad the string so each string has the same length
                     total_padding += module_to_line_length[module]
                     string = string.ljust(total_padding)
                     # replace last to chars with a separator and padding
                     string = string[0:-2] + "| "
 
                 print(string)
-            print(self.line_seperator * self.max_line_len)
+            print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN)
 
         for memory_module in memory_modules:
             self.pretty_print_memory(memory_module)
 
-    def pretty_print_memory(self, module: Module) -> None:
+    def pretty_print_memory(self, module: Memory) -> None:
+        """Print a memory module in a compact and readable format.
+
+        Parameters
+        ----------
+        module : Memory
+            Memory module to print the state of.
+        """
         print(module.name)
-        print(self.line_seperator * self.max_line_len)
+        print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN)
 
         longest_line_len = module.get_longest_line_len()
         # longest_line_len = self.get_longest_memory_value(module.memory)
         string = ""
         last_mem_len = module.get_largest_mem_adr()
 
-        for i, value in enumerate(module.memory):
+        for i, value in enumerate(module.get_state()["memory"]):
 
-            # create a new string containg the adress and the value
-            # of the memory adress formattet to fit with the largest
-            # adress and largest memory value
+            # create a new string containing the address and the value
+            # of the memory address formatted to fit with the largest
+            # address and largest memory value
             new_row = (str(i).ljust(last_mem_len) + ": " + str(value)).ljust(
                 longest_line_len + last_mem_len + 3
             ) + "|"
 
             # only add the string if there is space for it, else
             # print the string and start a new
-            if len(string + new_row) + 1 > self.max_line_len:
+            if len(string + new_row) + 1 > Processor.MAX_LINE_LEN:
                 print(string)
                 string = new_row
             else:
@@ -500,16 +653,21 @@ class Processor:
                 else:
                     string = new_row
         print(string)
-        print(self.line_seperator * self.max_line_len)
+        print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN)
         print()
 
     def pretty_print_names(self, module_to_line_length: dict[Module, int]) -> None:
         """
-        Prints the name of the modules in one row, but pretty.
+        Print the name of the modules in one row with formatting.
 
+        Adds spacing between the names so that the longest state variable of
+        each module has space to be printed below the name.
 
-        Adds spacing between the names so that the modules
-        longest field will have space to be printed below.
+        Parameters
+        ----------
+        module_to_line_length : dict[Module, int]
+            Mapping from module to length of the longest state variable of
+            the module.
         """
         name_string = ""
         total_len = 0
@@ -524,9 +682,20 @@ class Processor:
     def group_pp_modules(
         self, module_to_line_length: dict[Module, int]
     ) -> list[dict[Module, int]]:
-        """
-        Groups the modules to be pretty printed into groups with a
-        total line length lower than 'self.max_line_len'.
+        """Group the modules to be pretty printed into groups with a
+        total line length lower than 'Processor.MAX_LINE_LEN'.
+
+        Parameters
+        ----------
+        module_to_line_length : dict[Module, int]
+            Mapping from module to length of the longest state variable of
+            the module (takes as the line length).
+
+        Returns
+        -------
+        list[dict[Module, int]]
+            List of mappings from module to line length, each mapping
+            representing a group of modules for printing.
         """
 
         groups = [{}]
@@ -535,7 +704,7 @@ class Processor:
         for module in module_to_line_length:
 
             # Make sure the line is not to long
-            if line_len + module_to_line_length[module] < self.max_line_len:
+            if line_len + module_to_line_length[module] < Processor.MAX_LINE_LEN:
                 line_len += module_to_line_length[module]
                 groups[group_index][module] = module_to_line_length[module]
 
@@ -549,8 +718,21 @@ class Processor:
 
     def fields_to_list(self, module: Module, ignore_keys=[]) -> list[str]:
         """
-        Return a list containing all the keys for a module except the name.
-        Optinal argument to ignore specific keys.
+        Return a list containing all state variable names, excluding module
+        name, for a module.
+        Optional argument to ignore specific state variables.
+
+        Parameters
+        ----------
+        module : Module
+            Module to get state variable names from.
+        ignore_keys : list[str]
+            List of state variable names to exclude.
+
+        Returns
+        -------
+        list[str]
+            List of state variable names, excluding module name, of a module.
         """
         return [
             key
@@ -558,12 +740,29 @@ class Processor:
             if key != "name" and key not in ignore_keys
         ]
 
-    def get_most_fields(self, modules: dict[Module:int], ignore_keys=[]) -> int:
-        """
-        Return the most number of a fields a module has.
+    def get_most_fields(self, modules: dict[Module, int], ignore_keys=None) -> int:
+        """Get the maximum number of state variables among all modules.
 
         Can optionally ignore keys.
+
+        Parameters
+        ----------
+        modules : dict[Module, int]
+            Mapping from module to length of the longest state variable of
+            the module. Used as a list of modules to take the maximum over.
+        ignore_keys : list[str]
+            State variables to exclude.
+
+        Returns
+        -------
+        int
+            The maximum of the number of module state variables among all
+            modules of the processor.
         """
+
+        if ignore_keys is None:
+            ignore_keys = []
+
         fields = 0
         for module in modules:
             module_fields = len(self.fields_to_list(module, ignore_keys))
@@ -575,6 +774,24 @@ class Processor:
     def add_state_breakpoint(
         self, module_name: str, state_name: str, value: Any
     ) -> None:
+        """Add a module state breakpoint to the processor.
+
+        Parameters
+        ----------
+            module_name : str
+                Name of the module for which a breakpoint will be added.
+            state_name : str
+                Name of the state variable of the module to put a breakpoint
+                on.
+            value : Any
+                Value of the state variable that triggers the breakpoint.
+
+        Raises
+        ------
+        ValueError
+            If the module does not exist or if the state variable does not
+            exist in the module.
+        """
 
         if module_name not in self.modules:
             raise ValueError(f"No module named {module_name}")
@@ -589,19 +806,43 @@ class Processor:
         self.breakpoint_id_counter += 1
 
     def remove_breakpoint(self, id: int) -> bool:
+        """Remove a specific breakpoint.
+
+        Parameters
+        ----------
+        id : int
+            ID of the breakpoint to be removed.
+        """
         if id in self.breakpoints:
             del self.breakpoints[id]
             return True
         return False
 
     def print_breakpoints(self) -> None:
+        """Print all breakpoints of the processor."""
         if not self.breakpoints:
             print("There are no breakpoints.")
         else:
             for bp_id, bp in self.breakpoints.items():
                 print(f"BP {bp_id}: {bp}")
 
-    def add_memory_breakpoint(self, module_name: str, adress: int, value: Any) -> None:
+    def add_memory_breakpoint(self, module_name: str, address: int, value: Any) -> None:
+        """Add a breakpoint for a memory module.
+
+        Parameters
+        ----------
+        module_name : str
+            Name of the memory module for which a breakpoint will be added.
+        address : int
+            Address of the memory module to break at.
+        value : Any
+            Value of the address that triggers the breakpoint.
+
+        Raises
+        ------
+        ValueError
+            If the module does not exist or is not a memory.
+        """
 
         if module_name not in self.modules:
             raise ValueError(f"No module named {module_name}")
@@ -610,14 +851,42 @@ class Processor:
         if "memory" not in module.get_state():
             raise ValueError(f"Module {module_name} is not a memory.")
 
-        bp = MemoryBreakpoint(module, adress, value)
+        bp = MemoryBreakpoint(module, address, value)
         self.breakpoints[self.breakpoint_id_counter] = bp
         self.breakpoint_id_counter += 1
 
     def get_breakpoint_lambdas(self) -> list[str]:
+        """Get all functions available for lambda breakpoints.
+
+        Returns
+        -------
+        list[str]
+            List of names of all functions of the processor that are available
+            for adding lambda breakpoints.
+        """
+
         return list(self.lambdas.keys())
 
     def add_lambda_breakpoint(self, lambda_name: str, **kwargs) -> None:
+        """Add a lambda breakpoint to the processor.
+
+        Module names used in the keyword arguments are substituted by the
+        actual modules before being passed on to the lambda breakpoint
+        function.
+
+        Parameters
+        ----------
+        lambda_name : str
+            Name of the lambda breakpoint function to be used by the
+            breakpoint.
+        **kwargs
+            Arguments to pass to the breakpoint function.
+
+        Raises
+        ------
+        ValueError
+            If the  lambda breakpoint function does not exist.
+        """
         # Substitute any module names for the module instances
         # This is to be able to externally create general lambda breakpoints
         # that can take any values as arguments while also having a way to take
@@ -636,4 +905,13 @@ class Processor:
         self.breakpoint_id_counter += 1
 
     def set_enabled_breakpoint(self, bp_id: int, is_enabled: bool) -> None:
+        """Toggle a breakpoint to enabled or disabled.
+
+        Parameters
+        ----------
+        bp_id : int
+            ID of the breakpoint to toggle.
+        is_enabled : bool
+            ``True`` to enable, ``False`` to disable the breakpoint.
+        """
         self.breakpoints[bp_id].set_enabled(is_enabled)
diff --git a/src/simudator/core/signal.py b/src/simudator/core/signal.py
index cfd6de19272193b4bd11aca329a0ad72ecb903f6..8db1d14133278add895bc9b776f778addb5116df 100644
--- a/src/simudator/core/signal.py
+++ b/src/simudator/core/signal.py
@@ -5,54 +5,79 @@ from typing import Any
 
 class Signal:
     """
-    Handles datatransportation between modules in the processor.
-    Each signal has one source module and can have multiple
-    destination modules.
+    Signal class to send data between modules in a processor.
+
+    Parameters
+    ----------
+    processor : Processor
+        Processor that the signal belongs to. Needed to queue processor modules
+        for update.
+    name : str
+        Name of the signal.
+    value : Any
+        Initial value of the signal.
     """
-    def __init__(self, processor: Processor, name: str = "Signal", value = None):
-        self.processor = processor
-        self.name = name
-        self.source = None
-        self.destinations = set()
-        self.value = value
+
+    __slots__ = ("_processor", "_name", "_source", "_destinations", "_value")
+
+    def __init__(self, processor: Processor, name: str = "Signal", value=None):
+        self._processor = processor
+        self._name = name
+        self._source = None
+        self._destinations = set()
+        self._value = value
 
     def add_destination(self, module: Module) -> None:
-        """
-        Add a destination module that the signal will send new
+        """Add a module as a destination that the signal will send new
         values to.
         """
-        self.destinations.add(module)
-
-    def set_source(self, module: Module) -> None:
-        """
-        Set source module that will send values through the signal.
-        """
-        self.source = module
+        self._destinations.add(module)
 
     def update_value(self, value) -> None:
         """
-        Sets value in signal and queues destination moduels for update.
+        Set the value of the signal and queue destination modules for update.
+
+        Parameters
+        ----------
+        value : Any
+            Value to set the signal to.
         """
-        if value != self.value:
-            self.value = value
+        if value != self._value:
+            self._value = value
             # Inform the processor on what modules have been given
             # signal so they can update
-            for destination in self.destinations:
-                self.processor.add_modules_to_update(destination)
+            for destination in self._destinations:
+                self._processor.add_modules_to_update(destination)
 
     def get_value(self) -> Any:
-        """
-        Get the signals current value.
-        """
-        return self.value
+        """Get the current value of the signal.
 
-    def set_value(self, value) -> None:
+        Returns
+        -------
+        Any
+            Value of the signal.
         """
-        Set the value of the signal without queuing the destination
-        modules of the signal for update. This method should not
-        be called by a module.
+        return self._value
+
+    def set_value(self, value: Any) -> None:
+        """Set the value of the signal without queuing the destination
+        modules of the signal for update.
+
+        This method should not be called by a module.
+
+        Parameters
+        ----------
+        value : any
+            Value to set the signal to.
         """
-        self.value = value
+        self._value = value
 
     def get_name(self) -> str:
-        return self.name
+        """Return the name of the signal.
+
+        Returns
+        -------
+        str
+            Name of the signal.
+        """
+        return self._name
diff --git a/src/simudator/gui/dialogs/lambda_breakpoint_dialog.py b/src/simudator/gui/dialogs/lambda_breakpoint_dialog.py
index 824131527bb809a225dc46337a9d0f4b96c2f0e6..9343ff10232952031b2d7e8aa40891d2b9996db4 100644
--- a/src/simudator/gui/dialogs/lambda_breakpoint_dialog.py
+++ b/src/simudator/gui/dialogs/lambda_breakpoint_dialog.py
@@ -13,6 +13,7 @@ from qtpy.QtWidgets import (
 
 
 class LambdaBreakpointDialog(QDialog):
+
     accepted = pyqtSignal(str, str, name="accepted")
 
     def __init__(
diff --git a/src/simudator/gui/gui.py b/src/simudator/gui/gui.py
index 2bc84de0c22dd359c7157b5d0558698da54726e5..fee557d1195f9741e49bcacd2ec3194c272681ff 100644
--- a/src/simudator/gui/gui.py
+++ b/src/simudator/gui/gui.py
@@ -132,8 +132,7 @@ class GUI(QMainWindow):
         self.cpu_running = False
 
         self.pipeline = PipeLine()
-        # Hide pipeline for now since it is not implemented yet
-        # self.pipeline.show()
+        self.init_pipeline()
 
         # Set Style, THESE ARE TEST AND DONT WORK
         app = QApplication.instance()
@@ -363,14 +362,31 @@ class GUI(QMainWindow):
                 case "CLOCKUPDATE":
                     signal.connect(self.updateCpuClockCycle)
 
+    def init_pipeline(self) -> None:
+        """Initialize the pipeline diagram. 
+
+        Sets its height, width and instructions.
+        """
+        size = self.cpu.get_pipeline_dimensions()
+
+        self.pipeline.set_height(size[0])
+        self.pipeline.set_width(size[1])
+        self.update_pipeline()
+
+        self.pipeline.show()
+
     def updateCpuListeners(self) -> None:
         """
-        Updates the graphics items in the scene and the clock.
+        Updates the graphics items in the scene, the clock, and the pipeline diagram.
 
         Used after the cpu has run or when the user has edited somehting.
         """
         self.cpu_graphics_scene.updateGraphicsItems()
         self.updateCpuClockCycle()
+        self.update_pipeline()
+
+    def update_pipeline(self) -> None:
+        self.pipeline.set_instructions(self.cpu.get_current_instructions())
 
     def updateCpuClockCycle(self) -> None:
         """
@@ -583,23 +599,31 @@ class GUI(QMainWindow):
 
     def folderSaveDialog(self) -> str:
         """
-        Opens a file explorer in a new window. Return the absolute path to the selected file.
+        Open a file explorer in a new window. Return the absolute path to the selected file.
+
+        Can return existing as well as non-existing files.
+        If the selected files does not exist it will be created.
         """
         dialog = QFileDialog()
         dialog.setFileMode(QFileDialog.AnyFile)
         dialog.setAcceptMode(QFileDialog.AcceptOpen)
         dialog.setDirectory("~/simudator")  # TODO: does this work when exported?
-        # this is static so none of the above matters...
+
+        # getSaveFileName() can returncan return existing file but also create new ones
         return dialog.getSaveFileName()[0]
 
     def folderLoadDialog(self) -> str:
         """
-        Opens a file explorer in a new window. Return the absolute path to the selected file.
+        Open a file explorer in a new window. Return the absolute path to the selected file.
+
+        Can only return existing files.
         """
         dialog = QFileDialog()
         dialog.setFileMode(QFileDialog.AnyFile)
         dialog.setAcceptMode(QFileDialog.AcceptOpen)
         dialog.setDirectory("~/simudator")  # TODO: does this work when exported?
+
+        # getOpenFileName() will only return already existing files
         return dialog.getOpenFileName()[0]
 
     def loadToolBarButtonClick(self) -> None:
@@ -832,7 +856,12 @@ class GUI(QMainWindow):
         if path == '':
             return
 
-        self.cpu.save_state_to_file(path)
+        res = self.cpu.save_state_to_file(path)
+
+        if res:
+            self.messageBox("File saved.")
+        else:
+            self.errorBox("Unable to save.")
 
     def openBreakpointWindow(self) -> None:
         """
diff --git a/src/simudator/gui/module_graphics_item/memory_graphic.py b/src/simudator/gui/module_graphics_item/memory_graphic.py
index 063ebb1d75a6c0b92be42ed3ce7506621315c98a..2817042cc772ef63fbbce36395542b585fda8eec 100644
--- a/src/simudator/gui/module_graphics_item/memory_graphic.py
+++ b/src/simudator/gui/module_graphics_item/memory_graphic.py
@@ -23,20 +23,26 @@ class MemoryWindow(QTableWidget):
     A class showing the contents of a memory module in a QTableWidget.
 
     This class assumes that the size of the memory module will remain constant.
+
+    Parameters
+    ----------
+    memory_module: An instance of the Memory base class.
+    column_size: An integer specifying the number of columns, optional.
+
     """
 
     def __init__(self, memory_module: Memory, column_size=-1):
         super().__init__()
-        self.memory = memory_module
-        self.column_size = column_size
-        self.memory_size = len(self.memory.get_state()["memory"])
-        self.set_column_size()
-        self.setColumnCount(self.column_size)
-        self.setRowCount(ceil(self.memory_size / self.column_size))
+        self._memory = memory_module
+        self._column_size = column_size
+        self._memory_size = len(self._memory.get_state()["memory"])
+        self._set_column_size()
+        self.setColumnCount(self._column_size)
+        self.setRowCount(ceil(self._memory_size / self._column_size))
         self.setHorizontalHeaderLabels(["+" + str(i) for i in range(4)])
 
         vertical_headers = []
-        for i in range(0, self.memory_size, self.column_size):
+        for i in range(0, self._memory_size, self._column_size):
             vertical_headers.append(str(hex(i)))
 
         self.setVerticalHeaderLabels(vertical_headers)
@@ -45,40 +51,50 @@ class MemoryWindow(QTableWidget):
 
     def update(self):
         """
-        Updates the content of the widget by loading in a new copy of the modules memory.
+        Update the content of this widget to reflect the content of the memory module.
         """
-        memory_content = self.memory.get_state()["memory"]
-        for i in range(self.memory_size):
+        memory_content = self._memory.get_state()["memory"]
+        for i in range(self._memory_size):
             value = memory_content[i]
-            row = i // self.column_size
-            col = i % self.column_size
+            row = i // self._column_size
+            col = i % self._column_size
             self.set_item(row, col, str(value))
 
     def set_item(self, row: int, col: int, text: str) -> None:
-        """Sets the text at position col row to the given text."""
+        """Set the text at specified table cell to the given text.
+
+        Parameters
+        ----------
+        row: int
+             The items row position in the pipeline diagram.
+        col: int
+             The items column position in the pipeline diagram.
+        text: str
+             The text to be displayed.
+        """
         item = QTableWidgetItem(text)
 
         self.setItem(row, col, item)
 
-    def set_column_size(self) -> None:
+    def _set_column_size(self) -> None:
         """
-        Sets the column size to a resonalbe value if the size wasnt given to the constructor.
+        Set the column size to a reasonable value if the size was not given to the constructor.
 
-        This function assumes that the attributes column_size and memory_size are set before it is called.
+        This function assumes that the attributes `column_size` and `memory_size` are set before it is called.
         """
 
-        if not self.column_size == -1:
+        if not self._column_size == -1:
             return
 
-        if self.memory_size > 200:
-            self.column_size = 4
+        if self._memory_size > 200:
+            self._column_size = 4
             return
 
-        if self.memory_size > 100:
-            self.column_size = 2
+        if self._memory_size > 100:
+            self._column_size = 2
             return
 
-        self.column_size = 1
+        self._column_size = 1
         return
 
 
@@ -110,34 +126,34 @@ class MemoryGraphicsItem(ModuleGraphicsItem):
         )
         self.output.setPos(self._width, self._height / 4)
 
-        self.adress = PortGraphicsItem(
-            self.module.get_adress_signal(), Orientation.DOWN, self
+        self.address = PortGraphicsItem(
+            self.module.get_address_signal(), Orientation.DOWN, self
         )
-        self.adress.setPos(self._width / 4, self._height)
+        self.address.setPos(self._width / 4, self._height)
 
         self.control = PortGraphicsItem(
             self.module.get_control_signal(), Orientation.DOWN, self
         )
         self.control.setPos(self._width * 3 / 4, self._height)
 
-        self.ports = [self.input, self.output, self.adress, self.control]
+        self.ports = [self.input, self.output, self.address, self.control]
 
         # Create name lable
         name_text = QGraphicsSimpleTextItem(self.state["name"], self)
         name_text.setPos(self._width / 2 - len(self.state["name"]) * self._CHAR_LEN, 0)
 
         # Create address lable
-        self.adress_text = QGraphicsSimpleTextItem(
-            "adress: " + str(self.state["current_adress"]), self
+        self.address_text = QGraphicsSimpleTextItem(
+            "address: " + str(self.state["current_address"]), self
         )
-        self.adress_text.setPos(self._width / 20, self._height / 8)
+        self.address_text.setPos(self._width / 20, self._height / 8)
 
     def update(self):
         """
         Also update memory window here.
         """
         self.state = self.module.get_state()
-        self.adress_text.setText("address: " + str(self.state["current_adress"]))
+        self.address_text.setText("address: " + str(self.state["current_address"]))
         if self.memory_window is not None:
             self.memory_window.update()
 
@@ -200,30 +216,30 @@ class MemoryGraphicsItem(ModuleGraphicsItem):
 
     @Slot(str, str, str)
     def memoryBreakpointAccepted(
-        self, module_name: str, adress: str, value: str
+        self, module_name: str, address: str, value: str
     ) -> None:
         """
         Takes the info from a breakpoint dialog and sends it to the gui
         """
         try:
-            self.new_memory_breakpoint_signal.emit(module_name, adress, value)
+            self.new_memory_breakpoint_signal.emit(module_name, address, value)
         except SyntaxError as e:
             self.errorMessageWidget.showMessage(str(e))
 
     @Slot(str, str, str)
-    def editMemoryAccepted(self, module_name: str, adress: str, value: str) -> None:
+    def editMemoryAccepted(self, module_name: str, address: str, value: str) -> None:
         """
         Takes the info from a edit memory dialog
         and updates the memory content accordingly.
         Also asks the gui to update.
         """
         try:
-            parsed_adress = int(adress)
+            parsed_address = int(address)
         except SyntaxError as e:
             self.errorMessageWidget.showMessage(str(e))
         else:
             module_state = self.module.get_state()
-            module_state['memory'][parsed_adress] = value
+            module_state['memory'][parsed_address] = value
             self.module.set_state(module_state)
             self.update_graphics_signal.emit()
 
diff --git a/src/simudator/gui/pipeline.py b/src/simudator/gui/pipeline.py
index 9023ef17475fa40212b42481e56e0235b0a12bf7..fc9d78012115a011bfc6574414ba6c8a478b6a67 100644
--- a/src/simudator/gui/pipeline.py
+++ b/src/simudator/gui/pipeline.py
@@ -3,7 +3,6 @@ from typing import Any
 from qtpy.QtWidgets import QTableWidget, QTableWidgetItem
 
 
-# TODO: Make the table unediatable
 class PipeLine(QTableWidget):
     """
     A class showing the current CPU instructions in a seperate window.
@@ -14,27 +13,40 @@ class PipeLine(QTableWidget):
 
     def __init__(self):
         super().__init__()
-        # Values below are temporary test values
+
+    def set_instructions(self, instructions: list[tuple[str, int, int]]) -> None:
         """
-        self.setRowCount(5)
-        self.setColumnCount(5)
-        self.instructions = ["Add", "Sub", "Mult", "Fetch", "Nop"]
-        self.set_item(0, 0, self.instructions[0])
-        self.set_item(1, 1, self.instructions[1])
-        self.set_item(2, 2, self.instructions[2])
-        self.set_item(3, 3, self.instructions[3])
+        Give the pipeline the current CPU instructions.
+
+        Parameters
+        ----------
+        instructions: A list of tuples of a string and two ints.
+                      The string containing the current instruction where the two ints
+                      represents the instructions col and row position.
+
+        See Also
+        --------
+        simudator.core.processor.get_current_instructions :
+            Instructions are set from get_current_instructions() in the Processor class.
         """
 
-    def set_instruction(self, instructions: list[str]) -> None:
-        """Give the pipeline the current CPU instructions."""
         self.instructions = instructions
+        for i in range(len(self.instructions)):
+            instruction = self.instructions[i][0]
+            col = self.instructions[i][1]
+            row = self.instructions[i][2]
+            self.set_item(row, col, instruction)
 
     def set_height(self, height: int) -> None:
-        """Sets the height of the pipeline."""
+        """
+        Sets the height of the pipeline.
+        """
         self.setColumnCount(height)
 
     def set_width(self, width: int) -> None:
-        """Sets the width of the pipeline."""
+        """
+        Sets the width of the pipeline.
+        """
         self.setRowCount(width)
 
     def set_row_labels(self, labels: list[str]) -> None:
@@ -60,7 +72,9 @@ class PipeLine(QTableWidget):
         raise NotImplemented
 
     def set_item(self, row: int, col: int, text: str) -> None:
-        """Sets the text at position col row to the given text."""
+        """
+        Sets the text at position col row to the given text.
+        """
         item = QTableWidgetItem(text)
 
         self.setItem(row, col, item)
diff --git a/src/simudator/processor/mia/gui/ar_graphic.py b/src/simudator/processor/mia/gui/ar_graphic.py
index 06dcdb07c61c9aa0efd98e98ce8eadd0ef6e6569..c6e5bf2647c055c90dfe17cedb9f52b22c5d44f4 100644
--- a/src/simudator/processor/mia/gui/ar_graphic.py
+++ b/src/simudator/processor/mia/gui/ar_graphic.py
@@ -18,20 +18,20 @@ class ArGraphicsItem(IntegerRegisterGraphicsItem):
         )
 
         # Draw bus port
-        bus_port = PortGraphicsItem(self.module.output_s, parent=self)
+        bus_port = PortGraphicsItem(self.module.signals["out_content"], parent=self)
         bus_port.setPos(width, self._RECT_HEIGHT / 2)
         self.ports.append(bus_port)
 
         # Draw to alu port
         to_alu_port = PortGraphicsItem(
-            self.module.alu_output_signal, Orientation.LEFT, parent=self
+            self.module.signals["out_alu"], Orientation.LEFT, parent=self
         )
         to_alu_port.setPos(0, self._RECT_HEIGHT / 2)
         self.ports.append(to_alu_port)
 
         # Draw from alu port
         from_alu_port = PortGraphicsItem(
-            self.module.input_s, Orientation.UP, parent=self
+            self.module.signals["in_content"], Orientation.UP, parent=self
         )
         from_alu_port.setPos(width / 2, 0)
         self.ports.append(from_alu_port)
diff --git a/src/simudator/processor/mia/gui/asr_graphic.py b/src/simudator/processor/mia/gui/asr_graphic.py
index 752643f1e7d83340e8531d8260c210aaff5dce34..079a9250692b6153e227c8f958f5c15ee3ef32d3 100644
--- a/src/simudator/processor/mia/gui/asr_graphic.py
+++ b/src/simudator/processor/mia/gui/asr_graphic.py
@@ -19,13 +19,13 @@ class AsrGraphicsItem(IntegerRegisterGraphicsItem):
         )
 
         # Draw bus port
-        bus_port = PortGraphicsItem(self.module.input_s, parent=self)
+        bus_port = PortGraphicsItem(self.module.signals["in_content"], parent=self)
         bus_port.setPos(width, self._RECT_HEIGHT / 2)
         self.ports.append(bus_port)
 
         # Draw to memory port
         to_memory_port = PortGraphicsItem(
-            self.module.output_s, Orientation.LEFT, parent=self
+            self.module.signals["out_content"], Orientation.LEFT, parent=self
         )
         to_memory_port.setPos(0, self._RECT_HEIGHT / 2)
         self.ports.append(to_memory_port)
diff --git a/src/simudator/processor/mia/gui/bus_graphic.py b/src/simudator/processor/mia/gui/bus_graphic.py
index 476a14d19eab26fe06b80b2997fd9df0e19f0de8..397be42ae3edf32b9f8daebf264ee5c6ccd96f9d 100644
--- a/src/simudator/processor/mia/gui/bus_graphic.py
+++ b/src/simudator/processor/mia/gui/bus_graphic.py
@@ -66,6 +66,19 @@ class BusGraphicsItem(ModuleGraphicsItem):
     def update(self):
         pass
 
+    def generateActions(self) -> None:
+        """
+        Generate action for the bus. The only generated action is 'show all ports'.
+
+        The default implementation generates actions such as edit break points
+        and edit module state. As the bus is state less none of these two
+        are applicable.
+        """
+        self.show_ports_action = QAction("Show all ports")
+        self.show_ports_action.triggered.connect(self.showPorts)
+        self.show_ports_action.setEnabled(not self.isLocked)
+        self.actions.append(self.show_ports_action)
+
     def shouldIgnoreAction(self, action: QAction) -> bool:
         """
         Disables left clicking, which would normally allow the user to
diff --git a/src/simudator/processor/mia/gui/hr_graphic.py b/src/simudator/processor/mia/gui/hr_graphic.py
index 5b3331194bcd799b08339e9789dac56c1511263e..ce4ba72814d3022f05caba2a3487b9122c648cb3 100644
--- a/src/simudator/processor/mia/gui/hr_graphic.py
+++ b/src/simudator/processor/mia/gui/hr_graphic.py
@@ -17,11 +17,11 @@ class HrGraphicsItem(IntegerRegisterGraphicsItem):
         )
 
         # Draw to bus port
-        to_bus_port = PortGraphicsItem(self.module.input_s, parent=self)
+        to_bus_port = PortGraphicsItem(self.module.signals["in_content"], parent=self)
         to_bus_port.setPos(width, self._RECT_HEIGHT / 4)
         self.ports.append(to_bus_port)
 
         # Draw from bus port
-        from_bus_port = PortGraphicsItem(self.module.output_s, parent=self)
+        from_bus_port = PortGraphicsItem(self.module.signals["out_content"], parent=self)
         from_bus_port.setPos(width, self._RECT_HEIGHT * 3 / 4)
         self.ports.append(from_bus_port)
diff --git a/src/simudator/processor/mia/gui/mia_alu_graphic.py b/src/simudator/processor/mia/gui/mia_alu_graphic.py
index 3bcd78a49acde6c541745bc96fc2e65b64373f22..737c7325cdaef600f745f4040fe317e782c80887 100644
--- a/src/simudator/processor/mia/gui/mia_alu_graphic.py
+++ b/src/simudator/processor/mia/gui/mia_alu_graphic.py
@@ -25,18 +25,18 @@ class AluGraphicsItem(ModuleGraphicsItem):
         )
 
         # make port for input a
-        port_a = PortGraphicsItem(self.module.input_signal_a, Orientation.UP, self)
+        port_a = PortGraphicsItem(self.module.signals["in_input_a"], Orientation.UP, self)
         port_a.setPos(self.RECT_WIDTH * 3 / 4, 0)
         self.ports.append(port_a)
 
         # make port for input b
-        port_b = PortGraphicsItem(self.module.input_signal_b, Orientation.UP, self)
+        port_b = PortGraphicsItem(self.module.signals["in_input_b"], Orientation.UP, self)
         port_b.setPos(self.RECT_WIDTH / 4, 0)
         self.ports.append(port_b)
 
         # make port for output
         output_port = PortGraphicsItem(
-            self.module.output_signal, Orientation.DOWN, self
+            self.module.signals["out_result"], Orientation.DOWN, self
         )
         output_port.setPos(self.RECT_WIDTH / 2, self.RECT_HEIGHT)
         self.ports.append(output_port)
diff --git a/src/simudator/processor/mia/gui/mia_grx_graphic.py b/src/simudator/processor/mia/gui/mia_grx_graphic.py
index a3de6f2b8187675b442864930a1e81387cb7ff1d..d7d94295ee0adc6f33fc7dc5f1c60d5beaa9bfc5 100644
--- a/src/simudator/processor/mia/gui/mia_grx_graphic.py
+++ b/src/simudator/processor/mia/gui/mia_grx_graphic.py
@@ -28,10 +28,11 @@ class GrxGraphicsItem(ModuleGraphicsItem):
     REGISTER_MARGIN = 10
     LINE_LENGTH = 30
     MUX_WIDTH = 80
+    REGISTER_LENGTH = 4
 
-    def __init__(self, moudle: GRX):
+    def __init__(self, module: GRX):
         self.register_text_labels = []
-        super().__init__(moudle)
+        super().__init__(module)
 
     def draw_graphics_item(self):
         # The width of the register
@@ -43,7 +44,9 @@ class GrxGraphicsItem(ModuleGraphicsItem):
         for index, register_value in enumerate(self.module.registers):
             # Make text to display for register
             name = "Gr" + str(index)
-            hex_length = math.ceil(self.state["bit_length"] / 4)
+
+            # Mia uses 4 hex numbers to represent the registers
+            hex_length = self.REGISTER_LENGTH
             value_text = f"0x{register_value:0{hex_length}x}"
             full_text = name + ": " + value_text[2:]
 
@@ -123,14 +126,14 @@ class GrxGraphicsItem(ModuleGraphicsItem):
         )
 
         # Add ports to and from bus
-        from_bus_port = PortGraphicsItem(self.module.from_bus, Orientation.RIGHT, self)
+        from_bus_port = PortGraphicsItem(self.module.signals["in_input"], Orientation.RIGHT, self)
         from_bus_port.setPos(
             rect_width + self.LINE_LENGTH + self.MUX_WIDTH,
             # Use the buses port margins so the ports align nicely
             mux_height / 2 - BusGraphicsItem.PORT_MARGIN / 2,
         )
 
-        to_bus_port = PortGraphicsItem(self.module.to_bus, Orientation.RIGHT, self)
+        to_bus_port = PortGraphicsItem(self.module.signals["out_content"], Orientation.RIGHT, self)
         to_bus_port.setPos(
             rect_width + self.LINE_LENGTH + self.MUX_WIDTH,
             # Use the buses port margins so the ports align nicely
@@ -145,7 +148,9 @@ class GrxGraphicsItem(ModuleGraphicsItem):
         for index, register_value in enumerate(self.module.registers):
             text = self.register_text_labels[index]
             name = "Gr" + str(index)
-            hex_length = math.ceil(self.state["bit_length"] / 4)
+
+            # Mia uses 4 hex numbers for each register
+            hex_length = self.REGISTER_LENGTH
 
             value_text = f"0x{register_value:0{hex_length}x}"
             full_text = name + ": " + value_text[2:]
diff --git a/src/simudator/processor/mia/gui/mia_memory_graphic.py b/src/simudator/processor/mia/gui/mia_memory_graphic.py
index 569c36f3ee4da73a10e8ba0d86777bc5d9fd6561..ec29b7717ebc86efe8b0762dc07f3abef6ca34a1 100644
--- a/src/simudator/processor/mia/gui/mia_memory_graphic.py
+++ b/src/simudator/processor/mia/gui/mia_memory_graphic.py
@@ -2,12 +2,12 @@ import ast
 
 from qtpy.QtCore import Slot
 from qtpy.QtWidgets import (
+    QErrorMessage,
     QGraphicsRectItem,
     QGraphicsSimpleTextItem,
     QTextEdit,
     QVBoxLayout,
     QWidget,
-    QErrorMessage,
 )
 
 from simudator.core.modules import Memory
@@ -21,38 +21,6 @@ from simudator.gui.port_graphics_item import PortGraphicsItem
 from simudator.processor.mia.gui.mia_memory_content_dialog import MiaMemoryContentDialog
 
 
-class MiaMemoryWindow(QWidget):
-    """
-    Widget for showing content of memory
-    """
-
-    ROW_LENGTH = 4
-
-    def __init__(self, memory_module: Memory):
-        super().__init__()
-        self.module = memory_module
-
-        self.text = QTextEdit("")
-        layout = QVBoxLayout()
-        layout.addWidget(self.text)
-        self.setLayout(layout)
-
-        self.update()
-
-    def update(self):
-        memory_str = ""
-        for adress, value in enumerate(self.module.memory):
-            # Add adress and content to string
-            # Make sure its unifrom lenght so rows are consistent
-            memory_str += f"0x{adress:02x}" + ": " + f"0x{value:04x}" + "   " + "\t"
-
-            # Make new line when we reach end of row
-            if adress % self.ROW_LENGTH == self.ROW_LENGTH - 1:
-                memory_str += "\n"
-
-        self.text.setText(memory_str)
-
-
 class MiaMemoryGraphicsItem(MemoryGraphicsItem):
     """
     Graphics module for a Memory module.
@@ -61,7 +29,7 @@ class MiaMemoryGraphicsItem(MemoryGraphicsItem):
     def __init__(self, memory_module: Memory, **kwargs):
         super().__init__(memory_module, **kwargs)
         self.memory_window = None
-        self.errorMessageWidget = QErrorMessage()
+        self._errorMessageWidget = QErrorMessage()
 
     def draw_graphics_item(self) -> None:
         # Same as normal memory but no control signal
@@ -73,31 +41,31 @@ class MiaMemoryGraphicsItem(MemoryGraphicsItem):
 
         # Make ports
         self.input = PortGraphicsItem(
-            self.module.get_input_signal(), Orientation.RIGHT, self
+            self.module.signals["in_input"], Orientation.RIGHT, self
         )
         self.input.setPos(self._width, self._height / 8)
 
         self.output = PortGraphicsItem(
-            self.module.get_output_signal(), Orientation.RIGHT, self
+            self.module.signals["out_content"], Orientation.RIGHT, self
         )
         self.output.setPos(self._width, self._height / 4)
 
-        self.adress = PortGraphicsItem(
-            self.module.get_adress_signal(), Orientation.DOWN, self
+        self.address = PortGraphicsItem(
+            self.module.signals["in_address"], Orientation.DOWN, self
         )
-        self.adress.setPos(self._width / 4, self._height)
+        self.address.setPos(self._width / 4, self._height)
 
-        self.ports = [self.input, self.output, self.adress]
+        self.ports = [self.input, self.output, self.address]
 
         # Create name lable
         name_text = QGraphicsSimpleTextItem(self.state["name"], self)
         name_text.setPos(self._width / 2 - len(self.state["name"]) * self._CHAR_LEN, 0)
 
-        # Create adress lable
-        self.adress_text = QGraphicsSimpleTextItem(
-            "adress: " + str(self.state["current_adress"]), self
+        # Create address lable
+        self.address_text = QGraphicsSimpleTextItem(
+            "address: " + str(self.state["current_address"]), self
         )
-        self.adress_text.setPos(self._width / 20, self._height / 8)
+        self.address_text.setPos(self._width / 20, self._height / 8)
 
     def showMemoryContents(self) -> None:
         """
@@ -116,46 +84,51 @@ class MiaMemoryGraphicsItem(MemoryGraphicsItem):
 
     @Slot(str, str, str)
     def memoryBreakpointAccepted(
-        self, module_name: str, adress: str, value: str
+        self, module_name: str, address: str, value: str
     ) -> None:
         """
         Same as prent function but also prases data so it is hexadecimal.
         """
         try:
-            parsed_adress = int(adress, 16)
+            parsed_address = int(address, 16)
             parsed_value = ast.literal_eval(value)
             self.new_memory_breakpoint_signal.emit(
-                module_name, str(parsed_adress), str(parsed_value)
+                module_name, str(parsed_address), str(parsed_value)
             )
         except SyntaxError as e:
-            self.errorMessageWidget.showMessage(str(e))
+            self._errorMessageWidget.showMessage(str(e))
         except ValueError:
-            self.errorMessageWidget.showMessage(
+            self._errorMessageWidget.showMessage(
                 "You must enter a hexadecimal" "number preceeded by '0x' (e.g." "0xc3)."
             )
 
     @Slot(str, str, str)
-    def editMemoryAccepted(self, module_name: str, adress: str, value: str) -> None:
+    def editMemoryAccepted(self, module_name: str, address: str, value: str) -> None:
         """
         Same as prent function but also prases data so it is hexadecimal.
         """
         try:
-            parsed_adress = int(adress, 16)
+            parsed_adress = int(address, 16)
             parsed_value = ast.literal_eval(value)
         except SyntaxError as e:
-            self.errorMessageWidget.showMessage(str(e))
+            self._errorMessageWidget.showMessage(str(e))
         except ValueError:
-            self.errorMessageWidget.showMessage(
+            self._errorMessageWidget.showMessage(
                 "You must enter a hexadecimal" "number preceeded by '0x' (e.g." "0xc3)."
             )
         else:
             module_state = self.module.get_state()
             try:
+                if parsed_adress < 0:
+                    raise IndexError
                 module_state['memory'][parsed_adress] = parsed_value
             except IndexError:
-                self.errorMessageWidget.showMessage(
-                    "Address entered was larger than memory space. Max address is 0xff"
+                self._errorMessageWidget.showMessage(
+                    "Address entered was outside the memory space. Adress must be between 0 and 0xff."
                 )
             else:
                 self.module.set_state(module_state)
                 self.update_graphics_signal.emit()
+            module_state['memory'][parsed_adress] = parsed_value
+            self.module.set_state(module_state)
+            self.update_graphics_signal.emit()
diff --git a/src/simudator/processor/mia/gui/pc_graphic.py b/src/simudator/processor/mia/gui/pc_graphic.py
index 498de090b8af31e97563f590d28c193658d91440..34ed5fcbe1aa599609cd154e72968116ac713818 100644
--- a/src/simudator/processor/mia/gui/pc_graphic.py
+++ b/src/simudator/processor/mia/gui/pc_graphic.py
@@ -27,11 +27,13 @@ class PcGraphicsItem(IntegerRegisterGraphicsItem):
         )
 
         # Draw to bus port
-        to_bus_port = PortGraphicsItem(self.module.bus_input_s, parent=self)
+        to_bus_port = PortGraphicsItem(self.module.signals["in_input"], parent=self)
         to_bus_port.setPos(width, self._RECT_HEIGHT / 4)
         self.ports.append(to_bus_port)
 
         # Draw from bus port
-        from_bus_port = PortGraphicsItem(self.module.bus_output_s, parent=self)
+        from_bus_port = PortGraphicsItem(
+            self.module.signals["out_content"], parent=self
+        )
         from_bus_port.setPos(width, self._RECT_HEIGHT * 3 / 4)
         self.ports.append(from_bus_port)
diff --git a/src/simudator/processor/mia/gui/supc_graphic.py b/src/simudator/processor/mia/gui/supc_graphic.py
index 730a1a292056febc7c76df298eba09ea3cd13edb..fab770b680df22d54ffad668165fc4091ac17d11 100644
--- a/src/simudator/processor/mia/gui/supc_graphic.py
+++ b/src/simudator/processor/mia/gui/supc_graphic.py
@@ -18,13 +18,15 @@ class SupcGraphicsItem(IntegerRegisterGraphicsItem):
         )
 
         # Draw to mPc port
-        to_upc_port = PortGraphicsItem(self.module.input_s, Orientation.UP, parent=self)
+        to_upc_port = PortGraphicsItem(
+            self.module.signals["in_content"], Orientation.UP, parent=self
+        )
         to_upc_port.setPos(width * 2 / 8, 0)
         self.ports.append(to_upc_port)
 
         # Draw from uPc port
         from_upc_port = PortGraphicsItem(
-            self.module.output_s, Orientation.UP, parent=self
+            self.module.signals["out_content"], Orientation.UP, parent=self
         )
         from_upc_port.setPos(width * 6 / 8, 0)
         self.ports.append(from_upc_port)
diff --git a/src/simudator/processor/mia/gui/upc_graphic.py b/src/simudator/processor/mia/gui/upc_graphic.py
index 180c318e322fa512eb7b3bb5cc91b3cc508d8126..85d27b9a1715fbf22970baf711611fb7e5e9ac9c 100644
--- a/src/simudator/processor/mia/gui/upc_graphic.py
+++ b/src/simudator/processor/mia/gui/upc_graphic.py
@@ -27,25 +27,25 @@ class uPcGraphicsItem(IntegerRegisterGraphicsItem):
         )
 
         # Draw from K1 port
-        from_k1 = PortGraphicsItem(self.module.from_k1, Orientation.UP, parent=self)
+        from_k1 = PortGraphicsItem(self.module.signals["in_k1"], Orientation.UP, parent=self)
         from_k1.setPos(width * 2 / 8, 0)
         self.ports.append(from_k1)
 
         # Draw from K2 port
-        from_k2 = PortGraphicsItem(self.module.from_k2, Orientation.UP, parent=self)
+        from_k2 = PortGraphicsItem(self.module.signals["in_k2"], Orientation.UP, parent=self)
         from_k2.setPos(width * 6 / 8, 0)
         self.ports.append(from_k2)
 
         # Draw to SUPc port
         to_supc_port = PortGraphicsItem(
-            self.module.from_supc, Orientation.DOWN, parent=self
+            self.module.signals["out_supc"], Orientation.DOWN, parent=self
         )
         to_supc_port.setPos(width * 2 / 8, self._RECT_HEIGHT)
         self.ports.append(to_supc_port)
 
         # Draw from SuPc port
         from_supc_port = PortGraphicsItem(
-            self.module.to_supc, Orientation.DOWN, parent=self
+            self.module.signals["in_supc"], Orientation.DOWN, parent=self
         )
         from_supc_port.setPos(width * 6 / 8, self._RECT_HEIGHT)
         self.ports.append(from_supc_port)
diff --git a/src/simudator/processor/mia/mia.py b/src/simudator/processor/mia/mia.py
index 62b7fe1d17a859be159499ee9d36d7cd6fcfe7ef..1b6d95d866d8a1a63dff4809f2a4eb62a32faf21 100644
--- a/src/simudator/processor/mia/mia.py
+++ b/src/simudator/processor/mia/mia.py
@@ -1,4 +1,5 @@
 import sys
+from typing import Any
 
 from simudator.cli.cli import CLI
 from simudator.core.modules import Flag
@@ -31,7 +32,6 @@ from simudator.processor.mia.modules.mia_grx import GRX
 from simudator.processor.mia.modules.mia_memory import MiaMemory
 from simudator.processor.mia.modules.micro_memory import MicroMemory
 from simudator.processor.mia.modules.micro_pc import MicroPC
-
 from simudator.processor.mia.modules.pc import PC
 
 
@@ -138,8 +138,6 @@ class MIA_CPU(Processor):
         # PM specific
         pm_size = 256
         pm_bus_id = 0b010
-        pm_adress_padding = 2
-        pm_value_padding = 4
         pm = MiaMemory(
             pm_bus,
             bus_pm,
@@ -147,8 +145,6 @@ class MIA_CPU(Processor):
             bus_control,
             pm_size,
             pm_bus_id,
-            value_padding=pm_value_padding,
-            adress_padding=pm_adress_padding,
             name="PM",
         )
 
@@ -160,8 +156,6 @@ class MIA_CPU(Processor):
 
         # K1 specific
         k1_size = 16
-        k1_adress_padding = 2
-        k1_value_padding = 2
         k1 = MiaMemory(
             always_write_signal,
             uPC_k1,
@@ -170,14 +164,10 @@ class MIA_CPU(Processor):
             k1_size,
             always_write_id,
             name="K1",
-            adress_padding=k1_adress_padding,
-            value_padding=k1_value_padding,
         )
 
         # K2 specific
         k2_size = 4
-        k2_adress_padding = 2
-        k2_value_padding = 2
         k2 = MiaMemory(
             always_write_signal,
             uPC_k2,
@@ -186,8 +176,6 @@ class MIA_CPU(Processor):
             k2_size,
             always_write_id,
             name="K2",
-            adress_padding=k2_adress_padding,
-            value_padding=k2_value_padding,
         )
 
         # IR specific
@@ -281,9 +269,28 @@ class MIA_CPU(Processor):
     def is_new_instruction(self) -> bool:
         return self.get_module("uPC").value == 0
 
-    def get_current_instruction(self) -> str:
+    def get_current_instructions(self) -> list[tuple[str, int, int]]:
+        asm_instr = self.get_asm_instruction()
+        micro_instr = self.get_micro_instruction()
+        asm = (asm_instr, 1, 0)
+        micro = (micro_instr, 1, 1)
+        asm_title = ("Assembler", 0, 0)
+        micro_title = ("Micro", 0, 1)
+        return [asm, micro, asm_title, micro_title]
+
+    def get_micro_instruction(self) -> str:
+        """
+        Return a string containing the address for the current micro instruction in hex, e.g. 0a4b.
+        """
+        uM = self.get_module("uM").get_state()
+        curr_instr = uM["curr_instr"]
+        memory = uM["memory"]
+
+        return '0x' + format(memory[curr_instr], '07x')
+
+    def get_asm_instruction(self) -> str:
         """
-        Return a string containing the label for the current instruction.
+        Return a string containing the label for the current assembly instruction.
 
         If the label doesnt exist, the string is empty.
         """
@@ -291,6 +298,14 @@ class MIA_CPU(Processor):
         op_code = ir.op
         return self.get_module("K1").get_label(int(op_code))
 
+    def get_pipeline_dimensions(self) -> tuple[int, int]:
+        """
+        Mia should show the current assembly instruction and the current micro instruction.
+
+        One Extra column to specify what the values represent.
+        """
+        return (2, 2)
+
     def run_asm_instruction(self, num_instructions=1) -> None:
         """
         Runs 'num_instructions' assembler instructions on Mia.
@@ -331,19 +346,19 @@ class MIA_CPU(Processor):
         grx = self.get_module("GRx")
 
         bus_signal_pairs = [
-            (asr.input_s, None),
-            (ir.from_bus_s, ir.to_bus_s),
-            (pm.input_s, pm.output_s),
+            (asr.signals["in_content"], None),
+            (ir.signals["in_input"], ir.signals["out_output"]),
+            (pm.signals["in_input"], pm.signals["out_content"]),
             (None, None),
-            (pc.bus_input_s, pc.bus_output_s),
+            (pc.signals["in_input"], pc.signals["out_content"]),
             (None, None),
-            (alu.input_signal_b, None),
+            (alu.signals["in_input_b"], None),
             (None, None),
-            (None, ar.output_s),
+            (None, ar.signals["out_content"]),
             (None, None),
-            (hr.input_s, hr.output_s),
+            (hr.signals["in_content"], hr.signals["out_content"]),
             (None, None),
-            (grx.from_bus, grx.to_bus),
+            (grx.signals["in_input"], grx.signals["out_content"]),
         ]
 
         gui.addModuleGraphicsItem(
diff --git a/src/simudator/processor/mia/modules/alu.py b/src/simudator/processor/mia/modules/alu.py
index 13b3e68a3be7aff98378331911ab17dc973b0fee..482da580be76ab06e8f941cbf00ef8febd577e5c 100644
--- a/src/simudator/processor/mia/modules/alu.py
+++ b/src/simudator/processor/mia/modules/alu.py
@@ -14,6 +14,8 @@ class ALU(Module):
     Will do all calulations as two's complement binary numbers.
     """
 
+    __slots__ = ("z_value", "n_value", "o_value", "c_value", "hr_value")
+
     WORD_LENGTH = 16
 
     def __init__(
@@ -30,27 +32,20 @@ class ALU(Module):
         o_signal: Signal,
         c_signal: Signal,
     ):
-        super().__init__(name)
-
-        # Input Signals
-        self.input_signal_a = input_signal_a
-        self.input_signal_b = input_signal_b
-        self.control_signal = control_signal
-        self.input_signal_hr = input_signal_hr
-
-        # Add destinations
-        self.input_signal_a.add_destination(self)
-        self.input_signal_b.add_destination(self)
-        self.control_signal.add_destination(self)
-        self.input_signal_hr.add_destination(self)
-
-        # Output Signals
-        self.output_signal = output_signal
-        self.output_signal_hr = output_signal_hr
-        self.z_signal = z_signal
-        self.n_signal = n_signal
-        self.o_signal = o_signal
-        self.c_signal = c_signal
+
+        signals = {
+            "in_input_a": input_signal_a,
+            "in_input_b": input_signal_b,
+            "in_control": control_signal,
+            "in_hr": input_signal_hr,
+            "out_result": output_signal,
+            "out_hr": output_signal_hr,
+            "out_flag_z": z_signal,
+            "out_flag_n": n_signal,
+            "out_flag_o": o_signal,
+            "out_flag_c": c_signal,
+        }
+        super().__init__(signals, name)
 
         # flag values
         self.z_value = 0
@@ -74,8 +69,9 @@ class ALU(Module):
         pass
 
     def save_state_to_file(self, file_path: str) -> bool:
-        """
-        The ali is state-less.
+        """Do nothing.
+
+        The alu is state-less.
         """
         return True
 
@@ -85,12 +81,12 @@ class ALU(Module):
         and update outputs with result.
         """
 
-        value_a = self.input_signal_a.get_value()
-        value_b = self.input_signal_b.get_value()
-        value_hr = self.input_signal_hr.get_value()
+        value_a = self.signals["in_input_a"].get_value()
+        value_b = self.signals["in_input_b"].get_value()
+        value_hr = self.signals["in_hr"].get_value()
 
         # bitmask control value to get first 4 bits
-        control_value = self.control_signal.get_value()
+        control_value = self.signals["in_control"].get_value()
 
         # If control value is none do nothing
         if control_value is None:
@@ -172,12 +168,12 @@ class ALU(Module):
                 return
 
         # Update output signal and flags
-        self.output_signal.update_value(output_value)
-        self.z_signal.update_value(self.z_value)
-        self.n_signal.update_value(self.n_value)
-        self.o_signal.update_value(self.o_value)
-        self.c_signal.update_value(self.c_value)
-        self.output_signal_hr.update_value(self.hr_value)
+        self.signals["out_result"].update_value(output_value)
+        self.signals["out_flag_z"].update_value(self.z_value)
+        self.signals["out_flag_n"].update_value(self.n_value)
+        self.signals["out_flag_o"].update_value(self.o_value)
+        self.signals["out_flag_c"].update_value(self.c_value)
+        self.signals["out_hr"].update_value(self.hr_value)
 
     def print_module(self) -> None:
         print(
@@ -185,33 +181,19 @@ class ALU(Module):
             self.name,
             "\n -----",
             "\n a_value: ",
-            self.input_signal_a.get_value(),
+            self.signals["in_input_a"].get_value(),
             "\n b_value: ",
-            self.input_signal_b.get_value(),
+            self.signals["in_input_b"].get_value(),
             "\n control_signal: ",
-            self.control_signal.get_value(),
+            self.signals["in_control"].get_value(),
         )
 
-    def get_input_signals(self) -> list[Signal]:
-        return [
-            self.input_signal_a,
-            self.input_signal_b,
-            self.control_signal,
-            self.input_signal_hr,
-        ]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [
-            self.output_signal,
-            self.output_signal_hr,
-            self.z_signal,
-            self.n_signal,
-            self.o_signal,
-            self.c_signal,
-        ]
-
     def reset(self) -> None:
-        pass
+        self.z_value = 0
+        self.n_value = 0
+        self.o_value = 0
+        self.c_value = 0
+        self.hr_value = None
 
     # ----- ALU operations -----
 
@@ -442,7 +424,7 @@ class ALU(Module):
 
         if hr_value is not None:
             # get hr as a binary
-            hr_binary = self.int_to_bin(self.input_signal_hr.get_value())
+            hr_binary = self.int_to_bin(self.signals["in_hr"].get_value())
 
             long_word_binary = hr_binary[1:] + binary_number + [hr_binary[0]]
 
@@ -582,7 +564,11 @@ class ALU(Module):
     # ----- Internal operations -----
 
     def update_flags(
-        self, binary_a: [int], binary_b: [int], binary_result: [int], carry: int
+        self,
+        binary_a: list[int],
+        binary_b: list[int],
+        binary_result: list[int],
+        carry: int,
     ) -> None:
         """
         Will update the flags depending on inputed values and the result.
@@ -623,7 +609,7 @@ class ALU(Module):
         """
 
         # Make empty binary number for result
-        new_binary_number = [0 for i in range(self.WORD_LENGTH)]
+        new_binary_number = [0 for _ in range(self.WORD_LENGTH)]
 
         # Change each bits value
         for bit_index in range(self.WORD_LENGTH):
@@ -634,14 +620,16 @@ class ALU(Module):
 
         return new_binary_number
 
-    def add_binary_numbers(self, binary_a: [int], binary_b: [int]) -> ([int], int):
+    def add_binary_numbers(
+        self, binary_a: list[int], binary_b: list[int]
+    ) -> tuple[list[int], int]:
         """
         Returns a tuple with the sum of two binary numbers
         and the carry bit from the calculations
         """
 
         # Make new binary number to store results
-        binary_sum = [0 for i in range(self.WORD_LENGTH)]
+        binary_sum = [0 for _ in range(self.WORD_LENGTH)]
 
         # Need carry for calculation
         carry = 0
@@ -665,22 +653,22 @@ class ALU(Module):
 
         return (binary_sum, carry)
 
-    def binary_and(self, binary_a: int, binary_b: int) -> int:
+    def binary_and(self, binary_a: list[int], binary_b: list[int]) -> list[int]:
         """
         Calculate and between binary two numbers
         """
-        binary_and = [0 for i in range(self.WORD_LENGTH)]
+        binary_and = [0 for _ in range(self.WORD_LENGTH)]
 
         for binary_index in range(self.WORD_LENGTH):
             binary_and[binary_index] = binary_a[binary_index] & binary_b[binary_index]
 
         return binary_and
 
-    def binary_or(self, binary_a: [int], binary_b: [int]) -> [int]:
+    def binary_or(self, binary_a: list[int], binary_b: list[int]) -> list[int]:
         """
         Calculate or between two binary numbers
         """
-        binary_or = [0 for i in range(self.WORD_LENGTH)]
+        binary_or = [0 for _ in range(self.WORD_LENGTH)]
 
         for binary_index in range(self.WORD_LENGTH):
             binary_or[binary_index] = binary_a[binary_index] | binary_b[binary_index]
diff --git a/src/simudator/processor/mia/modules/ar.py b/src/simudator/processor/mia/modules/ar.py
index 19eca435ca649d49f28481f82a4d77495975a071..5ca55569f7678c4a57cc67b5adb8a4f8090a0129 100644
--- a/src/simudator/processor/mia/modules/ar.py
+++ b/src/simudator/processor/mia/modules/ar.py
@@ -8,6 +8,11 @@ class AR(IntegerRegister, MiaBusConnector):
     Register for saving AlU calculations in MIA.
     """
 
+    # Python does not allow multiple inherintence if more than one of the
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = ("bus_id", "bus_control_s")
+
     def __init__(
         self,
         alu_input_signal: Signal,
@@ -18,34 +23,29 @@ class AR(IntegerRegister, MiaBusConnector):
         bit_length: int,
         name="AR",
     ) -> None:
-        self.alu_output_signal = alu_output_signal
 
         IntegerRegister.__init__(
             self, alu_input_signal, bus_output_signal, bit_length, name=name
         )
         MiaBusConnector.__init__(self, bus_control_signal, bus_id)
 
+        self.signals["out_alu"] = alu_output_signal
+
     def output_register(self) -> None:
         # always update alu
-        self.alu_output_signal.update_value(self.value)
+        self.signals["out_alu"].update_value(self._value)
 
         # only update bus when asked too
         if self.write_to_bus():
             IntegerRegister.output_register(self)
         else:
-            self.output_s.update_value(None)
+            self.signals["out_content"].update_value(None)
 
     def update_logic(self) -> None:
         if self.write_to_bus():
             IntegerRegister.output_register(self)
         else:
-            self.output_s.update_value(None)
-
-    def get_input_signals(self) -> list[Signal]:
-        return [self.input_s]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [self.alu_output_signal, self.output_s, self.bus_control_s]
+            self.signals["out_content"].update_value(None)
 
     def print_module(self):
         print(
@@ -53,7 +53,7 @@ class AR(IntegerRegister, MiaBusConnector):
             self.name,
             "\n -----",
             "\n value: ",
-            hex(self.value),
+            hex(self._value),
             "\n bit length: ",
-            self.bit_length,
+            self._bit_length,
         )
diff --git a/src/simudator/processor/mia/modules/asr.py b/src/simudator/processor/mia/modules/asr.py
index 3b5a53ed96341aa7c51ad678eaa542353ff35999..52db394554dbaf045e44ca106ad3df0a7af6b361 100644
--- a/src/simudator/processor/mia/modules/asr.py
+++ b/src/simudator/processor/mia/modules/asr.py
@@ -8,6 +8,11 @@ class ASR(IntegerRegister, MiaBusConnector):
     Register for controlling the memory andress in MIA.
     """
 
+    # Python does not allow multiple inherintence if more than one of the
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = ("bus_id", "bus_control_s")
+
     def __init__(
         self,
         bus_input_signal: Signal,
@@ -30,19 +35,13 @@ class ASR(IntegerRegister, MiaBusConnector):
         if self.write_to_bus():
             self.output_register()
 
-    def get_input_signals(self) -> [Signal]:
-        return [self.input_s, self.bus_control_s]
-
-    def get_output_signals(self) -> [Signal]:
-        return [self.output_s]
-
     def print_module(self):
         print(
             "",
             self.name,
             "\n -----",
             "\n value: ",
-            hex(self.value),
+            hex(self._value),
             "\n bit length: ",
-            self.bit_length,
+            self._bit_length,
         )
diff --git a/src/simudator/processor/mia/modules/bus.py b/src/simudator/processor/mia/modules/bus.py
index 1ae6d091be69a3f738ffe1f7345e2ebf22ac7c80..62f56271db27a2d4971f99152dff25e19f38fe52 100644
--- a/src/simudator/processor/mia/modules/bus.py
+++ b/src/simudator/processor/mia/modules/bus.py
@@ -10,17 +10,18 @@ class Bus(Module):
     input signal can send a value to the bus at a time.
     """
 
+    __slots__ = ()
+
     def __init__(
         self, inputs: list[Signal] = [], outputs: list[Signal] = [], name: str = "Bus"
     ) -> None:
-        super().__init__(name)
-
-        self.inputs = inputs
-        self.outputs = outputs
 
-        # add self as destination to all inputs
-        for input_signal in self.inputs:
-            input_signal.add_destination(self)
+        signals = {}
+        for i, s in enumerate(inputs):
+            signals[f"in_input_{i}"] = s
+        for i, s in enumerate(outputs):
+            signals[f"out_output_{i}"] = s
+        super().__init__(signals, name)
 
     def update_logic(self) -> None:
         """
@@ -30,7 +31,7 @@ class Bus(Module):
         nothing will be transferred onto the output signals.
         """
         value = None
-        for input_signal in self.inputs:
+        for input_signal in self.get_input_signals():
             if input_signal.get_value() is not None:
                 if value is not None:
                     # Two input signals are writing to the bus at the
@@ -40,7 +41,7 @@ class Bus(Module):
                 value = input_signal.get_value()
 
         if value is not None:
-            for output_signal in self.outputs:
+            for output_signal in self.get_output_signals():
                 output_signal.update_value(value)
 
     def get_state(self) -> dict:
@@ -55,12 +56,6 @@ class Bus(Module):
     def reset(self) -> None:
         pass
 
-    def get_input_signals(self) -> list[Signal]:
-        return self.inputs
-
-    def get_output_signals(self) -> list[Signal]:
-        return self.outputs
-
     def get_name(self) -> str:
         return self.name
 
@@ -77,7 +72,8 @@ class Bus(Module):
         pass
 
     def save_state_to_file(self, file_path: str) -> bool:
-        """
-        The ali is state-less.
+        """Do nothing.
+
+        The bus is state-less.
         """
         return True
diff --git a/src/simudator/processor/mia/modules/hr.py b/src/simudator/processor/mia/modules/hr.py
index 2a2d869792a87f4e21b16ad208798140fa4d0ad4..bd74491b83632dcbe52679eaba363326fece177c 100644
--- a/src/simudator/processor/mia/modules/hr.py
+++ b/src/simudator/processor/mia/modules/hr.py
@@ -8,6 +8,11 @@ class HR(IntegerRegister, MiaBusConnector):
     Register for saving large AlU calculations in MIA.
     """
 
+    # Python does not allow multiple inherintence if more than one of the 
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = ("bus_id", "bus_control_s")
+
     def __init__(
         self,
         alu_input_signal: Signal,
@@ -16,19 +21,18 @@ class HR(IntegerRegister, MiaBusConnector):
         bus_output_signal: Signal,
         bus_control_signal: Signal,
         bus_id: int,
-        bit_lenght: int,
+        bit_length: int,
         name="HR",
     ) -> None:
-        self.alu_output_signal = alu_output_signal
-        self.alu_input_signal = alu_input_signal
-
-        self.alu_input_signal.add_destination(self)
-
         IntegerRegister.__init__(
-            self, bus_input_signal, bus_output_signal, bit_lenght, name=name
+            self, bus_input_signal, bus_output_signal, bit_length, name=name
         )
         MiaBusConnector.__init__(self, bus_control_signal, bus_id)
 
+        alu_input_signal.add_destination(self)
+        self.signals["in_alu"] = alu_input_signal
+        self.signals["out_alu"] = alu_output_signal
+
     def update_register(self) -> None:
         """
         Updates the internal values from the signals from the bus and
@@ -39,9 +43,10 @@ class HR(IntegerRegister, MiaBusConnector):
         if self.read_from_bus():
             IntegerRegister.update_register(self)
 
-        elif self.alu_input_signal.get_value() is not None:
-            input_value = self.alu_input_signal.get_value()
-            self.value = input_value & self.mask
+        elif self.signals["in_alu"].get_value() is not None:
+            input_value = self.signals["in_alu"].get_value()
+            mask = 2**self._bit_length - 1
+            self._value = input_value & mask
 
     def output_register(self) -> None:
         """
@@ -50,13 +55,13 @@ class HR(IntegerRegister, MiaBusConnector):
         output the alu signal.
         """
 
-        self.alu_output_signal.update_value(self.value)
+        self.signals["out_alu"].update_value(self._value)
 
         if self.write_to_bus():
             IntegerRegister.output_register(self)
 
         else:
-            self.output_s.update_value(None)
+            self.signals["out_content"].update_value(None)
 
     def update_logic(self):
         self.output_register()
@@ -67,17 +72,7 @@ class HR(IntegerRegister, MiaBusConnector):
             self.name,
             "\n -----",
             "\n value: ",
-            hex(self.value),
+            hex(self._value),
             "\n bit length: ",
-            self.bit_length,
+            self._bit_length,
         )
-
-    def get_input_signals(self) -> list[Signal]:
-        return [self.alu_input_signal, self.input_s]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [
-            self.alu_output_signal,
-            self.bus_control_s,
-            self.output_s,
-        ]
diff --git a/src/simudator/processor/mia/modules/ir.py b/src/simudator/processor/mia/modules/ir.py
index 6d0e4ca0b0658e4492fd53e79ed1cfb002784387..00ee7de233523a7795616879f8c8ff968071e181 100644
--- a/src/simudator/processor/mia/modules/ir.py
+++ b/src/simudator/processor/mia/modules/ir.py
@@ -12,6 +12,11 @@ class IR(Module, MiaBusConnector):
     several fields.
     """
 
+    # Python does not allow multiple inherintence if more than one of the
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = ("op", "grx", "m", "a", "bus_id", "instruction", "bus_control_s")
+
     def __init__(
         self,
         to_bus: Signal,
@@ -24,20 +29,16 @@ class IR(Module, MiaBusConnector):
         bus_id=0,
         name: str = "IR",
     ) -> None:
-        MiaBusConnector.__init__(self, bus_control, bus_id)
-        Module.__init__(self, name)
-
-        # Input signals
-        self.from_bus_s = from_bus
 
-        # Output signals
-        self.to_bus_s = to_bus
-        self.op_s = op_s
-        self.grx_s = grx_s
-        self.m_s = m_s
-
-        # Register as destination of input signals
-        from_bus.add_destination(self)
+        signals = {
+            "in_input": from_bus,
+            "out_output": to_bus,
+            "out_op": op_s,
+            "out_grx": grx_s,
+            "out_m": m_s,
+        }
+        Module.__init__(self, signals, name)
+        MiaBusConnector.__init__(self, bus_control, bus_id)
 
         # Internal values
         self.op = 0
@@ -53,7 +54,7 @@ class IR(Module, MiaBusConnector):
         from the bus and bitshift each part of the instruction.
         """
         if self.read_from_bus():
-            self.instruction = self.from_bus_s.get_value()
+            self.instruction = self.signals["in_input"].get_value()
             self.op = self.instruction >> 12
             self.grx = (self.instruction >> 10) & 0b11
             self.m = (self.instruction >> 8) & 0b11
@@ -65,14 +66,14 @@ class IR(Module, MiaBusConnector):
         and m_s, and output its whole instruction to the bus when
         asked to.
         """
-        self.op_s.update_value(self.op)
-        self.grx_s.update_value(self.grx)
-        self.m_s.update_value(self.m)
+        self.signals["out_op"].update_value(self.op)
+        self.signals["out_grx"].update_value(self.grx)
+        self.signals["out_m"].update_value(self.m)
 
         if self.write_to_bus():
-            self.to_bus_s.update_value(self.instruction)
+            self.signals["out_output"].update_value(self.instruction)
         else:
-            self.to_bus_s.update_value(None)
+            self.signals["out_output"].update_value(None)
 
     def update_logic(self) -> None:
         self.output_register()
@@ -132,27 +133,12 @@ class IR(Module, MiaBusConnector):
             bin(self.a),
         )
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
-        """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("Instruction: " + hex(self.instruction)[2:] + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\n"
+        content += "Instruction: " + hex(self.instruction)[2:] + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def load_from_str(self, state_string):
         string_pair = state_string.split(": ")
         # TODO: Maybe check if it starts with instruction: ?
         self.instruction = int(string_pair[1], 16)
-
-    def get_input_signals(self) -> list[Signal]:
-        return [self.from_bus_s]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [
-            self.to_bus_s,
-            self.op_s,
-            self.grx_s,
-            self.m_s,
-        ]
diff --git a/src/simudator/processor/mia/modules/lc.py b/src/simudator/processor/mia/modules/lc.py
index bbf0915ad365861e07d19ca326dc4beae0982f72..357e46be5fed5ee27e523a6e2a426f49411a00bc 100644
--- a/src/simudator/processor/mia/modules/lc.py
+++ b/src/simudator/processor/mia/modules/lc.py
@@ -12,6 +12,15 @@ class LC(Module):
     increase by one or read from mM_uADR.
     """
 
+    __slots__ = (
+        "value",
+        "read_from_bus",
+        "read_from_uADR",
+        "decrement_by_one",
+        "bit_length",
+        "mask",
+    )
+
     def __init__(
         self,
         mM_control: Signal,
@@ -50,14 +59,15 @@ class LC(Module):
             Optional bit length of the loop counter.
         """
 
-        # init the name
-        super().__init__(name)
-
         # signals
-        self.mM_control_s = mM_control
-        self.bus_input_s = bus_input
-        self.l_flag_s = l_flag
-        self.mM_uADR_s = mM_uADR
+        signals = {
+            "in_control": mM_control,
+            "in_input": bus_input,
+            "in_address": mM_uADR,
+            "out_flag_l": l_flag,
+        }
+
+        super().__init__(signals, name)
 
         # the value of the loop counter
         self.value = value
@@ -71,11 +81,6 @@ class LC(Module):
         self.bit_length = bit_length
         self.mask = 2**self.bit_length - 1
 
-        # set the loop counter as the destination of its input signals
-        self.mM_control_s.add_destination(self)
-        self.bus_input_s.add_destination(self)
-        self.mM_uADR_s.add_destination(self)
-
     def update_register(self) -> None:
         """Reads bit 12 and 13 from the micro memory and updates the
         loop counter.
@@ -85,7 +90,7 @@ class LC(Module):
         3 - loads the 7 least significant bits from the uADR field.
         """
 
-        match self.mM_control_s.get_value():
+        match self.signals["in_control"].get_value():
             case 0b00:  # LC is not effected
                 self.decrement_by_one = False
                 self.read_from_bus = False
@@ -107,11 +112,11 @@ class LC(Module):
                 self.read_from_uADR = True
 
         if self.read_from_bus:
-            input_value = self.bus_input_s.get_value()
+            input_value = self.signals["in_input"].get_value()
             self.value = input_value & self.mask
 
         if self.read_from_uADR:
-            input_value = self.mM_uADR_s.get_value()
+            input_value = self.signals["in_address"].get_value()
             self.value = input_value & self.mask
 
         if self.decrement_by_one:
@@ -132,11 +137,11 @@ class LC(Module):
         Otherwise set it to zero.
         """
         if self.value == 0:
-            self.l_flag_s.update_value(1)
+            self.signals["out_flag_l"].update_value(1)
         else:
-            self.l_flag_s.update_value(0)
+            self.signals["out_flag_l"].update_value(0)
 
-    def get_state(self) -> dict[str:Any]:
+    def get_state(self) -> dict[str, Any]:
         """Returns a dict of the loop counter state.
         These states are changable via set_states.
 
@@ -155,7 +160,7 @@ class LC(Module):
         state["decrement_by_one"] = self.decrement_by_one
         return state
 
-    def set_state(self, state: dict[str:Any]) -> None:
+    def set_state(self, state: dict[str, Any]) -> None:
         """Sets the loop counter state to one given in dict."""
         self.name = state["name"]
         self.value = state["value"]
@@ -177,12 +182,10 @@ class LC(Module):
         self.read_from_uADR = False
         self.decrement_by_one = False
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """Tries to save the modules state to a given file."""
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("value: " + hex(self.value)[2:] + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\n"
+        content += "value: " + hex(self.value)[2:] + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def load_from_str(self, state_string):
         string_pair = state_string.split(": ")
@@ -203,9 +206,3 @@ class LC(Module):
             "\n read from bus: ",
             self.read_from_bus,
         )
-
-    def get_input_signals(self) -> list[Signal]:
-        return [self.mM_control_s, self.bus_input_s, self.mM_uADR_s]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [self.l_flag_s]
diff --git a/src/simudator/processor/mia/modules/mia_bus_connect.py b/src/simudator/processor/mia/modules/mia_bus_connect.py
index caf7fe303c30b5b8c9ed5a71b5281882d4303036..e5607b1f82509d93fba475bce0ebcc7b68bd3729 100644
--- a/src/simudator/processor/mia/modules/mia_bus_connect.py
+++ b/src/simudator/processor/mia/modules/mia_bus_connect.py
@@ -9,6 +9,11 @@ class MiaBusConnector:
     Has logic for controlling when to read and write to bus.
     """
 
+    # Python does not allow multiple inherintence if more than one of the 
+    # parent classes uses __slots__. Since the modules that inherit from MiaBusConnector
+    # also inherits from other classes we leave this class __slots__ empty.
+    __slots__ = ()
+
     def __init__(self, bus_control: Signal, bus_id: int = 0) -> None:
         # init the name
         self.bus_id = bus_id
diff --git a/src/simudator/processor/mia/modules/mia_grx.py b/src/simudator/processor/mia/modules/mia_grx.py
index 1c5a33ef42b3d995c065aeeb31e107b4039cf8bb..27d40fbe8a6a6ef760e2bebb458d3cdd8e38197e 100644
--- a/src/simudator/processor/mia/modules/mia_grx.py
+++ b/src/simudator/processor/mia/modules/mia_grx.py
@@ -14,6 +14,18 @@ class GRX(Module, MiaBusConnector):
     registers should be indexed by the GRx bits or the M bits.
     """
 
+    # Python does not allow multiple inherintence if more than one of the 
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = (
+        "grx_control",
+        "m_control",
+        "s_control",
+        "bus_id",
+        "registers",
+        "bus_control_s",
+    )
+
     def __init__(
         self,
         to_bus: Signal,
@@ -25,31 +37,23 @@ class GRX(Module, MiaBusConnector):
         bus_id: int,
         name="GRx",
         registers=[0 for _ in range(4)],
-        bit_length=16,
     ) -> None:
         # Set connection to/from bus and bus_id
         MiaBusConnector.__init__(self, bus_control, bus_id)
 
-        # Set the name
-        Module.__init__(self, name)
-
         # Other signals
-        self.to_bus = to_bus
-        self.from_bus = from_bus
-        self.m_control_s = m_control
-        self.s_control_s = s_control
-        self.grx_control_s = grx_control
-
-        # Set self as destination of its signals
-        self.m_control_s.add_destination(self)
-        self.s_control_s.add_destination(self)
-        self.grx_control_s.add_destination(self)
-
-        # set the regusters
-        self.registers = registers
+        signals = {
+            "in_input": from_bus,
+            "in_control_grx": grx_control,
+            "in_control_s": s_control,
+            "in_control_m": m_control,
+            "out_content": to_bus,
+        }
+        # Init module superclass
+        Module.__init__(self, signals, name)
 
-        # set the bit_length
-        self.bit_length = bit_length
+        # set the registers
+        self.registers = registers
 
     def update_register(self):
         """
@@ -58,17 +62,17 @@ class GRX(Module, MiaBusConnector):
         the GRx bits if s == 0 or M bits if s == 1.
         """
         if self.read_from_bus():
-            s_control_value = self.s_control_s.get_value()
+            s_control_value = self.signals["in_control_s"].get_value()
 
             grx_index = 0
             if s_control_value == 0b0:
-                grx_index = self.grx_control_s.get_value()
+                grx_index = self.signals["in_control_grx"].get_value()
             elif s_control_value == 0b1:
-                grx_index = self.m_control_s.get_value()
+                grx_index = self.signals["in_control_m"].get_value()
             else:
                 raise ValueError("The value of the s signal" "must be 0b0 or 0b1 !!!")
 
-            self.registers[grx_index] = self.from_bus.get_value()
+            self.registers[grx_index] = self.signals["in_input"].get_value()
 
     def output_register(self):
         """
@@ -77,19 +81,19 @@ class GRX(Module, MiaBusConnector):
         the GRx bits if s == 0 or M bits if s == 1.
         """
         if self.write_to_bus():
-            s_control_value = self.s_control_s.get_value()
+            s_control_value = self.signals["in_control_s"].get_value()
 
             grx_index = 0
             if s_control_value == 0b0:
-                grx_index = self.grx_control_s.get_value()
+                grx_index = self.signals["in_control_grx"].get_value()
             elif s_control_value == 0b1:
-                grx_index = self.m_control_s.get_value()
+                grx_index = self.signals["in_control_m"].get_value()
             else:
                 raise ValueError("The value of the s signal" "must be 0b0 or 0b1 !!!")
 
-            self.to_bus.update_value(self.registers[grx_index])
+            self.signals["out_content"].update_value(self.registers[grx_index])
         else:
-            self.to_bus.update_value(None)
+            self.signals["out_content"].update_value(None)
 
     def update_logic(self) -> None:
         self.output_register()
@@ -101,7 +105,6 @@ class GRX(Module, MiaBusConnector):
         """
         state = {
             "name": self.name,
-            "bit_length": self.bit_length,
             "registers": self.registers[:],
         }
         return state
@@ -118,8 +121,6 @@ class GRX(Module, MiaBusConnector):
         Sets the grx state to one given in dict.
         """
         self.name = state["name"]
-        if "bit_length" in state:
-            self.bit_length = state["bit_length"]
         self.registers = state["registers"]
 
     def reset(self) -> None:
@@ -129,20 +130,15 @@ class GRX(Module, MiaBusConnector):
         for i in range(len(self.registers)):
             self.registers[i] = 0
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
-        Returns true on success and false if something went wrong.
-        """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\n"
         for index, value in enumerate(self.registers):
-            file.write(hex(index)[2:] + ": ")
-            file.write(hex(value)[2:])
-            file.write("\n")
+            content += str(hex(index)[2:]) + ": "
+            content += str(hex(value)[2:])
+            content += "\n"
 
-        file.write("\n")
-        file.close()
+        content += "\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def load_from_str(self, state_string) -> None:
         """
@@ -165,8 +161,6 @@ class GRX(Module, MiaBusConnector):
             "\n -----",
             "\n bus_id: ",
             self.bus_id,
-            "\n bit_length: ",
-            self.bit_length,
             "\n GR0: ",
             hex(self.registers[0]),
             "\n GR1: ",
@@ -176,15 +170,3 @@ class GRX(Module, MiaBusConnector):
             "\n GR3: ",
             hex(self.registers[3]),
         )
-
-    def get_input_signals(self) -> list[Signal]:
-        return [
-            self.bus_control_s,
-            self.from_bus,
-            self.m_control_s,
-            self.s_control_s,
-            self.grx_control_s,
-        ]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [self.to_bus]
diff --git a/src/simudator/processor/mia/modules/mia_memory.py b/src/simudator/processor/mia/modules/mia_memory.py
index 9aab5263db44b74c37df12640876bf42481b22f3..4b2ca42c137f718c4260e5ab9064f2a5a8b2d573 100644
--- a/src/simudator/processor/mia/modules/mia_memory.py
+++ b/src/simudator/processor/mia/modules/mia_memory.py
@@ -11,16 +11,19 @@ class MiaMemory(MiaBusConnector, Memory):
     to/from the mia bus.
     """
 
+    # Python does not allow multiple inherintence if more than one of the
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = ("label_adress_mapping", "bus_id", "bus_control_s")
+
     def __init__(
         self,
         input_signal: Signal,
         output_signal: Signal,
-        adress_signal: Signal,
+        address_signal: Signal,
         bus_control_s: Signal,
         size: int = 1,
         bus_id: int = 0,
-        value_padding: int = 2,
-        adress_padding: int = 2,
         name: str = "PM",
     ) -> None:
         MiaBusConnector.__init__(self, bus_control_s, bus_id)
@@ -29,49 +32,50 @@ class MiaMemory(MiaBusConnector, Memory):
             input_signal,
             output_signal,
             Signal(None),
-            adress_signal,
+            address_signal,
             size,
             name=name,
-            value_padding=value_padding,
-            adress_padding=adress_padding,
         )
         self.bus_control_s.add_destination(self)
 
-        # TODO: only one of these are needed
         self.label_adress_mapping = {}
 
     def update_register(self) -> None:
         """
-        Updates the value of the current adress.
+        Updates the value of the current address.
         """
         if self.read_from_bus():
-            self.memory[self.current_adress] = self.input_s.get_value()
+            self._memory[self._current_address] = self.signals["in_input"].get_value()
 
     def output_register(self) -> None:
         """
-        Outputs the value of the current adress.
+        Outputs the value of the current address.
         """
         if self.write_to_bus():
-            self.output_s.update_value(self.memory[self.current_adress])
+            self.signals["out_content"].update_value(
+                self._memory[self._current_address]
+            )
         else:
-            self.output_s.update_value(None)
+            self.signals["out_content"].update_value(None)
 
     def update_logic(self) -> None:
         """
-        Outputs the value of the current adress unless current adress
+        Outputs the value of the current address unless current address
         is None
         """
-        adress_value = self.adress_s.get_value()
-        if adress_value is not None:
-            self.current_adress = adress_value
+        address_value = self.signals["in_address"].get_value()
+        if address_value is not None:
+            self._current_address = address_value
             if self.write_to_bus():
-                self.output_s.update_value(self.memory[self.current_adress])
+                self.signals["out_content"].update_value(
+                    self._memory[self._current_address]
+                )
             else:
-                self.output_s.update_value(None)
+                self.signals["out_content"].update_value(None)
 
     def reset(self) -> None:
-        size = len(self.memory)
-        self.memory = [0 for i in range(size)]
+        size = len(self._memory)
+        self._memory = [0 for _ in range(size)]
 
     def load_from_str(self, state_string) -> None:
         """
@@ -89,7 +93,7 @@ class MiaMemory(MiaBusConnector, Memory):
                 # Last character of the adress is a semicolon
                 adress = int(line_data[0][:-1], 16)
 
-                self.memory[adress] = value
+                self._memory[adress] = value
 
                 # There is an asm instruction label
                 if len(line_data) == 3:
@@ -103,13 +107,3 @@ class MiaMemory(MiaBusConnector, Memory):
             return self.label_adress_mapping[adress]
 
         return ""
-
-    def get_input_signals(self) -> list[Signal]:
-        return [
-            self.bus_control_s,
-            self.input_s,
-            self.adress_s,
-        ]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [self.output_s]
diff --git a/src/simudator/processor/mia/modules/micro_memory.py b/src/simudator/processor/mia/modules/micro_memory.py
index 35e2cd6f9a6622a9cf0e6d46140f4b02f6dfc9b0..5995909ed6befcdbe0f8e821d4b519216bee40e8 100644
--- a/src/simudator/processor/mia/modules/micro_memory.py
+++ b/src/simudator/processor/mia/modules/micro_memory.py
@@ -17,13 +17,29 @@ class MicroMemory(Module):
     cycle.
     """
 
+    __slots__ = (
+        "curr_instr",
+        "z_flag_val",
+        "n_flag_val",
+        "c_flag_val",
+        "o_flag_val",
+        "l_flag_val",
+        "memory",
+        "halt",
+        "adress_padding",
+        "value_padding",
+    )
+
+    MEMORY_ADDRESS_PADDING = 2
+    MEMORY_VALUE_PADDING = 7
+
     def __init__(
         self,
         alu: Signal,
         bus_control: Signal,
         bus_constant: Signal,
-        s: Signal,
-        p: Signal,
+        grx_control: Signal,
+        pc_control: Signal,
         lc_control: Signal,
         lc_uadr: Signal,
         upc: Signal,
@@ -48,9 +64,9 @@ class MicroMemory(Module):
             from the bus.
         bus_constant : Signal
             Signal for sending uADR instruction field to bus.
-        s : Signal
-            Control signal for [XXXXXXXXXX]
-        p : Signal
+        grx_control : Signal
+            Control signal for GRx.
+        pc_control : Signal
             Control signal for program counter PC.
         lc_control : Signal
             Control signal for loop counter LC.
@@ -81,36 +97,25 @@ class MicroMemory(Module):
             None
         """
 
-        super().__init__(name)
-
-        # Input signals
-        self.upc_s = upc
-        self.z_flag_s = z_flag
-        self.n_flag_s = n_flag
-        self.c_flag_s = c_flag
-        self.o_flag_s = o_flag
-        self.l_flag_s = l_flag
-
-        # Register self as destination for input signals
-        self.upc_s.add_destination(self)
-        self.z_flag_s.add_destination(self)
-        self.n_flag_s.add_destination(self)
-        self.c_flag_s.add_destination(self)
-        self.o_flag_s.add_destination(self)
-        self.l_flag_s.add_destination(self)
-
-        # Output control signals
-        self.alu_s = alu
-        self.bus_control_s = bus_control
-        self.s_s = s
-        self.p_s = p
-        self.lc_control_s = lc_control
-        self.upc_control_s = upc_control
-
-        # Output data signals
-        self.bus_constant_s = bus_constant
-        self.upc_uadr_s = upc_uadr
-        self.lc_uadr_s = lc_uadr
+        signals = {
+            "in_upc": upc,
+            "in_flag_z": z_flag,
+            "in_flag_n": n_flag,
+            "in_flag_c": c_flag,
+            "in_flag_o": o_flag,
+            "in_flag_l": l_flag,
+            "out_alu": alu,
+            "out_grx": grx_control,
+            "out_pc": pc_control,
+            "out_bus": bus_constant,
+            "out_upc": upc_uadr,
+            "out_lc": lc_uadr,
+            "out_control_bus": bus_control,
+            "out_control_lc": lc_control,
+            "out_control_upc": upc_control,
+        }
+
+        super().__init__(signals, name)
 
         # Internal variables
         self.curr_instr = 0
@@ -119,7 +124,7 @@ class MicroMemory(Module):
         self.c_flag_val = 0
         self.o_flag_val = 0
         self.l_flag_val = 0
-        self.memory = [0 for i in range(128)]
+        self.memory = [0 for _ in range(128)]
         self.halt = False  # Used for signalling a HALT
 
         # paddings to ensure saving to file has the correct format
@@ -134,16 +139,16 @@ class MicroMemory(Module):
         micro controller sets control signals to other modules in accordance
         with the micro instruction pointed to.
         """
-        instruction = self.upc_s.get_value()
+        instruction = self.signals["in_upc"].get_value()
         if instruction is None:
             return
 
         self.curr_instr = instruction
-        self.z_flag_val = self.z_flag_s.get_value()
-        self.n_flag_val = self.n_flag_s.get_value()
-        self.c_flag_val = self.c_flag_s.get_value()
-        self.o_flag_val = self.o_flag_s.get_value()
-        self.l_flag_val = self.l_flag_s.get_value()
+        self.z_flag_val = self.signals["in_flag_z"].get_value()
+        self.n_flag_val = self.signals["in_flag_n"].get_value()
+        self.c_flag_val = self.signals["in_flag_c"].get_value()
+        self.o_flag_val = self.signals["in_flag_o"].get_value()
+        self.l_flag_val = self.signals["in_flag_l"].get_value()
 
         # Extract the different fields of the micro instruction
         instr = self.memory[self.curr_instr]
@@ -163,16 +168,16 @@ class MicroMemory(Module):
             # by the ALU
             # Increase uPC by 1
             instr_constant = instr & 0b1111111111111111
-            self.alu_s.update_value(alu_field)
-            self.bus_control_s.update_value((0b000, 0b000))
-            self.bus_constant_s.update_value(instr_constant)
-            self.upc_control_s.update_value(0)
+            self.signals["out_alu"].update_value(alu_field)
+            self.signals["out_control_bus"].update_value((0b000, 0b000))
+            self.signals["out_bus"].update_value(instr_constant)
+            self.signals["out_control_upc"].update_value(0)
 
             # Make sure other control signals are set to 0 so that there
             # is no unexpected behaviour from leftover signal values
-            self.lc_control_s.update_value(0)
-            self.p_s.update_value(0)
-            self.s_s.update_value(0)
+            self.signals["out_control_lc"].update_value(0)
+            self.signals["out_pc"].update_value(0)
+            self.signals["out_grx"].update_value(0)
             return
 
         if (0b0001 <= alu_field <= 0b0010) or (0b0100 <= alu_field <= 0b1000):
@@ -184,43 +189,43 @@ class MicroMemory(Module):
         # Handle LC according to the LC field
         match lc_field:
             case 0b00:
-                self.lc_control_s.update_value(0b00)
+                self.signals["out_control_lc"].update_value(0b00)
             case 0b01:
-                self.lc_control_s.update_value(0b01)
+                self.signals["out_control_lc"].update_value(0b01)
             case 0b10:
-                self.lc_control_s.update_value(0b10)
+                self.signals["out_control_lc"].update_value(0b10)
                 # fb_field = 0b1001 # Ignores actual FB field
             case 0b11:
-                self.lc_control_s.update_value(0b11)
-                self.lc_uadr_s.update_value(uadr_field & 0b01111111)
+                self.signals["out_control_lc"].update_value(0b11)
+                self.signals["out_lc"].update_value(uadr_field & 0b01111111)
 
         # Update control signals that require no special handling
-        self.alu_s.update_value(alu_field)
-        self.bus_constant_s.update_value(None)
-        self.bus_control_s.update_value((tb_field, fb_field))
-        self.s_s.update_value(s_field)
-        self.p_s.update_value(p_field)
+        self.signals["out_alu"].update_value(alu_field)
+        self.signals["out_bus"].update_value(None)
+        self.signals["out_control_bus"].update_value((tb_field, fb_field))
+        self.signals["out_grx"].update_value(s_field)
+        self.signals["out_pc"].update_value(p_field)
 
         # Handle uPC according to the SEQ field
         match seq_field:
             case 0b0000:
-                self.upc_control_s.update_value(0b000)
+                self.signals["out_control_upc"].update_value(0b000)
             case 0b0001:
-                self.upc_control_s.update_value(0b001)
+                self.signals["out_control_upc"].update_value(0b001)
             case 0b0010:
-                self.upc_control_s.update_value(0b010)
+                self.signals["out_control_upc"].update_value(0b010)
             case 0b0011:
-                self.upc_control_s.update_value(0b011)
+                self.signals["out_control_upc"].update_value(0b011)
             case 0b0100:
                 self._conditional_jump(self.z_flag_val, 0, uadr_field)
             case 0b0101:
-                self.upc_control_s.update_value(0b100)
-                self.upc_uadr_s.update_value(uadr_field)
+                self.signals["out_control_upc"].update_value(0b100)
+                self.signals["out_upc"].update_value(uadr_field)
             case 0b0110:
-                self.upc_control_s.update_value(0b110)
-                self.upc_uadr_s.update_value(uadr_field)
+                self.signals["out_control_upc"].update_value(0b110)
+                self.signals["out_upc"].update_value(uadr_field)
             case 0b0111:
-                self.upc_control_s.update_value(0b101)
+                self.signals["out_control_upc"].update_value(0b101)
             case 0b1000:
                 self._conditional_jump(self.z_flag_val, 1, uadr_field)
             case 0b1001:
@@ -237,7 +242,7 @@ class MicroMemory(Module):
                 self._conditional_jump(self.o_flag_val, 0, uadr_field)
             case 0b1111:
                 # Halt is handled by update_register
-                self.upc_control_s.update_value(0b011)
+                self.signals["out_control_upc"].update_value(0b011)
 
     def update_register(self) -> None:
         """
@@ -264,10 +269,10 @@ class MicroMemory(Module):
         true then uPC is increased by 1.
         """
         if flag == cond_value:
-            self.upc_control_s.update_value(0b100)
-            self.upc_uadr_s.update_value(uadr)
+            self.signals["out_control_upc"].update_value(0b100)
+            self.signals["out_upc"].update_value(uadr)
             return
-        self.upc_control_s.update_value(0b000)
+        self.signals["out_control_upc"].update_value(0b000)
 
     def print_module(self) -> None:
         """Print the contents of the micro memory.
@@ -284,6 +289,7 @@ class MicroMemory(Module):
         state = super().get_state()
         state["memory"] = self.memory[:]
         state["halt"] = self.halt
+        state["curr_instr"] = self.curr_instr
         return state
 
     def get_gui_state(self) -> dict[str, Any]:
@@ -300,31 +306,8 @@ class MicroMemory(Module):
     def output_register(self):
         pass
 
-    def get_input_signals(self) -> [Signal]:
-        return [
-            self.upc_s,
-            self.z_flag_s,
-            self.n_flag_s,
-            self.c_flag_s,
-            self.o_flag_s,
-            self.l_flag_s,
-        ]
-
     def get_uPC_signal(self):
-        return self.upc_s
-
-    def get_output_signals(self) -> [Signal]:
-        return [
-            self.alu_s,
-            self.bus_control_s,
-            self.s_s,
-            self.p_s,
-            self.lc_control_s,
-            self.upc_control_s,
-            self.bus_constant_s,
-            self.upc_uadr_s,
-            self.lc_uadr_s,
-        ]
+        return self.signals["in_upc"]
 
     def reset(self) -> None:
         self.curr_instr = 0
@@ -363,19 +346,17 @@ class MicroMemory(Module):
                 value = int(line_data[1], 16)
                 self.memory[adress] = value
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """Tries to save the modules state to a given file."""
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\n"
         for index, value in enumerate(self.memory):
-            # Write the adress in hex and add ': ' to the end
-            file.write(hex(index)[2:].rjust(self.adress_padding, "0") + ": ")
-            # Write the value in hex
-            file.write(hex(value)[2:].rjust(self.value_padding, "0"))
-            file.write("\n")
+            content += (
+                str(hex(index)[2:].rjust(self.MEMORY_ADDRESS_PADDING, "0")) + ": "
+            )
+            content += str(hex(value)[2:].rjust(self.MEMORY_VALUE_PADDING, "0"))
+            content += "\n"
 
-        file.write("\n")
-        file.close()
+        content += "\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def get_largest_mem_adr(self) -> int:
         """Helper function for pretty_print that returns the length of
@@ -383,11 +364,14 @@ class MicroMemory(Module):
         """
         return len(str(len(self.memory)))
 
-    def get_longest_line_len(self, ignore_keys=[]) -> int:
+    def get_longest_line_len(self, ignore_keys=None) -> int:
         """Helper function for pretty_print that returns the length of
         the longest value in the memory to print for a module.
         """
 
+        if ignore_keys is None:
+            ignore_keys = []
+
         longest_memory_line = 0
 
         for value in self.memory:
diff --git a/src/simudator/processor/mia/modules/micro_pc.py b/src/simudator/processor/mia/modules/micro_pc.py
index 087f785d3dd930a648fb6b69e920e2680642d7e2..efaaeb27dc330f321d87efc52e5c299380f3d1d2 100644
--- a/src/simudator/processor/mia/modules/micro_pc.py
+++ b/src/simudator/processor/mia/modules/micro_pc.py
@@ -11,6 +11,8 @@ class MicroPC(Module):
     to read from or write to.
     """
 
+    __slots__ = ("value", "bit_length")
+
     def __init__(
         self,
         control_signal: Signal,
@@ -20,34 +22,26 @@ class MicroPC(Module):
         from_supc: Signal,
         to_um: Signal,
         from_um: Signal,
-        bit_length: int = 8,
         name: str = "uPC",
     ) -> None:
-        super().__init__(name)
 
         self.value = 0
-        self.bit_length = bit_length
-
-        # Input signals
-        self.control_signal = control_signal
-        self.from_k1 = from_k1
-        self.from_k2 = from_k2
-        self.from_supc = from_supc
-        self.from_um = from_um
-
-        # Output signals
-        self.to_supc = to_supc
-        self.to_um = to_um
-
-        # Register as destination of the input signals
-        control_signal.add_destination(self)
-        from_k1.add_destination(self)
-        from_k2.add_destination(self)
-        from_supc.add_destination(self)
-        from_um.add_destination(self)
+
+        # Signals
+        signals = {
+            "in_control": control_signal,
+            "in_k1": from_k1,
+            "in_k2": from_k2,
+            "in_supc": from_supc,
+            "in_um": from_um,
+            "out_supc": to_supc,
+            "out_um": to_um,
+        }
+
+        super().__init__(signals, name)
 
     def update_register(self) -> None:
-        match self.control_signal.get_value():
+        match self.signals["in_control"].get_value():
             case 0b000:
                 self.value += 1
 
@@ -56,26 +50,25 @@ class MicroPC(Module):
                     self.value = 0
 
             case 0b001:
-                self.value = self.from_k1.get_value()
+                self.value = self.signals["in_k1"].get_value()
             case 0b010:
-                self.value = self.from_k2.get_value()
+                self.value = self.signals["in_k2"].get_value()
             case 0b011:
                 self.value = 0
             case 0b100:
-                self.value = self.from_um.get_value()
+                self.value = self.signals["in_um"].get_value()
             case 0b101:
-                self.value = self.from_supc.get_value()
+                self.value = self.signals["in_supc"].get_value()
             case 0b110:
-                self.to_supc.update_value(self.value + 1)
-                self.value = self.from_um.get_value()
+                self.signals["out_supc"].update_value(self.value + 1)
+                self.value = self.signals["in_um"].get_value()
 
     def output_register(self) -> None:
-        self.to_um.update_value(self.value)
+        self.signals["out_um"].update_value(self.value)
 
     def get_state(self) -> dict:
         state = super().get_state()
         state["value"] = self.value
-        state["bit_length"] = self.bit_length
         return state
 
     def get_gui_state(self) -> dict:
@@ -88,8 +81,6 @@ class MicroPC(Module):
     def set_state(self, state: dict) -> None:
         super().set_state(state)
         self.value = state["value"]
-        if "bit_length" in state:
-            self.bit_length = state["bit_length"]
 
     def reset(self) -> None:
         """
@@ -103,14 +94,10 @@ class MicroPC(Module):
         """
         pass
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """
-        Tries to save the modules state to a given file.
-        """
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("value: " + hex(self.value)[2:] + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\n"
+        content += "value: " + hex(self.value)[2:] + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def load_from_str(self, state_string):
         string_pair = state_string.split(": ")
@@ -119,18 +106,3 @@ class MicroPC(Module):
 
     def print_module(self) -> None:
         print("", self.name, "\n -----", "\n value: ", hex(self.value))
-
-    def get_input_signals(self) -> list[Signal]:
-        return [
-            self.control_signal,
-            self.from_k1,
-            self.from_k2,
-            self.from_supc,
-            self.from_um,
-        ]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [
-            self.to_supc,
-            self.to_um,
-        ]
diff --git a/src/simudator/processor/mia/modules/pc.py b/src/simudator/processor/mia/modules/pc.py
index 4fe148bf6555c4969938fc40c73f6d2bac2e0d0a..c80424982e48899236a49592e303cabdcbfcba29 100644
--- a/src/simudator/processor/mia/modules/pc.py
+++ b/src/simudator/processor/mia/modules/pc.py
@@ -17,6 +17,11 @@ class PC(Module, MiaBusConnector):
     value on the bus. If both are true it does nothing.
     """
 
+    # Python does not allow multiple inherintence if more than one of the 
+    # parent classes uses __slots__. Thus we also includes the __slots__
+    # from MiaBusConnector.
+    __slots__ = ("bus_id", "bus_control_s", "value", "increase_by_one")
+
     def __init__(
         self,
         p: Signal,
@@ -28,18 +33,13 @@ class PC(Module, MiaBusConnector):
         value=0,
     ) -> None:
         MiaBusConnector.__init__(self, bus_control, bus_id)
-        Module.__init__(self, name)
-
-        # control signals
-        self.p = p
-
-        # input/output signals
-        self.bus_input_s = bus_input
-        self.bus_output_s = bus_output
 
-        # set program counter as destination of all signlas
-        self.p.add_destination(self)
-        self.bus_input_s.add_destination(self)
+        signals = {
+            "in_control": p,
+            "in_input": bus_input,
+            "out_content": bus_output,
+        }
+        Module.__init__(self, signals, name)
 
         # internal state
         self.value = value
@@ -50,25 +50,25 @@ class PC(Module, MiaBusConnector):
     def output_register(self) -> None:
         """Output the value of the program counter to the bus."""
         if self.write_to_bus():
-            self.bus_output_s.update_value(self.value)
+            self.signals["out_content"].update_value(self.value)
         else:
-            self.bus_output_s.update_value(None)
+            self.signals["out_content"].update_value(None)
 
     def update_register(self) -> None:
-        """Updates the value of the PC according to the signals sent by the micro memory. 
+        """Updates the value of the PC according to the signals sent by the micro memory.
 
         If the program counter recives 'True' from both signals it
         should do nothing and wait for the next cycle.
         """
 
-        self.increase_by_one = self.p.get_value()
+        self.increase_by_one = self.signals["in_control"].get_value()
 
         if self.increase_by_one and self.read_from_bus():
             return
 
         if self.read_from_bus():
             # Read the 8 lowest bits from the bus
-            self.value = self.bus_input_s.get_value() & 0xFF
+            self.value = self.signals["in_input"].get_value() & 0xFF
 
         if self.increase_by_one:
             self.value += 1
@@ -76,7 +76,7 @@ class PC(Module, MiaBusConnector):
     def update_logic(self) -> None:
         self.output_register()
 
-    def get_state(self) -> dict[str:Any]:
+    def get_state(self) -> dict[str, Any]:
         """Returns a dict of the program counter state.
 
         These states are changable via set_states.
@@ -100,7 +100,7 @@ class PC(Module, MiaBusConnector):
         }
         return state
 
-    def set_state(self, state: dict[str:Any]) -> None:
+    def set_state(self, state: dict[str, Any]) -> None:
         """Sets the program counter state to one given in dict."""
         self.name = state["name"]
         self.value = state["value"]
@@ -112,12 +112,10 @@ class PC(Module, MiaBusConnector):
         self.value = 0
         self.increase_by_one = False
 
-    def save_state_to_file(self, file_path: str) -> None:
-        """Tries to save the modules state to a given file."""
-        file = open(file_path, "a")
-        file.write(self.name + ":\n")
-        file.write("value: " + hex(self.value)[2:] + "\n\n")
-        file.close()
+    def save_state_to_file(self, file_path: str) -> bool:
+        content = self.name + ":\n"
+        content += "value: " + hex(self.value)[2:] + "\n\n"
+        return super()._helper_save_state_to_file(file_path, content)
 
     def load_from_str(self, state_string):
         string_pair = state_string.split(": ")
@@ -126,9 +124,3 @@ class PC(Module, MiaBusConnector):
 
     def print_module(self) -> None:
         print("", self.name, "\n -----", "\n value: ", hex(self.value))
-
-    def get_input_signals(self) -> list[Signal]:
-        return [self.bus_input_s, self.p]
-
-    def get_output_signals(self) -> list[Signal]:
-        return [self.bus_output_s]
diff --git a/src/simudator/processor/simple/simple.py b/src/simudator/processor/simple/simple.py
index 7b66364741fab4904a2eb9d0cf6c41ba42c76587..2d01160fd065a187a574c6873ab474e67db04fd6 100644
--- a/src/simudator/processor/simple/simple.py
+++ b/src/simudator/processor/simple/simple.py
@@ -15,6 +15,8 @@ class SIMPLE_CPU(Processor):
     This cpu is made to test core modules and module graphics items for these modules.
     """
 
+    __slots__ = ()
+
     def __init__(self) -> None:
         super().__init__()
         # Creating all signals
diff --git a/test/test_core/test_breakpoint.py b/test/test_core/test_breakpoint.py
index 27986d7ff411f8cd8351b7c0a16238e9c90fe5bb..01f115d012ba1a4cf042244d71761be28dd28673 100644
--- a/test/test_core/test_breakpoint.py
+++ b/test/test_core/test_breakpoint.py
@@ -1,6 +1,6 @@
-from simudator.core.breakpoint_state import StateBreakpoint
-from simudator.core.breakpoint_memory import MemoryBreakpoint
 from simudator.core.breakpoint_lambda import LambdaBreakpoint
+from simudator.core.breakpoint_memory import MemoryBreakpoint
+from simudator.core.breakpoint_state import StateBreakpoint
 from simudator.core.module import Module
 from simudator.core.modules.memory import Memory
 from simudator.core.processor import Processor
@@ -9,7 +9,7 @@ from simudator.core.signal import Signal
 
 class DummyModule(Module):
     def __init__(self, name: str = "") -> None:
-        super().__init__(name)
+        super().__init__({}, name)
         self.value = 0
 
     def get_state(self) -> dict:
@@ -33,7 +33,7 @@ def test_state_breakpoint():
     assert bp.is_break()
 
     # Module state is no longer set to break value => should no longer break
-    # Also checks that a value of a different type than that of the break value 
+    # Also checks that a value of a different type than that of the break value
     # does not cause issues
     m.value = "-"
     assert not bp.is_break()
@@ -86,13 +86,13 @@ def test_memory_breakpoint():
 
 def test_lambda_breakpoint_no_args():
     """
-    Test the functionality of LambdaBreakpoint when supplied with a function 
+    Test the functionality of LambdaBreakpoint when supplied with a function
     that takes no arguments.
     """
     # Test functions with no arguments
     # Function returns False => should not break
     bp = LambdaBreakpoint(lambda: False)
-    assert not bp.is_break() 
+    assert not bp.is_break()
 
     # Function returns True => should break
     bp = LambdaBreakpoint(lambda: True)
@@ -101,9 +101,10 @@ def test_lambda_breakpoint_no_args():
 
 def test_lambda_breakpoint_args():
     """
-    Test the functionality of LambdaBreakpoint when supplied with a function 
+    Test the functionality of LambdaBreakpoint when supplied with a function
     that takes arguments.
     """
+
     # Test functions with arguments
     # Arguments of same type
     def func_1(num, thres):
@@ -120,18 +121,21 @@ def test_lambda_breakpoint_args():
     # Arguments of different types
     def str_float_comp(string, num):
         return float(string) == num
+
     kwargs = {"string": "2.5", "num": 2.5}
     bp = LambdaBreakpoint(str_float_comp, **kwargs)
     assert bp.is_break() == str_float_comp(**kwargs)
 
-def test_lambda_breakpoint_ref_args():  
+
+def test_lambda_breakpoint_ref_args():
     """
-    Test the functionality of LambdaBreakpoint when supplied with a function 
+    Test the functionality of LambdaBreakpoint when supplied with a function
     that takes references as arguments.
     """
     # Explicit comparison of references
     l1 = []
     l2 = []
+
     def func_list_ref_comp(l):
         return l is l2
 
@@ -145,13 +149,14 @@ def test_lambda_breakpoint_ref_args():
 
     # Test that changes to a reference are reflected in the breakpoint
     l = [1, 2, "a"]
+
     def func_list_comp(l):
         return l == [1, 2, "a", "b"]
 
     kwargs = {"l": l}
     bp = LambdaBreakpoint(func_list_comp, **kwargs)
 
-    # The supplied list is not equal to the internal list of the 
+    # The supplied list is not equal to the internal list of the
     # supplied function => shoud not break
     assert bp.is_break() == func_list_comp(l)
 
@@ -160,7 +165,6 @@ def test_lambda_breakpoint_ref_args():
     l.append("b")
     assert bp.is_break() == func_list_comp(l)
 
-
     # Test with reference to a more advanced data structure, e.g. a class
     class Dummy:
         def __init__(self):
@@ -176,8 +180,7 @@ def test_lambda_breakpoint_ref_args():
     assert bp.is_break() == func_dummy_val(dummy, value)
 
     # The supplied argument should be a reference, so changes to `dummy`
-    # should affect the break point 
-    dummy.value = value 
+    # should affect the break point
+    dummy.value = value
     assert bp.is_break() == func_dummy_val(dummy, value)
     assert bp.is_break() == (not func_dummy_val(dummy, not value))
-
diff --git a/test/test_core/test_demux.py b/test/test_core/test_demux.py
index acf92510f17faaf59c475e6426384214ac68eec8..e4c59c3d04f7dc5f73d11c131cfabf87b5301120 100644
--- a/test/test_core/test_demux.py
+++ b/test/test_core/test_demux.py
@@ -38,7 +38,6 @@ def test_get_state():
     assert state["name"] == "Demux"
     assert state["value"] == 7
     assert state["bit_length"] == 5
-    assert state["mask"] == 31
 
 
 def test_set_state():
@@ -46,7 +45,6 @@ def test_set_state():
     state["name"] = "Demux"
     state["value"] = 6
     state["bit_length"] = 2
-    state["mask"] = 3
 
     cpu = Processor()
     from_demux = Signal(cpu)
@@ -57,10 +55,10 @@ def test_set_state():
 
     demux.set_state(state)
 
-    assert demux.name == "Demux"
-    assert demux.value == 6
-    assert demux.bit_length == 2
-    assert demux.mask == 3
+    state = demux.get_state()
+    assert state["name"] == "Demux"
+    assert state["value"] == 6
+    assert state["bit_length"] == 2
 
 
 def test_writing_to_output():
diff --git a/test/test_core/test_mux.py b/test/test_core/test_mux.py
index 7dab6d3630af58eef3411ca0f7763b6fb839bf09..fed767a99b0ebbcd6460ec126adbde0b224e6fa3 100644
--- a/test/test_core/test_mux.py
+++ b/test/test_core/test_mux.py
@@ -38,7 +38,6 @@ def test_get_state():
     assert state["name"] == "Mux"
     assert state["value"] == 7
     assert state["bit_length"] == 5
-    assert state["mask"] == 31
 
 
 def test_set_state():
@@ -58,9 +57,8 @@ def test_set_state():
     mux.set_state(state)
 
     assert mux.name == "Mux"
-    assert mux.value == 6
-    assert mux.bit_length == 2
-    assert mux.mask == 3
+    assert mux._value == 6
+    assert mux._bit_length == 2
 
 
 def test_writing_to_output():
diff --git a/test/test_core/test_register.py b/test/test_core/test_register.py
index ae6f51127da4fb86673387e86e9abb1b2b83d000..1d1d85589a2a2851b7e9cf9710282b2c4eca8180 100644
--- a/test/test_core/test_register.py
+++ b/test/test_core/test_register.py
@@ -26,7 +26,7 @@ def test_set_state():
     register.set_state(state)
 
     assert register.name == "Reg"
-    assert register.value == 14
+    assert register._value == 14
 
 
 def test_get_state():
@@ -51,4 +51,4 @@ def test_signal_propagation():
     input_s.update_value(10)
     cpu.do_tick()
 
-    assert register.value == 10
+    assert register._value == 10
diff --git a/test/test_mia/test_lc.py b/test/test_mia/test_lc.py
index aaa591c611a661a94302578b79dd0c2ba4400d0d..407979aead137cf1d3d965f5377442dbc7f822c6 100644
--- a/test/test_mia/test_lc.py
+++ b/test/test_mia/test_lc.py
@@ -84,7 +84,7 @@ def test_write_to_l_flag():
     # The loop counter will now change its value from 1 to 0 which
     # should set the L flag to 1 in the same cycle
     cpu.do_tick()
-    assert l_flag.value == 1
+    assert l_flag._value == 1
 
 
 def test_reset_l_flag():
@@ -110,7 +110,7 @@ def test_reset_l_flag():
     mM_uADR_s.update_value(10)
 
     cpu.do_tick()
-    assert l_flag.value == 0
+    assert l_flag._value == 0
 
 
 def test_lc_do_nothing():
diff --git a/test/test_mia/test_step.py b/test/test_mia/test_step.py
new file mode 100644
index 0000000000000000000000000000000000000000..e83de422418ab74ad1b6adce9b5bdba52f6e0423
--- /dev/null
+++ b/test/test_mia/test_step.py
@@ -0,0 +1,35 @@
+from simudator.core.processor import Processor
+from simudator.processor.mia.mia import MIA_CPU
+
+
+def test_increment_10():
+    cpu = MIA_CPU()
+    cpu.load_state_from_file("mia_uppg3.txt")
+
+    for itr in range(10000, 10):
+        for _ in range(itr):
+            cpu.do_tick()
+
+        for i in range(itr, -1):
+            cpu.load_cycle(i)
+
+
+def test_max_undo():
+    cpu = MIA_CPU()
+    cpu.load_state_from_file("mia_uppg3.txt")
+    max_undo = Processor.CYCLES_TO_SAVE
+
+    for _ in range(2 * max_undo):
+        cpu.do_tick()
+
+    cpu.load_cycle(max_undo)
+
+
+def test_do_50000_undo_one():
+    cpu = MIA_CPU()
+    cpu.load_state_from_file("mia_uppg3.txt")
+
+    for _ in range(50000):
+        cpu.do_tick()
+
+    cpu.load_cycle(50000 - 1)