mirror of
https://github.com/egormanga/Slang.git
synced 2025-10-15 00:17:16 +03:00
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:
50
SBC/builtins.c
Normal file
50
SBC/builtins.c
Normal 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;
|
||||
}
|
@@ -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
50
SBC/dissbc.py
Executable 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
|
217
SBC/sbc.c
Normal file
217
SBC/sbc.c
Normal 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
53
SBC/stack.c
Normal 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
|
BIN
SBC/tests/hw.sbc
BIN
SBC/tests/hw.sbc
Binary file not shown.
Reference in New Issue
Block a user