├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── docs └── screenshots │ └── helloworld.png └── src ├── framebuffer.c ├── framebuffer.h ├── keyboard.c ├── keyboard.h ├── main.c ├── mul128.h ├── screen.c ├── screen.h ├── semu.c └── semu.h /.gitignore: -------------------------------------------------------------------------------- 1 | **/.vscode/ 2 | 3 | vulpinesystem 4 | 5 | vulpinesystem.exe 6 | SDL2.dll 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2022 ryfox/ry755 (VulpineSystem), and National Cheng Kung University, Taiwan (semu) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SDL2_CONFIG = sdl2-config 2 | CFLAGS = -g -Ofast -std=c99 -Wall -Wextra `$(SDL2_CONFIG) --cflags --libs` 3 | TARGET=vulpinesystem 4 | 5 | CFILES = src/main.c \ 6 | src/framebuffer.c \ 7 | src/keyboard.c \ 8 | src/screen.c \ 9 | src/semu.c 10 | 11 | $(TARGET): $(CFILES) 12 | $(CC) -o $@ $(filter %.c, $^) $(CFLAGS) 13 | 14 | clean: 15 | rm -rf vulpinesystem 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VulpineSystem 2 | 3 | **VulpineSystem** is a 64 bit fantasy computer based on the RISC-V architecture. It runs an extended/enhanced version of [**xv6**](https://github.com/VulpineSystem/xv6), a Unix-like operating system, and is intended to be used for learning operating system development skills. 4 | 5 | The RISC-V emulation core is based on [semu](https://github.com/jserv/semu). 6 | 7 | Screenshot of xv6: 8 | 9 | ![Screenshot](docs/screenshots/helloworld.png) 10 | 11 | ## Getting Started 12 | 13 | ### Building 14 | 15 | Simply run `make`. The resulting binary will be saved as `vulpinesystem`. 16 | 17 | ### Usage 18 | 19 | `./vulpinesystem []` 20 | 21 | The most common use case is passing the [**xv6**](https://github.com/VulpineSystem/xv6) kernel image as the first argument, and the filesystem image as the second argument: `./vulpinesystem ../xv6/kernel/xv6 ../xv6/fs.img` 22 | 23 | ### Toolchain Issues 24 | 25 | Note that the prebuilt RISC-V toolchains are compiled with the compressed instructions extension enabled. VulpineSystem does not support compressed instructions, so you will have to build a toolchain with that extension disabled. Follow the "Installation (Newlib)" instructions [here](https://github.com/riscv-collab/riscv-gnu-toolchain), but when running the `./configure` command, add `--with-arch=rv64g`. I also recommend running `make` with `-j12` (replace 12 with how many threads your CPU has) in order to speed up the build process. 26 | 27 | ## News 28 | 29 | I often post news about what I'm working on with VulpineSystem and xv6 on Cohost! You can find my posts about this [here](https://cohost.org/rc/tagged/vulpinesystem). 30 | 31 | ## License 32 | This project is licensed under the [MIT license](LICENSE). 33 | -------------------------------------------------------------------------------- /docs/screenshots/helloworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VulpineSystem/vulpinesystem/600a39850c66682d1a3f82a3cfead6e11c692a27/docs/screenshots/helloworld.png -------------------------------------------------------------------------------- /src/framebuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "semu.h" 12 | #include "framebuffer.h" 13 | #include "screen.h" 14 | 15 | extern struct cpu *cpu; 16 | 17 | void draw_framebuffer(struct Screen *screen) { 18 | SDL_Texture *texture = ScreenGetTexture(screen); 19 | SDL_UpdateTexture(texture, NULL, &cpu->bus->ram->data[FRAMEBUFFER_BASE - RAM_BASE], FRAMEBUFFER_WIDTH * 4); 20 | } 21 | -------------------------------------------------------------------------------- /src/framebuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "screen.h" 4 | 5 | #define FRAMEBUFFER_WIDTH 640 6 | #define FRAMEBUFFER_HEIGHT 480 7 | 8 | void draw_framebuffer(struct Screen *screen); 9 | -------------------------------------------------------------------------------- /src/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "keyboard.h" 12 | 13 | typedef struct node_s { 14 | struct node_s *prev; 15 | struct node_s *next; 16 | keycode_t code; 17 | } node_t; 18 | 19 | static node_t *head = NULL; 20 | static node_t *tail = NULL; 21 | 22 | keycode_t key_take(void) { 23 | node_t *node = head; 24 | 25 | if (node == NULL) { 26 | return 0; 27 | } 28 | 29 | if (node == tail) { 30 | head = NULL; 31 | tail = NULL; 32 | } else { 33 | head = node->next; 34 | head->prev = NULL; 35 | } 36 | 37 | keycode_t code = node->code; 38 | return free(node), code; 39 | } 40 | 41 | void key_put(keycode_t code) { 42 | if (code == 0) abort(); 43 | 44 | node_t *node = malloc(sizeof(node_t)); 45 | 46 | node->prev = tail; 47 | node->next = NULL; 48 | node->code = code; 49 | 50 | if (head == NULL) { 51 | head = node; 52 | } else { 53 | tail->next = node; 54 | } 55 | 56 | tail = node; 57 | } 58 | 59 | static const keycode_t key_map[SDL_NUM_SCANCODES] = { 60 | [SDL_SCANCODE_ESCAPE] = 0x01, 61 | [SDL_SCANCODE_1] = 0x02, 62 | [SDL_SCANCODE_KP_1] = 0x02, 63 | [SDL_SCANCODE_2] = 0x03, 64 | [SDL_SCANCODE_KP_2] = 0x03, 65 | [SDL_SCANCODE_3] = 0x04, 66 | [SDL_SCANCODE_KP_3] = 0x04, 67 | [SDL_SCANCODE_4] = 0x05, 68 | [SDL_SCANCODE_KP_4] = 0x05, 69 | [SDL_SCANCODE_5] = 0x06, 70 | [SDL_SCANCODE_KP_5] = 0x06, 71 | [SDL_SCANCODE_6] = 0x07, 72 | [SDL_SCANCODE_KP_6] = 0x07, 73 | [SDL_SCANCODE_7] = 0x08, 74 | [SDL_SCANCODE_KP_7] = 0x08, 75 | [SDL_SCANCODE_8] = 0x09, 76 | [SDL_SCANCODE_KP_8] = 0x09, 77 | [SDL_SCANCODE_9] = 0x0A, 78 | [SDL_SCANCODE_KP_9] = 0x0A, 79 | [SDL_SCANCODE_0] = 0x0B, 80 | [SDL_SCANCODE_KP_0] = 0x0B, 81 | [SDL_SCANCODE_MINUS] = 0x0C, 82 | [SDL_SCANCODE_EQUALS] = 0x0D, 83 | [SDL_SCANCODE_BACKSPACE] = 0x0E, 84 | [SDL_SCANCODE_TAB] = 0x0F, 85 | [SDL_SCANCODE_Q] = 0x10, 86 | [SDL_SCANCODE_W] = 0x11, 87 | [SDL_SCANCODE_E] = 0x12, 88 | [SDL_SCANCODE_R] = 0x13, 89 | [SDL_SCANCODE_T] = 0x14, 90 | [SDL_SCANCODE_Y] = 0x15, 91 | [SDL_SCANCODE_U] = 0x16, 92 | [SDL_SCANCODE_I] = 0x17, 93 | [SDL_SCANCODE_O] = 0x18, 94 | [SDL_SCANCODE_P] = 0x19, 95 | [SDL_SCANCODE_LEFTBRACKET] = 0x1A, 96 | [SDL_SCANCODE_RIGHTBRACKET] = 0x1B, 97 | [SDL_SCANCODE_RETURN] = 0x1C, 98 | [SDL_SCANCODE_KP_ENTER] = 0x1C, 99 | [SDL_SCANCODE_LCTRL] = 0x1D, 100 | [SDL_SCANCODE_A] = 0x1E, 101 | [SDL_SCANCODE_S] = 0x1F, 102 | [SDL_SCANCODE_D] = 0x20, 103 | [SDL_SCANCODE_F] = 0x21, 104 | [SDL_SCANCODE_G] = 0x22, 105 | [SDL_SCANCODE_H] = 0x23, 106 | [SDL_SCANCODE_J] = 0x24, 107 | [SDL_SCANCODE_K] = 0x25, 108 | [SDL_SCANCODE_L] = 0x26, 109 | [SDL_SCANCODE_SEMICOLON] = 0x27, 110 | [SDL_SCANCODE_APOSTROPHE] = 0x28, 111 | [SDL_SCANCODE_GRAVE] = 0x29, 112 | [SDL_SCANCODE_LSHIFT] = 0x2A, 113 | [SDL_SCANCODE_BACKSLASH] = 0x2B, 114 | [SDL_SCANCODE_Z] = 0x2C, 115 | [SDL_SCANCODE_X] = 0x2D, 116 | [SDL_SCANCODE_C] = 0x2E, 117 | [SDL_SCANCODE_V] = 0x2F, 118 | [SDL_SCANCODE_B] = 0x30, 119 | [SDL_SCANCODE_N] = 0x31, 120 | [SDL_SCANCODE_M] = 0x32, 121 | [SDL_SCANCODE_COMMA] = 0x33, 122 | [SDL_SCANCODE_PERIOD] = 0x34, 123 | [SDL_SCANCODE_SLASH] = 0x35, 124 | [SDL_SCANCODE_RSHIFT] = 0x36, 125 | [SDL_SCANCODE_KP_HASH] = 0x37, 126 | [SDL_SCANCODE_LALT] = 0x38, 127 | [SDL_SCANCODE_SPACE] = 0x39, 128 | [SDL_SCANCODE_CAPSLOCK] = 0x3A, 129 | [SDL_SCANCODE_F1] = 0x3B, 130 | [SDL_SCANCODE_F2] = 0x3C, 131 | [SDL_SCANCODE_F3] = 0x3D, 132 | [SDL_SCANCODE_F4] = 0x3E, 133 | [SDL_SCANCODE_F5] = 0x3F, 134 | [SDL_SCANCODE_F6] = 0x40, 135 | [SDL_SCANCODE_F7] = 0x41, 136 | [SDL_SCANCODE_F8] = 0x42, 137 | [SDL_SCANCODE_F9] = 0x43, 138 | [SDL_SCANCODE_F10] = 0x44, 139 | [SDL_SCANCODE_F11] = 0x57, 140 | [SDL_SCANCODE_F12] = 0x58, 141 | [SDL_SCANCODE_UP] = 0x67, 142 | [SDL_SCANCODE_DOWN] = 0x6C, 143 | [SDL_SCANCODE_LEFT] = 0x69, 144 | [SDL_SCANCODE_RIGHT] = 0x6A, 145 | }; 146 | 147 | keycode_t key_convert(int sdlcode) { 148 | if (sdlcode < 0 || sdlcode > SDL_NUM_SCANCODES) return 0; 149 | return key_map[sdlcode]; 150 | } 151 | 152 | void key_pressed(int sdlcode) { 153 | keycode_t code = key_convert(sdlcode); 154 | if (code) key_put(code); 155 | } 156 | 157 | void key_released(int sdlcode) { 158 | keycode_t code = key_convert(sdlcode) | 0x80; 159 | if (code) key_put(code); 160 | } 161 | -------------------------------------------------------------------------------- /src/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef unsigned char keycode_t; 4 | 5 | keycode_t key_take(void); 6 | void key_put(keycode_t code); 7 | 8 | keycode_t key_convert(int sdlcode); 9 | void key_pressed(int sdlcode); 10 | void key_released(int sdlcode); 11 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "framebuffer.h" 10 | #include "keyboard.h" 11 | #include "screen.h" 12 | #include "semu.h" 13 | 14 | #define FPS 60 15 | #define TPF 1 16 | #define TPS (FPS * TPF) 17 | 18 | struct cpu *cpu; 19 | 20 | uint32_t tick_start; 21 | uint32_t tick_end; 22 | int ticks = 0; 23 | bool done = false; 24 | 25 | void main_loop(void); 26 | void execute_instruction(void); 27 | 28 | int main(int argc, char *argv[]) { 29 | if (SDL_Init(SDL_INIT_VIDEO) != 0) { 30 | fatal("initialize SDL"); 31 | return 1; 32 | } 33 | 34 | SDL_ShowCursor(SDL_DISABLE); 35 | 36 | if (argc < 2) { 37 | printf("Usage: %s []\n", argv[0]); 38 | return 2; 39 | } 40 | 41 | FILE *f = fopen(argv[1], "rb"); 42 | if (!f) 43 | fatal("open raw kernel image"); 44 | 45 | uint8_t *binary = NULL; 46 | size_t fsize = read_file(f, &binary); 47 | fclose(f); 48 | 49 | if (argc == 3) { 50 | f = fopen(argv[2], "r+b"); 51 | if (!f) 52 | fatal("open disk image"); 53 | } 54 | 55 | cpu = cpu_new(binary, fsize, f); 56 | free(binary); 57 | 58 | ScreenCreate( 59 | FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, 60 | draw_framebuffer, 61 | key_pressed, 62 | key_released, 63 | NULL, 64 | NULL, 65 | NULL 66 | ); 67 | 68 | ScreenInit(); 69 | ScreenDraw(); 70 | 71 | tick_start = SDL_GetTicks(); 72 | tick_end = SDL_GetTicks(); 73 | 74 | while (!done) { 75 | main_loop(); 76 | 77 | tick_end = SDL_GetTicks(); 78 | int delay = 1000/TPS - (tick_end - tick_start); 79 | if (delay > 0) { 80 | SDL_Delay(delay); 81 | } else { 82 | //printf("time overrun %d\n", delay); 83 | } 84 | } 85 | 86 | fclose(f); 87 | return 0; 88 | } 89 | 90 | void main_loop(void) { 91 | int dt = SDL_GetTicks() - tick_start; 92 | tick_start = SDL_GetTicks(); 93 | if (!dt) 94 | dt = 1; 95 | 96 | int cycles_per_tick = CPU_HZ / TPS / dt; 97 | int extra_cycles = CPU_HZ / TPS - (cycles_per_tick * dt); 98 | 99 | for (int i = 0; i < dt; i++) { 100 | 101 | int cycles_left = cycles_per_tick; 102 | 103 | if (i == dt - 1) 104 | cycles_left += extra_cycles; 105 | 106 | while (cycles_left > 0) { 107 | execute_instruction(); 108 | cycles_left--; 109 | } 110 | } 111 | 112 | if ((ticks % TPF) == 0) { 113 | ScreenDraw(); 114 | } 115 | 116 | done = ScreenProcessEvents(); 117 | 118 | ticks++; 119 | } 120 | 121 | void execute_instruction(void) { 122 | // fetch instruction 123 | uint64_t insn; 124 | exception_t e; 125 | if ((e = cpu_fetch(cpu, &insn)) != OK) { 126 | cpu_take_trap(cpu, e, NONE); 127 | if (exception_is_fatal(e)) { 128 | printf("fatal exception while fetching instruction!"); 129 | exit(0); 130 | } 131 | insn = 0; 132 | } 133 | 134 | //printf("%" PRIX64 ": %" PRIX64 "\n", cpu->pc, insn); 135 | 136 | // advance pc 137 | cpu->pc += 4; 138 | 139 | // decode and execute 140 | if ((e = cpu_execute(cpu, insn)) != OK) { 141 | cpu_take_trap(cpu, e, NONE); 142 | if (exception_is_fatal(e)) { 143 | printf("fatal exception while executing instruction!"); 144 | exit(0); 145 | } 146 | } 147 | 148 | interrupt_t intr; 149 | if ((intr = cpu_check_pending_interrupt(cpu)) != NONE) 150 | cpu_take_trap(cpu, OK, intr); 151 | } 152 | -------------------------------------------------------------------------------- /src/mul128.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static inline uint64_t mulhu(uint64_t u, uint64_t v) 6 | { 7 | uint64_t a = u >> 32; 8 | uint64_t b = u & 0xffffffff; 9 | uint64_t c = v >> 32; 10 | uint64_t d = v & 0xffffffff; 11 | 12 | uint64_t ac = a * c; 13 | uint64_t bc = b * c; 14 | uint64_t ad = a * d; 15 | uint64_t bd = b * d; 16 | 17 | uint64_t mid34 = (bd >> 32) + (bc & 0xffffffff) + (ad & 0xffffffff); 18 | 19 | uint64_t hi64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32); 20 | 21 | return hi64; 22 | } 23 | 24 | static inline int64_t mulh(int64_t u, int64_t v) 25 | { 26 | return mulhu((uint64_t) u, (uint64_t) v) - ((u < 0) ? v : 0) - 27 | ((v < 0) ? u : 0); 28 | } 29 | 30 | static inline uint64_t mulhsu(int64_t u, uint64_t v) 31 | { 32 | return mulhu((uint64_t) u, (uint64_t) v) - ((u < 0) ? v : 0); 33 | } 34 | -------------------------------------------------------------------------------- /src/screen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "screen.h" 11 | 12 | #define SCREEN_ZOOM 1 13 | 14 | struct Screen MainScreen; 15 | 16 | int WindowWidth = 0; 17 | int WindowHeight = 0; 18 | 19 | bool ScreenFirstDraw = true; 20 | 21 | SDL_Window *ScreenWindow; 22 | SDL_Renderer *ScreenRenderer; 23 | 24 | SDL_Rect WindowRect; 25 | 26 | void ScreenInit() { 27 | ScreenWindow = SDL_CreateWindow( 28 | "VulpineSystem", 29 | SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 30 | (int)(WindowWidth * SCREEN_ZOOM), 31 | (int)(WindowHeight * SCREEN_ZOOM), 32 | SDL_WINDOW_HIDDEN 33 | ); 34 | 35 | if (!ScreenWindow) { 36 | fprintf(stderr, "failed to create window\n"); 37 | exit(1); 38 | } 39 | 40 | ScreenRenderer = SDL_CreateRenderer(ScreenWindow, -1, 0); 41 | 42 | if (!ScreenRenderer) { 43 | fprintf(stderr, "failed to create renderer\n"); 44 | exit(1); 45 | } 46 | 47 | WindowRect = (SDL_Rect) { 48 | .w = WindowWidth, 49 | .h = WindowHeight 50 | }; 51 | } 52 | 53 | void ScreenDraw() { 54 | MainScreen.Draw(&MainScreen); 55 | 56 | SDL_Rect screenrect = { 57 | .w = MainScreen.Width, 58 | .h = MainScreen.Height, 59 | }; 60 | 61 | SDL_Rect winrect = { 62 | .w = MainScreen.Width, 63 | .h = MainScreen.Height, 64 | .x = 0, 65 | .y = 0 66 | }; 67 | 68 | if ((WindowRect.w != screenrect.w) || (WindowRect.h != screenrect.h)) { 69 | int oldx; 70 | int oldy; 71 | 72 | SDL_GetWindowPosition(ScreenWindow, &oldx, &oldy); 73 | 74 | oldx += (WindowRect.w - screenrect.w)/2; 75 | oldy += (WindowRect.h - screenrect.h)/2; 76 | 77 | SDL_SetWindowSize(ScreenWindow, screenrect.w, screenrect.h); 78 | SDL_SetWindowPosition(ScreenWindow, oldx, oldy); 79 | 80 | WindowRect.w = screenrect.w; 81 | WindowRect.h = screenrect.h; 82 | } 83 | 84 | SDL_RenderClear(ScreenRenderer); 85 | SDL_RenderCopy(ScreenRenderer, MainScreen.Texture, &screenrect, &winrect); 86 | SDL_RenderPresent(ScreenRenderer); 87 | 88 | if (ScreenFirstDraw) { 89 | SDL_ShowWindow(ScreenWindow); 90 | ScreenFirstDraw = false; 91 | } 92 | } 93 | 94 | int ScreenProcessEvents() { 95 | SDL_Event event; 96 | while (SDL_PollEvent(&event)) { 97 | switch (event.type) { 98 | case SDL_QUIT: { 99 | return 1; 100 | } 101 | 102 | case SDL_WINDOWEVENT: { 103 | break; 104 | } 105 | 106 | case SDL_MOUSEMOTION: { 107 | if (MainScreen.MouseMoved) 108 | MainScreen.MouseMoved(event.motion.x, event.motion.y); 109 | break; 110 | } 111 | 112 | case SDL_MOUSEBUTTONDOWN: { 113 | if (MainScreen.MousePressed) 114 | MainScreen.MousePressed(event.button.button); 115 | break; 116 | } 117 | 118 | 119 | case SDL_MOUSEBUTTONUP: { 120 | if (MainScreen.MouseReleased) 121 | MainScreen.MouseReleased(event.button.button); 122 | break; 123 | } 124 | 125 | case SDL_KEYDOWN: 126 | if (MainScreen.KeyPressed) 127 | MainScreen.KeyPressed(event.key.keysym.scancode); 128 | break; 129 | 130 | case SDL_KEYUP: 131 | if (MainScreen.KeyReleased) 132 | MainScreen.KeyReleased(event.key.keysym.scancode); 133 | break; 134 | } 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | struct SDL_Texture *ScreenGetTexture(struct Screen *screen) { 141 | if (screen->Texture) { 142 | return screen->Texture; 143 | } 144 | 145 | screen->Texture = SDL_CreateTexture( 146 | ScreenRenderer, 147 | SDL_PIXELFORMAT_ABGR8888, 148 | SDL_TEXTUREACCESS_STREAMING, 149 | screen->Width, 150 | screen->Height 151 | ); 152 | 153 | return screen->Texture; 154 | } 155 | 156 | void ScreenCreate( 157 | int w, int h, 158 | ScreenDrawF draw, 159 | ScreenKeyPressedF keypressed, 160 | ScreenKeyReleasedF keyreleased, 161 | ScreenMousePressedF mousepressed, 162 | ScreenMouseReleasedF mousereleased, 163 | ScreenMouseMovedF mousemoved 164 | ) { 165 | 166 | if (w > WindowWidth) 167 | WindowWidth = w; 168 | 169 | if (h > WindowHeight) 170 | WindowHeight = h; 171 | 172 | MainScreen.Width = w; 173 | MainScreen.Height = h; 174 | 175 | MainScreen.Draw = draw; 176 | MainScreen.KeyPressed = keypressed; 177 | MainScreen.KeyReleased = keyreleased; 178 | MainScreen.MousePressed = mousepressed; 179 | MainScreen.MouseReleased = mousereleased; 180 | MainScreen.MouseMoved = mousemoved; 181 | } 182 | -------------------------------------------------------------------------------- /src/screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Screen; 6 | 7 | typedef void (*ScreenDrawF)(struct Screen *screen); 8 | typedef void (*ScreenKeyPressedF)(int sdl_scancode); 9 | typedef void (*ScreenKeyReleasedF)(int sdl_scancode); 10 | typedef void (*ScreenMousePressedF)(int button); 11 | typedef void (*ScreenMouseReleasedF)(int button); 12 | typedef void (*ScreenMouseMovedF)(int dx, int dy); 13 | 14 | struct Screen { 15 | int Width; 16 | int Height; 17 | SDL_Texture *Texture; 18 | 19 | ScreenDrawF Draw; 20 | ScreenKeyPressedF KeyPressed; 21 | ScreenKeyReleasedF KeyReleased; 22 | ScreenMousePressedF MousePressed; 23 | ScreenMouseReleasedF MouseReleased; 24 | ScreenMouseMovedF MouseMoved; 25 | }; 26 | 27 | void ScreenInit(); 28 | 29 | void ScreenDraw(); 30 | 31 | int ScreenProcessEvents(); 32 | 33 | struct SDL_Texture *ScreenGetTexture(struct Screen *screen); 34 | 35 | void ScreenCreate( 36 | int w, int h, 37 | ScreenDrawF draw, 38 | ScreenKeyPressedF keypressed, 39 | ScreenKeyReleasedF keyreleased, 40 | ScreenMousePressedF mousepressed, 41 | ScreenMouseReleasedF mousereleased, 42 | ScreenMouseMovedF mousemoved 43 | ); 44 | -------------------------------------------------------------------------------- /src/semu.c: -------------------------------------------------------------------------------- 1 | // CPU emulator is a modified version of semu, written by Jim Huang (jserv) 2 | // https://github.com/jserv/semu 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "keyboard.h" 14 | #include "semu.h" 15 | #include "mul128.h" 16 | 17 | /* Range check 18 | * For any variable range checking: 19 | * if (x >= minx && x <= maxx) ... 20 | * it is faster to use bit operation: 21 | * if ((signed)((x - minx) | (maxx - x)) >= 0) ... 22 | */ 23 | #define RANGE_CHECK(x, minx, size) \ 24 | ((int32_t) ((x - minx) | (minx + size - 1 - x)) >= 0) 25 | 26 | /* CSR is Control Status Register representation in RISC-V privileged 27 | * architecture. 28 | */ 29 | 30 | /* Machine level CSRs */ 31 | enum { MSTATUS = 0x300, MEDELEG = 0x302, MIDELEG, MIE, MTVEC }; 32 | enum { MEPC = 0x341, MCAUSE, MTVAL, MIP }; 33 | 34 | /* Supervisor level CSRs */ 35 | enum { SSTATUS = 0x100, SIE = 0x104, STVEC }; 36 | enum { SEPC = 0x141, SCAUSE, STVAL, SIP, SATP = 0x180 }; 37 | 38 | enum { MIP_SSIP = 1ULL << 1, MIP_MSIP = 1ULL << 3, MIP_STIP = 1ULL << 5 }; 39 | enum { MIP_MTIP = 1ULL << 7, MIP_SEIP = 1ULL << 9, MIP_MEIP = 1ULL << 11 }; 40 | 41 | #define PAGE_SIZE 4096 /* should be configurable */ 42 | 43 | #define MAX(a, b) \ 44 | ({ \ 45 | __typeof__(a) _a = (a); \ 46 | __typeof__(b) _b = (b); \ 47 | _a > _b ? _a : _b; \ 48 | }) 49 | 50 | #define MIN(a, b) \ 51 | ({ \ 52 | __typeof__(a) _a = (a); \ 53 | __typeof__(b) _b = (b); \ 54 | _a < _b ? _a : _b; \ 55 | }) 56 | 57 | /* Check alignement of the address (Assume alignment is a power of 2.) 58 | * x is the address to be checked. 59 | * a is the alignment constaint and must be power of 2. 60 | */ 61 | #define IS_ALIGNED(x, a) (((x) & ((__typeof__(x)) (a) -1)) == 0) 62 | 63 | bool exception_is_fatal(const exception_t e) 64 | { 65 | switch (e) { 66 | case INSTRUCTION_ADDRESS_MISALIGNED: 67 | case INSTRUCTION_ACCESS_FAULT: 68 | case LOAD_ACCESS_FAULT: 69 | case STORE_AMO_ADDRESS_MISALIGNED: 70 | case STORE_AMO_ACCESS_FAULT: 71 | return true; 72 | default: 73 | return false; 74 | } 75 | } 76 | 77 | struct ram *ram_new(const uint8_t *code, const size_t code_size) 78 | { 79 | struct ram *ram = calloc(1, sizeof(struct ram)); 80 | ram->data = calloc(RAM_SIZE, 1); 81 | memcpy(ram->data, code, code_size); 82 | return ram; 83 | } 84 | 85 | exception_t ram_load(const struct ram *ram, 86 | const uint64_t addr, 87 | const uint64_t size, 88 | uint64_t *result) 89 | { 90 | uint64_t index = addr - RAM_BASE, tmp = 0; 91 | switch (size) { 92 | case 64: 93 | tmp |= (uint64_t) (ram->data[index + 7]) << 56; 94 | tmp |= (uint64_t) (ram->data[index + 6]) << 48; 95 | tmp |= (uint64_t) (ram->data[index + 5]) << 40; 96 | tmp |= (uint64_t) (ram->data[index + 4]) << 32; 97 | case 32: 98 | tmp |= (uint64_t) (ram->data[index + 3]) << 24; 99 | tmp |= (uint64_t) (ram->data[index + 2]) << 16; 100 | case 16: 101 | tmp |= (uint64_t) (ram->data[index + 1]) << 8; 102 | case 8: 103 | tmp |= (uint64_t) (ram->data[index + 0]) << 0; 104 | *result = tmp; 105 | return OK; 106 | default: 107 | return LOAD_ACCESS_FAULT; 108 | } 109 | } 110 | 111 | exception_t ram_store(struct ram *ram, 112 | const uint64_t addr, 113 | const uint64_t size, 114 | const uint64_t value) 115 | { 116 | uint64_t index = addr - RAM_BASE; 117 | switch (size) { 118 | case 64: 119 | ram->data[index + 7] = (value >> 56) & 0xff; 120 | ram->data[index + 6] = (value >> 48) & 0xff; 121 | ram->data[index + 5] = (value >> 40) & 0xff; 122 | ram->data[index + 4] = (value >> 32) & 0xff; 123 | case 32: 124 | ram->data[index + 3] = (value >> 24) & 0xff; 125 | ram->data[index + 2] = (value >> 16) & 0xff; 126 | case 16: 127 | ram->data[index + 1] = (value >> 8) & 0xff; 128 | case 8: 129 | ram->data[index + 0] = (value >> 0) & 0xff; 130 | return OK; 131 | default: 132 | return STORE_AMO_ACCESS_FAULT; 133 | } 134 | } 135 | 136 | void fatal(const char *msg) 137 | { 138 | fprintf(stderr, "ERROR: Failed to %s.\n", msg); 139 | exit(1); 140 | } 141 | 142 | struct clint *clint_new() 143 | { 144 | return calloc(1, sizeof(struct clint)); 145 | } 146 | 147 | static inline exception_t clint_load(const struct clint *clint, 148 | const uint64_t addr, 149 | const uint64_t size, 150 | uint64_t *result) 151 | { 152 | if (size != 64) 153 | return LOAD_ACCESS_FAULT; 154 | 155 | switch (addr) { 156 | case CLINT_MTIMECMP: 157 | *result = clint->mtimecmp; 158 | break; 159 | case CLINT_MTIME: 160 | *result = clint->mtime; 161 | break; 162 | default: 163 | *result = 0; 164 | } 165 | return OK; 166 | } 167 | 168 | static inline exception_t clint_store(struct clint *clint, 169 | const uint64_t addr, 170 | const uint64_t size, 171 | const uint64_t value) 172 | { 173 | if (size != 64) 174 | return STORE_AMO_ACCESS_FAULT; 175 | 176 | switch (addr) { 177 | case CLINT_MTIMECMP: 178 | clint->mtimecmp = value; 179 | break; 180 | case CLINT_MTIME: 181 | clint->mtime = value; 182 | break; 183 | } 184 | return OK; 185 | } 186 | 187 | struct plic *plic_new() 188 | { 189 | return calloc(1, sizeof(struct plic)); 190 | } 191 | 192 | exception_t plic_load(const struct plic *plic, 193 | const uint64_t addr, 194 | const uint64_t size, 195 | uint64_t *result) 196 | { 197 | if (size != 32) 198 | return LOAD_ACCESS_FAULT; 199 | 200 | switch (addr) { 201 | case PLIC_PENDING: 202 | *result = plic->pending; 203 | break; 204 | case PLIC_SENABLE: 205 | *result = plic->senable; 206 | break; 207 | case PLIC_SPRIORITY: 208 | *result = plic->spriority; 209 | break; 210 | case PLIC_SCLAIM: 211 | *result = plic->sclaim; 212 | break; 213 | default: 214 | *result = 0; 215 | } 216 | return OK; 217 | } 218 | 219 | exception_t plic_store(struct plic *plic, 220 | const uint64_t addr, 221 | const uint64_t size, 222 | const uint64_t value) 223 | { 224 | if (size != 32) 225 | return STORE_AMO_ACCESS_FAULT; 226 | 227 | switch (addr) { 228 | case PLIC_PENDING: 229 | plic->pending = value; 230 | break; 231 | case PLIC_SENABLE: 232 | plic->senable = value; 233 | break; 234 | case PLIC_SPRIORITY: 235 | plic->spriority = value; 236 | break; 237 | case PLIC_SCLAIM: 238 | plic->sclaim = value; 239 | break; 240 | } 241 | return OK; 242 | } 243 | 244 | static void *uart_thread_func(void *priv) 245 | { 246 | struct uart *uart = (struct uart *) priv; 247 | while (1) { 248 | struct pollfd pfd = {0, POLLIN, 0}; 249 | poll(&pfd, 1, 0); 250 | if (!(pfd.revents & POLLIN)) 251 | continue; 252 | 253 | char c; 254 | if (read(STDIN_FILENO, &c, 1) <= 0) /* an error or EOF */ 255 | continue; 256 | 257 | pthread_mutex_lock(&uart->lock); 258 | while ((uart->data[UART_LSR - UART_BASE] & UART_LSR_RX) == 1) 259 | pthread_cond_wait(&uart->cond, &uart->lock); 260 | 261 | uart->data[0] = c; 262 | uart->interrupting = true; 263 | uart->data[UART_LSR - UART_BASE] |= UART_LSR_RX; 264 | pthread_mutex_unlock(&uart->lock); 265 | } 266 | 267 | /* should not reach here */ 268 | return NULL; 269 | } 270 | 271 | struct uart *uart_new() 272 | { 273 | struct uart *uart = calloc(1, sizeof(struct uart)); 274 | uart->data[UART_LSR - UART_BASE] |= UART_LSR_TX; 275 | pthread_mutex_init(&uart->lock, NULL); 276 | pthread_cond_init(&uart->cond, NULL); 277 | 278 | pthread_create(&uart->tid, NULL, uart_thread_func, (void *) uart); 279 | return uart; 280 | } 281 | 282 | exception_t uart_load(struct uart *uart, 283 | const uint64_t addr, 284 | const uint64_t size, 285 | uint64_t *result) 286 | { 287 | if (size != 8) 288 | return LOAD_ACCESS_FAULT; 289 | 290 | pthread_mutex_lock(&uart->lock); 291 | switch (addr) { 292 | case UART_RHR: 293 | pthread_cond_broadcast(&uart->cond); 294 | uart->data[UART_LSR - UART_BASE] &= ~UART_LSR_RX; 295 | default: 296 | *result = uart->data[addr - UART_BASE]; 297 | } 298 | pthread_mutex_unlock(&uart->lock); 299 | return OK; 300 | } 301 | 302 | exception_t uart_store(struct uart *uart, 303 | const uint64_t addr, 304 | const uint64_t size, 305 | const uint64_t value) 306 | { 307 | if (size != 8) 308 | return STORE_AMO_ACCESS_FAULT; 309 | 310 | pthread_mutex_lock(&uart->lock); 311 | switch (addr) { 312 | case UART_THR: 313 | putchar(value & 0xff); 314 | fflush(stdout); 315 | break; 316 | default: 317 | uart->data[addr - UART_BASE] = value & 0xff; 318 | } 319 | pthread_mutex_unlock(&uart->lock); 320 | return OK; 321 | } 322 | 323 | bool uart_is_interrupting(struct uart *uart) 324 | { 325 | pthread_mutex_lock(&uart->lock); 326 | bool interrupting = uart->interrupting; 327 | uart->interrupting = false; 328 | pthread_mutex_unlock(&uart->lock); 329 | return interrupting; 330 | } 331 | 332 | struct disk *disk_new(FILE *disk) 333 | { 334 | struct disk *vio = calloc(1, sizeof(struct disk)); 335 | vio->disk = disk; 336 | vio->notify = -1; 337 | return vio; 338 | } 339 | 340 | exception_t disk_load(const struct disk *vio, 341 | const uint64_t addr, 342 | const uint64_t size, 343 | uint64_t *result) 344 | { 345 | if (size != 32) 346 | return LOAD_ACCESS_FAULT; 347 | 348 | switch (addr) { 349 | case DISK_MAGIC: 350 | *result = 0x666F7864; 351 | break; 352 | case DISK_VERSION: 353 | *result = 0x01; 354 | break; 355 | case DISK_NOTIFY: 356 | *result = vio->notify; 357 | break; 358 | case DISK_DIRECTION: 359 | *result = vio->direction; 360 | break; 361 | case DISK_BUFFER_ADDR_HIGH: 362 | *result = vio->buffer_address_high; 363 | break; 364 | case DISK_BUFFER_ADDR_LOW: 365 | *result = vio->buffer_address_low; 366 | break; 367 | case DISK_BUFFER_LEN_HIGH: 368 | *result = vio->buffer_length_high; 369 | break; 370 | case DISK_BUFFER_LEN_LOW: 371 | *result = vio->buffer_length_low; 372 | break; 373 | case DISK_SECTOR: 374 | *result = vio->sector; 375 | break; 376 | case DISK_DONE: 377 | *result = vio->done; 378 | break; 379 | default: 380 | *result = 0; 381 | } 382 | return OK; 383 | } 384 | 385 | exception_t disk_store(struct disk *vio, 386 | const uint64_t addr, 387 | const uint64_t size, 388 | const uint64_t value) 389 | { 390 | if (size != 32) 391 | return STORE_AMO_ACCESS_FAULT; 392 | 393 | switch (addr) { 394 | case DISK_NOTIFY: 395 | vio->notify = value; 396 | break; 397 | case DISK_DIRECTION: 398 | vio->direction = value; 399 | break; 400 | case DISK_BUFFER_ADDR_HIGH: 401 | vio->buffer_address_high = value; 402 | break; 403 | case DISK_BUFFER_ADDR_LOW: 404 | vio->buffer_address_low = value; 405 | break; 406 | case DISK_BUFFER_LEN_HIGH: 407 | vio->buffer_length_high = value; 408 | break; 409 | case DISK_BUFFER_LEN_LOW: 410 | vio->buffer_length_low = value; 411 | break; 412 | case DISK_SECTOR: 413 | vio->sector = value; 414 | break; 415 | case DISK_DONE: 416 | vio->done = value; 417 | break; 418 | } 419 | return OK; 420 | } 421 | 422 | static inline bool disk_is_interrupting(struct disk *vio) 423 | { 424 | if (vio->notify != -1) { 425 | vio->notify = -1; 426 | return true; 427 | } 428 | return false; 429 | } 430 | 431 | static inline uint64_t disk_read(const struct disk *vio, uint64_t addr) 432 | { 433 | uint8_t buffer; 434 | fseek(vio->disk, addr, SEEK_SET); 435 | fread(&buffer, 1, 1, vio->disk); 436 | return (uint64_t) buffer; 437 | } 438 | 439 | static inline void disk_write(struct disk *vio, 440 | uint64_t addr, 441 | uint64_t value) 442 | { 443 | uint8_t buffer = (uint8_t) value; 444 | fseek(vio->disk, addr, SEEK_SET); 445 | fwrite(&buffer, 1, 1, vio->disk); 446 | } 447 | 448 | exception_t kbd_load(const uint64_t addr, 449 | const uint64_t size, 450 | uint64_t *result) 451 | { 452 | if (size != 32) 453 | return LOAD_ACCESS_FAULT; 454 | 455 | switch (addr) { 456 | case KBD_GET: 457 | *result = (uint64_t) key_take(); 458 | break; 459 | default: 460 | *result = 0; 461 | } 462 | return OK; 463 | } 464 | 465 | struct bus *bus_new(struct ram *ram, struct disk *vio) 466 | { 467 | struct bus *bus = calloc(1, sizeof(struct bus)); 468 | bus->ram = ram, bus->disk = vio; 469 | bus->clint = clint_new(), bus->plic = plic_new(), bus->uart = uart_new(); 470 | return bus; 471 | } 472 | 473 | exception_t bus_load(const struct bus *bus, 474 | const uint64_t addr, 475 | const uint64_t size, 476 | uint64_t *result) 477 | { 478 | if (RANGE_CHECK(addr, CLINT_BASE, CLINT_SIZE)) 479 | return clint_load(bus->clint, addr, size, result); 480 | if (RANGE_CHECK(addr, PLIC_BASE, PLIC_SIZE)) 481 | return plic_load(bus->plic, addr, size, result); 482 | if (RANGE_CHECK(addr, UART_BASE, UART_SIZE)) 483 | return uart_load(bus->uart, addr, size, result); 484 | if (RANGE_CHECK(addr, DISK_BASE, DISK_SIZE)) 485 | return disk_load(bus->disk, addr, size, result); 486 | if (RANGE_CHECK(addr, KBD_BASE, KBD_SIZE)) 487 | return kbd_load(addr, size, result); 488 | if (RAM_BASE <= addr) 489 | return ram_load(bus->ram, addr, size, result); 490 | 491 | return LOAD_ACCESS_FAULT; 492 | } 493 | 494 | exception_t bus_store(struct bus *bus, 495 | const uint64_t addr, 496 | const uint64_t size, 497 | const uint64_t value) 498 | { 499 | if (RANGE_CHECK(addr, CLINT_BASE, CLINT_SIZE)) 500 | return clint_store(bus->clint, addr, size, value); 501 | if (RANGE_CHECK(addr, PLIC_BASE, PLIC_SIZE)) 502 | return plic_store(bus->plic, addr, size, value); 503 | if (RANGE_CHECK(addr, UART_BASE, UART_SIZE)) 504 | return uart_store(bus->uart, addr, size, value); 505 | if (RANGE_CHECK(addr, DISK_BASE, DISK_SIZE)) 506 | return disk_store(bus->disk, addr, size, value); 507 | if (RAM_BASE <= addr) 508 | return ram_store(bus->ram, addr, size, value); 509 | 510 | return STORE_AMO_ACCESS_FAULT; 511 | } 512 | 513 | void bus_disk_access(struct bus *bus) 514 | { 515 | uint32_t address_high; 516 | uint32_t address_low; 517 | uint64_t address; 518 | if (bus_load(bus, DISK_BUFFER_ADDR_HIGH, 32, &address_high) != OK) 519 | fatal("read high address"); 520 | if (bus_load(bus, DISK_BUFFER_ADDR_LOW, 32, &address_low) != OK) 521 | fatal("read low address"); 522 | address = address_high << 32 | address_low; 523 | 524 | uint32_t length_high; 525 | uint32_t length_low; 526 | uint64_t length; 527 | if (bus_load(bus, DISK_BUFFER_LEN_HIGH, 32, &length_high) != OK) 528 | fatal("read high length"); 529 | if (bus_load(bus, DISK_BUFFER_LEN_LOW, 32, &length_low) != OK) 530 | fatal("read low length"); 531 | length = length_high << 32 | length_low; 532 | 533 | uint32_t sector; 534 | if (bus_load(bus, DISK_SECTOR, 32, §or) != OK) 535 | fatal("read sector"); 536 | 537 | uint32_t direction; 538 | if (bus_load(bus, DISK_DIRECTION, 32, &direction) != OK) 539 | fatal("read direction"); 540 | 541 | if (direction == 1) { 542 | /* Read RAM data and write it to a disk directly (DMA). */ 543 | for (uint64_t i = 0; i < length; i++) { 544 | uint64_t data; 545 | if (bus_load(bus, address + i, 8, &data) != OK) 546 | fatal("read from RAM"); 547 | disk_write(bus->disk, sector * 512 + i, data); 548 | } 549 | } else { 550 | /* Read disk data and write it to RAM directly (DMA). */ 551 | for (uint64_t i = 0; i < length; i++) { 552 | uint64_t data = disk_read(bus->disk, sector * 512 + i); 553 | if (bus_store(bus, address + i, 8, data) != OK) 554 | fatal("write to RAM"); 555 | } 556 | } 557 | 558 | if (bus_store(bus, DISK_DONE, 32, 0) != OK) 559 | fatal("write done"); 560 | } 561 | 562 | struct cpu *cpu_new(uint8_t *code, const size_t code_size, FILE *disk) 563 | { 564 | struct cpu *cpu = calloc(1, sizeof(struct cpu)); 565 | 566 | /* Initialize the sp(x2) register. */ 567 | cpu->regs[2] = RAM_BASE + RAM_SIZE; 568 | 569 | cpu->bus = bus_new(ram_new(code, code_size), disk_new(disk)); 570 | cpu->pc = RAM_BASE, cpu->mode = MACHINE; 571 | 572 | return cpu; 573 | } 574 | 575 | static uint64_t cpu_load_csr(const struct cpu *cpu, const uint16_t addr); 576 | static inline void cpu_update_paging(struct cpu *cpu, const uint16_t csr_addr) 577 | { 578 | if (csr_addr != SATP) 579 | return; 580 | 581 | cpu->pagetable = 582 | (cpu_load_csr(cpu, SATP) & (((uint64_t) 1 << 44) - 1)) * PAGE_SIZE; 583 | cpu->enable_paging = (8 == (cpu_load_csr(cpu, SATP) >> 60)); 584 | } 585 | 586 | exception_t cpu_translate(const struct cpu *cpu, 587 | const uint64_t addr, 588 | const exception_t e, 589 | uint64_t *result) 590 | { 591 | if (!cpu->enable_paging) { 592 | *result = addr; 593 | return OK; 594 | } 595 | 596 | const uint64_t vpn[] = { 597 | (addr >> 12) & 0x1ff, 598 | (addr >> 21) & 0x1ff, 599 | (addr >> 30) & 0x1ff, 600 | }; 601 | int level = sizeof(vpn) / sizeof(vpn[0]) - 1; 602 | 603 | uint64_t a = cpu->pagetable; 604 | uint64_t pte; 605 | while (1) { 606 | exception_t exc = bus_load(cpu->bus, a + vpn[level] * 8, 64, &pte); 607 | if (exc != OK) 608 | return exc; 609 | bool v = pte & 1; 610 | bool r = (pte >> 1) & 1, w = (pte >> 2) & 1, x = (pte >> 3) & 1; 611 | if (!v || (!r && w)) 612 | return e; 613 | 614 | if (r || x) 615 | break; 616 | 617 | a = ((pte >> 10) & 0x0fffffffffff) * PAGE_SIZE; 618 | if (--level < 0) 619 | return e; 620 | } 621 | 622 | const uint64_t ppn[] = { 623 | (pte >> 10) & 0x1ff, 624 | (pte >> 19) & 0x1ff, 625 | (pte >> 28) & 0x03ffffff, 626 | }; 627 | 628 | uint64_t offset = addr & 0xfff; 629 | switch (level) { 630 | case 0: 631 | *result = (((pte >> 10) & 0x0fffffffffff) << 12) | offset; 632 | return OK; 633 | case 1: 634 | *result = (ppn[2] << 30) | (ppn[1] << 21) | (vpn[0] << 12) | offset; 635 | return OK; 636 | case 2: 637 | *result = (ppn[2] << 30) | (vpn[1] << 21) | (vpn[0] << 12) | offset; 638 | return OK; 639 | default: 640 | return e; 641 | } 642 | } 643 | 644 | /* Fetch an instruction from current PC from RAM. */ 645 | exception_t cpu_fetch(struct cpu *cpu, uint64_t *result) 646 | { 647 | uint64_t ppc; 648 | exception_t e = cpu_translate(cpu, cpu->pc, INSTRUCTION_PAGE_FAULT, &ppc); 649 | if (e != OK) 650 | return e; 651 | 652 | if (bus_load(cpu->bus, ppc, 32, result) != OK) 653 | return INSTRUCTION_ACCESS_FAULT; 654 | return OK; 655 | } 656 | 657 | static inline uint64_t cpu_load_csr(const struct cpu *cpu, const uint16_t addr) 658 | { 659 | if (addr == SIE) 660 | return cpu->csrs[MIE] & cpu->csrs[MIDELEG]; 661 | return cpu->csrs[addr]; 662 | } 663 | 664 | static inline void cpu_store_csr(struct cpu *cpu, 665 | const uint16_t addr, 666 | const uint64_t value) 667 | { 668 | if (addr == SIE) { 669 | cpu->csrs[MIE] = (cpu->csrs[MIE] & ~cpu->csrs[MIDELEG]) | 670 | (value & cpu->csrs[MIDELEG]); 671 | return; 672 | } 673 | cpu->csrs[addr] = value; 674 | } 675 | 676 | static inline exception_t cpu_load(struct cpu *cpu, 677 | const uint64_t addr, 678 | const uint64_t size, 679 | uint64_t *result) 680 | { 681 | uint64_t pa; 682 | exception_t e = cpu_translate(cpu, addr, LOAD_PAGE_FAULT, &pa); 683 | if (e != OK) 684 | return e; 685 | return bus_load(cpu->bus, pa, size, result); 686 | } 687 | 688 | static inline exception_t cpu_store(struct cpu *cpu, 689 | const uint64_t addr, 690 | const uint64_t size, 691 | const uint64_t value) 692 | { 693 | uint64_t pa; 694 | exception_t e = cpu_translate(cpu, addr, STORE_AMO_PAGE_FAULT, &pa); 695 | if (e != OK) 696 | return e; 697 | 698 | return bus_store(cpu->bus, pa, size, value); 699 | } 700 | 701 | exception_t cpu_execute(struct cpu *cpu, const uint64_t insn) 702 | { 703 | uint64_t opcode = insn & 0x7f; 704 | uint64_t rd = (insn >> 7) & 0x1f; 705 | uint64_t rs1 = (insn >> 15) & 0x1f, rs2 = (insn >> 20) & 0x1f; 706 | uint64_t funct3 = (insn >> 12) & 0x7, funct7 = (insn >> 25) & 0x7f; 707 | 708 | cpu->regs[0] = 0; /* x0 register is always zero */ 709 | 710 | exception_t e; 711 | switch (opcode) { 712 | case 0x03: { 713 | uint64_t imm = (int32_t) insn >> 20; 714 | uint64_t addr = cpu->regs[rs1] + imm; 715 | switch (funct3) { 716 | case 0x0: /* lb */ { 717 | uint64_t result; 718 | if ((e = cpu_load(cpu, addr, 8, &result)) != OK) 719 | return e; 720 | cpu->regs[rd] = (int8_t) result; 721 | break; 722 | } 723 | case 0x1: /* lh */ { 724 | uint64_t result; 725 | if ((e = cpu_load(cpu, addr, 16, &result)) != OK) 726 | return e; 727 | cpu->regs[rd] = (int16_t) result; 728 | break; 729 | } 730 | case 0x2: /* lw */ { 731 | uint64_t result; 732 | if ((e = cpu_load(cpu, addr, 32, &result)) != OK) 733 | return e; 734 | cpu->regs[rd] = (int32_t) result; 735 | break; 736 | } 737 | case 0x3: /* ld */ { 738 | uint64_t result; 739 | if ((e = cpu_load(cpu, addr, 64, &result)) != OK) 740 | return e; 741 | cpu->regs[rd] = result; 742 | break; 743 | } 744 | case 0x4: /* lbu */ { 745 | uint64_t result; 746 | if ((e = cpu_load(cpu, addr, 8, &result)) != OK) 747 | return e; 748 | cpu->regs[rd] = result; 749 | break; 750 | } 751 | case 0x5: /* lhu */ { 752 | uint64_t result; 753 | if ((e = cpu_load(cpu, addr, 16, &result)) != OK) 754 | return e; 755 | cpu->regs[rd] = result; 756 | break; 757 | } 758 | case 0x6: /* lwu */ { 759 | uint64_t result; 760 | if ((e = cpu_load(cpu, addr, 32, &result)) != OK) 761 | return e; 762 | cpu->regs[rd] = result; 763 | break; 764 | } 765 | default: 766 | return ILLEGAL_INSTRUCTION; 767 | } 768 | break; 769 | } 770 | case 0x0f: 771 | switch (funct3) { 772 | case 0x0: /* fence */ 773 | /* TODO: implemented */ 774 | break; 775 | default: 776 | return ILLEGAL_INSTRUCTION; 777 | } 778 | break; 779 | case 0x13: { 780 | uint64_t imm = (int32_t) (insn & 0xfff00000) >> 20; 781 | uint32_t shamt = imm & 0x3f; 782 | 783 | switch (funct3) { 784 | case 0x0: /* addi */ 785 | cpu->regs[rd] = cpu->regs[rs1] + imm; 786 | break; 787 | case 0x1: /* slli */ 788 | cpu->regs[rd] = cpu->regs[rs1] << shamt; 789 | break; 790 | case 0x2: /* slti */ 791 | cpu->regs[rd] = !!((int64_t) cpu->regs[rs1] < (int64_t) imm); 792 | break; 793 | case 0x3: /* sltiu */ 794 | cpu->regs[rd] = !!(cpu->regs[rs1] < imm); 795 | break; 796 | case 0x4: /* xori */ 797 | cpu->regs[rd] = cpu->regs[rs1] ^ imm; 798 | break; 799 | case 0x5: 800 | switch (funct7 >> 1) { 801 | case 0x00: /* srli */ 802 | cpu->regs[rd] = cpu->regs[rs1] >> shamt; 803 | break; 804 | case 0x10: /* srai */ 805 | cpu->regs[rd] = (int64_t) (cpu->regs[rs1]) >> shamt; 806 | break; 807 | default: 808 | return ILLEGAL_INSTRUCTION; 809 | } 810 | break; 811 | case 0x6: /* ori */ 812 | cpu->regs[rd] = cpu->regs[rs1] | imm; 813 | break; 814 | case 0x7: /* andi */ 815 | cpu->regs[rd] = cpu->regs[rs1] & imm; 816 | break; 817 | default: 818 | return ILLEGAL_INSTRUCTION; 819 | } 820 | break; 821 | } 822 | case 0x17: /* auipc */ { 823 | uint64_t imm = (int32_t) (insn & 0xfffff000); 824 | cpu->regs[rd] = cpu->pc + imm - 4; 825 | break; 826 | } 827 | case 0x1b: { 828 | uint64_t imm = (int32_t) insn >> 20; 829 | uint32_t shamt = imm & 0x1f; 830 | switch (funct3) { 831 | case 0x0: /* addiw */ 832 | cpu->regs[rd] = (int32_t) (cpu->regs[rs1] + imm); 833 | break; 834 | case 0x1: /* slliw */ 835 | cpu->regs[rd] = (int32_t) (cpu->regs[rs1] << shamt); 836 | break; 837 | case 0x5: { 838 | switch (funct7) { 839 | case 0x00: /* srliw */ 840 | cpu->regs[rd] = (int32_t) ((uint32_t) cpu->regs[rs1] >> shamt); 841 | break; 842 | case 0x20: /* sraiw */ 843 | cpu->regs[rd] = (int32_t) (cpu->regs[rs1]) >> shamt; 844 | break; 845 | default: 846 | return ILLEGAL_INSTRUCTION; 847 | } 848 | break; 849 | } 850 | default: 851 | return ILLEGAL_INSTRUCTION; 852 | } 853 | break; 854 | } 855 | case 0x23: { 856 | uint64_t imm = (uint64_t) ((int32_t) (insn & 0xfe000000) >> 20) | 857 | ((insn >> 7) & 0x1f); 858 | uint64_t addr = cpu->regs[rs1] + imm; 859 | switch (funct3) { 860 | case 0x0: /* sb */ 861 | if ((e = cpu_store(cpu, addr, 8, cpu->regs[rs2])) != OK) 862 | return e; 863 | break; 864 | case 0x1: /* sh */ 865 | if ((e = cpu_store(cpu, addr, 16, cpu->regs[rs2])) != OK) 866 | return e; 867 | break; 868 | case 0x2: /* sw */ 869 | if ((e = cpu_store(cpu, addr, 32, cpu->regs[rs2])) != OK) 870 | return e; 871 | break; 872 | case 0x3: /* sd */ 873 | if ((e = cpu_store(cpu, addr, 64, cpu->regs[rs2])) != OK) 874 | return e; 875 | break; 876 | default: 877 | return ILLEGAL_INSTRUCTION; 878 | } 879 | break; 880 | } 881 | case 0x2f: { 882 | uint64_t funct5 = (funct7 & 0x7c) >> 2; 883 | if (funct3 == 0x2 && funct5 == 0x00) { /* amoadd.w */ 884 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 885 | return LOAD_ADDRESS_MISALIGNED; 886 | uint64_t t; 887 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 888 | return e; 889 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, t + cpu->regs[rs2])) != 890 | OK) 891 | return e; 892 | cpu->regs[rd] = (int32_t) t; 893 | } else if (funct3 == 0x2 && funct5 == 0x01) { /* amoswap.w */ 894 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 895 | return LOAD_ADDRESS_MISALIGNED; 896 | uint64_t t; 897 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 898 | return e; 899 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, cpu->regs[rs2])) != OK) 900 | return e; 901 | cpu->regs[rd] = (int32_t) t; 902 | } else if (funct3 == 0x2 && funct5 == 0x04) { /* amoxor.w */ 903 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 904 | return LOAD_ADDRESS_MISALIGNED; 905 | uint64_t t; 906 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 907 | return e; 908 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, t ^ cpu->regs[rs2])) != 909 | OK) 910 | return e; 911 | cpu->regs[rd] = (int32_t) t; 912 | } else if (funct3 == 0x2 && funct5 == 0x08) { /* amoor.w */ 913 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 914 | return LOAD_ADDRESS_MISALIGNED; 915 | uint64_t t; 916 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 917 | return e; 918 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, t | cpu->regs[rs2])) != 919 | OK) 920 | return e; 921 | cpu->regs[rd] = (int32_t) t; 922 | } else if (funct3 == 0x2 && funct5 == 0x0c) { /* amoand.w */ 923 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 924 | return LOAD_ADDRESS_MISALIGNED; 925 | uint64_t t; 926 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 927 | return e; 928 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, t & cpu->regs[rs2])) != 929 | OK) 930 | return e; 931 | cpu->regs[rd] = (int32_t) t; 932 | } else if (funct3 == 0x2 && funct5 == 0x10) { /* amomin.w */ 933 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 934 | return LOAD_ADDRESS_MISALIGNED; 935 | uint64_t t; 936 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 937 | return e; 938 | int32_t min = MIN((int32_t) t, (int32_t) cpu->regs[rs2]); 939 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, min)) != OK) 940 | return e; 941 | cpu->regs[rd] = (int32_t) t; 942 | } else if (funct3 == 0x2 && funct5 == 0x14) { /* amomax.w */ 943 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 944 | return LOAD_ADDRESS_MISALIGNED; 945 | uint64_t t; 946 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 947 | return e; 948 | int32_t max = MAX((int32_t) t, (int32_t) cpu->regs[rs2]); 949 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, max)) != OK) 950 | return e; 951 | cpu->regs[rd] = (int32_t) t; 952 | } else if (funct3 == 0x2 && funct5 == 0x18) { /* amominu.w */ 953 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 954 | return LOAD_ADDRESS_MISALIGNED; 955 | uint64_t t; 956 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 957 | return e; 958 | uint32_t min = MIN((uint32_t) t, (uint32_t) cpu->regs[rs2]); 959 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, min)) != OK) 960 | return e; 961 | cpu->regs[rd] = (int32_t) t; 962 | } else if (funct3 == 0x2 && funct5 == 0x1c) { /* amomaxu.w */ 963 | if (!IS_ALIGNED(cpu->regs[rs1], 4)) 964 | return LOAD_ADDRESS_MISALIGNED; 965 | uint64_t t; 966 | if ((e = cpu_load(cpu, cpu->regs[rs1], 32, &t)) != OK) 967 | return e; 968 | uint32_t max = MAX((uint32_t) t, (uint32_t) cpu->regs[rs2]); 969 | if ((e = cpu_store(cpu, cpu->regs[rs1], 32, max)) != OK) 970 | return e; 971 | cpu->regs[rd] = (int32_t) t; 972 | } else if (funct3 == 0x3 && funct5 == 0x00) { /* amoadd.d */ 973 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 974 | return LOAD_ADDRESS_MISALIGNED; 975 | uint64_t t; 976 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 977 | return e; 978 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, t + cpu->regs[rs2])) != 979 | OK) 980 | return e; 981 | cpu->regs[rd] = t; 982 | } else if (funct3 == 0x3 && funct5 == 0x01) { /* amoswap.d */ 983 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 984 | return LOAD_ADDRESS_MISALIGNED; 985 | uint64_t t; 986 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 987 | return e; 988 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, cpu->regs[rs2])) != OK) 989 | return e; 990 | cpu->regs[rd] = t; 991 | } else if (funct3 == 0x3 && funct5 == 0x04) { /* amoxor.d */ 992 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 993 | return LOAD_ADDRESS_MISALIGNED; 994 | uint64_t t; 995 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 996 | return e; 997 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, t ^ cpu->regs[rs2])) != 998 | OK) 999 | return e; 1000 | cpu->regs[rd] = t; 1001 | } else if (funct3 == 0x3 && funct5 == 0x08) { /* amoor.d */ 1002 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 1003 | return LOAD_ADDRESS_MISALIGNED; 1004 | uint64_t t; 1005 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 1006 | return e; 1007 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, t | cpu->regs[rs2])) != 1008 | OK) 1009 | return e; 1010 | cpu->regs[rd] = t; 1011 | } else if (funct3 == 0x3 && funct5 == 0x0c) { /* amoand.d */ 1012 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 1013 | return LOAD_ADDRESS_MISALIGNED; 1014 | uint64_t t; 1015 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 1016 | return e; 1017 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, t & cpu->regs[rs2])) != 1018 | OK) 1019 | return e; 1020 | cpu->regs[rd] = t; 1021 | } else if (funct3 == 0x3 && funct5 == 0x10) { /* amomin.d */ 1022 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 1023 | return LOAD_ADDRESS_MISALIGNED; 1024 | uint64_t t; 1025 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 1026 | return e; 1027 | int64_t min = MIN((int64_t) t, (int64_t) cpu->regs[rs2]); 1028 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, min)) != OK) 1029 | return e; 1030 | cpu->regs[rd] = t; 1031 | } else if (funct3 == 0x3 && funct5 == 0x14) { /* amomax.d */ 1032 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 1033 | return LOAD_ADDRESS_MISALIGNED; 1034 | uint64_t t; 1035 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 1036 | return e; 1037 | int64_t max = MAX((int64_t) t, (int64_t) cpu->regs[rs2]); 1038 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, max)) != OK) 1039 | return e; 1040 | cpu->regs[rd] = t; 1041 | } else if (funct3 == 0x3 && funct5 == 0x18) { /* amominu.d */ 1042 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 1043 | return LOAD_ADDRESS_MISALIGNED; 1044 | uint64_t t, min; 1045 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 1046 | return e; 1047 | min = MIN((uint64_t) t, (uint64_t) cpu->regs[rs2]); 1048 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, min)) != OK) 1049 | return e; 1050 | cpu->regs[rd] = t; 1051 | } else if (funct3 == 0x3 && funct5 == 0x1c) { /* amomaxu.d */ 1052 | if (!IS_ALIGNED(cpu->regs[rs1], 8)) 1053 | return LOAD_ADDRESS_MISALIGNED; 1054 | uint64_t t, max; 1055 | if ((e = cpu_load(cpu, cpu->regs[rs1], 64, &t)) != OK) 1056 | return e; 1057 | max = MAX((uint64_t) t, (uint64_t) cpu->regs[rs2]); 1058 | if ((e = cpu_store(cpu, cpu->regs[rs1], 64, max)) != OK) 1059 | return e; 1060 | cpu->regs[rd] = t; 1061 | } else { 1062 | return ILLEGAL_INSTRUCTION; 1063 | } 1064 | break; 1065 | } 1066 | case 0x33: { 1067 | uint32_t shamt = cpu->regs[rs2] & 0x3f; 1068 | if (funct3 == 0x0 && funct7 == 0x00) { /* add */ 1069 | cpu->regs[rd] = cpu->regs[rs1] + cpu->regs[rs2]; 1070 | } else if (funct3 == 0x0 && funct7 == 0x01) { /* mul */ 1071 | cpu->regs[rd] = cpu->regs[rs1] * cpu->regs[rs2]; 1072 | } else if (funct3 == 0x0 && funct7 == 0x20) { /* sub */ 1073 | cpu->regs[rd] = cpu->regs[rs1] - cpu->regs[rs2]; 1074 | } else if (funct3 == 0x1 && funct7 == 0x00) { /* sll */ 1075 | cpu->regs[rd] = cpu->regs[rs1] << shamt; 1076 | } else if (funct3 == 0x1 && funct7 == 0x01) { /* mulh */ 1077 | #if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ 1078 | cpu->regs[rd] = ((__int128) (int64_t) cpu->regs[rs1] * 1079 | (__int128) (int64_t) cpu->regs[rs2]) >> 1080 | 64; 1081 | #else 1082 | cpu->regs[rd] = 1083 | mulh((int64_t) cpu->regs[rs1], (int64_t) cpu->regs[rs2]); 1084 | #endif 1085 | } else if (funct3 == 0x2 && funct7 == 0x00) { /* slt */ 1086 | cpu->regs[rd] = 1087 | !!((int64_t) cpu->regs[rs1] < (int64_t) cpu->regs[rs2]); 1088 | } else if (funct3 == 0x2 && funct7 == 0x01) { /* mulhsu */ 1089 | #if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ 1090 | cpu->regs[rd] = ((__int128) (int64_t) cpu->regs[rs1] * 1091 | (unsigned __int128) cpu->regs[rs2]) >> 1092 | 64; 1093 | #else 1094 | cpu->regs[rd] = 1095 | mulhsu((int64_t) cpu->regs[rs1], (uint64_t) cpu->regs[rs2]); 1096 | #endif 1097 | } else if (funct3 == 0x3 && funct7 == 0x00) { /* sltu */ 1098 | cpu->regs[rd] = !!(cpu->regs[rs1] < cpu->regs[rs2]); 1099 | } else if (funct3 == 0x3 && funct7 == 0x01) { /* mulhu */ 1100 | #if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ 1101 | cpu->regs[rd] = ((unsigned __int128) cpu->regs[rs1] * 1102 | (unsigned __int128) cpu->regs[rs2]) >> 1103 | 64; 1104 | #else 1105 | cpu->regs[rd] = 1106 | mulhu((uint64_t) cpu->regs[rs1], (uint64_t) cpu->regs[rs2]); 1107 | 1108 | #endif 1109 | } else if (funct3 == 0x4 && funct7 == 0x00) { /* xor */ 1110 | cpu->regs[rd] = cpu->regs[rs1] ^ cpu->regs[rs2]; 1111 | } else if (funct3 == 0x4 && funct7 == 0x01) { /* div */ 1112 | int64_t dividend = (int64_t) cpu->regs[rs1]; 1113 | int64_t divisor = (int64_t) cpu->regs[rs2]; 1114 | if (divisor == 0) { 1115 | cpu->regs[rd] = -1; 1116 | } else if (dividend == INT64_MIN && divisor == -1) { /* overflow */ 1117 | cpu->regs[rd] = INT64_MIN; 1118 | } else { 1119 | cpu->regs[rd] = dividend / divisor; 1120 | } 1121 | } else if (funct3 == 0x5 && funct7 == 0x00) { /* srl */ 1122 | cpu->regs[rd] = cpu->regs[rs1] >> shamt; 1123 | } else if (funct3 == 0x5 && funct7 == 0x01) { /* divu */ 1124 | cpu->regs[rd] = (cpu->regs[rs2] == 0) 1125 | ? UINT64_MAX 1126 | : (cpu->regs[rs1] / cpu->regs[rs2]); 1127 | } else if (funct3 == 0x5 && funct7 == 0x20) { /* sra */ 1128 | cpu->regs[rd] = (int64_t) cpu->regs[rs1] >> shamt; 1129 | } else if (funct3 == 0x6 && funct7 == 0x01) { /* rem */ 1130 | if (cpu->regs[rs2] == 0) { 1131 | cpu->regs[rd] = cpu->regs[rs1]; 1132 | } else if ((int64_t) cpu->regs[rs1] == INT64_MIN && 1133 | (int64_t) cpu->regs[rs2] == -1) { /* overflow */ 1134 | cpu->regs[rd] = 0; 1135 | } else { 1136 | cpu->regs[rd] = 1137 | (int64_t) cpu->regs[rs1] % (int64_t) cpu->regs[rs2]; 1138 | } 1139 | } else if (funct3 == 0x6 && funct7 == 0x00) { /* or */ 1140 | cpu->regs[rd] = cpu->regs[rs1] | cpu->regs[rs2]; 1141 | } else if (funct3 == 0x7 && funct7 == 0x00) { /* and */ 1142 | cpu->regs[rd] = cpu->regs[rs1] & cpu->regs[rs2]; 1143 | } else if (funct3 == 0x7 && funct7 == 0x01) { /* remu */ 1144 | cpu->regs[rd] = (cpu->regs[rs2] == 0) 1145 | ? cpu->regs[rs1] 1146 | : (cpu->regs[rs1] % cpu->regs[rs2]); 1147 | } else { 1148 | return ILLEGAL_INSTRUCTION; 1149 | } 1150 | break; 1151 | } 1152 | case 0x37: /* lui */ 1153 | cpu->regs[rd] = (int32_t) (insn & 0xfffff000); 1154 | break; 1155 | case 0x3b: { 1156 | uint32_t shamt = cpu->regs[rs2] & 0x1f; 1157 | if (funct3 == 0x0 && funct7 == 0x00) { /* addw */ 1158 | cpu->regs[rd] = (int32_t) (cpu->regs[rs1] + cpu->regs[rs2]); 1159 | } else if (funct3 == 0x0 && funct7 == 0x01) { /* mulw */ 1160 | cpu->regs[rd] = (int32_t) cpu->regs[rs1] * (int32_t) cpu->regs[rs2]; 1161 | } else if (funct3 == 0x0 && funct7 == 0x20) { /* subw */ 1162 | cpu->regs[rd] = (int32_t) (cpu->regs[rs1] - cpu->regs[rs2]); 1163 | } else if (funct3 == 0x1 && funct7 == 0x00) { /* sllw */ 1164 | cpu->regs[rd] = (int32_t) ((uint32_t) cpu->regs[rs1] << shamt); 1165 | } else if (funct3 == 0x4 && funct7 == 0x01) { /* divw */ 1166 | if (cpu->regs[rs2] == 0) { 1167 | cpu->regs[rd] = -1; 1168 | } else if ((int32_t) cpu->regs[rs1] == INT32_MIN && 1169 | (int32_t) cpu->regs[rs2] == -1) { /* overflow */ 1170 | cpu->regs[rd] = INT32_MIN; 1171 | } else { 1172 | cpu->regs[rd] = 1173 | (int32_t) cpu->regs[rs1] / (int32_t) cpu->regs[rs2]; 1174 | } 1175 | } else if (funct3 == 0x5 && funct7 == 0x00) { /* srlw */ 1176 | cpu->regs[rd] = (int32_t) ((uint32_t) cpu->regs[rs1] >> shamt); 1177 | } else if (funct3 == 0x5 && funct7 == 0x01) { /* divuw */ 1178 | cpu->regs[rd] = (cpu->regs[rs2] == 0) 1179 | ? UINT64_MAX 1180 | : (int32_t) ((uint32_t) cpu->regs[rs1] / 1181 | (uint32_t) cpu->regs[rs2]); 1182 | } else if (funct3 == 0x5 && funct7 == 0x20) { /* sraw */ 1183 | cpu->regs[rd] = (int32_t) cpu->regs[rs1] >> (int32_t) shamt; 1184 | } else if (funct3 == 0x6 && funct7 == 0x01) { /* remw */ 1185 | if (cpu->regs[rs2] == 0) { 1186 | cpu->regs[rd] = cpu->regs[rs1]; 1187 | } else if ((int32_t) cpu->regs[rs1] == INT32_MIN && 1188 | (int32_t) cpu->regs[rs2] == -1) { /* overflow */ 1189 | cpu->regs[rd] = 0; 1190 | } else { 1191 | cpu->regs[rd] = 1192 | (int32_t) cpu->regs[rs1] % (int32_t) cpu->regs[rs2]; 1193 | } 1194 | } else if (funct3 == 0x7 && funct7 == 0x01) { /* remuw */ 1195 | cpu->regs[rd] = (cpu->regs[rs2] == 0) 1196 | ? cpu->regs[rs1] 1197 | : (int32_t) ((uint32_t) cpu->regs[rs1] % 1198 | (uint32_t) cpu->regs[rs2]); 1199 | } else { 1200 | return ILLEGAL_INSTRUCTION; 1201 | } 1202 | break; 1203 | } 1204 | case 0x63: { 1205 | uint64_t imm = (uint64_t) ((int32_t) (insn & 0x80000000) >> 19) | 1206 | ((insn & 0x80) << 4) | ((insn >> 20) & 0x7e0) | 1207 | ((insn >> 7) & 0x1e); 1208 | 1209 | switch (funct3) { 1210 | case 0x0: /* beq */ 1211 | if (cpu->regs[rs1] == cpu->regs[rs2]) 1212 | cpu->pc += imm - 4; 1213 | break; 1214 | case 0x1: /* bne */ 1215 | if (cpu->regs[rs1] != cpu->regs[rs2]) 1216 | cpu->pc += imm - 4; 1217 | break; 1218 | case 0x4: /* blt */ 1219 | if ((int64_t) cpu->regs[rs1] < (int64_t) cpu->regs[rs2]) 1220 | cpu->pc += imm - 4; 1221 | break; 1222 | case 0x5: /* bge */ 1223 | if ((int64_t) cpu->regs[rs1] >= (int64_t) cpu->regs[rs2]) 1224 | cpu->pc += imm - 4; 1225 | break; 1226 | case 0x6: /* bltu */ 1227 | if (cpu->regs[rs1] < cpu->regs[rs2]) 1228 | cpu->pc += imm - 4; 1229 | break; 1230 | case 0x7: /* bgeu */ 1231 | if (cpu->regs[rs1] >= cpu->regs[rs2]) 1232 | cpu->pc += imm - 4; 1233 | break; 1234 | default: 1235 | return ILLEGAL_INSTRUCTION; 1236 | } 1237 | break; 1238 | } 1239 | case 0x67: { /* jalr */ 1240 | uint64_t t = cpu->pc; 1241 | uint64_t imm = (int32_t) (insn & 0xfff00000) >> 20; 1242 | cpu->pc = (cpu->regs[rs1] + imm) & ~1; 1243 | 1244 | cpu->regs[rd] = t; 1245 | break; 1246 | } 1247 | case 0x6f: { /* jal */ 1248 | cpu->regs[rd] = cpu->pc; 1249 | 1250 | uint64_t imm = (uint64_t) ((int32_t) (insn & 0x80000000) >> 11) | 1251 | (insn & 0xff000) | ((insn >> 9) & 0x800) | 1252 | ((insn >> 20) & 0x7fe); 1253 | 1254 | cpu->pc += imm - 4; 1255 | break; 1256 | } 1257 | case 0x73: { 1258 | uint16_t addr = (insn & 0xfff00000) >> 20; 1259 | switch (funct3) { 1260 | case 0x0: 1261 | if (rs2 == 0x0 && funct7 == 0x0) { /* ecall */ 1262 | switch (cpu->mode) { 1263 | case USER: 1264 | case SUPERVISOR: 1265 | case MACHINE: 1266 | return 8 + cpu->mode; /* ECALL_FROM_{U,S,M}MODE */ 1267 | } 1268 | } else if (rs2 == 0x1 && funct7 == 0x0) { /* ebreak */ 1269 | return BREAKPOINT; 1270 | } else if (rs2 == 0x2 && funct7 == 0x8) { /* sret */ 1271 | cpu->pc = cpu_load_csr(cpu, SEPC); 1272 | cpu->mode = 1273 | ((cpu_load_csr(cpu, SSTATUS) >> 8) & 1) ? SUPERVISOR : USER; 1274 | cpu_store_csr(cpu, SSTATUS, 1275 | ((cpu_load_csr(cpu, SSTATUS) >> 5) & 1) 1276 | ? cpu_load_csr(cpu, SSTATUS) | (1 << 1) 1277 | : cpu_load_csr(cpu, SSTATUS) & ~(1 << 1)); 1278 | cpu_store_csr(cpu, SSTATUS, 1279 | cpu_load_csr(cpu, SSTATUS) | (1 << 5)); 1280 | cpu_store_csr(cpu, SSTATUS, 1281 | cpu_load_csr(cpu, SSTATUS) & ~(1 << 8)); 1282 | } else if (rs2 == 0x2 && funct7 == 0x18) { /* mret */ 1283 | cpu->pc = cpu_load_csr(cpu, MEPC); 1284 | uint64_t mpp = (cpu_load_csr(cpu, MSTATUS) >> 11) & 3; 1285 | cpu->mode = mpp == 2 ? MACHINE : (mpp == 1 ? SUPERVISOR : USER); 1286 | cpu_store_csr(cpu, MSTATUS, 1287 | ((cpu_load_csr(cpu, MSTATUS) >> 7) & 1) 1288 | ? cpu_load_csr(cpu, MSTATUS) | (1 << 3) 1289 | : cpu_load_csr(cpu, MSTATUS) & ~(1 << 3)); 1290 | cpu_store_csr(cpu, MSTATUS, 1291 | cpu_load_csr(cpu, MSTATUS) | (1 << 7)); 1292 | cpu_store_csr(cpu, MSTATUS, 1293 | cpu_load_csr(cpu, MSTATUS) & ~(3 << 11)); 1294 | } else if (funct7 == 0x9) { /* sfence.vma */ 1295 | /* TODO: implemented */ 1296 | } else { 1297 | return ILLEGAL_INSTRUCTION; 1298 | } 1299 | break; 1300 | case 0x1: { /* csrrw */ 1301 | uint64_t t = cpu_load_csr(cpu, addr); 1302 | cpu_store_csr(cpu, addr, cpu->regs[rs1]); 1303 | cpu->regs[rd] = t; 1304 | cpu_update_paging(cpu, addr); 1305 | break; 1306 | } 1307 | case 0x2: { /* csrrs */ 1308 | uint64_t t = cpu_load_csr(cpu, addr); 1309 | cpu_store_csr(cpu, addr, t | cpu->regs[rs1]); 1310 | cpu->regs[rd] = t; 1311 | cpu_update_paging(cpu, addr); 1312 | break; 1313 | } 1314 | case 0x3: { /* csrrc */ 1315 | uint64_t t = cpu_load_csr(cpu, addr); 1316 | cpu_store_csr(cpu, addr, t & ~cpu->regs[rs1]); 1317 | cpu->regs[rd] = t; 1318 | cpu_update_paging(cpu, addr); 1319 | break; 1320 | } 1321 | case 0x5: /* csrrwi */ 1322 | cpu->regs[rd] = cpu_load_csr(cpu, addr); 1323 | cpu_store_csr(cpu, addr, rs1); 1324 | cpu_update_paging(cpu, addr); 1325 | break; 1326 | case 0x6: { /* csrrsi */ 1327 | uint64_t t = cpu_load_csr(cpu, addr); 1328 | cpu_store_csr(cpu, addr, t | rs1); 1329 | cpu->regs[rd] = t; 1330 | cpu_update_paging(cpu, addr); 1331 | break; 1332 | } 1333 | case 0x7: { /* csrrci */ 1334 | uint64_t t = cpu_load_csr(cpu, addr); 1335 | cpu_store_csr(cpu, addr, t & ~rs1); 1336 | cpu->regs[rd] = t; 1337 | cpu_update_paging(cpu, addr); 1338 | break; 1339 | } 1340 | default: 1341 | return ILLEGAL_INSTRUCTION; 1342 | } 1343 | break; 1344 | } 1345 | default: 1346 | return ILLEGAL_INSTRUCTION; 1347 | } 1348 | 1349 | return OK; 1350 | } 1351 | 1352 | void cpu_take_trap(struct cpu *cpu, const exception_t e, const interrupt_t intr) 1353 | { 1354 | uint64_t exception_pc = cpu->pc - 4; 1355 | cpu_mode_t prev_mode = cpu->mode; 1356 | 1357 | bool is_interrupt = (intr != NONE); 1358 | uint64_t cause = e; 1359 | if (is_interrupt) 1360 | cause = ((uint64_t) 1 << 63) | (uint64_t) intr; 1361 | 1362 | if (prev_mode <= SUPERVISOR && 1363 | (((cpu_load_csr(cpu, MEDELEG) >> (uint32_t) cause) & 1) != 0)) { 1364 | cpu->mode = SUPERVISOR; 1365 | if (is_interrupt) { 1366 | uint64_t vec = (cpu_load_csr(cpu, STVEC) & 1) ? (4 * cause) : 0; 1367 | cpu->pc = (cpu_load_csr(cpu, STVEC) & ~1) + vec; 1368 | } else { 1369 | cpu->pc = cpu_load_csr(cpu, STVEC) & ~1; 1370 | } 1371 | cpu_store_csr(cpu, SEPC, exception_pc & ~1); 1372 | cpu_store_csr(cpu, SCAUSE, cause); 1373 | cpu_store_csr(cpu, STVAL, 0); 1374 | cpu_store_csr(cpu, SSTATUS, 1375 | ((cpu_load_csr(cpu, SSTATUS) >> 1) & 1) 1376 | ? cpu_load_csr(cpu, SSTATUS) | (1 << 5) 1377 | : cpu_load_csr(cpu, SSTATUS) & ~(1 << 5)); 1378 | cpu_store_csr(cpu, SSTATUS, cpu_load_csr(cpu, SSTATUS) & ~(1 << 1)); 1379 | if (prev_mode == USER) { 1380 | cpu_store_csr(cpu, SSTATUS, cpu_load_csr(cpu, SSTATUS) & ~(1 << 8)); 1381 | } else { 1382 | cpu_store_csr(cpu, SSTATUS, cpu_load_csr(cpu, SSTATUS) | (1 << 8)); 1383 | } 1384 | } else { 1385 | cpu->mode = MACHINE; 1386 | 1387 | if (is_interrupt) { 1388 | uint64_t vec = (cpu_load_csr(cpu, MTVEC) & 1) ? 4 * cause : 0; 1389 | cpu->pc = (cpu_load_csr(cpu, MTVEC) & ~1) + vec; 1390 | } else { 1391 | cpu->pc = cpu_load_csr(cpu, MTVEC) & ~1; 1392 | } 1393 | cpu_store_csr(cpu, MEPC, exception_pc & ~1); 1394 | cpu_store_csr(cpu, MCAUSE, cause); 1395 | cpu_store_csr(cpu, MTVAL, 0); 1396 | cpu_store_csr(cpu, MSTATUS, 1397 | ((cpu_load_csr(cpu, MSTATUS) >> 3) & 1) 1398 | ? cpu_load_csr(cpu, MSTATUS) | (1 << 7) 1399 | : cpu_load_csr(cpu, MSTATUS) & ~(1 << 7)); 1400 | cpu_store_csr(cpu, MSTATUS, cpu_load_csr(cpu, MSTATUS) & ~(1 << 3)); 1401 | cpu_store_csr(cpu, MSTATUS, cpu_load_csr(cpu, MSTATUS) & ~(3 << 11)); 1402 | } 1403 | } 1404 | 1405 | enum { DISK_IRQ = 1, UART_IRQ = 10 }; 1406 | 1407 | interrupt_t cpu_check_pending_interrupt(struct cpu *cpu) 1408 | { 1409 | if (cpu->mode == MACHINE && ((cpu_load_csr(cpu, MSTATUS) >> 3) & 1) == 0) 1410 | return NONE; 1411 | if (cpu->mode == SUPERVISOR && ((cpu_load_csr(cpu, SSTATUS) >> 1) & 1) == 0) 1412 | return NONE; 1413 | 1414 | do { 1415 | uint64_t irq; 1416 | if (uart_is_interrupting(cpu->bus->uart)) { 1417 | irq = UART_IRQ; 1418 | } else if (disk_is_interrupting(cpu->bus->disk)) { 1419 | bus_disk_access(cpu->bus); 1420 | irq = DISK_IRQ; 1421 | } else 1422 | break; 1423 | 1424 | bus_store(cpu->bus, PLIC_SCLAIM, 32, irq); 1425 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) | MIP_SEIP); 1426 | } while (0); 1427 | 1428 | uint64_t pending = cpu_load_csr(cpu, MIE) & cpu_load_csr(cpu, MIP); 1429 | if (pending & MIP_MEIP) { 1430 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) & ~MIP_MEIP); 1431 | return MACHINE_EXTERNAL_INTERRUPT; 1432 | } 1433 | if (pending & MIP_MSIP) { 1434 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) & ~MIP_MSIP); 1435 | return MACHINE_SOFTWARE_INTERRUPT; 1436 | } 1437 | if (pending & MIP_MTIP) { 1438 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) & ~MIP_MTIP); 1439 | return MACHINE_TIMER_INTERRUPT; 1440 | } 1441 | if (pending & MIP_SEIP) { 1442 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) & ~MIP_SEIP); 1443 | return SUPERVISOR_EXTERNAL_INTERRUPT; 1444 | } 1445 | if (pending & MIP_SSIP) { 1446 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) & ~MIP_SSIP); 1447 | return SUPERVISOR_SOFTWARE_INTERRUPT; 1448 | } 1449 | if (pending & MIP_STIP) { 1450 | cpu_store_csr(cpu, MIP, cpu_load_csr(cpu, MIP) & ~MIP_STIP); 1451 | return SUPERVISOR_TIMER_INTERRUPT; 1452 | } 1453 | 1454 | return NONE; 1455 | } 1456 | 1457 | size_t read_file(FILE *f, uint8_t *r[]) 1458 | { 1459 | fseek(f, 0, SEEK_END); 1460 | long fsize = ftell(f); 1461 | fseek(f, 0, SEEK_SET); 1462 | 1463 | uint8_t *content = malloc(fsize + 1); 1464 | if (fread(content, fsize, 1, f) != 1) /* less than fsize bytes */ 1465 | fatal("read file content"); 1466 | 1467 | content[fsize] = 0; 1468 | *r = content; 1469 | 1470 | return fsize; 1471 | } 1472 | -------------------------------------------------------------------------------- /src/semu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CPU_HZ 33000000 4 | 5 | #define N_REG 32 6 | #define N_CSR 4096 7 | 8 | #define RAM_SIZE (1024 * 1024 * 8) 9 | #define RAM_BASE 0x80000000 10 | 11 | #define FRAMEBUFFER_BASE 0x80600000 12 | 13 | #define CLINT_BASE 0x2000000 14 | #define CLINT_SIZE 0x10000 15 | #define CLINT_MTIMECMP (CLINT_BASE + 0x4000) 16 | #define CLINT_MTIME (CLINT_BASE + 0xbff8) 17 | 18 | #define PLIC_BASE 0xC000000 19 | #define PLIC_SIZE 0x4000000 20 | #define PLIC_PENDING (PLIC_BASE + 0x1000) 21 | #define PLIC_SENABLE (PLIC_BASE + 0x2080) 22 | #define PLIC_SPRIORITY (PLIC_BASE + 0x201000) 23 | #define PLIC_SCLAIM (PLIC_BASE + 0x201004) 24 | 25 | #define UART_BASE 0x10000000 26 | #define UART_SIZE 0x100 27 | enum { UART_RHR = UART_BASE, UART_THR = UART_BASE }; 28 | enum { UART_LCR = UART_BASE + 3, UART_LSR = UART_BASE + 5 }; 29 | enum { UART_LSR_RX = 1, UART_LSR_TX = 1 << 5 }; 30 | 31 | #define DISK_BASE 0x10001000 32 | #define DISK_SIZE 0x100 33 | #define DISK_MAGIC (DISK_BASE + 0x000) 34 | #define DISK_VERSION (DISK_BASE + 0x004) 35 | #define DISK_NOTIFY (DISK_BASE + 0x008) 36 | #define DISK_DIRECTION (DISK_BASE + 0x00C) 37 | #define DISK_BUFFER_ADDR_HIGH (DISK_BASE + 0x010) 38 | #define DISK_BUFFER_ADDR_LOW (DISK_BASE + 0x014) 39 | #define DISK_BUFFER_LEN_HIGH (DISK_BASE + 0x018) 40 | #define DISK_BUFFER_LEN_LOW (DISK_BASE + 0x01C) 41 | #define DISK_SECTOR (DISK_BASE + 0x020) 42 | #define DISK_DONE (DISK_BASE + 0x024) 43 | 44 | #define KBD_BASE 0x10002000 45 | #define KBD_SIZE 0x100 46 | #define KBD_GET (KBD_BASE + 0x000) 47 | 48 | /* USER is a mode for application which runs on operating system. 49 | * SUPERVISOR is a mode for operating system. 50 | * MACHINE is a mode for RISC-V hart internal operation, sometimes called 51 | * kernal-mode or protect-mode in other architecture. 52 | */ 53 | typedef enum { USER = 0x0, SUPERVISOR = 0x1, MACHINE = 0x3 } cpu_mode_t; 54 | 55 | struct cpu { 56 | uint64_t regs[N_REG], pc; 57 | uint64_t csrs[N_CSR]; 58 | cpu_mode_t mode; 59 | struct bus *bus; 60 | bool enable_paging; 61 | uint64_t pagetable; 62 | }; 63 | 64 | struct bus { 65 | struct ram *ram; 66 | struct clint *clint; 67 | struct plic *plic; 68 | struct uart *uart; 69 | struct disk *disk; 70 | }; 71 | 72 | struct ram { 73 | uint8_t *data; 74 | }; 75 | 76 | struct clint { 77 | uint64_t mtime, mtimecmp; 78 | }; 79 | 80 | struct plic { 81 | uint64_t pending; 82 | uint64_t senable; 83 | uint64_t spriority; 84 | uint64_t sclaim; 85 | }; 86 | 87 | struct uart { 88 | uint8_t data[UART_SIZE]; 89 | bool interrupting; 90 | 91 | pthread_t tid; 92 | pthread_mutex_t lock; 93 | pthread_cond_t cond; 94 | }; 95 | 96 | struct disk { 97 | uint32_t buffer_address_high; 98 | uint32_t buffer_address_low; 99 | uint32_t buffer_length_high; 100 | uint32_t buffer_length_low; 101 | uint32_t sector; 102 | uint32_t notify; 103 | uint32_t direction; 104 | uint32_t done; 105 | FILE *disk; 106 | }; 107 | 108 | typedef enum { 109 | OK = -1, 110 | INSTRUCTION_ADDRESS_MISALIGNED = 0, 111 | INSTRUCTION_ACCESS_FAULT = 1, 112 | ILLEGAL_INSTRUCTION = 2, 113 | BREAKPOINT = 3, 114 | LOAD_ADDRESS_MISALIGNED = 4, 115 | LOAD_ACCESS_FAULT = 5, 116 | STORE_AMO_ADDRESS_MISALIGNED = 6, 117 | STORE_AMO_ACCESS_FAULT = 7, 118 | INSTRUCTION_PAGE_FAULT = 12, 119 | LOAD_PAGE_FAULT = 13, 120 | STORE_AMO_PAGE_FAULT = 15, 121 | } exception_t; 122 | 123 | typedef enum { 124 | NONE = -1, 125 | SUPERVISOR_SOFTWARE_INTERRUPT = 1, 126 | MACHINE_SOFTWARE_INTERRUPT = 3, 127 | SUPERVISOR_TIMER_INTERRUPT = 5, 128 | MACHINE_TIMER_INTERRUPT = 7, 129 | SUPERVISOR_EXTERNAL_INTERRUPT = 9, 130 | MACHINE_EXTERNAL_INTERRUPT = 11, 131 | } interrupt_t; 132 | 133 | void fatal(const char *msg); 134 | bool exception_is_fatal(const exception_t e); 135 | size_t read_file(FILE *f, uint8_t *r[]); 136 | struct cpu *cpu_new(uint8_t *code, const size_t code_size, FILE *disk); 137 | exception_t cpu_fetch(struct cpu *cpu, uint64_t *result); 138 | void cpu_take_trap(struct cpu *cpu, const exception_t e, const interrupt_t intr); 139 | exception_t cpu_execute(struct cpu *cpu, const uint64_t insn); 140 | interrupt_t cpu_check_pending_interrupt(struct cpu *cpu); 141 | --------------------------------------------------------------------------------