commit dc25e4200eeff678683392c400adeef6f13cc0e8
parent 8ecfb0be1f42f3f88bc7117830ed054dd9ebe8d0
Author: Brian Swetland <swetland@frotz.net>
Date: Mon, 2 Mar 2020 18:49:14 -0800
import Peter Dr Wachter's risc5 emulator
from: git@github.com:pdewacht/oberon-risc-emu.git
rev: e7bf0bb74a29c532bc867d17bda3b19f7f6ba5e6
Just the core, removing all the project oberon peripherals.
Will add "syscalls" for open/close/read/write file io
Diffstat:
6 files changed, 600 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-all: bin/tlc bin/fs bin/r5d bin/mkinstab
+all: bin/tlc bin/fs bin/r5d bin/r5e bin/mkinstab
clean:
rm -rf bin out
@@ -15,9 +15,13 @@ bin/fs: src/fs.c src/fs.h
@mkdir -p bin
$(CC) -o $@ $(CFLAGS) src/fs.c
-bin/r5d: src/r5d.c src/risc5dis.c src/risc5.h out/risc5ins.h
+bin/r5d: src/r5d.c src/risc5dis.c
@mkdir -p bin
- $(CC) -o $@ $(CFLAGS) src/r5d.c src/risc5dis.c src/risc5.h
+ $(CC) -o $@ $(CFLAGS) src/r5d.c src/risc5dis.c
+
+bin/r5e: src/r5e.c src/risc5emu.c src/risc5emu-fp.c src/risc5dis.c
+ @mkdir -p bin
+ $(CC) -o $@ $(CFLAGS) src/r5e.c src/risc5emu.c src/risc5emu-fp.c src/risc5dis.c
bin/mkinstab: src/mkinstab.c
@mkdir -p bin
@@ -27,3 +31,9 @@ out/risc5ins.h: src/risc5ins.txt bin/mkinstab
@mkdir -p out
bin/mkinstab < src/risc5ins.txt > $@
+src/risc5dis.c: src/risc5.h out/risc5ins.h
+src/risc5emu.c: src/risc5emu.h src/risc5.h
+src/risc5emu-fp.c: src/risc5emu-fp.h
+src/r5d.c: src/risc5.h
+src/r5e.c: src/risc5emu.h
+
diff --git a/src/r5e.c b/src/r5e.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+
+#include "risc5emu.h"
+
+uint32_t data[] = {
+ 0x40000030, // mov r0, 0x40
+ 0x51000100, // mov r1, 0xffff0100
+ 0xA0100000, // sw r0, [r1, 0]
+ 0xE7FFFFFE, // b -3
+};
+
+int main(int argc, char** argv) {
+ risc_t *r = risc_new();
+
+ for (unsigned n = 0; n < sizeof(data); n += 4) {
+ risc_store_word(r, n, data[n/4]);
+ }
+
+ risc_run(r, 100);
+ return 0;
+}
diff --git a/src/risc5emu-fp.c b/src/risc5emu-fp.c
@@ -0,0 +1,172 @@
+// Copyright © 2014 Peter De Wachter
+//
+// Permission to use, copy, modify, and/or distribute this software for
+// any purpose with or without fee is hereby granted, provided that the
+// above copyright notice and this permission notice appear in all
+// copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// based on src/risc-fp.c from git@github.com:pdewacht/oberon-risc-emu.git
+
+#include "risc5emu-fp.h"
+
+uint32_t fp_add(uint32_t x, uint32_t y, bool u, bool v) {
+ bool xs = (x & 0x80000000) != 0;
+ uint32_t xe;
+ int32_t x0;
+ if (!u) {
+ xe = (x >> 23) & 0xFF;
+ uint32_t xm = ((x & 0x7FFFFF) << 1) | 0x1000000;
+ x0 = (int32_t)(xs ? -xm : xm);
+ } else {
+ xe = 150;
+ x0 = (int32_t)(x & 0x00FFFFFF) << 8 >> 7;
+ }
+
+ bool ys = (y & 0x80000000) != 0;
+ uint32_t ye = (y >> 23) & 0xFF;
+ uint32_t ym = ((y & 0x7FFFFF) << 1);
+ if (!u && !v) ym |= 0x1000000;
+ int32_t y0 = (int32_t)(ys ? -ym : ym);
+
+ uint32_t e0;
+ int32_t x3, y3;
+ if (ye > xe) {
+ uint32_t shift = ye - xe;
+ e0 = ye;
+ x3 = shift > 31 ? x0 >> 31 : x0 >> shift;
+ y3 = y0;
+ } else {
+ uint32_t shift = xe - ye;
+ e0 = xe;
+ x3 = x0;
+ y3 = shift > 31 ? y0 >> 31 : y0 >> shift;
+ }
+
+ uint32_t sum = ((xs << 26) | (xs << 25) | (x3 & 0x01FFFFFF))
+ + ((ys << 26) | (ys << 25) | (y3 & 0x01FFFFFF));
+
+ uint32_t s = (((sum & (1 << 26)) ? -sum : sum) + 1) & 0x07FFFFFF;
+
+ uint32_t e1 = e0 + 1;
+ uint32_t t3 = s >> 1;
+ if ((s & 0x3FFFFFC) != 0) {
+ while ((t3 & (1<<24)) == 0) {
+ t3 <<= 1;
+ e1--;
+ }
+ } else {
+ t3 <<= 24;
+ e1 -= 24;
+ }
+
+ bool xn = (x & 0x7FFFFFFF) == 0;
+ bool yn = (y & 0x7FFFFFFF) == 0;
+
+ if (v) {
+ return (int32_t)(sum << 5) >> 6;
+ } else if (xn) {
+ return (u | yn) ? 0 : y;
+ } else if (yn) {
+ return x;
+ } else if ((t3 & 0x01FFFFFF) == 0 || (e1 & 0x100) != 0) {
+ return 0;
+ } else {
+ return ((sum & 0x04000000) << 5) | (e1 << 23) | ((t3 >> 1) & 0x7FFFFF);
+ }
+}
+
+uint32_t fp_mul(uint32_t x, uint32_t y) {
+ uint32_t sign = (x ^ y) & 0x80000000;
+ uint32_t xe = (x >> 23) & 0xFF;
+ uint32_t ye = (y >> 23) & 0xFF;
+
+ uint32_t xm = (x & 0x7FFFFF) | 0x800000;
+ uint32_t ym = (y & 0x7FFFFF) | 0x800000;
+ uint64_t m = (uint64_t)xm * ym;
+
+ uint32_t e1 = (xe + ye) - 127;
+ uint32_t z0;
+ if ((m & (1ULL << 47)) != 0) {
+ e1++;
+ z0 = ((m >> 23) + 1) & 0xFFFFFF;
+ } else {
+ z0 = ((m >> 22) + 1) & 0xFFFFFF;
+ }
+
+ if (xe == 0 || ye == 0) {
+ return 0;
+ } else if ((e1 & 0x100) == 0) {
+ return sign | ((e1 & 0xFF) << 23) | (z0 >> 1);
+ } else if ((e1 & 0x80) == 0) {
+ return sign | (0xFF << 23) | (z0 >> 1);
+ } else {
+ return 0;
+ }
+}
+
+uint32_t fp_div(uint32_t x, uint32_t y) {
+ uint32_t sign = (x ^ y) & 0x80000000;
+ uint32_t xe = (x >> 23) & 0xFF;
+ uint32_t ye = (y >> 23) & 0xFF;
+
+ uint32_t xm = (x & 0x7FFFFF) | 0x800000;
+ uint32_t ym = (y & 0x7FFFFF) | 0x800000;
+ uint32_t q1 = (uint32_t)(xm * (1ULL << 25) / ym);
+
+ uint32_t e1 = (xe - ye) + 126;
+ uint32_t q2;
+ if ((q1 & (1 << 25)) != 0) {
+ e1++;
+ q2 = (q1 >> 1) & 0xFFFFFF;
+ } else {
+ q2 = q1 & 0xFFFFFF;
+ }
+ uint32_t q3 = q2 + 1;
+
+ if (xe == 0) {
+ return 0;
+ } else if (ye == 0) {
+ return sign | (0xFF << 23);
+ } else if ((e1 & 0x100) == 0) {
+ return sign | ((e1 & 0xFF) << 23) | (q3 >> 1);
+ } else if ((e1 & 0x80) == 0) {
+ return sign | (0xFF << 23) | (q2 >> 1);
+ } else {
+ return 0;
+ }
+}
+
+struct idiv idiv(uint32_t x, uint32_t y, bool signed_div) {
+ bool sign = ((int32_t)x < 0) & signed_div;
+ uint32_t x0 = sign ? -x : x;
+
+ uint64_t RQ = x0;
+ for (int S = 0; S < 32; ++S) {
+ uint32_t w0 = (uint32_t)(RQ >> 31);
+ uint32_t w1 = w0 - y;
+ if ((int32_t)w1 < 0) {
+ RQ = ((uint64_t)w0 << 32) | ((RQ & 0x7FFFFFFFU) << 1);
+ } else {
+ RQ = ((uint64_t)w1 << 32) | ((RQ & 0x7FFFFFFFU) << 1) | 1;
+ }
+ }
+
+ struct idiv d = { (uint32_t)RQ, (uint32_t)(RQ >> 32) };
+ if (sign) {
+ d.quot = -d.quot;
+ if (d.rem) {
+ d.quot -= 1;
+ d.rem = y - d.rem;
+ }
+ }
+ return d;
+}
diff --git a/src/risc5emu-fp.h b/src/risc5emu-fp.h
@@ -0,0 +1,32 @@
+// Copyright © 2014 Peter De Wachter
+//
+// Permission to use, copy, modify, and/or distribute this software for
+// any purpose with or without fee is hereby granted, provided that the
+// above copyright notice and this permission notice appear in all
+// copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// based on src/risc-fp.h from git@github.com:pdewacht/oberon-risc-emu.git
+
+#ifndef RISC_FP_H
+#define RISC_FP_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+uint32_t fp_add(uint32_t x, uint32_t y, bool u, bool v);
+uint32_t fp_mul(uint32_t x, uint32_t y);
+uint32_t fp_div(uint32_t x, uint32_t y);
+
+struct idiv { uint32_t quot, rem; };
+struct idiv idiv(uint32_t x, uint32_t y, bool signed_div);
+
+#endif // RISC_FP_H
diff --git a/src/risc5emu.c b/src/risc5emu.c
@@ -0,0 +1,345 @@
+// Copyright © 2014 Peter De Wachter
+//
+// Permission to use, copy, modify, and/or distribute this software for
+// any purpose with or without fee is hereby granted, provided that the
+// above copyright notice and this permission notice appear in all
+// copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// based on src/risc5.c from git@github.com:pdewacht/oberon-risc-emu.git
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "risc5emu.h"
+#include "risc5emu-fp.h"
+#include "risc5.h"
+
+static void disasm(uint32_t pc, uint32_t ins) {
+ char buf[256];
+ risc5dis(pc, ins, buf);
+ printf("%08x: %08x %s\n", pc, ins, buf);
+}
+
+// Our memory layout is slightly different from the FPGA implementation:
+// The FPGA uses a 20-bit address bus and thus ignores the top 12 bits,
+// while we use all 32 bits. This allows us to have more than 1 megabyte
+// of RAM.
+
+#define DefaultMemSize 0x00100000
+#define IOStart 0xFFFF0000
+
+struct RISC {
+ uint32_t PC;
+ uint32_t R[16];
+ uint32_t H;
+ bool Z, N, C, V;
+
+ uint32_t *RAM;
+ uint32_t mem_size;
+};
+
+enum {
+ MOV, LSL, ASR, ROR,
+ AND, ANN, IOR, XOR,
+ ADD, SUB, MUL, DIV,
+ FAD, FSB, FML, FDV,
+};
+
+static uint32_t risc_load_io(struct RISC *risc, uint32_t address);
+static void risc_store_io(struct RISC *risc, uint32_t address, uint32_t value);
+
+struct RISC *risc_new() {
+ struct RISC *risc = calloc(1, sizeof(*risc));
+ risc->mem_size = DefaultMemSize;
+ risc->RAM = calloc(1, risc->mem_size);
+ risc_reset(risc);
+ return risc;
+}
+
+void risc_reset(struct RISC *risc) {
+ risc->PC = 0;
+}
+
+void risc_run(struct RISC *risc, int cycles) {
+ // The progress value is used to detect that the RISC cpu is busy
+ // waiting on the millisecond counter or on the keyboard ready
+ // bit. In that case it's better to just pause emulation until the
+ // next frame.
+ for (int i = 0; i < cycles; i++) {
+ risc_single_step(risc);
+ }
+}
+
+void risc_single_step(struct RISC *risc) {
+ uint32_t ir;
+ if (risc->PC < risc->mem_size / 4) {
+ ir = risc->RAM[risc->PC];
+ } else {
+ fprintf(stderr, "Branched into the void (PC=0x%08X), resetting...\n", risc->PC);
+ risc_reset(risc);
+ return;
+ }
+
+ disasm(risc->PC << 2, ir);
+ risc->PC++;
+
+ const uint32_t pbit = 0x80000000;
+ const uint32_t qbit = 0x40000000;
+ const uint32_t ubit = 0x20000000;
+ const uint32_t vbit = 0x10000000;
+
+ if ((ir & pbit) == 0) {
+ // Register instructions
+ uint32_t a = (ir & 0x0F000000) >> 24;
+ uint32_t b = (ir & 0x00F00000) >> 20;
+ uint32_t op = (ir & 0x000F0000) >> 16;
+ uint32_t im = ir & 0x0000FFFF;
+ uint32_t c = ir & 0x0000000F;
+
+ uint32_t a_val, b_val, c_val;
+ b_val = risc->R[b];
+ if ((ir & qbit) == 0) {
+ c_val = risc->R[c];
+ } else if ((ir & vbit) == 0) {
+ c_val = im;
+ } else {
+ c_val = 0xFFFF0000 | im;
+ }
+
+ switch (op) {
+ case MOV: {
+ if ((ir & ubit) == 0) {
+ a_val = c_val;
+ } else if ((ir & qbit) != 0) {
+ a_val = c_val << 16;
+ } else if ((ir & vbit) != 0) {
+ a_val = 0xD0 | // ???
+ (risc->N * 0x80000000U) |
+ (risc->Z * 0x40000000U) |
+ (risc->C * 0x20000000U) |
+ (risc->V * 0x10000000U);
+ } else {
+ a_val = risc->H;
+ }
+ break;
+ }
+ case LSL: {
+ a_val = b_val << (c_val & 31);
+ break;
+ }
+ case ASR: {
+ a_val = ((int32_t)b_val) >> (c_val & 31);
+ break;
+ }
+ case ROR: {
+ a_val = (b_val >> (c_val & 31)) | (b_val << (-c_val & 31));
+ break;
+ }
+ case AND: {
+ a_val = b_val & c_val;
+ break;
+ }
+ case ANN: {
+ a_val = b_val & ~c_val;
+ break;
+ }
+ case IOR: {
+ a_val = b_val | c_val;
+ break;
+ }
+ case XOR: {
+ a_val = b_val ^ c_val;
+ break;
+ }
+ case ADD: {
+ a_val = b_val + c_val;
+ if ((ir & ubit) != 0) {
+ a_val += risc->C;
+ }
+ risc->C = a_val < b_val;
+ risc->V = ((a_val ^ c_val) & (a_val ^ b_val)) >> 31;
+ break;
+ }
+ case SUB: {
+ a_val = b_val - c_val;
+ if ((ir & ubit) != 0) {
+ a_val -= risc->C;
+ }
+ risc->C = a_val > b_val;
+ risc->V = ((b_val ^ c_val) & (a_val ^ b_val)) >> 31;
+ break;
+ }
+ case MUL: {
+ uint64_t tmp;
+ if ((ir & ubit) == 0) {
+ tmp = (int64_t)(int32_t)b_val * (int64_t)(int32_t)c_val;
+ } else {
+ tmp = (uint64_t)b_val * (uint64_t)c_val;
+ }
+ a_val = (uint32_t)tmp;
+ risc->H = (uint32_t)(tmp >> 32);
+ break;
+ }
+ case DIV: {
+ if ((int32_t)c_val > 0) {
+ if ((ir & ubit) == 0) {
+ a_val = (int32_t)b_val / (int32_t)c_val;
+ risc->H = (int32_t)b_val % (int32_t)c_val;
+ if ((int32_t)risc->H < 0) {
+ a_val--;
+ risc->H += c_val;
+ }
+ } else {
+ a_val = b_val / c_val;
+ risc->H = b_val % c_val;
+ }
+ } else {
+ struct idiv q = idiv(b_val, c_val, ir & ubit);
+ a_val = q.quot;
+ risc->H = q.rem;
+ }
+ break;
+ }
+ case FAD: {
+ a_val = fp_add(b_val, c_val, ir & ubit, ir & vbit);
+ break;
+ }
+ case FSB: {
+ a_val = fp_add(b_val, c_val ^ 0x80000000, ir & ubit, ir & vbit);
+ break;
+ }
+ case FML: {
+ a_val = fp_mul(b_val, c_val);
+ break;
+ }
+ case FDV: {
+ a_val = fp_div(b_val, c_val);
+ break;
+ }
+ default: {
+ abort(); // unreachable
+ }
+ }
+ risc_set_register(risc, a, a_val);
+ }
+ else if ((ir & qbit) == 0) {
+ // Memory instructions
+ uint32_t a = (ir & 0x0F000000) >> 24;
+ uint32_t b = (ir & 0x00F00000) >> 20;
+ int32_t off = ir & 0x000FFFFF;
+ off = (off ^ 0x00080000) - 0x00080000; // sign-extend
+
+ uint32_t address = risc->R[b] + off;
+ if ((ir & ubit) == 0) {
+ uint32_t a_val;
+ if ((ir & vbit) == 0) {
+ a_val = risc_load_word(risc, address);
+ } else {
+ a_val = risc_load_byte(risc, address);
+ }
+ risc_set_register(risc, a, a_val);
+ } else {
+ if ((ir & vbit) == 0) {
+ risc_store_word(risc, address, risc->R[a]);
+ } else {
+ risc_store_byte(risc, address, (uint8_t)risc->R[a]);
+ }
+ }
+ }
+ else {
+ // Branch instructions
+ bool t = (ir >> 27) & 1;
+ switch ((ir >> 24) & 7) {
+ case 0: t ^= risc->N; break;
+ case 1: t ^= risc->Z; break;
+ case 2: t ^= risc->C; break;
+ case 3: t ^= risc->V; break;
+ case 4: t ^= risc->C | risc->Z; break;
+ case 5: t ^= risc->N ^ risc->V; break;
+ case 6: t ^= (risc->N ^ risc->V) | risc->Z; break;
+ case 7: t ^= true; break;
+ default: abort(); // unreachable
+ }
+ if (t) {
+ if ((ir & vbit) != 0) {
+ risc_set_register(risc, 15, risc->PC * 4);
+ }
+ if ((ir & ubit) == 0) {
+ uint32_t c = ir & 0x0000000F;
+ risc->PC = risc->R[c] / 4;
+ } else {
+ int32_t off = ir & 0x00FFFFFF;
+ off = (off ^ 0x00800000) - 0x00800000; // sign-extend
+ risc->PC = risc->PC + off;
+ }
+ }
+ }
+}
+
+void risc_set_register(struct RISC *risc, int reg, uint32_t value) {
+ printf(" R%d = 0x%08x\n", reg, value);
+ risc->R[reg] = value;
+ risc->Z = value == 0;
+ risc->N = (int32_t)value < 0;
+}
+
+uint32_t risc_load_word(struct RISC *risc, uint32_t address) {
+ if (address < risc->mem_size) {
+ return risc->RAM[address/4];
+ } else {
+ return risc_load_io(risc, address);
+ }
+}
+
+uint8_t risc_load_byte(struct RISC *risc, uint32_t address) {
+ uint32_t w = risc_load_word(risc, address);
+ return (uint8_t)(w >> (address % 4 * 8));
+}
+
+void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value) {
+ if (address < risc->mem_size) {
+ printf(" mem@ 0x%08x = 0x%08x\n", address, value);
+ risc->RAM[address/4] = value;
+ } else {
+ risc_store_io(risc, address, value);
+ }
+}
+
+void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value) {
+ if (address < risc->mem_size) {
+ uint32_t w = risc_load_word(risc, address);
+ uint32_t shift = (address & 3) * 8;
+ w &= ~(0xFFu << shift);
+ w |= (uint32_t)value << shift;
+ risc_store_word(risc, address, w);
+ } else {
+ risc_store_io(risc, address, (uint32_t)value);
+ }
+}
+
+static uint32_t risc_load_io(struct RISC *risc, uint32_t address) {
+ return 0;
+}
+
+static void risc_store_io(struct RISC *risc, uint32_t address, uint32_t value) {
+ printf(" io@ 0x%08x = 0x%08x\n", address, value);
+ switch (address - IOStart) {
+ case 0x100: {
+ unsigned char x = value;
+ write(1, &x, 1);
+ }
+ }
+}
+
diff --git a/src/risc5emu.h b/src/risc5emu.h
@@ -0,0 +1,17 @@
+
+#include <stdint.h>
+
+typedef struct RISC risc_t;
+
+struct RISC *risc_new();
+
+void risc_reset(struct RISC *risc);
+void risc_run(struct RISC *risc, int cycles);
+
+void risc_single_step(struct RISC *risc);
+void risc_set_register(struct RISC *risc, int reg, uint32_t value);
+uint32_t risc_load_word(struct RISC *risc, uint32_t address);
+uint8_t risc_load_byte(struct RISC *risc, uint32_t address);
+void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value);
+void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value);
+