diff --git a/.github/workflows/deploy-wasm.yml b/.github/workflows/deploy-wasm.yml index 682a71b92..5ee81973a 100644 --- a/.github/workflows/deploy-wasm.yml +++ b/.github/workflows/deploy-wasm.yml @@ -10,13 +10,107 @@ on: branches: - master repository_dispatch: # listening to rv32emu-prebuilt events - types: [deploy_wasm] + types: [deploy_user_wasm, deploy_system_wasm] jobs: - wasm-deploy: + wasm-system-deploy: if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' + github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm' + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + - name: install-dependencies + run: | + sudo apt-get update -q=2 + sudo apt-get install -q=2 device-tree-compiler + - name: Verify if the JS or HTML files has been modified + id: changed-files + uses: tj-actions/changed-files@v46 + with: + files: | + assets/wasm/html/system.html + assets/wasm/js/system-pre.js + # Files below may have a potential performance impact (reference from benchmark.yml) + src/devices/*.c + src/system.c + src/riscv.c + src/decode.c + src/emulate.c + src/rv32_template.c + src/rv32_constopt.c + - name: install emcc + if: ${{ steps.changed-files.outputs.any_modified == 'true' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm') }} + run: | + git clone https://github.com/emscripten-core/emsdk -b 3.1.51 + cd emsdk + ./emsdk install latest + ./emsdk activate latest + source ./emsdk_env.sh + echo "$PATH" >> $GITHUB_PATH + shell: bash + - name: fetch artifact + run: | + make artifact + # get from rv32emu-prebuilt + wget -O build/shareware_doom_iwad.zip "https://raw.githubusercontent.com/sysprog21/rv32emu-prebuilt/doom-artifact/shareware_doom_iwad.zip" + unzip -d build/ build/shareware_doom_iwad.zip + - name: build with emcc and move application files to /tmp + if: ${{ steps.changed-files.outputs.any_modified == 'true' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm') }} + run: | + make CC=emcc ENABLE_SYSTEM=1 ENABLE_SDL=1 INITRD_SIZE=32 -j + mkdir /tmp/rv32emu-system-demo + mv assets/wasm/html/system.html /tmp/rv32emu-system-demo/index.html + mv assets/wasm/js/coi-serviceworker.min.js /tmp/rv32emu-system-demo + mv build/rv32emu.js /tmp/rv32emu-system-demo + mv build/rv32emu.wasm /tmp/rv32emu-system-demo + mv build/rv32emu.worker.js /tmp/rv32emu-system-demo + ls -al /tmp/rv32emu-system-demo + - name: Check out the rv32emu-system-demo repo + if: ${{ steps.changed-files.outputs.any_modified == 'true' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm') }} + uses: actions/checkout@v4 + with: + persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token. + repository: sysprog21/rv32emu-demo + - name: Create local changes + if: ${{ steps.changed-files.outputs.any_modified == 'true' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm') }} + run: | + mkdir -p system + mv /tmp/rv32emu-system-demo/index.html ./system + mv /tmp/rv32emu-system-demo/coi-serviceworker.min.js ./system + mv /tmp/rv32emu-system-demo/rv32emu.js ./system + mv /tmp/rv32emu-system-demo/rv32emu.wasm ./system + mv /tmp/rv32emu-system-demo/rv32emu.worker.js ./system + - name: Commit files + if: ${{ steps.changed-files.outputs.any_modified == 'true' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm') }} + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add system/ + git commit -m "Add changes to system emulation" + - name: Push changes + if: ${{ steps.changed-files.outputs.any_modified == 'true' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_system_wasm') }} + uses: ad-m/github-push-action@master + with: + repository: sysprog21/rv32emu-demo + github_token: ${{ secrets.RV32EMU_DEMO_TOKEN }} + branch: main + wasm-user-deploy: + needs: wasm-system-deploy # run jobs sequentially since two jobs operate on same reposity: rv32emu-demo + if: always() # ensures wasm-user-deploy runs regardless of the outcome or condition of wasm-system-deploy runs-on: ubuntu-latest steps: - name: Check out the repo @@ -26,8 +120,8 @@ jobs: uses: tj-actions/changed-files@v46 with: files: | - assets/wasm/html/index.html - assets/wasm/js/pre.js + assets/wasm/html/user.html + assets/wasm/js/user-pre.js build/*.elf tools/gen-elf-list-js.py # Files below may have a potential performance impact (reference from benchmark.yml) @@ -39,12 +133,10 @@ jobs: - name: install emcc if: ${{ steps.changed-files.outputs.any_modified == 'true' || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' }} + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_user_wasm') }} run: | - git clone https://github.com/emscripten-core/emsdk.git + git clone https://github.com/emscripten-core/emsdk -b 3.1.51 cd emsdk - git pull - git checkout 3.1.51 ./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh @@ -53,21 +145,17 @@ jobs: - name: fetch artifact run: | make artifact - # Hack Cloudflare 403 Forbidden on GitHub Runner for Doom artifact download - wget --header="User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0" \ - --header="Referer: https://www.doomworld.com/" \ - --header="Accept-Language: en-US,en;q=0.9" \ - -O build/shareware_doom_iwad.zip \ - "https://www.doomworld.com/3ddownloads/ports/shareware_doom_iwad.zip" + # get from rv32emu-prebuilt + wget -O build/shareware_doom_iwad.zip "https://raw.githubusercontent.com/sysprog21/rv32emu-prebuilt/doom-artifact/shareware_doom_iwad.zip" unzip -d build/ build/shareware_doom_iwad.zip - name: build with emcc and move application files to /tmp if: ${{ steps.changed-files.outputs.any_modified == 'true' || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' }} + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_user_wasm') }} run: | make CC=emcc ENABLE_SDL=1 mkdir /tmp/rv32emu-demo - mv assets/wasm/html/index.html /tmp/rv32emu-demo + mv assets/wasm/html/user.html /tmp/rv32emu-demo/index.html mv assets/wasm/js/coi-serviceworker.min.js /tmp/rv32emu-demo mv build/elf_list.js /tmp/rv32emu-demo mv build/rv32emu.js /tmp/rv32emu-demo @@ -77,7 +165,7 @@ jobs: - name: Check out the rv32emu-demo repo if: ${{ steps.changed-files.outputs.any_modified == 'true' || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' }} + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_user_wasm') }} uses: actions/checkout@v4 with: persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token. @@ -85,7 +173,7 @@ jobs: - name: Create local changes if: ${{ steps.changed-files.outputs.any_modified == 'true' || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' }} + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_user_wasm') }} run: | mv /tmp/rv32emu-demo/index.html . mv /tmp/rv32emu-demo/coi-serviceworker.min.js . @@ -96,16 +184,16 @@ jobs: - name: Commit files if: ${{ steps.changed-files.outputs.any_modified == 'true' || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' }} + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_user_wasm') }} run: | git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" git add --all - git commit -m "Add changes" + git commit -m "Add changes to user emulation" - name: Push changes if: ${{ steps.changed-files.outputs.any_modified == 'true' || github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' }} + (github.event_name == 'repository_dispatch' && github.event.action == 'deploy_user_wasm') }} uses: ad-m/github-push-action@master with: repository: sysprog21/rv32emu-demo diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 899fb4705..743c1447b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,12 +80,8 @@ jobs: make artifact make ENABLE_SYSTEM=1 artifact make ENABLE_ARCH_TEST=1 artifact - # Hack Cloudflare 403 Forbidden on GitHub Runner for Doom artifact download - wget --header="User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0" \ - --header="Referer: https://www.doomworld.com/" \ - --header="Accept-Language: en-US,en;q=0.9" \ - -O build/shareware_doom_iwad.zip \ - "https://www.doomworld.com/3ddownloads/ports/shareware_doom_iwad.zip" + # get from rv32emu-prebuilt + wget -O build/shareware_doom_iwad.zip "https://raw.githubusercontent.com/sysprog21/rv32emu-prebuilt/doom-artifact/shareware_doom_iwad.zip" unzip -d build/ build/shareware_doom_iwad.zip if: ${{ always() }} - name: default build using emcc @@ -394,12 +390,8 @@ jobs: | head -n 1 \ | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') make LATEST_RELEASE=$LATEST_RELEASE ENABLE_ARCH_TEST=1 artifact - # Hack Cloudflare 403 Forbidden on GitHub Runner for Doom artifact download - wget --header="User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15" \ - --header="Referer: https://www.doomworld.com/" \ - --header="Accept-Language: en-US,en;q=0.9" \ - -O build/shareware_doom_iwad.zip \ - "https://www.doomworld.com/3ddownloads/ports/shareware_doom_iwad.zip" + # get from rv32emu-prebuilt + wget -O build/shareware_doom_iwad.zip "https://raw.githubusercontent.com/sysprog21/rv32emu-prebuilt/doom-artifact/shareware_doom_iwad.zip" unzip -d build/ build/shareware_doom_iwad.zip if: ${{ always() }} - name: default build using emcc diff --git a/Makefile b/Makefile index 41c8324bb..36e113414 100644 --- a/Makefile +++ b/Makefile @@ -286,8 +286,8 @@ $(OUT)/emulate.o: CFLAGS += -foptimize-sibling-calls -fomit-frame-pointer -fno-s include mk/external.mk include mk/artifact.mk -include mk/wasm.mk include mk/system.mk +include mk/wasm.mk all: config $(BUILD_DTB) $(BUILD_DTB2C) $(BIN) diff --git a/README.md b/README.md index 43028be79..845f70503 100644 --- a/README.md +++ b/README.md @@ -378,16 +378,25 @@ $ source ~/emsdk/emsdk_env.sh Change the Emscripten SDK environment path if necessary. At this point, you can build and start a web server service to serve WebAssembly by running: +- user space emulation: ```shell -$ make CC=emcc start-web +$ make CC=emcc start-web -j8 +``` +- system emulation: +```shell +$ make CC=emcc start-web ENABLE_SYSTEM=1 INITRD_SIZE=32 -j8 ``` You would see the server's IP:PORT in your terminal. Copy and paste it to the browsers and you just access the index page of `rv32emu`. -You would see a dropdown menu which you can use to select the ELF executable. Select one and -click the Run button to run it. +You would see a dropdown menu which you can use to select the ELF executable for user space emulation, select one and +click the 'Run' button to run it. For system emulation, click the 'Run Linux' button to boot Linux. + +Alternatively, you may want to view a hosted `rv32emu` since building takes some time. +- [user space emulation demo page](https://sysprog21.github.io/rv32emu-demo/) +- [system emulation demo page](https://sysprog21.github.io/rv32emu-demo/system) -Alternatively, you may want to view a hosted `rv32emu` [demo page](https://sysprog21.github.io/rv32emu-demo/) since building takes some time. +Both pages can be easily switched using the navigation button. ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. diff --git a/assets/wasm/html/system.html b/assets/wasm/html/system.html new file mode 100644 index 000000000..3c9710a0d --- /dev/null +++ b/assets/wasm/html/system.html @@ -0,0 +1,315 @@ + + + + + + Emscripten-Generated Code + + + + + + + +
+
Downloading...
+ + + Resize canvas + Lock/hide mouse + pointer     + + + + + + + + +
+

Experience graphics and sounds(remember to turn on your speaker) by running doom-riscv, quake, smolnes.

+
+ +
+ +
+ +
+ +
+ +
+ + + + + + diff --git a/assets/wasm/html/index.html b/assets/wasm/html/user.html similarity index 98% rename from assets/wasm/html/index.html rename to assets/wasm/html/user.html index 5beae7e63..e2fdf6951 100644 --- a/assets/wasm/html/index.html +++ b/assets/wasm/html/user.html @@ -121,6 +121,10 @@ + +
@@ -137,6 +141,13 @@ var spinnerElement = document.getElementById('spinner'); var runButton = document.getElementById("runButton"); runButton.addEventListener("click", runButtonClickHandler); + var toSysEmuButton = document.getElementById("toSysEmuButton"); + toSysEmuButton.addEventListener("click", toSysEmuButtonClickHandler); + + function toSysEmuButtonClickHandler() { + console.log("Navigate to system emulation"); + window.location.href = "./system" + } var elfDropdown = document.getElementById("elfDropdown"); for (var i = 0; i < elfFiles.length; i++) { @@ -163,11 +174,7 @@ element.scrollTop = element.scrollHeight; } Module._indirect_rv_halt(); - /* important to add some delay for waiting cancellation of main loop before next run */ - /* Otherwise, get error: only one main loop can be existed */ - setTimeout(() => { - Module['onRuntimeInitialized'](target_elf); - }, 1000); + Module['run_user'](target_elf); } var Module = { diff --git a/assets/wasm/js/pre.js b/assets/wasm/js/pre.js deleted file mode 100644 index c541967ea..000000000 --- a/assets/wasm/js/pre.js +++ /dev/null @@ -1,9 +0,0 @@ -Module['noInitialRun'] = true; -Module['onRuntimeInitialized'] = function(target_elf) { - if(target_elf === undefined){ - console.warn("target elf executable is undefined"); - return; - } - - callMain([target_elf]); -}; diff --git a/assets/wasm/js/system-pre.js b/assets/wasm/js/system-pre.js new file mode 100644 index 000000000..5fca6f083 --- /dev/null +++ b/assets/wasm/js/system-pre.js @@ -0,0 +1,40 @@ +Module["noInitialRun"] = true; + +Module["run_system"] = function (cli_param) { + callMain(cli_param.split(" ")); +}; + +// index.html's preRun needs to access this, thus declaring as global +let term; + +Module["onRuntimeInitialized"] = function () { + const input_buf_ptr = Module._get_input_buf(); + const input_buf_cap = Module._get_input_buf_cap(); + + term = new Terminal({ + cols: 120, + rows: 11, + }); + term.open(document.getElementById("terminal")); + + term.onKey(({ key, domEvent }) => { + + let heap = new Uint8Array( + Module.HEAPU8.buffer, + input_buf_ptr, + key.length, + ); + + for (let i = 0; i < key.length && i < input_buf_cap; i++) { + heap[i] = key.charCodeAt(i); + } + // Fill zero + for (let i = key.length; i < input_buf_cap; i++) { + heap[i] = 0; + } + + Module._set_input_buf_size(key.length); + + term.scrollToBottom(); + }); +}; diff --git a/assets/wasm/js/user-pre.js b/assets/wasm/js/user-pre.js new file mode 100644 index 000000000..f0925222f --- /dev/null +++ b/assets/wasm/js/user-pre.js @@ -0,0 +1,10 @@ +Module["noInitialRun"] = true; + +Module["run_user"] = function (target_elf) { + if (target_elf === undefined) { + console.warn("target elf executable is undefined"); + return; + } + + callMain([target_elf]); +}; diff --git a/mk/wasm.mk b/mk/wasm.mk index cf4d2c9e9..21c0d2d60 100644 --- a/mk/wasm.mk +++ b/mk/wasm.mk @@ -3,7 +3,7 @@ deps_emcc := ASSETS := assets/wasm WEB_HTML_RESOURCES := $(ASSETS)/html WEB_JS_RESOURCES := $(ASSETS)/js -EXPORTED_FUNCS := _main,_indirect_rv_halt +EXPORTED_FUNCS := _main,_indirect_rv_halt,_get_input_buf,_get_input_buf_cap,_set_input_buf_size DEMO_DIR := demo WEB_FILES := $(BIN).js \ $(BIN).wasm \ @@ -29,7 +29,19 @@ CFLAGS_emcc += -sINITIAL_MEMORY=2GB \ -s"EXPORTED_FUNCTIONS=$(EXPORTED_FUNCS)" \ -sSTACK_SIZE=4MB \ -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency \ - --embed-file build/jit-bf.elf@/jit-bf.elf \ + --embed-file build/timidity@/etc/timidity \ + -DMEM_SIZE=0x20000000 \ + -DCYCLE_PER_STEP=2000000 \ + -O3 \ + -w + +ifeq ($(call has, SYSTEM), 1) +CFLAGS_emcc += --embed-file build/linux-image/Image@Image \ + --embed-file build/linux-image/rootfs.cpio@rootfs.cpio \ + --embed-file build/minimal.dtb@/minimal.dtb \ + --pre-js $(WEB_JS_RESOURCES)/system-pre.js +else +CFLAGS_emcc += --embed-file build/jit-bf.elf@/jit-bf.elf \ --embed-file build/coro.elf@/coro.elf \ --embed-file build/fibonacci.elf@/fibonacci.elf \ --embed-file build/hello.elf@/hello.elf \ @@ -40,14 +52,11 @@ CFLAGS_emcc += -sINITIAL_MEMORY=2GB \ --embed-file build/riscv32@/riscv32 \ --embed-file build/DOOM1.WAD@/DOOM1.WAD \ --embed-file build/id1/pak0.pak@/id1/pak0.pak \ - --embed-file build/timidity@/etc/timidity \ - -DMEM_SIZE=0x60000000 \ - -DCYCLE_PER_STEP=2000000 \ - --pre-js $(WEB_JS_RESOURCES)/pre.js \ - -O3 \ - -w + --pre-js $(WEB_JS_RESOURCES)/user-pre.js +endif + -$(OUT)/elf_list.js: tools/gen-elf-list-js.py +$(OUT)/elf_list.js: artifact tools/gen-elf-list-js.py $(Q)tools/gen-elf-list-js.py > $@ # used to download all dependencies of elf executable and bundle into single wasm @@ -132,11 +141,22 @@ define cp-web-file endef # WEB_FILES could be cleaned and recompiled, thus do not mix these two files into WEB_FILES -STATIC_WEB_FILES := $(WEB_HTML_RESOURCES)/index.html \ - $(WEB_JS_RESOURCES)/coi-serviceworker.min.js +STATIC_WEB_FILES := $(WEB_JS_RESOURCES)/coi-serviceworker.min.js +ifeq ($(call has, SYSTEM), 1) +STATIC_WEB_FILES += $(WEB_HTML_RESOURCES)/system.html +else +STATIC_WEB_FILES += $(WEB_HTML_RESOURCES)/user.html +endif + +start_web_deps := check-demo-dir-exist $(BIN) +ifeq ($(call has, SYSTEM), 1) +start_web_deps += $(BUILD_DTB) $(BUILD_DTB2C) +endif -start-web: check-demo-dir-exist $(BIN) +start-web: $(start_web_deps) + $(Q)rm -f $(DEMO_DIR)/*.html $(foreach T, $(WEB_FILES), $(call cp-web-file, $(T))) $(foreach T, $(STATIC_WEB_FILES), $(call cp-web-file, $(T))) + $(Q)mv $(DEMO_DIR)/*.html $(DEMO_DIR)/index.html $(Q)python3 -m http.server --bind $(DEMO_IP) $(DEMO_PORT) --directory $(DEMO_DIR) endif diff --git a/src/devices/uart.c b/src/devices/uart.c index f554165f9..407c178e1 100644 --- a/src/devices/uart.c +++ b/src/devices/uart.c @@ -5,12 +5,17 @@ #include #include +#include #include #include #include #include #include +#if defined(__EMSCRIPTEN__) +#include "emsc.h" +#endif + #include "uart.h" /* Emulate 8250 (plain, without loopback mode support) */ @@ -33,15 +38,42 @@ void u8250_update_interrupts(u8250_state_t *uart) uart->current_intr = ilog2(uart->pending_intrs); } +#if defined(__EMSCRIPTEN__) +#define INPUT_BUF_MAX_CAP 16 +static char input_buf[INPUT_BUF_MAX_CAP]; +static uint8_t input_buf_start = 0; +uint8_t input_buf_size = 0; + +char *get_input_buf() +{ + return input_buf; +} + +uint8_t get_input_buf_cap() +{ + return INPUT_BUF_MAX_CAP; +} + +void set_input_buf_size(uint8_t size) +{ + input_buf_size = size; +} +#endif + void u8250_check_ready(u8250_state_t *uart) { if (uart->in_ready) return; +#if defined(__EMSCRIPTEN__) + if (input_buf_size) + uart->in_ready = true; +#else struct pollfd pfd = {uart->in_fd, POLLIN, 0}; poll(&pfd, 1, 0); if (pfd.revents & POLLIN) uart->in_ready = true; +#endif } static void u8250_handle_out(u8250_state_t *uart, uint8_t value) @@ -57,12 +89,19 @@ static uint8_t u8250_handle_in(u8250_state_t *uart) if (!uart->in_ready) return value; +#if defined(__EMSCRIPTEN__) + value = (uint8_t) input_buf[input_buf_start]; + input_buf_start++; + if (--input_buf_size == 0) + input_buf_start = 0; +#else if (read(uart->in_fd, &value, 1) < 0) rv_log_error("Failed to read UART input: %s", strerror(errno)); +#endif uart->in_ready = false; - u8250_check_ready(uart); - if (value == 1) { /* start of heading (Ctrl-a) */ + if (value == 1) { /* start of heading (Ctrl-a) */ + u8250_check_ready(uart); if (getchar() == 120) { /* keyboard x */ rv_log_info("RISC-V emulator is destroyed"); exit(EXIT_SUCCESS); diff --git a/src/emsc.h b/src/emsc.h new file mode 100644 index 000000000..2ea925ff0 --- /dev/null +++ b/src/emsc.h @@ -0,0 +1,18 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#pragma once + +#include + +void indirect_rv_halt(); + +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) +extern uint8_t input_buf_size; + +char *get_input_buf(); +uint8_t get_input_buf_cap(); +void set_input_buf_size(uint8_t size); +#endif diff --git a/src/emulate.c b/src/emulate.c index ae8835e9e..9bc4bb373 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -10,10 +10,6 @@ #include #include -#ifdef __EMSCRIPTEN__ -#include -#endif - #if RV32_HAS(EXT_F) #include #include "softfp.h" @@ -23,6 +19,19 @@ extern struct target_ops gdbstub_ops; #endif +#if defined(__EMSCRIPTEN__) +#include "emsc.h" +#if RV32_HAS(SYSTEM) +EM_JS(void, enable_run_button, (), { + document.getElementById('runSysButton').disabled = false; +}); +#else +EM_JS(void, enable_run_button, (), { + document.getElementById('runButton').disabled = false; +}); +#endif +#endif + #include "decode.h" #include "mpool.h" #include "riscv.h" @@ -1009,6 +1018,9 @@ static void rv_check_interrupt(riscv_t *rv) if (peripheral_update_ctr-- == 0) { peripheral_update_ctr = 64; +#if defined(__EMSCRIPTEN__) + escape_seq: +#endif u8250_check_ready(PRIV(rv)->uart); if (PRIV(rv)->uart->in_ready) emu_update_uart_interrupts(rv); @@ -1031,6 +1043,11 @@ static void rv_check_interrupt(riscv_t *rv) break; case (SUPERVISOR_EXTERNAL_INTR & 0xf): SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_EXTERNAL_INTR, 0); +#if defined(__EMSCRIPTEN__) + /* escape sequence has more than 1 byte */ + if (input_buf_size) + goto escape_seq; +#endif break; default: break; @@ -1174,6 +1191,7 @@ void rv_step(void *arg) emscripten_cancel_main_loop(); rv_delete(rv); /* clean up and reuse memory */ rv_log_info("RISC-V emulator is destroyed"); + enable_run_button(); } #endif } diff --git a/src/log.c b/src/log.c index ad2746c6e..f534245e1 100644 --- a/src/log.c +++ b/src/log.c @@ -52,7 +52,11 @@ static void stdout_callback(log_event_t *ev) ev->file, ev->line); #endif /* RV32_HAS(LOG_COLOR) */ vfprintf(ev->udata, ev->fmt, ev->ap); +#if defined(__EMSCRIPTEN__) + fprintf(ev->udata, "\r\n"); +#else fprintf(ev->udata, "\n"); +#endif fflush(ev->udata); } diff --git a/src/main.c b/src/main.c index 8e366519c..0a3ca86cf 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,19 @@ #include #include +#if defined(__EMSCRIPTEN__) +#include "emsc.h" +#if RV32_HAS(SYSTEM) +EM_JS(void, disable_run_button, (), { + document.getElementById('runSysButton').disabled = true; +}); +#else +EM_JS(void, disable_run_button, (), { + document.getElementById('runButton').disabled = true; +}); +#endif +#endif + #include "elf.h" #include "riscv.h" #include "utils.h" @@ -286,6 +299,10 @@ int main(int argc, char **args) } rv_log_info("RISC-V emulator is created and ready to run"); +#if defined(__EMSCRIPTEN__) + disable_run_button(); +#endif + rv_run(rv); /* dump registers as JSON */ diff --git a/src/riscv.c b/src/riscv.c index 2a1e59f72..66c302835 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -27,8 +27,8 @@ #define STDERR_FILENO FILENO(stderr) #endif -#ifdef __EMSCRIPTEN__ -#include +#if defined(__EMSCRIPTEN__) +#include "emsc.h" #endif #include "elf.h" @@ -234,7 +234,11 @@ static void map_file(char **ram_loc, const char *name) struct stat st; fstat(fd, &st); -#if HAVE_MMAP +/* EMSCRIPTEN: We don't currently support location hints for the address of the + * mapping */ +/* https://github.com/emscripten-core/emscripten/blob/52bc455316b2f44d3a94104776a335a5861ad73b/system/lib/libc/emscripten_mmap.c#L105 + */ +#if HAVE_MMAP && !defined(__EMSCRIPTEN__) /* remap to a memory region */ *ram_loc = mmap(*ram_loc, st.st_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, fd, 0);