Huge work; i'm too tired to write commit message for all of it. Pleez let me sleep acouple of hours. Made most tests work. Started SBC.

This commit is contained in:
egormanga
2020-03-18 07:00:12 +03:00
parent 7badd31e42
commit 27d951227d
51 changed files with 3004 additions and 717 deletions

50
SBC/builtins.c Normal file
View File

@@ -0,0 +1,50 @@
#include <stdarg.h>
#include <string.h>
typedef struct atom {
enum {b, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128} type;
union {
_Bool* b;
int8_t* i8;
uint8_t* u8;
int16_t* i16;
uint16_t* u16;
int32_t* i32;
uint32_t* u32;
int64_t* i64;
uint64_t* u64;
void* data;
};
} atom_t;
typedef atom_t (*builtin_function)(int nargs, atom_t args[nargs]);
typedef struct builtin {
const char* name;
builtin_function fp;
} builtin_t;
/// XXX ///
atom_t _builtin_println(int nargs, atom_t args[nargs]) {
static int res;
res = puts(args[0].data);
return (atom_t){i32, .i32 = &res};
}
/// XXX ///
builtin_t builtins[] = {
{"println", _builtin_println},
{NULL, NULL}};
builtin_function get_builtin(const char* name) {
for (builtin_t* i = builtins; i->name != NULL; i++)
if (strcmp(i->name, name) == 0)
return i->fp;
return NULL;
}

View File

@@ -6,9 +6,22 @@
### NOP
0x00
> Does nothing.
### RET
### END
0x01
> Closes previously opened block.
### POP
0x02
> Drops `TOS`.
### RET
0x03
> Returns `TOS` to caller.
### BLTIN
0x04
> Reads string from the bytecode until null byte (max 255 bytes) and pushes builtin function with that name.
### CODE
0x05
> Reads code from the bytecode until the corresponding `END` instruction and pushes a reference to it and then its length.
> Opens a block.
## Unary
@@ -44,7 +57,7 @@
0x19
> Pushes integer of smallest possible width equal to rounded `TOS`.
### CTOS
0x1a
0x1A
> Pushes string consisting of char `TOS` and a null byte.
@@ -62,7 +75,7 @@
### DIV
0x23
> Pushes `TOS1 / TOS`.
### FLRDIV
### IDIV
0x24
> Pushes `TOS1 // TOS`.
### MOD
@@ -71,46 +84,88 @@
### POW
0x26
> Pushes `TOS1 ** TOS`.
### SHL
### LSH
0x27
> Pushes `TOS1 << TOS`.
### SHR
### RSH
0x28
> Pushes `TOS1 >> TOS`.
### AND
0x29
> Pushes `TOS1 & TOS`.
### OR
0x2a
0x2A
> Pushes `TOS1 | TOS`.
### XOR
0x2b
0x2B
> Pushes `TOS1 ^ TOS`.
## Comparisons
### EQ
0x30
> Pushes `TOS1 == TOS`.
### NE
0x31
> Pushes `TOS1 != TOS`.
### LT
0x32
> Pushes `TOS1 < TOS`.
### GT
0x33
> Pushes `TOS1 > TOS`.
### LE
0x34
> Pushes `TOS1 <= TOS`.
### GE
0x35
> Pushes `TOS1 >= TOS`.
### IS
0x36
> Pushes `TOS1 is TOS`.
### ISNOT
0x37
> Pushes `TOS1 is not TOS`.
## Flow control
### IF
0x40
> If `TOS` is false, skips bytecode until corresponding `ELSE` (if exists) or `END`.
> Opens a block.
### ELSE
0x41
> Pops last `IF` result from `IF`-stack, and if it is true, skips bytecode to corresponding `END`.
> Opens a block.
### EXEC
0x42
> Executes code block `TOS1` of length `TOS` and pushes the result.
## With argument
### ALLOC*(bytes)*
0xa0
0xA0
> Pushes reference to `calloc(1, bytes)`.
### EXTEND*(bytes)*
0xa1
0xA1
> Extends integer `TOS` width to `bytes` bytes if narrower.
### CONST*(bytes)*
0xa2
0xA2
> Reads next `bytes` bytes of bytecode and pushes a reference to a copy of them.
### JUMPF*(offset)*
0xa3
0xA3
> Jumps `offset` bytes of bytecode forward.
### JUMPB*(offset)*
0xa4
0xA4
> Jumps `offset` bytes of bytecode backward.
### SCPGET*(cell)*
0xa5
0xA5
> Pushes the value of cell `cell` of local scope variables array.
### SCPSET*(cell)*
0xa6
0xA6
> Sets the value of cell `cell` of local scope variables array to `TOS`.
### CALL*(nargs)*
0xa7
> Pops `nargs ^ (1 << 7)` TODO
0xA7
> Calls `TOS` with `nargs` arguments popped from stack (below the callable).

50
SBC/dissbc.py Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/python3
# SBC Disassembler
from utils.nolog import *
opcodes = {int(m[2], 16): m[1] for m in re.finditer(r'### (\w+).*?\n(\w+)', open(os.path.join(os.path.dirname(sys.argv[0]), 'bytecode.md')).read())}
HASARG = 0xa0
def dissbc(code, no_opnames=False):
cp = 0
codesize = len(code)
blocklvl = 0
while (cp < codesize):
opcode = code[cp]; cp += 1
opname = opcodes.get(opcode)
if (opname == 'END'): blocklvl -= 1
print('\t'*blocklvl, end='')
if (opname is None): print("\033[2mUNKNOWN: %02x\033[0m" % opcode); continue
print(f"\033[1m0x{opcode:02x}{' '+opname if (not no_opnames) else ''}\033[0m", end='')
if (opcode > HASARG):
arg = code[cp]; cp += 1
print("(%d|0x%02x)" % (arg, arg), end='')
if (opname == 'CONST'):
const = code[cp:cp+arg]
cp += arg
print(':', '0x'+const.hex(), end='')
print(' |', str().join(chr(i) if (32 <= i < 128) else '.' for i in const), end='')
elif (opname == 'BLTIN'):
name = bytearray()
while (code[cp] != 0):
name.append(code[cp]); cp += 1
else: cp += 1
print(':', name.decode('ascii'), end='')
if (opname in ('IF', 'ELSE', 'CODE')):
print(':', end='')
blocklvl += 1
print()
@apmain
@aparg('file', metavar='<file.sbc>')
@aparg('--no-opnames', action='store_true')
def main(cargs):
dissbc(open(cargs.file, 'rb').read(), no_opnames=cargs.no_opnames)
if (__name__ == '__main__'): exit(main(nolog=True), nolog=True)
# by Sdore, 2020

BIN
SBC/sbc Executable file

Binary file not shown.

217
SBC/sbc.c Normal file
View File

@@ -0,0 +1,217 @@
// SBC
#define _GNU_SOURCE
#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "builtins.c"
#include "stack.c"
typedef uint8_t code_t;
enum {
// Standalone
NOP = 0x00,
END = 0x01,
POP = 0x02,
RET = 0x03,
BLTIN = 0x04,
CODE = 0x05,
// Unary
POS = 0x10,
NEG = 0x11,
NOT = 0x12,
INV = 0x13,
ATOI = 0x14,
ITOA = 0x15,
ITOF = 0x16,
CEIL = 0x17,
FLR = 0x18,
RND = 0x19,
CTOS = 0x1A,
// Binary
ADD = 0x20,
SUB = 0x21,
MUL = 0x22,
DIV = 0x23,
IDIV = 0x24,
MOD = 0x25,
POW = 0x26,
SHL = 0x27,
SHR = 0x28,
AND = 0x29,
OR = 0x2A,
XOR = 0x2B,
// Comparisons
EQ = 0x30,
NE = 0x31,
LT = 0x32,
GT = 0x33,
LE = 0x34,
GE = 0x35,
IS = 0x36,
ISNOT = 0x37,
// Flow control
IF = 0x40,
ELSE = 0x41,
EXEC = 0x42,
// With argument
ALLOC = 0xA0,
EXTEND = 0xA1,
CONST = 0xA2,
JUMPF = 0xA3,
JUMPB = 0xA4,
SCPGET = 0xA5,
SCPSET = 0xA6,
CALL = 0xA7,
HASARG = 0xA0,
};
atom_t exec(code_t* code, uint32_t codesize) { // TODO: freeing
stack_t* st = stack();
atom_t scp[255]; // TODO
code_t* cb[255]; // TODO
uint32_t cbi = 0;
uint32_t cp = 0;
while (cp < codesize) {
code_t opcode = code[cp++];
uint32_t ocp = cp;
switch (opcode) {
// Standalone
case NOP: break;
case END: break;
case POP: stack_pop(st); break;
case RET: return st->top->data;
case BLTIN: {
char name[256];
strncpy(name, (char*)code+cp, 255);
while (code[cp++] != '\0');
stack_push(st, (atom_t){.data = get_builtin(name)});
}; break;
case CODE: {
code_t* code_block = malloc(codesize);
static uint32_t cbp = 0;
uint32_t blocklvl = 1;
while (cp < codesize) {
code_t c = code[cp++];
if (c == CODE ||
c == IF ||
c == ELSE) blocklvl++;
else if (c == END) blocklvl--;
if (blocklvl <= 0) break;
code_block[cbp++] = c;
if (c > HASARG) code_block[cbp++] = code[cp++];
if (c == CONST)
for (uint8_t i = code_block[cbp-1]; i > 0; i--)
code_block[cbp++] = code[cp++];
else if (c == BLTIN)
do code_block[cbp++] = code[cp++];
while (code[cp-1] != '\0');
}
cb[cbi++] = realloc(code_block, cbp);
free(code_block);
stack_push(st, (atom_t){u32, .u32 = &cbp});
stack_push(st, (atom_t){.data = &cb[cbi-1]});
}; break;
// Unary
case POS: *st->top->data.i32 = abs(*st->top->data.i32); break;
case ITOA: {
char s[12];
fprintf(stderr, "-- %x\n", *st->top->data.i32);
snprintf(s, sizeof(s)/sizeof(*s), "%d", *st->top->data.i32);
st->top->data.data = strdup(s);
}; break;
// Binary (TODO)
case ADD: *st->top->data.i32 += *stack_pop(st).i32; break;
case SUB: *st->top->data.i32 -= *stack_pop(st).i32; break;
// Comparisons
case LT: *st->top->data.b = *stack_pop(st).i32 > *st->top->data.i32; break;
// Flow control
case EXEC: {
uint32_t exec_codesize = *stack_pop(st).u32;
code_t* exec_code = stack_pop(st).data;
stack_push(st, exec(exec_code, exec_codesize));
}; break;
// With argument
case CONST: {
uint8_t len = code[cp++];
stack_push(st, (atom_t){.data = memcpy(malloc(len), code+cp, len)});
fprintf(stderr, "-- l=%02x: %x\n", len, *st->top->data.i32);
cp += len;
}; break;
case SCPGET: {
uint8_t cell = code[cp++];
stack_push(st, scp[cell]);
}; break;
case SCPSET: {
uint8_t cell = code[cp++];
scp[cell] = stack_pop(st);
fprintf(stderr, "-- c%d = %d\n", cell, *scp[cell].i32);
}; break;
case CALL: {
uint8_t nargs = code[cp++];
fprintf(stderr, "-- nargs=%d\n", nargs);
builtin_function func = stack_pop(st).data;
atom_t args[nargs];
for (uint8_t i = 0; i < nargs; i++)
args[i] = stack_pop(st);
stack_push(st, func(nargs, args));
}; break;
default: fprintf(stderr, "Not Implemented opcode: 0x%02x\n", opcode); exit(3);
}
fprintf(stderr, "[%02x", opcode);
if (opcode > HASARG) fprintf(stderr, "(%d|0x%02x)", code[ocp], code[ocp++]);
fputc(':', stderr);
do fprintf(stderr, " %02x", code[ocp++]);
while (ocp < cp && ocp < codesize);
if (st->top != NULL) {
fprintf(stderr, ", TOS = %u(0x%02x)", *st->top->data.i32, *st->top->data.i32);
if (isprint(*(char*)st->top->data.data)) {
fprintf(stderr, " | ");
for (char* p = st->top->data.data; *p != '\0'; p++)
fputc(isprint(*p)?*p:'.', stderr);
}
}
fprintf(stderr, "]\n");
}
return (atom_t){.data = NULL};
}
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file.sbc>\n", basename(argv[0]));
exit(1);
}
FILE* fd = fopen(argv[1], "rb");
fseek(fd, 0, SEEK_END);
long fsize = ftell(fd);
fseek(fd, 0, SEEK_SET);
code_t code[fsize];
fread(code, sizeof(*code), fsize, fd);
fclose(fd);
exec(code, fsize);
}
// by Sdore, 2020

53
SBC/stack.c Normal file
View File

@@ -0,0 +1,53 @@
/*
#define CAT(a, b) a##_##b
#define TEMPLATE(name, type) CAT(name, type)
#define stack TEMPLATE(stack, T)
#define stack_t TEMPLATE(stack_t, T)
#define stack_item TEMPLATE(stack_item, T)
#define stack_push TEMPLATE(stack_push, T)
#define stack_pop TEMPLATE(stack_pop, T)
*/
#define T atom_t
typedef struct stack {
struct stack_item* top;
} stack_t;
struct stack_item {
T data;
struct stack_item* below;
};
stack_t* stack() {
stack_t* st = malloc(sizeof(*st));
st->top = NULL;
return st;
}
void stack_push(stack_t* st, T data) {
struct stack_item* new = malloc(sizeof(*new));
new->data = data;
if (st->top != NULL) new->below = st->top;
st->top = new;
}
T stack_pop(stack_t* st) {
assert (st->top != NULL);
struct stack_item* item = st->top;
st->top = item->below;
T data = item->data;
free(item);
return data;
}
/*
#undef stack
#undef stack_t
#undef stack_item
#undef stack_push
#undef stack_pop
*/
#undef T

Binary file not shown.