Note
This project was completed as part of the Secure Software Engineering course during the Summer Term 2025 of our undergraduate studies, offered by Prof. Dr. Henning Meier at Coburg University. It was originally developed on our university’s GitLab instance and later partially migrated here, so some elements like issues or CI/CD pipelines may be missing.
Important
This project is licensed under the MIT License.
Here you can find also two explainer videos:
- Group Number: 5
- Project Name: cheap-16 | cheap-sixteen
- Project Team Members:
- Emil Feuerlein (GitHub Page)
- Felix Wagner (GitHub Page)
- Seyedmasih Tabaei (GitHub Page)
- Release Version: 1.0
- Project Logo:

| Feature | Note |
|---|---|
| Graphical user interface (GUI) | Created with egui. |
| Handcrafted parser | Implemented our own parser with error checking and reporting. |
| Own instruction set architecture | Defined our very own instruction set architecture (documented here) with all the trimmings. |
| Project size | Wrote more than 6000 lines of codes. |
| Extensive Git (Lab) usage | Extended the list of files to be ignored by Git and attributes related to the Git and used a pre-commit plugin. |
| Extensive testing | Written more than 300 unit tests and tested our program manually. |
| Own Docker image | Built our own Docker image for the sake of faster pipeline flow. |
| Logging and tracing | Used various methods to log and trace our executions. |
| Easy-to-comprehend code and linear code execution | Followed the principle of KISS, minimized branch divergence, avoided polymorphism as much as possible, and preferred linear code execution. |
| Extensive documentation | Composed doc strings, normal comments, architecture documentations and created supporting diagrams. |
| Conflict-free collaboration | Avoided merge conflicts by clearly separating responsibilities. |
| Structured work | Used conventional commits and semantic versioning. |
We implemented all instructions except CLEAR and SETPX since they needed a graphics library, and we decided to omit both for the sake of simplicity. We also omitted both undo/redo features because of missing capacities in our team.
In this report, you can also see a curated list of feedbacks that you gave us and their consideration states. Thank you so much for your support and valuable feedback.
The decision to implement an emulator in Rust is largely based on the advantages of the language in the area of memory security. Rust prevents typical errors such as use-after-free or invalid memory accesses at compile time. This ensures that emulator users cannot inadvertently or manipulative access critical memory areas and/or program data at runtime. At the same time, Rust's modern type system, modular structure and stable tool chain promote long-lasting, maintainable code - a decisive advantage for long-term projects that are intended to go beyond simple prototypes.
The program is primarily intended to provide students and teachers in an academic environment with the opportunity to develop a basic understanding of the structure and behavior of simple processor architectures. The deliberately reduced instruction set with only elementary arithmetic and logical operations is very easy to learn. Our GUI is ideal for teaching central concepts such as memory access, register work and instruction execution without being overwhelmed by technical complexity. The emulator thus creates a controlled, transparent environment for didactically motivated experiments, tasks and teaching scenarios.
Here you find a list of similar projects:
| key | value |
|---|---|
| Name | Assembly source code compilation. |
| ID | 1 |
| Description | User should be able to compile an assembly source code. |
| Actor | User |
| Pre-conditions | Source code is present within the text editor. |
| Steps | Click the 'Build' button. |
| Post-conditions | Compilation status (aka. diagnostics) will be shown. User sees now a 'Flash' button. A *cheapx file will be saved. |
| key | value |
|---|---|
| Name | Assembly source file compilation. |
| ID | 2 |
| Description | User should be able to compile an assembly source file. A *cheapx file will be saved. |
| Actor | User |
| Pre-conditions | Source file is selected. |
| Steps | Click the 'Build' button. |
| Post-conditions | Compilation status (aka. diagnostics) will be shown. User sees now a 'Flash' button. |
| key | value |
|---|---|
| Name | Load compiled source code. |
| ID | 3 |
| Description | User clicks on 'Flash' button. By clicking the 'Flash' button the emulator loads the compiled program. |
| Actor | User |
| Pre-conditions | A source code was compiled successfully. |
| Steps | Click the 'Flash' button |
| Post-conditions | The user sees a output about the flashing status and new buttons : 'Execute' and 'Execute Stepwise' |
| key | value |
|---|---|
| Name | Execute |
| ID | 4 |
| Description | User clicks on 'Execute' button. By clicking the button the emulator loads the flashed file. |
| Actor | User |
| Pre-conditions | A source code was flashed successfully. |
| Steps | Click the 'Execute' button. |
| Post-conditions | The User sees the result saved in a register / memory / as a flag, based on command. |
| key | value |
|---|---|
| Name | Stepwise Execution. |
| ID | 5 |
| Description | User clicks on 'Execute Stepwise' button. |
| Actor | User |
| Pre-conditions | A source code was flashed successfully. |
| Steps | Click the 'Execute Stepwise' button. |
| Post-conditions | User sees new buttons: 'Next Step' 'Stop' 'Reset' 'Undo' |
| key | value |
|---|---|
| Name | Execute one line (= one instruction). |
| ID | 6 |
| Description | User clicks on 'Next step' button to execute one line. |
| Actor | User |
| Pre-conditions | A source code was flashed successfully, and the stepwise execution was started. |
| Steps | Click the 'Next step' button. |
| Post-conditions | The User sees the result saved in a register / memory / as a flag, based on command. |
| key | value |
|---|---|
| Name | Reset Emulator. |
| ID | 7 |
| Description | User clicks on 'reset' button. By clicking the button the emulator resets all values in register / memory / flags to random values |
| Actor | User |
| Pre-conditions | A source code was flashed successfully, and the stepwise execution was started. |
| Steps | Click the 'Reset' button. |
| Post-conditions | The User sees the result saved in a register / memory / as a flag, based on command. |
| key | value |
|---|---|
| Name | Undo line (=instruction). |
| ID | 8 |
| Description | User clicks on 'Undo' button. By clicking the button the UI shows results of the last operation |
| Actor | User |
| Pre-conditions | A source code was flashed successfully, and the stepwise execution was started. Additionally, at least one operation was executed |
| Steps | Click the 'Undo' button. |
| Post-conditions | The User sees the previous result saved in a register / memory / as a flag, based on command. |
| key | value |
|---|---|
| Name | Stop stepwise execution. |
| ID | 9 |
| Description | User clicks on 'Stop' button. By clicking the button the UI exits the execution mode. |
| Actor | User |
| Pre-conditions | A source code was flashed successfully, and the stepwise execution was started. |
| Steps | Click the 'Stop' button. |
| Post-conditions | The User sees the UI in editor mode with the buttons: 'Save File' 'Load File' 'Build' |
| key | value |
|---|---|
| Name | Load a file. |
| ID | 10 |
| Description | User clicks on 'Load File' button. By clicking the button the UI opens a dialogue with the file organization program of the OS. |
| Actor | User |
| Pre-conditions | Program was started. |
| Steps | Click the 'Load file' button. |
| Post-conditions | The User sees the loaded file in the editor area of the window. |
| key | value |
|---|---|
| Name | Save a file |
| ID | 11 |
| Description | User clicks on 'Save file' button. By clicking the button the UI may open a dialogue with the file organization program of the os. this depends on the scenario: If the file was loaded earlier no dialogue will be opened. |
| Actor | User |
| Pre-conditions | Program was started |
| Steps | Click the 'Save file' button. |
| Post-conditions | The User finds the saved file with ending : cheaps int his file system. |
| key | value |
|---|---|
| Name | Edit / create text |
| ID | 12 |
| Description | User has an area to write text here code into. |
| Actor | User |
| Pre-conditions | Program was started |
| Steps | Write into the text editor area. |
| Post-conditions | The User sees his written text. |
Seeing this diagram raises the question of why the action_panel was not included in the Panel trait.
This is due to the fact that action_panel requires a reference to the MyApp object in order to invoke its public methods and trigger the associated logic behind the buttons.
One might argue that the draw function in the trait could have been adapted to pass a reference to MyApp, and that panels not requiring it could simply ignore the reference. However, in accordance with the principle of least privilege, we deliberately chose not to expose such access to all panels and instead implemented the current design.
git clone <repo-link>
cd SSE_Projekt05/cheap-sixteen
cargo build
cargo runHere you can find also two supporting videos:
Grid:
| ... | ... |
|---|---|
| Code editor | Registers panel |
| Code editor | Flags panel |
| Code editor | Application logs panel |
it depends on the program state which buttons are accessible:
- Load file: Loads a file of arbitrary format
- Save file: Saves file of arbitrary format
- Build and flash : Compiles a file with *.cheaps ending to *cheapx in the same folder and loads the resulting *.cheapx binary into the emulator
- Execute: Runs all instructions on the emulator at once
- Execute Stepwise: Enters debug mode (here called step by step mode)
- Next Step: Execute next line
- Stop: Exit debug mode
- Reset: Reset debug mode (to start from the beginning)
- View: Load File | Save file | Build and Flash
- ViewAfterFlash: Execute | Execute stepwise |Back
- StepByStep: Stop | Next Step | Reset
The project provides various pieces of code example that can be found in the directory test_data.
In order to compile and running this code just click:
- Load file -> A dialogue will open to choose a file. Select the file path/to/test_data.
- Build and Flash
- Execute | Execute Stepwise
- Optional: If Execute Stepwise was selected continue clicking Next Step.
A summary of all instruction supported in the architecture can be found here.
Here you can see a curated list of points and feedbacks that we got either per e-mail or via direct communication and their consideration states:
| Feedback | Status | Notes |
|---|---|---|
| Missing use-case identifiers | Done | Added successfully. |
| Useless benchmarking stage | Done | Removed successfully. |
| Dead and deprecated code | Done | Cleaned-up successfully. |
| Duplicated code | (Partially) done | Deduplicated successfully. |
| Missing report | Done | Created and linked. |
| Missing code documentation | (Partially) done | Added a huge number of doc strings. |
| Missing user documentation | Done | Added to the report. |
| Dummy project name | Done | Renamed the project. |
| Low coverage | (partially) done | Increased the coverage successfully. |
| Shallow use-cases | (Partially) done | Added more context. |
We encountered following challenges or problems during this project:
- Huge project (notes from Masih: my bad)
- Linter issues
- Low code coverage
- Partially unsuccessfull team communication (notes from Masih: my bad)
If we had the chance to re-do the project, we would probably consider following points:
- More intensive team-internal communication
- Better management, project planning, and state documentation
- Earlier confrontation with and deeper comprehension of deployment server and pipeline because of their usefulness
- Smaller project scope
- Earlier integration of units
- As-you-go documentation
While researching GUI libraries for Rust, a helpful discussion on Reddit revealed that egui, iced, Tauri, and Slint are among the most popular libraries. However, many contributors in that thread criticized iced for complexity and performance issues, while Tauri and Slint received only a few comments. In contrast, egui was repeatedly praised for its simplicity and ease of use, leaving a notably positive impression.
By learning egui, we found its core concept straightforward: to implement a GUI for an application, the app logic should be encapsulated in a struct that implements the eframe::App trait. This trait requires defining an update() function, which describes the GUI and is called multiple times per second to update the interface (typically around 60 times per second by default).
To start the GUI, eframe::run_native() is used. It takes configuration information such as window options and a boxed instance of the app struct implementing the App trait.
The official egui website presents egui as a tool for simple and effective information representation and modeling. However, we did not evaluate its performance in graphically demanding scenarios.
Throughout development, we experienced egui as a very well-documented framework. Still, there were occasional issues with deprecated functions that produced warnings. As a result, we had to refactor the code several times to maintain compatibility with the current version. This raises whether the program will still run as smoothly in two to three years, once the framework has evolved further. If not, this would undermine one of Rust’s key advantages for our project: its reputation as a long-lived and stable language.
As no critical data or computing resources are accessed by the software, an attack would most likely aim to crash the application. Input validation, a widely known principle especially in web services, is only required in our case for the code editor. The content entered there is later parsed and translated by our parser, which throws errors if invalid input appears. One interesting test missing would check whether extremely long input in the text editor could eventually cause the application to crash.
In an article, we read about GUI Element Misuse, short "GEM". This technique considers UI elements that should only be usable under specific conditions, such as at certain times or by users with special roles. In our case, this applies to the available actions in the action panel: flashing a program should only be possible after building it, and executing it should only be possible after flashing. These state-based actions were tested here.
Problematically the draw method in the action panel could not be tested itself as it needed an egui::UI object. But this object cannot be created in a test. Therefore, a method copying the draw methods logic was created.
Testing a UI might appear very easy at first glance—just try to use the program. However, this assumption often stems from a developer’s perspective and overlooks the complexity of real user behavior. Developers tend to understand how the program is supposed to work, which can lead to a lack of insight into how users actually interact with the interface.
As a result, functionality tests that were only performed by developers should have been extended to a group of people with no prior involvement in the project. Unfortunately, this was not the case. Consequently, usability was also not properly tested.
In addition to functional and usability testing, mockup testing and integration testing would have been necessary to ensure a more complete evaluation of the application.
In the file application logic, there are, to some extent, two tests for a logic method responsible for saving a file to the current path. To verify correct functionality and guard against potential misuse, the following positive test cases were written:
- If the current path is empty, an error should be thrown.
- If the current path is not empty, the content of the file should be saved as expected.
This should be done for all functions that are important for the application logic. Further tests that would complement the testing are single tests if the panels for the GUI are loaded properly in different scenarios. Additionally, their own logic should be tested.




