Задать вопрос
@Lolcaty

Как в этом коде перенести загрузчик в виртуальный машинный код?

Есть вот такой код
#include <iostream>
#include <vector>
#include <chrono>
#include <array>
#include <iomanip>
#include <algorithm>
#include <bitset>
#include <string>
#include <sstream>
static constexpr uint16_t USER_PROGRAM_BASE = 0x2000;
 
// Набор машинных инструкций виртуального компьютера
enum Opcode {
    MOV_IMM = 0x1,   // Загрузка константы в регистр: MOV_IMM rX, <imm>
    ADD = 0x2,   // Сложение: ADD r_dst, r_src1, r_src2
    OUT = 0x3,   // Вывод значения регистра (в десятичном виде)
    MOV_REG = 0x4,   // Перемещение данных между регистрами: MOV_REG r_dst, r_src
    JMP = 0x5,   // Безусловный переход: JMP <offset> (относительно базового адреса программы)
    SLEEP = 0x6,   // Задержка: SLEEP <кол-во циклов>
    IN = 0x7,   // Чтение символа с консоли: IN rX
    PRINT = 0x8,   // Вывод символа (нижние 8 бит регистра интерпретируются как char)
    HALT = 0x9    // Завершение исполнения программы
};
 
class CPU {
private:
    static constexpr size_t MEMORY_SIZE = 65536;
    std::array<std::bitset<256>, 4> registers;
    uint16_t ip = 0;
    uint16_t program_base = 0;
    std::vector<uint16_t> memory;
    uint64_t sleep_cycles = 0;
    uint64_t clock_freq;
    std::chrono::time_point<std::chrono::high_resolution_clock> last_tick;
    std::string bitsToDec(const std::bitset<256>& bits) {
        std::string dec = "0";
        for (int i = 255; i >= 0; --i) {
            int carry = 0;
            for (int j = dec.size() - 1; j >= 0; --j) {
                int digit = (dec[j] - '0') * 2 + carry;
                carry = digit / 10;
                dec[j] = (digit % 10) + '0';
            }
            if (carry > 0)
                dec.insert(dec.begin(), carry + '0');
            if (bits[i]) {
                carry = 1;
                for (int j = dec.size() - 1; j >= 0; --j) {
                    int digit = (dec[j] - '0') + carry;
                    carry = digit / 10;
                    dec[j] = (digit % 10) + '0';
                    if (carry == 0)
                        break;
                }
                if (carry > 0)
                    dec.insert(dec.begin(), carry + '0');
            }
        }
        return dec.empty() ? "0" : dec;
    }
    uint8_t getDestReg(uint16_t instr) { return (instr >> 10) & 0x03; }
    uint8_t getSrcReg1(uint16_t instr) { return (instr >> 8) & 0x03; }
    uint8_t getSrcReg2(uint16_t instr) { return (instr >> 6) & 0x03; }
    uint8_t getImmediateValue(uint16_t instr) { return instr & 0xFF; }
    uint16_t getJumpOffset(uint16_t instr) { return instr & 0x0FFF; }
    void syncClock() {
        auto now = std::chrono::high_resolution_clock::now();
        std::chrono::nanoseconds target_duration(1000000000 / clock_freq);
        auto elapsed = now - last_tick;
        if (elapsed < target_duration) {
            while (std::chrono::high_resolution_clock::now() - last_tick < target_duration) {}
        }
        last_tick = std::chrono::high_resolution_clock::now();
    }
public:
    CPU(uint64_t freq) : clock_freq(freq), memory(MEMORY_SIZE, 0) {
        last_tick = std::chrono::high_resolution_clock::now();
    }
    void load_program(uint16_t address, const std::vector<uint16_t>& program) {
        if (address + program.size() >= MEMORY_SIZE) {
            throw std::out_of_range("Программа требует слишком много памяти");
        }
        std::copy(program.begin(), program.end(), memory.begin() + address);
    }
    void start_program(uint16_t entry_point) {
        ip = entry_point;
        program_base = entry_point;
        registers.fill(std::bitset<256>(0));
        sleep_cycles = 0;
    }
    void execute() {
        while (ip < MEMORY_SIZE) {
            syncClock();
            if (sleep_cycles > 0) {
                sleep_cycles--;
                continue;
            }
            uint16_t instr = memory[ip++];
            uint8_t opcode = (instr >> 12) & 0x0F;
            switch (opcode) {
            case MOV_IMM: {
                uint8_t reg = getDestReg(instr);
                uint8_t imm = getImmediateValue(instr);
                registers[reg] = std::bitset<256>(imm);
                break;
            }
            case ADD: {
                uint8_t dst = getDestReg(instr);
                uint8_t src1 = getSrcReg1(instr);
                uint8_t src2 = getSrcReg2(instr);
                bool carry = false;
                std::bitset<256> result;
                for (int i = 0; i < 256; ++i) {
                    bool b1 = registers[src1][i];
                    bool b2 = registers[src2][i];
                    uint8_t sum = b1 + b2 + carry;
                    result[i] = sum % 2;
                    carry = sum >= 2;
                }
                if (carry && program_base == USER_PROGRAM_BASE) {
                    printf("Прерывание: обнаружено переполнение 256-битного значения. Завершение программы.\n");
                    return;
                }
                registers[dst] = result;
                break;
            }
            case OUT: {
                uint8_t reg = getDestReg(instr);
                printf("OUT: %s\n", bitsToDec(registers[reg]).c_str());
                break;
            }
            case MOV_REG: {
                uint8_t dst = getDestReg(instr);
                uint8_t src = getSrcReg1(instr);
                registers[dst] = registers[src];
                break;
            }
            case JMP: {
                uint16_t offset = getJumpOffset(instr);
                ip = program_base + offset;
                break;
            }
            case SLEEP: {
                sleep_cycles = instr & 0x0FFF;
                break;
            }
            case IN: {
                uint8_t reg = getDestReg(instr);
                char ch;
                std::cin.get(ch);
                registers[reg] = std::bitset<256>(static_cast<uint16_t>(ch));
                break;
            }
            case PRINT: {
                uint8_t reg = getDestReg(instr);
                char ch = static_cast<char>(registers[reg].to_ulong() & 0xFF);
                printf("%c", ch);
                break;
            }
            case HALT: {
                return;
            }
            default:
                printf("Неизвестная инструкция: 0x%X\n", instr);
                return;
            }
        }
    }
};
void runLoader(CPU& cpu) {
    printf("Добро пожаловать в эмулятор ОС\n\n");
    printf("Доступные команды:\n");
    printf("  run <машинный код>  - загрузить и выполнить программу. Машинный код задаётся шестнадцатеричными числами через пробел.\n");
    printf("  help                - вывести справку по командам.\n");
    printf("  programs            - показать доступные для запуска программы\n");
    printf("  exit                - завершить работу ОС.\n\n");
    const uint16_t PROGRAM_LOAD_ADDRESS = USER_PROGRAM_BASE;
    while (true) {
        printf("OS> ");
        std::string line;
        std::getline(std::cin, line);
        if (line.empty())
            std::getline(std::cin, line);
        std::istringstream iss(line);
        std::string command;
        iss >> command;
        if (command == "exit") {
            printf("Завершение работы ОС.\n");
            break;
        }
        else if (command == "help") {
            printf("\nДоступные команды:\n");
            printf("  run <машинный код> - загрузить и выполнить программу. Пример:\n");
            printf("    run 0x1001 0x1401 0x3000 ... 0x9000\n");
            printf("  programs - показать доступные для запуска программы\n");
            printf("  exit                - выйти из ОС.\n\n");
        }
        else if (command == "run") {
            std::vector<uint16_t> program;
            std::string token;
            while (iss >> token) {
                try {
                    if (token.find("0x") == 0 || token.find("0X") == 0)
                        token = token.substr(2);
                    uint16_t instr = static_cast<uint16_t>(std::stoul(token, nullptr, 16));
                    program.push_back(instr);
                }
                catch (std::exception& e) {
                    fprintf(stderr, "Ошибка преобразования токена '%s': %s\n", token.c_str(), e.what());
                }
            }
            if (program.empty()) {
                fprintf(stderr, "Не указан машинный код для загрузки.\n");
                continue;
            }
            try {
                cpu.load_program(PROGRAM_LOAD_ADDRESS, program);
                cpu.start_program(PROGRAM_LOAD_ADDRESS);
                printf("Запуск программы...\n");
                cpu.execute();
                printf("\nПрограмма завершила работу.\n");
            }
            catch (std::exception& e) {
                fprintf(stderr, "Ошибка при загрузке/исполнении программы: %s\n", e.what());
            }
        }
        else if (command == "programs") {
            printf("\nДоступные программы (Вы можете скопировать их и вставить в команду run):\n");
            printf("\nВычисление чисел Фибоначчи: 0x1001 0x1401 0x3000 0x63E8 0x3400 0x63E8 0x2840 0x3800 0x63E8 0x4100 0x4600 0x5006\n");
        }
        else {
            fprintf(stderr, "Неизвестная команда. Введите 'help' для справки.\n");
        }
    }
}
int main() {
    setlocale(LC_ALL, "ru");
    int fr;
    printf("Введите скорость работы виртуального компьютера (Рекомендуется 1000): ");
    std::cin >> fr;
    printf("\n\n");
    CPU cpu(fr);
    runLoader(cpu);
    return 0;
}


Задача такая - Нужно весь функционал RunLoader перенести в виртуальную среду CPU. Что-бы загрузчик запускался в этом виртуальном процессоре и в этой виртуальной памяти и все оперции производил там.

Как это можно сделать?
  • Вопрос задан
  • 98 просмотров
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 1
axifive
@axifive
Software Engineer
Да просто перенести runLoader() в класс, и добавить его вызов в конструкторе.

spoiler
class CPU {
private:
    static constexpr size_t MEMORY_SIZE = 65536;
    std::array<std::bitset<256>, 4> registers;
    uint16_t ip = 0;
    uint16_t program_base = 0;
    std::vector<uint16_t> memory;
    uint64_t sleep_cycles = 0;
    uint64_t clock_freq;
    std::chrono::time_point<std::chrono::high_resolution_clock> last_tick;
    std::string bitsToDec(const std::bitset<256>& bits) {
        std::string dec = "0";
        for (int i = 255; i >= 0; --i) {
            int carry = 0;
            for (int j = dec.size() - 1; j >= 0; --j) {
                int digit = (dec[j] - '0') * 2 + carry;
                carry = digit / 10;
                dec[j] = (digit % 10) + '0';
            }
            if (carry > 0)
                dec.insert(dec.begin(), carry + '0');
            if (bits[i]) {
                carry = 1;
                for (int j = dec.size() - 1; j >= 0; --j) {
                    int digit = (dec[j] - '0') + carry;
                    carry = digit / 10;
                    dec[j] = (digit % 10) + '0';
                    if (carry == 0)
                        break;
                }
                if (carry > 0)
                    dec.insert(dec.begin(), carry + '0');
            }
        }
        return dec.empty() ? "0" : dec;
    }
    void runLoader() {
        printf("Добро пожаловать в эмулятор ОС\n\n");
        printf("Доступные команды:\n");
        printf("  run <машинный код>  - загрузить и выполнить программу. Машинный код задаётся шестнадцатеричными числами через пробел.\n");
        printf("  help                - вывести справку по командам.\n");
        printf("  programs            - показать доступные для запуска программы\n");
        printf("  exit                - завершить работу ОС.\n\n");
        const uint16_t PROGRAM_LOAD_ADDRESS = USER_PROGRAM_BASE;
        while (true) {
            printf("OS> ");
            std::string line;
            std::getline(std::cin, line);
            if (line.empty())
                std::getline(std::cin, line);
            std::istringstream iss(line);
            std::string command;
            iss >> command;
            if (command == "exit") {
                printf("Завершение работы ОС.\n");
                break;
            }
            else if (command == "help") {
                printf("\nДоступные команды:\n");
                printf("  run <машинный код> - загрузить и выполнить программу. Пример:\n");
                printf("    run 0x1001 0x1401 0x3000 ... 0x9000\n");
                printf("  programs - показать доступные для запуска программы\n");
                printf("  exit                - выйти из ОС.\n\n");
            }
            else if (command == "run") {
                std::vector<uint16_t> program;
                std::string token;
                while (iss >> token) {
                    try {
                        if (token.find("0x") == 0 || token.find("0X") == 0)
                            token = token.substr(2);
                        uint16_t instr = static_cast<uint16_t>(std::stoul(token, nullptr, 16));
                        program.push_back(instr);
                    }
                    catch (std::exception& e) {
                        fprintf(stderr, "Ошибка преобразования токена '%s': %s\n", token.c_str(), e.what());
                    }
                }
                if (program.empty()) {
                    fprintf(stderr, "Не указан машинный код для загрузки.\n");
                    continue;
                }
                try {
                    load_program(PROGRAM_LOAD_ADDRESS, program);
                    start_program(PROGRAM_LOAD_ADDRESS);
                    printf("Запуск программы...\n");
                    execute();
                    printf("\nПрограмма завершила работу.\n");
                }
                catch (std::exception& e) {
                    fprintf(stderr, "Ошибка при загрузке/исполнении программы: %s\n", e.what());
                }
            }
            else if (command == "programs") {
                printf("\nДоступные программы (Вы можете скопировать их и вставить в команду run):\n");
                printf("\nВычисление чисел Фибоначчи: 0x1001 0x1401 0x3000 0x63E8 0x3400 0x63E8 0x2840 0x3800 0x63E8 0x4100 0x4600 0x5006\n");
            }
            else {
                fprintf(stderr, "Неизвестная команда. Введите 'help' для справки.\n");
            }
        }
    }
    uint8_t getDestReg(uint16_t instr) { return (instr >> 10) & 0x03; }
    uint8_t getSrcReg1(uint16_t instr) { return (instr >> 8) & 0x03; }
    uint8_t getSrcReg2(uint16_t instr) { return (instr >> 6) & 0x03; }
    uint8_t getImmediateValue(uint16_t instr) { return instr & 0xFF; }
    uint16_t getJumpOffset(uint16_t instr) { return instr & 0x0FFF; }
    void syncClock() {
        auto now = std::chrono::high_resolution_clock::now();
        std::chrono::nanoseconds target_duration(1000000000 / clock_freq);
        auto elapsed = now - last_tick;
        if (elapsed < target_duration) {
            while (std::chrono::high_resolution_clock::now() - last_tick < target_duration) {}
        }
        last_tick = std::chrono::high_resolution_clock::now();
    }
public:
    CPU(uint64_t freq) : clock_freq(freq), memory(MEMORY_SIZE, 0) {
        last_tick = std::chrono::high_resolution_clock::now();
        runLoader();
    }
    void load_program(uint16_t address, const std::vector<uint16_t>& program) {
        if (address + program.size() >= MEMORY_SIZE) {
            throw std::out_of_range("Программа требует слишком много памяти");
        }
        std::copy(program.begin(), program.end(), memory.begin() + address);
    }
    void start_program(uint16_t entry_point) {
        ip = entry_point;
        program_base = entry_point;
        registers.fill(std::bitset<256>(0));
        sleep_cycles = 0;
    }
    void execute() {
        while (ip < MEMORY_SIZE) {
            syncClock();
            if (sleep_cycles > 0) {
                sleep_cycles--;
                continue;
            }
            uint16_t instr = memory[ip++];
            uint8_t opcode = (instr >> 12) & 0x0F;
            switch (opcode) {
            case MOV_IMM: {
                uint8_t reg = getDestReg(instr);
                uint8_t imm = getImmediateValue(instr);
                registers[reg] = std::bitset<256>(imm);
                break;
            }
            case ADD: {
                uint8_t dst = getDestReg(instr);
                uint8_t src1 = getSrcReg1(instr);
                uint8_t src2 = getSrcReg2(instr);
                bool carry = false;
                std::bitset<256> result;
                for (int i = 0; i < 256; ++i) {
                    bool b1 = registers[src1][i];
                    bool b2 = registers[src2][i];
                    uint8_t sum = b1 + b2 + carry;
                    result[i] = sum % 2;
                    carry = sum >= 2;
                }
                if (carry && program_base == USER_PROGRAM_BASE) {
                    printf("Прерывание: обнаружено переполнение 256-битного значения. Завершение программы.\n");
                    return;
                }
                registers[dst] = result;
                break;
            }
            case OUT: {
                uint8_t reg = getDestReg(instr);
                printf("OUT: %s\n", bitsToDec(registers[reg]).c_str());
                break;
            }
            case MOV_REG: {
                uint8_t dst = getDestReg(instr);
                uint8_t src = getSrcReg1(instr);
                registers[dst] = registers[src];
                break;
            }
            case JMP: {
                uint16_t offset = getJumpOffset(instr);
                ip = program_base + offset;
                break;
            }
            case SLEEP: {
                sleep_cycles = instr & 0x0FFF;
                break;
            }
            case IN: {
                uint8_t reg = getDestReg(instr);
                char ch;
                std::cin.get(ch);
                registers[reg] = std::bitset<256>(static_cast<uint16_t>(ch));
                break;
            }
            case PRINT: {
                uint8_t reg = getDestReg(instr);
                char ch = static_cast<char>(registers[reg].to_ulong() & 0xFF);
                printf("%c", ch);
                break;
            }
            case HALT: {
                return;
            }
            default:
                printf("Неизвестная инструкция: 0x%X\n", instr);
                return;
            }
        }
    }
};
int main() {
    setlocale(LC_ALL, "ru");
    int fr;
    printf("Введите скорость работы виртуального компьютера (Рекомендуется 1000): ");
    std::cin >> fr;
    printf("\n\n");
    CPU cpu(fr);
    //runLoader(cpu);
    return 0;
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы