compiler

Unnamed Compiled Systems Language Project
git clone http://frotz.net/git/compiler.git
Log | Files | Refs

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:
MMakefile | 16+++++++++++++---
Asrc/r5e.c | 21+++++++++++++++++++++
Asrc/risc5emu-fp.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/risc5emu-fp.h | 32++++++++++++++++++++++++++++++++
Asrc/risc5emu.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/risc5emu.h | 17+++++++++++++++++
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); +