├── .github └── workflows │ ├── react.yml │ └── vscode.yml ├── .gitignore ├── .gitmodules ├── README.md ├── masm-tasm ├── .eslintrc.json ├── .gitignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh.md ├── dev │ └── downloadBundle.js ├── doc │ ├── Notes.md │ ├── Thanks.md │ └── develop.md ├── i18n │ └── i18n.zh-cn.json ├── language-configuration.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── pics │ ├── demo_PLFeature.gif │ ├── demo_diagnose_tasm.gif │ ├── demo_diagnose_tasm_zh.gif │ ├── demo_dosbox_tasm.gif │ ├── demo_dosbox_tasm_zh.gif │ ├── demo_jsdos.gif │ ├── demo_msdos_masm.gif │ ├── demo_msdos_masm_zh.gif │ ├── demo_web-russiacube.gif │ ├── opendosbox.gif │ └── opendosbox_zh.gif ├── pnpm-lock.yaml ├── resources │ ├── dosboxasm.png │ ├── hoverinfo.md │ └── instructions-reference.json ├── samples │ ├── .gitignore │ ├── 1.asm │ ├── 3中文路径hasError.asm │ ├── multi │ │ ├── 2.asm │ │ └── mac.inc │ ├── readme.md │ └── test.asm ├── snippets.json ├── src │ ├── ASM │ │ ├── main.ts │ │ ├── manager.ts │ │ ├── statusBar.ts │ │ └── vscode-dosbox.ts │ ├── diagnose │ │ ├── codeAction.ts │ │ ├── diagnoseMASM.ts │ │ ├── diagnoseMasm-error-list.ts │ │ ├── diagnoseTASM.ts │ │ ├── main.ts │ │ └── messageCollector.ts │ ├── emulators │ │ ├── dosbox.ts │ │ ├── jsdosHost.ts │ │ ├── jsdosWeb.ts │ │ ├── main-nodejs.ts │ │ ├── main.ts │ │ └── msdos-player.ts │ ├── extension.ts │ ├── language │ │ ├── AsmDocumentFormattingEdit.ts │ │ ├── AsmReference.ts │ │ ├── Hover.ts │ │ ├── hoverFelix.ts │ │ ├── hoverFromCppdoc.ts │ │ ├── hoverFromMarkdown.ts │ │ ├── main.ts │ │ ├── scanDoc.ts │ │ └── wordinfo.ts │ ├── readme.md │ ├── test │ │ ├── runTest.ts │ │ └── suite │ │ │ ├── ASM.test.ts │ │ │ ├── diagnose.test.ts │ │ │ ├── extension.test.ts │ │ │ └── index.ts │ ├── utils │ │ ├── configuration.ts │ │ ├── downloadFile.ts │ │ ├── eol.ts │ │ ├── i18n-default.json │ │ ├── i18n.ts │ │ ├── logger.ts │ │ └── util.ts │ └── web │ │ ├── ASM.ts │ │ ├── extension.ts │ │ └── test │ │ └── suite │ │ ├── ASM.test.ts │ │ ├── extsniosn.test.ts │ │ ├── index.ts │ │ └── workspace.test.ts ├── syntaxes │ ├── README.md │ └── assembly.tmLanguage.json ├── tsconfig.json └── webpack.config.js ├── react-app ├── .gitignore ├── .vscode │ ├── settings.json │ └── tasks.json ├── README.md ├── develop.js ├── gitee.package.js ├── package.json ├── pnpm-lock.yaml ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.test.tsx │ ├── App.tsx │ ├── actionButtons.tsx │ ├── bundle.config.json │ ├── bundle.ts │ ├── dos-player.tsx │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ └── setupTests.ts └── tsconfig.json └── vscode-dosbox ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .vsce ├── .vscodeignore ├── darwin.vscodeignore ├── linux.vscodeignore ├── web.vscodeignore └── win32.vscodeignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh.md ├── dev ├── package.js ├── postinstall.js └── update-emulators.sh ├── package.json ├── pnpm-lock.yaml ├── src ├── README.md ├── api.ts ├── dosbox │ ├── conf.ts │ ├── dosbox.ts │ ├── fromBundle.ts │ └── main.ts ├── emulators │ ├── README.md │ ├── build.ts │ ├── cache.ts │ ├── dos │ │ ├── bundle │ │ │ ├── dos-bundle.ts │ │ │ └── dos-conf.ts │ │ └── dosbox │ │ │ └── ts │ │ │ ├── create-worker.ts │ │ │ ├── direct.ts │ │ │ ├── worker-server.js │ │ │ ├── worker.origin.ts │ │ │ └── worker.ts │ ├── emulators.ts │ ├── http.origin.ts │ ├── http.ts │ ├── impl │ │ ├── ci-impl.ts │ │ ├── emulators-impl.ts │ │ ├── modules.origin.ts │ │ └── modules.ts │ ├── janus │ │ ├── janus-impl.ts │ │ └── janus.d.ts │ ├── keys.ts │ ├── libzip │ │ └── libzip.ts │ ├── main.ts │ └── protocol │ │ ├── messages-queue.ts │ │ ├── protocol.h │ │ └── protocol.ts ├── extension.ts ├── jsdos-bundle │ └── bundle.ts ├── jsdos-ci-shell │ ├── jsdosKey.ts │ ├── jsdosKeyCode.ts │ └── main.ts ├── jsdos │ ├── Jsdos.ts │ ├── hooks.ts │ ├── main.ts │ ├── runInHost.ts │ └── runInWebview.ts ├── msdos-player │ └── main.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── conf.test.ts │ │ ├── dosbox.test.ts │ │ ├── extension.test.ts │ │ ├── index.ts │ │ ├── jsdos.test.ts │ │ ├── jsdosWeb.test.ts │ │ └── util.ts ├── util │ ├── checkInstallation.ts │ └── logger.ts ├── web │ ├── extension.ts │ └── test │ │ └── suite │ │ ├── extension.test.ts │ │ ├── index.ts │ │ └── jsdos.test.ts └── webview │ ├── connection.ts │ ├── index.ts │ ├── loadJsdos.ts │ ├── onGetCi.ts │ └── util.ts ├── tsconfig.json ├── web ├── .gitignore ├── README.md ├── index.html └── res │ └── empty.jsdos └── webpack.config.js /.github/workflows/react.yml: -------------------------------------------------------------------------------- 1 | # 📖 https://gohugo.io/hosting-and-deployment/hosting-on-github/ 2 | name: github pages 3 | 4 | on: 5 | push: 6 | tags: 7 | - "**" # Set a branch to deploy 8 | pull_request: 9 | 10 | jobs: 11 | deploy: 12 | runs-on: windows-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Use Node.js 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: '16.x' 20 | - uses: pnpm/action-setup@v2.2.2 21 | with: 22 | version: "*" 23 | 24 | - name: build react app 25 | working-directory: ./react-app 26 | run: | 27 | pnpm i 28 | pnpm build 29 | 30 | - name: Deploy 31 | uses: peaceiris/actions-gh-pages@v3 32 | if: github.ref == 'refs/heads/main' 33 | with: 34 | github_token: ${{ secrets.GITHUB_TOKEN }} 35 | publish_dir: ./react-app/build 36 | -------------------------------------------------------------------------------- /.github/workflows/vscode.yml: -------------------------------------------------------------------------------- 1 | name: vscode extension 2 | on: 3 | push: 4 | branches: 5 | - "**" 6 | tags: 7 | - "v*" 8 | 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | os: [macos-latest, ubuntu-latest, windows-latest] 14 | # fail-fast: false 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/setup-node@v2 18 | with: 19 | node-version: '16.x' 20 | - uses: pnpm/action-setup@v2.2.2 21 | with: 22 | version: "*" 23 | 24 | - name: linux Install dosbox and dosbox-x tool 25 | run: | 26 | sudo apt-get update 27 | sudo apt-get install dosbox 28 | sudo apt install flatpak 29 | sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 30 | sudo flatpak install flathub com.dosbox_x.DOSBox-X -y 31 | if: runner.os == 'Linux' 32 | - name: macOS Install dosbox and dosbox-x tool 33 | run: | 34 | brew update 35 | brew install dosbox 36 | brew install dosbox-x 37 | if: runner.os == 'macOS' 38 | 39 | - name: start xvfb for linux 40 | run: | 41 | /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 42 | echo ">>> Started xvfb" 43 | if: runner.os == 'Linux' 44 | 45 | - uses: actions/checkout@v2 46 | with: 47 | submodules: true 48 | 49 | - name: install and test vscode-dosbox 50 | run: | 51 | pnpm install 52 | pnpm compile 53 | pnpm test-web 54 | pnpm test 55 | working-directory: ./vscode-dosbox 56 | env: 57 | DISPLAY: ":99.0" 58 | 59 | - name: package 60 | run: node dev/package.js 61 | working-directory: ./vscode-dosbox 62 | 63 | - name: install and test ./masm-tasm 64 | run: | 65 | pnpm install 66 | pnpm compile-dev 67 | pnpm test 68 | pnpm test-web 69 | working-directory: ./masm-tasm 70 | env: 71 | DISPLAY: ":99.0" 72 | 73 | - run: pnpm lint 74 | if: runner.os == 'Linux' 75 | 76 | build: 77 | needs: test 78 | if: success() 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v2 82 | with: 83 | submodules: true 84 | 85 | - uses: actions/setup-node@v2 86 | with: 87 | node-version: "14" 88 | - uses: pnpm/action-setup@v2.2.2 89 | with: 90 | version: 6.0.2 91 | 92 | - name: install and package vscode-dosbox 93 | working-directory: ./vscode-dosbox 94 | run: | 95 | pnpm 96 | node dev/package.js --all 97 | - uses: actions/upload-artifact@v2 98 | with: 99 | name: "vscepackages" 100 | path: "./vscode-dosbox/*.vsix" 101 | 102 | - run: npx vsce publish --packagePath $(find . -iname "*.vsix") 103 | working-directory: ./vscode-dosbox 104 | env: 105 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 106 | if: success() && startsWith( github.ref, 'refs/tags/') 107 | 108 | - name: Publish 109 | if: success() && startsWith( github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest' 110 | run: npx vsce publish 111 | working-directory: ./masm-tasm 112 | env: 113 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 114 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_store 2 | node_modules 3 | dist 4 | out 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bundles"] 2 | path = bundles 3 | url = git@github.com:dosasm/assembly-tool.git 4 | branch = gh-pages 5 | [submodule "vscode-dosbox/emu"] 6 | path = vscode-dosbox/emu 7 | url = git@github.com:dosasm/dosemu.git 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DOSRUN 2 | 3 | This repository contains sources files of some projects for the study of assembly language. 4 | These projects are not actively maintained. 5 | Bugs will be fixed to my best but new features is difficult for me to add. 6 | 7 | Please contact me if you are interested in maintaining this project. 8 | 9 | | | masm-tasm|vscode-dosbox| 10 | |---|----------|-------------| 11 | |English|[README](./masm-tasm/README.md)|[README](./vscode-dosbox/README.md)| 12 | |中文|[README.zh](./masm-tasm/README.zh.md)|[README.zh](./vscode-dosbox/README.zh.md)| 13 | -------------------------------------------------------------------------------- /masm-tasm/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "extends": [ 12 | "plugin:@typescript-eslint/recommended" 13 | ], 14 | "rules": { 15 | "@typescript-eslint/semi": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "warn", 18 | "@typescript-eslint/no-use-before-define": "off", 19 | "@typescript-eslint/explicit-module-boundary-types": "off" 20 | }, 21 | "ignorePatterns": [ 22 | "test/**/index.ts", 23 | "test/**/runTest.ts", 24 | "node_modules", 25 | "**/*.js", 26 | "src/test/suite/diagnose.test.ts" 27 | ] 28 | } -------------------------------------------------------------------------------- /masm-tasm/.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | /dist 3 | node_modules 4 | .vscode-test/ 5 | .vscode-test-web/ 6 | *.vsix 7 | 8 | **/.DS_store 9 | 10 | **.jsdos 11 | -------------------------------------------------------------------------------- /masm-tasm/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /masm-tasm/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/dist/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/dist/test/suite/index", 29 | "${workspaceFolder}/samples" 30 | ], 31 | "outFiles": [ 32 | "${workspaceFolder}/dist/**/*.js" 33 | ], 34 | "preLaunchTask": "npm: watch-tsc", 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /masm-tasm/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off", 11 | // "editor.formatOnSave": true, 12 | "markdownlint.config": { 13 | "MD033": false 14 | } 15 | } -------------------------------------------------------------------------------- /masm-tasm/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$ts-webpack-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "label": "npm: watch" 15 | }, 16 | { 17 | "type": "npm", 18 | "script": "compile", 19 | "label": "npm: compile" 20 | }, 21 | { 22 | "type": "npm", 23 | "script": "watch-tsc", 24 | "problemMatcher": "$tsc-watch", 25 | "isBackground": true, 26 | "presentation": { 27 | "reveal": "never" 28 | }, 29 | "label": "npm: watch-tsc" 30 | }, 31 | { 32 | "type": "npm", 33 | "script": "lint", 34 | "problemMatcher": [ 35 | "$eslint-stylish" 36 | ], 37 | "label": "npm: lint", 38 | "detail": "eslint src --ext ts" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /masm-tasm/.vscodeignore: -------------------------------------------------------------------------------- 1 | .github/ 2 | .vscode/** 3 | .vscode-test/** 4 | .vscode-test-web/** 5 | src/ 6 | doc/ 7 | pics/ 8 | dev/ 9 | samples/ 10 | 11 | **/test/** 12 | 13 | **/*.map 14 | **/*.ts 15 | **/*.md 16 | !README.md 17 | !CHANGELOG.md 18 | !resources/hoverinfo.md 19 | 20 | **/.gitignore 21 | **/azure-pipelines.yml 22 | **/tsconfig.json 23 | **/.eslintrc.json 24 | **/gulpfile.js 25 | **/webpack.config.js 26 | 27 | .vscodeignore 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /masm-tasm/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 xsrolau-liu 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 | -------------------------------------------------------------------------------- /masm-tasm/README.zh.md: -------------------------------------------------------------------------------- 1 | # 16 位/32 位 DOS 汇编语言支持 2 | 3 | [中文](README.zh.md)|[English](README.md) 4 | 5 | :raising_hand:实现对 DOSBox 等汇编工具的快速调用。主要针对 DOS 下的单文件汇编语言学习,可能适合学习《汇编语言》、《微机原理》等课程,主要功能特性如下: 6 | 7 | - :bookmark_tabs:(**语法支持**)代码高亮,大纲信息,悬浮提示,代码格式化,错误信息标注功能 8 | - :electric_plug:(**运行调试**)提供编辑器右键菜单选项:在汇编语言的编辑器添加了“打开 dosbox,运行,调试”的三个选项 9 | - :bar_chart: 提供 diagnose**错误信息标注**功能:假如汇编未通过,会根据汇编器输出来标明错误信息与位置,可以在命令面板输入`清除MASM/TASM的所有问题信息`清除本插件输出的 diagnose 问题信息 10 | - :computer: 支持包括 Web 在内的所有 VSCode 版本和平台,参见[平台支持](#平台支持) 11 | - 注:该插件为学习 DOS 下的汇编语言开发,可能并不适合复杂的多文件汇编 12 | 13 | ## :rocket:DEMO 示例 14 | 15 | ![jsdos demo](pics/demo_jsdos.gif) 16 | 17 | ### Demo 1 :flashlight: 代码格式化与错误输出 18 | 19 | | 格式化代码 | 错误信息输出 | 20 | | ---------------------------- | ----------------------------------- | 21 | | ![](pics/demo_PLFeature.gif) | ![](pics/demo_diagnose_tasm_zh.gif) | 22 | 23 | 提供一些“编程语言特性”(悬浮提示,代码格式化,跳到定义,查看引用)来方便代码编写与阅读,如果不喜欢可以在设置`masmtasm.language.Hover`,`masmtasm.language.programmaticFeatures`中关闭,重启之后会生效。同时也可以使用其他插件提供的语言功能如[ASM Code Lens](https://marketplace.visualstudio.com/items?itemName=maziac.asm-code-lens) 提供的 language ID `asm-collection` 24 | 25 | ### Demo 2 :running:: 运行调试代码 26 | 27 | | 调用 DOSBox 运行 TASM | 调用 msdos-player 运行 MASM | 28 | | --------------------------------- | -------------------------------- | 29 | | ![](pics/demo_dosbox_tasm_zh.gif) | ![](pics/demo_msdos_masm_zh.gif) | 30 | 31 | 当打开一个`ASM`后缀的汇编文件时,可以在编辑器右击,会出现以下三个选项: 32 | 33 | 1. 打开 DOS 环境:打开 DOSBox,然后就可以手动在打开的 DOSBox 窗口输入指令进行操作 34 | 2. 运行当前程序(汇编+链接+运行):生成 exe 程序并运行 35 | 3. 调试当前程序(汇编+链接+调试):生成 exe 程序并调试,使用 MASM 则会调用 debug 调试,使用 TASM 会调用 td 调试 36 | 37 | #### 运行调试说明 38 | 39 | - 如果所有汇编代码都存放在一个文件中,建议使用单文件模式将`masmtasm.ASM.mode`设置为`single file`,插件将会首先复制文件到插件的独立地址中,再进行操作。 40 | - 如果代码非常复杂,建议设置`masmtasm.ASM.mode`为`workspace`,插件会直接挂载当前工作文件夹,可能会污染当天工作目录(对于 jsdos 会直接复制当前文件夹中的所有文件)。 41 | 42 | ## 平台支持 43 | 44 | 插件依赖[vscode-dosbox](https://marketplace.visualstudio.com/items?itemName=xsro.vscode-dosbox) 来与 DOS 模拟器交互。 45 | `vscode-dosbox`打包了 win 平台上的二进制文件,参考[它的文档](https://github.com/dosasm/vscode-dosbox/blob/main/README.zh.md#安装依赖) 在其他平台安装相关 DOS 模拟器。 46 | 47 | ## 自定义 Actions 48 | 49 | 可以通过设置`masmtasm.ASM.actions`来修改运行和调试的 DOS 命令。比如,需要编译成 COM 文件,可以添加如下设置。同时将`masmtasm.ASM.assembler`设置为 Action 的键`TASM-com` 50 | 51 | ```json 52 | "masmtasm.ASM.actions": { 53 | "TASM-com": { 54 | "baseBundle": "/TASM.jsdos", 55 | "before": [ 56 | "set PATH=C:\\TASM" 57 | ], 58 | "run": [ 59 | "TASM ${file}", 60 | "TLINK /t ${filename}", 61 | "${filename}" 62 | ], 63 | "debug": [ 64 | "TASM /zi ${file}", 65 | "TLINK /t/v/3 ${filename}.obj", 66 | "TD ${filename}.exe" 67 | ] 68 | } 69 | } 70 | "masmtasm.ASM.assembler":"TASM-com" 71 | ``` 72 | 73 | ## :cd:插件调用 dosbox 时会挂载哪些目录 74 | 75 | | DOSBox | 电脑中的真实目录 | 76 | | ------ | ---------------- | 77 | | C: | 汇编工具目录 | 78 | | D: | 插件汇编工作目录 | 79 | 80 | ### 编译成 COM 文件 81 | 82 | ### 插件安装路径一般在哪里 83 | 84 | VSCode 文档中关于插件安装路径的说明[VSCode-doc](https://code.visualstudio.com/docs/editor/extension-gallery#_where-are-extensions-installed),摘录如下 85 | 86 | - Windows `%USERPROFILE%\.vscode\extensions` 87 | - macOS `~/.vscode/extensions` 88 | - Linux `~/.vscode/extensions` 89 | 90 | ### :clap:文档 & 感谢 & 许可 91 | 92 | - 感谢南邮的《微型计算机原理与接口技术》课程 93 | - 该插件受[Woodykaixa](https://github.com/Woodykaixa)的 [masm-code](https://github.com/Woodykaixa/masm-code)启发 94 | - 插件通过[DOSBox](https://www.dosbox.com)、[caiiiycuk](https://github.com/caiiiycuk)的[JS DOS](https://js-dos.com/) 和[MSDOS player](http://takeda-toshiya.my.coocan.jp/msdos)模拟 DOS 环境 95 | - 插件使用了[Roncho](https://marketplace.visualstudio.com/publishers/Roncho)的[Assembly (TASM)](https://marketplace.visualstudio.com/items?itemName=Roncho.assembly-8086)中的汇编语法信息 96 | - 一些[相关信息](doc/license_and_info.md)和[鸣谢](doc/Thanks.md) 97 | - gitee 上的一些笔记和代码: [笔记](https://dosasm.gitee.io/),[代码](https://gitee.com/dosasm/asmcodes) 98 | - 一些相关资料:[wiki](https://github.com/dosasm/masm-tasm/wiki) 99 | - 插件难免会有一些 bug,欢迎到 github 发[issue](https://github.com/dosasm/masm-tasm/issues)或者邮件`xsro@foxmail.com`,一起交流和完善。 100 | 101 | Enjoy!:smile: 102 | -------------------------------------------------------------------------------- /masm-tasm/dev/downloadBundle.js: -------------------------------------------------------------------------------- 1 | const { existsSync,copyFileSync } = require('fs'); 2 | const path = require('path'); 3 | const pkg = require("../package.json"); 4 | 5 | const actions = pkg.contributes.configuration.properties['masmtasm.ASM.actions'].default 6 | const assemblers = Object.keys(actions).map(key => actions[key].baseBundle.replace('/', "")); 7 | 8 | const srcFolder = path.resolve(__dirname,"..","..","bundles") 9 | const dstFolder = path.resolve(__dirname, "..", "resources"); 10 | 11 | async function main() { 12 | for (const asm of assemblers) { 13 | const dst = path.resolve(dstFolder, asm); 14 | const src = path.resolve(srcFolder, asm); 15 | if(!existsSync(src)){ 16 | console.warn("can't find file "+src+"[skip]"); 17 | continue 18 | } 19 | if (existsSync(dst)) { 20 | console.log('already added', asm) 21 | } else { 22 | console.log(src,dstFolder) 23 | copyFileSync(src,dst) 24 | } 25 | } 26 | } 27 | 28 | main() -------------------------------------------------------------------------------- /masm-tasm/doc/Notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## 1 4 | 5 | VSCode 类型与汇编类型的对应 6 | 7 | | assembly symbol | vscode symbol | 汇编关键字 | vscode 关键字 | 8 | | --------------- | ------------- | ---------- | ------------- | 9 | | macro | Module | 宏 | 模块 | 10 | | segment | Class | 段 | 类 | 11 | | procedure | Function | 子程序 | 函数 | 12 | | struct | Struct | 结构体 | 结构体 | 13 | | label | Key | 标号 | 键 | 14 | | variable | Variable | 变量 | 变量 | 15 | 16 | ## d1 17 | 18 | Concat all typescript files 19 | 20 | ```powershell 21 | Get-ChildItem -Recurse src/**.ts | ForEach-Object { 22 | echo "`n## $(Resolve-Path -Path $_ -Relative)`n`n``````typescript" ; 23 | Get-Content $_ -Encoding utf8; 24 | echo "``````" 25 | 26 | } | Out-File .\all.md 27 | ``` 28 | -------------------------------------------------------------------------------- /masm-tasm/doc/Thanks.md: -------------------------------------------------------------------------------- 1 | # Thank You 2 | 3 | - [#3](https://github.com/dosasm/masm-tasm/issues/3): [WMF1997](https://github.com/WMF1997) 4 | - [#4](https://github.com/dosasm/masm-tasm/issues/4): [Define2017](https://github.com/Define2017) 5 | - [#6](https://github.com/dosasm/masm-tasm/issues/6): [my-vegatable-has-exploded](https://github.com/my-vegatable-has-exploded) 6 | - [#10](https://github.com/dosasm/masm-tasm/issues/10): [lllsy12138](https://github.com/lllsy12138) 7 | 8 | ## licence and info 9 | 10 | - MS DOS player:[license](license_msdos_player/) 11 | - DOSBox: 12 | - masm-code: [license](https://github.com/Woodykaixa/masm-code/blob/5c73ee031789047a46d5682628338e2eb7d19190/package.json#L8) 13 | - masm 14 | -------------------------------------------------------------------------------- /masm-tasm/doc/develop.md: -------------------------------------------------------------------------------- 1 | # Develop 2 | 3 | ## STEPS 4 | 5 | ### 0.use GIT to clone this repository 6 | 7 | ```sh 8 | #cd myFolder 9 | git clone https://github.com/dosasm/masm-tasm.git 10 | ``` 11 | 12 | ### 1.install nodejs and npm and pnpm 13 | 14 | the website of nodejs: , 15 | 16 | ```sh 17 | sudo apt install nodejs 18 | sudo apt install npm 19 | npm install --global pnpm 20 | ``` 21 | 22 | ### 2.install dependence 23 | 24 | ```sh 25 | pnpm install 26 | ``` 27 | 28 | ### 3.generate `visx` package 29 | 30 | ```sh 31 | npx vsce package 32 | ``` 33 | 34 | ### Debug 35 | 36 | ```sh 37 | pnpm watch 38 | ``` 39 | 40 | Press F5 or Click `run` -> `Start Debugging` 41 | -------------------------------------------------------------------------------- /masm-tasm/i18n/i18n.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "activate.hello": "成功激活拓展masm/tasm", 3 | "ascii.NUL": "(空字符)", 4 | "ascii.SOH": "(标题开始)", 5 | "ascii.STX": "(正文开始)", 6 | "ascii.ETX": "(正文结束)", 7 | "ascii.EOT": "(传输结束)", 8 | "ascii.ENQ": "(请求)", 9 | "ascii.ACK": "(回应/响应/收到通知)", 10 | "ascii.BEL": "(响铃)", 11 | "ascii.BS": "(退格)", 12 | "ascii.HT": "(水平制表符)", 13 | "ascii.LFNL": "(换行键)", 14 | "ascii.VT": "(垂直制表符)", 15 | "ascii.FFNP": "(换页键)", 16 | "ascii.CR": "(回车键)", 17 | "ascii.SO": "(不用切换)", 18 | "ascii.SI": "(启用切换)", 19 | "ascii.DLE": "(数据链路转义)", 20 | "ascii.DC1": "(设备控制1/传输开始)", 21 | "ascii.DC2": "(设备控制2)", 22 | "ascii.DC3": "(设备控制3/传输中断)", 23 | "ascii.DC4": "(设备控制4)", 24 | "ascii.NAK": "(无响应/非正常响应/拒绝接收)", 25 | "ascii.SYN": "(同步空闲)", 26 | "ascii.ETB": "(传输块结束/块传输终止)", 27 | "ascii.CAN": "(取消)", 28 | "ascii.EM": "(已到介质末端/介质存储已满/介质中断)", 29 | "ascii.SUB": "(替补/替换)", 30 | "ascii.ESC": "(逃离/取消)", 31 | "ascii.FS": "(文件分割符)", 32 | "ascii.GS": "(组分隔符/分组符)", 33 | "ascii.RS": "(记录分离符)", 34 | "ascii.US": "(单元分隔符)", 35 | "ascii.space": "(空格)", 36 | "ascii.DEL": "(删除)", 37 | "num.hex": "十六进制数", 38 | "num.oct": "八进制数", 39 | "num.dec": "十进制数", 40 | "num.bin": "二进制数", 41 | "keykind.Command": "操作码助记符", 42 | "keykind.Memory": "数据定义指令", 43 | "keykind.Instruction": "伪指令助记符", 44 | "keykind.Register": "Register寄存器", 45 | "keykind.Saved": "Saved保留字", 46 | "keykind.Size": "Size", 47 | "keykind.Label": "Label标号", 48 | "keykind.Macro": "Macro宏指令", 49 | "keykind.Procedure": "Proc子程序", 50 | "keykind.Structure": "Structure 结构体", 51 | "keykind.Variable": "Variable 变量", 52 | "keykind.Segment": "Segment 段", 53 | "ASM.singleFileMode": "[ASM] single file Mode执行命令前文件将被复制到如下目录\n\t {0}", 54 | "ASM.openemu.msg": "[执行命令] 在文件的位置打开DOS模拟器{2}并配置{1}环境\n\t{0}", 55 | "ASM.run.msg": "[执行命令] 在{2}中使用{1}运行汇编文件\n\t{0}", 56 | "ASM.debug.msg": "[执行命令] 在{2}中使用{1}调试汇编文件\n\t{0}", 57 | "diag.msg": "[ASM] 插件从如下信息中收集到 {0}条错误,{1}条警告", 58 | "ASM.error": "汇编错误,参看输出面板", 59 | "ASM.warn": "成功汇编链接生成EXE,但是汇编时产生了警告信息({0}warning),可能无法运行/调试,是否继续操作", 60 | "ASM.continue": "继续", 61 | "ASM.stop": "否" 62 | } -------------------------------------------------------------------------------- /masm-tasm/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment 4 | "lineComment": ";" 5 | }, 6 | // symbols used as brackets 7 | "brackets": [ 8 | ["[", "]"], 9 | ["SEGMENT","ENDS"], 10 | ["STRUCT","ENDS"], 11 | ["MACRO","ENDM"], 12 | ["PROC","ENDP"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["[", "]"], 17 | ["\"", "\""], 18 | ["'", "'"] 19 | ], 20 | // symbols that that can be used to surround a selection 21 | "surroundingPairs": [ 22 | ["[", "]"], 23 | ["\"", "\""], 24 | ["'", "'"], 25 | ], 26 | "folding": { 27 | "markers": { 28 | "start": "^\\s*\\w+\\s+proc|struct|PROC|STRUCT|SEGMENT|MACRO|macro", 29 | "end": "^\\s*\\w*\\s*endp|ends|ENDP|ENDS|ENDM|endm" 30 | } 31 | }, 32 | "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\:\\;\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)|('.')|(\\;.+$)", 33 | } -------------------------------------------------------------------------------- /masm-tasm/package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.openEmu": "Open Emulator", 3 | "editor.runAsm": "Run ASM code", 4 | "editor.debugAsm": "Debug ASM code", 5 | "command.cleanalldianose": "MASM/TASM: Clean all diagnose information generated by the extension", 6 | "config.mode.singleFile": "copy your file to a seperate space, and do actions there", 7 | "config.mode.workspace": "do actions in the current workspace folder\n\n- use `mount` for dosbox and dosbox-x,copy all files in workspace folder for jsdos\n- follow 8.3 filename rule", 8 | "config.assembler.description": "use TASM or MASM to operate your assembly codes\n\n should be the key of #masmtasm.ASM.actions#", 9 | "config.emulator.description": "DOS environment emulator", 10 | "config.emulator.jsdos": "Use jsdos(wdosbox), run in webview", 11 | "config.emulator.dosbox": "Use DOSBox", 12 | "config.emulator.dosboxX": "Use DOSBox-x", 13 | "config.emulator.player": "Use MSDOS-player", 14 | "config.savefirst": "Save the file before Open dosbox, run and debug ASM codes", 15 | "config.boxrun.description": "What to do after run code in dosbox", 16 | "config.boxrun.enum1": "do nothing, manually input exit or click 'x' or press 'Ctrl+F9' to exit", 17 | "config.boxrun.enum2": "exit DOSBox automatically", 18 | "config.boxrun.enum3": "pause and then exit", 19 | "config.boxrun.choose": "use choose command to decide keep dosbox or not", 20 | "config.boxconfig.description": "configuration for DOSBox, use format like the default value, see [dosbox](https://www.dosbox.com/wiki/Dosbox.conf),Please don't set `autoexec` here", 21 | "config.boxXconfig.description": "configuration for DOSBox-X, use format like the default value, see [dosbox-x](https://dosbox-x.com/wiki/), Please don't set `autoexec `here", 22 | "config.hover": "Display Hover information or not, restart VSCode to apply", 23 | "config.cpp-docs.links": "Links for fetch hover data from [cppdocs](https://github.com/MicrosoftDocs/cpp-docs). \"{vid}\" will be replaced as `Visual Studio Code Language ID`,\"{mid}\" will be replaced as `MLCP language code`, see [vscode-loc](https://github.com/microsoft/vscode-loc)", 24 | "config.PLF": "Experimental programmatic language features like outline,jump to definition/reference. Restart needed", 25 | "config.format.align.description": "Align code in different ways", 26 | "config.format.align.enum.indent.label": "Indent only", 27 | "config.format.align.enum.label.label": "Align to label", 28 | "config.format.align.enum.segment.label": "Align within segment", 29 | "config.format.align.enum.indent.md": "Indent the code with fixed size.\n\n```asm\nL1:\n mov ax, 1\n mov bx, 2\nLabel2:\n mov cx, 3\n mov dx, 4\n```", 30 | "config.format.align.enum.label.md": "Align the code to its label\n\n```asm\nL1:\n mov ax, 1\n mov bx, 2\nLabel2:\n mov cx, 3\n mov dx, 4\n```", 31 | "config.format.align.enum.segment.md": "Align the code within segment\n\n```asm\nL1:\n mov ax, 1\n mov bx, 2\nLabel2:\n mov cx, 3\n mov dx, 4\n```", 32 | "config.format.casing.description": "Format the casing of identifiers\n\n`lower` - all lower case\n\n`upper` - all upper case\n\n`title` - title case\n\n`off` - do not change the casing", 33 | "config.format.casing.instruction": "Instructions, e.g. `MOV` `JMP` `PUSH`", 34 | "config.format.casing.directive": "Directives, e.g. `INCLUDE` `END` `PROC`", 35 | "config.format.casing.register": "Registers, e.g. `AX` `SI` `CS`", 36 | "config.format.casing.operator": "Operators, e.g. `HIGH` `LOW` `PTR`", 37 | "config.format.alignOperand.description": "Align operands", 38 | "config.format.alignTrailingComment.description": "Align trailing comments", 39 | "config.format.alignSingleLineComment.description": "Align single line comments", 40 | "config.format.spaceAfterComma.description": "Add space after comma", 41 | "config.format.spaceAfterComma.enum.always": "Always keep a space after comma", 42 | "config.format.spaceAfterComma.enum.never": "Remove space after comma", 43 | "config.format.spaceAfterComma.enum.off": "Do not change the space after comma" 44 | } -------------------------------------------------------------------------------- /masm-tasm/package.nls.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.openEmu": "打开DOS环境", 3 | "editor.runAsm": "运行当前程序(汇编+链接+运行)", 4 | "editor.debugAsm": "调试当前程序(汇编+链接+调试)", 5 | "command.cleanalldianose": "MASM/TASM: 清除MASM/TASM的所有问题信息", 6 | "config.mode.singleFile": "针对所有代码存储在一个文件中的模式,插件会首先复制文件并对副本进行操作", 7 | "config.mode.workspace": "针对文件存在依赖的模式,插件会直接在当前工作文件夹中操作。\n\n- dosbox会直接在工作区文件夹挂载,jsdos会复制整个工作区所有文件\n- 注意DOS系统常常需要遵守8.3规则", 8 | "config.assembler.description": "选择使用的汇编工具", 9 | "config.emulator.description": "选择使用的DOS环境模拟器", 10 | "config.emulator.jsdos": "使用jsdos在webview中运行jsdos(wdosbox)来提供DOS环境", 11 | "config.emulator.dosbox": "使用dosbox模拟DOS系统环境,非windows系统需要先安装dosbox并添加到PATH", 12 | "config.emulator.dosboxX": "使用DOSBox-x模拟dos环境", 13 | "config.emulator.player": "尽量使用msdos player模拟,不会弹出一个窗口,但是可能无法得到程序预期的运行结果", 14 | "config.savefirst": "在运行之前先保存文件,不保存的话,无法体现当前变更", 15 | "config.boxrun.description": "选择在dosbox中运行代码时,运行代码结束后执行什么操作", 16 | "config.boxrun.enum1": "运行之后在dosbox窗口停留,可以输入exit,点击右上角“x”,或者按Ctrl+F9关闭DOSBOX", 17 | "config.boxrun.enum2": "运行程序之后直接关闭DOSBOX", 18 | "config.boxrun.enum3": "运行程序之后先停顿一下,输入任意字符后关闭DOSBOX窗口", 19 | "config.boxrun.choose": "使用choose命令,来选择是否关闭dosbox", 20 | "config.boxconfig.description": "dosbox的配置信息,将会写到插件专用的配置文件中, 参见 [dosbox官网的说明](https://www.dosbox.com/wiki/Dosbox.conf),请勿设置`autoexec`", 21 | "config.boxXconfig.description": "dosbox-x的配置信息,将会写到插件专用的配置文件中, 参见 [dosbox-x官网的说明](https://dosbox-x.com/wiki/),请勿设置`autoexec`", 22 | "config.hover": "是否显示悬浮提示(hover),可能需要重启vscode来应用变更", 23 | "config.cpp-docs.links": "用于获取[cppdocs](https://github.com/MicrosoftDocs/cpp-docs)信息的链接, \"{vid}\"将被替换为`Visual Studio Code Language ID`,\"{mid}\" 将被替换为`MLCP language code`, 参见 [vscode-loc](https://github.com/microsoft/vscode-loc)", 24 | "config.PLF": "使用提供大纲,跳到定义,查找引用等programmatic features", 25 | "config.format.align.description": "以不同方式对齐代码", 26 | "config.format.align.enum.indent.label": "仅缩进", 27 | "config.format.align.enum.label.label": "对齐到标签", 28 | "config.format.align.enum.segment.label": "对齐到段", 29 | "config.format.align.enum.indent.md": "以固定的长度缩进代码\n\n```asm\nL1:\n mov ax, 1\n mov bx, 2\nLabel2:\n mov cx, 3\n mov dx, 4\n```", 30 | "config.format.align.enum.label.md": "代码对齐到其所在的标签\n\n```asm\nL1:\n mov ax, 1\n mov bx, 2\nLabel2:\n mov cx, 3\n mov dx, 4\n```", 31 | "config.format.align.enum.segment.md": "代码在段中对齐\n\n```asm\nL1:\n mov ax, 1\n mov bx, 2\nLabel2:\n mov cx, 3\n mov dx, 4\n```", 32 | "config.format.casing.description": "格式化标识符的大小写\n\n`lower` - 全部小写\n\n`upper` - 全部大写\n\n`title` - 首字母大写\n\n`off` - 不改变大小写", 33 | "config.format.casing.instruction": "指令, 例如`MOV` `JMP` `PUSH`", 34 | "config.format.casing.directive": "指令, 例如`INCLUDE` `END` `PROC`", 35 | "config.format.casing.register": "寄存器, 例如`AX` `SI` `CS`", 36 | "config.format.casing.operator": "运算符, 例如`HIGH` `LOW` `PTR`", 37 | "config.format.alignOperand.description": "是否对齐操作数", 38 | "config.format.alignTrailingComment.description": "是否对齐行末注释", 39 | "config.format.alignSingleLineComment.description": "是否对齐单行注释", 40 | "config.format.spaceAfterComma.description": "是否在逗号后添加空格", 41 | "config.format.spaceAfterComma.enum.always": "总是在逗号后保持一个空格", 42 | "config.format.spaceAfterComma.enum.never": "删除逗号后的空格", 43 | "config.format.spaceAfterComma.enum.off": "不改变逗号后的空格" 44 | } -------------------------------------------------------------------------------- /masm-tasm/pics/demo_PLFeature.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_PLFeature.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_diagnose_tasm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_diagnose_tasm.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_diagnose_tasm_zh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_diagnose_tasm_zh.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_dosbox_tasm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_dosbox_tasm.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_dosbox_tasm_zh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_dosbox_tasm_zh.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_jsdos.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_jsdos.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_msdos_masm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_msdos_masm.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_msdos_masm_zh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_msdos_masm_zh.gif -------------------------------------------------------------------------------- /masm-tasm/pics/demo_web-russiacube.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/demo_web-russiacube.gif -------------------------------------------------------------------------------- /masm-tasm/pics/opendosbox.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/opendosbox.gif -------------------------------------------------------------------------------- /masm-tasm/pics/opendosbox_zh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/pics/opendosbox_zh.gif -------------------------------------------------------------------------------- /masm-tasm/resources/dosboxasm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/masm-tasm/resources/dosboxasm.png -------------------------------------------------------------------------------- /masm-tasm/samples/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | **/*.OBJ 3 | **/*.MAP 4 | **/*.EXE 5 | -------------------------------------------------------------------------------- /masm-tasm/samples/1.asm: -------------------------------------------------------------------------------- 1 | ; a simple hello word sample 2 | .386 3 | DATA SEGMENT USE16 4 | MESG DB 'hello tasm',0AH,'$' 5 | DATA ENDS 6 | CODE SEGMENT USE16 7 | ASSUME CS:CODE,DS:DATA 8 | BEG: MOV AX,DATA 9 | MOV DS, AX 10 | MOV CX,8 11 | LAST:MOV AH,9 12 | MOV DX, OFFSET MESG 13 | INT 21H 14 | LOOP LAST 15 | MOV AH,4CH 16 | INT 21H ;BACK TO DOS 17 | CODE ENDS 18 | END BEG -------------------------------------------------------------------------------- /masm-tasm/samples/3中文路径hasError.asm: -------------------------------------------------------------------------------- 1 | ; a simple hello word sample 2 | .386 3 | DATA SEGMENT USE16 4 | MESG DB 'hello tasm',0AH,'$' 5 | DATA ENDS 6 | CODE SEGMENT USE16 7 | ASSUME CS:CODE,DS:DATA 8 | BEG: MOV AX,DATA 9 | MOV DS, AX 10 | MOV CX,8 11 | LAST:MOV AH,9 12 | MOV DX, OFFSET MESG 13 | INT 21H 14 | LOP LAST 15 | MOV AH,4CH 16 | INT 21H ;BACK TO DOS 17 | CODE ENDS 18 | END BEG -------------------------------------------------------------------------------- /masm-tasm/samples/multi/2.asm: -------------------------------------------------------------------------------- 1 | include multi\mac.inc 2 | .model small 3 | .stack 64 4 | .data 5 | msg db "hello","$" 6 | .code 7 | main proc far 8 | mov ax,@DaTa 9 | mov ds,ax 10 | mov ah,9 11 | mov dx,offset msg 12 | int 21h 13 | hlt 14 | MOV AH,4CH 15 | INT 21H ;BACK TO DOS 16 | main endp 17 | end main -------------------------------------------------------------------------------- /masm-tasm/samples/multi/mac.inc: -------------------------------------------------------------------------------- 1 | mac MACRO par 2 | 3 | ENDM -------------------------------------------------------------------------------- /masm-tasm/samples/readme.md: -------------------------------------------------------------------------------- 1 | # assembly samples 2 | 3 | this folder contains some assembly source code 4 | -------------------------------------------------------------------------------- /masm-tasm/samples/test.asm: -------------------------------------------------------------------------------- 1 | .model small 2 | .STACK 1024 3 | .data 4 | message db 8 dup(?),0dh,0ah,"$" 5 | .code 6 | main: mov ax,@data ;主程序开始 7 | mov ds,ax 8 | mov al,"A" ;子程序参数1 9 | mov si,offset message ;子程序参数2 10 | call BinToAsc ;调用子程序BinToAsc 11 | mov ah,9 12 | mov dx,offset message 13 | int 21h ;显示字符串 14 | mov ax,4c00h 15 | int 21h ;主程序结束 16 | 17 | BinToAsc PROC ;子程序开始 18 | push cx 19 | push si 20 | mov cx,8 21 | L1: shl al,1 ;逻辑左移 22 | mov BYTE PTR [si],"0" 23 | jnc L2 ;CF=0则跳转 24 | mov BYTE PTR [si],"1" 25 | L2: inc si 26 | loop L1 27 | pop si 28 | pop cx 29 | ret 30 | BinToAsc ENDP ;子程序结束 31 | end main -------------------------------------------------------------------------------- /masm-tasm/snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "Macro": { 3 | "prefix": "macro", 4 | "body": [ 5 | "${1:name} MACRO ${2:params}", 6 | "\t$0", 7 | "ENDM" 8 | ] 9 | }, 10 | "Procedure": { 11 | "prefix": "proc", 12 | "body": [ 13 | ";${2:description}", 14 | "${1:name} PROC", 15 | "\t$0", 16 | "${1:name} ENDP" 17 | ] 18 | }, 19 | "DOS print Char": { 20 | "prefix": "cprint", 21 | "body": [ 22 | "MOV DL, '${1:char}'", 23 | "MOV AH, 2H", 24 | "INT 21H" 25 | ], 26 | "description": "Prints a charcters" 27 | }, 28 | "helloworld": { 29 | "prefix": "helloword", 30 | "body": [ 31 | ".386", 32 | "DATA SEGMENT USE16", 33 | "${1:MESG DB 'Hello word','$'}", 34 | "DATA ENDS", 35 | "CODE SEGMENT USE16", 36 | "\tASSUME CS:CODE,DS:DATA", 37 | "BEG:", 38 | "\tMOV AX,DATA", 39 | "\tMOV DS,AX", 40 | "${2:\tMOV AH,9\n\tMOV DX, OFFSET MESG\n\tINT 21H}", 41 | "\tMOV AH,4CH", 42 | "\tINT 21H;back to dos", 43 | "CODE ENDS", 44 | "END BEG" 45 | ], 46 | "description": "HELLO WORD assembly sample" 47 | } 48 | } -------------------------------------------------------------------------------- /masm-tasm/src/ASM/main.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { emulist } from "../emulators/main"; 3 | import { nodejs_emu_list } from "../emulators/main-nodejs"; 4 | import { ActionType } from "../utils/configuration"; 5 | import { activateManager } from "./manager"; 6 | import * as statusBar from './statusBar'; 7 | 8 | export async function activate(context: vscode.ExtensionContext) { 9 | statusBar.activate(context); 10 | 11 | const execAction = activateManager(context, emulist.concat( 12 | nodejs_emu_list, 13 | )); 14 | 15 | context.subscriptions.push( 16 | vscode.commands.registerCommand('masm-tasm.openEmulator', (uri: vscode.Uri) => execAction(ActionType.open, uri)), 17 | vscode.commands.registerCommand('masm-tasm.runASM', (uri: vscode.Uri) => execAction(ActionType.run, uri)), 18 | vscode.commands.registerCommand('masm-tasm.debugASM', (uri: vscode.Uri) => execAction(ActionType.debug, uri)) 19 | ); 20 | } -------------------------------------------------------------------------------- /masm-tasm/src/ASM/statusBar.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as conf from '../utils/configuration'; 3 | 4 | const bar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 5 | 6 | const emu = [ 7 | conf.DosEmulatorType.jsdos, 8 | conf.DosEmulatorType.dosbox, 9 | conf.DosEmulatorType.dosboxX, 10 | conf.DosEmulatorType.msdos 11 | ]; 12 | 13 | const iterms: string[] = []; 14 | for (const a of Object.keys(conf.extConf.actions)) { 15 | for (const e of emu) { 16 | //if running in browser only use jsdos 17 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 18 | if ((process as any).browser && e !== conf.DosEmulatorType.jsdos) { 19 | continue; 20 | } 21 | 22 | //ignore msdos player by default 23 | if (conf.extConf.actions[a].support === undefined && e === conf.DosEmulatorType.msdos) { 24 | continue; 25 | } 26 | 27 | //hide msdos player in nonwin system 28 | if (process.platform !== "win32" && e === conf.DosEmulatorType.msdos) { 29 | continue; 30 | } 31 | 32 | if (conf.extConf.actions[a].support) { 33 | if (!conf.extConf.actions[a].support?.includes(e)) 34 | continue; 35 | } 36 | 37 | iterms.push(e + '\t' + a); 38 | } 39 | } 40 | 41 | function showStatus() { 42 | bar.command = 'masmtasm.updateEmuASM'; 43 | bar.text = `${conf.extConf.emulator} ${conf.extConf.asmType}`; 44 | bar.show(); 45 | } 46 | 47 | async function statusBarCommand() { 48 | const _conf = vscode.workspace.getConfiguration('masmtasm.ASM'); 49 | 50 | const placeHolder = 'choose DOS environment emulator and assembler'; 51 | const Selected = await vscode.window.showQuickPick(iterms, { placeHolder }); 52 | if (Selected) { 53 | const [emu1, asm1] = Selected?.split('\t'); 54 | const target = vscode.ConfigurationTarget.Global; 55 | await _conf.update('emulator', emu1, target); 56 | await _conf.update('assembler', asm1, target); 57 | showStatus(); 58 | } 59 | } 60 | 61 | export function activate(context: vscode.ExtensionContext): void { 62 | const disposable = vscode.commands.registerCommand('masmtasm.updateEmuASM', statusBarCommand); 63 | context.subscriptions.push(disposable); 64 | showStatus(); 65 | } -------------------------------------------------------------------------------- /masm-tasm/src/ASM/vscode-dosbox.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API from extension [vscode-dosbox](https://marketplace.visualstudio.com/items?itemName=xsro.vscode-dosbox) 3 | * originally from https://github.com/dosasm/vscode-dosbox/blob/main/src/api.ts 4 | */ 5 | 6 | import { CommandInterface, Emulators } from 'emulators'; 7 | import * as Jszip from 'jszip'; 8 | import * as vscode from 'vscode'; 9 | 10 | export interface DosboxResult { 11 | stdout: string, 12 | stderr: string, 13 | exitCode: number | null; 14 | } 15 | 16 | export interface Dosbox { 17 | /** 18 | * update the main part of the dosbox configuration file 19 | */ 20 | updateConf(section: string, key: string, value: string | number | boolean): void, 21 | /** 22 | * update the autoexec section of the dosbox configuration file 23 | */ 24 | updateAutoexec(context: string[]): void, 25 | /** 26 | * update the conf file from jsdos bundle 27 | * 28 | * @param bundle the bundle data 29 | * @param tempFolder the destination to exact the bundle file 30 | * @param useBundleConf use the bundle's dosbox.conf to update the dosbox's one (default false) 31 | */ 32 | fromBundle(bundle: Uint8Array, tempFolder: vscode.Uri, useBundleConf?: boolean): Promise 33 | /** 34 | * run the emulator 35 | * 36 | * @param params the parameter passed to dosbox via command line 37 | */ 38 | run(params?: string[]): Promise 39 | } 40 | 41 | export interface Jsdos { 42 | /** 43 | * set the jsdos bundle to use 44 | * 45 | * @deprecated use jszip 46 | * @param bundle the Uint8Array data of the jsdos bundle or its Uri 47 | * @param updateConf use the conf file in the bundle 48 | */ 49 | setBundle(bundle: vscode.Uri | Uint8Array, updateConf?: boolean): void, 50 | /** 51 | * the [jszip object](https://stuk.github.io/jszip/) 52 | * 53 | * change this to change the bundle's data, 54 | * the extension call it to generate bundle data 55 | */ 56 | jszip: Jszip; 57 | updateConf(section: string, key: string, value: string | number | boolean): boolean, 58 | updateAutoexec(context: string[]): void, 59 | /** 60 | * run jsdos in the VSCode's extension Host 61 | * 62 | * @todo make this also work in web extension 63 | * @returns [CommandInterface](https://js-dos.com/v7/build/docs/command-interface) 64 | */ 65 | runInHost(): Promise, 66 | /** 67 | * run **jsdos in the webview**. This works in all platform including web 68 | * 69 | * @param bundle the Uint8Array data of the jsdos bundle 70 | * @returns the vscode webview running JSDos 71 | */ 72 | runInWebview(): Promise, 73 | } 74 | 75 | export interface API { 76 | /** 77 | * [jsdos](https://js-dos.com/v7/build/) emulator 78 | * is the core of jsdos -- the simpliest API to run DOS games in browser 79 | * 80 | * @see https://github.com/js-dos/emulators 81 | */ 82 | emulators: Emulators; 83 | /** 84 | * run Jsdos in ExtensionHost or Webview 85 | */ 86 | jsdos: Jsdos; 87 | 88 | /** 89 | * run DOSBox via child_process 90 | */ 91 | dosbox: Dosbox; 92 | /** 93 | * run DOSBox-x via child_process 94 | */ 95 | dosboxX: Dosbox; 96 | 97 | /** 98 | * run msdos player via cmd.exe 99 | * 100 | * @returns a terminal to control 101 | */ 102 | msdosPlayer(msdosArgs?: string[], command?: string): vscode.Terminal; 103 | /**path of the packed msdos player */ 104 | msdosPath: string, 105 | /**path of the packed command.com file */ 106 | commandPath: string 107 | } -------------------------------------------------------------------------------- /masm-tasm/src/diagnose/codeAction.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | /** 3 | * Provides code actions corresponding to diagnostic problems. 4 | */ 5 | export class SeeinCPPDOCS implements vscode.CodeActionProvider { 6 | 7 | public static readonly providedCodeActionKinds = [ 8 | vscode.CodeActionKind.QuickFix 9 | ]; 10 | 11 | provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext): vscode.CodeAction[] { 12 | // for each diagnostic entry that has the matching `code`, create a code action command 13 | return context.diagnostics 14 | .filter(diagnostic => this.isObjCode(diagnostic.code)) 15 | .map(diagnostic => this.createCommandCodeAction(diagnostic.code as { value: string | number; target: vscode.Uri })); 16 | } 17 | private isObjCode(code: string | number | { value: string | number; target: vscode.Uri } | undefined): boolean { 18 | if (typeof code === 'object') { return 'target' in code; } 19 | return false; 20 | } 21 | private createCommandCodeAction(code: { value: string | number; target: vscode.Uri }): vscode.CodeAction { 22 | const action = new vscode.CodeAction('See in msvc', vscode.CodeActionKind.QuickFix); 23 | action.command = { 24 | command: "vscode.open", 25 | arguments: [code.target], 26 | title: 'Learn more about this error in microsoft cpp-docs', 27 | tooltip: 'This will open the cpp-docs page of masm.' 28 | }; 29 | action.isPreferred = true; 30 | return action; 31 | } 32 | } -------------------------------------------------------------------------------- /masm-tasm/src/diagnose/diagnoseMASM.ts: -------------------------------------------------------------------------------- 1 | import { TextDocument, DiagnosticCollection, Diagnostic, DiagnosticSeverity } from "vscode"; 2 | import { ASMdiagnostic, DIAGINFO } from './main'; 3 | export function masmDiagnose(MASMmsg: string, doc: TextDocument, collection: DiagnosticCollection): DIAGINFO { 4 | const diagnostics: Diagnostic[] = []; 5 | let error = 0, warn = 0; 6 | const severity = (str: string): DiagnosticSeverity | undefined => { 7 | switch (str) { 8 | case 'error': 9 | case 'fatal error': 10 | error++; 11 | return DiagnosticSeverity.Error; 12 | case 'warning': 13 | warn++; 14 | return DiagnosticSeverity.Warning; 15 | } 16 | }; 17 | //.ASM(1): fatal error A1000: cannot open file : mac.inc 18 | //const masm = new RegExp(/(?=\n)T.ASM\((\d+)\):(.*)(?=\nT.ASM)/, 's'); 19 | const masm0 = /\((\d+)\): (error|warning|fatal error)\s+([A-Z]\d+):\s+(.*)/; 20 | //const masml = /\s*T.ASM\((\d+)\): Out of memory/g; 21 | const allmsg = MASMmsg.split('\nT.ASM'); 22 | allmsg.forEach( 23 | (value) => { 24 | let RegExec = masm0.exec(value); 25 | const diag: ASMdiagnostic = new ASMdiagnostic(); 26 | if (RegExec) { 27 | diag.line = parseInt(RegExec[1]); 28 | diag.severity = severity(RegExec[2]); 29 | diag.code = RegExec[3]; 30 | diag.message = RegExec[4]; 31 | } 32 | RegExec = /(\w*)\((\d+)\): Macro Called From/.exec(value); 33 | if (RegExec) { 34 | diag.macro.uri = doc.uri; 35 | diag.macro.name = RegExec[1]; 36 | diag.macro.line = parseInt(RegExec[2]); 37 | } 38 | const diagnostic: Diagnostic | undefined = diag.toVscDiagnostic(doc); 39 | if (diagnostic) { 40 | diagnostic.source = "MASM6.11"; 41 | diagnostics.push(diagnostic); 42 | } 43 | } 44 | ); 45 | collection.set(doc.uri, diagnostics); 46 | return { error, warn, diagnostics }; 47 | } -------------------------------------------------------------------------------- /masm-tasm/src/diagnose/diagnoseMasm-error-list.ts: -------------------------------------------------------------------------------- 1 | const link = "https://docs.microsoft.com/en-us/cpp/assembler/masm/"; 2 | const list = [ 3 | { 4 | type: "fatal-error", 5 | codes: [ 6 | "a1000", "a1005", "a1007", "a1008", "a1009", "a1010", "a1011", "a1016", "a1017" 7 | ], 8 | }, 9 | { 10 | type: "nonfatal-error", 11 | codes: [ 12 | "a2004", "a2006", "a2008", "a2010", "a2019", "a2022", "a2031", "a2034", "a2037", "a2038", "a2039", "a2044", "a2047", 13 | "a2050", "a2054", "a2055", "a2057", "a2059", "a2060", "a2063", "a2064", "a2065", "a2066", "a2069", "a2070", "a2074", 14 | "a2078", "a2079", "a2083", "a2085", "a2096", "a2097", "a2107", "a2119", "a2133", "a2137", "a2189", "a2206", "a2219" 15 | ] 16 | }, 17 | { 18 | type: "warning", 19 | codes: [ 20 | "a4004", "a4012", "a4014" 21 | ] 22 | } 23 | ]; 24 | /** 25 | * offer a link like `https://docs.microsoft.com/en-us/cpp/assembler/masm/ml-nonfatal-error-a2008` 26 | * for reference 27 | * @param str the code of the error information 28 | */ 29 | export function getInternetlink(str: string): string | undefined { 30 | for (const val of list) { 31 | for (const code of val.codes) { 32 | if (str.toLowerCase() === code) { 33 | return `${link}ml-${val.type}-${code}`; 34 | } 35 | } 36 | } 37 | return undefined; 38 | } -------------------------------------------------------------------------------- /masm-tasm/src/diagnose/diagnoseTASM.ts: -------------------------------------------------------------------------------- 1 | import { Diagnostic, TextDocument, DiagnosticCollection, DiagnosticSeverity } from "vscode"; 2 | import { ASMdiagnostic, DIAGINFO } from './main'; 3 | /** 4 | * Process the output of TASM assembler 5 | * @param TASMmsg the output of TASM 6 | * @param doc the doc of the source codes 7 | * @param collection the DiagnosticCollection for assign 8 | */ 9 | export function tasmDiagnose(TASMmsg: string, doc: TextDocument, collection: DiagnosticCollection): DIAGINFO { 10 | const diagnostics: Diagnostic[] = []; 11 | const tasm = /\s*\*+(Error|Warning|Fatal)\*+\s+(.*)\((\d+)\)\s+(.*)/; 12 | const tasmMacro = /\s*\*+(Error|Warning|Fatal)\*+\s+(.*)\((\d+)\) (.*)\((\d+)\)\s+(.*)/; 13 | let error = 0, warn = 0; 14 | const severity = (str: string): DiagnosticSeverity | undefined => { 15 | switch (str) { 16 | case 'Error': 17 | case 'Fatal': 18 | error++; 19 | return DiagnosticSeverity.Error; 20 | case 'Warning': 21 | warn++; 22 | return DiagnosticSeverity.Warning; 23 | } 24 | }; 25 | 26 | const allmsg = TASMmsg.split('\n'); 27 | allmsg.forEach( 28 | (value) => { 29 | let RegExec = tasmMacro.exec(value); 30 | const diag: ASMdiagnostic = new ASMdiagnostic(); 31 | let VSCdiag: Diagnostic | undefined; 32 | if (RegExec) { 33 | diag.severity = severity(RegExec[1]); 34 | diag.line = parseInt(RegExec[3]);//2错误所在行 35 | diag.macro.local = true; 36 | diag.macro.name = RegExec[4];//3宏名 37 | diag.macro.line = parseInt(RegExec[5]);//4错误所在宏的位置 38 | diag.macro.uri = doc.uri; 39 | diag.message = RegExec[6];//5错误名称 40 | VSCdiag = diag.toVscDiagnostic(doc); 41 | } 42 | if (VSCdiag === undefined) { 43 | RegExec = tasm.exec(value); 44 | if (RegExec && RegExec.length === 5) { 45 | diag.severity = severity(RegExec[1]); 46 | diag.line = parseInt(RegExec[3]); 47 | diag.message = RegExec[4]; 48 | VSCdiag = diag.toVscDiagnostic(doc); 49 | } 50 | } 51 | if (VSCdiag) { 52 | VSCdiag.source = "TASM4.1"; 53 | diagnostics.push(VSCdiag); 54 | } 55 | } 56 | ); 57 | collection.set(doc.uri, diagnostics); 58 | return { error, warn, diagnostics }; 59 | } 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /masm-tasm/src/diagnose/messageCollector.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export function messageCollector(): [(msg: string) => void, Promise] { 4 | let allmsg = ""; 5 | let resolve: ((value: string) => void) | undefined = undefined; 6 | return [ 7 | (msg: string) => { 8 | allmsg += msg; 9 | let re = allmsg.match(/Microsoft \(R\) MASM Compatibility Driver([\s\S]*)Microsoft \(R\) Segmented Executable Linker/); 10 | if (re && re[1] && resolve) { 11 | resolve(re[1]); 12 | resolve = undefined; 13 | } 14 | re = allmsg.match(/Turbo Assembler Version 4.1 Copyright \(c\) 1988, 1996 Borland International([\s\S]*)Turbo Link Version 7\./); 15 | if (re && re[1] && resolve) { 16 | resolve(re[1]); 17 | resolve = undefined; 18 | } 19 | re = allmsg.match(/Microsoft \(R\) Macro Assembler Version 5.00([\s\S]*)Microsoft \(R\) Overlay Linker Version 3.60/); 20 | if (re && re[1] && resolve) { 21 | resolve(re[1]); 22 | resolve = undefined; 23 | } 24 | } 25 | , 26 | new Promise( 27 | _resolve => resolve = _resolve 28 | )]; 29 | 30 | } -------------------------------------------------------------------------------- /masm-tasm/src/emulators/jsdosHost.ts: -------------------------------------------------------------------------------- 1 | //TODO -------------------------------------------------------------------------------- /masm-tasm/src/emulators/jsdosWeb.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, workspace } from "vscode"; 2 | import { DosEmulatorType } from "../utils/configuration"; 3 | import { ActionContext, AsmResult, ExecAction, API } from "../ASM/manager"; 4 | import { getFiles, uriUtils } from "../utils/util"; 5 | 6 | import * as conf from "../utils/configuration"; 7 | import * as path from "path"; 8 | import { messageCollector } from "../diagnose/messageCollector"; 9 | 10 | export class JsdosWeb implements ExecAction { 11 | name: DosEmulatorType | DosEmulatorType[] = DosEmulatorType.jsdos; 12 | async run(context: ExtensionContext, ctx: ActionContext, api: API): Promise { 13 | await api.jsdos.jszip.loadAsync(ctx.bundleData); 14 | 15 | let fileInJsdos = ""; 16 | if (ctx.mountMode === conf.MountMode.workspace) { 17 | for await (const f of getFiles(ctx.workspaceFolderUri)) { 18 | const rel = path.relative(ctx.workspaceFolderUri.fsPath, f.fsPath); 19 | const dst = path.posix.join('code/', rel); 20 | const _data = await workspace.fs.readFile(f); 21 | api.jsdos.jszip.file(dst, _data); 22 | } 23 | const rel = path.relative( 24 | ctx.workspaceFolderUri.fsPath, 25 | ctx.fileUri.fsPath, 26 | ); 27 | fileInJsdos = "D:\\"+ rel.replace(/\//g,"\\"); 28 | } else if (ctx.mountMode === conf.MountMode.single) { 29 | api.jsdos.jszip.file('code/test' + uriUtils.extname(ctx.fileUri), ctx.doc.getText()); 30 | fileInJsdos = "D:\\test" + uriUtils.extname(ctx.fileUri); 31 | } 32 | 33 | const fileinfo = path.parse(fileInJsdos); 34 | const autoexec = [ 35 | `mount c .`, 36 | `mount d ./code`, 37 | 'd:' 38 | ]; 39 | const before = conf.extConf.action.before; 40 | if (before) { 41 | autoexec.push(...before); 42 | } 43 | function cb(val: string) { 44 | const r = val 45 | .replace("${file}", fileInJsdos) 46 | .replace("${filename}", fileInJsdos.replace(fileinfo.ext, "")); 47 | if (val.startsWith('>')) { 48 | return r.replace(">", ""); 49 | } 50 | return r; 51 | } 52 | if (ctx.actionType === conf.ActionType.run) { 53 | autoexec.push(...conf.extConf.action.run.map(cb)); 54 | } 55 | if (ctx.actionType === conf.ActionType.debug) { 56 | autoexec.push(...conf.extConf.action.debug.map(cb)); 57 | } 58 | api.jsdos.updateAutoexec(autoexec); 59 | const webview = await api.jsdos.runInWebview(); 60 | if (ctx.actionType !== conf.ActionType.open) { 61 | const [hook, promise] = messageCollector(); 62 | webview.onDidReceiveMessage(e => { 63 | switch (e.command) { 64 | case 'stdout': 65 | hook(e.value); 66 | break; 67 | } 68 | }); 69 | const message = await promise; 70 | return { message, webview }; 71 | } 72 | throw new Error("can't get message from" + this.name); 73 | } 74 | } -------------------------------------------------------------------------------- /masm-tasm/src/emulators/main-nodejs.ts: -------------------------------------------------------------------------------- 1 | import { Dosbox } from "./dosbox"; 2 | import { msdos } from "./msdos-player"; 3 | 4 | export const nodejs_emu_list = [ 5 | new Dosbox(), 6 | new msdos(), 7 | ]; -------------------------------------------------------------------------------- /masm-tasm/src/emulators/main.ts: -------------------------------------------------------------------------------- 1 | import { JsdosWeb } from "./jsdosWeb"; 2 | 3 | export const emulist = [ 4 | new JsdosWeb(), 5 | ]; -------------------------------------------------------------------------------- /masm-tasm/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { localize, loadI18n } from './utils/i18n'; 3 | 4 | import * as lan from './language/main'; 5 | import * as asm from './ASM/main'; 6 | 7 | export function activate(context: vscode.ExtensionContext): void { 8 | 9 | loadI18n(context); 10 | 11 | //provide programmaic language features like hover,references,outline(symbol) 12 | lan.activate(context); 13 | //provide run and debug features via DOS emulators 14 | asm.activate(context); 15 | 16 | console.log(localize("activate.hello")); 17 | } 18 | 19 | // this method is called when your extension is deactivated 20 | export function deactivate(): void { 21 | console.log('extension deactivated'); 22 | } 23 | -------------------------------------------------------------------------------- /masm-tasm/src/language/AsmReference.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { DocInfo, linetype } from './scanDoc'; 3 | import { KeywordType } from './wordinfo'; 4 | export class AsmReferenceProvider implements vscode.ReferenceProvider { 5 | provideReferences(document: vscode.TextDocument, position: vscode.Position): vscode.Location[] { 6 | const range = document.getWordRangeAtPosition(new vscode.Position(position.line, position.character)); 7 | let output: vscode.Location[] = []; 8 | const docinfo = DocInfo.getDocInfo(document); //scan thdocumente 9 | if (range) { 10 | const word = document.getText(range); 11 | output = getrefer(docinfo, word, document); 12 | } 13 | return output; 14 | } 15 | } 16 | function getrefer(docinfo: DocInfo, word: string, doc: vscode.TextDocument): vscode.Location[] { 17 | const output: vscode.Location[] = []; 18 | let r: vscode.Range, skip = false; 19 | const def = docinfo.findSymbol(word); 20 | if (def?.location) { 21 | output.push(def.location(doc.uri)); 22 | docinfo.lines.forEach( 23 | (item, index) => { 24 | switch (item.type) { 25 | case linetype.macro: skip = true; break; 26 | case linetype.endm: skip = false; break; 27 | case linetype.label: 28 | if (skip === false) { 29 | //TODO:match the symbol more exactly 30 | if (def?.type === KeywordType.Variable && item.operand?.match(new RegExp("\\b" + word + "\\b"))) { 31 | const start = item.str.indexOf(word); 32 | r = new vscode.Range(index, start, index, start + word.length); 33 | } 34 | else if (def?.type === KeywordType.Macro && item.operator === word) { 35 | const start = item.str.indexOf(word); 36 | r = new vscode.Range(index, start, index, start + word.length); 37 | } 38 | else if ((def?.type === KeywordType.Procedure || def?.type === KeywordType.Label) && item.operand === word) { 39 | const start = item.str.indexOf(word); 40 | r = new vscode.Range(index, start, index, start + word.length); 41 | } 42 | if (r) { output.push(new vscode.Location(doc.uri, r)); } 43 | } 44 | else { 45 | 46 | } 47 | 48 | } 49 | } 50 | ); 51 | } 52 | return output; 53 | } -------------------------------------------------------------------------------- /masm-tasm/src/language/hoverFelix.ts: -------------------------------------------------------------------------------- 1 | import { Uri, workspace } from "vscode"; 2 | 3 | const fs = workspace.fs; 4 | 5 | /**the information iterm for instructions */ 6 | interface InstructionsJSON { 7 | linkprefix: string; 8 | sections: string[]; 9 | from: string; 10 | collection: [ 11 | /**the code of section */ 12 | number, 13 | /**the keyword name */ 14 | string, 15 | /**the summary */ 16 | string, 17 | /**link path of https://www.felixcloutier.com/x86/ */ 18 | string, 19 | ][]; 20 | } 21 | 22 | /**offer information of Intel x86 instructions 23 | * use data from `/resources/instruction-refernces` 24 | * which is generated from https://www.felixcloutier.com/x86/ 25 | */ 26 | export class FELIX { 27 | 28 | static async create(fileuri: Uri): Promise { 29 | const arr = await fs.readFile(fileuri); 30 | try { 31 | const data = new TextDecoder().decode(arr); 32 | const json = JSON.parse(data); 33 | return new FELIX(json); 34 | } catch (e) { 35 | console.error(e); 36 | } 37 | throw new Error(); 38 | } 39 | 40 | constructor(private target: InstructionsJSON) { 41 | 42 | } 43 | 44 | public findKeyword(word: string): string | undefined { 45 | const items = this.target.collection.filter( 46 | val => val[1].toLowerCase() === word.toLowerCase() 47 | ); 48 | if (items.length > 0) { 49 | const section = this.target.sections[items[0][0]]; 50 | let md = `**${word}** ${section}\n\n`; 51 | md += items.map( 52 | val => `- [link](${this.target.linkprefix}${val[3]}) ${val[2]} ` 53 | ).join('\n'); 54 | return md; 55 | } 56 | return undefined; 57 | } 58 | } -------------------------------------------------------------------------------- /masm-tasm/src/language/hoverFromMarkdown.ts: -------------------------------------------------------------------------------- 1 | import * as yaml from 'js-yaml'; 2 | import { env, Uri, workspace } from 'vscode'; 3 | import { keywordType } from './Hover'; 4 | const fs = workspace.fs; 5 | 6 | interface HoverInfoItem { 7 | head: { 8 | type: keywordType; 9 | keyword: string | string[]; 10 | 'i18n': string[]; 11 | }; 12 | info: string[]; 13 | } 14 | 15 | /**get hover information from markdown*/ 16 | export class HoverFromMarkdown { 17 | constructor(private target: HoverInfoItem[]) { 18 | 19 | } 20 | static async create(uri: Uri): Promise { 21 | const arr = await fs.readFile(uri); 22 | const str = new TextDecoder().decode(arr).replace(/\r\n/g, '\n'); 23 | const target: HoverInfoItem[] = []; 24 | const regex = /```yaml\n([\s\S]+?)\n```([\s\S]+?)(?=```)/g; 25 | let re = regex.exec(str); 26 | while (re?.length === 3) { 27 | const [, yamlcode, content] = re; 28 | const head = yaml.load(yamlcode); 29 | if (content.includes('---')) { 30 | const info = content.split('\n---\n'); 31 | target.push({ head, info } as HoverInfoItem); 32 | } 33 | re = regex.exec(str); 34 | } 35 | return new HoverFromMarkdown(target); 36 | } 37 | 38 | findKeyword(word: string, types: keywordType[]): string | undefined { 39 | const compare = (val1: string, val2: string): boolean => val1.toLowerCase() === val2.toLowerCase(); 40 | const finded = this.target.find( 41 | val => { 42 | const key = val.head?.keyword; 43 | if (types.includes(val.head.type)) { 44 | if (key && typeof key === 'string') { 45 | return compare(key, word); 46 | } else if (Array.isArray(key)) { 47 | return key.some(val => compare(val, word)); 48 | } 49 | } 50 | return false; 51 | } 52 | ); 53 | if (finded) { 54 | let idx = 0; 55 | const lang = env.language; 56 | if (lang !== 'en' && Object.keys(finded.head).includes('i18n')) { 57 | const idx2 = finded.head['i18n'].findIndex(val => val === lang); 58 | idx = idx2 > -1 ? idx2 + 1 : 0; 59 | } 60 | if (finded.info.length > idx) { 61 | return finded.info[idx]; 62 | } else { 63 | return finded.info[0]; 64 | } 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /masm-tasm/src/language/main.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { DocInfo } from "./scanDoc"; 3 | import { AsmDocFormat } from './AsmDocumentFormattingEdit'; 4 | import { AsmHoverProvider } from './Hover'; 5 | import { AsmReferenceProvider } from './AsmReference'; 6 | 7 | class AsmDefProvider implements vscode.DefinitionProvider { 8 | provideDefinition(document: vscode.TextDocument, position: vscode.Position): vscode.Definition { 9 | const range = document.getWordRangeAtPosition(new vscode.Position(position.line, position.character)); 10 | const docinfo = DocInfo.getDocInfo(document);//scan thdocumente 11 | const wordo = document.getText(range); 12 | const tasmsymbol = docinfo.findSymbol(wordo); 13 | if (tasmsymbol) { 14 | return tasmsymbol.location(document.uri); 15 | } 16 | return []; 17 | } 18 | } 19 | 20 | class Asmsymbolprovider implements vscode.DocumentSymbolProvider { 21 | provideDocumentSymbols(document: vscode.TextDocument): vscode.DocumentSymbol[] { 22 | const sym = DocInfo.getDocInfo(document).tree; 23 | if (sym) { 24 | return sym; 25 | } 26 | return []; 27 | } 28 | } 29 | 30 | export function activate(context: vscode.ExtensionContext): void { 31 | const programmaticFeatures = vscode.workspace.getConfiguration("masmtasm.language"); 32 | if (programmaticFeatures.get("Hover")) { 33 | context.subscriptions.push(vscode.languages.registerHoverProvider('assembly', new AsmHoverProvider(context))); 34 | } 35 | if (programmaticFeatures.get("programmaticFeatures")) { 36 | context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider("assembly", new Asmsymbolprovider())); 37 | context.subscriptions.push(vscode.languages.registerDefinitionProvider("assembly", new AsmDefProvider())); 38 | context.subscriptions.push(vscode.languages.registerReferenceProvider("assembly", new AsmReferenceProvider())); 39 | context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider("assembly", new AsmDocFormat())); 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /masm-tasm/src/readme.md: -------------------------------------------------------------------------------- 1 | # Source Code Folder 2 | 3 | - folder `language`: provide Programmatic language features 4 | - folder `utils`: chore like i18n 5 | - folder `diagnose`: process the message of Assembler 6 | - folder `ASM`: interface for interaction with assembler and emulator 7 | - folder `emulators`: codes for interaction with assembler and emulator 8 | -------------------------------------------------------------------------------- /masm-tasm/src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * https://code.visualstudio.com/api/working-with-extensions/testing-extension#custom-setup-with-vscodetestelectron 3 | */ 4 | 5 | import * as cp from 'child_process'; 6 | import * as path from 'path'; 7 | import * as fs from "fs"; 8 | import { 9 | downloadAndUnzipVSCode, 10 | resolveCliArgsFromVSCodeExecutablePath, 11 | runTests 12 | } from '@vscode/test-electron'; 13 | 14 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 15 | const extensionTestsPath = path.resolve(__dirname, './suite/index.js'); 16 | 17 | async function test(version:string){ 18 | const vscodeExecutablePath = await downloadAndUnzipVSCode(version); 19 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 20 | const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); 21 | 22 | const extensions = [ 23 | "xsro.vscode-dosbox" 24 | ]; 25 | 26 | const vscedosbox = path.resolve(__dirname, "../..", "..", "vscode-dosbox"); 27 | if (fs.existsSync(vscedosbox)) { 28 | const dirs = await fs.promises.readdir(vscedosbox); 29 | const dir = dirs.find(val => val.includes(`vscode-dosbox-${process.platform}-${process.arch}`)); 30 | if (dir) { 31 | console.log("found " + dir); 32 | extensions[0] = path.resolve(vscedosbox, dir); 33 | } 34 | } 35 | 36 | // Use cp.spawn / cp.exec for custom setup 37 | cp.spawnSync(cli, [...args,'--install-extension', ...extensions], { 38 | encoding: 'utf-8', 39 | stdio: 'inherit' 40 | }); 41 | 42 | const sampleFolder = path.resolve(__dirname, '../../samples'); 43 | const launchArgs: string[] = [ 44 | "--disable-workspace-trust", 45 | sampleFolder 46 | ]; 47 | 48 | // Run the extension test 49 | await runTests({ 50 | // Use the specified `code` executable 51 | vscodeExecutablePath, 52 | extensionDevelopmentPath, 53 | extensionTestsPath, 54 | launchArgs 55 | }); 56 | } 57 | 58 | async function main() { 59 | try { 60 | await test('stable'); 61 | const pack=fs.readFileSync(path.resolve( extensionDevelopmentPath,"package.json"),"utf-8") 62 | const least=JSON.parse(pack).engines.vscode.replace("^",""); 63 | await test(least); 64 | 65 | } catch (err) { 66 | console.error('Failed to run tests'); 67 | process.exit(1); 68 | } 69 | } 70 | 71 | main(); -------------------------------------------------------------------------------- /masm-tasm/src/test/suite/diagnose.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | 7 | // import * as myExtension from '../../extension'; 8 | 9 | suite('Diagnose test', function () { 10 | 11 | 12 | test('TASM Error message', function () { 13 | this.skip(); 14 | const message = 'Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International\r\n' + 15 | '\r\n' + 16 | 'Assembling file: test.asm\r\n' + 17 | '**Error** test.asm(14) Illegal instruction\r\n' + 18 | 'Error messages: 1\r\n' + 19 | 'Warning messages: None\r\n' + 20 | 'Passes: 1\r\n' + 21 | 'Remaining memory: 468k\r\n' + 22 | '\r\n' + 23 | 'Turbo Link Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International\r\n' + 24 | "Fatal: Unable to open file 'test.obj'\r\n" + 25 | 'Illegal command: test.\r\n'; 26 | 27 | }); 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /masm-tasm/src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)); 13 | assert.equal(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); -------------------------------------------------------------------------------- /masm-tasm/src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | import * as vscode from 'vscode'; 5 | 6 | export function run(): Promise { 7 | // Create the mocha test 8 | const mocha = new Mocha({ 9 | ui: 'tdd', 10 | color: true, 11 | timeout: 2000, 12 | retries: 3, 13 | }); 14 | 15 | if (process.platform === 'linux') { 16 | vscode.workspace.getConfiguration('vscode-dosbox').update( 17 | "command.dosboxX", 18 | "flatpak run com.dosbox_x.DOSBox-X -silent -nogui", 19 | vscode.ConfigurationTarget.Global 20 | ); 21 | } 22 | 23 | const testsRoot = path.resolve(__dirname, '..'); 24 | 25 | return new Promise((c, e) => { 26 | glob('**/*.test.js', { cwd: testsRoot }, (err, files) => { 27 | if (err) { 28 | return e(err); 29 | } 30 | 31 | // Add files to the test suite 32 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 33 | 34 | try { 35 | // Run the mocha test 36 | mocha.run(failures => { 37 | if (failures > 0) { 38 | e(new Error(`${failures} tests failed.`)); 39 | } else { 40 | c(); 41 | } 42 | }); 43 | } catch (err) { 44 | console.error(err); 45 | e(err); 46 | } 47 | }); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /masm-tasm/src/utils/configuration.ts: -------------------------------------------------------------------------------- 1 | /** the id of the profile,for example: 2 | * 3 | * - MASM: including masm.exe,link.exe,debug.exe 4 | * - TASM: including tasm.exe,tlink.exe,TD.exe 5 | */ 6 | type profileId = string; 7 | export type Assembler = profileId; 8 | 9 | /** 10 | * the profile of the action 11 | */ 12 | type ActionProfile = { 13 | baseBundle: string, 14 | before?: string[], 15 | run: string[], 16 | debug: string[], 17 | support?: string[], 18 | }; 19 | 20 | export type ACTIONS = { 21 | [id: profileId]: ActionProfile 22 | }; 23 | 24 | /**the emulator for the 16bit DOS environment 25 | * - `dosbox` is the most famous one 26 | * - `msdos`( player) is designed for running in windows cmd 27 | * - `jsdos` is designed for runing in browser 28 | * - `auto` is a mode to partly solve the problem of TD's hardly running in msdos 29 | */ 30 | export enum DosEmulatorType { 31 | dosbox = 'dosbox', 32 | dosboxX = 'dosbox-x', 33 | msdos = 'msdos player', 34 | jsdos = 'jsdos', 35 | } 36 | 37 | /**the action type for run a assembly file */ 38 | export enum ActionType { 39 | open, 40 | run, 41 | debug 42 | } 43 | 44 | /**how to mount the assembly file to DOS emulator */ 45 | export enum MountMode { 46 | single = "single file", 47 | workspace = "workspace" 48 | } 49 | 50 | import * as vscode from 'vscode'; 51 | 52 | class ExtensionConfiguration { 53 | public get _conf() { 54 | return vscode.workspace.getConfiguration('masmtasm'); 55 | } 56 | 57 | public get(id: string, defaultValue: T) { 58 | const value: T | undefined = this._conf.get(id); 59 | if (value === undefined) { 60 | return defaultValue; 61 | } 62 | return value; 63 | } 64 | 65 | public get actions(): ACTIONS { 66 | const actions: ACTIONS | undefined = this._conf.get("ASM.actions"); 67 | if (actions === undefined) { 68 | throw new Error('`masmtasm.ASM.actions` is undefined'); 69 | } 70 | return actions; 71 | } 72 | public get asmType(): Assembler { 73 | const asmType: Assembler | undefined = vscode.workspace.getConfiguration('masmtasm').get('ASM.assembler'); 74 | if (asmType === undefined) { 75 | throw new Error('`masmtasm.ASM.assembler` is undefined'); 76 | } 77 | if (Object.keys(this.actions).includes(asmType)) { 78 | return asmType; 79 | } else { 80 | if (asmType === 'MASM' && Object.keys(this.actions).includes('MASM-v6.11')) { 81 | return 'MASM-v6.11'; 82 | } 83 | vscode.window.showErrorMessage(`${asmType} is not defined in "masmtasm.ASM.actions"`); 84 | throw new Error(`${asmType} is not defined in "masmtasm.ASM.actions"`); 85 | } 86 | } 87 | public get action(): ActionProfile { 88 | return this.actions[this.asmType]; 89 | } 90 | 91 | public get emulator(): DosEmulatorType { 92 | const emu: DosEmulatorType | undefined = vscode.workspace.getConfiguration('masmtasm').get('ASM.emulator'); 93 | return emu ? emu : DosEmulatorType.jsdos; 94 | } 95 | } 96 | 97 | export const extConf = new ExtensionConfiguration(); -------------------------------------------------------------------------------- /masm-tasm/src/utils/downloadFile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API for downloading information from web 3 | */ 4 | import { logger } from "./logger"; 5 | import fetch from 'node-fetch'; 6 | 7 | export async function downloadFromMultiSources(urls: string[]): Promise { 8 | for (const url of urls) { 9 | try { 10 | const data = await fetch(url); 11 | const val = await data.text(); 12 | if (val) { 13 | return val; 14 | } 15 | } catch (e) { 16 | logger.channel(`donwload ${url}\nerror ${JSON.stringify(e)}`); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /masm-tasm/src/utils/eol.ts: -------------------------------------------------------------------------------- 1 | import { EndOfLine } from "vscode"; 2 | 3 | export function eolString(eol: EndOfLine) { 4 | switch (eol) { 5 | case EndOfLine.CRLF: 6 | return "\r\n"; 7 | case EndOfLine.LF: 8 | return "\n"; 9 | } 10 | } -------------------------------------------------------------------------------- /masm-tasm/src/utils/i18n-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "activate.hello": "Congratulations, your extension \"masm-tasm\" is now active!", 3 | "ascii.NUL": "(NULL)", 4 | "ascii.SOH": "(Start Of Headling)", 5 | "ascii.STX": "(Start Of Text)", 6 | "ascii.ETX": "(End Of Text)", 7 | "ascii.EOT": "(End Of Transmission)", 8 | "ascii.ENQ": "(Enquiry)", 9 | "ascii.ACK": "(Acknowledge)", 10 | "ascii.BEL": "(Bell)", 11 | "ascii.BS": "(Backspace)", 12 | "ascii.HT": "(Horizontal Tab)", 13 | "ascii.LFNL": "(Line Feed/New Line)", 14 | "ascii.VT": "(Vertical Tab)", 15 | "ascii.FFNP": "(Form Feed/New Page)", 16 | "ascii.CR": "(Carriage Return)", 17 | "ascii.SO": "(Shift Out)", 18 | "ascii.SI": "(Shift In)", 19 | "ascii.DLE": "(Data Link Escape)", 20 | "ascii.DC1": "(Device Control 1/Transmission On)", 21 | "ascii.DC2": "(Device Control 2)", 22 | "ascii.DC3": "(Device Control 3/Transmission Off)", 23 | "ascii.DC4": "(Device Control 4)", 24 | "ascii.NAK": "(Negative Acknowledge)", 25 | "ascii.SYN": "(Synchronous Idle)", 26 | "ascii.ETB": "(End of Transmission Block)", 27 | "ascii.CAN": "(Cancel)", 28 | "ascii.EM": "(End of Medium)", 29 | "ascii.SUB": "(Substitute)", 30 | "ascii.ESC": "(Escape)", 31 | "ascii.FS": "(File Separator)", 32 | "ascii.GS": "(Group Separator)", 33 | "ascii.RS": "(Record Separator)", 34 | "ascii.US": "(Unit Separator)", 35 | "ascii.space": "(Space)", 36 | "ascii.DEL": "(Delete)", 37 | "num.hex": "Hexadecimal Number", 38 | "num.oct": "Octal Number", 39 | "num.dec": "Decimal Number", 40 | "num.bin": "Binary Number", 41 | "keykind.Command": "(Opcode mnemonics)", 42 | "keykind.Memory": "(Data definitions)", 43 | "keykind.Instruction": "(Assembly directives)", 44 | "keykind.Register": "(Register)", 45 | "keykind.Saved": "(Saved)", 46 | "keykind.Size": "(Size)", 47 | "keykind.Label": "(Label)", 48 | "keykind.Macro": "(Macro)", 49 | "keykind.Procedure": "(Procedure)", 50 | "keykind.Structure": "(Structure)", 51 | "keykind.Variable": "(Variable)", 52 | "keykind.Segment": "(Segment)", 53 | "ASM.singleFileMode": "[ASM] single file Mode, your file will be copied to folder\n\t {0}", 54 | "ASM.openemu.msg": "[ASM]open DOS emulator {2} set {1} in folder of: \n\t{0}", 55 | "ASM.run.msg": "[ASM]run code with {1} in {2}\n\t{0}", 56 | "ASM.debug.msg": "[ASM]debug code with {1} in {2}\n\t{0}", 57 | "diag.msg": "[ASM] extension collect {0} Error,{1} Warn from following message:", 58 | "ASM.error": "Error, see the output channel", 59 | "ASM.warn": "成功汇编链接生成EXE,但是汇编时产生了警告信息({0}warning),可能无法运行/调试,是否继续操作", 60 | "ASM.continue": "继续", 61 | "ASM.stop": "否" 62 | } -------------------------------------------------------------------------------- /masm-tasm/src/utils/i18n.ts: -------------------------------------------------------------------------------- 1 | import { workspace, ExtensionContext, env, Uri } from "vscode"; 2 | import * as defaultMessage from './i18n-default.json'; 3 | let localMessage: { [id: string]: string } | null = null; 4 | 5 | type MessageKey = keyof (typeof defaultMessage); 6 | const fs = workspace.fs; 7 | 8 | export async function loadI18n(context: ExtensionContext): Promise { 9 | if (env.language === 'zh-cn') { 10 | const uri = Uri.joinPath(context.extensionUri, '/i18n/i18n.zh-cn.json'); 11 | const data = await fs.readFile(uri); 12 | localMessage = JSON.parse(data.toString()); 13 | } 14 | } 15 | 16 | export function localize(key: MessageKey, ...args: string[]): string { 17 | let value: string; 18 | if (localMessage && Object.keys(localMessage).includes(key)) { 19 | value = localMessage[key]; 20 | } else { 21 | value = defaultMessage[key]; 22 | } 23 | for (const argidx in args) { 24 | value = value.replace(`{${argidx}}`, args[argidx]); 25 | } 26 | return value; 27 | } -------------------------------------------------------------------------------- /masm-tasm/src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { localize } from './i18n'; 3 | 4 | class Logger { 5 | outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel('masm-tasm'); 6 | log = console.log; 7 | warn = console.warn; 8 | error = console.error; 9 | localize = localize; 10 | 11 | channel(...vals: string[]) { 12 | for (const val of vals) { 13 | this.outputChannel.append(`\n${val.trim()}`); 14 | } 15 | this.outputChannel.append('\n'); 16 | return this.outputChannel; 17 | } 18 | } 19 | 20 | export const logger = new Logger(); -------------------------------------------------------------------------------- /masm-tasm/src/utils/util.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { URI, Utils } from 'vscode-uri'; 3 | 4 | const fs = vscode.workspace.fs; 5 | 6 | async function openDir(uri: URI, onFile: (file: URI) => Thenable) { 7 | const iterms = await fs.readDirectory(uri); 8 | for (const [name, type] of iterms) { 9 | const _uri = Utils.resolvePath(uri, name); 10 | if (type === vscode.FileType.File) { 11 | await onFile(_uri); 12 | } 13 | else if (type === vscode.FileType.Directory) { 14 | await openDir(_uri, onFile); 15 | } 16 | } 17 | } 18 | 19 | export async function* getFiles(dir: string | vscode.Uri): AsyncGenerator { 20 | const rootDir = typeof dir === 'string' ? vscode.Uri.file(dir) : dir; 21 | const dirents = await fs.readDirectory(rootDir); 22 | for (const dirent of dirents) { 23 | const res = vscode.Uri.joinPath(rootDir, dirent[0]); 24 | if (dirent[1] === vscode.FileType.Directory) { 25 | yield* getFiles(res); 26 | } else { 27 | yield res; 28 | } 29 | } 30 | } 31 | 32 | export const uriUtils = { 33 | ...Utils, 34 | filename: function (uri: URI) { 35 | return Utils.basename(uri).replace("." + Utils.extname(uri), ""); 36 | }, 37 | info: function (uri: URI) { 38 | return { 39 | file: uriUtils.basename(uri), 40 | dir: uriUtils.dirname(uri), 41 | ext: uriUtils.extname(uri), 42 | filename: uriUtils.filename(uri) 43 | }; 44 | } 45 | }; 46 | 47 | export async function emptyFolder(uri: URI) { 48 | await fs.createDirectory(uri); 49 | const callback = (file: URI) => fs.delete(file); 50 | await openDir(uri, callback); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /masm-tasm/src/web/ASM.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { emulist } from "../emulators/main"; 3 | import { ActionType } from "../utils/configuration"; 4 | import { activateManager } from "../ASM/manager"; 5 | import * as statusBar from '../ASM/statusBar'; 6 | 7 | export async function activate(context: vscode.ExtensionContext) { 8 | statusBar.activate(context); 9 | 10 | const execAction = activateManager(context, emulist); 11 | 12 | context.subscriptions.push( 13 | vscode.commands.registerCommand('masm-tasm.openEmulator', (uri: vscode.Uri) => execAction(ActionType.open, uri)), 14 | vscode.commands.registerCommand('masm-tasm.runASM', (uri: vscode.Uri) => execAction(ActionType.run, uri)), 15 | vscode.commands.registerCommand('masm-tasm.debugASM', (uri: vscode.Uri) => execAction(ActionType.debug, uri)) 16 | ); 17 | } -------------------------------------------------------------------------------- /masm-tasm/src/web/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { localize, loadI18n } from '../utils/i18n'; 3 | 4 | import * as lan from '../language/main'; 5 | import * as asm from './ASM'; 6 | 7 | export function activate(context: vscode.ExtensionContext): void { 8 | 9 | loadI18n(context); 10 | 11 | //provide programmaic language features like hover,references,outline(symbol) 12 | lan.activate(context); 13 | //provide run and debug features via DOS emulators 14 | asm.activate(context); 15 | 16 | console.log(localize("activate.hello", 'Congratulations, your extension "masm-tasm" is now active!')); 17 | } 18 | 19 | // this method is called when your extension is deactivated 20 | export function deactivate(): void { 21 | console.log('extension deactivated'); 22 | } 23 | -------------------------------------------------------------------------------- /masm-tasm/src/web/test/suite/ASM.test.ts: -------------------------------------------------------------------------------- 1 | import { singleFileTestSuite, workspaceTestSuite } from "../../../test/suite/ASM.test"; 2 | 3 | singleFileTestSuite; 4 | workspaceTestSuite; -------------------------------------------------------------------------------- /masm-tasm/src/web/test/suite/extsniosn.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Web Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); -------------------------------------------------------------------------------- /masm-tasm/src/web/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | // imports mocha for the browser, defining the `mocha` global. 2 | require('mocha/mocha'); 3 | 4 | export function run(): Promise { 5 | 6 | return new Promise((c, e) => { 7 | mocha.setup({ 8 | ui: 'tdd', 9 | reporter: undefined 10 | }); 11 | 12 | // bundles all files in the current directory matching `*.test` 13 | const importAll = (r: __WebpackModuleApi.RequireContext) => r.keys().forEach(r); 14 | importAll(require.context('.', true, /\.test$/)); 15 | 16 | try { 17 | // Run the mocha test 18 | mocha.run(failures => { 19 | if (failures > 0) { 20 | e(new Error(`${failures} tests failed.`)); 21 | } else { 22 | c(); 23 | } 24 | }); 25 | } catch (err) { 26 | console.error(err); 27 | e(err); 28 | } 29 | }); 30 | } -------------------------------------------------------------------------------- /masm-tasm/src/web/test/suite/workspace.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as assert from 'assert'; 3 | 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | import * as vscode from 'vscode'; 7 | import { AsmResult } from '../../../ASM/manager'; 8 | import { DosEmulatorType, Assembler } from '../../../utils/configuration'; 9 | 10 | // import * as myExtension from '../../extension'; 11 | 12 | const folders = vscode.workspace.workspaceFolders; 13 | if (folders === undefined) { 14 | throw new Error(); 15 | } 16 | const samplesUri = folders[0].uri; 17 | 18 | suite('workspace mode Test Suite', function () { 19 | vscode.window.showInformationMessage('Start all tests.'); 20 | const MASMorTASM = [ 21 | 'MASM-v5.00', 22 | 'MASM-v6.11', 23 | 'TASM', 24 | ]; 25 | const emulator: DosEmulatorType[] = [ 26 | // DosEmulatorType.dosbox, 27 | // DosEmulatorType.dosboxX, 28 | DosEmulatorType.jsdos, 29 | ]; 30 | 31 | const filelist: [string, number][] = [ 32 | ['1.asm', 0], 33 | // ['3中文路径hasError.asm', 1], 34 | ['multi/2.asm', 0], 35 | ]; 36 | 37 | const args: [string, number, DosEmulatorType, Assembler][] = []; 38 | for (const file of filelist) { 39 | for (const emu of emulator) { 40 | for (const asm of MASMorTASM) { 41 | args.push([file[0], file[1], emu, asm]); 42 | } 43 | } 44 | } 45 | 46 | this.beforeEach(async function () { 47 | await vscode.commands.executeCommand('workbench.action.closeAllEditors'); 48 | }); 49 | 50 | shuffle(args).forEach((val) => { testAsmCode(...val); }); 51 | }); 52 | 53 | function testAsmCode(file: string, shouldErr: number, emu: DosEmulatorType, asm: Assembler): void { 54 | test(`test file ${file} in ${emu} use ${asm} want should ${shouldErr} error`, 55 | async function () { 56 | this.timeout('60s'); 57 | this.slow('20s'); 58 | 59 | //open test file. NOTE: the extension will be activated when open .asm file 60 | const samplefile = vscode.Uri.joinPath(samplesUri, file); 61 | 62 | //update settings 63 | await vscode.workspace.getConfiguration('masmtasm').update("dosbox.run", "exit"); 64 | await vscode.workspace.getConfiguration('masmtasm').update("ASM.mode", "workspace"); 65 | await vscode.workspace.getConfiguration('masmtasm').update("ASM.emulator", emu); 66 | await vscode.workspace.getConfiguration('masmtasm').update("ASM.assembler", asm); 67 | 68 | //assert the extension activated and command contributed 69 | const vscodecmds = await vscode.commands.getCommands(true); 70 | const cmd = 'masm-tasm.runASM'; 71 | if (!vscodecmds.includes(cmd)) { 72 | await vscode.extensions.getExtension('xsro.masm-tasm')?.activate(); 73 | } 74 | const vscodecmds2 = await vscode.commands.getCommands(true); 75 | assert.ok(vscodecmds2.includes(cmd)); 76 | 77 | //assert message processed 78 | const _result = await vscode.commands.executeCommand(cmd, samplefile); 79 | const { message, error } = _result as AsmResult; 80 | assert.strictEqual(error, shouldErr, message); 81 | }); 82 | } 83 | 84 | function shuffle(arr: T[]): T[] { 85 | for (let i = 1; i < arr.length; i++) { 86 | const random = Math.floor(Math.random() * (i + 1)); 87 | [arr[i], arr[random]] = [arr[random], arr[i]]; 88 | } 89 | return arr; 90 | } -------------------------------------------------------------------------------- /masm-tasm/syntaxes/README.md: -------------------------------------------------------------------------------- 1 | # syntaxes 2 | 3 | File provides [syntax-highlight](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide) for assembly language in VSCode.VS Code's tokenization engine is powered by [TextMate grammars](https://macromates.com/manual/en/language_grammars). 4 | 5 | ## assembly language references 6 | 7 | - [Microsoft Macro Assembler reference](https://docs.microsoft.com/cpp/assembler/masm/microsoft-macro-assembler-reference) 8 | - [x86 and amd64 instruction reference](https://www.felixcloutier.com/x86/) 9 | -------------------------------------------------------------------------------- /masm-tasm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "outDir": "dist", 6 | "lib": [ 7 | "DOM", 8 | "ES2017" 9 | ], 10 | "sourceMap": true, 11 | "resolveJsonModule": true, 12 | "rootDir": "src", 13 | "strict": true /* enable all strict type-checking options */ 14 | /* Additional Checks */ 15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test-web" 22 | ] 23 | } -------------------------------------------------------------------------------- /react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # js-dos dist files 26 | public/emulators*/ 27 | # js-dos bundles 28 | public/bundles/ -------------------------------------------------------------------------------- /react-app/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } -------------------------------------------------------------------------------- /react-app/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "start", 7 | "problemMatcher": [ 8 | "$ts-webpack-watch" 9 | ], 10 | "label": "npm: start", 11 | "detail": "react-scripts start", 12 | "isBackground": true 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /react-app/README.md: -------------------------------------------------------------------------------- 1 | # Dosrun Playground 2 | 3 | Powered by [js-dos](https://js-dos.com), this is a simply react web app for developing in DOS environment 4 | 5 | Thanks to [caiiiycuk](https://github.com/caiiiycuk/) and the Community. see [doc](https://dosasm.github.io/docs/tutorial-playGround/playGround) 6 | 7 | --- 8 | 9 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 10 | 11 | ## Available Scripts 12 | 13 | In the project directory, you can run: 14 | 15 | ### `pnpm start` 16 | 17 | Runs the app in the development mode.\ 18 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 19 | 20 | The page will reload if you make edits.\ 21 | You will also see any lint errors in the console. 22 | 23 | ### `pnpm test` 24 | 25 | Launches the test runner in the interactive watch mode.\ 26 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 27 | 28 | ### `pnpm build` 29 | 30 | Builds the app for production to the `build` folder.\ 31 | It correctly bundles React in production mode and optimizes the build for the best performance. 32 | 33 | The build is minified and the filenames include the hashes.\ 34 | Your app is ready to be deployed! 35 | 36 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 37 | 38 | ### `pnpm eject` 39 | 40 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 41 | 42 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 43 | 44 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 45 | 46 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 47 | 48 | ## Learn More 49 | 50 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 51 | 52 | To learn React, check out the [React documentation](https://reactjs.org/). 53 | -------------------------------------------------------------------------------- /react-app/develop.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { profiles } = require('./src/bundle.config.json'); 4 | 5 | if (process.argv.includes('copy') || process.argv.includes('copy-emulators')) { 6 | copyDir( 7 | path.resolve(__dirname, 'node_modules', 'emulators', 'dist'), 8 | path.resolve(__dirname, 'public', 'emulators') 9 | ); 10 | copyDir( 11 | path.resolve(__dirname, 'node_modules', 'emulators-ui', 'dist'), 12 | path.resolve(__dirname, 'public', 'emulators-ui') 13 | ); 14 | } 15 | 16 | if (process.argv.includes('copy') || process.argv.includes('copy-bundles')) { 17 | const srcDir = path.resolve(__dirname, '../', 'bundles'); 18 | const dstDir = path.resolve(__dirname, 'public', 'bundles'); 19 | if (!fs.existsSync(dstDir)) { 20 | fs.mkdirSync(dstDir) 21 | } 22 | for (const p of profiles) { 23 | const filename = path.basename(p.baseBundle); 24 | fs.copyFileSync( 25 | path.resolve(srcDir, filename), 26 | path.resolve(dstDir, filename) 27 | ) 28 | } 29 | } 30 | 31 | function copyDir(src, dst) { 32 | if (!fs.existsSync(dst)) { 33 | fs.mkdirSync(dst, { recursive: true }); 34 | } 35 | const iterms = fs.readdirSync(src); 36 | for (const i of iterms) { 37 | const file = path.resolve(src, i); 38 | const s = fs.statSync(file); 39 | if (s.isFile()) { 40 | const filedst = path.resolve(dst, i); 41 | fs.copyFileSync(file, filedst); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /react-app/gitee.package.js: -------------------------------------------------------------------------------- 1 | const pack=require('./package.json'); 2 | const fs=require('fs'); 3 | 4 | pack.homepage="https://dosasm.gitee.io/react-app/"; 5 | fs.writeFileSync('./package.json',JSON.stringify(pack),{encoding:'utf-8'}) 6 | 7 | -------------------------------------------------------------------------------- /react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://dosasm.github.io/dosrun", 6 | "dependencies": { 7 | "@material-ui/core": "^4.12.3", 8 | "@material-ui/icons": "^4.11.2", 9 | "@testing-library/jest-dom": "^5.11.4", 10 | "@testing-library/react": "^11.1.0", 11 | "@testing-library/user-event": "^12.1.10", 12 | "@types/jest": "^26.0.15", 13 | "@types/node": "^12.0.0", 14 | "@types/react": "^17.0.0", 15 | "@types/react-dom": "^17.0.0", 16 | "@uiw/react-textarea-code-editor": "^1.3.1", 17 | "emulators": "^0.71.0", 18 | "emulators-ui": "^0.68.0", 19 | "jszip": "^3.7.1", 20 | "react": "^17.0.2", 21 | "react-dom": "^17.0.2", 22 | "react-scripts": "4.0.3", 23 | "typescript": "^4.1.2", 24 | "web-vitals": "^1.0.1" 25 | }, 26 | "scripts": { 27 | "start": "react-scripts start", 28 | "postinstall": "node develop copy", 29 | "build": "react-scripts build", 30 | "test": "react-scripts test", 31 | "eject": "react-scripts eject" 32 | }, 33 | "eslintConfig": { 34 | "extends": [ 35 | "react-app", 36 | "react-app/jest" 37 | ] 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/react-app/public/favicon.ico -------------------------------------------------------------------------------- /react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Playground - An x86 assembly playGround in JSDos environment 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 |
41 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/react-app/public/logo192.png -------------------------------------------------------------------------------- /react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/react-app/public/logo512.png -------------------------------------------------------------------------------- /react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | .ground-selectors { 41 | grid-area: selectors; 42 | } 43 | .ground-buttons { 44 | grid-area: buttons; 45 | } 46 | .control-buttons { 47 | float: right; 48 | } 49 | .ground-dosbox { 50 | grid-area: dosbox; 51 | } 52 | .ground-editor { 53 | grid-area: editor; 54 | overflow: auto; 55 | height: 80vh; 56 | } 57 | 58 | .popup-stdout { 59 | font-family: monospace; 60 | overflow: visible; 61 | } 62 | 63 | @media only screen and (orientation: portrait) { 64 | .ground-Container { 65 | display: grid; 66 | grid-template-areas: 67 | "selectors buttons" 68 | "dosbox dosbox" 69 | "editor editor"; 70 | } 71 | } 72 | 73 | @media only screen and (orientation: landscape) { 74 | .ground-Container { 75 | display: grid; 76 | grid-template-areas: 77 | "selectors buttons" 78 | "dosbox editor"; 79 | grid-template-columns: 50% auto; 80 | grid-gap: 1%; 81 | padding: 10px; 82 | } 83 | } 84 | 85 | footer { 86 | text-align: center; 87 | width: 100%; 88 | } 89 | -------------------------------------------------------------------------------- /react-app/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /react-app/src/bundle.ts: -------------------------------------------------------------------------------- 1 | import JSZip from "jszip"; 2 | import { homepage } from '../package.json' 3 | 4 | export class BundleZip { 5 | public zip: JSZip = new JSZip(); 6 | constructor( 7 | ) { 8 | this.zip = new JSZip(); 9 | this.zip.file('.jsdos/dosbox.conf', '[AUTOEXEC]') 10 | } 11 | async download(bundleUrl: string) { 12 | const a = new URL(homepage); 13 | const response = await fetch(a.pathname + bundleUrl); 14 | if (response.status === 200 || response.status === 0) { 15 | const blob = await response.blob(); 16 | this.zip = await JSZip.loadAsync(blob); 17 | return this.zip 18 | } 19 | } 20 | async getBundle(autoexec?: string[], file?: { path: string, text: string }) { 21 | if (this.zip) { 22 | const zip = this.zip; 23 | if (autoexec) 24 | zip.file('.jsdos/dosbox.conf', '[AUTOEXEC]\n' + autoexec.join('\n')); 25 | if (file) { 26 | // eslint-disable-next-line no-control-regex 27 | const text = file.text.replace(/[^\x00-\x7F]/g, "")//only keep ASCII charactors in file 28 | .replace(/\r/g, "").replace(/\n/g, '\r\n')//use crlf (\r\n) as eol 29 | zip.file(file.path, text); 30 | } 31 | const bundle = await zip.generateAsync({ type: "uint8array" }); 32 | return bundle; 33 | } 34 | } 35 | async readFile(path: string) { 36 | if (this.zip) { 37 | return this.zip.file(path)?.async('string'); 38 | } 39 | } 40 | } 41 | 42 | /**run jsdos and get the ci for render */ 43 | export async function loadBundle(baseBundle: string): Promise { 44 | //https://stuk.github.io/jszip/documentation/examples/get-binary-files-ajax.html 45 | const response = await fetch(baseBundle); 46 | if (response.status === 200 || response.status === 0) { 47 | const blob = await response.blob(); 48 | const zip = await JSZip.loadAsync(blob); 49 | return zip 50 | // zip.file('.jsdos/dosbox.conf', '[AUTOEXEC]\n' + autoexec.join('\n')); 51 | // const bundle = await zip.generateAsync({ type: "uint8array" }); 52 | // const ci = emulators.dosboxWorker(bundle); 53 | // return ci; 54 | } 55 | throw new Error(); 56 | } -------------------------------------------------------------------------------- /react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /react-app/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /react-app/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-app/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /react-app/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /react-app/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /react-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } -------------------------------------------------------------------------------- /vscode-dosbox/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/naming-convention": "warn", 11 | "@typescript-eslint/semi": "warn", 12 | "curly": "warn", 13 | "eqeqeq": "warn", 14 | "no-throw-literal": "warn", 15 | "semi": "off" 16 | }, 17 | "extends": ["prettier"], 18 | "ignorePatterns": ["**/*.d.ts", "src/emulators"] 19 | } 20 | -------------------------------------------------------------------------------- /vscode-dosbox/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | *node_modules 4 | .vscode-test-web/ 5 | .vscode-test/ 6 | .emulators 7 | *.vsix 8 | build 9 | /.vscodeignore 10 | 11 | **/.DS_store 12 | -------------------------------------------------------------------------------- /vscode-dosbox/.prettierignore: -------------------------------------------------------------------------------- 1 | .*/ 2 | out/ 3 | dist/ 4 | emu/ 5 | src/emulators/ -------------------------------------------------------------------------------- /vscode-dosbox/.vsce/.vscodeignore: -------------------------------------------------------------------------------- 1 | .*/** 2 | .*ignore 3 | src/** 4 | out/** 5 | node_modules/** 6 | .gitmodules 7 | vsc-extension-quickstart.md 8 | README.*.md 9 | webpack.config.js 10 | .yarnrc 11 | **/tsconfig.json 12 | **/.eslintrc.json 13 | **/*.map 14 | **/*.ts 15 | **/.DS_store 16 | 17 | .github/ 18 | 19 | **/test/ 20 | 21 | dev/ 22 | 23 | -------------------------------------------------------------------------------- /vscode-dosbox/.vsce/darwin.vscodeignore: -------------------------------------------------------------------------------- 1 | emu 2 | !emu/**/*.conf -------------------------------------------------------------------------------- /vscode-dosbox/.vsce/linux.vscodeignore: -------------------------------------------------------------------------------- 1 | emu 2 | !emu/**/*.conf -------------------------------------------------------------------------------- /vscode-dosbox/.vsce/web.vscodeignore: -------------------------------------------------------------------------------- 1 | emu 2 | !emu/dosbox/dosbox-0.74.conf -------------------------------------------------------------------------------- /vscode-dosbox/.vsce/win32.vscodeignore: -------------------------------------------------------------------------------- 1 | emu 2 | !emu/dosbox 3 | !emu/**/*.conf -------------------------------------------------------------------------------- /vscode-dosbox/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | ] 8 | } -------------------------------------------------------------------------------- /vscode-dosbox/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/dist/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Run Web Extension ", 23 | "type": "pwa-extensionHost", 24 | "debugWebWorkerHost": true, 25 | "request": "launch", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionDevelopmentKind=web" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/dist/**/*.js" 32 | ], 33 | "preLaunchTask": "npm: watch" 34 | }, 35 | { 36 | "name": "Extension Tests", 37 | "type": "extensionHost", 38 | "request": "launch", 39 | "runtimeExecutable": "${execPath}", 40 | "args": [ 41 | "--disable-extensions", 42 | "--extensionDevelopmentPath=${workspaceFolder}", 43 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 44 | ], 45 | "outFiles": [ 46 | "${workspaceFolder}/out/**/*.js" 47 | ], 48 | "preLaunchTask": "npm: compile-tsc" 49 | }, 50 | { 51 | "name": "Extension Tests web", 52 | "type": "extensionHost", 53 | "debugWebWorkerHost": true, 54 | "request": "launch", 55 | "args": [ 56 | "--extensionDevelopmentPath=${workspaceFolder}", 57 | "--extensionDevelopmentKind=web", 58 | "--extensionTestsPath=${workspaceFolder}/dist/web/test/suite/index" 59 | ], 60 | "outFiles": [ 61 | "${workspaceFolder}/dist/web/**/*.js" 62 | ], 63 | "preLaunchTask": "npm: watch" 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /vscode-dosbox/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false, // set this to true to hide the "out" folder with the compiled JS files 5 | ".vscode-test-web": true 6 | }, 7 | "search.exclude": { 8 | "out": true // set this to false to include "out" folder in search results 9 | }, 10 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 11 | "typescript.tsc.autoDetect": "off", 12 | "editor.defaultFormatter": "esbenp.prettier-vscode", 13 | //The auto format may cause some problem 14 | "editor.formatOnSave": false, 15 | } -------------------------------------------------------------------------------- /vscode-dosbox/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "compile", 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | }, 13 | "problemMatcher": [ 14 | "$ts-webpack", 15 | "$tslint-webpack" 16 | ] 17 | }, 18 | { 19 | "type": "npm", 20 | "script": "watch", 21 | "group": "build", 22 | "isBackground": true, 23 | // "runOptions": { 24 | // "runOn": "folderOpen" 25 | // }, 26 | "problemMatcher": [ 27 | "$ts-webpack-watch", 28 | "$tslint-webpack-watch" 29 | ] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /vscode-dosbox/.vscodeignore: -------------------------------------------------------------------------------- 1 | .*/** 2 | .*ignore 3 | src/** 4 | out/** 5 | node_modules/** 6 | .gitmodules 7 | vsc-extension-quickstart.md 8 | README.*.md 9 | webpack.config.js 10 | .yarnrc 11 | **/tsconfig.json 12 | **/.eslintrc.json 13 | **/*.map 14 | **/*.ts 15 | **/.DS_store 16 | 17 | .github/ 18 | 19 | **/test/ 20 | 21 | dev/ 22 | 23 | 24 | emu 25 | !emu/**/*.conf -------------------------------------------------------------------------------- /vscode-dosbox/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## preview 4 | 5 | - 0.0.1: offer basic commands 6 | -------------------------------------------------------------------------------- /vscode-dosbox/README.md: -------------------------------------------------------------------------------- 1 | # vscode-DOSBox 2 | 3 | Offer an Interface for running DOSBox and its variants in your platform. 4 | 5 | ## supported DOSBox and platforms 6 | 7 | - [JSDos](https://js-dos.com/): all platform including web 8 | - [DOSBox](https://www.dosbox.com/): binaries packaged for windows system, need to install manually for other OS 9 | - [DOSBox-x](https://dosbox-x.com/): binaries packaged for windows system, need to install manually for other OS 10 | - [MSDos-player](http://takeda-toshiya.my.coocan.jp/msdos/index.html): only runs in windows system and binaries packaged 11 | 12 | ## Features 13 | 14 | - offer commands for open supported platform 15 | - offer API for other extensions: see [api.ts](src/api.ts) 16 | - (TODO) use this extension for DOS game playing 17 | 18 | ## Dependency Installation 19 | 20 | ### for Windows 21 | 22 | All binary files for windows system haven been packaged in the extension, 23 | 24 | ### Install DOSBox and DOSBox-x for macOS via homebrew (recommend) 25 | 26 | ```sh 27 | brew install dosbox 28 | brew install dosbox-x 29 | ``` 30 | 31 | ### Install DOSBox for macOS via packages 32 | 33 | 1. download DMG file from dosbox's [website](https://www.dosbox.com/download.php?main=1) 34 | 2. click to open the dmg file 35 | 3. drag the application to `/Applications` folder 36 | 4. set the following settings in your VSCode's setting 37 | 38 | ```json 39 | "vscode-dosbox.command.dosbox":"open -a dosbox --args", 40 | ``` 41 | 42 | ### Install DOSBox-x for macOS via packages 43 | 44 | 1. download ZIP file for macOS from dosbox-x's [website](https://dosbox-x.com) 45 | 2. Unzip the file and drag your `.app` file to `/Applications` folder 46 | 3. follow this [macOS's doc](https://support.apple.com/en-us/HT202491) to set up security settings 47 | 1. Go to Security & Privacy 48 | 2. Click the Open Anyway button in the General pane to confirm your intent to open or install the app 49 | 4. set the following setting in your VSCode's settings 50 | 51 | ```json 52 | "vscode-dosbox.command.dosboxX":"open -a dosbox-x --args", 53 | ``` 54 | 55 | ### Install DOSBox for Linux 56 | 57 | for example, 58 | 59 | ```sh 60 | sudo apt install dosbox 61 | ``` 62 | 63 | For more, see [DOSBox's website](https://www.dosbox.com/download.php?main=1) 64 | 65 | ### Install DOSBox-x for Linux 66 | 67 | according to [dosbox-x's instructions](https://github.com/joncampbell123/dosbox-x/blob/master/INSTALL.md#linux-packages-flatpak-and-more), 68 | we can use [flatpak](https://www.flatpak.org/setup/) to install DOSBox-X. 69 | 70 | ```sh 71 | # install flatpak 72 | sudo apt install flatpak 73 | # use flatpak to install DOSBox-X 74 | flatpak install flathub com.dosbox_x.DOSBox-X 75 | ``` 76 | 77 | then set the following setting in your VSCode's settings 78 | 79 | ```json 80 | "vscode-dosbox.command.dosboxX":"flatpak run com.dosbox_x.DOSBox-X", 81 | ``` 82 | 83 | you may need to change output of SDL for better screen effect 84 | 85 | ```json 86 | "vscode-dosbox.dosbox.config": { 87 | "SDL.output":"overlay", 88 | } 89 | ``` 90 | 91 | ## Extension Settings 92 | 93 | This extension contributes the following settings: 94 | 95 | - `vscode-dosbox.command.dosbox`: customize your command to open dosbox 96 | - `vscode-dosbox.command.dosboxX`: customize your command to open dosbox-x 97 | 98 | ## Report 99 | 100 | report bugs in [github issues](https://github.com/dosasm/vscode-dosbox/issues) 101 | 102 | ## Known Issues 103 | 104 | Calling out known issues can help limit users opening duplicate issues against your extension. 105 | 106 | ## Release Notes 107 | -------------------------------------------------------------------------------- /vscode-dosbox/README.zh.md: -------------------------------------------------------------------------------- 1 | # vscode-DOSBox 2 | 3 | 提供一个在 VSCode 中运行 DOSBox 及其变体的接口 4 | 5 | ## 支持的 DOSBox 和 VSCode 平台 6 | 7 | - [JSDos](https://js-dos.com/): 支持包括 web 端的所有平台 8 | - [DOSBox](https://www.dosbox.com/): windows 系统下打包了可执行文件,linux 和 macOS 系统需要手动安装 9 | - [DOSBox-x](https://dosbox-x.com/): windows 系统下打包了可执行文件,linux 和 macOS 系统需要手动安装 10 | - [MSDos-player](http://takeda-toshiya.my.coocan.jp/msdos/index.html): windows 系统下打包了可执行文件,其他平台无相应程序 11 | 12 | ## 功能 13 | 14 | - 提供打开相关 DOS 模拟器的简单接口,参看 [api.ts](src/api.ts) 15 | - 提供打开相关 DOS 模拟器的简单命令,可以在命令面板测试与调用 16 | 17 | ## 安装依赖 18 | 19 | ### Windows 系统用户 20 | 21 | 所有的可执行文件都已经打包在安装包中。可以通过修改设置来使用不同的模拟器 22 | 23 | ### 在 mac 平台使用 homebrew 安装 DOSBox 和 DOSBox-x (推荐) 24 | 25 | ```sh 26 | brew install dosbox 27 | brew install dosbox-x 28 | ``` 29 | 30 | [homebrew](https://brew.sh/)官网有安装 homebrew 的详细教程,[tuna](https://mirrors.tuna.tsinghua.edu.cn/help/homebrew/)也提供了镜像安装和换源方法。 31 | 32 | ### 在 mac 平台从 dmg 文件安装 DOSBox 33 | 34 | 1. 从 DOSBox 的[官网](https://www.dosbox.com/download.php?main=1)下载 DMG 文件 35 | 2. 点击 dmg 文件 36 | 3. 将程序拖拽到`/Applications`文件夹 37 | 4. 在 VSCode 中添加如下设置,配置用于打开 DOSBox 的命令 38 | 39 | ```json 40 | "vscode-dosbox.command.dosbox":"open -a dosbox --args", 41 | ``` 42 | 43 | 可能需要修改 sdl 输出方式,来使显示效果满意,例如: 44 | 45 | ```json 46 | "vscode-dosbox.dosbox.config": { 47 | "SDL.output":"overlay", 48 | } 49 | ``` 50 | 51 | ### 在 mac 平台从 zip 文件安装 DOSBox-x 52 | 53 | 1. 从 dosbox-x 的 [官网](https://dosbox-x.com) 下载 zip 压缩包文件 54 | 2. 解压并将需要的话`.app`文件拖拽到 `/Applications`文件夹 55 | 3. 按照[macOS 的文档](https://support.apple.com/zh-cn/HT202491)设置安全选项 56 | 1. 在“系统偏好设置”中,前往“安全性与隐私”。 57 | 1. 点按“通用”面板中的“仍要打开”按钮,以确认您打算打开或安装这个 App。 58 | 4. 在 VSCode 中添加如下设置,配置用于打开 DOSBox 的命令 59 | 60 | ```json 61 | "vscode-dosbox.command.dosboxX":"open -a dosbox-x --args", 62 | ``` 63 | 64 | ### 在 Linux 平台安装 dosbox 65 | 66 | Ubuntu 等可以使用 apt 的发行版可以使用如下命令安装 67 | 68 | ```sh 69 | sudo apt install dosbox 70 | ``` 71 | 72 | 参看[DOSBox's website](https://www.dosbox.com/download.php?main=1) 73 | 74 | ### 在 Linux 平台安装 dosbox-x 75 | 76 | 根据[dosbox-x's instructions](https://github.com/joncampbell123/dosbox-x/blob/master/INSTALL.md#linux-packages-flatpak-and-more), 77 | 我们可以使用[flatpak](https://www.flatpak.org/setup/)来安装 DOSBox-X. 78 | 79 | ```sh 80 | # install flatpak 81 | sudo apt install flatpak 82 | # use flatpak to install DOSBox-X 83 | flatpak install flathub com.dosbox_x.DOSBox-X 84 | ``` 85 | 86 | 最后在 VSCode 的设置中添加如下内容 87 | 88 | ```json 89 | "vscode-dosbox.command.dosboxX":"flatpak run com.dosbox_x.DOSBox-X", 90 | ``` 91 | 92 | ## 插件设置 93 | 94 | 该插件主要包括如下设置: 95 | 96 | - `vscode-dosbox.command.dosbox`: 自定义打开 dosbox 的命令 97 | - `vscode-dosbox.command.dosboxX`: 自定义打开 dosbox-x 的命令 98 | 99 | ## jsdos 兼容情况 100 | 101 | | environment | platform | dosboxWorker | dosboxDirect | 102 | | ----------------- | -------- | ------------ | ------------ | 103 | | extensionhost | nodejs | ❌ | ✅ | 104 | | webview | browser | ✅ | ✅ | 105 | | web extensionhost | Worker | ✅ | ❌ | 106 | | web webview | browser | ✅ | ✅ | 107 | 108 | ## 报告问题 109 | 110 | 在[github issues](https://github.com/dosasm/vscode-dosbox/issues)中,提交遇到的问题。 111 | 112 | ## Release Notes 113 | -------------------------------------------------------------------------------- /vscode-dosbox/dev/package.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const pak = require("../package.json"); 3 | const path = require("path"); 4 | const os = require("os"); 5 | const cp = require("child_process"); 6 | 7 | const projectDir = path.resolve(__dirname, ".."); 8 | 9 | const validTargets = [ 10 | "win32-x64", 11 | "win32-ia32", 12 | "win32-arm64", 13 | "linux-x64", 14 | "linux-arm64", 15 | "linux-armhf", 16 | "darwin-x64", 17 | "darwin-arm64", 18 | "alpine-x64", 19 | "alpine-arm64", 20 | "web", 21 | ]; 22 | 23 | function generateIgnore(platform, arch) { 24 | const ignorePath = path.resolve(projectDir, ".vscodeignore"); 25 | const baseContent = fs.readFileSync( 26 | path.resolve(projectDir, ".vsce/.vscodeignore"), 27 | "utf-8" 28 | ); 29 | const platformContent = fs.readFileSync( 30 | path.resolve(projectDir, `.vsce/${platform}.vscodeignore`), 31 | "utf-8" 32 | ); 33 | fs.writeFileSync( 34 | ignorePath, 35 | baseContent + "\n" + platformContent.replace(/\{arch\}/g, arch) 36 | ); 37 | } 38 | 39 | async function package(platform, arch, folder = "build") { 40 | const target = platform + (arch ? "-" + arch : ""); 41 | generateIgnore(platform, arch); 42 | 43 | !fs.existsSync(folder) && fs.mkdirSync(folder); 44 | 45 | const packagePath = `${folder}/${pak.name}-${target}-${pak.version}.vsix`; 46 | const run = (command) => cp.execSync(command, { stdio: "inherit" }); 47 | run( 48 | `pnpm vsce package --no-dependencies --target ${target} --out ${packagePath}` 49 | ); 50 | 51 | console.log( 52 | ` 53 | start creating package in platform:${process.platform},arch:${process.arch} 54 | package path: ${packagePath} 55 | `, 56 | { target, platform, arch } 57 | ); 58 | } 59 | 60 | async function main() { 61 | if (process.argv.includes("--all")) { 62 | for (const target of validTargets) { 63 | [platform, arch] = target.split("-"); 64 | await package(platform, arch); 65 | } 66 | } else if (process.argv.includes("--target")) { 67 | const targetIdx = process.argv.findIndex((val) => val.includes("--target")); 68 | if (targetIdx >= 0) { 69 | target = process.argv[targetIdx + 1].startsWith("web") 70 | ? "web" 71 | : process.argv[targetIdx + 1]; 72 | [platform, arch] = target.split("-"); 73 | await package(platform, arch); 74 | } 75 | } else { 76 | let { platform, arch } = process; 77 | await package(platform, arch); 78 | } 79 | } 80 | 81 | main(); 82 | -------------------------------------------------------------------------------- /vscode-dosbox/dev/postinstall.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { resolve } = require("path"); 3 | 4 | function copydir(dir, dst) { 5 | for (const file of fs.readdirSync(dir)) { 6 | const src = resolve(dir, file); 7 | const s = fs.statSync(src); 8 | if (s.isFile()) { 9 | fs.cpSync(src, resolve(dst, file)); 10 | } 11 | } 12 | } 13 | 14 | const mkdirSync = (dir) => !fs.existsSync(dir) && fs.mkdirSync(dir,{recursive:true}); 15 | mkdirSync("dist/emulators/"); 16 | mkdirSync("dist/emulators-ui/"); 17 | copydir("node_modules/emulators/dist", "dist/emulators/"); 18 | copydir("node_modules/emulators-ui/dist", "dist/emulators-ui/"); 19 | -------------------------------------------------------------------------------- /vscode-dosbox/dev/update-emulators.sh: -------------------------------------------------------------------------------- 1 | persist_list=( 2 | "http.ts" 3 | "dos/dosbox/ts/worker.ts" 4 | "impl/modules.ts" 5 | ) 6 | version=v0.73.7 7 | 8 | # mkdir -p .emulators 9 | # curl -fsSL https://github.com/js-dos/emulators/archive/refs/tags/$version.zip \ 10 | # -x 192.168.1.6:7890\ 11 | # -o .emulators/$version.zip 12 | # 7z x .emulators/$version.zip -o.emulators/ 13 | 14 | folder=.emulators/emulators-${version:1:10}/src/ 15 | cd $folder 16 | for file in `find . -name "*.ts"` 17 | do 18 | contain=no 19 | for persist in ${persist_list[@]} 20 | do 21 | if [ "$file" == "./$persist" ] 22 | then contain=yes 23 | fi 24 | done 25 | if [ "$contain" == "yes" ] 26 | then dst=../../../src/emulators/${file/.ts}.origin.ts 27 | else dst=../../../src/emulators/$file 28 | fi 29 | echo copy from $file to $dst 30 | cp $file $dst 31 | done 32 | cd - -------------------------------------------------------------------------------- /vscode-dosbox/src/README.md: -------------------------------------------------------------------------------- 1 | # SOURCE CODE 2 | 3 | The code can be complicated for codes are designed for run in following environment 4 | 5 | - `global`: nodejs vscode extension host, all codes 6 | - `window`: webview script, the `emulators` folder 7 | - `self`: webworker vscode extension host, jsdos related codes including `emulators`,`jsdos` 8 | 9 | The code in [emulators/](emulators/) are copied from for modification to run in VSCode. 10 | 11 | Thanks to [caiiiycuk](https://github.com/caiiiycuk). 12 | -------------------------------------------------------------------------------- /vscode-dosbox/src/dosbox/conf.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Manage DOSBox's configuration file 3 | */ 4 | export class Conf { 5 | private _target: string[] = []; 6 | private eol = "\n"; 7 | constructor(confStr: string = "") { 8 | if (confStr.includes("\r\n")) { 9 | this.eol = "\r\n"; 10 | } 11 | this._target = confStr.split(this.eol); 12 | if (!Array.isArray(this._target)) { 13 | throw new Error("error target"); 14 | } 15 | } 16 | 17 | /**check whether the config file has the item or not 18 | * @returns line number of the section or key 19 | */ 20 | has(section: string, key?: string): number | undefined { 21 | let sectionIdx = this._target.findIndex((val) => { 22 | return val.trim().toLowerCase() === `[${section.trim().toLowerCase()}]`; 23 | }); 24 | if (key === undefined && sectionIdx >= 0) { 25 | return sectionIdx; 26 | } 27 | for (let i = sectionIdx + 1; i < this._target.length; i++) { 28 | const line = this._target[i]; 29 | if (typeof line !== "string") { 30 | continue; 31 | } 32 | if (line.trimLeft().startsWith("[")) { 33 | break; 34 | } 35 | const kv = line.replace(/#.*/g, ""); 36 | const [_key, _value] = kv.split("="); 37 | if (key && key.trim().toLowerCase() === _key.trim().toLowerCase()) { 38 | return i; 39 | } 40 | } 41 | return undefined; 42 | } 43 | 44 | get(section: string, key: string) { 45 | const idx = this.has(section, key); 46 | if (idx !== undefined) { 47 | const [name, value] = this._target[idx] 48 | .replace(/#.*/g, "") 49 | .trim() 50 | .split("="); 51 | if (value) { 52 | return value.trim(); 53 | } 54 | } 55 | } 56 | 57 | update(section: string, key: string, value: boolean | number | string) { 58 | let idx = this.has(section, key); 59 | if (idx !== undefined) { 60 | this._target[idx] = `${key} = ${value.toString()}`; 61 | return; 62 | } 63 | idx = this.has(section); 64 | if (idx !== undefined) { 65 | this._target.splice(idx + 1, 0, `${key}=${value}`); 66 | return; 67 | } else { 68 | this._target.push(`[${section}]`); 69 | this._target.push(`${key}=${value}`); 70 | return; 71 | } 72 | } 73 | 74 | updateAutoexec(context: string[]) { 75 | const section = "[autoexec]"; 76 | const idx = this._target.findIndex((val) => val === section); 77 | if (idx >= 0) { 78 | for (let i = idx + 1; i < this._target.length; i++) { 79 | if (!this._target[i].trim().startsWith("#")) { 80 | this._target[i] = "#" + this._target[i]; 81 | } 82 | if (this._target[i].trim().startsWith("[")) { 83 | break; 84 | } 85 | } 86 | this._target.splice(idx + 1, 0, ...context); 87 | } else { 88 | this._target.push("[autoexec]", ...context); 89 | } 90 | } 91 | toString() { 92 | return this._target.join(this.eol); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /vscode-dosbox/src/dosbox/dosbox.ts: -------------------------------------------------------------------------------- 1 | import * as cp from "child_process"; 2 | import * as vscode from "vscode"; 3 | import { logger } from "../util/logger"; 4 | import { Conf } from "./conf"; 5 | import * as api from "../api"; 6 | import { DosboxResult } from "../api"; 7 | import { fromBundle } from "./fromBundle"; 8 | 9 | const fs = vscode.workspace.fs; 10 | 11 | export class DOSBox implements api.Dosbox { 12 | private _conf: Conf = new Conf(""); 13 | constructor( 14 | public readonly command: string, 15 | public dstConfPath: vscode.Uri, 16 | public cwd?: string 17 | ) {} 18 | 19 | /** set the dosbox configuration file template 20 | * 21 | * @param template the path of the reference.conf file 22 | */ 23 | async setConf(template: vscode.Uri) { 24 | const text = await fs.readFile(template); 25 | this._conf = new Conf(text.toString()); 26 | } 27 | 28 | updateConf = ( 29 | section: string, 30 | key: string, 31 | value: string | number | boolean 32 | ) => this._conf.update(section, key, value); 33 | updateAutoexec = (context: string[]) => this._conf.updateAutoexec(context); 34 | 35 | async run(params?: string[], cpHandler?: (p: cp.ChildProcess) => void) { 36 | const text = new TextEncoder().encode(this._conf.toString()); 37 | await fs.writeFile(this.dstConfPath, text); 38 | const cmd = this.command; 39 | const parameter = Array.isArray(params) 40 | ? params.join(" ") 41 | : `-conf "${this.dstConfPath.fsPath}"`; 42 | const command = cmd.includes("") 43 | ? cmd.replace("", parameter) 44 | : cmd + " " + parameter; 45 | console.log(command); 46 | return new Promise((resolve, reject) => { 47 | const p = cp.exec(command, { cwd: this.cwd }, (error, stdout, stderr) => { 48 | if (error) { 49 | logger.error(JSON.stringify(error), p, this); 50 | vscode.window.showErrorMessage( 51 | "can't open dosbox with command: " + command + "cwd:" + this.cwd 52 | ); 53 | reject(error); 54 | } else { 55 | resolve({ stdout, stderr, exitCode: p.exitCode }); 56 | } 57 | }); 58 | if (cpHandler) { 59 | cpHandler(p); 60 | } 61 | }); 62 | } 63 | 64 | async fromBundle( 65 | bundle: Uint8Array, 66 | tempFolder: vscode.Uri, 67 | useBundleConf: boolean = false 68 | ): Promise { 69 | const confText = await fromBundle(bundle, tempFolder); 70 | if (useBundleConf) { 71 | this._conf = new Conf(confText); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vscode-dosbox/src/dosbox/fromBundle.ts: -------------------------------------------------------------------------------- 1 | import * as Jszip from "jszip"; 2 | import * as vscode from "vscode"; 3 | 4 | const fs = vscode.workspace.fs; 5 | 6 | /** 7 | * open dosbox-like emulator from bundle 8 | * 9 | * @param bundle bundle data 10 | * @param tempFolder the folder for exact all files to 11 | * @returns the dosbox.conf file in the jsdos bundle 12 | */ 13 | export async function fromBundle( 14 | bundle: Uint8Array, 15 | tempFolder: vscode.Uri 16 | ): Promise { 17 | const zip = new Jszip(); 18 | await zip.loadAsync(bundle); 19 | Object.keys(zip.files).forEach(async function (filename) { 20 | const e = zip.files[filename]; 21 | const data = await e.async("uint8array"); 22 | const dest = vscode.Uri.joinPath(tempFolder, filename); 23 | if (e.dir === false) { 24 | await fs.writeFile(dest, data); 25 | } 26 | }); 27 | const filename = ".jsdos/dosbox.conf"; 28 | if (zip.file(filename)) { 29 | const data = await zip.files[filename].async("string"); 30 | return data.replace(/mount\s+c\s+\./g, `mount c "${tempFolder.fsPath}"`); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/README.md: -------------------------------------------------------------------------------- 1 | # Emulators Typescript Glue Code 2 | 3 | These code are copied from for modification to run in VSCode. 4 | 5 | Thanks to [caiiiycuk](https://github.com/caiiiycuk). 6 | 7 | ## Important 8 | 9 | Turn OFF the autoformat setting when changing files in this folder. 10 | 11 | ## Main Changes 12 | 13 | - [modules.ts](impl/modules.ts): change method for load wasm module file 14 | - [emulators-impl.ts](impl/ci-impl.ts): expose API for: 15 | - take over [HTTPRequest](http.ts) for load wasm module 16 | - set path of the `wDosbox.js` -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/build.ts: -------------------------------------------------------------------------------- 1 | 2 | /* eslint-disable */ 3 | // Autogenerated 4 | // ------------- 5 | // gulpfile.ts/wasm.ts --> generateBuildInfo 6 | 7 | export const Build = { 8 | version: "0.73.7 (0142877a165198f5859777e8460a3249)", 9 | buildSeed: 1657511324990, 10 | "wdosbox.shared.wasm": { 11 | "size": 1477023, 12 | "gzSize": 506358 13 | }, 14 | "wdosbox.shared.js": { 15 | "size": 143644, 16 | "gzSize": 36441 17 | }, 18 | "wdosbox.wasm": { 19 | "size": 1464583, 20 | "gzSize": 502182 21 | }, 22 | "wdosbox.js": { 23 | "size": 124362, 24 | "gzSize": 32522 25 | }, 26 | "wlibzip.wasm": { 27 | "size": 112931, 28 | "gzSize": 54551 29 | }, 30 | "wlibzip.js": { 31 | "size": 76373, 32 | "gzSize": 19886 33 | } 34 | , 35 | }; 36 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/dos/bundle/dos-bundle.ts: -------------------------------------------------------------------------------- 1 | // # DosBundle 2 | // Is a complete bundle that contains everything needed to start dosbox server 3 | 4 | import { DosConfig, createDosConfig, toDosboxConf } from "./dos-conf"; 5 | import LibZip from "../../libzip/libzip"; 6 | 7 | import { httpRequest } from "../../http"; 8 | 9 | import { WasmModule } from "../../impl/modules"; 10 | 11 | // ### DosArchiveSource 12 | export interface DosArchiveSource { 13 | // source (archive) to download and extract via `extractAll` 14 | 15 | // **url** where archive is located 16 | url: string; 17 | 18 | // **path** 19 | path: string; 20 | 21 | // **type** 22 | type?: "zip"; 23 | // archive type, now only Zip is supported 24 | } 25 | 26 | export default class DosBundle { 27 | public config: DosConfig; 28 | public sources: DosArchiveSource[]; 29 | 30 | private libzipWasm: WasmModule; 31 | 32 | constructor(libzipWasm: WasmModule) { 33 | this.config = createDosConfig(); 34 | this.sources = []; 35 | this.libzipWasm = libzipWasm; 36 | } 37 | 38 | autoexec(...lines: string[]): DosBundle { 39 | this.config.autoexec.options.script.value = lines.join("\n"); 40 | return this; 41 | } 42 | 43 | cycles(cycles: string): DosBundle { 44 | this.config.cpu.options.cycles.value = cycles; 45 | return this; 46 | } 47 | 48 | // ### extract 49 | extract(url: string, path = "/", type: "zip" = "zip"): DosBundle { 50 | // simplified version of extractAll, works only for one archive. It calls extractAll inside. 51 | return this.extractAll([{ url, path, type }]); 52 | } 53 | 54 | // ### extractAll 55 | extractAll(sources: DosArchiveSource[]): DosBundle { 56 | // eslint-disable-next-line max-len 57 | // download given [`sources`](https://js-dos.com/6.22/docs/api/generate.html?page=jsdos-bundle#dosfs-dosarchivesource) 58 | // and extract them 59 | this.sources.push(...sources); 60 | return this; 61 | } 62 | 63 | async toUint8Array(overwriteConfig = false): Promise { 64 | const module = {}; 65 | await this.libzipWasm.instantiate(module); 66 | const libzip = new LibZip(module, "/home/web_user"); 67 | const conf = await toDosboxConf(this.config); 68 | 69 | const promises = []; 70 | for (const source of this.sources) { 71 | if (source.type !== "zip") { 72 | throw new Error("Only Zip is supported"); 73 | } 74 | 75 | const resource = httpRequest(source.url, { 76 | responseType: "arraybuffer", 77 | }).then((buffer: string | ArrayBuffer) => { 78 | return { 79 | source, 80 | data: new Uint8Array(buffer as ArrayBuffer), 81 | }; 82 | }); 83 | 84 | promises.push(resource); 85 | } 86 | 87 | if (!overwriteConfig) { 88 | await libzip.writeFile(".jsdos/dosbox.conf", conf); 89 | await libzip.writeFile(".jsdos/readme.txt", readmeTxt); 90 | await libzip.writeFile(".jsdos/jsdos.json", JSON.stringify(this.config, null, 2)); 91 | } 92 | 93 | const resources = await Promise.all(promises); 94 | for (const resource of resources) { 95 | libzip.zipToFs(resource.data, resource.source.path); 96 | } 97 | 98 | if (overwriteConfig) { 99 | await libzip.writeFile(".jsdos/dosbox.conf", conf); 100 | await libzip.writeFile(".jsdos/readme.txt", readmeTxt); 101 | await libzip.writeFile(".jsdos/jsdos.json", JSON.stringify(this.config, null, 2)); 102 | } 103 | 104 | const bundle = await libzip.zipFromFs(); 105 | libzip.destroy(); 106 | 107 | return bundle; 108 | } 109 | } 110 | 111 | 112 | const readmeTxt = ` 113 | Please visit our website: 114 | 115 | _ __ 116 | (_)____ ____/ /___ _____ _________ ____ ___ 117 | / / ___/_____/ __ / __ \\/ ___// ___/ __ \\/ __ \`__ \\ 118 | / (__ )_____/ /_/ / /_/ (__ )/ /__/ /_/ / / / / / / 119 | __/ /____/ \\__,_/\\____/____(_)___/\\____/_/ /_/ /_/ 120 | /___/ 121 | `; 122 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/dos/dosbox/ts/create-worker.ts: -------------------------------------------------------------------------------- 1 | import { isBrowser, isNode, isWebWorker, isJsDom, isDeno } from "browser-or-node"; 2 | 3 | export async function createWorker(workerUrl: string): Promise { 4 | let worker: Worker | undefined = undefined; 5 | 6 | //if runs in webview we need to use fetch API 7 | if (isBrowser) { 8 | const blob = await fetch(workerUrl) 9 | .then(result => result.blob()); 10 | const blobUrl = URL.createObjectURL(blob); 11 | worker = new Worker(blobUrl); 12 | console.log(`created a worker with script ${blobUrl} in browser`); 13 | } 14 | else if (isNode) { 15 | const {Worker}=eval('require("worker_threads")') 16 | worker = new Worker(workerUrl) as Worker; 17 | console.log(`created a worker with script ${workerUrl} in nodejs`); 18 | } 19 | else { 20 | worker = new Worker(workerUrl); 21 | console.log(`created a worker with script ${workerUrl}`); 22 | } 23 | return worker; 24 | } -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/dos/dosbox/ts/direct.ts: -------------------------------------------------------------------------------- 1 | import { WasmModule } from "../../../impl/modules"; 2 | import { TransportLayer, MessageHandler, ClientMessage, ServerMessage } from "../../../protocol/protocol"; 3 | import { MessagesQueue } from "../../../protocol/messages-queue"; 4 | 5 | export async function dosDirect(wasmModule: WasmModule, sessionId: string): Promise { 6 | const messagesQueue = new MessagesQueue(); 7 | let handler: MessageHandler = messagesQueue.handler.bind(messagesQueue); 8 | 9 | const module: any = {}; 10 | 11 | module.postMessage = (name: ServerMessage, props: {[key: string]: any}) => { 12 | handler(name, props); 13 | }; 14 | 15 | const sleepHandler = (e: MessageEvent) => { 16 | const data = e.data; 17 | if (data?.name === "ws-sync-sleep" && data.props.sessionId === sessionId) { 18 | postMessage({ name: "wc-sync-sleep", props: data.props }, "*"); 19 | } 20 | }; 21 | 22 | const transportLayer: TransportLayer = { 23 | sessionId, 24 | sendMessageToServer: (name: ClientMessage, props?: {[key: string]: any}) => { 25 | module.messageHandler({ data: { name, props } }); 26 | }, 27 | initMessageHandler: (newHandler: MessageHandler) => { 28 | handler = newHandler; 29 | messagesQueue.sendTo(handler); 30 | }, 31 | exit: () => { 32 | if (typeof window !== "undefined") { 33 | window.removeEventListener("message", sleepHandler); 34 | } 35 | }, 36 | }; 37 | 38 | (transportLayer as any).module = module; 39 | 40 | if (typeof window !== "undefined") { 41 | window.addEventListener("message", sleepHandler, { passive: true }); 42 | } 43 | 44 | await wasmModule.instantiate(module); 45 | module.callMain([sessionId]); 46 | 47 | return transportLayer; 48 | } 49 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/dos/dosbox/ts/worker-server.js: -------------------------------------------------------------------------------- 1 | var worker = typeof importScripts === "function"; 2 | 3 | if (worker) { 4 | onmessage = (e) => { 5 | const data = e.data; 6 | if (data === undefined) { 7 | return; 8 | } 9 | 10 | if (data.name === "wc-install") { 11 | const sessionId = data.props.sessionId; 12 | const module = {}; 13 | 14 | if (data.props.module !== undefined) { 15 | const wasmModule = data.props.module; 16 | const instantiateWasm = (info, receiveInstance) => { 17 | info.env = info.env || {}; 18 | WebAssembly.instantiate(wasmModule, info) 19 | .then((instance) => receiveInstance(instance, wasmModule)); 20 | return; // no-return 21 | }; 22 | 23 | module.instantiateWasm = instantiateWasm; 24 | } 25 | 26 | module.onRuntimeInitialized = () => { 27 | module.callMain([sessionId]); 28 | }; 29 | 30 | new WDOSBOX(module); 31 | return; 32 | } 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/dos/dosbox/ts/worker.origin.ts: -------------------------------------------------------------------------------- 1 | import { WasmModule } from "../../../impl/modules"; 2 | import { TransportLayer, MessageHandler, ClientMessage } from "../../../protocol/protocol"; 3 | import { MessagesQueue } from "../../../protocol/messages-queue"; 4 | 5 | export async function dosWorker(workerUrl: string, 6 | wasmModule: WasmModule, 7 | sessionId: string): Promise { 8 | const messagesQueue = new MessagesQueue(); 9 | let handler: MessageHandler = messagesQueue.handler.bind(messagesQueue); 10 | 11 | const worker = new Worker(workerUrl); 12 | worker.onerror = (e) => { 13 | handler("ws-err", { type: e.type, filename: e.filename, message: e.message }); 14 | }; 15 | worker.onmessage = (e) => { 16 | const data = e.data; 17 | if (data?.name !== undefined) { 18 | handler(data.name, data.props); 19 | } 20 | }; 21 | 22 | await wasmModule.instantiate({}); 23 | 24 | const transportLayer: TransportLayer = { 25 | sessionId, 26 | sendMessageToServer: (name: ClientMessage, props?: {[key: string]: any}) => { 27 | worker.postMessage({ name, props }); 28 | }, 29 | initMessageHandler: (newHandler: MessageHandler) => { 30 | handler = newHandler; 31 | messagesQueue.sendTo(handler); 32 | }, 33 | exit: () => { 34 | worker.terminate(); 35 | }, 36 | }; 37 | 38 | try { 39 | transportLayer.sendMessageToServer("wc-install", { 40 | module: (wasmModule as any).wasmModule, 41 | sessionId, 42 | }); 43 | } catch (e) { 44 | transportLayer.sendMessageToServer("wc-install", { sessionId }); 45 | } 46 | 47 | return transportLayer; 48 | } 49 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/dos/dosbox/ts/worker.ts: -------------------------------------------------------------------------------- 1 | import { WasmModule } from "../../../impl/modules"; 2 | import { TransportLayer, MessageHandler, ClientMessage } from "../../../protocol/protocol"; 3 | import { MessagesQueue } from "../../../protocol/messages-queue"; 4 | import { createWorker as _cw } from "./create-worker"; 5 | 6 | export type CreateWorker=(url: string) => Promise | Worker 7 | let createWorker:CreateWorker=_cw 8 | export const setCreateWorker=function(f:CreateWorker){createWorker=f} 9 | 10 | export async function dosWorker(workerUrl: string, 11 | wasmModule: WasmModule, 12 | sessionId: string): Promise { 13 | const messagesQueue = new MessagesQueue(); 14 | let handler: MessageHandler = messagesQueue.handler.bind(messagesQueue); 15 | 16 | const worker = await createWorker(workerUrl); 17 | worker.onerror = (e) => { 18 | handler("ws-err", { type: e.type, filename: e.filename, message: e.message }); 19 | }; 20 | worker.onmessage = (e) => { 21 | const data = e.data; 22 | if (data?.name !== undefined) { 23 | handler(data.name, data.props); 24 | } 25 | }; 26 | 27 | await wasmModule.instantiate({}); 28 | 29 | const transportLayer: TransportLayer = { 30 | sessionId, 31 | sendMessageToServer: (name: ClientMessage, props?: {[key: string]: any}) => { 32 | worker.postMessage({ name, props }); 33 | }, 34 | initMessageHandler: (newHandler: MessageHandler) => { 35 | handler = newHandler; 36 | messagesQueue.sendTo(handler); 37 | }, 38 | exit: () => { 39 | worker.terminate(); 40 | }, 41 | }; 42 | 43 | try { 44 | transportLayer.sendMessageToServer("wc-install", { 45 | module: (wasmModule as any).wasmModule, 46 | sessionId, 47 | }); 48 | } catch (e) { 49 | transportLayer.sendMessageToServer("wc-install", { sessionId }); 50 | } 51 | 52 | return transportLayer; 53 | } 54 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/http.origin.ts: -------------------------------------------------------------------------------- 1 | export interface XhrOptions { 2 | method?: string; 3 | progress?: (total: number, loaded: number) => void; 4 | data?: string; 5 | responseType?: XMLHttpRequestResponseType; 6 | } 7 | 8 | export const httpRequest = XhrRequest; 9 | 10 | // # XhrRequest 11 | // `XhrRequest` is small wrapper over XMLHttpRequest, that provides some 12 | // handy methods 13 | function XhrRequest(url: string, options: XhrOptions): Promise { 14 | return new Promise((resolve, reject) => { 15 | new Xhr(url, { 16 | ...options, 17 | success: resolve, 18 | fail: (message: string) => { 19 | reject(new Error(message)); 20 | }, 21 | }); 22 | }); 23 | } 24 | 25 | // private implementation 26 | interface XhrOptionsInternal extends XhrOptions { 27 | success?: (response: any) => void; 28 | fail?: (message: string) => void; 29 | } 30 | // * `success` - callback when resource is downloaded 31 | // * `fail` - fail callback 32 | 33 | 34 | // Class Xhr does not have any public methods 35 | class Xhr { 36 | private resource: string; 37 | private options: XhrOptionsInternal; 38 | private xhr: XMLHttpRequest | null = null; 39 | private total = 0; 40 | private loaded = 0; 41 | 42 | constructor(url: string, options: XhrOptionsInternal) { 43 | this.resource = url; 44 | this.options = options; 45 | this.options.method = options.method || "GET"; 46 | 47 | if (this.options.method !== "GET") { 48 | throw new Error("Method " + this.options.method + " is not supported"); 49 | } 50 | 51 | this.makeHttpRequest(); 52 | } 53 | 54 | private makeHttpRequest() { 55 | this.xhr = new XMLHttpRequest(); 56 | this.xhr.open(this.options.method || "GET", this.resource, true); 57 | if (this.options.method === "POST") { 58 | this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 59 | } 60 | this.xhr.overrideMimeType("text/plain; charset=x-user-defined"); 61 | 62 | let progressListner; 63 | if (typeof (progressListner = this.xhr).addEventListener === "function") { 64 | progressListner.addEventListener("progress", (evt) => { 65 | this.total = evt.total; 66 | this.loaded = evt.loaded; 67 | if (this.options.progress) { 68 | return this.options.progress(evt.total, evt.loaded); 69 | } 70 | }); 71 | } 72 | 73 | let errorListener; 74 | if (typeof (errorListener = this.xhr).addEventListener === "function") { 75 | errorListener.addEventListener("error", () => { 76 | if (this.options.fail) { 77 | this.options.fail("Unalbe to download '" + this.resource + 78 | "', code: " + (this.xhr as XMLHttpRequest).status); 79 | return delete this.options.fail; 80 | } 81 | }); 82 | } 83 | this.xhr.onreadystatechange = () => { 84 | return this.onReadyStateChange(); 85 | }; 86 | if (this.options.responseType) { 87 | this.xhr.responseType = this.options.responseType; 88 | } 89 | this.xhr.send(this.options.data); 90 | } 91 | 92 | private onReadyStateChange() { 93 | const xhr = (this.xhr as XMLHttpRequest); 94 | if (xhr.readyState === 4) { 95 | if (xhr.status === 200) { 96 | if (this.options.success) { 97 | const total = Math.max(this.total, this.loaded); 98 | if (this.options.progress !== undefined) { 99 | this.options.progress(total, total); 100 | } 101 | 102 | return this.options.success(xhr.response); 103 | } 104 | } else if (this.options.fail) { 105 | this.options.fail("Unable to download '" + this.resource + "', code: " + xhr.status); 106 | return delete this.options.fail; 107 | } 108 | } 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/http.ts: -------------------------------------------------------------------------------- 1 | import * as origin from "./http.origin" 2 | 3 | export type XhrOptions = origin.XhrOptions; 4 | 5 | export type ReadContent=((url: string, options: origin.XhrOptions)=> Promise) 6 | let readContent:ReadContent|undefined=undefined 7 | export const setReadContent=function(f:ReadContent){readContent=f} 8 | 9 | export let httpRequest = async function(url: string, options: origin.XhrOptions):Promise{ 10 | if(readContent){ 11 | const r=await readContent(url,options); 12 | if(r!==undefined){ 13 | return r; 14 | } 15 | } 16 | return origin.httpRequest(url,options); 17 | } -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/impl/emulators-impl.ts: -------------------------------------------------------------------------------- 1 | import { Build } from "../build"; 2 | import { Emulators, CommandInterface, BackendOptions } from "../emulators"; 3 | 4 | import { IWasmModules, WasmModulesImpl } from "./modules"; 5 | 6 | import DosBundle from "../dos/bundle/dos-bundle"; 7 | import { dosDirect } from "../dos/dosbox/ts/direct"; 8 | import { dosWorker } from "../dos/dosbox/ts/worker"; 9 | import Janus from "../janus/janus-impl"; 10 | 11 | import { TransportLayer, CommandInterfaceOverTransportLayer } from "../protocol/protocol"; 12 | 13 | class EmulatorsImpl implements Emulators { 14 | pathPrefix = ""; 15 | version = Build.version; 16 | wdosboxJs = "wdosbox.js"; 17 | 18 | private wasmModulesPromise?: Promise; 19 | 20 | async dosBundle(): Promise { 21 | const modules = await this.wasmModules(); 22 | const libzipWasm = await modules.libzip(); 23 | return new DosBundle(libzipWasm); 24 | } 25 | 26 | async dosboxNode(bundle: Uint8Array | Uint8Array[], options?: BackendOptions): Promise { 27 | return this.dosboxDirect(bundle, options); 28 | } 29 | 30 | async dosboxDirect(bundle: Uint8Array | Uint8Array[], options?: BackendOptions): Promise { 31 | const modules = await this.wasmModules(); 32 | const dosboxWasm = await modules.dosbox(); 33 | const transportLayer = await dosDirect(dosboxWasm, "session-" + Date.now()); 34 | return this.backend(bundle, transportLayer, options); 35 | } 36 | 37 | async dosboxWorker(bundle: Uint8Array | Uint8Array[], options?: BackendOptions): Promise { 38 | const modules = await this.wasmModules(); 39 | const dosboxWasm = await modules.dosbox(); 40 | const transportLayer = await dosWorker(this.pathPrefix + this.wdosboxJs, dosboxWasm, "session-" + Date.now()); 41 | return this.backend(bundle, transportLayer, options); 42 | } 43 | 44 | async janus(restUrl: string): Promise { 45 | // eslint-disable-next-line new-cap 46 | return Janus(restUrl); 47 | } 48 | 49 | async backend(bundle: Uint8Array | Uint8Array[], transportLayer: TransportLayer, 50 | options?: BackendOptions): Promise { 51 | return new Promise((resolve, reject) => { 52 | const ci = new CommandInterfaceOverTransportLayer( 53 | Array.isArray(bundle) ? bundle : [bundle], 54 | transportLayer, 55 | (err) => { 56 | if (err !== null) { 57 | reject(err); 58 | } else { 59 | // can be called from ctor, without timeout can be undefined 60 | setTimeout(() => resolve(ci), 4); 61 | } 62 | }, 63 | options || {}, 64 | ); 65 | }); 66 | } 67 | 68 | wasmModules(): Promise { 69 | if (this.wasmModulesPromise !== undefined) { 70 | return this.wasmModulesPromise; 71 | } 72 | 73 | const make = async () => { 74 | return new WasmModulesImpl(this.pathPrefix, this.wdosboxJs); 75 | }; 76 | 77 | this.wasmModulesPromise = make(); 78 | return this.wasmModulesPromise; 79 | } 80 | 81 | async dosDirect(bundle: Uint8Array | Uint8Array[]): Promise { 82 | return this.dosboxDirect(bundle); 83 | } 84 | 85 | async dosWorker(bundle: Uint8Array | Uint8Array[]): Promise { 86 | return this.dosboxWorker(bundle); 87 | } 88 | } 89 | 90 | const emulators = new EmulatorsImpl(); 91 | 92 | export default emulators; 93 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/keys.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | 3 | class KeyCodes { 4 | public KBD_NONE = 0; 5 | public KBD_0 = 48; 6 | public KBD_1 = 49; 7 | public KBD_2 = 50; 8 | public KBD_3 = 51; 9 | public KBD_4 = 52; 10 | public KBD_5 = 53; 11 | public KBD_6 = 54; 12 | public KBD_7 = 55; 13 | public KBD_8 = 56; 14 | public KBD_9 = 57; 15 | public KBD_a = 65; 16 | public KBD_b = 66; 17 | public KBD_c = 67; 18 | public KBD_d = 68; 19 | public KBD_e = 69; 20 | public KBD_f = 70; 21 | public KBD_g = 71; 22 | public KBD_h = 72; 23 | public KBD_i = 73; 24 | public KBD_j = 74; 25 | public KBD_k = 75; 26 | public KBD_l = 76; 27 | public KBD_m = 77; 28 | public KBD_n = 78; 29 | public KBD_o = 79; 30 | public KBD_p = 80; 31 | public KBD_q = 81; 32 | public KBD_r = 82; 33 | public KBD_s = 83; 34 | public KBD_t = 84; 35 | public KBD_u = 85; 36 | public KBD_v = 86; 37 | public KBD_w = 87; 38 | public KBD_x = 88; 39 | public KBD_y = 89; 40 | public KBD_z = 90; 41 | public KBD_f1 = 290; 42 | public KBD_f2 = 291; 43 | public KBD_f3 = 292; 44 | public KBD_f4 = 293; 45 | public KBD_f5 = 294; 46 | public KBD_f6 = 295; 47 | public KBD_f7 = 296; 48 | public KBD_f8 = 297; 49 | public KBD_f9 = 298; 50 | public KBD_f10 = 299; 51 | public KBD_f11 = 300; 52 | public KBD_f12 = 301; 53 | 54 | /* Now the weirder keys */ 55 | public KBD_kp0 = 320; 56 | public KBD_kp1 = 321; 57 | public KBD_kp2 = 322; 58 | public KBD_kp3 = 323; 59 | public KBD_kp4 = 324; 60 | public KBD_kp5 = 325; 61 | public KBD_kp6 = 326; 62 | public KBD_kp7 = 327; 63 | public KBD_kp8 = 328; 64 | public KBD_kp9 = 329; 65 | 66 | public KBD_kpperiod = 330; 67 | public KBD_kpdivide = 331; 68 | public KBD_kpmultiply = 332; 69 | public KBD_kpminus = 333; 70 | public KBD_kpplus = 334; 71 | public KBD_kpenter = 335; 72 | 73 | public KBD_esc = 256; 74 | public KBD_tab = 258; 75 | public KBD_backspace = 259; 76 | public KBD_enter = 257; 77 | public KBD_space = 32; 78 | public KBD_leftalt = 342; 79 | public KBD_rightalt = 346; 80 | public KBD_leftctrl = 341; 81 | public KBD_rightctrl = 345; 82 | public KBD_leftshift = 340; 83 | public KBD_rightshift = 344; 84 | public KBD_capslock = 280; 85 | public KBD_scrolllock = 281; 86 | public KBD_numlock = 282; 87 | public KBD_grave = 96; 88 | public KBD_minus = 45; 89 | public KBD_equals = 61; 90 | public KBD_backslash = 92; 91 | public KBD_leftbracket = 91; 92 | public KBD_rightbracket = 93; 93 | public KBD_semicolon = 59; 94 | public KBD_quote = 39; 95 | public KBD_period = 46; 96 | public KBD_comma = 44; 97 | public KBD_slash = 47; 98 | public KBD_printscreen = 283; 99 | public KBD_pause = 284; 100 | public KBD_insert = 260; 101 | public KBD_home = 268; 102 | public KBD_pageup = 266; 103 | public KBD_delete = 261; 104 | public KBD_end = 269; 105 | public KBD_pagedown = 267; 106 | public KBD_left = 263; 107 | public KBD_up = 265; 108 | public KBD_down = 264; 109 | public KBD_right = 262; 110 | public KBD_extra_lt_gt = 348; // ??? 111 | } 112 | 113 | export const Keys = new KeyCodes(); 114 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Try my best to make the least modification to the source code 3 | */ 4 | import emulatorsImpl from "./impl/emulators-impl"; 5 | export {ReadContent,setReadContent,httpRequest} from "./http"; 6 | export {CreateWorker,setCreateWorker} from "./dos/dosbox/ts/worker"; 7 | export {CommandInterface,CommandInterfaceEvents} from "./emulators"; 8 | export {LoadWasmModule,setLoadWasmModule,WasmModule,host} from "./impl/modules" 9 | export const emulators=emulatorsImpl; 10 | if(typeof window!=="undefined"){ 11 | console.log("asserted"); 12 | (window as any).emulators=emulators; 13 | } 14 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/protocol/messages-queue.ts: -------------------------------------------------------------------------------- 1 | import { ServerMessage, MessageHandler } from "./protocol"; 2 | 3 | interface DelayedMessage { 4 | name: ServerMessage, 5 | props: {[key: string]: any}, 6 | } 7 | 8 | export class MessagesQueue { 9 | private messages: DelayedMessage[] = []; 10 | public handler(name: ServerMessage, props: {[key: string]: any}) { 11 | this.messages.push({ name, props }); 12 | } 13 | public sendTo(handler: MessageHandler) { 14 | for (const next of this.messages) { 15 | handler(next.name, next.props); 16 | } 17 | 18 | this.messages = []; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /vscode-dosbox/src/emulators/protocol/protocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by caiiiycuk on 27.02.2020. 3 | // 4 | 5 | #ifndef JS_DOS_JS_DOS_PROTOCOL_H 6 | #define JS_DOS_JS_DOS_PROTOCOL_H 7 | 8 | #include 9 | #include 10 | 11 | void client_frame_set_size(int width, int height); 12 | void client_frame_update_lines(uint32_t* lines, uint32_t count, void* rgba); 13 | void client_sound_init(int freq); 14 | void client_sound_push(const float* samples, int num_samples); 15 | void client_stdout(const char* data, uint32_t amount); 16 | 17 | void client_log(const char* tag, const char* message); 18 | void client_warn(const char* tag, const char* message); 19 | void client_error(const char* tag, const char* message); 20 | 21 | extern int server_run(); 22 | extern void server_add_key(KBD_KEYS key, bool pressed, uint64_t pressedMs); 23 | extern void server_mouse_moved(float x, float y, bool relative, uint64_t movedMs); 24 | extern void server_mouse_button(int button, bool pressed, uint64_t pressedMs); 25 | extern void server_mouse_sync(uint64_t syncMs); 26 | extern void server_pause(); 27 | extern void server_resume(); 28 | extern void server_mute(); 29 | extern void server_unmute(); 30 | extern void server_exit(); 31 | 32 | 33 | #endif // JS_DOS_JS_DOS_PROTOCOL_H 34 | -------------------------------------------------------------------------------- /vscode-dosbox/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as dosbox from "./dosbox/main"; 3 | import * as player from "./msdos-player/main"; 4 | import * as jsdos from "./jsdos/main"; 5 | import * as adapted from "./emulators/main"; 6 | export { API } from "./api"; 7 | import * as D from "./api"; 8 | import { logger } from "./util/logger"; 9 | 10 | export async function activate( 11 | context: vscode.ExtensionContext 12 | ): Promise { 13 | console.log("run in nonweb mode:" + context.extensionMode.toString()); 14 | logger.logExtensionInfo(context); 15 | 16 | const api = { 17 | ...(await dosbox.activate(context)), 18 | ...player.activate(context), 19 | ...jsdos.activate(context), 20 | emulators: adapted.emulators, 21 | }; 22 | 23 | return api; 24 | } 25 | 26 | export function deactivate() {} 27 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos-bundle/bundle.ts: -------------------------------------------------------------------------------- 1 | //methods for manipute jsdos-bundles 2 | import * as JSZip from "jszip"; 3 | import * as vscode from "vscode"; 4 | 5 | const fs = vscode.workspace.fs; 6 | 7 | export interface CreateBundleOptions { 8 | sample?: string; 9 | boxConf?: string; 10 | mount?: { dir: vscode.Uri; disk: string }[]; 11 | } 12 | 13 | /**create a jsdos bundle file by add new files to the sample. 14 | * powered by https://stuk.github.io/jszip/ 15 | */ 16 | export async function createBundle({ 17 | sample, 18 | boxConf, 19 | mount, 20 | }: CreateBundleOptions): Promise { 21 | const zip = new JSZip(); 22 | if (sample) { 23 | const zipdata = await fs.readFile(vscode.Uri.file(sample)); 24 | await zip.loadAsync(zipdata); 25 | } 26 | 27 | zip.file(".jsdos/dosbox.conf", boxConf ? boxConf : ""); 28 | 29 | if (mount) { 30 | for (const m of mount) { 31 | await allFiles(m.dir, async (uri: vscode.Uri) => { 32 | const arr = await fs.readFile(uri); 33 | const dst = 34 | "/home/web_user/" + m.disk + uri.path.replace(m.dir.path, ""); 35 | zip.file(dst, arr); 36 | }); 37 | } 38 | } 39 | 40 | return zip; 41 | } 42 | 43 | async function allFiles( 44 | dir: vscode.Uri, 45 | callback: (file: vscode.Uri) => Promise 46 | ) { 47 | const dirs = await fs.readDirectory(dir); 48 | const r: string[] | undefined = vscode.workspace 49 | .getConfiguration("vscode-dosbox") 50 | .get("jsdos.ignore"); 51 | const regs = r ? r.map((val) => new RegExp(val)) : [/\.git/, /\.vscode/]; 52 | for (const [term, type] of dirs) { 53 | const uri = vscode.Uri.joinPath(dir, term); 54 | if (regs.some((val) => term.match(val))) { 55 | continue; 56 | } 57 | if (type === vscode.FileType.File) { 58 | await callback(uri); 59 | } else if (type === vscode.FileType.Directory) { 60 | await allFiles(uri, callback); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos-ci-shell/jsdosKey.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | 3 | export enum EmulatorKeyCode { 4 | KBD_NONE = 0, 5 | KBD_0 = 48, 6 | KBD_1 = 49, 7 | KBD_2 = 50, 8 | KBD_3 = 51, 9 | KBD_4 = 52, 10 | KBD_5 = 53, 11 | KBD_6 = 54, 12 | KBD_7 = 55, 13 | KBD_8 = 56, 14 | KBD_9 = 57, 15 | KBD_a = 65, 16 | KBD_b = 66, 17 | KBD_c = 67, 18 | KBD_d = 68, 19 | KBD_e = 69, 20 | KBD_f = 70, 21 | KBD_g = 71, 22 | KBD_h = 72, 23 | KBD_i = 73, 24 | KBD_j = 74, 25 | KBD_k = 75, 26 | KBD_l = 76, 27 | KBD_m = 77, 28 | KBD_n = 78, 29 | KBD_o = 79, 30 | KBD_p = 80, 31 | KBD_q = 81, 32 | KBD_r = 82, 33 | KBD_s = 83, 34 | KBD_t = 84, 35 | KBD_u = 85, 36 | KBD_v = 86, 37 | KBD_w = 87, 38 | KBD_x = 88, 39 | KBD_y = 89, 40 | KBD_z = 90, 41 | KBD_f1 = 290, 42 | KBD_f2 = 291, 43 | KBD_f3 = 292, 44 | KBD_f4 = 293, 45 | KBD_f5 = 294, 46 | KBD_f6 = 295, 47 | KBD_f7 = 296, 48 | KBD_f8 = 297, 49 | KBD_f9 = 298, 50 | KBD_f10 = 299, 51 | KBD_f11 = 300, 52 | KBD_f12 = 301, 53 | 54 | /*Now the weirder keys */ 55 | KBD_kp0 = 320, 56 | KBD_kp1 = 321, 57 | KBD_kp2 = 322, 58 | KBD_kp3 = 323, 59 | KBD_kp4 = 324, 60 | KBD_kp5 = 325, 61 | KBD_kp6 = 326, 62 | KBD_kp7 = 327, 63 | KBD_kp8 = 328, 64 | KBD_kp9 = 329, 65 | 66 | KBD_kpperiod = 330, 67 | KBD_kpdivide = 331, 68 | KBD_kpmultiply = 332, 69 | KBD_kpminus = 333, 70 | KBD_kpplus = 334, 71 | KBD_kpenter = 335, 72 | 73 | KBD_esc = 256, 74 | KBD_tab = 258, 75 | KBD_backspace = 259, 76 | KBD_enter = 257, 77 | KBD_space = 32, 78 | KBD_leftalt = 342, 79 | KBD_rightalt = 346, 80 | KBD_leftctrl = 341, 81 | KBD_rightctrl = 345, 82 | KBD_leftshift = 340, 83 | KBD_rightshift = 344, 84 | KBD_capslock = 280, 85 | KBD_scrolllock = 281, 86 | KBD_numlock = 282, 87 | KBD_grave = 96, 88 | KBD_minus = 45, 89 | KBD_equals = 61, 90 | KBD_backslash = 92, 91 | KBD_leftbracket = 91, 92 | KBD_rightbracket = 93, 93 | KBD_semicolon = 59, 94 | KBD_quote = 39, 95 | KBD_period = 46, 96 | KBD_comma = 44, 97 | KBD_slash = 47, 98 | KBD_printscreen = 283, 99 | KBD_pause = 284, 100 | KBD_insert = 260, 101 | KBD_home = 268, 102 | KBD_pageup = 266, 103 | KBD_delete = 261, 104 | KBD_end = 269, 105 | KBD_pagedown = 267, 106 | KBD_left = 263, 107 | KBD_up = 265, 108 | KBD_down = 264, 109 | KBD_right = 262, 110 | KBD_extra_lt_gt = 348, // ??? 111 | } 112 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos-ci-shell/main.ts: -------------------------------------------------------------------------------- 1 | import { CommandInterface } from "emulators"; 2 | import str2code, { KeyPress, KeyPressType } from "./jsdosKeyCode"; 3 | 4 | export class JsdosShell { 5 | /**manage the queue of key events */ 6 | private codes: KeyPress[] = []; 7 | constructor(private ci: CommandInterface) { 8 | setInterval(() => { 9 | if (this.ci && this.codes.length > 0) { 10 | const key = this.codes.shift(); 11 | if (key) { 12 | if (key.type === KeyPressType.press) { 13 | this.ci.simulateKeyPress(key.keyCode); 14 | } 15 | if (key.type === KeyPressType.pressdown) { 16 | this.ci.sendKeyEvent(key.keyCode, true); 17 | } 18 | if (key.type === KeyPressType.pressup) { 19 | this.ci.sendKeyEvent(key.keyCode, false); 20 | } 21 | } 22 | } 23 | }, 60); 24 | } 25 | public onStdout = this.ci.events().onStdout; 26 | /**exec shell command in jsdos */ 27 | public shell(cmd: string): void { 28 | const codes = str2code(cmd); 29 | if (cmd.trim().toLowerCase() === "exit") { 30 | console.warn( 31 | "===EXIT cmd detected: run `ci.exit()` instead of sending this to dosbox===" 32 | ); 33 | this.ci.exit(); 34 | } 35 | this.codes.push(...codes); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos/hooks.ts: -------------------------------------------------------------------------------- 1 | import { isNode } from "browser-or-node"; 2 | import { URI } from "vscode-uri"; 3 | import { WasmModule, host, httpRequest } from "../emulators/main"; 4 | 5 | export function loadWasmModule( 6 | url: URI, 7 | moduleName: string, 8 | onprogress: (stage: string, total: number, loaded: number) => void 9 | ): Promise { 10 | if (isNode) { 11 | return loadWasmModuleNode(url, moduleName, onprogress); 12 | } else { 13 | return loadWasmModuleBrowser(url, moduleName, onprogress); 14 | } 15 | } 16 | 17 | function loadWasmModuleNode( 18 | url: URI, 19 | moduleName: string, 20 | // eslint-disable-next-line 21 | onprogress: (stage: string, total: number, loaded: number) => void 22 | ) { 23 | if (host.globals.compiled[moduleName] !== undefined) { 24 | return host.globals.compiled[moduleName]; 25 | } 26 | const wdosboxurl=url.fsPath; 27 | 28 | const emModule = eval(`require`)(wdosboxurl); 29 | const compiledModulePromise = Promise.resolve( 30 | new CompiledNodeModule(emModule) 31 | ); 32 | if (moduleName) { 33 | host.globals.compiled[moduleName] = compiledModulePromise; 34 | } 35 | 36 | return compiledModulePromise; 37 | } 38 | 39 | function loadWasmModuleBrowser( 40 | url: URI, 41 | moduleName: string, 42 | onprogress: (stage: string, total: number, loaded: number) => void 43 | ) { 44 | if (host.globals.compiled[moduleName] !== undefined) { 45 | return host.globals.compiled[moduleName]; 46 | } 47 | 48 | async function load() { 49 | const fromIndex = url.path.lastIndexOf("/"); 50 | const wIndex = url.path.indexOf("w", fromIndex); 51 | const isWasmUrl = wIndex === fromIndex + 1 && wIndex >= 0; 52 | 53 | if (!host.wasmSupported || !isWasmUrl) { 54 | throw new Error( 55 | "Starting from js-dos 6.22.60 js environment is not supported" 56 | ); 57 | } 58 | 59 | const wasmUrl = URI.from({ 60 | ...url, 61 | path: url.path.replace(".js", ".wasm"), 62 | }); 63 | const binaryPromise = httpRequest(wasmUrl.toString(), { 64 | responseType: "arraybuffer", 65 | progress: (total, loaded) => { 66 | onprogress("Resolving DosBox (" + url + ")", total, loaded); 67 | }, 68 | }); 69 | const scriptPromise = httpRequest(url.toString(), { 70 | progress: (total, loaded) => { 71 | onprogress("Resolving DosBox", total, loaded); 72 | }, 73 | }); 74 | 75 | const [binary, script] = await Promise.all([binaryPromise, scriptPromise]); 76 | const wasmModule = await WebAssembly.compile(binary as ArrayBuffer); 77 | const instantiateWasm = (info: any, receiveInstance: any) => { 78 | info.env = info.env || {}; 79 | WebAssembly.instantiate(wasmModule, info).then((instance) => 80 | receiveInstance(instance, wasmModule) 81 | ); 82 | return; // no-return 83 | }; 84 | 85 | eval.call(host.globals, script as string); 86 | 87 | return new CompiledBrowserModule( 88 | wasmModule, 89 | host.globals.exports[moduleName], 90 | instantiateWasm 91 | ); 92 | } 93 | 94 | const promise = load(); 95 | 96 | if (moduleName) { 97 | host.globals.compiled[moduleName] = promise; 98 | } 99 | 100 | return promise; 101 | } 102 | 103 | class CompiledNodeModule implements WasmModule { 104 | private emModule: any; 105 | constructor(emModule: any) { 106 | this.emModule = emModule; 107 | } 108 | 109 | instantiate(initialModule: any): Promise { 110 | return new Promise((resolve) => { 111 | initialModule.onRuntimeInitialized = () => { 112 | resolve(); 113 | }; 114 | 115 | // eslint-disable-next-line new-cap 116 | new this.emModule(initialModule); 117 | }); 118 | } 119 | } 120 | 121 | class CompiledBrowserModule implements WasmModule { 122 | public wasmModule: WebAssembly.Module; 123 | private module: any; 124 | private instantiateWasm: any; 125 | 126 | constructor( 127 | wasmModule: WebAssembly.Module, 128 | module: any, 129 | instantiateWasm: any 130 | ) { 131 | this.wasmModule = wasmModule; 132 | this.module = module; 133 | this.instantiateWasm = instantiateWasm; 134 | } 135 | 136 | instantiate(initialModule: any): Promise { 137 | return new Promise((resolve) => { 138 | initialModule.instantiateWasm = this.instantiateWasm; 139 | initialModule.onRuntimeInitialized = () => { 140 | resolve(); 141 | }; 142 | // eslint-disable-next-line new-cap 143 | new this.module(initialModule); 144 | }); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos/main.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { createBundle } from "../jsdos-bundle/bundle"; 3 | import { Jsdos } from "../jsdos/Jsdos"; 4 | 5 | const input = "Input your url"; 6 | const empty = "empty (only load jsdos)"; 7 | 8 | const webresources = [ 9 | { 10 | name: "digger.com (jsdos demo)", 11 | url: "https://doszone-uploads.s3.dualstack.eu-central-1.amazonaws.com/original/2X/2/24b00b14f118580763440ecaddcc948f8cb94f14.jsdos", 12 | }, 13 | ]; 14 | 15 | export function activate(context: vscode.ExtensionContext) { 16 | const jsdos = new Jsdos(context); 17 | 18 | let disposable = vscode.commands.registerCommand( 19 | "dosbox.openJsdos", 20 | async (bundle?: vscode.Uri) => { 21 | if (bundle) { 22 | jsdos.setBundle(bundle); 23 | jsdos.runInWebview(undefined); 24 | } else { 25 | const items = webresources.map((val) => val.name); 26 | items.unshift(input, empty); 27 | const pickedName = await vscode.window.showQuickPick(items); 28 | let picked: vscode.Uri | null | undefined = undefined; 29 | if (pickedName === empty) { 30 | picked = null; 31 | } else if (pickedName === input) { 32 | const _uri = await vscode.window.showInputBox({ placeHolder: input }); 33 | if (_uri) { 34 | picked = vscode.Uri.parse(_uri); 35 | } 36 | } else { 37 | const res = webresources.find((val) => val.name === pickedName); 38 | if (res) { 39 | picked = vscode.Uri.parse(res.url); 40 | } 41 | } 42 | if (picked !== undefined) { 43 | jsdos.runInWebview(picked); 44 | } 45 | } 46 | } 47 | ); 48 | 49 | let disposable2 = vscode.commands.registerCommand( 50 | "dosbox.startJsdos", 51 | async (_bundle?: vscode.Uri) => { 52 | let bundle: vscode.Uri | null | undefined = _bundle; 53 | if ( 54 | vscode.workspace.workspaceFolders && 55 | vscode.workspace.workspaceFolders[0] 56 | ) { 57 | const workspaceBundle = await createBundle({ 58 | mount: [ 59 | { 60 | dir: vscode.workspace.workspaceFolders[0].uri, 61 | disk: "d", 62 | }, 63 | ], 64 | }).catch(console.warn); 65 | if (workspaceBundle) { 66 | jsdos.jszip = workspaceBundle; 67 | jsdos.updateAutoexec(["@mount c .", "@mount d ./d", "d:"]); 68 | bundle = undefined; 69 | } 70 | } 71 | const ci = await jsdos.runInHost(bundle); 72 | const t = ci.terminal(); 73 | t.show(); 74 | } 75 | ); 76 | 77 | context.subscriptions.push(disposable, disposable2); 78 | 79 | return { jsdos, createBundle }; 80 | } 81 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos/runInHost.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { JsdosShell } from "../jsdos-ci-shell/main"; 3 | 4 | export function createTerminal(shell: JsdosShell) { 5 | const writeEmitter = new vscode.EventEmitter(); 6 | const pty: vscode.Pseudoterminal = { 7 | onDidWrite: writeEmitter.event, 8 | open: () => { 9 | writeEmitter.fire( 10 | "Jsdos Terminal all changes after launch \x1b[31mwill not\x1b[0m be applied to this shell\r\n" 11 | ); 12 | shell.onStdout((val) => { 13 | writeEmitter.fire(val); 14 | }); 15 | }, 16 | close: () => {}, 17 | handleInput: (data) => { 18 | if (data.charCodeAt(0) === 127) { 19 | writeEmitter.fire(""); 20 | } else { 21 | writeEmitter.fire(data); 22 | } 23 | shell.shell(data); 24 | }, 25 | }; 26 | 27 | return vscode.window.createTerminal({ name: "Jsdos Terminal", pty }); 28 | } 29 | -------------------------------------------------------------------------------- /vscode-dosbox/src/jsdos/runInWebview.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { logger } from "../util/logger"; 3 | 4 | export function runInWebview( 5 | context: vscode.ExtensionContext, 6 | bundle: Uint8Array | string 7 | ): vscode.WebviewPanel { 8 | const viewColumn: vscode.ViewColumn | undefined = vscode.workspace 9 | .getConfiguration("vscode-dosbox") 10 | .get("jsdosWeb.viewColumn"); 11 | const panel = vscode.window.createWebviewPanel( 12 | "jsdos pannel", 13 | "jsdos" + new Date().toLocaleTimeString(), 14 | viewColumn ?? vscode.ViewColumn.Beside, 15 | { 16 | enableScripts: true, 17 | retainContextWhenHidden: true, 18 | //hint: the below settings should be folder's uri 19 | localResourceRoots: [ 20 | vscode.Uri.joinPath( 21 | context.extensionUri, 22 | "node_modules/emulators/dist" 23 | ), 24 | vscode.Uri.joinPath( 25 | context.extensionUri, 26 | "node_modules/emulators-ui/dist" 27 | ), 28 | vscode.Uri.joinPath(context.extensionUri, "dist"), 29 | vscode.Uri.joinPath(context.extensionUri, "src"), 30 | ], 31 | } 32 | ); 33 | 34 | const asWeb = (str: string): string => { 35 | const fullpath = vscode.Uri.joinPath(context.extensionUri, str); 36 | const uri = panel.webview.asWebviewUri(fullpath); 37 | const link = uri.toString(true); 38 | return link; 39 | }; 40 | 41 | const jsdosScript = (process as any).browser 42 | ? { 43 | emu: "https://js-dos.com/v7/build/releases/latest/emulators/emulators.js", 44 | emuDist: "https://js-dos.com/v7/build/releases/latest/js-dos/", 45 | ui: "https://js-dos.com/v7/build/releases/latest/emulators-ui/emulators-ui.js", 46 | uiCss: 47 | "https://js-dos.com/v7/build/releases/latest/emulators-ui/emulators-ui.css", 48 | } 49 | : { 50 | emu: asWeb( "/dist/emulators/emulators.js"), 51 | emuDist: asWeb("/dist/emulators/"), 52 | ui: asWeb("/dist/emulators-ui/emulators-ui.js"), 53 | uiCss: asWeb("/dist/emulators-ui/emulators-ui.css"), 54 | }; 55 | 56 | panel.webview.html = ` 57 | 58 | 59 | 60 | 61 | 62 | 72 | 73 | 74 | 75 | 76 | 77 | pause 78 | sound 79 | 83 |
84 |
85 |
86 | 92 |

loading

93 | 94 | 95 | 96 | 97 | `; 98 | 99 | // Handle messages from the webview 100 | panel.webview.onDidReceiveMessage( 101 | (message) => { 102 | const { command, value } = message; 103 | switch (command) { 104 | case "loaded": 105 | const bundlePath = typeof bundle === "string" ? bundle : undefined; 106 | panel.webview.postMessage({ 107 | command: "start", 108 | bundle, 109 | bundlePath, 110 | }); 111 | return; 112 | case "memoryContents": 113 | let str = "\n"; 114 | for (const key in value) { 115 | if (typeof value[key] === "object") { 116 | str += key + ":\t" + JSON.stringify(value[key]) + "\n"; 117 | } else { 118 | str = key + ":" + value[key] + "\t" + str; 119 | } 120 | } 121 | logger 122 | .channel("[debug]" + new Date().toLocaleTimeString() + "\n" + str) 123 | .show(); 124 | } 125 | }, 126 | undefined, 127 | context.subscriptions 128 | ); 129 | 130 | return panel; 131 | } 132 | -------------------------------------------------------------------------------- /vscode-dosbox/src/msdos-player/main.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | export function activate(context: vscode.ExtensionContext) { 4 | const { arch } = process; 5 | const msdosPath = context.asAbsolutePath( 6 | `./emu/msdos_player/win32-${arch === "arm64" ? "ia32" : arch}/msdos.exe` 7 | ); 8 | const commandPath = context.asAbsolutePath("./emu/msdos_player/command.com"); 9 | function player( 10 | msdosArgs: string[] = ["-e", "-d"], 11 | command: string = commandPath 12 | ) { 13 | const t = vscode.window.createTerminal({ 14 | name: "msdos-player", 15 | shellPath: "cmd.exe", 16 | shellArgs: ["/C", msdosPath, ...msdosArgs, command], 17 | }); 18 | return t; 19 | } 20 | 21 | let disposable = vscode.commands.registerCommand( 22 | "dosbox.openMsdosPlayer", 23 | () => { 24 | if (process.platform !== "win32") { 25 | throw new Error("msdos player can only run in win32 system"); 26 | } 27 | player().show(); 28 | } 29 | ); 30 | 31 | context.subscriptions.push(disposable); 32 | 33 | return { 34 | msdosPath, 35 | commandPath, 36 | msdosPlayer: player, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | 3 | import { runTests } from "@vscode/test-electron"; 4 | 5 | async function main(): Promise { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, "../../"); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, "./suite/index"); 14 | 15 | const launchArgs: string[] = []; 16 | 17 | const DELAY = (timeout: number): Promise => 18 | new Promise((resolve) => { 19 | setTimeout(resolve, timeout); 20 | }); 21 | // Download VS Code, unzip it and run the integration test 22 | await runTests({ 23 | extensionDevelopmentPath, 24 | extensionTestsPath, 25 | launchArgs, 26 | }); 27 | 28 | //test in all supported version of vscode 29 | //last test says ok with 1.47.1 but the platform specified extension should be over 1.61, so the following make no sense now 30 | for (let ver = 60; ver >= 61; ver--) { 31 | await DELAY(20000); 32 | await runTests({ 33 | version: `1.${ver.toString()}.1`, 34 | extensionDevelopmentPath, 35 | extensionTestsPath, 36 | launchArgs, 37 | }); 38 | await DELAY(20000); 39 | } 40 | } catch (err) { 41 | console.error("Failed to run tests"); 42 | process.exit(1); 43 | } 44 | } 45 | 46 | main(); 47 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/conf.test.ts: -------------------------------------------------------------------------------- 1 | import { Conf } from "../../dosbox/conf"; 2 | import * as assert from "assert"; 3 | import * as fs from "fs"; 4 | import * as path from "path"; 5 | 6 | suite("dosbox conf test", function () { 7 | const file = path.resolve( 8 | __dirname, 9 | "../../..", 10 | "emu/dosbox/dosbox-0.74.conf" 11 | ); 12 | const content = fs.readFileSync(file, { encoding: "utf-8" }); 13 | const conf = new Conf(content); 14 | 15 | test("update conf", () => { 16 | conf.update("sdl", "output", "test"); 17 | assert.ok(conf.get("sdl", "output") === "test", conf.toString()); 18 | const strs = conf 19 | .toString() 20 | .split("\n") 21 | .filter((val) => val.trimLeft().startsWith("output")); 22 | assert.equal(strs.length, 1, conf.toString()); 23 | }); 24 | test("update conf no key", () => { 25 | conf.update("sdl", "test", "test"); 26 | assert.equal(conf.get("sdl", "test"), "test", conf.toString()); 27 | }); 28 | test("update conf no section no key", () => { 29 | conf.update("test", "test", "test"); 30 | assert.equal(conf.get("test", "test"), "test", conf.toString()); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/dosbox.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from "vscode"; 6 | import * as Jszip from "jszip"; 7 | import * as myExtension from "../../extension"; 8 | import * as os from "os"; 9 | import * as path from "path"; 10 | import * as fs from "fs"; 11 | import { randomString } from "./util"; 12 | 13 | const testFolderName = Math.floor(Math.random() * 10 ** 10).toString(); 14 | const testDir = path.join(os.tmpdir(), testFolderName); 15 | fs.mkdirSync(testDir, { recursive: true }); 16 | console.log(`use ${testDir} as test dir`); 17 | 18 | let api: myExtension.API | undefined = undefined; 19 | 20 | suite("test DOSBox-like API", function () { 21 | this.beforeEach(async function () { 22 | //dosbox-X should install via flatpak so update configuration 23 | if (process.platform === "linux") { 24 | vscode.workspace 25 | .getConfiguration("vscode-dosbox") 26 | .update( 27 | "command.dosboxX", 28 | "flatpak run com.dosbox_x.DOSBox-X -silent -nogui", 29 | vscode.ConfigurationTarget.Global 30 | ); 31 | } 32 | const extension = vscode.extensions.getExtension("xsro.vscode-dosbox"); 33 | api = await extension?.activate(); 34 | assert.ok( 35 | api !== undefined, 36 | api ? Object.keys(api).toString() : "api can't get" 37 | ); 38 | }); 39 | 40 | test("test dosbox API", async function () { 41 | if (api) { 42 | const data = randomString(); 43 | const fileName = "DOSBOX.TXT"; 44 | api.dosbox.updateAutoexec([ 45 | `mount c ${testDir}`, 46 | "c:", 47 | `echo ${data} >${fileName}`, 48 | "exit", 49 | ]); 50 | await api.dosbox.run(); 51 | const testFile = path.join(testDir, fileName); 52 | assert.ok(fs.existsSync(testFile)); 53 | const data2 = fs.readFileSync(testFile, { encoding: "utf-8" }); 54 | assert.equal(data, data2.trim()); 55 | } 56 | }); 57 | 58 | test("test dosbox-x API", async function () { 59 | //according to https://github.com/flathub/com.dosbox.DOSBox#limitations 60 | //For security reasons, this Flatpak is sandboxed and only has access to the user's Documents folder. 61 | let testDir2 = testDir; 62 | if (process.platform === "linux") { 63 | testDir2 = __dirname; 64 | console.log("make and use " + testDir2); 65 | } 66 | if (api) { 67 | const data = randomString(); 68 | const fileName = "DOSBOXX.TXT"; 69 | api.dosboxX.updateAutoexec([ 70 | `mount c ${testDir2}`, 71 | "c:", 72 | `echo ${data} > ${fileName}`, 73 | "exit", 74 | ]); 75 | const r = await api.dosboxX.run(); 76 | const testFile = path.join(testDir2, fileName); 77 | console.log(JSON.stringify(r)); 78 | assert.ok( 79 | fs.existsSync(testFile), 80 | fs.readdirSync(testDir2, { encoding: "utf-8" }).join("\n") 81 | ); 82 | const data2 = fs.readFileSync(testFile, { encoding: "utf-8" }); 83 | assert.equal(data, data2.trim()); 84 | } 85 | }); 86 | 87 | test("start from jsdos bundle", async function () { 88 | const zip = new Jszip(); 89 | const data = randomString(); 90 | const testFolder = vscode.Uri.file( 91 | path.resolve(testDir, "dosbox-from-bundle") 92 | ); 93 | const fileName = "TEST.TXT"; 94 | zip.file( 95 | ".jsdos/dosbox.conf", 96 | ` 97 | [autoexec] 98 | mount c . 99 | echo ${data} >C:\\${fileName} 100 | exit` 101 | ); 102 | const bundleData = await zip.generateAsync({ type: "uint8array" }); 103 | if (api) { 104 | await api.dosbox.fromBundle(bundleData, testFolder, true); 105 | await api.dosbox.run(); 106 | } 107 | const testFile = path.join(testFolder.fsPath, fileName); 108 | assert.ok(fs.existsSync(testFile)); 109 | const data2 = fs.readFileSync(testFile, { encoding: "utf-8" }); 110 | assert.equal(data, data2.trim()); 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from "vscode"; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite("Extension Test Suite", () => { 9 | vscode.window.showInformationMessage("Start all tests."); 10 | 11 | test("Sample test", () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as Mocha from "mocha"; 3 | import * as glob from "glob"; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: "tdd", 9 | color: true, 10 | timeout: -1, 11 | retries: 3, 12 | }); 13 | 14 | const testsRoot = path.resolve(__dirname, ".."); 15 | 16 | return new Promise((c, e) => { 17 | glob("**/*.test.js", { cwd: testsRoot }, (err, files) => { 18 | if (err) { 19 | return e(err); 20 | } 21 | 22 | // Add files to the test suite 23 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 24 | 25 | try { 26 | // Run the mocha test 27 | mocha.run((failures) => { 28 | if (failures > 0) { 29 | e(new Error(`${failures} tests failed.`)); 30 | } else { 31 | c(); 32 | } 33 | }); 34 | } catch (err) { 35 | console.error(err); 36 | e(err); 37 | } 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/jsdos.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | // You can import and use all API from the 'vscode' module 3 | // as well as import your extension to test it 4 | import * as vscode from "vscode"; 5 | import * as myExtension from "../../extension"; 6 | import { randomString } from "./util"; 7 | 8 | let api: myExtension.API; 9 | 10 | export const jsdosHostTestSuite = suite("test jsdos API", function () { 11 | this.beforeEach(async function () { 12 | const extension = vscode.extensions.getExtension("xsro.vscode-dosbox"); 13 | if (api === undefined) { 14 | api = await extension?.activate(); 15 | } 16 | assert.ok( 17 | api !== undefined, 18 | api ? Object.keys(api).toString() : "api can't get" 19 | ); 20 | }); 21 | 22 | test("launch jsdos in extension host direct", async function () { 23 | if (!process.platform) { 24 | console.log("skip test dosboxDirect mode in web host"); 25 | this.skip(); 26 | } 27 | const ci = await api.jsdos.runInHost(undefined, false); 28 | assert.ok(typeof ci.width() === "number"); 29 | 30 | if (!process.platform) { 31 | this.timeout(10000); 32 | this.retries(3); 33 | } 34 | const t = ci.terminal(); 35 | t.show(); 36 | const testStr = randomString(); 37 | t.sendText(`echo ${testStr}\r`); 38 | const p = new Promise((resolve) => { 39 | let stdout = ""; 40 | ci.events().onStdout((val) => { 41 | stdout += val; 42 | if (stdout.includes(testStr)) { 43 | setTimeout(() => { 44 | resolve(stdout); 45 | }); 46 | } 47 | }); 48 | }); 49 | const stdout = await p; 50 | assert.ok(stdout, stdout); 51 | ci.exit(); 52 | t.dispose(); 53 | }); 54 | 55 | test("launch jsdos in extension host webworker", async function () { 56 | if (process.platform) { 57 | console.log("skip test dosboxDirect mode in local machine host"); 58 | this.skip(); 59 | } 60 | const ci = await api.jsdos.runInHost(undefined, true); 61 | assert.ok(typeof ci.width() === "number"); 62 | ci.exit(); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/jsdosWeb.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from "vscode"; 6 | import * as myExtension from "../../web/extension"; 7 | import { randomString } from "./util"; 8 | 9 | let api: myExtension.API; 10 | 11 | export const jsdosWebTestSuite = suite( 12 | "test jsdos API running in Webview", 13 | function () { 14 | this.beforeEach(async function () { 15 | const extension = vscode.extensions.getExtension("xsro.vscode-dosbox"); 16 | if (api === undefined) { 17 | api = await extension?.activate(); 18 | } 19 | assert.ok( 20 | api !== undefined, 21 | api ? Object.keys(api).toString() : "api can't get" 22 | ); 23 | }); 24 | 25 | test("launch jsdos in webview with empty bundle", async function () { 26 | const panel = await api.jsdos.runInWebviewPanel(null); 27 | 28 | const testStr = randomString(); 29 | 30 | const stdouts = await new Promise((resolve, reject) => { 31 | let stdouts: string = ""; 32 | const id = setInterval(() => { 33 | panel.webview.postMessage({ 34 | command: "stdin", 35 | value: `echo ${testStr}\r`, 36 | }); 37 | }); 38 | 39 | panel.webview.onDidReceiveMessage((e) => { 40 | if (e.command === "stdout") { 41 | stdouts += e.value; 42 | if (stdouts.includes(testStr)) { 43 | clearInterval(id); 44 | resolve(stdouts); 45 | } 46 | } 47 | }); 48 | }); 49 | assert.ok(stdouts.length > 10, stdouts); 50 | panel.dispose(); 51 | }); 52 | 53 | test("launch jsdos in webview with https url", async function () { 54 | const uri = vscode.Uri.parse( 55 | "https://cdn.dos.zone/original/2X/2/24b00b14f118580763440ecaddcc948f8cb94f14.jsdos" 56 | ); 57 | const panel = await api.jsdos.runInWebviewPanel(uri); 58 | 59 | const stdouts = await new Promise((resolve, reject) => { 60 | const stdouts: string[] = []; 61 | panel.webview.onDidReceiveMessage((e) => { 62 | if (e.command === "stdout") { 63 | stdouts.push(e.value); 64 | if (e.value.includes("C:\\>DIGGER.COM")) { 65 | resolve(stdouts); 66 | } 67 | } 68 | }); 69 | }); 70 | assert.ok(stdouts.length > 10, JSON.stringify(stdouts)); 71 | panel.dispose(); 72 | }); 73 | } 74 | ); 75 | -------------------------------------------------------------------------------- /vscode-dosbox/src/test/suite/util.ts: -------------------------------------------------------------------------------- 1 | export function randomString() { 2 | return Math.floor(Math.random() * 100000).toString(); 3 | } 4 | -------------------------------------------------------------------------------- /vscode-dosbox/src/util/checkInstallation.ts: -------------------------------------------------------------------------------- 1 | import * as cp from "child_process"; 2 | import { logger } from "./logger"; 3 | -------------------------------------------------------------------------------- /vscode-dosbox/src/util/logger.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | class Logger { 4 | static channel = vscode.window.createOutputChannel("vscode-DOSBox"); 5 | log = console.log; 6 | warn = console.warn; 7 | error = console.error; 8 | channel(text: string) { 9 | Logger.channel.append(text.trim() + "\n"); 10 | return Logger.channel; 11 | } 12 | logExtensionInfo(context: vscode.ExtensionContext) { 13 | const { platform, arch } = process; 14 | const target = 15 | platform === undefined && (process as any).browser 16 | ? "web" 17 | : platform + "-" + arch; 18 | 19 | logger.channel(`running at ${target} 20 | extensionUri: ${context.extensionUri} 21 | globalStorageUri: ${context.globalStorageUri} 22 | extensionMode: ${context.extensionMode} 23 | logUri: ${context.logUri} 24 | `); 25 | } 26 | } 27 | 28 | export const logger = new Logger(); 29 | -------------------------------------------------------------------------------- /vscode-dosbox/src/web/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import * as vscode from "vscode"; 4 | import * as jsdosWeb from "../jsdos/main"; 5 | import { logger } from "../util/logger"; 6 | 7 | // this method is called when your extension is activated 8 | // your extension is activated the very first time the command is executed 9 | export function activate(context: vscode.ExtensionContext) { 10 | // Use the console to output diagnostic information (console.log) and errors (console.error) 11 | // This line of code will only be executed once when your extension is activated 12 | logger.log( 13 | 'Congratulations, your extension "xsro.vscode-dosbox" is now active in the web extension host!' 14 | ); 15 | logger.logExtensionInfo(context); 16 | return { 17 | ...jsdosWeb.activate(context), 18 | }; 19 | } 20 | 21 | export type API = ReturnType; 22 | 23 | // this method is called when your extension is deactivated 24 | export function deactivate() {} 25 | -------------------------------------------------------------------------------- /vscode-dosbox/src/web/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from "vscode"; 6 | import * as myExtension from "../../extension"; 7 | 8 | suite("Web Extension Test Suite", () => { 9 | vscode.window.showInformationMessage("Start all tests."); 10 | 11 | test("Sample test", () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /vscode-dosbox/src/web/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | // imports mocha for the browser, defining the `mocha` global. 2 | require("mocha/mocha"); 3 | 4 | export function run(): Promise { 5 | return new Promise((c, e) => { 6 | mocha.setup({ 7 | ui: "tdd", 8 | reporter: undefined, 9 | timeout: "60s", 10 | }); 11 | 12 | // bundles all files in the current directory matching `*.test` 13 | const importAll = (r: __WebpackModuleApi.RequireContext) => 14 | r.keys().forEach(r); 15 | importAll(require.context(".", true, /\.test$/)); 16 | 17 | try { 18 | // Run the mocha test 19 | mocha.run((failures) => { 20 | if (failures > 0) { 21 | e(new Error(`${failures} tests failed.`)); 22 | } else { 23 | c(); 24 | } 25 | }); 26 | } catch (err) { 27 | console.error(err); 28 | e(err); 29 | } 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /vscode-dosbox/src/web/test/suite/jsdos.test.ts: -------------------------------------------------------------------------------- 1 | import { jsdosHostTestSuite } from "../../../test/suite/jsdos.test"; 2 | import { jsdosWebTestSuite } from "../../../test/suite/jsdosWeb.test"; 3 | 4 | jsdosHostTestSuite; 5 | jsdosWebTestSuite; 6 | -------------------------------------------------------------------------------- /vscode-dosbox/src/webview/connection.ts: -------------------------------------------------------------------------------- 1 | import { loghtml } from "./util"; 2 | 3 | declare const acquireVsCodeApi: 4 | | undefined 5 | | (() => { postMessage: (val: unknown) => undefined }); 6 | 7 | let postMessage = (val: unknown) => console.log(val); 8 | 9 | if (acquireVsCodeApi) { 10 | const vscode = acquireVsCodeApi(); 11 | postMessage = vscode.postMessage; 12 | } 13 | 14 | function listenMessage(command: string, listener: (message: unknown) => any) { 15 | // Handle the message inside the webview 16 | window.addEventListener("message", async (event) => { 17 | const message = event.data; // The JSON data our extension sent 18 | loghtml(message.command); 19 | if (message.command === command) { 20 | await listener(message); 21 | } 22 | }); 23 | } 24 | 25 | export class VscConnect { 26 | static post = postMessage; 27 | static listen = listenMessage; 28 | } 29 | -------------------------------------------------------------------------------- /vscode-dosbox/src/webview/index.ts: -------------------------------------------------------------------------------- 1 | import * as load from "./loadJsdos"; 2 | import { loghtml } from "./util"; 3 | import { VscConnect } from "./connection"; 4 | 5 | declare const jsdosconfig: { 6 | bundlePath: string | undefined; 7 | }; 8 | 9 | //auto load bundle when webview loaded 10 | if (jsdosconfig.bundlePath) { 11 | loghtml("auto loading from " + jsdosconfig); 12 | load.start(jsdosconfig.bundlePath); 13 | loghtml("", false); 14 | } 15 | 16 | //load bundle from message 17 | VscConnect.listen("start", async (message: any) => { 18 | console.log("===bundle received==="); 19 | if ( 20 | typeof message.bundlePath === "string" || 21 | typeof message.bundle === "string" 22 | ) { 23 | const bundlePath = 24 | message.bundlePath === undefined ? message.bundle : message.bundlePath; 25 | loghtml("bundle url: " + bundlePath); 26 | load.start(message.bundlePath); 27 | loghtml("", false); 28 | } else { 29 | //since Uint8array is not works well in VSCode 30 | //try my best to make cold work will 31 | const _bundle = message.bundle; 32 | let bundleData: Uint8Array = Array.isArray(_bundle) 33 | ? Uint8Array.from(_bundle) //in nodejs env, the Uint8array is always received as an array {[id:number]:number} 34 | : Uint8Array.from(Object.values(_bundle)); //in browser env, always received as an Object {[id:string]:number} 35 | //some times received as an object with data 36 | if (bundleData.length === 0 && _bundle.data) { 37 | bundleData = Uint8Array.from(_bundle.data); 38 | loghtml("bundle type: " + _bundle.type); 39 | } 40 | loghtml("bundle Array received lenghth: " + bundleData.length); 41 | loghtml("", false); 42 | load.start(bundleData); 43 | } 44 | }); 45 | 46 | loghtml("index.js loaded"); 47 | console.log("===loaded==="); 48 | 49 | VscConnect.post({ 50 | command: "loaded", 51 | }); 52 | -------------------------------------------------------------------------------- /vscode-dosbox/src/webview/loadJsdos.ts: -------------------------------------------------------------------------------- 1 | import { emulators } from "../emulators/main"; 2 | import { EmulatorsUi } from "emulators-ui"; 3 | import { CommandInterface } from "emulators"; 4 | import { onGetCi } from "./onGetCi"; 5 | 6 | declare const emulatorsUi: EmulatorsUi; 7 | declare const jsdosconfig: { 8 | pathPrefix: string; 9 | bundlePath: string | undefined; 10 | lastBundle?: string | Uint8Array | undefined; 11 | }; 12 | 13 | emulators.pathPrefix = jsdosconfig.pathPrefix; 14 | 15 | let jsdosElement = document.getElementById("root"); 16 | 17 | type MyEmulatorFunction = "dosboxWorker" | "dosboxDirect"; 18 | 19 | export let emulatorFunction: MyEmulatorFunction = "dosboxWorker"; 20 | document.getElementById("emulatorFunction")?.addEventListener("input", (e) => { 21 | const func = (e.target as any).value as MyEmulatorFunction; 22 | emulatorFunction = func; 23 | const text = document.getElementById("show"); 24 | if (text) { 25 | text.hidden = func !== "dosboxDirect"; 26 | } 27 | if (jsdosconfig.lastBundle) { 28 | start(jsdosconfig.lastBundle); 29 | } 30 | if (jsdosElement) { 31 | const _canvas = jsdosElement.getElementsByClassName("emulator-canvas"); 32 | _canvas[0].remove(); 33 | } 34 | (e.target as any).blur(); 35 | }); 36 | 37 | async function fromUrl(bundle: string) { 38 | const ci = await emulatorsUi 39 | .dos(jsdosElement as HTMLDivElement, { 40 | emulatorFunction, 41 | }) 42 | .run(bundle); 43 | return ci; 44 | } 45 | 46 | async function fromBundleData(bundleData: Uint8Array) { 47 | const layers = emulatorsUi.dom.layers(jsdosElement as HTMLDivElement); 48 | const ci = await emulators[emulatorFunction](bundleData); 49 | 50 | layers.hideLoadingLayer(); 51 | emulatorsUi.graphics.webGl(layers, ci); 52 | emulatorsUi.controls.keyboard(layers, ci); 53 | emulatorsUi.controls.mouse(false, 1.0, layers, ci); 54 | 55 | emulatorsUi.sound.audioNode(ci); 56 | return ci; 57 | } 58 | 59 | export async function start( 60 | bundle: Uint8Array | string 61 | ): Promise { 62 | jsdosconfig.lastBundle = bundle; 63 | let ci; 64 | if (typeof bundle === "string") { 65 | ci = await fromUrl(bundle); 66 | } else { 67 | ci = await fromBundleData(bundle); 68 | } 69 | if (ci) { 70 | onGetCi(ci); 71 | return ci; 72 | } else { 73 | throw new Error("can't load from: " + bundle); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /vscode-dosbox/src/webview/onGetCi.ts: -------------------------------------------------------------------------------- 1 | import { CommandInterface } from "emulators"; 2 | import { JsdosShell } from "../jsdos-ci-shell/main"; 3 | import { VscConnect } from "./connection"; 4 | import { emulatorFunction } from "./loadJsdos"; 5 | 6 | export let ci: CommandInterface | null = null; 7 | 8 | export function onGetCi(_ci: CommandInterface) { 9 | if (ci) { 10 | ci.pause(); 11 | ci.exit(); 12 | } 13 | ci = _ci; 14 | 15 | if (ci) { 16 | renderUi(ci); 17 | const shell = new JsdosShell(ci); 18 | shell.onStdout((value) => { 19 | VscConnect.post({ 20 | command: "stdout", 21 | value, 22 | }); 23 | }); 24 | VscConnect.listen("stdin", (message: any) => shell.shell(message.value)); 25 | } 26 | } 27 | 28 | const copyDosMemory = false; 29 | function renderUi(ci: CommandInterface) { 30 | const soundElement = document.getElementById( 31 | "sound" 32 | ) as HTMLInputElement | null; 33 | if (soundElement) { 34 | soundElement.checked = true; 35 | soundElement.addEventListener("input", (e) => { 36 | if ((e.target as any).checked) { 37 | ci.unmute(); 38 | } else { 39 | ci.mute(); 40 | } 41 | }); 42 | } 43 | 44 | document.getElementById("debug")?.addEventListener("input", (e) => { 45 | if (e.target && (e.target as any).checked) { 46 | //access memory 47 | //https://js-dos.com/v7/build/docs/dosbox-direct#accessing-memory 48 | ci.pause(); 49 | const text = document.getElementById("show"); 50 | if (emulatorFunction === "dosboxDirect") { 51 | (ci as any).transport.module._dumpMemory(copyDosMemory); 52 | const cts = (ci as any).transport.module.memoryContents; 53 | if (text) { 54 | text.innerHTML = JSON.stringify(cts, undefined, "\t"); 55 | } 56 | VscConnect.post({ command: "memoryContents", value: cts }); 57 | } 58 | } else { 59 | ci.resume(); 60 | } 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /vscode-dosbox/src/webview/util.ts: -------------------------------------------------------------------------------- 1 | export function loghtml(msg: string, add: boolean = true) { 2 | const e = document.getElementById("loadingInfo"); 3 | if (e?.innerHTML) { 4 | if (add) { 5 | e.innerHTML += ";" + msg; 6 | } else { 7 | e.innerHTML = msg; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vscode-dosbox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "outDir": "out", 6 | "sourceMap": true, 7 | "rootDir": "src", 8 | "strict": true /* enable all strict type-checking options */ 9 | /* Additional Checks */ 10 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 11 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 12 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 13 | }, 14 | "exclude": ["node_modules", ".vscode-test-web", "web", "emu"] 15 | } 16 | -------------------------------------------------------------------------------- /vscode-dosbox/web/.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | dist/ -------------------------------------------------------------------------------- /vscode-dosbox/web/README.md: -------------------------------------------------------------------------------- 1 | # run both emulators and emulators-ui in client 2 | 3 | run both emulators and emulators-ui in browser environment and expose an interface to control. 4 | -------------------------------------------------------------------------------- /vscode-dosbox/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 |
35 |
36 |
37 | 44 |

loading

45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /vscode-dosbox/web/res/empty.jsdos: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosasm/masm-tasm/b87e2ddafd0f0f6e5da0bf282912b557adacde07/vscode-dosbox/web/res/empty.jsdos --------------------------------------------------------------------------------