315 lines
6.8 KiB
C
315 lines
6.8 KiB
C
// SPDX-FileCopyrightText: 2008-2011 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "bfvm.h"
|
|
|
|
static ut8 bfvm_op(BfvmCPU *c) {
|
|
// XXX: this is slow :(
|
|
ut8 buf[4] = { 0 };
|
|
if (c && c->iob.read_at && !c->iob.read_at(c->iob.io, c->eip, buf, 4)) {
|
|
return 0xff;
|
|
}
|
|
return buf[0];
|
|
}
|
|
|
|
RZ_API int bfvm_in_trap(BfvmCPU *c) {
|
|
switch (bfvm_op(c)) {
|
|
case 0x00:
|
|
case 0xcc:
|
|
case 0xff:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
RZ_API void bfvm_reset(BfvmCPU *c) {
|
|
memset(c->mem, '\0', c->size);
|
|
memset(c->input_buf, '\0', c->input_size);
|
|
memset(c->screen_buf, '\0', c->screen_size);
|
|
c->base = BFVM_DATA_ADDR;
|
|
c->input = BFVM_INPUT_ADDR;
|
|
c->input_idx = 0;
|
|
c->screen = BFVM_SCREEN_ADDR;
|
|
c->screen_idx = 0;
|
|
c->eip = 0; // TODO look forward nops
|
|
c->ptr = 0;
|
|
c->esp = c->base;
|
|
}
|
|
|
|
RZ_API int bfvm_init(BfvmCPU *c, ut32 size, int circular) {
|
|
memset(c, '\0', sizeof(BfvmCPU));
|
|
|
|
/* data */
|
|
c->mem = (ut8 *)malloc(size);
|
|
if (!c->mem) {
|
|
return 0;
|
|
}
|
|
memset(c->mem, '\0', size);
|
|
|
|
/* setup */
|
|
c->circular = circular;
|
|
c->size = size;
|
|
|
|
// TODO: use RzBuffer or so here.. this is spagueti
|
|
/* screen */
|
|
c->screen = BFVM_SCREEN_ADDR;
|
|
c->screen_size = BFVM_SCREEN_SIZE;
|
|
c->screen_buf = (ut8 *)malloc(c->screen_size);
|
|
memset(c->screen_buf, '\0', c->screen_size);
|
|
|
|
/* input */
|
|
c->input_size = BFVM_INPUT_SIZE;
|
|
c->input_buf = (ut8 *)malloc(c->input_size);
|
|
bfvm_reset(c);
|
|
return 1;
|
|
}
|
|
|
|
RZ_API BfvmCPU *bfvm_new(RzIOBind *iob) {
|
|
BfvmCPU *c = RZ_NEW0(BfvmCPU);
|
|
bfvm_init(c, 4096, 1);
|
|
memcpy(&c->iob, iob, sizeof(c->iob));
|
|
return c;
|
|
}
|
|
|
|
RZ_API BfvmCPU *bfvm_free(BfvmCPU *c) {
|
|
free(c->mem);
|
|
c->mem = 0;
|
|
free(c->screen_buf);
|
|
c->screen_buf = 0;
|
|
free(c);
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API ut8 *bfvm_get_ptr_at(BfvmCPU *c, ut64 at) {
|
|
if (at >= c->base) {
|
|
at -= c->base;
|
|
} else if (at >= c->size) {
|
|
at = c->circular ? 0 : c->size - 1;
|
|
}
|
|
return c->mem + at;
|
|
}
|
|
|
|
RZ_API ut8 *bfvm_get_ptr(BfvmCPU *c) {
|
|
// return bfvm_cpu.mem;
|
|
return bfvm_get_ptr_at(c, c->ptr);
|
|
}
|
|
|
|
RZ_API ut8 bfvm_get(BfvmCPU *c) {
|
|
ut8 *ptr = bfvm_get_ptr(c);
|
|
return ptr ? *ptr : 0;
|
|
}
|
|
|
|
RZ_API void bfvm_inc(BfvmCPU *c) {
|
|
ut8 *mem = bfvm_get_ptr(c);
|
|
if (mem != NULL) {
|
|
mem[0]++;
|
|
}
|
|
}
|
|
|
|
RZ_API void bfvm_dec(BfvmCPU *c) {
|
|
ut8 *mem = bfvm_get_ptr(c);
|
|
if (mem != NULL) {
|
|
mem[0]--;
|
|
}
|
|
}
|
|
|
|
RZ_API int bfvm_reg_set(BfvmCPU *c, const char *str) {
|
|
char *ptr = strchr(str, ' ');
|
|
if (!ptr) {
|
|
return 0;
|
|
}
|
|
if (strstr(str, "eip")) {
|
|
c->eip = rz_num_math(NULL, ptr + 1);
|
|
} else if (strstr(str, "esp")) {
|
|
c->esp = rz_num_math(NULL, ptr + 1);
|
|
} else if (strstr(str, "ptr")) {
|
|
c->ptr = rz_num_math(NULL, ptr + 1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* screen and input */
|
|
RZ_API void bfvm_peek(BfvmCPU *c) {
|
|
int idx = c->input_idx;
|
|
ut8 *ptr = bfvm_get_ptr(c);
|
|
|
|
if (idx >= c->input_size) {
|
|
idx = 0;
|
|
}
|
|
|
|
if (ptr) {
|
|
*ptr = c->input_buf[idx];
|
|
c->input_idx = idx + 1;
|
|
}
|
|
}
|
|
|
|
RZ_API void bfvm_poke(BfvmCPU *c) {
|
|
int idx = c->screen_idx;
|
|
c->screen_buf[idx] = bfvm_get(c);
|
|
c->screen_idx = idx + 1;
|
|
}
|
|
|
|
RZ_API int bfvm_trace_op(BfvmCPU *c, ut8 op) {
|
|
ut8 g;
|
|
switch (op) {
|
|
case '\0':
|
|
eprintf(" ; trap (%02x)\n", op);
|
|
// fallthrough
|
|
case '.':
|
|
case ',':
|
|
case '+':
|
|
case '-':
|
|
case '>':
|
|
case '<':
|
|
eprintf("%c", op);
|
|
break;
|
|
case '[':
|
|
case ']':
|
|
g = bfvm_get(c);
|
|
eprintf("%c ; [ptr] = %d\n", op, g);
|
|
if (g != 0) {
|
|
eprintf("[");
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define T if (c->trace)
|
|
/* debug */
|
|
RZ_API int bfvm_step(BfvmCPU *c, int over) {
|
|
ut8 op2, op = bfvm_op(c);
|
|
|
|
do {
|
|
T bfvm_trace_op(c, op);
|
|
switch (op) {
|
|
case '\0':
|
|
/* trap */
|
|
return 1;
|
|
case '.':
|
|
// bfvm_get_ptr (c);
|
|
bfvm_poke(c);
|
|
break;
|
|
case ',':
|
|
bfvm_peek(c);
|
|
/* TODO read */
|
|
break;
|
|
case '+':
|
|
bfvm_inc(c);
|
|
break;
|
|
case '-':
|
|
bfvm_dec(c);
|
|
break;
|
|
case '>':
|
|
c->ptr++;
|
|
break;
|
|
case '<':
|
|
c->ptr--;
|
|
break;
|
|
case '[':
|
|
break;
|
|
case ']':
|
|
if (bfvm_get(c) != 0) {
|
|
do {
|
|
/* control underflow */
|
|
if (c->eip < (c->eip - 1)) {
|
|
c->eip = 0;
|
|
break;
|
|
}
|
|
c->eip--;
|
|
} while (bfvm_op(c) != '[');
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
c->eip++;
|
|
op2 = bfvm_op(c);
|
|
} while (over && op == op2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
RZ_API int bfvm_contsc(BfvmCPU *c) {
|
|
c->breaked = 0;
|
|
while (!c->breaked) {
|
|
bfvm_step(c, 0);
|
|
if (bfvm_in_trap(c)) {
|
|
eprintf("Trap instruction at 0x%08" PFMT64x "\n", c->eip);
|
|
break;
|
|
}
|
|
switch (bfvm_op(c)) {
|
|
case ',':
|
|
eprintf("contsc: read from input trap\n");
|
|
c->breaked = 1;
|
|
continue;
|
|
case '.':
|
|
eprintf("contsc: print to screen trap\n");
|
|
c->breaked = 1;
|
|
continue;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
RZ_API int bfvm_cont(BfvmCPU *c, ut64 until) {
|
|
c->breaked = 0;
|
|
while (!c->breaked && c->eip != until) {
|
|
bfvm_step(c, 0);
|
|
if (bfvm_in_trap(c)) {
|
|
eprintf("Trap instruction at 0x%" PFMT64x "\n", c->eip);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
RZ_API int bfvm_trace(BfvmCPU *c, ut64 until) {
|
|
c->trace = 1;
|
|
bfvm_cont(c, until);
|
|
c->trace = 0;
|
|
return 0;
|
|
}
|
|
|
|
RZ_API void bfvm_show_regs(BfvmCPU *c, int rad) {
|
|
if (rad) {
|
|
eprintf("fs regs\n");
|
|
eprintf("f eip @ 0x%08" PFMT64x "\n", (ut64)c->eip);
|
|
eprintf("f esp @ 0x%08" PFMT64x "\n", (ut64)c->esp);
|
|
eprintf("f ptr @ 0x%08" PFMT64x "\n", (ut64)c->ptr + c->base);
|
|
eprintf("fs *\n");
|
|
} else {
|
|
ut8 ch = bfvm_get(c);
|
|
eprintf(" eip 0x%08" PFMT64x " esp 0x%08" PFMT64x "\n",
|
|
(ut64)c->eip, (ut64)c->esp);
|
|
eprintf(" ptr 0x%08x [ptr] %d = 0x%02x '%c'\n",
|
|
(ut32)c->ptr, ch, ch, IS_PRINTABLE(ch) ? ch : ' ');
|
|
}
|
|
}
|
|
|
|
RZ_API void bfvm_maps(BfvmCPU *c, int rad) {
|
|
if (rad) {
|
|
eprintf("fs sections\n");
|
|
eprintf("e cmd.vprompt=px@screen\n");
|
|
eprintf("f section_code @ 0x%08" PFMT64x "\n", (ut64)BFVM_CODE_ADDR);
|
|
eprintf("f section_code_end @ 0x%08" PFMT64x "\n", (ut64)BFVM_CODE_ADDR + BFVM_CODE_SIZE);
|
|
eprintf("f section_data @ 0x%08" PFMT64x "\n", (ut64)c->base);
|
|
eprintf("f section_data_end @ 0x%08" PFMT64x "\n", (ut64)c->base + c->size);
|
|
eprintf("f screen @ 0x%08" PFMT64x "\n", (ut64)c->screen);
|
|
eprintf("f section_screen @ 0x%08" PFMT64x "\n", (ut64)c->screen);
|
|
eprintf("f section_screen_end @ 0x%08" PFMT64x "\n", (ut64)c->screen + c->screen_size);
|
|
eprintf("f input @ 0x%08" PFMT64x "\n", (ut64)c->input);
|
|
eprintf("f section_input @ 0x%08" PFMT64x "\n", (ut64)c->input);
|
|
eprintf("f section_input_end @ 0x%08" PFMT64x "\n", (ut64)c->input + c->input_size);
|
|
eprintf("fs *\n");
|
|
} else {
|
|
eprintf("0x%08" PFMT64x " - 0x%08" PFMT64x " rwxu 0x%08" PFMT64x " .code\n",
|
|
(ut64)0, (ut64)c->size, (ut64)c->size);
|
|
eprintf("0x%08" PFMT64x " - 0x%08" PFMT64x " rw-- 0x%08" PFMT64x " .data\n",
|
|
(ut64)c->base, (ut64)(c->base + c->size), (ut64)c->size);
|
|
eprintf("0x%08" PFMT64x " - 0x%08" PFMT64x " rw-- 0x%08" PFMT64x " .screen\n",
|
|
(ut64)c->screen, (ut64)(c->screen + c->screen_size), (ut64)c->screen_size);
|
|
eprintf("0x%08" PFMT64x " - 0x%08" PFMT64x " rw-- 0x%08" PFMT64x " .input\n",
|
|
(ut64)c->input, (ut64)(c->input + c->input_size), (ut64)c->input_size);
|
|
}
|
|
}
|