aboutsummaryrefslogtreecommitdiff
path: root/pu3/pu3.cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'pu3/pu3.cpu.c')
-rw-r--r--pu3/pu3.cpu.c143
1 files changed, 101 insertions, 42 deletions
diff --git a/pu3/pu3.cpu.c b/pu3/pu3.cpu.c
index a10ca3f..40f395f 100644
--- a/pu3/pu3.cpu.c
+++ b/pu3/pu3.cpu.c
@@ -1,4 +1,5 @@
-#define _POSIX_C_SOURCE 200809L
+#define _GNU_SOURCE
+#define inline __attribute__((always_inline))
//
#include <stdint.h>
@@ -66,6 +67,28 @@ typedef uint64_t pu3_inst_t;
#define set_data_u16(__v, __x) __v = (((__v) & 0xFFFF0000FFFFFFFFULL) | prep_data_u16(__x))
#define set_dest_u32(__v, __x) __v = (((__v) & 0xFFFFFFFF00000000ULL) | prep_dest_u32(__x))
+// clang-format off
+struct {
+ uint32_t nop, hlt, mov, add, sub, inc, dec, mul, lsh, rsh, and, not, jmp, jnz, dmv, ddm;
+} opcode = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+typedef uint32_t pu3_opcode;
+// clang-format on
+
+inline pu3_inst_t build_inst(bool swap, bool deref, pu3_opcode opcode, uint32_t lhs, uint16_t rhs) {
+ pu3_inst_t output = 0;
+ byte spare = ((swap & 1) << 7) | ((deref & 1) << 6);
+ byte op = opcode & 0b1111;
+
+ set_spare(output, spare);
+ set_scroll_byte(output, op);
+ set_data_u16(output, rhs);
+ set_dest_u32(output, lhs);
+
+ return output;
+}
+
typedef struct {
pu3_inst_t *memory_u64;
uint32_t *memory_u32;
@@ -76,7 +99,7 @@ typedef struct {
uint32_t mem_size;
uint32_t *pc;
- uint32_t *time_slice;
+ uint32_t time_slice;
} pu3_ctx;
// instruct packing:
@@ -87,25 +110,37 @@ typedef struct {
// uint32_t dest;
// } pu3_inst_t;
-// instructions
-// 0: nop
-// 1: hlt
-// 2: mov [target], src
-// 3: add [target], src
-// 4: sub [target], src
-// 5: inc [target], ___
-// 6: dec [target], ___
-// 7: mul [target], src
-// 8: lsh [target], src
-// 9: rsh [target], src
-// 10: and [target], src
-// 11: not [target], src
-// 12: jmp [target], src
-// 13: jnz [target], src
-// 14: ???
-// 15: ???
-
-#define branchless(c, x, y) (((-(!!(c))) & (x)) | (~(-(!!(c))) & (y)))
+// ------------------------------------------------------------------------------------------
+// instructions list
+// 0: nop ________, ___ (( advances program counter by lhs ))
+// 1: hlt ________, ___ (( stops the cpu ))
+// 2: mov [target], src (( moves rhs to lhs ))
+// 3: add [target], src (( adds rhs to lhs ))
+// 4: sub [target], src (( subtracts rhs from lhs ))
+// 5: inc [target], ___ (( increments lhs ))
+// 6: dec [target], ___ (( decrements lhs ))
+// 7: mul [target], src (( multiplies lhs by rhs ))
+// 8: lsh [target], src (( left shifts lhs by rhs ))
+// 9: rsh [target], src (( right shifts lhs by rhs ))
+// 10: and [target], src (( logical ANDs lhs with rhs ))
+// 11: not [target], src (( stores !*lhs in lhs ))
+// 12: jmp [target], src (( jumps to lhs ))
+// 13: jnz [target], src (( jumps to lhs if rhs is non-zero ))
+// 14: dmv [target], ___ (( sets lhs to *lhs ))
+// 15: ddm [target], src (( set m[m[lhs]] to m[rhs] ))
+// ------------------------------------------------------------------------------------------
+// explanation
+// we see lhs and rhs. these are, in order, the u32 dest and the u16 data.
+// the following are executed, in order, in each cycle (if required):
+// 1. lhs and rhs are swapped (to allow access to all of the u32 memory range)-
+// -if the leftmost bit of spare is set. (spare & 0b10000000 != 0)
+// 2. lhs is converted to memory[ lhs ] unconditionally
+// 3. rhs is converted to memory[ rhs ] only if the second leftmmost bit of spare is set.
+// (spare & 0b01000000 != 0)
+// ------------------------------------------------------------------------------------------
+// WARNING: the fields left blank in the instruction list are NOT PROCESSED,
+// this means they're ignored even if there's a dereference or a swap.
+// ------------------------------------------------------------------------------------------
#define prep_raw() \
({ \
@@ -115,17 +150,23 @@ typedef struct {
#define prep_vals() \
({ \
spare = get_spare(cur); \
- lhs = get_dest_u32(cur); \
- rhs = (uint32_t) get_data_u16(cur); \
- if (spare & 0x40) rhs = mem32[ rhs ]; \
if (unlikely(spare & 0x80)) { \
- out = lhs; \
- lhs = rhs; \
- rhs = out; \
+ rhs = get_dest_u32(cur); \
+ lhs = (uint32_t) get_data_u16(cur); \
+ } else { \
+ lhs = get_dest_u32(cur); \
+ rhs = (uint32_t) get_data_u16(cur); \
} \
+ if (spare & 0x40) rhs = mem32[ rhs ]; \
})
#define prep_only_left() ({ lhs = get_dest_u32(cur); })
+// spare = lhs;
+// spare = *spare;
+//
+
+void disassemble_pu3_inst(pu3_inst_t inst);
+
pu3(pu3_ctx ctx) {
uint64_t i = 0;
pu3_inst_t cur;
@@ -150,7 +191,12 @@ pu3(pu3_ctx ctx) {
pu3_start:
i++;
+ if (unlikely(ctx.time_slice == i)) goto pu3_inst_1;
cur = *pc++;
+
+ // printf("%li: ", pc - mem64 - 1);
+ // disassemble_pu3_inst(cur);
+
#ifndef PU3_NO_INTRINSICS
__builtin_prefetch(pc + 1, 0, 3);
#endif
@@ -210,8 +256,8 @@ pu3_inst_10: // and
goto pu3_start;
pu3_inst_11: // not
- prep_vals();
- mem32[ lhs ] = !rhs;
+ prep_only_left();
+ mem32[ lhs ] = !mem32[ lhs ];
goto pu3_start;
pu3_inst_12: // jmp
@@ -228,16 +274,19 @@ pu3_inst_14: // dmv
prep_vals();
mem32[ lhs ] = mem32[ mem32[ lhs ] ];
goto pu3_start;
-pu3_inst_15: // unused
+
+pu3_inst_15: // ddm
+ prep_vals();
+ mem32[ mem32[ lhs ] ] = mem32[ rhs ];
goto pu3_start;
pu3_inst_1: // hlt
*ctx.pc = pc - mem64;
- return *ctx.time_slice - i;
+ return ctx.time_slice - i;
}
const string op_names[] = { "nop", "hlt", "mov", "add", "sub", "inc", "dec", "mul",
- "lsh", "rsh", "and", "not", "jmp", "jnz", "dmv", "???" };
+ "lsh", "rsh", "and", "not", "jmp", "jnz", "dmv", "ddm" };
// bench: measure using clock()
double bench_cycle(byte instr, pu3_ctx ctx) {
@@ -277,24 +326,34 @@ double bench_cycle(byte instr, pu3_ctx ctx) {
return insts_per_s / 1e6; // return M inst/s
}
-int main() {
- const uint32_t SIZE = 1 << 23;
+#define S 1 << 23
+
+pu3_ctx *init_pu3() {
+ const uint32_t SIZE = S;
let mem_arr = calloc(SIZE, sizeof(pu3_inst_t));
if (!mem_arr) {
perror("memory allocation failed");
- return 1;
+ return NULL;
}
- pu3_ctx ctx = { .memory = { .memory_u64 = mem_arr, .memory_u32 = mem_arr },
- .mem_size = SIZE,
- .pc = calloc(1, sizeof(uint32_t)),
- .time_slice = calloc(1, sizeof(uint32_t)) };
+ pu3_ctx *ctx = calloc(1, sizeof(pu3_ctx));
+ *ctx = (pu3_ctx) { .memory = { .memory_u64 = mem_arr, .memory_u32 = mem_arr },
+ .mem_size = SIZE,
+ .pc = calloc(1, sizeof(uint32_t)),
+ .time_slice = 0 };
+
+ return ctx;
+}
+
+pu3_benchmark() {
+ pu3_ctx *__ctx = init_pu3();
+ pu3_ctx ctx = *__ctx;
int i;
int total = 0;
double benchs = 0;
- for (i = 0; i < 15; i++) {
+ for (i = 0; i < 16; i++) {
if (i == 1 || i == 12 || i == 13) continue;
total++;
benchs += bench_cycle(i, ctx);
@@ -305,8 +364,8 @@ int main() {
"TOTAL: %fMHz\n",
benchs / total);
- free(mem_arr);
+ free(ctx.memory.memory_u32);
free(ctx.pc);
- free(ctx.time_slice);
+ free(__ctx);
return 0;
} \ No newline at end of file