217 lines
5.0 KiB
C
217 lines
5.0 KiB
C
|
/* Copyright (c) 2020-2024 AO MCST
|
||
|
* Distributed under the terms of MIT License.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "dynasm/dasm_proto.h"
|
||
|
#include "dynasm/dasm_e2k.h"
|
||
|
#include <sys/mman.h>
|
||
|
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
|
||
|
#define MAP_ANONYMOUS MAP_ANON
|
||
|
#endif
|
||
|
|
||
|
static void* link_and_encode(dasm_State** d)
|
||
|
{
|
||
|
size_t sz;
|
||
|
void* buf;
|
||
|
dasm_link(d, &sz);
|
||
|
buf = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||
|
dasm_encode(d, buf);
|
||
|
mprotect(buf, sz, PROT_READ | PROT_EXEC);
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
#define TAPE_SIZE 30000
|
||
|
#define MAX_NESTING 100
|
||
|
|
||
|
typedef struct bf_state
|
||
|
{
|
||
|
unsigned char* tape;
|
||
|
unsigned char (*get_ch)(struct bf_state*);
|
||
|
void (*put_ch)(struct bf_state*, unsigned char);
|
||
|
} bf_state_t;
|
||
|
|
||
|
#define bad_program(s) exit(fprintf(stderr, "bad program near %.16s: %s\n", program, s))
|
||
|
|
||
|
static void(* bf_compile(const char* program) )(bf_state_t*)
|
||
|
{
|
||
|
unsigned loops[MAX_NESTING];
|
||
|
int nloops = 0;
|
||
|
int n;
|
||
|
dasm_State* d;
|
||
|
unsigned npc = 8;
|
||
|
unsigned nextpc = 0;
|
||
|
|.arch e2k
|
||
|
|.section code
|
||
|
dasm_init(&d, DASM_MAXSECTION);
|
||
|
|.globals lbl_
|
||
|
void* labels[lbl__MAX];
|
||
|
dasm_setupglobal(&d, labels, lbl__MAX);
|
||
|
|.actionlist bf_actions
|
||
|
dasm_setup(&d, bf_actions);
|
||
|
dasm_growpc(&d, npc);
|
||
|
|.define rArg1, r0
|
||
|
|.define rTmp1, r1
|
||
|
|.define aPtr, r2
|
||
|
|.define aState, r3
|
||
|
|.define aTapeBegin, r4
|
||
|
|.define aTapeEnd, r5
|
||
|
|.define cArg1, b0
|
||
|
|.define cArg2, b1
|
||
|
|.define cRet1, rArg1
|
||
|
|.type state, bf_state_t
|
||
|
dasm_State** Dst = &d;
|
||
|
|.code
|
||
|
|->bf_main:
|
||
|
|<
|
||
|
| setwd wsz = 0x8, nfx = 0x1, dbl = 0x0
|
||
|
| setbn rsz = 0x3, rbs = 0x4, rcur = 0x0
|
||
|
| ldd 0, rArg1, state->tape, aPtr
|
||
|
| addd 1, rArg1, 0x0, aState
|
||
|
|>
|
||
|
|<
|
||
|
| subd 0, aPtr, 0x1, aTapeBegin
|
||
|
| addd 1, aPtr, TAPE_SIZE-1, aTapeEnd
|
||
|
|>
|
||
|
for(;;) {
|
||
|
switch(*program++) {
|
||
|
case '<':
|
||
|
for(n = 1; *program == '<'; ++n, ++program);
|
||
|
|<
|
||
|
| disp ctpr1, >1
|
||
|
| subd 0, aPtr, n%TAPE_SIZE, aPtr
|
||
|
|>
|
||
|
| cmpbedb 0, aPtr, aTapeBegin, pred0
|
||
|
| addd 0, aPtr, TAPE_SIZE, aPtr, pred0
|
||
|
break;
|
||
|
case '>':
|
||
|
for(n = 1; *program == '>'; ++n, ++program);
|
||
|
|<
|
||
|
| disp ctpr1, >1
|
||
|
| addd 0, aPtr, n%TAPE_SIZE, aPtr
|
||
|
|>
|
||
|
| cmpbedb 0, aPtr, aTapeEnd, pred0
|
||
|
| subd 0, aPtr, TAPE_SIZE, aPtr, ~pred0
|
||
|
break;
|
||
|
case '+':
|
||
|
for(n = 1; *program == '+'; ++n, ++program);
|
||
|
| ldb 0, aPtr, 0x0, rTmp1
|
||
|
| addd 0, rTmp1, n, rTmp1
|
||
|
| stb 2, aPtr, 0x0, rTmp1
|
||
|
break;
|
||
|
case '-':
|
||
|
for(n = 1; *program == '-'; ++n, ++program);
|
||
|
| ldb 0, aPtr, 0x0, rTmp1
|
||
|
| subd 0, rTmp1, n, rTmp1
|
||
|
| stb 2, aPtr, 0x0, rTmp1
|
||
|
break;
|
||
|
case ',':
|
||
|
|<
|
||
|
| addd 0, aState, 0x0, cArg1
|
||
|
| ldd 2, aState, state->get_ch, rTmp1
|
||
|
|>
|
||
|
| movtd 0, rTmp1, ctpr1
|
||
|
| call ctpr1, wbs = 0x4
|
||
|
| stb 2, aPtr, 0x0, cRet1
|
||
|
break;
|
||
|
case '.':
|
||
|
|<
|
||
|
| ldb 0, aPtr, 0x0, cArg2
|
||
|
| addd 1, aState, 0x0, cArg1
|
||
|
| ldd 2, aState, state->put_ch, rTmp1
|
||
|
|>
|
||
|
| movtd 0, rTmp1, ctpr1
|
||
|
| call ctpr1, wbs = 0x4
|
||
|
break;
|
||
|
case '[':
|
||
|
if(nloops == MAX_NESTING)
|
||
|
bad_program("Nesting too deep");
|
||
|
if(program[0] == '-' && program[1] == ']') {
|
||
|
program += 2;
|
||
|
| addd 0, 0x0, 0x0, rTmp1
|
||
|
| stb 2, aPtr, 0x0, rTmp1
|
||
|
} else {
|
||
|
if(nextpc == npc) {
|
||
|
npc *= 2;
|
||
|
dasm_growpc(&d, npc);
|
||
|
}
|
||
|
|<
|
||
|
| disp ctpr1, =>nextpc+1
|
||
|
| ldb 0, aPtr, 0x0, rTmp1
|
||
|
|>
|
||
|
| cmpedb 0, rTmp1, 0x0, pred0
|
||
|
| ct ctpr1, pred0
|
||
|
|=>nextpc:
|
||
|
loops[nloops++] = nextpc;
|
||
|
nextpc += 2;
|
||
|
}
|
||
|
break;
|
||
|
case ']':
|
||
|
if(nloops == 0)
|
||
|
bad_program("] without matching [");
|
||
|
--nloops;
|
||
|
|<
|
||
|
| disp ctpr1, =>loops[nloops]
|
||
|
| ldb 0, aPtr, 0x0, rTmp1
|
||
|
|>
|
||
|
| cmpedb 0, rTmp1, 0x0, pred0
|
||
|
| ct ctpr1, ~pred0
|
||
|
|=>loops[nloops]+1:
|
||
|
break;
|
||
|
case 0:
|
||
|
if(nloops != 0)
|
||
|
program = "<EOF>", bad_program("[ without matching ]");
|
||
|
| return ctpr3
|
||
|
| ct ctpr3
|
||
|
link_and_encode(&d);
|
||
|
dasm_free(&d);
|
||
|
return (void(*)(bf_state_t*))labels[lbl_bf_main];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void bf_putchar(bf_state_t* s, unsigned char c)
|
||
|
{
|
||
|
putchar((int)c);
|
||
|
}
|
||
|
|
||
|
static unsigned char bf_getchar(bf_state_t* s)
|
||
|
{
|
||
|
return (unsigned char)getchar();
|
||
|
}
|
||
|
|
||
|
static void bf_run(const char* program)
|
||
|
{
|
||
|
bf_state_t state;
|
||
|
unsigned char tape[TAPE_SIZE] = {0};
|
||
|
state.tape = tape;
|
||
|
state.get_ch = bf_getchar;
|
||
|
state.put_ch = bf_putchar;
|
||
|
bf_compile(program)(&state);
|
||
|
}
|
||
|
|
||
|
int main(int argc, char** argv)
|
||
|
{
|
||
|
if(argc == 2) {
|
||
|
long sz;
|
||
|
char* program;
|
||
|
FILE* f = fopen(argv[1], "r");
|
||
|
if(!f) {
|
||
|
fprintf(stderr, "Cannot open %s\n", argv[1]);
|
||
|
return 1;
|
||
|
}
|
||
|
fseek(f, 0, SEEK_END);
|
||
|
sz = ftell(f);
|
||
|
program = (char*)malloc(sz + 1);
|
||
|
fseek(f, 0, SEEK_SET);
|
||
|
program[fread(program, 1, sz, f)] = 0;
|
||
|
fclose(f);
|
||
|
bf_run(program);
|
||
|
return 0;
|
||
|
} else {
|
||
|
fprintf(stderr, "Usage: %s INFILE.bf\n", argv[0]);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|