152 lines
3.8 KiB
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);
|
|
}
|