-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
108 lines (92 loc) · 3.67 KB
/
main.cpp
File metadata and controls
108 lines (92 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>
#include <x86lab/vm.hpp>
#include <x86lab/assembler.hpp>
#include <x86lab/ui/cli.hpp>
#include <x86lab/ui/tui.hpp>
#include <x86lab/ui/imgui.hpp>
#include <x86lab/runner.hpp>
#include <filesystem>
using namespace X86Lab;
static void help() {
std::cerr << "X86Lab: A playground for x86 assembly programming." <<
std::endl;
std::cerr << "Usage:" << std::endl;
std::cerr << " x86lab [options] <file>" << std::endl << std::endl;
std::cerr << "Options:" << std::endl;
std::cerr << " --help This message" << std::endl;
std::cerr << "<file> is a file path to an assembly file that must be "
"compatible with the NASM assembler. Any NASM directive within this "
"file is valid and accepted" << std::endl;
}
static void run(std::string const& fileName) {
// Run code in `fileName` starting directly in 64 bits mode.
std::shared_ptr<Ui::Backend> ui(new Ui::Imgui());
// Initilize UI.
if (!ui->init()) {
throw X86Lab::Error("Cannot initialize UI", 0);
}
// Assemble the code.
ui->log("Assembling code in " + fileName);
std::shared_ptr<Code> const code(new Code(fileName));
ui->log("Assembled code is " + std::to_string(code->size()) + " bytes");
// Create the VM and load the code in memory.
bool exitRequested(false);
// By default the VM starts in 64-bit long mode. This can be changed through
// the interface. Changing the start CPU mode resets the VM.
// FIXME: To avoid any issue when running the example code, hardcode the
// start mode to be 16 bit when running the example. This is a horrendous
// hack.
bool const runningDemo(std::filesystem::path(fileName).filename()==
"jumpToProtectedAndLongModes.asm");
Vm::CpuMode startCpuMode(runningDemo?
Vm::CpuMode::RealMode:Vm::CpuMode::LongMode);
while (!exitRequested) {
// When the user requests resetting the VM we simply destroy the VM and
// re-create it from scratch. This is much easier compared to manually
// resetting the state of the CPU and memory.
// FIXME: We need a way to specify the size of the VM.
std::shared_ptr<Vm> vm(new Vm(startCpuMode, 4 * X86Lab::PAGE_SIZE));
vm->loadCode(*code);
ui->log("Code loaded");
// Runner instances are a bit ephemeral, as soon as their run() return
// they cannot be used anymore.
Runner runner(vm, code, ui);
Runner::ReturnReason const retReason(runner.run());
if (retReason == Runner::ReturnReason::Quit) {
exitRequested = true;
} else if (retReason == Runner::ReturnReason::Reset16) {
startCpuMode = Vm::CpuMode::RealMode;
} else if (retReason == Runner::ReturnReason::Reset32) {
startCpuMode = Vm::CpuMode::ProtectedMode;
} else if (retReason == Runner::ReturnReason::Reset64) {
startCpuMode = Vm::CpuMode::LongMode;
}
}
}
int main(int argc, char **argv) {
if (argc < 2) {
std::cerr << "Error, not enough arguments" << std::endl;
help();
std::exit(1);
}
for (int i(1); i < argc - 1; ++i) {
std::string const arg(argv[i]);
if (arg == "--help") {
help();
std::exit(0);
} else {
std::cerr << "Error, invalid argument " << arg << std::endl;
help();
std::exit(0);
}
}
std::string const fileName(argv[argc - 1]);
try {
run(fileName);
} catch (Error const& error) {
std::string const msg(error.what());
std::perror(("Error: " + msg).c_str());
std::exit(1);
}
return 0;
}