brainfuck/src/bf_emulator.c

152 lines
3.8 KiB
C

#include "bf_emulator.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
void bf_emulator_init(struct bf_emulator *emulator, uint32_t cell_size, uint32_t data_len)
{
assert(cell_size == 8 || cell_size == 16 || cell_size == 32);
emulator->cell_size = cell_size;
emulator->data = calloc(data_len, cell_size / 8);
emulator->data_len = data_len;
}
void bf_emulator_deinit(struct bf_emulator *emulator)
{
free(emulator->data);
emulator->data = NULL;
emulator->data_len = 0;
}
static void bf_emulator_set(struct bf_emulator *emulator, uint32_t index, uint32_t value)
{
if (emulator->cell_size == 8) {
((uint8_t*)emulator->data)[index] = value;
} else if (emulator->cell_size == 16) {
((uint16_t*)emulator->data)[index] = value;
} else if (emulator->cell_size == 32) {
((uint32_t*)emulator->data)[index] = value;
}
}
static uint32_t bf_emulator_get(struct bf_emulator *emulator, uint32_t index)
{
if (emulator->cell_size == 8) {
return ((uint8_t*)emulator->data)[index];
} else if (emulator->cell_size == 16) {
return ((uint16_t*)emulator->data)[index];
} else if (emulator->cell_size == 32) {
return ((uint32_t*)emulator->data)[index];
}
return 0;
}
static void bf_emulator_inc(struct bf_emulator *emulator, uint32_t index)
{
bf_emulator_set(emulator, index, bf_emulator_get(emulator, index) + 1);
}
static void bf_emulator_dec(struct bf_emulator *emulator, uint32_t index)
{
bf_emulator_set(emulator, index, bf_emulator_get(emulator, index) - 1);
}
enum bf_step_result bf_emulator_step(struct bf_emulator *emulator)
{
if (emulator->program_pointer == emulator->program_len) {
return BF_STEP_FINISHED;
}
char inst = emulator->program[emulator->program_pointer];
switch (inst) {
case '>':
emulator->data_pointer = (emulator->data_pointer + 1) % emulator->data_len;
break;
case '<':
emulator->data_pointer = (emulator->data_pointer - 1 + emulator->data_len) % emulator->data_len;
break;
case '-':
bf_emulator_dec(emulator, emulator->data_pointer);
break;
case '+':
bf_emulator_inc(emulator, emulator->data_pointer);
break;
case '.':
putchar(bf_emulator_get(emulator, emulator->data_pointer));
break;
case ',':
bf_emulator_set(emulator, emulator->data_pointer, getchar());
break;
case '[':
if (bf_emulator_get(emulator, emulator->data_pointer) == 0) {
uint32_t open_blocks = 0;
while (true) {
emulator->program_pointer++;
if (emulator->program_pointer == emulator->program_len) {
return BF_STEP_ERROR;
}
if (emulator->program[emulator->program_pointer] == '[') {
open_blocks++;
}
if (emulator->program[emulator->program_pointer] == ']') {
if (open_blocks == 0) break;
open_blocks--;
}
}
}
break;
case ']':
if (bf_emulator_get(emulator, emulator->data_pointer) != 0) {
uint32_t closing_blocks = 0;
while (true) {
if (emulator->program_pointer == 0) {
return BF_STEP_ERROR;
}
emulator->program_pointer--;
if (emulator->program[emulator->program_pointer] == ']') {
closing_blocks++;
}
if (emulator->program[emulator->program_pointer] == '[') {
if (closing_blocks == 0) break;
closing_blocks--;
}
}
}
break;
default:
break;
};
emulator->program_pointer++;
return BF_STEP_OK;
}
enum bf_step_result bf_emulator_continue(struct bf_emulator *emulator)
{
while (true) {
enum bf_step_result result = bf_emulator_step(emulator);
if (result != BF_STEP_OK) {
return result;
}
}
}
enum bf_step_result bf_emulator_run(struct bf_emulator *emulator, const char *program, size_t program_len)
{
emulator->data_pointer = 0;
memset(emulator->data, 0, emulator->data_len);
emulator->program = program;
emulator->program_len = program_len;
emulator->program_pointer = 0;
return bf_emulator_continue(emulator);
}