From 49f510d2d60129526832bfcd9c0f4049962bc80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20B=C3=B6hm?= Date: Mon, 18 May 2009 20:53:42 +0200 Subject: move stuff around and create initial source structure --- src/emu/.gitignore | 3 + src/emu/Makefile | 19 +++++ src/emu/TODO | 4 + src/emu/asm.c | 33 ++++++++ src/emu/asm.h | 8 ++ src/emu/cpu.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/emu/cpu.h | 29 +++++++ src/emu/log.h | 33 ++++++++ src/emu/mem.c | 3 + src/emu/mem.h | 8 ++ src/emu/opc.c | 63 ++++++++++++++ src/emu/opc.h | 68 +++++++++++++++ src/emu/riscas.c | 40 +++++++++ src/emu/risci.c | 134 ++++++++++++++++++++++++++++++ src/emu/syscall.c | 23 ++++++ src/emu/syscall.h | 16 ++++ src/emu/test.S | 15 ++++ 17 files changed, 736 insertions(+) create mode 100644 src/emu/.gitignore create mode 100644 src/emu/Makefile create mode 100644 src/emu/TODO create mode 100644 src/emu/asm.c create mode 100644 src/emu/asm.h create mode 100644 src/emu/cpu.c create mode 100644 src/emu/cpu.h create mode 100644 src/emu/log.h create mode 100644 src/emu/mem.c create mode 100644 src/emu/mem.h create mode 100644 src/emu/opc.c create mode 100644 src/emu/opc.h create mode 100644 src/emu/riscas.c create mode 100644 src/emu/risci.c create mode 100644 src/emu/syscall.c create mode 100644 src/emu/syscall.h create mode 100644 src/emu/test.S (limited to 'src/emu') diff --git a/src/emu/.gitignore b/src/emu/.gitignore new file mode 100644 index 0000000..44067fa --- /dev/null +++ b/src/emu/.gitignore @@ -0,0 +1,3 @@ +riscas +risci +test diff --git a/src/emu/Makefile b/src/emu/Makefile new file mode 100644 index 0000000..86d4aba --- /dev/null +++ b/src/emu/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -std=c99 -D_GNU_SOURCE -Wall -Wextra -pedantic -ggdb3 +LDFLAGS = -lreadline + +all: risci riscas + +risci: asm.c cpu.c mem.c opc.c syscall.c + +riscas: asm.c opc.c + +test: riscas test.S + ./riscas test.S test + +check: risci test + ./risci -d test + +clean: + rm -f test + rm -f riscas risci diff --git a/src/emu/TODO b/src/emu/TODO new file mode 100644 index 0000000..e3445bc --- /dev/null +++ b/src/emu/TODO @@ -0,0 +1,4 @@ +- define application binary interface +- add some more usefull system calls +- enhance trap handling +- check arithmetic operations for overflow and signed correctness diff --git a/src/emu/asm.c b/src/emu/asm.c new file mode 100644 index 0000000..caca72a --- /dev/null +++ b/src/emu/asm.c @@ -0,0 +1,33 @@ +#include +#include + +#include "cpu.h" +#include "log.h" +#include "opc.h" + +uint32_t compile(const char *line) +{ + char mnem[4]; + int32_t a = 0, b = 0, c = 0; + + /* arithmetic & logic */ + if (sscanf(line, "%3s r%2d, r%2d, r%2d", mnem, &a, &b, &c) == 4) + return mnemonic2opc(mnem) | ((a & 0x1F) << 21) | ((b & 0x1F) << 16) | ((c & 0x1F) << 11); + + /* load/store & branch */ + if (sscanf(line, "%3s r%2d, r%2d, %d", mnem, &a, &b, &c) == 4) + return mnemonic2opc(mnem) | ((a & 0x1F) << 21) | ((b & 0x1F) << 16) | (c & 0xFFFF); + + if (sscanf(line, "%3s r%2d, %d", mnem, &b, &c) == 3) + return mnemonic2opc(mnem) | ((b & 0x1F) << 16) | (c & 0xFFFF); + + /* jump */ + if (sscanf(line, "%3s r%2d", mnem, &a) == 2) + return mnemonic2opc(mnem) | ((a & 0x1F) << 21); + + /* misc */ + if (sscanf(line, "%3s", mnem) == 1) + return mnemonic2opc(mnem); + + return 0xFFFFFFFF; +} diff --git a/src/emu/asm.h b/src/emu/asm.h new file mode 100644 index 0000000..9a4fd37 --- /dev/null +++ b/src/emu/asm.h @@ -0,0 +1,8 @@ +#ifndef _ASM_H +#define _ASM_H + +#include + +uint32_t compile(const char *line); + +#endif diff --git a/src/emu/cpu.c b/src/emu/cpu.c new file mode 100644 index 0000000..b0664cd --- /dev/null +++ b/src/emu/cpu.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "log.h" +#include "mem.h" +#include "opc.h" +#include "syscall.h" + +// program counter +uint32_t PC; + +// status bits +bool N, Z; + +// 32 general purpose registers +uint32_t GPR[32]; + +/* extract operands from the instruction regiter */ +static inline +uint32_t __OPEXT(uint32_t IR, uint8_t start, uint8_t length) +{ + return (IR >> start) & ((1 << length) - 1); +} + +#define OPEXT(start, length) __OPEXT(IR, start, length) + +/* operands */ +#define OPCODE OPEXT(26, 6) +#define REGa OPEXT(21, 5) +#define REGb OPEXT(16, 5) +#define REGc OPEXT(11, 5) +#define IMMc OPEXT(0, 16) + +/* cpu traps */ +void trap(int num) +{ + switch (num) { + case TRP_UNALIGNED: + raise(SIGSEGV); + break; + case TRP_DIVBYZERO: + raise(SIGFPE); + break; + case TRP_SYSCALL: + do_syscall(); + break; + case TRP_ILL: + raise(SIGILL); + break; + } +} + +void execute(uint32_t IR) +{ + /* decode op-code */ + uint8_t opcode = OPCODE; + int32_t a, b, c; + + if (opcode < OPC_MOV) { + /* arithmetic & logic */ + a = REGa; + b = REGb; + c = REGc; + + debug("PC@%#08x: %-3s r%i, r%i, r%i", PC, + opc2mnemonic(IR), a, b, c); + } + + else if (opcode < OPC_J) { + /* load/store & branch */ + a = REGa; + b = REGb; + c = IMMc; + + /* sign extension */ + if (c >= 0x8000) + c -= 0x10000; + + if (opcode < OPC_LB) { + debug("PC@%#08x: %-3s r%i, %i", PC, + opc2mnemonic(IR), b, c); + } else { + debug("PC@%#08x: %-3s r%i, r%i, %i", PC, + opc2mnemonic(IR), a, b, c); + } + } + + else if (opcode < OPC_SYS) { + /* jump */ + a = REGa; + b = c = 0; + + debug("PC@%#08x: %-3s r%i", PC, + opc2mnemonic(IR), a); + } + + else { + /* misc */ + a = b = c = 0; + + debug("PC@%#08x: %-3s", PC, + opc2mnemonic(IR)); + } + + // make sure r0 is zero + GPR[0] = 0; + + // buffer for load/store instructions + uint8_t tmp8; + uint16_t tmp16; + uint32_t tmp32; + + switch (opcode) { + case OPC_ADD: + // XXX: signed/unsigned? + GPR[a] = GPR[b] + GPR[c]; + break; + case OPC_SUB: + // XXX: signed/unsigned? + GPR[a] = GPR[b] - GPR[c]; + break; + case OPC_MUL: + // XXX: signed/unsigned? + GPR[a] = GPR[b] * GPR[c]; + break; + case OPC_DIV: + // XXX: signed/unsigned? + if (GPR[c] == 0) + trap(TRP_DIVBYZERO); + else + GPR[a] = GPR[b] / GPR[c]; + break; + case OPC_MOD: + // XXX: signed/unsigned? + GPR[a] = GPR[b] % GPR[c]; + break; + case OPC_SHL: + // XXX: signed/unsigned? + GPR[a] = GPR[b] << GPR[c]; + break; + case OPC_SHR: + // XXX: signed/unsigned? + GPR[a] = GPR[b] >> GPR[c]; + break; + case OPC_AND: + GPR[a] = GPR[b] & GPR[c]; + break; + case OPC_OR: + GPR[a] = GPR[b] | GPR[c]; + break; + case OPC_XOR: + GPR[a] = GPR[b] ^ GPR[c]; + break; + case OPC_NOR: + GPR[a] = ~(GPR[b] & GPR[c]); + break; + case OPC_MOV: + GPR[b] = c; + break; + case OPC_LB: + memcpy(&tmp8, &MEM[GPR[a] + c], sizeof(uint8_t)); + GPR[b] = tmp8; + break; + case OPC_LH: + if ((GPR[a] + c) & 0x1) + trap(TRP_UNALIGNED); + memcpy(&tmp16, &MEM[GPR[a] + c], sizeof(uint16_t)); + GPR[b] = tmp16; + break; + case OPC_LW: + if ((GPR[a] + c) & 0x2) + trap(TRP_UNALIGNED); + memcpy(&tmp32, &MEM[GPR[a] + c], sizeof(uint32_t)); + GPR[b] = tmp32; + break; + case OPC_SB: + tmp8 = GPR[b]; + memcpy(&MEM[GPR[a] + c], &tmp8, sizeof(uint8_t)); + break; + case OPC_SH: + if ((GPR[a] + c) & 0x1) + trap(TRP_UNALIGNED); + tmp16 = GPR[b]; + memcpy(&MEM[GPR[a] + c], &tmp16, sizeof(uint16_t)); + break; + case OPC_SW: + if ((GPR[a] + c) & 0x2) + trap(TRP_UNALIGNED); + tmp32 = GPR[b]; + memcpy(&MEM[GPR[a] + c], &tmp32, sizeof(uint32_t)); + break; + case OPC_CMP: + Z = (GPR[b] == (uint32_t) c); + N = (GPR[b] < (uint32_t) c); + break; + case OPC_BEQ: + if (Z) + PC += c * sizeof(uint32_t); + break; + case OPC_BNE: + if (!Z) + PC += c * sizeof(uint32_t); + break; + case OPC_BLT: + if (N) + PC += c * sizeof(uint32_t); + break; + case OPC_BGE: + if (!N) + PC += c * sizeof(uint32_t); + break; + case OPC_BLE: + if (Z || N) + PC += c * sizeof(uint32_t); + break; + case OPC_BGT: + if (!Z && !N) + PC += c * sizeof(uint32_t); + case OPC_J: + PC = GPR[a]; + break; + case OPC_JAL: + GPR[31] = PC + sizeof(uint32_t); + PC = GPR[a]; + case OPC_SYS: + trap(TRP_SYSCALL); + break; + default: + /* illegal instruction */ + trap(TRP_ILL); + } +} diff --git a/src/emu/cpu.h b/src/emu/cpu.h new file mode 100644 index 0000000..43ccf00 --- /dev/null +++ b/src/emu/cpu.h @@ -0,0 +1,29 @@ +#ifndef _CPU_H +#define _CPU_H + +#include +#include + +/* cpu traps */ +enum { + TRP_UNALIGNED, + TRP_DIVBYZERO, + TRP_SYSCALL, + TRP_ILL, +}; + +void trap(int num); + +/* program counter */ +extern uint32_t PC; + +/* status bits */ +extern bool N, Z; + +/* 32 general purpose registers */ +extern uint32_t GPR[32]; + +/* main cpu execution function */ +void execute(uint32_t IR); + +#endif diff --git a/src/emu/log.h b/src/emu/log.h new file mode 100644 index 0000000..6660c66 --- /dev/null +++ b/src/emu/log.h @@ -0,0 +1,33 @@ +#ifndef _LOG_H +#define _LOG_H + +#include +#include + +extern bool is_debug; + +#define debug(...) do { \ + if (is_debug) { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ +} while (0) + +#define error(...) do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} while (0) + +#define die(...) do { \ + error(__VA_ARGS__); \ + exit(EXIT_FAILURE); \ +} while (0) + +#define pdie(...) do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, ": "); \ + perror(NULL); \ + exit(EXIT_FAILURE); \ +} while (0) + +#endif diff --git a/src/emu/mem.c b/src/emu/mem.c new file mode 100644 index 0000000..e102eaf --- /dev/null +++ b/src/emu/mem.c @@ -0,0 +1,3 @@ +#include "mem.h" + +uint8_t MEM[4096]; diff --git a/src/emu/mem.h b/src/emu/mem.h new file mode 100644 index 0000000..34b13fa --- /dev/null +++ b/src/emu/mem.h @@ -0,0 +1,8 @@ +#ifndef _MEM_H +#define _MEM_H + +#include + +extern uint8_t MEM[]; + +#endif diff --git a/src/emu/opc.c b/src/emu/opc.c new file mode 100644 index 0000000..870a272 --- /dev/null +++ b/src/emu/opc.c @@ -0,0 +1,63 @@ +#include +#include + +#include "opc.h" + +typedef struct opc_mapping { + const char *name; + uint32_t opcode; +} opc_mapping_t; + +opc_mapping_t opc_map[] = { + { "ADD", OPC_ADD }, + { "SUB", OPC_SUB }, + { "MUL", OPC_MUL }, + { "DIV", OPC_DIV }, + { "MOD", OPC_MOD }, + { "SHL", OPC_SHL }, + { "SHR", OPC_SHR }, + { "AND", OPC_AND }, + { "OR", OPC_OR }, + { "XOR", OPC_XOR }, + { "NOR", OPC_NOR }, + { "MOV", OPC_MOV }, + { "LB", OPC_LB }, + { "LH", OPC_LH }, + { "LW", OPC_LW }, + { "SB", OPC_SB }, + { "SH", OPC_SH }, + { "SW", OPC_SW }, + { "CMP", OPC_CMP }, + { "BEQ", OPC_BEQ }, + { "BNE", OPC_BNE }, + { "BLT", OPC_BLT }, + { "BGE", OPC_BGE }, + { "BLE", OPC_BLE }, + { "BGT", OPC_BGT }, + { "J", OPC_J }, + { "JAL", OPC_JAL }, + { "SYS", OPC_SYS }, + { NULL, 0 } +}; + +uint32_t mnemonic2opc(const char *mnemonic) +{ + for (uint8_t i = 0; opc_map[i].name; i++) { + if (strcmp(opc_map[i].name, mnemonic) == 0) + return opc_map[i].opcode << 26; + } + + return ~0; +} + +const char *opc2mnemonic(uint32_t IR) +{ + uint32_t opcode = IR >> 26; + + for (uint8_t i = 0; opc_map[i].name; i++) { + if (opc_map[i].opcode == opcode) + return opc_map[i].name; + } + + return NULL; +} diff --git a/src/emu/opc.h b/src/emu/opc.h new file mode 100644 index 0000000..45d21a8 --- /dev/null +++ b/src/emu/opc.h @@ -0,0 +1,68 @@ +#ifndef _OPC_H +#define _OPC_H + +#include + +/* instructions formats: + * --------------------- + * + * arithmetic: + * |000|xxx|aaaaa|bbbbb|ccccc|00000000000| + * logic: + * |001|xxx|aaaaa|bbbbb|ccccc|00000000000| + * load & store: + * |010|xxx|aaaaa|bbbbb|cccccccccccccccc| + * branch: + * |011|xxx|aaaaa|bbbbb|cccccccccccccccc| + * jump: + * |100|xxx|aaaaa|000000000000000000000| + * misc: + * |111|xxx|??????????????????????????| + * + */ + +/* arithmetic */ +#define OPC_ADD 000 +#define OPC_SUB 001 +#define OPC_MUL 002 +#define OPC_DIV 003 +#define OPC_MOD 004 +#define OPC_SHL 005 +#define OPC_SHR 006 + +/* logic */ +#define OPC_AND 010 +#define OPC_OR 011 +#define OPC_XOR 012 +#define OPC_NOR 013 + +/* load & store */ +#define OPC_MOV 020 +#define OPC_LB 021 +#define OPC_LH 022 +#define OPC_LW 023 +#define OPC_SB 024 +#define OPC_SH 025 +#define OPC_SW 026 + +/* branch instructions */ +#define OPC_CMP 030 +#define OPC_BEQ 031 +#define OPC_BNE 032 +#define OPC_BLT 033 +#define OPC_BGE 034 +#define OPC_BLE 035 +#define OPC_BGT 036 + +/* jump instructions */ +#define OPC_J 040 +#define OPC_JAL 041 + +/* misc */ +#define OPC_SYS 070 + +/* conversion functions */ +uint32_t mnemonic2opc(const char *mnemonic); +const char *opc2mnemonic(uint32_t IR); + +#endif diff --git a/src/emu/riscas.c b/src/emu/riscas.c new file mode 100644 index 0000000..e4180b2 --- /dev/null +++ b/src/emu/riscas.c @@ -0,0 +1,40 @@ +#include +#include +#include + +#include "asm.h" +#include "log.h" + +static +void usage(int rc) +{ + fprintf(stderr, "Usage: riscas \n"); + exit(rc); +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + usage(EXIT_FAILURE); + } + + FILE *sfd; + if ((sfd = fopen(argv[1], "r")) == NULL) + pdie("could not open source %s", argv[1]); + + FILE *pfd; + if ((pfd = fopen(argv[2], "w")) == NULL) + pdie("could not open program %s", argv[2]); + + char line[128]; + while (fgets(line, 128, sfd)) { + uint32_t IR = compile(line); + + if (IR == 0xFFFFFFFF) + die("illegal instruction: %s", line); + + fwrite(&IR, sizeof(uint32_t), 1, pfd); + } + + return 0; +} diff --git a/src/emu/risci.c b/src/emu/risci.c new file mode 100644 index 0000000..8c4e30f --- /dev/null +++ b/src/emu/risci.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asm.h" +#include "cpu.h" +#include "log.h" +#include "opc.h" + +bool is_debug = false; +bool is_interactive = false; + +/* global program buffer */ +uint8_t *P = NULL; + +static +void usage(int rc) +{ + fprintf(stderr, "Usage: risci [-dhi] \n"); + exit(rc); +} + +static +void signal_handler(int sig) +{ + switch (sig) { + case SIGILL: + /* SIGILL is raised by the cpu for an unknown instruction */ + error("ERROR: illegal instruction."); + if (!is_interactive) + exit(-1); + break; + case SIGSEGV: + /* SIGSEGV is raised for unaligned memory access */ + error("ERROR: unaligned memory access."); + if (!is_interactive) + exit(-1); + break; + case SIGFPE: + /* SIGFPE is raised by devision with zero */ + error("ERROR: division by zero."); + exit(-1); + break; + } +} + +static +void read_program(const char *program) +{ + struct stat sb; + if (lstat(program, &sb) == -1) + pdie("cannot stat program"); + + if (sb.st_size % sizeof(uint32_t)) + die("program does not align to op-code size of %u bytes", sizeof(uint32_t)); + + int pfd; + if ((pfd = open(program, O_RDONLY)) == -1) + pdie("could not open program"); + + P = malloc(sb.st_size + sizeof(uint32_t)); + if (read(pfd, P, sb.st_size) != sb.st_size) + die("premature end of program"); + + memset(P + sb.st_size, 0xFF, sizeof(uint32_t)); +} + +static +uint32_t next_instruction(void) +{ + if (is_interactive) { + /* read next instruction from stdin */ + printf("%03d", PC/4); + return compile(readline("> ")); + } + + uint32_t tmp; + memcpy(&tmp, &P[PC], sizeof(uint32_t)); + return tmp; +} + +int main(int argc, char *argv[]) +{ + int ch; + + while ((ch = getopt(argc, argv, "dhi")) != -1) { + switch (ch) { + case 'd': + is_debug = true; + break; + case 'h': + usage(EXIT_SUCCESS); + case 'i': + is_interactive = true; + break; + case '?': + default: + usage(EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + + /* catch cpu signal traps */ + signal(SIGILL, signal_handler); + signal(SIGFPE, signal_handler); + signal(SIGSEGV, signal_handler); + + /* load program from file if we're not in interactive mode */ + if (!is_interactive) { + if (argc < 1) + usage(EXIT_FAILURE); + + read_program(argv[0]); + } + + /* reset program counter to first instruction */ + PC = 0; + + /* start instruction loop */ + while (1) { + execute(next_instruction()); + PC += 4; + } + + /* not reached, program is terminated by signal traps from the cpu */ +} diff --git a/src/emu/syscall.c b/src/emu/syscall.c new file mode 100644 index 0000000..fe0d83e --- /dev/null +++ b/src/emu/syscall.c @@ -0,0 +1,23 @@ +#include +#include + +#include "cpu.h" +#include "mem.h" +#include "syscall.h" + +void do_syscall(void) +{ + switch (GPR[1]) { + case SYS_exit: + exit(GPR[2]); + break; + case SYS_read: + GPR[2] = read(GPR[2], &MEM[GPR[3]], GPR[4]); + break; + case SYS_write: + GPR[2] = write(GPR[2], &MEM[GPR[3]], GPR[4]); + break; + default: + GPR[2] = -1; + } +} diff --git a/src/emu/syscall.h b/src/emu/syscall.h new file mode 100644 index 0000000..7c7265c --- /dev/null +++ b/src/emu/syscall.h @@ -0,0 +1,16 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + +/* calling convention: + * - pass syscall number in GPR[1] + * - pass arguments in GPR[2]-GPR[9] + * - return code is passed in GPR[2] + */ + +#define SYS_exit 0x00 +#define SYS_read 0x01 +#define SYS_write 0x02 + +void do_syscall(void); + +#endif diff --git a/src/emu/test.S b/src/emu/test.S new file mode 100644 index 0000000..747295f --- /dev/null +++ b/src/emu/test.S @@ -0,0 +1,15 @@ +MOV r1, 102 +SB r0, r1, 0 +MOV r1, 111 +SB r0, r1, 1 +SB r0, r1, 2 +MOV r1, 10 +SB r0, r1, 3 +MOV r1, 2 +MOV r2, 0 +MOV r3, 0 +MOV r4, 4 +SYS +MOV r1, 0 +MOV r2, 1 +SYS -- cgit v1.2.3