Skip to content
This repository has been archived by the owner on Oct 4, 2019. It is now read-only.

Как работает VM

КолумбиЯ edited this page Sep 21, 2019 · 4 revisions

1. Что нам вообще нужно запоминать?

1.1 У нас есть какая-то общая структура, которая хранит информацию о игре с такими полями:

  • uint8_t *map - память, на которой сражаются наши чемпионы. Инициализируем через ft_memalloc(sizeof(unit8_t) * MEM_SIZE) дабы не словить segfault при изменении #define MEM_SIZE (4*1024)
  • t_champ **champs - массив указателей на наших чемпионов. Что хранится в структуре чемпиона, описано ниже
  • champs_num - кол-во чемпионов
  • t_champ *last - последний живой чемпион. Нужен, чтобы в конце вывести результат
  • bool aff - флаг отвечающий за то, нужно ли нам выводить символ в при выполнении инструкции aff
  • int dump_mode - сколько октетов будем выводить, если был передан флаг -dump. По стандарту, там один вариант, но как несложный бонус, добавим второй вариант. Теперь будем выводить или 32, или 64 значения в строке. Если флага не было, значение в этом поле будет -1
  • int dump_cycles - относится к тому же флагу -dump. Запоминаем, через сколько циклов, собственно, вывести память и остановить выполнение виртуальной машины
  • int debug_mode - сколько октетов будем выводить, если передан флаг -debug
  • int debug_cycles - через сколько циклов будет выводиться карта, если передан флаг -debug
  • int cycles - запоминаем, сколько же прошло циклов с начала программы
  • t_carriage *carriages - список кареток. Каретки выполняют операции, но также могут умереть (memento mori). Что хранится в структуре кареток, описано ниже
  • int carriages_num - кол-во кареток
  •  int cycles_to_die - кол-во циклов, после которых нужно провести проверку для убийства кареток. Это значение будет меняться, но изначально равно дефайну
  • int cycles_after_check, int checks, int lives - все эти значение нужны для изменения cycles_to_die

1.2 Структура чемпиона содержит следующую информацию:

  • int id - уникальный номер чемпиона. Следует учитывать, что чем больше id - тем раньше исполнится код чемпиона
  • char *name - имя
  • char *comment - коммент
  • uint8_t *code - код
  • int size - размер кода
  • int live_cycle - последний цикл, на котором чемпион был замечен живым
  • t_champ *next - следующий чемпион в списке
    P.S. Изначально чемпионы будут считываться в список, но потом в саму структуру corewar он будем записан в виде массива

1.3 Структура каретки:

  • int id - уникальный номер каретки
  • t_champ *champ - чемпион, который породил картерку
  • bool carry - флажок, необходимый для выполнения некоторых инструкций
  • int pc - расположение каретки
  • int step - на сколько байтов нужно будет сместиться каретке
  • int *reg - массив регистров (P.S. Сказано, что размер регистра в байтах определен в дефайне. Я, откровенно говоря, хз как красиво работать с регистрами, длинна которых не 4 байта. Поэтому у нас будет int. "Пока работает, трогать не надо")
  • uint8_t instruction - код инструкции, которую нужно будет исполнить
  • int cycles_to_ex - через сколько циклов нужно будет исполнить инструкцию
  • uint8_t arg_types[3] - коды аргументов, требуемые инструкцией
  • int live_cycle - когда последний раз была исполнена инструкция live. Это нужно будет, чтобы потом убить каретку
  • t_carriage *next - указатель на следующую каретку в списке

2. Что и как делать?

2.1 Считать аргументы

Тут в принципе ничего сложного нет. Смотрим на наш аргумент, если это какой-то из флагов, обрабатываем его, если файл чемпиона, парсим и добавляем чемпиона в список. Если какой-то бред - дропаем ошибку. Тут только стоит поговорить и том, как парсить файл с чемпионом.

  1. Нужно считать 4 байта, преобразовать в int и сравнить с MAGIC_HEADER
  2. Дальше считать PROG_NAME_LENGTH байт и записать в champ->name. Если считалось меньше байт, чем нужно, значит файл - говно!
  3. Потом считать 4 байта. Они должны быть NULL.
  4. Считать 4 байта как число. Это будет размер чемпиона. Если он size < 0 || size > CHAMP_MAX_SIZE - дропаем ошибку.
  5. Повторяем пункт 2, только для коммента.
  6. Пункт 3.
  7. Считываем код чемпиона. Важно попробовать после этого прочитать ещё хотя бы 1 байт. Если оно что-то считало, то файл - ...

2.2 Инициализировать начальные данные

В нашу карту, где будут сражаться чемпионы, пихаем их коды на одинаковом расстоянии. Создаём список кареток. Тут всё просто

2.3 Само сражение

Тут всё сложно. Но и просто тоже.
В принципе, я работаю, пока у меня есть каретки. Что я там делаю?

  1. Если у меня кол-во циклов равно dump_cycles - просто вывожу память и ливаю
  2. Если у меня кол-во циклов равно debug_cycles - вывожу память и жду нажатия, потом работаю дальше
  3. А вот сейчас - самое классное: нужно пройтись по всем кареткам и выполнить инструкции. 3.1) Если у каретки в данный момент cycles_to_ex равно нулю, значит, она новенькая-готовенькая, ей нужно считать код инструкции, на которой она стоит. Если этот код ещё и валидный, то нужно обновить ей cycles_to_ex. 3.2) Уменьшаем cycles_to_ex. Если после этого оно равно нулю, пора каретке выходить на работу 3.3) Если у каретки валидный код инструкции, получим эту инструкции (у нас для этого есть заготовленный массивчик, просто по индексу доступились и всё готово). Таакс, нужно считать коды аргументов, если того требует данная инструкция. Потом валидируем, что считанные коды действительно такие, как требует инструкция. Потом валидируем значения аргументов. Там нам только нужно, чтобы если аргумент - регистр, то его значение 1 <= value <= REG_NUMBER