├── .eslintrc.json
├── .github
├── DISCUSSION_TEMPLATE
│ ├── announcements.yml
│ └── todos.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_proposal.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ ├── coverage.yml
│ └── npm-publish.yml
├── .gitignore
├── .gitmessage.txt
├── .npmignore
├── .prettierrc.json
├── COMMIT_README.md
├── README.md
├── babel.config.json
├── index.ts
├── jest.config.json
├── package-lock.json
├── package.json
├── sample_input
├── example01.o
├── example1.s
├── example2.s
├── example3.s
├── example4.s
├── example5.s
├── example6.s
└── example7.s
├── sample_output
├── example1.o
├── example2.o
├── example3.o
├── example4.o
├── example5.o
├── example6.o
└── example7.o
├── simulator_sample_output
├── example01.o
├── example02.o
├── example03.o
├── example04.o
├── example05.o
├── example06.o
└── example07.o
├── src
├── simulator
│ ├── assembler.ts
│ └── run.ts
└── utils
│ ├── constants.ts
│ └── functions.ts
├── tests
├── integration
│ ├── assembler.test.ts
│ └── simulator.test.ts
└── unit
│ ├── makeSymbolTable.test.ts
│ ├── recordDataSection.test.ts
│ └── recordTextSection.test.ts
├── tsconfig.json
└── typescript.eslintrc.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/recommended",
9 | "plugin:@typescript-eslint/recommended-requiring-type-checking",
10 | "plugin:prettier/recommended"
11 | ],
12 | "overrides": [],
13 | "parser": "@typescript-eslint/parser",
14 | "plugins": ["@typescript-eslint", "import"],
15 | "parserOptions": {
16 | "project": "./tsconfig.json",
17 | "ecmaVersion": "latest",
18 | "sourceType": "module"
19 | },
20 | "root": true,
21 | "rules": {
22 | "semi": [2, "always"],
23 | "@typescript-eslint/no-unused-vars": [
24 | "warn", // or "error"
25 | {
26 | "argsIgnorePattern": "^_",
27 | "varsIgnorePattern": "^_",
28 | "caughtErrorsIgnorePattern": "^_"
29 | }
30 | ]
31 | },
32 | "ignorePatterns": ["dist/", "node_modules/", "coverage/"],
33 | "settings": {
34 | "import/resolver": {
35 | "typescript": {}
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/DISCUSSION_TEMPLATE/announcements.yml:
--------------------------------------------------------------------------------
1 | title: '[Announcement] '
2 | labels: ['Announcement']
3 | body:
4 | - type: markdown
5 | attributes:
6 | value: |
7 | Announcement
8 | - type: textarea
9 | id: improvements and changes
10 | attributes:
11 | label: Top 3 improvements and changes
12 | description: 'What are the top 3 improvements we could make to this project?'
13 | value: |
14 | 1.
15 | 2.
16 | 3.
17 | ...
18 | render: bash
19 | validations:
20 | required: true
21 | - type: markdown
22 | attributes:
23 | value: |
24 | ## Additional
25 | And some more additionals
26 | - type: input
27 | id: has-id
28 | attributes:
29 | label: Suggestions
30 | description: A description about suggestions to help you
31 | validations:
32 | required: true
33 | - type: dropdown
34 | id: download
35 | attributes:
36 | label: Which area of this project could be most improved?
37 | options:
38 | - Documentation
39 | - New Features
40 | - Pull request review time
41 | - testing improvements
42 | - Bug fix time
43 | - Release cadence
44 | validations:
45 | required: true
46 | - type: checkboxes
47 | attributes:
48 | label: new release version
49 | options:
50 | - label: yes, it has deployed with new version
51 | - label: no, it's not version change
52 |
--------------------------------------------------------------------------------
/.github/DISCUSSION_TEMPLATE/todos.yml:
--------------------------------------------------------------------------------
1 | title: '[TODO] '
2 | labels: ['TODO']
3 | body:
4 | - type: markdown
5 | attributes:
6 | value: |
7 | TODO
8 | - type: textarea
9 | id: TODO
10 | attributes:
11 | label: TODOS with main points and conditions
12 | description: 'TODOS with main points with 3 conditions'
13 | value: |
14 |
15 | ## [main points]
16 |
17 | > what you have to do
18 |
19 | 1.
20 | 2.
21 | 3.
22 | ...
23 |
24 | ## [conditions]
25 |
26 | > follow these condition
27 |
28 | 1. if features are added, it should have test code with jest (unit test, integration test).
29 | 2. if it has changes with previous version, it should be written in README.md.
30 | 3. if it fix buges, describe which bug is found, and how solve it.
31 | ...
32 |
33 | render: markdown
34 | validations:
35 | required: true
36 | - type: dropdown
37 | id: download
38 | attributes:
39 | label: which category?
40 | options:
41 | - Documentation
42 | - New Features
43 | - Pull request review time
44 | - testing improvements
45 | - Bug fix time
46 | - Release cadence
47 | validations:
48 | required: true
49 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report 🪲
2 | description: Create a bug report to help us improve
3 | title: '[Bug]: '
4 | labels: ['🐞 Bug Report']
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | ### Please provide all the information requested. Issues that do not follow this format are likely to stall.
10 | ---
11 | - type: input
12 | id: version
13 | attributes:
14 | label: Version
15 | description: |
16 | The version of Mips-simulator-js you are using.
17 | Is it the [latest](https://github.com/mipsSimulatorUNIST/simulator/tree/release)? Test and see if the bug has already been fixed.
18 | placeholder: ex. 2.1.9
19 | validations:
20 | required: true
21 | - type: textarea
22 | id: reproduction
23 | attributes:
24 | label: Steps to reproduce
25 | description: Provide a detailed list of steps that reproduce the issue.
26 | placeholder:
27 | validations:
28 | required: true
29 | - type: textarea
30 | id: behavior
31 | attributes:
32 | label: Expected and Actual behavior
33 | description: A description of what you expect to happen. A clear and concise description of the unexpected behavior.
34 | placeholder: I expect to see X or Y, but A bug happened!
35 | validations:
36 | required: true
37 | - type: textarea
38 | id: context
39 | attributes:
40 | label: Additional context
41 | description: Anything else that might be relevant
42 | validations:
43 | required: false
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_proposal.yml:
--------------------------------------------------------------------------------
1 | name: Feature Proposal 🚀
2 | description: Submit a proposal for a new feature
3 | title: '[Feature]: '
4 | labels: [':rocket: Feature Proposal']
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | ### Thank you for taking the time to suggest a new feature!
10 | - type: textarea
11 | id: description
12 | attributes:
13 | label: '🚀 Feature Proposal'
14 | description: A clear and concise description of what the feature is.
15 | validations:
16 | required: true
17 | - type: textarea
18 | id: solution
19 | attributes:
20 | label: Motivation
21 | description: Outline your motivation for the proposal. How will it make mips-simulator-js better?
22 | validations:
23 | required: true
24 | - type: textarea
25 | id: alternatives
26 | attributes:
27 | label: Example
28 | description: Describe how this feature would be used.
29 | validations:
30 | required: false
31 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## ISSUE <#00> {이슈 이름}
2 | 여기에 이슈에 대해서 간단하게 설명해주세요
3 |
4 |
5 |
6 |
7 | ## CHANGES
8 | 어떤 코드를 만들었는지 혹은 어떤 코드를 수정해서 문제를 해결했는지 적어주세요
9 |
10 |
11 |
12 |
13 | ## TEST
14 | 어떤 부분을 테스트 해보면 좋을지 어떻게 테스트하면 되는지 간단하게 설명해주세요
15 |
16 | ## EX
17 | 예시 사진이 있다면 여기에 첨부해주세요
18 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | # 구독할 이벤트
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 |
10 | # jobs 단위로 개별 서버(정확히는 Docker 컨테이너 단위라고 한다.)에서 작업이 수행된다.
11 | # 각 작업은 병렬로 실행 된다고 하는데, needs: build와 같이 표시해서 기다릴 수도 있다.
12 | jobs:
13 | build:
14 | # Ubuntu, Windows, MacOS를 지원한다.
15 | runs-on: ubuntu-latest
16 |
17 | # node-version 과 같이 배열로 돼있으면, 해당 원소를 순회하면서 작업이 반복해서 실행된다.
18 | # 응용해서 runs-on에 여러 OS에서 돌릴 수도 있다.
19 | strategy:
20 | matrix:
21 | node-version: [14.x] # 템플릿 기본값: [10.x, 12.x, 14.x]
22 |
23 | # uses 개념은 다른 사람이 작성한 내용을 실행하는 개념이다.
24 | # actions/checkout: GitHub의 마지막 커밋으로 Checkout 한다.
25 | # actions/setup-node: Node.js를 설치한다.
26 | # run 개념은 명령어를 실행한다. 셸 스크립트와 동일하다.
27 | steps:
28 | - uses: actions/checkout@v2
29 | - name: Use Node.js ${{ matrix.node-version }}
30 | uses: actions/setup-node@v1
31 | with:
32 | node-version: ${{ matrix.node-version }}
33 | # npm ci는 npm install과 같은 기능을 수행한다.
34 | - run: npm ci
35 | # --if-present 옵션은 npm 스크립트가 존재할 때만 실행시키라는 의미이다.
36 | # 만약 build 스크립트가 없는 경우, 오류 없이 지나간다.
37 | - run: npm run build --if-present
38 | - run: npm run lint
39 | - run: npm test
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: Deploy Coverage content to Pages
3 |
4 | on:
5 | workflow_dispatch:
6 | push:
7 | branches: ['main']
8 |
9 | concurrency:
10 | group: 'pages'
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 | - uses: actions/configure-pages@v2
19 | - uses: actions/setup-node@v3
20 | with:
21 | node-version: 16
22 | - run: npm ci --legacy-peer-deps
23 | - run: npm run pre-test
24 | env:
25 | CI: false
26 | - name: Upload pages artifact
27 | uses: actions/upload-pages-artifact@v1
28 | with:
29 | path: './coverage/lcov-report'
30 |
31 | deploy:
32 | needs: build
33 | permissions:
34 | pages: write
35 | id-token: write
36 | environment:
37 | name: github-pages
38 | url: ${{ steps.deployment.outputs.page_url }}
39 | runs-on: ubuntu-latest
40 | steps:
41 | - name: Deploy to GitHub Pages
42 | id: deployment
43 | uses: actions/deploy-pages@v1
44 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: npm publish
2 |
3 | on:
4 | release:
5 | types: [created]
6 | branches:
7 | - main
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with:
16 | node-version: 16
17 | - name: Delete node_modules and package-lock.json
18 | run: rm -rf node_modules package-lock.json
19 | - name: Clear npm cache
20 | run: npm cache clean --force
21 | - name: Install yocto-queue as a direct dependency
22 | run: npm install yocto-queue --save
23 | - name: Install dependencies
24 | run: npm install
25 | - name: Run tests
26 | run: npm run pre-test
27 |
28 | publish-npm:
29 | needs: build
30 | runs-on: ubuntu-latest
31 | steps:
32 | - uses: actions/checkout@v3
33 | - uses: actions/setup-node@v3
34 | with:
35 | node-version: 16
36 | registry-url: https://registry.npmjs.org/
37 | - name: Delete node_modules and package-lock.json
38 | run: rm -rf node_modules package-lock.json
39 | - name: Clear npm cache
40 | run: npm cache clean --force
41 | - name: Install yocto-queue as a direct dependency
42 | run: npm install yocto-queue --save
43 | - name: Install dependencies
44 | run: npm install
45 | - name: Publish package to npm
46 | run: npm run publish-npm
47 | env:
48 | NODE_AUTH_TOKEN: ${{secrets.mips_token}}
49 |
50 | sync-release:
51 | needs: publish-npm
52 |
53 | runs-on: ubuntu-latest
54 | steps:
55 | - name: Checkout
56 | uses: actions/checkout@v2
57 | with:
58 | ref: main
59 |
60 | - name: Merge release to main
61 | uses: devmasx/merge-branch@master
62 | with:
63 | type: now
64 | from_branch: main
65 | target_branch: release
66 | github_token: ${{ github.token }}
67 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # node.js
2 | #
3 | coverage/
4 | node_modules/
5 | npm-debug.log
6 | yarn-error.log
7 |
8 | dist/
9 | .DS_Store
--------------------------------------------------------------------------------
/.gitmessage.txt:
--------------------------------------------------------------------------------
1 | ################
2 | # UNIST MIPS SIMULATOR GIT MESSAGE TEMPLATE
3 | # [커밋 타입 종류]
4 | # feat : 새로운 기능 추가
5 | # fix : 버그 수정
6 | # docs : 문서 수정
7 | # test : 테스트 코드 추가
8 | # refact : 코드 리팩토링
9 | # style : 코드 의미에 영향을 주지 않는 변경사항
10 | # chore : 빌드 부분 혹은 패키지 매니저 수정사항
11 | ################
12 |
13 | # '#' 라인은 주석입니다.
14 | # [#Issue Number] <타입> : <제목> 의 형식으로 제목을 아래 공백줄에 작성
15 | # 제목은 50자 이내 / 변경사항이 "무엇"인지 명확히 작성 / 끝에 마침표 금지
16 | # 예) feat : 로그인 기능 추가
17 | # [#Issue Number]를 커밋메세지 앞에 추가하면 이슈별로 커밋을 관리할 수 있음
18 | # --이 라인 밑에 "[#Issue Number] <타입> : <제목>" 작성해주세요--
19 |
20 | # 바로 아래 공백은 지우지 마세요 (제목과 본문의 분리를 위함)
21 |
22 | ################
23 | # 본문(구체적인 내용)을 아랫줄에 작성
24 | # 여러 줄의 메시지를 작성할 땐 "-"로 구분 (한 줄은 72자 이내)
25 | # --이 라인 밑에 "본문" 작성해주세요--
26 |
27 | ################
28 | # 꼬릿말(footer)을 아랫줄에 작성 (현재 커밋과 관련된 이슈 번호 추가 등)
29 | # 예) Close #7
30 | # --이 라인 밑에 "꼬릿말" 작성해주세요--
31 |
32 | ################
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | src/
3 | coverage/
4 | .github/
5 | tests/
6 | .eslintrc.json
7 | .gitignore
8 | .gitmessage.txt
9 | .prettierrc.json
10 | babel.config.json
11 | COMMIT_README.md
12 | index.ts
13 | tsconfig.json
14 | typescript.eslintrc.json
15 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": false,
3 | "jsxBracketSameLine": true,
4 | "semi": true,
5 | "singleQuote": true,
6 | "trailingComma": "all",
7 | "arrowParens": "avoid",
8 | "endOfLine": "auto",
9 | "tabWidth": 2
10 | }
11 |
--------------------------------------------------------------------------------
/COMMIT_README.md:
--------------------------------------------------------------------------------
1 | # GIT Message Template
2 |
3 | ## .gitmessage.txt
4 | 커밋 시 Git은 commit.template 옵션에 설정한 템플릿 파일을 보여준다.
5 |
6 | 커밋 메시지 템플릿을 지정하면 커밋 메시지를 작성할 때 일정한 스타일을 유지할 수 있다.
7 |
8 | .gitmessage.txt 파일은 아래와 같이 구성되어있으며, 커밋시에 적용됩니다.
9 |
10 | + [#Issue Number]를 커밋메세지 앞에 추가하면 이슈별로 커밋을 관리할 수 있음
11 |
12 | ```
13 | ################
14 | # UNIST MIPS SIMULATOR GIT MESSAGE TEMPLATE
15 | # [커밋 타입 종류]
16 | # feat : 새로운 기능 추가
17 | # fix : 버그 수정
18 | # docs : 문서 수정
19 | # test : 테스트 코드 추가
20 | # refact : 코드 리팩토링
21 | # style : 코드 의미에 영향을 주지 않는 변경사항
22 | # chore : 빌드 부분 혹은 패키지 매니저 수정사항
23 | ################
24 |
25 | # '#' 라인은 주석입니다.
26 | # [#Issue Number] <타입> : <제목> 의 형식으로 제목을 아래 공백줄에 작성
27 | # 제목은 50자 이내 / 변경사항이 "무엇"인지 명확히 작성 / 끝에 마침표 금지
28 | # 예) feat : 로그인 기능 추가
29 | # --이 라인 밑에 "[#Issue Number] <타입> : <제목>" 작성해주세요--
30 |
31 | # 바로 아래 공백은 지우지 마세요 (제목과 본문의 분리를 위함)
32 |
33 | ################
34 | # 본문(구체적인 내용)을 아랫줄에 작성
35 | # 여러 줄의 메시지를 작성할 땐 "-"로 구분 (한 줄은 72자 이내)
36 | # --이 라인 밑에 "본문" 작성해주세요--
37 |
38 | ################
39 | # 꼬릿말(footer)을 아랫줄에 작성 (현재 커밋과 관련된 이슈 번호 추가 등)
40 | # 예) Close #7
41 | # --이 라인 밑에 "꼬릿말" 작성해주세요--
42 |
43 | ################
44 | ```
45 |
46 | ## 적용 방법
47 | ### 템플릿 파일 설정
48 | 템플릿 파일을 설정해놓으면, git commit 명령을 실행할 때 지정한 템플릿 메시지를 편집기에서 매번 사용할 수 있다.
49 |
50 | 템플릿 파일을 설정한다는 것은 commit.template에 .gitmessage.txt 파일을 등록한다는 의미다.
51 |
52 | 템플릿 파일을 설정하는 명령은 아래와 같다.
53 |
54 | ```
55 | git config --global commit.template .gitmessage.txt
56 | ```
57 | ----
58 |
59 | ### 에디터 사용 방법
60 | ```
61 | git add .
62 | git commit
63 | ```
64 | 위 명령어를 사용하면 변경된 파일들을 스테이지에 올리고 commit을 실행한다.
65 |
66 | 이후 설정된 template이 터미널 에디터에 출력되고,
67 |
68 | ' i '를 눌러 INSERT 모드로 변경한다.
69 |
70 | 변경한 후에는
71 |
72 | ```
73 | ESC + :wq! + ENTER
74 | ```
75 |
76 | 를 통해 커밋 메세지 저장 후 커밋을 완료할 수 있다.
77 |
78 | 만약 commit 메세지 작성 중 commit을 취소하고 싶다면,
79 |
80 | ```
81 | ESC + :qa + ENTER
82 | ```
83 | 를 통해 commit 작업을 취소할 수 있다.
84 |
85 | ----
86 |
87 | ## Reference
88 | https://velog.io/@bky373/Git-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%85%9C%ED%94%8C%EB%A6%BF
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MIPS Simulator
2 |
3 | 
4 | 
5 | 
6 | 
7 |
8 | You can use Node.js MIPS Simulator with [npm](https://www.npmjs.com/package/mips-simulator-js)
9 |
10 | [_Git Repository_ →](https://github.com/mipsSimulatorUNIST/simulator)
11 |
12 | [_Coverage Status_ →](https://mipssimulatorunist.github.io/simulator/)
13 |
14 |
15 | [demo simulator→](https://mipssimulatorunist.github.io/reactGUI/simulator)
16 |
17 | 
18 |
19 | > ⚠️ [Changes](#changes)
20 | >
21 | > Example code in this document is working in `>= version 2.1.5`
22 | >
23 | > if you are using previous version, please read ⚠️ [Changes](#changes)
24 |
25 | ## Introduction
26 |
27 | This open source provides functions to implement MIPS simulation in node.js environment.
28 |
29 | We currently support functions that
30 | [assembler - convert an assembly file to a binary file](#assembler)
31 | and
32 | [simulator - simulate actual assembly files](#simulator).
33 |
34 | ## Installation
35 |
36 | ```bash
37 | $ npm install --save mips-simulator-js
38 | ```
39 |
40 | ## assembler
41 |
42 | > assemble provides some functions for making binary file to assembly file.
43 |
44 | ### makeInput
45 |
46 | function for parsing `binary file` to `string array`.
47 |
48 | ```typescript
49 | export function makeInput(inputFolderName: string, inputFileName : string) {
50 | return assemblyInput : string[]
51 | }
52 | ```
53 |
54 | ### makeObjectFile
55 |
56 | function for making file from `string array` to `binary file`.
57 |
58 | ```typescript
59 | export function makeObjectFile(
60 | outputFolderPath: string,
61 | outputFileName: string,
62 | content: string[],
63 | ) {
64 | return;
65 | // make .o file to outputFolderPath
66 | }
67 | ```
68 |
69 | ### assemble
70 |
71 | function for convert `assembly instructions` to `binary instructions`.
72 |
73 | > #### >= version 2.1.5
74 | >
75 | > `arrayOutputType` has deleted, assemble function only return string[] as output.
76 |
77 | > #### >= version 2.1.0
78 | >
79 | > `arrayOutputType` : if you want to get output with string, it should be false (default : true (string array))
80 | >
81 | > `mappingDetailRequest`: if you want to get mapping data (which assembly instruction map into specific binary instruction), it should be true (default : false)
82 |
83 | ```typescript
84 | interface IAssemble {
85 | output: string[];
86 | mappingDetail: IMapDetail[] | null;
87 | }
88 |
89 | export function assemble = (
90 | assemblyInstructions: string[],
91 | mappingDetailRequest = false
92 | ) {
93 | ...
94 | return {output, mappingDetail} : IAssemble
95 | };
96 | ```
97 |
98 | > **Mapping Detail Sample**
99 | >
100 | > ```typescript
101 | > const mappingDetailOutput: IMapDetail[] = [
102 | > {
103 | > key: 0,
104 | > assembly: "\t.data",
105 | > binary: [{ lineNumber: 0, data: "00000000000000000000000001011000" }],
106 | > },
107 | > {
108 | > key: 1,
109 | > assembly: "data1:\t.word\t100",
110 | > binary: [{ lineNumber: 24, data: "00000000000000000000000001100100" }],
111 | > },
112 | > ...{
113 | > key: 6,
114 | > assembly: '\tand\t$17, $17, $0',
115 | > binary: [{lineNumber: 2, data: '00000010001000001000100000100100'}],
116 | > },
117 | > {
118 | > key: 7,
119 | > assembly: '\tand\t$18, $18, $0',
120 | > binary: [{lineNumber: 3, data: '00000010010000001001000000100100'}],
121 | > },
122 | > ...{
123 | > key: 9,
124 | > assembly: '\tla\t$9, data2',
125 | > binary: [
126 | > {lineNumber: 5, data: '00111100000010010001000000000000'},
127 | > {lineNumber: 6, data: '00110101001010010000000000000100'},
128 | > ],
129 | > },
130 | > ...{
131 | > key: 29,
132 | > assembly: '\tj\tlab1',
133 | > binary: [{lineNumber: 22, data: '00001000000100000000000000000110'}],
134 | > },
135 | > {key: 30, assembly: 'lab5:', binary: []},
136 | > {
137 | > key: 31,
138 | > assembly: '\tori\t$16, $16, 0xf0f0',
139 | > binary: [{lineNumber: 23, data: '00110110000100001111000011110000'}],
140 | > },
141 | > ];
142 | > ```
143 |
144 | ## simulator
145 |
146 | > function for getting `simulating data` as result or process
147 |
148 | `cycle`: the number of step requested by user for instructions
149 |
150 | `returnCycles`: if user want to get process data, returnCycles should be True. (default : false)
151 |
152 | ```typescript
153 |
154 | export interface simulatorOutputType {
155 | PC: string;
156 | registers: {[key: string]: string};
157 | dataSection: {[key: string]: string} | Record;
158 | stackSection: {[key: string]: string} | Record;
159 | }
160 |
161 | export interface ISimulatorOutput {
162 | result: simulatorOutputType;
163 | history: simulatorOutputType[] | null;
164 | }
165 |
166 | export function simulator(
167 | assemblyInstructions: string[],
168 | cycleNum: number,
169 | returnHistory = false,
170 | ): Promise {
171 | ...
172 | return returnHistory ? {result, history: CYCLES} : {result, history: null};
173 | };
174 | ```
175 |
176 | > **output (after number of cycle)**
177 | >
178 | > ```js
179 | > {
180 | > PC: '0x00400058',
181 | > registers: {
182 | > R0: '0x00000000',
183 | > R1: '0x00000000',
184 | > R2: '0x00000000',
185 | > R3: '0x0000000a',
186 | > R4: '0x10000000',
187 | > R5: '0x00000000',
188 | > R6: '0x00000000',
189 | > R7: '0x00000000',
190 | > R8: '0x00000000',
191 | > R9: '0x00000000',
192 | > R10: '0x00000000',
193 | > R11: '0x00000000',
194 | > R12: '0x00000000',
195 | > R13: '0x00000000',
196 | > R14: '0x00000000',
197 | > R15: '0x00000000',
198 | > R16: '0x00000000',
199 | > R17: '0x00000000',
200 | > R18: '0x00000000',
201 | > R19: '0x00000000',
202 | > R20: '0x00000000',
203 | > R21: '0x00000000',
204 | > R22: '0x00000000',
205 | > R23: '0x00000000',
206 | > R24: '0x00000000',
207 | > R25: '0x00000000',
208 | > R26: '0x00000000',
209 | > R27: '0x00000000',
210 | > R28: '0x00000000',
211 | > R29: '0x80000000',
212 | > R30: '0x00000000',
213 | > R31: '0x00000000'
214 | > },
215 | > dataSection: {
216 | > '0x10000000': '0x00000001',
217 | > '0x10000004': '0x0000000a',
218 | > '0x10000008': '0x00000000'
219 | > },
220 | > stackSection: {
221 | > '0x7ffffff4': '0x0000000a',
222 | > '0x7ffffff8': '0x00000000',
223 | > '0x7ffffffc': '0x00000000'
224 | > }
225 | > }
226 | > ```
227 |
228 | ## Usage
229 |
230 | ### **Assembly Language → Binary Instruction**
231 |
232 | ```js
233 | // import functions
234 | import { assemble } from "mips-simulator-js";
235 |
236 | /*
237 | * if the inputFilePath is '/Users/user/simulator/sample_input/sample/example1.s',
238 | * currDirectory : '/Users/user/simulator'
239 | * inputFolderPath : 'sample_input/sample'
240 | * inputFileName: 'example1.s'
241 | */
242 | const inputFolderName = 'sample_input/sample';
243 | const inputFileName = 'example1.s';
244 | /*
245 | * if the outputFilePath is '/Users/user/simulator/sample_input/sample/example1.s',
246 | * currDirectory : '/Users/user/simulator'
247 | * outputFolderPath : 'sample_input/sample'
248 | * outputFileName: 'example1.o'
249 | * content : ['01010', '01010']
250 | */
251 | const outputFolderPath = 'sample_input/sample';
252 | const outputFileName = 'example1.o';
253 |
254 | const assemblyInstructions = makeInput(inputFolderName, inputFileName);
255 | const { output, mappingDetail } = assemble(assemblyInstructions);
256 |
257 | makeObjectFile(outputFolderPath, outputFileName, output);
258 | ```
259 |
260 | ### Input/Output
261 |
262 |
263 |
264 |
265 |
266 |
267 | ### **Simulator**
268 |
269 | ```typescript
270 | // import functions
271 | import { simulator } from "mips-simulator-js";
272 |
273 | const inputFolderName = 'sample_input/sample';
274 | const inputFileName = 'example1.s';
275 |
276 | /*
277 | * input : assemblyInstructions: string[], cycle: number, returnCycles: boolean
278 |
279 | * assemblyInstructions is same as assemblyInstructions in assemble function above.
280 |
281 | * cycle is the number of cycles you want to execute.
282 | * Executing one cycle means that executing one instruction.
283 |
284 | * returnCycles determines the type of return.
285 | * If returnCycles = false (default), Returns only the final form of the result.
286 | * If returnCycles = true, Returns an object containing information of all cycles.
287 |
288 | ex) returnCycles = false, you can use this function as below form.
289 | const result = simulator(makeInput('sample_input', 'example1.s'), 10000, false)
290 |
291 | ex) returnCycles = true, you can use this function as below form.
292 | interface SimulatorResult {
293 | output: simulatorOutputType;
294 | cycles: simulatorOutputType[];
295 | }
296 | */
297 |
298 | const assemblyInstructions = makeInput(inputFolderName, inputFileName);
299 |
300 | const fetchSimulator = async (fileContent: string[] | null) => {
301 | const output = await simulator(fileContent, 1000, true);
302 | return output;
303 | };
304 |
305 | const {result, history} = fetchSimulator(assemblyInstructions);
306 | ```
307 |
308 | ### Input/Output
309 |
310 |
311 |
312 |
313 |
314 |
315 | ## Usage for React/Next
316 |
317 | You can check out the code and example from [**mips-react-example-ts**](https://github.com/mipsSimulatorUNIST/mips-react-examples-ts)
318 |
319 | ### Problem
320 |
321 | If you use this npm package in your `react` or `next` project, problems will occur in the 'fs', 'path', and 'process' parts that load files.
322 |
323 |
324 |
325 | This problem is caused by the webpack version. For details, refer to the [**webpack official documentation**](https://webpack.kr/migrate/5/#upgrade-webpack-4-and-its-pluginsloaders).
326 |
327 | ### Solution
328 |
329 | The solution is to change the webpack configuration to `false` as shown below and import the file using `fetch`.
330 |
331 | 1. Change webpack config and package settings
332 |
333 | ```js
334 | // node_modules/react-scripts/config/webpack.config.json
335 | module.exports = function (webpackEnv) {
336 | // ...
337 | return {
338 | // ...
339 | resolve: {
340 | // ...
341 | // Add This!👇
342 | fallback: {
343 | "fs": false,
344 | "path": false,
345 | "process": false,
346 | },
347 | // ...
348 | }
349 | }
350 | }
351 | // package.json
352 | {
353 | // ...
354 | "dependencies": {},
355 | "devDependencies": {},
356 |
357 | // Add This👇️
358 | "browser": {
359 | "fs": false,
360 | "os": false,
361 | "path": false
362 | }
363 | }
364 | ```
365 |
366 | 2. Creating a file calling function using fetch (This function is a replacement for 'makeInput' provided by the library for use in `React` / `Next`.)
367 |
368 | ```js
369 | const fetchFile = async (filePath: string) => {
370 | await fetch(filePath)
371 | .then(response => response.text())
372 | .then(text => {
373 | // Create a function to use and put it here!
374 | });
375 | };
376 |
377 | // Example
378 |
379 | const [fileContent, setFileContent] = useState('');
380 | const [binaryInstruction, setBinaryInstruction] = useState(
381 | null
382 | );
383 |
384 | useEffect(() => {
385 | const fetchFile = async (filePath: string) => {
386 | await fetch(filePath)
387 | .then(response => response.text())
388 | .then(text => {
389 | setFileContent(text.split('\n'));
390 | });
391 | };
392 | const filePath = `sample_input/example01.s`;
393 | fetchFile(filePath);
394 | }, [setFileContent]);
395 |
396 | useEffect(() => {
397 | if (fileContent)
398 | setBinaryInstruction(assemble(fileContent).split("\n"));
399 | }, [fileContent]);
400 |
401 | ```
402 |
403 | ### ⚠️ Caution
404 |
405 | In the browser, unlike in the local environment, only files or documents in the public path can be used, and the default path is automatically designated as public. Therefore, the assembly file to be converted into an object file using assembler must be stored in the `public` folder.
406 |
407 | ## Changes
408 |
409 | ### >= version 2.1.5
410 |
411 | `arrayOutputType` has deleted, `assemble` function only return string[] as output.
412 |
413 | Add data section, text section size, binary value of data segment and PC to the `mapping table`.
414 |
415 | ### >= version 2.1.3
416 |
417 | #### new parameter for assemble `>= version 2.1.1`
418 |
419 | `arrayOutputType` : if you want to get output with string, it should be false (default : true (string array))
420 |
421 | `mappingDetailRequest`: if you want to get mapping data (which assembly instruction map into specific binary instruction), it should be true (default : false)
422 |
423 | #### parameter naming changes: `>= version 2.1.1`
424 |
425 | - `assemblerFile` => `assemblyInstructions` (in `assemble`, `simulator`)
426 | - `cycle` => `cycleNum` (in `simulator`)
427 | - `returnCycles` => `returnHistory` (in `simulator`)
428 |
429 | #### return type changes:
430 |
431 | `>= version 2.1.3`
432 |
433 | - `ISimulatorOutput` => `Promise` (in `simulator`)
434 |
435 | `>= version 2.1.1`
436 |
437 | - `output` => `{output, mappingDetail}` (in `assemble`)
438 | - `ISimulatorOutput | simulatorOutputType` => `ISimulatorOutput` (in `simulator`)
439 |
440 | ## Supported Instruction
441 |
442 | you can check
443 | [**MIPS Reference**](https://inst.eecs.berkeley.edu/~cs61c/resources/MIPS_Green_Sheet.pdf)
444 | å
445 | In this library, we support below instructions
446 |
447 | | Instruction | Format | opcode | funct |
448 | | :---------: | :----: | :----: | :----: |
449 | | SLL | R | 000000 | 000000 |
450 | | SRL | R | 000000 | 000010 |
451 | | JR | R | 000000 | 001000 |
452 | | ADD | R | 000000 | 100000 |
453 | | ADDU | R | 000000 | 100001 |
454 | | AND | R | 000000 | 100100 |
455 | | NOR | R | 000000 | 100111 |
456 | | OR | R | 000000 | 100101 |
457 | | SLT | R | 000000 | 101010 |
458 | | SLTU | R | 000000 | 101011 |
459 | | SUB | R | 000000 | 100010 |
460 | | SUBU | R | 000000 | 100011 |
461 | | LUI | I | 001111 | null |
462 | | BEQ | I | 000100 | null |
463 | | BNE | I | 000101 | null |
464 | | LW | I | 100011 | null |
465 | | LHU | I | 100101 | null |
466 | | SW | I | 101011 | null |
467 | | SH | I | 101001 | null |
468 | | ADDI | I | 001000 | null |
469 | | ADDIU | I | 001001 | null |
470 | | ANDI | I | 001100 | null |
471 | | ORI | I | 001101 | null |
472 | | SLTI | I | 001010 | null |
473 | | SLTIU | I | 001011 | null |
474 | | J | J | 000010 | null |
475 | | JAL | J | 000011 | null |
476 |
477 | ## pseudo Instruction
478 |
479 | #### **la (load address)**
480 |
481 | `la $2, VAR1`
482 |
483 | - `VAR1` is a label in the data section. It should be converted to lui and ori instructions.
484 | - lui $register, upper 16bit address
485 | ori $register, lower 16bit address
486 | If the lower 16bit address is 0x0000, the ori instruction is useless.
487 |
488 | - Case1) load address is 0x1000 0000
489 |
490 | lui $2, 0x1000
491 |
492 | - Case2) load address is 0x1000 0004
493 |
494 | lui $2, 0x1000
495 |
496 | ori $2, $2, 0x0004
497 |
498 | #### **move**
499 |
500 | `move $1, $2`
501 |
502 | It should be converted to add instruction with $0 as a target register(rt).
503 |
504 | ## Contribution
505 |
506 | If you want to contribute to [**mips-simulator-js**](https://www.npmjs.com/package/mips-simulator-js), please come in [**_Git Repository_**](https://github.com/mipsSimulatorUNIST/simulator) and clone!
507 |
508 | We have completed building CI, and test automation is also ready.
509 |
510 | We are using testing library with `jest`
511 |
512 | All work on Mips-simulator-js happens directly on [Github](https://github.com/mipsSimulatorUNIST/simulator). Both core team members and external contributors send pull requests which go through the same review process.
513 |
514 | ### Contribution process
515 |
516 | Thank you for your interest in contributing to Mips-simulator-js. Before you begin writing code, it is important that you share your intention to contribute with the team, based on the type of contribution
517 |
518 | 1. You want to **propose a new feature** and implement it.
519 | - Post about your intended feature in an [issue](https://github.com/mipsSimulatorUNIST/simulator/issues), then implement it.
520 | - We suggest that the branch name that you implement is better to be {type}/{issue number}/{issue name}. ex) feature/118/githubAction, bugfix/120/typo
521 |
522 | 2. You want to **implement a feature or bug-fix** for an outstanding issue.
523 | - Search for your issue in the [Mips-simulator-js issue list](https://github.com/mipsSimulatorUNIST/simulator/issues).
524 | - Pick an issue and comment that you'd like to work on the feature or bug-fix.
525 | - If you need more context on a particular issue, please ask and we shall provide.
526 |
527 | 3. **Open pull request**
528 | - You implement and test your feature or bug-fix, please submit a Pull Request to [https://github.com/mipsSimulatorUNIST/simulator/pulls](https://github.com/mipsSimulatorUNIST/simulator/pulls) with some test case.
529 | - Once a pull request is accepted and CI is passing, there is nothing else you need to do. we will check and merge the PR for you.
530 |
531 | **_Always opening_** to join this project for developing this library.
532 |
533 | ❗️[_ISSUE_ →](https://github.com/mipsSimulatorUNIST/simulator/issues)
534 |
535 | ✅ [_Pull Request_ →](https://github.com/mipsSimulatorUNIST/simulator/pulls)
536 |
537 | ### required environment (global)
538 |
539 | ```bash
540 | $ npm install typescript -g
541 | ```
542 |
543 | ## License
544 |
545 | Licensed under the MIT License, Copyright © 2023-present MIPS-Simulator-UNIST
546 |
--------------------------------------------------------------------------------
/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-typescript", "@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | import {makeBinaryObject, makeBinaryArray} from '@src/simulator/assembler';
2 | import {initialize, initializeMem} from '@src/utils/constants';
3 | import {
4 | IMapDetail,
5 | mainProcess,
6 | makeMappingDetail,
7 | simulatorOutputType,
8 | } from '@src/utils/functions';
9 |
10 | export interface IAssemble {
11 | readonly output: string[];
12 | readonly mappingDetail: IMapDetail[] | null;
13 | }
14 |
15 | export interface ISimulatorOutput {
16 | readonly result: simulatorOutputType;
17 | history: simulatorOutputType[] | null;
18 | }
19 |
20 | export function assemble(
21 | assemblyInstructions: string[],
22 | mappingDetailRequest = false,
23 | ): IAssemble {
24 | /*
25 | * input : assemblyInstructions: string[]
26 | * Enter the path where the assembly file is located.
27 |
28 | * If the assemlbyFile path is /Users/junghaejune/simulator/sample_input/example1.s
29 | * you can enter path /Users/junghaejune/simulator/sample_input/example1.s into assemblyInstructions.
30 | * If you don't know the path exactly, you can use makeInput function in functions.ts
31 | * Let your current directory is simulator (/Users/junghaejune/simulator).
32 | * Then you only put makeInput("sample_input", "example1.s") into assemblyInstructions
33 |
34 | * output: string[]
35 | * The assembly file is converted to a string in binary form.
36 |
37 | ex)
38 | input: sample_input/example1.s
39 | ...
40 | main:
41 | and $17, $17, $0
42 | and $18, $18, $0
43 | ...
44 |
45 | output:
46 | [00000010001000001000100000100100,
47 | ...
48 | 00000010010000001001000000100100]
49 |
50 | */
51 |
52 | const {
53 | dataSectionSize,
54 | textSectionSize,
55 | binaryText,
56 | binaryData,
57 | mappingTable,
58 | dataSeg,
59 | textSeg,
60 | } = makeBinaryObject(assemblyInstructions);
61 |
62 | let mappingDetail: IMapDetail[] | null = null;
63 |
64 | const output: string[] = makeBinaryArray(
65 | dataSectionSize,
66 | textSectionSize,
67 | binaryText,
68 | binaryData,
69 | );
70 |
71 | if (mappingDetailRequest) {
72 | mappingDetail = makeMappingDetail(
73 | assemblyInstructions,
74 | dataSeg,
75 | textSeg,
76 | mappingTable,
77 | output,
78 | );
79 | }
80 |
81 | return {output, mappingDetail};
82 | }
83 |
84 | export async function simulator(
85 | assemblyInstructions: string[],
86 | cycleNum: number,
87 | returnHistory = false,
88 | ): Promise {
89 | /*
90 | * input : assemblyInstructions: string[], cycle: number, returnCycles: boolean
91 | * assemblyInstructions is same as assemblyInstructions in assemble function above.
92 | * cycle is the number of cycles you want to execute.
93 | * Executing one cycle means that executing one instruction.
94 | * returnCycles determines the type of return.
95 | * If returnCycles = false (default), Returns only the final form of the result.
96 | * If returnCycles = true, Returns an object containing information of all cycles.
97 |
98 | ex) returnCycles = false, you can use this function as below form.
99 | const result = simulator(makeInput('sample_input', 'example1.s'), 10000, false)
100 |
101 | ex) returnCycles = true, you can use this function as below form.
102 | interface SimulatorResult {
103 | output: simulatorOutputType;
104 | cycles: simulatorOutputType[];
105 | }
106 |
107 | const result = simulator(
108 | makeInput('sample_input', 'example1.s'),
109 | 10000,
110 | true,
111 | ) as SimulatorResult;
112 |
113 | * output : The object of Register File.
114 |
115 | ex)
116 | [
117 | {
118 | PC: '0x00400000',
119 | registers: {
120 | R0: '0x00000000',
121 | R1: '0x00000000',
122 | ...
123 | R31: '0x00000000'
124 | },
125 | dataSection: {
126 | '0x10000000': '0x00000064',
127 | ...
128 | },
129 | stackSection: {}
130 | }
131 | ]
132 | */
133 | const {dataSectionSize, textSectionSize, binaryText, binaryData} =
134 | makeBinaryObject(assemblyInstructions);
135 |
136 | initializeMem();
137 |
138 | const CYCLES: simulatorOutputType[] = new Array();
139 | const INST_INFO = initialize(
140 | binaryText.concat(binaryData),
141 | textSectionSize,
142 | dataSectionSize,
143 | );
144 |
145 | const result = await mainProcess(INST_INFO, cycleNum, CYCLES);
146 |
147 | return new Promise((resolve, reject) => {
148 | try {
149 | const output: ISimulatorOutput = {result, history: null};
150 | if (returnHistory) output.history = CYCLES;
151 | resolve(output);
152 | } catch (error) {
153 | reject(error);
154 | }
155 | });
156 | }
157 |
--------------------------------------------------------------------------------
/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "clearMocks": true,
3 | "verbose": true,
4 | "collectCoverage": true,
5 | "modulePathIgnorePatterns": ["dist"],
6 | "moduleNameMapper": {
7 | "@root(.*)$": "/$1",
8 | "@src(.*)$": "/src$1",
9 | "@utils(.*)$": "/src/utils$1",
10 | "@simulator(.*)$": "/src/simulator$1"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mips-simulator-js",
3 | "version": "2.1.11",
4 | "description": "MIPS Simulator npm package",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.js",
7 | "types": "./dist/index.d.js",
8 | "scripts": {
9 | "build": "rm -rf ./dist/ && tsc",
10 | "lint": "./node_modules/.bin/eslint .",
11 | "test": "jest",
12 | "pre-test": "npm run build && npm run lint && npm run test",
13 | "publish-npm": "npm run pre-test && npm publish"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/mipsSimulatorUNIST/simulator.git"
18 | },
19 | "author": "mipsSimulatorUNIST",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/mipsSimulatorUNIST/simulator/issues"
23 | },
24 | "homepage": "https://github.com/mipsSimulatorUNIST/simulator#readme",
25 | "keywords": [
26 | "simulator",
27 | "mips",
28 | "assembler"
29 | ],
30 | "devDependencies": {
31 | "@babel/preset-env": "^7.20.2",
32 | "@babel/preset-typescript": "^7.18.6",
33 | "@types/jest": "^29.2.5",
34 | "@types/node": "^18.11.18",
35 | "@typescript-eslint/eslint-plugin": "^5.52.0",
36 | "@typescript-eslint/parser": "^5.52.0",
37 | "babel-jest": "^29.3.1",
38 | "eslint": "^8.31.0",
39 | "eslint-config-prettier": "^8.6.0",
40 | "eslint-import-resolver-typescript": "^3.5.3",
41 | "eslint-plugin-import": "^2.27.5",
42 | "eslint-plugin-prettier": "^4.2.1",
43 | "jest": "^29.3.1",
44 | "prettier": "^2.8.2",
45 | "tsc-alias": "^1.8.2",
46 | "typescript": "^4.9.4"
47 | },
48 | "dependencies": {
49 | "ts-node": "^10.9.1"
50 | },
51 | "browser": {
52 | "fs": false,
53 | "path": false,
54 | "process": false
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/sample_input/example01.o:
--------------------------------------------------------------------------------
1 | 000000000000000000000000010110000000000000000000000000000000110000000010001000001000100000100100000000100100000010010000001001000011110000001000000100000000000000111100000010010001000000000000001101010010100100000000000001000000000101000000010100000010010000000001011000000101100000100100001000100011000100000000000000010010000101101011000000000000000100000001001000000100100000100101000101010110100011111111111111000010001001010010000000000000001000100001011010110000000000000001000000000001000110010000010000000000000000010010100010000100001000000010001100101001100000100100000101010110100111111111111110100000000010111111001010000010000000000010001100101000000000100111000100010100100000000000000000010000100000010000000000000000011000110110000100001111000011110000000000000000000000000000011001000000000000000000000000001100100000010010001101000101011001111000
--------------------------------------------------------------------------------
/sample_input/example1.s:
--------------------------------------------------------------------------------
1 | .data
2 | data1: .word 100
3 | data2: .word 200
4 | data3: .word 0x12345678
5 | .text
6 | main:
7 | and $17, $17, $0
8 | and $18, $18, $0
9 | la $8, data1
10 | la $9, data2
11 | and $10, $10, $0
12 | lab1:
13 | and $11, $11, $0
14 | lab2:
15 | addi $17, $17, 0x1
16 | addi $11, $11, 0x1
17 | or $9, $9, $0
18 | bne $11, $8, lab2
19 | lab3:
20 | addi $18, $18, 0x2
21 | addi $11, $11, 1
22 | sll $18, $17, 1
23 | srl $17, $18, 1
24 | and $19, $17, $18
25 | bne $11, $9, lab3
26 | lab4:
27 | add $5, $5, $31
28 | nor $16, $17, $18
29 | beq $10, $8, lab5
30 | j lab1
31 | lab5:
32 | ori $16, $16, 0xf0f0
--------------------------------------------------------------------------------
/sample_input/example2.s:
--------------------------------------------------------------------------------
1 | .data
2 | array: .word 3
3 | .word 123
4 | .word 4346
5 | array2: .word 0x11111111
6 | .text
7 | main:
8 | addiu $2, $0, 1024
9 | addu $3, $2, $2
10 | or $4, $3, $2
11 | sll $6, $5, 16
12 | addiu $7, $6, 9999
13 | subu $8, $7, $2
14 | nor $9, $4, $3
15 | ori $10, $2, 255
16 | srl $11, $6, 5
17 | la $4, array2
18 | and $13, $11, $5
19 | andi $14, $4, 100
20 | lui $17, 100
21 | addiu $2, $0, 0xa
--------------------------------------------------------------------------------
/sample_input/example3.s:
--------------------------------------------------------------------------------
1 | .data
2 | data1: .word 100
3 | data2: .word 200
4 | data3: .word 0x12345678
5 | .word 0x12341
6 | .text
7 | main:
8 | lui $3, 0x1000
9 | lhu $5, 0($3)
10 | lhu $8, 4($3)
11 | lw $9, 8($3)
12 | lw $10, 12($3)
13 | addiu $5, $5, 24
14 | addiu $6, $0, 124
15 | addu $7, $5, $6
16 | sh $5, 16($3)
17 | sh $6, 20($3)
18 | sh $7, 24($3)
19 | addiu $3, $3, 12
20 | lw $12, -4($3)
21 | lhu $13, -8($3)
22 | lhu $14, -12($3)
--------------------------------------------------------------------------------
/sample_input/example4.s:
--------------------------------------------------------------------------------
1 | .data
2 | .text
3 | main:
4 | addu $2, $4, $5
5 | addu $2, $6, $7
6 | subu $9, $3, $2
7 | lab1:
8 | and $11, $11, $0
9 | addiu $10, $10, 0x1
10 | or $6, $6, $0
11 | jal lab3
12 | lab3:
13 | sll $7, $6, 2
14 | srl $5, $4, 2
15 | sltiu $9, $10, 100
16 | beq $9, $0, lab4
17 | jr $31
18 | lab4:
19 | sltu $4, $2, $3
20 | bne $4, $0, lab5
21 | j lab1
22 | lab5:
23 | ori $16, $16, 0xf0f0
--------------------------------------------------------------------------------
/sample_input/example5.s:
--------------------------------------------------------------------------------
1 | .data
2 | data1: .word 0x12c
3 | data2: .word 0xc8
4 | .text
5 | main:
6 | and $10, $10, $0
7 | and $11, $11, $0
8 | la $8, data1
9 | la $9, data2
10 | addiu $10, $10, 0x1
11 | sll $10, $10, 1
12 | sll $11, $11, 1
13 | loop:
14 | addiu $10, $10, 0x1
15 | addiu $11, $11, 1
16 | or $9, $9, $0
17 | subu $18, $18, $10
18 | sll $18, $17, 1
19 | sll $17, $18, 1
20 | addu $11, $11, $31
21 | nor $16, $17, $18
22 | bne $11, $8, loop
23 | j exit
24 | exit:
25 | andi $15, $15, 0x0f
--------------------------------------------------------------------------------
/sample_input/example6.s:
--------------------------------------------------------------------------------
1 | .data
2 | sum: .word 0x1000000
3 | data1: .word 0x10
4 | data2: .word 0x100
5 | iter: .word 0x1
6 | .text
7 | main:
8 | addiu $29, $29, -8
9 | sw $30, 4($29)
10 | addu $30, $29, $0
11 | j loop1
12 | loop3:
13 | la $2, sum
14 | slti $2, $2, 3
15 | bne $2, $0, loop2
16 | la $2, sum
17 | srl $3, $2, 1
18 | sw $2, 0($2)
19 | loop2:
20 | la $2, iter
21 | addiu $3, $2, 1
22 | sw $3, 0($2)
23 | loop1:
24 | la $2, iter
25 | addi $28, $0, 10
26 | slt $2, $2, $28
27 | bne $2, $0, loop3
28 | addu $2, $2, $0
29 | move $29, $30
30 | lw $30, 4($29)
31 | addiu $29, $29, 8
--------------------------------------------------------------------------------
/sample_input/example7.s:
--------------------------------------------------------------------------------
1 | .data
2 | data1: .word 1
3 | data2: .word 10
4 | .text
5 | main:
6 | addiu $29, $29, -24
7 | sw $30, 20($29)
8 | addu $30, $29, $0
9 | sw $0, 8($30)
10 | loop1:
11 | lw $2, 8($30)
12 | sltiu $2, $2, 20
13 | beq $2, $0, loop2
14 | lui $4, 0x1000
15 | lw $2, 0($4)
16 | sw $2, 12($30)
17 | lw $3, 4($4)
18 | sw $3, 0($4)
19 | lw $3, 12($30)
20 | sw $3, 4($4)
21 | lw $2, 8($30)
22 | addiu $2, $2, 1
23 | sw $2, 8($30)
24 | j loop1
25 | loop2:
26 | addu $2, $2, $0
27 | addu $29, $30, $0
28 | lw $30, 20($29)
29 | addiu $29, $29, 24
--------------------------------------------------------------------------------
/sample_output/example1.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000001011000
2 | 00000000000000000000000000001100
3 | 00000010001000001000100000100100
4 | 00000010010000001001000000100100
5 | 00111100000010000001000000000000
6 | 00111100000010010001000000000000
7 | 00110101001010010000000000000100
8 | 00000001010000000101000000100100
9 | 00000001011000000101100000100100
10 | 00100010001100010000000000000001
11 | 00100001011010110000000000000001
12 | 00000001001000000100100000100101
13 | 00010101011010001111111111111100
14 | 00100010010100100000000000000010
15 | 00100001011010110000000000000001
16 | 00000000000100011001000001000000
17 | 00000000000100101000100001000010
18 | 00000010001100101001100000100100
19 | 00010101011010011111111111111010
20 | 00000000101111110010100000100000
21 | 00000010001100101000000000100111
22 | 00010001010010000000000000000001
23 | 00001000000100000000000000000110
24 | 00110110000100001111000011110000
25 | 00000000000000000000000001100100
26 | 00000000000000000000000011001000
27 | 00010010001101000101011001111000
28 |
--------------------------------------------------------------------------------
/sample_output/example2.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000000111100
2 | 00000000000000000000000000010000
3 | 00100100000000100000010000000000
4 | 00000000010000100001100000100001
5 | 00000000011000100010000000100101
6 | 00000000000001010011010000000000
7 | 00100100110001110010011100001111
8 | 00000000111000100100000000100011
9 | 00000000100000110100100000100111
10 | 00110100010010100000000011111111
11 | 00000000000001100101100101000010
12 | 00111100000001000001000000000000
13 | 00110100100001000000000000001100
14 | 00000001011001010110100000100100
15 | 00110000100011100000000001100100
16 | 00111100000100010000000001100100
17 | 00100100000000100000000000001010
18 | 00000000000000000000000000000011
19 | 00000000000000000000000001111011
20 | 00000000000000000001000011111010
21 | 00010001000100010001000100010001
22 |
--------------------------------------------------------------------------------
/sample_output/example3.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000000111100
2 | 00000000000000000000000000010000
3 | 00111100000000110001000000000000
4 | 10010100011001010000000000000000
5 | 10010100011010000000000000000100
6 | 10001100011010010000000000001000
7 | 10001100011010100000000000001100
8 | 00100100101001010000000000011000
9 | 00100100000001100000000001111100
10 | 00000000101001100011100000100001
11 | 10100100011001010000000000010000
12 | 10100100011001100000000000010100
13 | 10100100011001110000000000011000
14 | 00100100011000110000000000001100
15 | 10001100011011001111111111111100
16 | 10010100011011011111111111111000
17 | 10010100011011101111111111110100
18 | 00000000000000000000000001100100
19 | 00000000000000000000000011001000
20 | 00010010001101000101011001111000
21 | 00000000000000010010001101000001
22 |
--------------------------------------------------------------------------------
/sample_output/example4.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000001000000
2 | 00000000000000000000000000000000
3 | 00000000100001010001000000100001
4 | 00000000110001110001000000100001
5 | 00000000011000100100100000100011
6 | 00000001011000000101100000100100
7 | 00100101010010100000000000000001
8 | 00000000110000000011000000100101
9 | 00001100000100000000000000000111
10 | 00000000000001100011100010000000
11 | 00000000000001000010100010000010
12 | 00101101010010010000000001100100
13 | 00010001001000000000000000000001
14 | 00000011111000000000000000001000
15 | 00000000010000110010000000101011
16 | 00010100100000000000000000000001
17 | 00001000000100000000000000000011
18 | 00110110000100001111000011110000
19 |
--------------------------------------------------------------------------------
/sample_output/example5.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000001001100
2 | 00000000000000000000000000001000
3 | 00000001010000000101000000100100
4 | 00000001011000000101100000100100
5 | 00111100000010000001000000000000
6 | 00111100000010010001000000000000
7 | 00110101001010010000000000000100
8 | 00100101010010100000000000000001
9 | 00000000000010100101000001000000
10 | 00000000000010110101100001000000
11 | 00100101010010100000000000000001
12 | 00100101011010110000000000000001
13 | 00000001001000000100100000100101
14 | 00000010010010101001000000100011
15 | 00000000000100011001000001000000
16 | 00000000000100101000100001000000
17 | 00000001011111110101100000100001
18 | 00000010001100101000000000100111
19 | 00010101011010001111111111110111
20 | 00001000000100000000000000010010
21 | 00110001111011110000000000001111
22 | 00000000000000000000000100101100
23 | 00000000000000000000000011001000
24 |
--------------------------------------------------------------------------------
/sample_output/example6.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000001011100
2 | 00000000000000000000000000010000
3 | 00100111101111011111111111111000
4 | 10101111101111100000000000000100
5 | 00000011101000001111000000100001
6 | 00001000000100000000000000001110
7 | 00111100000000100001000000000000
8 | 00101000010000100000000000000011
9 | 00010100010000000000000000000011
10 | 00111100000000100001000000000000
11 | 00000000000000100001100001000010
12 | 10101100010000100000000000000000
13 | 00111100000000100001000000000000
14 | 00110100010000100000000000001100
15 | 00100100010000110000000000000001
16 | 10101100010000110000000000000000
17 | 00111100000000100001000000000000
18 | 00110100010000100000000000001100
19 | 00100000000111000000000000001010
20 | 00000000010111000001000000101010
21 | 00010100010000001111111111110001
22 | 00000000010000000001000000100001
23 | 00000011110000001110100000100000
24 | 10001111101111100000000000000100
25 | 00100111101111010000000000001000
26 | 00000001000000000000000000000000
27 | 00000000000000000000000000010000
28 | 00000000000000000000000100000000
29 | 00000000000000000000000000000001
30 |
--------------------------------------------------------------------------------
/sample_output/example7.o:
--------------------------------------------------------------------------------
1 | 00000000000000000000000001011000
2 | 00000000000000000000000000001000
3 | 00100111101111011111111111101000
4 | 10101111101111100000000000010100
5 | 00000011101000001111000000100001
6 | 10101111110000000000000000001000
7 | 10001111110000100000000000001000
8 | 00101100010000100000000000010100
9 | 00010000010000000000000000001011
10 | 00111100000001000001000000000000
11 | 10001100100000100000000000000000
12 | 10101111110000100000000000001100
13 | 10001100100000110000000000000100
14 | 10101100100000110000000000000000
15 | 10001111110000110000000000001100
16 | 10101100100000110000000000000100
17 | 10001111110000100000000000001000
18 | 00100100010000100000000000000001
19 | 10101111110000100000000000001000
20 | 00001000000100000000000000000100
21 | 00000000010000000001000000100001
22 | 00000011110000001110100000100001
23 | 10001111101111100000000000010100
24 | 00100111101111010000000000011000
25 | 00000000000000000000000000000001
26 | 00000000000000000000000000001010
27 |
--------------------------------------------------------------------------------
/simulator_sample_output/example01.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x00400020
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x00000000
8 | R3: 0x00000000
9 | R4: 0x00000000
10 | R5: 0x00000000
11 | R6: 0x00000000
12 | R7: 0x00000000
13 | R8: 0x10000000
14 | R9: 0x10000004
15 | R10: 0x00000000
16 | R11: 0x000009c2
17 | R12: 0x00000000
18 | R13: 0x00000000
19 | R14: 0x00000000
20 | R15: 0x00000000
21 | R16: 0x00000000
22 | R17: 0x000009c3
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x00000000
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00000000
37 |
38 | Data section
39 | 0x10000000: 0x00000064
40 | 0x10000004: 0x000000c8
41 | 0x10000008: 0x12345678
42 | 0x1000000c: 0x00000000
43 |
44 |
45 |
--------------------------------------------------------------------------------
/simulator_sample_output/example02.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x0040003c
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x0000000a
8 | R3: 0x00000800
9 | R4: 0x1000000c
10 | R5: 0x00000000
11 | R6: 0x00000000
12 | R7: 0x0000270f
13 | R8: 0x0000230f
14 | R9: 0xfffff3ff
15 | R10: 0x000004ff
16 | R11: 0x00000000
17 | R12: 0x00000000
18 | R13: 0x00000000
19 | R14: 0x00000004
20 | R15: 0x00000000
21 | R16: 0x00000000
22 | R17: 0x00640000
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x00000000
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00000000
37 |
38 | Data section
39 | 0x10000000: 0x00000003
40 | 0x10000004: 0x0000007b
41 | 0x10000008: 0x000010fa
42 | 0x1000000c: 0x11111111
43 | 0x10000010: 0x00000000
44 |
45 |
46 |
--------------------------------------------------------------------------------
/simulator_sample_output/example03.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x0040003c
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x00000000
8 | R3: 0x1000000c
9 | R4: 0x00000000
10 | R5: 0x0000007c
11 | R6: 0x0000007c
12 | R7: 0x000000f8
13 | R8: 0x000000c8
14 | R9: 0x12345678
15 | R10: 0x00012341
16 | R11: 0x00000000
17 | R12: 0x12345678
18 | R13: 0x000000c8
19 | R14: 0x00000064
20 | R15: 0x00000000
21 | R16: 0x00000000
22 | R17: 0x00000000
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x00000000
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00000000
37 |
38 | Data section
39 | 0x10000000: 0x00000064
40 | 0x10000004: 0x000000c8
41 | 0x10000008: 0x12345678
42 | 0x1000000c: 0x00012341
43 | 0x10000010: 0x0000007c
44 | 0x10000014: 0x0000007c
45 | 0x10000018: 0x000000f8
46 |
47 |
48 |
--------------------------------------------------------------------------------
/simulator_sample_output/example04.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x00400020
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x00000000
8 | R3: 0x00000000
9 | R4: 0x00000000
10 | R5: 0x00000000
11 | R6: 0x00000000
12 | R7: 0x00000000
13 | R8: 0x00000000
14 | R9: 0x00000001
15 | R10: 0x00000001
16 | R11: 0x00000000
17 | R12: 0x00000000
18 | R13: 0x00000000
19 | R14: 0x00000000
20 | R15: 0x00000000
21 | R16: 0x00000000
22 | R17: 0x00000000
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x00000000
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00400020
37 |
38 |
--------------------------------------------------------------------------------
/simulator_sample_output/example05.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x00400028
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x00000000
8 | R3: 0x00000000
9 | R4: 0x00000000
10 | R5: 0x00000000
11 | R6: 0x00000000
12 | R7: 0x00000000
13 | R8: 0x10000000
14 | R9: 0x10000004
15 | R10: 0x00000459
16 | R11: 0x00000457
17 | R12: 0x00000000
18 | R13: 0x00000000
19 | R14: 0x00000000
20 | R15: 0x00000000
21 | R16: 0xffffffff
22 | R17: 0x00000000
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x00000000
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00000000
37 |
38 | Data section
39 | 0x10000000: 0x0000012c
40 | 0x10000004: 0x000000c8
41 | 0x10000008: 0x00000000
42 |
43 |
44 |
--------------------------------------------------------------------------------
/simulator_sample_output/example06.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x0040005c
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x00000000
8 | R3: 0x00000000
9 | R4: 0x00000000
10 | R5: 0x00000000
11 | R6: 0x00000000
12 | R7: 0x00000000
13 | R8: 0x00000000
14 | R9: 0x00000000
15 | R10: 0x00000000
16 | R11: 0x00000000
17 | R12: 0x00000000
18 | R13: 0x00000000
19 | R14: 0x00000000
20 | R15: 0x00000000
21 | R16: 0x00000000
22 | R17: 0x00000000
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x0000000a
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00000000
37 |
38 | Data section
39 | 0x10000000: 0x01000000
40 | 0x10000004: 0x00000010
41 | 0x10000008: 0x00000100
42 | 0x1000000c: 0x00000001
43 | 0x10000010: 0x00000000
44 |
45 |
46 | Stack section
47 | 0x7ffffffc: 0x00000000
48 |
49 |
50 |
--------------------------------------------------------------------------------
/simulator_sample_output/example07.o:
--------------------------------------------------------------------------------
1 | Program Counter
2 | PC: 0x00400058
3 |
4 | Registers
5 | R0: 0x00000000
6 | R1: 0x00000000
7 | R2: 0x00000000
8 | R3: 0x0000000a
9 | R4: 0x10000000
10 | R5: 0x00000000
11 | R6: 0x00000000
12 | R7: 0x00000000
13 | R8: 0x00000000
14 | R9: 0x00000000
15 | R10: 0x00000000
16 | R11: 0x00000000
17 | R12: 0x00000000
18 | R13: 0x00000000
19 | R14: 0x00000000
20 | R15: 0x00000000
21 | R16: 0x00000000
22 | R17: 0x00000000
23 | R18: 0x00000000
24 | R19: 0x00000000
25 | R20: 0x00000000
26 | R21: 0x00000000
27 | R22: 0x00000000
28 | R23: 0x00000000
29 | R24: 0x00000000
30 | R25: 0x00000000
31 | R26: 0x00000000
32 | R27: 0x00000000
33 | R28: 0x00000000
34 | R29: 0x80000000
35 | R30: 0x00000000
36 | R31: 0x00000000
37 |
38 | Data section
39 | 0x10000000: 0x00000001
40 | 0x10000004: 0x0000000a
41 | 0x10000008: 0x00000000
42 |
43 |
44 | Stack section
45 | 0x7ffffff4: 0x0000000a
46 | 0x7ffffff8: 0x00000000
47 | 0x7ffffffc: 0x00000000
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/simulator/assembler.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BYTES_PER_WORD,
3 | instT,
4 | instList,
5 | MEM_DATA_START,
6 | MEM_TEXT_START,
7 | section,
8 | symbolT,
9 | SYMBOL_TABLE,
10 | resetSymbolTable,
11 | } from '@utils/constants';
12 | import {numToBits, symbolTableAddEntry, toHexAndPad} from '@utils/functions';
13 |
14 | export const makeSymbolTable = (inputs: string[]) => {
15 | /*
16 | * make symbol table from assembly file
17 | * using SYMBOL_TABLE in constants.ts
18 | *
19 | * 'dataSeg'에 data 저장
20 | * 'textSeg'에 text 저장
21 | *
22 | * .text
23 | * - indicates that following items are stored in the user text segment, typically instructions
24 | * - It always starts from 0x400000 (MEM_TEXT_START)
25 |
26 | * .data
27 | * - indicates that following data items are stored in the data segment
28 | * - It always starts from 0x10000000 (MEM_DATA_START)
29 | *
30 | * return
31 | * {
32 | * dataSeg : dataSeg,
33 | * textSeg : textSeg,
34 | * dataSectionSize : dataSectionSize,
35 | * textSectionSize : textSectionSize
36 | * }
37 | *
38 | * [USAGE EXAMPLE]
39 | * const {dataSeg, textSeg, dataSectionSize, textSectionSize} = makeSymbolTable(inputs);
40 | */
41 | resetSymbolTable();
42 | let address = 0;
43 | let curSection = section.MAX_SIZE;
44 |
45 | let dataSectionSize = 0;
46 | let textSectionSize = 0;
47 |
48 | const dataSeg: string[] = [];
49 | const textSeg: string[] = [];
50 |
51 | inputs.forEach((input: string) => {
52 | const splited: string[] = input.split('\t').filter(s => s !== ''); // ex. ['array:', '.word', '3']
53 | const symbol: symbolT = new symbolT();
54 |
55 | if (splited[0] == '.data') {
56 | curSection = section.DATA;
57 | address = MEM_DATA_START;
58 | return;
59 | } else if (splited[0] == '.text') {
60 | curSection = section.TEXT;
61 | address = MEM_TEXT_START;
62 | return;
63 | } else if (curSection === section.DATA) {
64 | if (splited.length === 2) {
65 | // ex. ['.word','123']
66 | dataSeg.push(splited[1]);
67 | } else {
68 | // ex. ['array:', '.word', '3']
69 | symbol.address = address;
70 | symbol.name = splited[0].replace(':', '');
71 | symbolTableAddEntry(symbol);
72 | dataSeg.push(splited[2]);
73 | }
74 |
75 | dataSectionSize += BYTES_PER_WORD;
76 | } else if (curSection === section.TEXT) {
77 | if (splited.length === 1) {
78 | // ex. ['main:']
79 | symbol.name = splited[0].replace(':', '');
80 | symbol.address = address;
81 | symbolTableAddEntry(symbol);
82 | return;
83 | } else {
84 | // ex. ['and', '$17, $17, $0']
85 | const name: string = splited[0];
86 | textSeg.push(input); // ex. 'and $17, $17, $0'
87 |
88 | if (name === 'la') {
89 | const targetSymbol: string = splited[1].split(' ')[1]; // ex. 'data1'
90 | const targetAddress: string = toHexAndPad(SYMBOL_TABLE[targetSymbol]);
91 | if (targetAddress.slice(4) !== '0000') {
92 | textSectionSize += BYTES_PER_WORD;
93 | address += BYTES_PER_WORD;
94 | }
95 | }
96 | }
97 | textSectionSize += BYTES_PER_WORD;
98 | }
99 |
100 | address += BYTES_PER_WORD;
101 | });
102 |
103 | return {dataSeg, textSeg, dataSectionSize, textSectionSize};
104 | };
105 |
106 | export function recordTextSection(textSeg: string[]): [string[], number[][]] {
107 | /*
108 | * parameter로 textSeg를 받는다.
109 | * textSeg 있는 text들 한 줄 씩 체크해서 binaryText 리스트에 바이너리 문장으로 추가
110 | * 명령어 타입별(R, I, J)로 명령어 이름별로 묶어서 번역
111 | *
112 | * binaryText 이라는 list에 명령어를 번역한 binary 문장을 한 줄씩 추가
113 | * return binaryText
114 | * binaryText: ['00000000000000000000000001011000', '00000000000000000000000000001100']
115 | */
116 |
117 | let curAddress: number = MEM_TEXT_START;
118 | let instruct: string[];
119 | let address: number;
120 | let rs: string;
121 | let rt: string;
122 | let rd: string;
123 | let imm: string;
124 | let shamt: string;
125 | let immReg: string;
126 | const binaryText: string[] = [];
127 | const mappingTable: number[][] = [];
128 |
129 | let binaryInstructionCounter = 0;
130 |
131 | textSeg.forEach((text, i) => {
132 | mappingTable.push([]);
133 | instruct = text.slice(1).replace(/ /g, '').split(/,|\t/);
134 | const opName: string = instruct[0];
135 |
136 | if (opName === 'la') {
137 | address = SYMBOL_TABLE[instruct[2]];
138 | rt = numToBits(Number(instruct[1].replace('$', '')), 5);
139 | imm = numToBits(parseInt(address.toString(16).slice(0, 4), 16), 16);
140 | binaryText.push('001111' + '00000' + rt + imm);
141 |
142 | if (address.toString(16).slice(4, 8) !== '0000') {
143 | imm = numToBits(parseInt(address.toString(16).slice(4, 8), 16), 16);
144 | binaryText.push('001101' + rt + rt + imm);
145 | curAddress += BYTES_PER_WORD;
146 |
147 | // if two binary instructions are made by la instruction, then it should map 'current assembly instruction' to 'two binary instructions'
148 | mappingTable[i].push(binaryInstructionCounter);
149 | binaryInstructionCounter++;
150 | }
151 | } else if (opName === 'move') {
152 | //op = ADD op "000000"
153 | rs = numToBits(Number(instruct[2].replace('$', '')), 5);
154 | rt = '00000';
155 | rd = numToBits(Number(instruct[1].replace('$', '')), 5);
156 | shamt = '00000';
157 | binaryText.push('000000' + rs + rt + rd + shamt + '100000'); //funct = "100000"
158 | } else {
159 | const opInfo: instT = instList[opName];
160 | if (opInfo.type === 'R') {
161 | if (opInfo.name === 'sll' || opInfo.name === 'srl') {
162 | rs = '00000';
163 | rt = numToBits(Number(instruct[2].replace('$', '')), 5);
164 | rd = numToBits(Number(instruct[1].replace('$', '')), 5);
165 | shamt = numToBits(Number(instruct[3]), 5);
166 | } else if (opInfo.name === 'jr') {
167 | rs = numToBits(Number(instruct[1].replace('$', '')), 5);
168 | rt = '00000';
169 | rd = '00000';
170 | shamt = '00000';
171 | } else {
172 | rs = numToBits(Number(instruct[2].replace('$', '')), 5);
173 | rt = numToBits(Number(instruct[3].replace('$', '')), 5);
174 | rd = numToBits(Number(instruct[1].replace('$', '')), 5);
175 | shamt = '00000';
176 | }
177 | binaryText.push(opInfo.op + rs + rt + rd + shamt + opInfo.funct);
178 | } else if (opInfo.type === 'I') {
179 | if (opInfo.name === 'lui') {
180 | rt = numToBits(Number(instruct[1].replace('$', '')), 5);
181 | rs = '00000';
182 | imm =
183 | instruct[2].slice(0, 2) === '0x'
184 | ? numToBits(parseInt(instruct[2].slice(2), 16), 16)
185 | : numToBits(Number(instruct[2]), 16);
186 | } else if (opInfo.name === 'beq' || opInfo.name === 'bne') {
187 | imm = numToBits(
188 | Number((SYMBOL_TABLE[instruct[3]] - (curAddress + 4)) / 4),
189 | 16,
190 | );
191 | rs = numToBits(Number(instruct[1].replace('$', '')), 5);
192 | rt = numToBits(Number(instruct[2].replace('$', '')), 5);
193 | } else if (
194 | opInfo.name === 'lw' ||
195 | opInfo.name === 'lhu' ||
196 | opInfo.name === 'sw' ||
197 | opInfo.name === 'sh'
198 | ) {
199 | immReg = instruct[2].split('(')[1].split(')')[0];
200 | rs = numToBits(Number(immReg.replace('$', '')), 5);
201 | rt = numToBits(Number(instruct[1].replace('$', '')), 5);
202 | imm = numToBits(Number(instruct[2].split('(')[0]), 16);
203 | } else {
204 | rs = numToBits(Number(instruct[2].replace('$', '')), 5);
205 | rt = numToBits(Number(instruct[1].replace('$', '')), 5);
206 |
207 | imm =
208 | instruct[3].slice(0, 2) === '0x'
209 | ? numToBits(parseInt(instruct[3].slice(2), 16), 16)
210 | : numToBits(Number(instruct[3]), 16);
211 | }
212 | binaryText.push(opInfo.op + rs + rt + imm);
213 | } else if (opInfo.type === 'J') {
214 | address = Number(SYMBOL_TABLE[instruct[1]]) / 4;
215 | binaryText.push(opInfo.op + numToBits(address, 26));
216 | }
217 | }
218 | curAddress += BYTES_PER_WORD;
219 |
220 | mappingTable[i].push(binaryInstructionCounter);
221 | binaryInstructionCounter++;
222 | });
223 |
224 | return [binaryText, mappingTable];
225 | }
226 |
227 | export function recordDataSection(dataSeg: string[]): string[] {
228 | /*
229 | * input값을 dataSeg를 받는다.
230 | * dataSeg에 있는 data들 한 줄 씩 체크해서 binaryData 리스트에 바이너리 문장으로 추가
231 | * data값을 그대로 binary 문자로 번역
232 | *
233 | * binaryData 이라는 list에 명령어를 번역한 binary 문장을 한 줄씩 추가
234 | * return binaryData
235 | * ex) binaryData: ['00000010001000001000100000100100', '00000010010000001001000000100100']
236 | */
237 |
238 | let dataNum: number;
239 | const binaryData: string[] = [];
240 | for (const data of dataSeg) {
241 | dataNum =
242 | data.slice(0, 2) === '0x' ? parseInt(data.slice(2), 16) : Number(data);
243 | binaryData.push(numToBits(dataNum));
244 | }
245 | return binaryData;
246 | }
247 |
248 | export function makeBinaryObject(inputs: string[]) {
249 | const {dataSeg, textSeg, dataSectionSize, textSectionSize} =
250 | makeSymbolTable(inputs);
251 | const [binaryText, mappingTable]: [string[], number[][]] =
252 | recordTextSection(textSeg);
253 | const binaryData: string[] = recordDataSection(dataSeg);
254 |
255 | return {
256 | dataSectionSize,
257 | textSectionSize,
258 | binaryText,
259 | binaryData,
260 | mappingTable,
261 | dataSeg,
262 | textSeg,
263 | };
264 | }
265 |
266 | export function makeBinaryArray(
267 | dataSectionSize: number,
268 | textSectionSize: number,
269 | binaryText: string[],
270 | binaryData: string[],
271 | ): string[] {
272 | const output: string[] = [
273 | numToBits(textSectionSize, 32),
274 | numToBits(dataSectionSize, 32),
275 | ...binaryText,
276 | ...binaryData,
277 | ];
278 |
279 | return output;
280 | }
281 |
282 | export function makeBinaryString(
283 | dataSectionSize: number,
284 | textSectionSize: number,
285 | binaryText: string[],
286 | binaryData: string[],
287 | ): string {
288 | /*
289 | * output에 text 문장 개수를 binary로 번역해서 추가
290 | * output에 data 개수를 binary로 번역해서 추가
291 | *
292 | */
293 | const binarySize: string[] = [
294 | numToBits(textSectionSize, 32),
295 | numToBits(dataSectionSize, 32),
296 | ];
297 | let output = '';
298 |
299 | binarySize.concat(binaryText, binaryData).map(binaryLine => {
300 | output += `${binaryLine}\n`;
301 | });
302 |
303 | return output;
304 | }
305 |
--------------------------------------------------------------------------------
/src/simulator/run.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BYTES_PER_WORD,
3 | currentState,
4 | changeRunBit,
5 | instruction,
6 | MEM_TEXT_START,
7 | NUM_INST,
8 | } from '@utils/constants';
9 | import {getInstInfo, memRead, memWrite, memWriteHalf} from '@utils/functions';
10 |
11 | export function OPCODE(INST: instruction): number {
12 | return INST.opcode;
13 | }
14 |
15 | export function setOPCODE(INST: instruction, VAL: number): void {
16 | INST.opcode = VAL;
17 | }
18 |
19 | export function FUNC(INST: instruction): number {
20 | return INST.funcCode;
21 | }
22 |
23 | export function setFUNC(INST: instruction, VAL: number): void {
24 | INST.funcCode = VAL;
25 | }
26 |
27 | export function RS(INST: instruction): number {
28 | return INST.rs;
29 | }
30 |
31 | export function setRS(INST: instruction, VAL: number): void {
32 | INST.rs = VAL;
33 | }
34 |
35 | export function RT(INST: instruction): number {
36 | return INST.rt;
37 | }
38 |
39 | export function setRT(INST: instruction, VAL: number): void {
40 | INST.rt = VAL;
41 | }
42 |
43 | export function RD(INST: instruction): number {
44 | return INST.rd;
45 | }
46 |
47 | export function setRD(INST: instruction, VAL: number): void {
48 | INST.rd = VAL;
49 | }
50 |
51 | export function SHAMT(INST: instruction): number {
52 | return INST.shamt;
53 | }
54 |
55 | export function setSHAMT(INST: instruction, VAL: number): void {
56 | INST.shamt = VAL;
57 | }
58 |
59 | export function IMM(INST: instruction): number {
60 | return INST.imm;
61 | }
62 |
63 | export function setIMM(INST: instruction, VAL: number): void {
64 | INST.imm = VAL;
65 | }
66 |
67 | export function IOFFSET(INST: instruction): number {
68 | return IMM(INST);
69 | }
70 |
71 | export function TARGET(INST: instruction): number {
72 | return INST.target;
73 | }
74 |
75 | export function setTARGET(INST: instruction, VAL: number): void {
76 | INST.target = VAL;
77 | }
78 |
79 | export function signEX(X: number): number {
80 | if (X & 0x8000) return X | 0xffff0000;
81 | else return X;
82 | }
83 |
84 | export function zeroEX(X: number): number {
85 | return X & 0x0000ffff;
86 | }
87 |
88 | export function jumpINST(TARGET: number): void {
89 | currentState.PC = TARGET;
90 | }
91 |
92 | export function loadINST(LD: number, MASK: number): number {
93 | return LD & MASK;
94 | }
95 |
96 | export function process_instruction() {
97 | /*
98 | * Procedure: process_instruction
99 | * Purpose: Process one instruction
100 | */
101 | const info: instruction = getInstInfo(currentState.PC);
102 |
103 | // R type
104 | if (OPCODE(info) === 0x0) {
105 | const rs: number = RS(info);
106 | const rt: number = RT(info);
107 | const rd: number = RD(info);
108 | const shamt: number = SHAMT(info);
109 | const funcCode: number = FUNC(info);
110 |
111 | // ADD
112 | if (funcCode === 32) {
113 | currentState.REGS[rd] = currentState.REGS[rs] + currentState.REGS[rt];
114 | }
115 | // ADDU
116 | else if (funcCode === 33) {
117 | currentState.REGS[rd] = currentState.REGS[rs] + currentState.REGS[rt];
118 | }
119 | // SUB
120 | else if (funcCode === 34) {
121 | currentState.REGS[rd] = currentState.REGS[rs] - currentState.REGS[rt];
122 | }
123 | // SUBU
124 | else if (funcCode === 35) {
125 | currentState.REGS[rd] = currentState.REGS[rs] - currentState.REGS[rt];
126 | }
127 | // AND
128 | else if (funcCode === 36) {
129 | currentState.REGS[rd] = currentState.REGS[rs] & currentState.REGS[rt];
130 | }
131 | // OR
132 | else if (funcCode === 37) {
133 | currentState.REGS[rd] = currentState.REGS[rs] | currentState.REGS[rt];
134 | }
135 | // NOR
136 | else if (funcCode === 39) {
137 | currentState.REGS[rd] = ~(currentState.REGS[rs] | currentState.REGS[rt]);
138 | }
139 | // SLT
140 | else if (funcCode === 42) {
141 | if (currentState.REGS[rs] < currentState.REGS[rt])
142 | currentState.REGS[rd] = 1;
143 | else currentState.REGS[rd] = 0;
144 | }
145 | // SLTU
146 | else if (funcCode === 43) {
147 | if (currentState.REGS[rs] < currentState.REGS[rt])
148 | currentState.REGS[rd] = 1;
149 | else currentState.REGS[rd] = 0;
150 | }
151 | // SLL
152 | else if (funcCode === 0) {
153 | currentState.REGS[rd] = currentState.REGS[rt] << shamt;
154 | }
155 | // SRL
156 | else if (funcCode === 2) {
157 | currentState.REGS[rd] = currentState.REGS[rt] >> shamt;
158 | }
159 | // JR
160 | else if (funcCode === 8) {
161 | currentState.PC = currentState.REGS[rs];
162 | }
163 |
164 | if (funcCode !== 8) currentState.PC += BYTES_PER_WORD;
165 | }
166 | // J
167 | else if (OPCODE(info) === 0x2) {
168 | const target: number = TARGET(info) << 2;
169 | jumpINST(target);
170 | }
171 | // JAL
172 | else if (OPCODE(info) === 0x3) {
173 | const target: number = TARGET(info) << 2;
174 | currentState.REGS[31] = currentState.PC + 8;
175 | jumpINST(target);
176 | }
177 | // I type
178 | else {
179 | const rs: number = RS(info);
180 | const rt: number = RT(info);
181 | const imm: number = IMM(info);
182 | const opcode: number = OPCODE(info);
183 |
184 | // ADDI
185 | if (opcode === 0x8) {
186 | currentState.REGS[rt] = currentState.REGS[rs] + signEX(imm);
187 | }
188 | // ADDIU
189 | else if (opcode === 0x9) {
190 | currentState.REGS[rt] = currentState.REGS[rs] + signEX(imm);
191 | }
192 | // ANDI
193 | else if (opcode === 0xc) {
194 | currentState.REGS[rt] = currentState.REGS[rs] & zeroEX(imm);
195 | }
196 | // BEQ
197 | else if (opcode === 0x4) {
198 | if (currentState.REGS[rs] === currentState.REGS[rt])
199 | currentState.PC += imm * 4;
200 | }
201 | // BNE
202 | else if (opcode === 0x5) {
203 | if (currentState.REGS[rs] !== currentState.REGS[rt]) {
204 | currentState.PC += signEX(imm) * 4;
205 | }
206 | }
207 | // LHU
208 | else if (opcode === 0x25) {
209 | currentState.REGS[rt] = loadINST(
210 | memRead(currentState.REGS[rs] + signEX(IOFFSET(info))),
211 | 0x0000ffff,
212 | );
213 | }
214 | // LUI
215 | else if (opcode === 0xf) {
216 | currentState.REGS[rt] = imm << 16;
217 | }
218 | // LW
219 | else if (opcode == 0x23) {
220 | currentState.REGS[rt] = memRead(
221 | currentState.REGS[rs] + signEX(IOFFSET(info)),
222 | );
223 | }
224 | // ORI
225 | else if (opcode === 0xd) {
226 | currentState.REGS[rt] = currentState.REGS[rs] | zeroEX(imm);
227 | }
228 | // SLTI
229 | else if (opcode === 0xa) {
230 | if (currentState.REGS[rs] < signEX(imm)) currentState.REGS[rt] = 1;
231 | else currentState.REGS[rt] = 0;
232 | }
233 | // SLTIU
234 | else if (opcode === 0xb) {
235 | if (currentState.REGS[rs] < signEX(imm)) currentState.REGS[rt] = 1;
236 | else currentState.REGS[rt] = 0;
237 | }
238 | // SH
239 | else if (opcode === 0x29) {
240 | memWriteHalf(
241 | currentState.REGS[rs] + signEX(IOFFSET(info)),
242 | currentState.REGS[rt],
243 | );
244 | }
245 | // SW
246 | else if (opcode === 0x2b) {
247 | memWrite(
248 | currentState.REGS[rs] + signEX(IOFFSET(info)),
249 | currentState.REGS[rt],
250 | );
251 | }
252 |
253 | currentState.PC += BYTES_PER_WORD;
254 | }
255 |
256 | if (currentState.PC - MEM_TEXT_START === NUM_INST * 4) {
257 | changeRunBit();
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/src/utils/constants.ts:
--------------------------------------------------------------------------------
1 | import {
2 | initMemory,
3 | initInstInfo,
4 | parseData,
5 | parseInstr,
6 | } from '@utils/functions';
7 |
8 | export interface Iinstruction {
9 | opcode: number;
10 | funcCode: number;
11 | value: number;
12 | target: number;
13 | rs: number;
14 | rt: number;
15 | imm: number;
16 | rd: number;
17 | shamt: number;
18 | encoding: number;
19 | expr: number;
20 | }
21 |
22 | interface IBcolors {
23 | readonly BLUE: string;
24 | readonly YELLOW: string;
25 | readonly GREEN: string;
26 | readonly RED: string;
27 | readonly ENDC: string;
28 | }
29 |
30 | interface ISection {
31 | readonly DATA: number;
32 | readonly TEXT: number;
33 | readonly MAX_SIZE: number;
34 | }
35 |
36 | export interface SymbolTableType {
37 | readonly name: string;
38 | readonly address: number;
39 | }
40 |
41 | export interface IinstList {
42 | readonly [key: string]: instT;
43 | }
44 |
45 | export interface ISYMBOL_TABLE {
46 | [key: string]: number;
47 | }
48 |
49 | export const DEBUG = 0;
50 | export const MAX_SYMBOL_TABLE_SIZE = 1024;
51 |
52 | export const MEM_TEXT_START = 0x00400000;
53 | export const MEM_TEXT_SIZE = 0x00100000;
54 | export const MEM_DATA_START = 0x10000000;
55 | export const MEM_DATA_SIZE = 0x00100000;
56 | export const MEM_STACK_START = 0x80000000;
57 | export const MEM_STACK_SIZE = 0x00100000;
58 |
59 | export const BYTES_PER_WORD = 4;
60 | export const INST_LIST_LEN = 27;
61 |
62 | export const MIPS_REGS = 32;
63 | export const MEM_GROW_UP = 1;
64 | export const MEM_GROW_DOWN = -1;
65 | export const MEM_NREGIONS = 3;
66 | export const DEBUG_SET = 0;
67 | export const MEM_DUMP_SET = 1;
68 |
69 | export let RUN_BIT = 1;
70 | export let INSTRUCTION_COUNT = 0;
71 |
72 | /*
73 | Main memory
74 | memory will be dynamically allocated at initialization
75 | */
76 | export let memText: memRegionT;
77 | export let memData: memRegionT;
78 | export let memStack: memRegionT;
79 | export let memRegions: memRegionT[];
80 | export let currentState: cpuState;
81 | export let NUM_INST: number;
82 |
83 | export const INST_INFO: Iinstruction[] = [];
84 |
85 | export const section: ISection = {
86 | DATA: 0,
87 | TEXT: 1,
88 | MAX_SIZE: 2,
89 | };
90 |
91 | export const bcolors: IBcolors = {
92 | BLUE: '\x1B[34m',
93 | YELLOW: '\x1B[33m',
94 | GREEN: '\x1B[32m',
95 | RED: '\x1B[31m',
96 | ENDC: '\x1B[0m',
97 | };
98 |
99 | const start = `[${bcolors.BLUE}START${bcolors.ENDC}] `;
100 | const done = `[${bcolors.YELLOW}DONE${bcolors.ENDC}] `;
101 | const success = `[${bcolors.GREEN}SUCCESS${bcolors.ENDC}] `;
102 | const error = `[${bcolors.RED}ERROR${bcolors.ENDC}] `;
103 |
104 | export const pType: string[] = [start, done, success, error];
105 | // Structure Declaration
106 | export class instT {
107 | name: string;
108 | type: string;
109 | op: string;
110 | funct: string;
111 |
112 | constructor(name: string, op: string, type: string, funct: string) {
113 | this.name = name;
114 | this.op = op;
115 | this.type = type;
116 | this.funct = funct;
117 | }
118 | }
119 |
120 | export class symbolT {
121 | name: string;
122 | address: number;
123 |
124 | constructor() {
125 | this.name = '';
126 | this.address = 0;
127 | }
128 | }
129 |
130 | export class laStruct {
131 | op: string;
132 | rt: string;
133 | imm: string;
134 |
135 | constructor(op: string, rt: string, imm: string) {
136 | this.op = op;
137 | this.rt = rt;
138 | this.imm = imm;
139 | }
140 | }
141 |
142 | export class cpuState {
143 | PC: number;
144 | REGS: number[];
145 |
146 | constructor() {
147 | this.PC = 0;
148 | this.REGS = Array.from({length: 32}, () => 0);
149 | this.REGS[29] = MEM_STACK_START;
150 | }
151 | }
152 |
153 | export class instruction {
154 | opcode: number;
155 | funcCode: number;
156 | value: number;
157 | target: number;
158 | rs: number;
159 | rt: number;
160 | imm: number;
161 | rd: number;
162 | shamt: number;
163 | encoding: number;
164 | expr: number;
165 |
166 | constructor() {
167 | this.opcode = 0;
168 | this.funcCode = 0;
169 | this.value = 0;
170 | this.target = 0;
171 | this.rs = 0;
172 | this.rt = 0;
173 | this.imm = 0;
174 | this.rd = 0;
175 | this.shamt = 0;
176 | this.encoding = 0;
177 | this.expr = 0;
178 | }
179 | }
180 |
181 | export function initialize(
182 | binary: string[],
183 | textSize: number,
184 | dataSize: number,
185 | ): Iinstruction[] {
186 | initMemory();
187 |
188 | // Load program and service routines into mem
189 | let textIndex = 0;
190 | let size = 0;
191 |
192 | NUM_INST = ~~(textSize / 4);
193 |
194 | // initial memory allocation of text segment
195 | for (let i = 0; i < NUM_INST; i++) INST_INFO.push(new instruction());
196 | initInstInfo(NUM_INST, INST_INFO);
197 |
198 | binary.forEach(buffer => {
199 | if (size < textSize) {
200 | INST_INFO[textIndex] = parseInstr(buffer, size);
201 | textIndex += 1;
202 | } else if (size < textSize + dataSize) {
203 | parseData(buffer, size - textSize);
204 | }
205 | size += 4;
206 | });
207 |
208 | currentState.PC = MEM_TEXT_START;
209 |
210 | RUN_BIT = 1;
211 | return INST_INFO;
212 | }
213 | /*
214 | All simulated memory will be managed by this class
215 | use the mem_write and mem_read functions to
216 | access/modify the simulated memory
217 | */
218 | export class memRegionT {
219 | start: number;
220 | size: number;
221 | mem: number[];
222 | offBound: number;
223 | type: number;
224 | dirty: boolean;
225 | constructor(start: number, size: number, type: number = MEM_GROW_UP) {
226 | this.start = start;
227 | this.size = size;
228 | this.mem = [];
229 | this.offBound = -(size - 4) * type;
230 | this.type = type;
231 | this.dirty = false;
232 | }
233 | }
234 |
235 | // Global Variable Declaration
236 | const SLL = new instT('sll', '000000', 'R', '000000');
237 | const SRL = new instT('srl', '000000', 'R', '000010');
238 | const JR = new instT('jr', '000000', 'R', '001000');
239 | const ADD = new instT('add', '000000', 'R', '100000');
240 | const ADDU = new instT('addu', '000000', 'R', '100001');
241 | const AND = new instT('and', '000000', 'R', '100100');
242 | const NOR = new instT('nor', '000000', 'R', '100111');
243 | const OR = new instT('or', '000000', 'R', '100101');
244 | const SLT = new instT('slt', '000000', 'R', '101010');
245 | const SLTU = new instT('sltu', '000000', 'R', '101011');
246 | const SUB = new instT('sub', '000000', 'R', '100010');
247 | const SUBU = new instT('subu', '000000', 'R', '100011');
248 | const LUI = new instT('lui', '001111', 'I', '');
249 | const BEQ = new instT('beq', '000100', 'I', '');
250 | const BNE = new instT('bne', '000101', 'I', '');
251 | const LW = new instT('lw', '100011', 'I', '');
252 | const LHU = new instT('lhu', '100101', 'I', '');
253 | const SW = new instT('sw', '101011', 'I', '');
254 | const SH = new instT('sh', '101001', 'I', '');
255 | const ADDI = new instT('addi', '001000', 'I', '');
256 | const ADDIU = new instT('addiu', '001001', 'I', '');
257 | const ANDI = new instT('andi', '001100', 'I', '');
258 | const ORI = new instT('ori', '001101', 'I', '');
259 | const SLTI = new instT('slti', '001010', 'I', '');
260 | const SLTIU = new instT('sltiu', '001011', 'I', '');
261 | const J = new instT('j', '000010', 'J', '');
262 | const JAL = new instT('jal', '000011', 'J', '');
263 |
264 | export const instList: IinstList = {
265 | add: ADD,
266 | addi: ADDI,
267 | addiu: ADDIU,
268 | addu: ADDU,
269 | and: AND,
270 | andi: ANDI,
271 | beq: BEQ,
272 | bne: BNE,
273 | j: J,
274 | jal: JAL,
275 | jr: JR,
276 | lhu: LHU,
277 | lui: LUI,
278 | lw: LW,
279 | nor: NOR,
280 | or: OR,
281 | ori: ORI,
282 | slt: SLT,
283 | slti: SLTI,
284 | sltiu: SLTIU,
285 | sltu: SLTU,
286 | sll: SLL,
287 | srl: SRL,
288 | sh: SH,
289 | sw: SW,
290 | sub: SUB,
291 | subu: SUBU,
292 | };
293 |
294 | // Global symbol table
295 | export const symbolStruct = new symbolT();
296 |
297 | export let SYMBOL_TABLE: ISYMBOL_TABLE = {};
298 |
299 | export const resetSymbolTable = () => {
300 | SYMBOL_TABLE = {};
301 | };
302 |
303 | export const initializeMem = () => {
304 | INSTRUCTION_COUNT = 0;
305 | RUN_BIT = 1;
306 | memText = new memRegionT(MEM_TEXT_START, MEM_TEXT_SIZE);
307 | memData = new memRegionT(MEM_DATA_START, MEM_DATA_SIZE);
308 | memStack = new memRegionT(
309 | MEM_STACK_START - MEM_STACK_SIZE,
310 | MEM_STACK_SIZE,
311 | MEM_GROW_DOWN,
312 | );
313 | memRegions = [memText, memData, memStack];
314 | currentState = new cpuState();
315 | };
316 |
317 | /* INSTRUCTION COUNT ADD */
318 | export const instAddOne = () => {
319 | INSTRUCTION_COUNT += 1;
320 | };
321 |
322 | export const numInstSub = () => {
323 | NUM_INST -= 1;
324 | };
325 |
326 | export const changeRunBit = () => {
327 | RUN_BIT = 0;
328 | };
329 |
--------------------------------------------------------------------------------
/src/utils/functions.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as fs from 'fs';
3 | import {exit} from 'process';
4 |
5 | import {
6 | process_instruction,
7 | setFUNC,
8 | setIMM,
9 | setOPCODE,
10 | setRD,
11 | setRS,
12 | setRT,
13 | setSHAMT,
14 | setTARGET,
15 | } from '@simulator/run';
16 |
17 | import {
18 | bcolors,
19 | currentState,
20 | DEBUG,
21 | DEBUG_SET,
22 | INST_INFO,
23 | instAddOne,
24 | instruction,
25 | Iinstruction,
26 | memData,
27 | memStack,
28 | memRegions,
29 | MIPS_REGS,
30 | MEM_GROW_UP,
31 | MEM_DUMP_SET,
32 | MEM_NREGIONS,
33 | MEM_DATA_START,
34 | MEM_TEXT_START,
35 | pType,
36 | RUN_BIT,
37 | SYMBOL_TABLE,
38 | SymbolTableType,
39 | } from '@utils/constants';
40 |
41 | export interface simulatorOutputType {
42 | readonly PC: string;
43 | readonly registers: {[key: string]: string};
44 | readonly dataSection: {[key: string]: string} | Record;
45 | readonly stackSection: {[key: string]: string} | Record;
46 | }
47 |
48 | export interface IBinaryData {
49 | readonly lineNumber: number;
50 | readonly data: string;
51 | }
52 |
53 | export interface IMapDetail {
54 | readonly key: number;
55 | readonly assembly: string;
56 | readonly binary: IBinaryData[];
57 | }
58 |
59 | export function parseInstr(buffer: string, index: number): instruction {
60 | const instr: instruction = new instruction();
61 |
62 | setOPCODE(instr, fromBinary(buffer.slice(0, 6)));
63 |
64 | // R type
65 | if (instr.opcode === 0x0) {
66 | setRS(instr, fromBinary(buffer.slice(6, 11)));
67 | setRT(instr, fromBinary(buffer.slice(11, 16)));
68 | setRD(instr, fromBinary(buffer.slice(16, 21)));
69 | setSHAMT(instr, fromBinary(buffer.slice(21, 26)));
70 | setFUNC(instr, fromBinary(buffer.slice(26, 32)));
71 | }
72 |
73 | // J type
74 | else if (instr.opcode === 0x2 || instr.opcode === 0x3) {
75 | setTARGET(instr, fromBinary(buffer.slice(6, 32)));
76 | }
77 |
78 | // I type
79 | else {
80 | setRS(instr, fromBinary(buffer.slice(6, 11)));
81 | setRT(instr, fromBinary(buffer.slice(11, 16)));
82 | setIMM(instr, fromBinary(buffer.slice(16, 32)));
83 | }
84 |
85 | memWrite(MEM_TEXT_START + index, fromBinary(buffer));
86 | return instr;
87 | }
88 |
89 | export function parseData(buffer: string, index: number) {
90 | //[TODO] Implement this function
91 | memWrite(MEM_DATA_START + index, fromBinary(buffer));
92 | return;
93 | }
94 |
95 | export function printParseResult(
96 | INST_INFO: Iinstruction[],
97 | textSize: number,
98 | dataSize: number,
99 | ) {
100 | console.log('Instruction Information');
101 | /*
102 | TYPE I
103 | 0x8: (0x001000)ADDI
104 | 0x9: (0x001001)ADDIU
105 | 0xc: (0x001100)ANDI
106 | 0x4: (0x000100)BEQ
107 | 0x5: (0x000101)BNE
108 | 0x25: (0x011001)LHU
109 | 0xf: (0x001111)LUI
110 | 0x23: (0x100011)LW
111 | 0xd: (0x001101)ORI
112 | 0xa: (0x001010)SLTI
113 | 0xb: (0x001011)SLTIU
114 | 0x29: (0x011101)SH
115 | 0x2b: (0x101011)SW
116 |
117 | TYPE R
118 | 0x0: (0x000000)ADD, ADDU, AND, NOR, OR, SLT, SLTU, SLL, SRL, SUB, SUBU if JR
119 |
120 | TYPE J
121 | 0x2: (0x000010)J
122 | 0x3: (0x000011)JAL
123 | */
124 |
125 | const TypeIList = [
126 | 0x8, 0x9, 0xc, 0x4, 0x5, 0x25, 0xf, 0x23, 0xd, 0xa, 0xb, 0x29, 0x2b,
127 | ];
128 | const TypeRList = [0x0];
129 | const TypeJList = [0x2, 0x3];
130 |
131 | for (let i = 0; i < textSize / 4; i++) {
132 | console.log(`INST_INFO[${i}].value : 0x${INST_INFO[i].value.toString(16)}`);
133 | console.log(`INST_INFO[${i}].opcode : ${INST_INFO[i].opcode}`);
134 |
135 | if (INST_INFO[i].opcode in TypeIList) {
136 | console.log(`INST_INFO[${i}].rs : ${INST_INFO[i].rs}`);
137 | console.log(`INST_INFO[${i}].rt : ${INST_INFO[i].rt}`);
138 | console.log(`INST_INFO[${i}].imm : ${INST_INFO[i].imm}`);
139 | } else if (INST_INFO[i].opcode in TypeRList) {
140 | console.log(`INST_INFO[${i}].funcCode : ${INST_INFO[i].funcCode}`);
141 | console.log(`INST_INFO[${i}].rs : ${INST_INFO[i].rs}`);
142 | console.log(`INST_INFO[${i}].rt : ${INST_INFO[i].rt}`);
143 | console.log(`INST_INFO[${i}].rd : ${INST_INFO[i].rd}`);
144 | console.log(`INST_INFO[${i}].shamt : ${INST_INFO[i].shamt}`);
145 | } else if (INST_INFO[i].opcode in TypeJList) {
146 | console.log(`INST_INFO[${i}].target : ${INST_INFO[i].target}`);
147 | } else {
148 | console.log('Not available instrution\n');
149 | }
150 | }
151 | console.log('Memory Dump - Text Segment\n');
152 | for (let i = 0; i < textSize; i += 4) {
153 | console.log(
154 | `text_seg[${i}] : 0x${memRead(MEM_TEXT_START + i).toString(16)}`,
155 | );
156 | }
157 | for (let i = 0; i < dataSize; i += 4) {
158 | console.log(
159 | `text_seg[${i}] : 0x${memRead(MEM_DATA_START + i).toString(16)}`,
160 | );
161 | }
162 | console.log(`Current PC: ${currentState.PC.toString(16)}`);
163 | }
164 |
165 | export function numToBits(num: number, pad = 32): string {
166 | // 10진수 정수를 2진수 bit로 변경해서 return
167 | if (num >= 0) {
168 | return num.toString(2).padStart(pad, '0'); //양수일때
169 | } else {
170 | num = 2 ** pad + num;
171 | return num.toString(2).padStart(pad, '0'); //음수일때;
172 | }
173 | }
174 |
175 | export function toHexAndPad(num: number, pad = 8): string {
176 | /*
177 | * num : Number or String(숫자 형식, 10진법), pad : Number
178 | * input : 18 => output: '00000012'
179 | * Recommend | num을 Number 타입으로 넣을 것
180 | */
181 | return Number(num).toString(16).padStart(pad, '0');
182 | }
183 |
184 | export function symbolTableAddEntry(symbol: SymbolTableType) {
185 | SYMBOL_TABLE[symbol.name] = symbol.address;
186 | if (DEBUG) {
187 | log(1, `${symbol.name}: 0x${toHexAndPad(symbol.address)}`);
188 | }
189 | }
190 |
191 | export function log(printType: number, content: string) {
192 | console.log(pType[printType] + content);
193 | }
194 |
195 | export function isEmpty(value: string | null | undefined | object) {
196 | const emptyArray: string[] = [''];
197 | if (
198 | value === '' ||
199 | value === null ||
200 | value === undefined ||
201 | (value !== null &&
202 | typeof value === 'object' &&
203 | !Object.keys(value).length) ||
204 | value === emptyArray
205 | ) {
206 | return true;
207 | } else {
208 | return false;
209 | }
210 | }
211 |
212 | // Parsing an assembly file(*.s) into a list
213 | export function makeInput(
214 | inputFolderName: string,
215 | inputFileName: string,
216 | ): string[] {
217 | /*
218 | if the inputFilePath is /Users/junghaejune/simulator/sample_input/sample/example1.s,
219 | currDirectory : /Users/junghaejune/simulator
220 | inputFolderPath : sample_input/sample
221 | inputFileName: example1.s
222 | */
223 |
224 | const currDirectory: string = process.cwd();
225 | const inputFilePath: string = path.join(
226 | currDirectory,
227 | inputFolderName,
228 | inputFileName,
229 | );
230 |
231 | try {
232 | if (fs.existsSync(inputFilePath) === false) throw 'INPUT_PATH_ERROR';
233 |
234 | const input: string = fs.readFileSync(inputFilePath, 'utf-8');
235 |
236 | if (isEmpty(input)) throw 'INPUT_EMPTY';
237 | return input.split('\n');
238 | } catch (err) {
239 | if (err === 'INPUT_PATH_ERROR') {
240 | log(
241 | 3,
242 | `No input file ${inputFileName} exists. Please check the file name and path.`,
243 | );
244 | } else if (err === 'INPUT_EMPTY') {
245 | log(
246 | 3,
247 | `input file ${inputFileName} is not opened. Please check the file`,
248 | );
249 | } else console.error(err);
250 | exit(1);
251 | }
252 | }
253 |
254 | export function simulatorUnitTest(
255 | testCase: simulatorOutputType,
256 | output: simulatorOutputType,
257 | ) {
258 | function printResult(
259 | origin: {[key: string]: string} | Record,
260 | compare: {[key: string]: string} | Record,
261 | ) {
262 | Object.keys(origin).map(key => {
263 | if (compare[key]) {
264 | const color =
265 | origin[key] === compare[key] ? bcolors.GREEN : bcolors.RED;
266 | console.log(
267 | `${color}${key} : ${origin[key]} ${key} : ${compare[key]}${bcolors.ENDC}`,
268 | );
269 | } else {
270 | console.log(`${bcolors.RED}${key} : ${origin[key]}${bcolors.ENDC}`);
271 | }
272 | });
273 | }
274 |
275 | type keyType = 'registers' | 'dataSection' | 'stackSection';
276 | const keyList: keyType[] = ['registers', 'dataSection', 'stackSection'];
277 |
278 | console.log(`---------------PC---------------`);
279 | console.log(
280 | `${testCase.PC === output.PC ? bcolors.GREEN : bcolors.RED}PC : ${
281 | testCase.PC
282 | } PC : ${output.PC}${bcolors.ENDC}\n`,
283 | );
284 |
285 | keyList.map(key => {
286 | console.log(`---------------${key}---------------`);
287 | printResult(testCase[key], output[key]);
288 | console.log('\n');
289 | });
290 | }
291 |
292 | export function parseSimulatorOutput(rawOutput: string): simulatorOutputType {
293 | //input : test simulator input
294 | //ouput : object type -> { register : {PC:, R0:,...}, dataSection:{}, stackSection{}}
295 |
296 | function splitHelper(input: string): [string, string] {
297 | const returnValue = input.split(/:|\n/);
298 | return returnValue.length === 2
299 | ? [returnValue[0], returnValue[1].trim()]
300 | : null;
301 | }
302 |
303 | function setTypeParser(
304 | input: string,
305 | ): {[key: string]: string} | Record {
306 | const returnSet = {};
307 | input
308 | .split(/\n/)
309 | .filter(e => e !== '')
310 | .map(element => {
311 | const result = splitHelper(element);
312 | result ? (returnSet[result[0]] = result[1]) : null;
313 | });
314 |
315 | return returnSet;
316 | }
317 |
318 | const outputList = rawOutput
319 | .split(/Program Counter\n|Registers\n|Data section|Stack section\n/)
320 | .filter(e => e !== '');
321 |
322 | const PC = setTypeParser(outputList[0]);
323 | const registers = setTypeParser(outputList[1]);
324 | const dataSection = setTypeParser(outputList[2] || '');
325 | const stackSection = setTypeParser(outputList[3] || '');
326 |
327 | return {PC: PC.PC, registers, dataSection, stackSection};
328 | }
329 |
330 | export function makeOutput(
331 | inputFolderName: string,
332 | inputFileName: string,
333 | ): string {
334 | /*
335 | if the inputFilePath is /Users/junghaejune/simulator/sample_input/sample/example1.s,
336 | currDirectory : /Users/junghaejune/simulator
337 | inputFolderPath : sample_input/sample
338 | inputFileName: example1.s
339 | */
340 |
341 | const currDirectory: string = process.cwd();
342 | const inputFilePath: string = path.join(
343 | currDirectory,
344 | inputFolderName,
345 | inputFileName,
346 | );
347 |
348 | try {
349 | if (fs.existsSync(inputFilePath) === false) throw 'INPUT_PATH_ERROR';
350 |
351 | const input: string = fs.readFileSync(inputFilePath, 'utf-8');
352 |
353 | if (isEmpty(input)) throw 'INPUT_EMPTY';
354 | return input;
355 | } catch (err) {
356 | if (err === 'INPUT_PATH_ERROR') {
357 | log(
358 | 3,
359 | `No input file ${inputFileName} exists. Please check the file name and path.`,
360 | );
361 | } else if (err === 'INPUT_EMPTY') {
362 | log(
363 | 3,
364 | `input file ${inputFileName} is not opened. Please check the file`,
365 | );
366 | } else console.error(err);
367 | exit(1);
368 | }
369 | }
370 |
371 | // Create an Object file(*.o) in the desired path
372 | export function makeObjectFile(
373 | outputFolderPath: string,
374 | outputFileName: string,
375 | content: string[],
376 | ) {
377 | /*
378 | if the outputFilePath is /Users/junghaejune/simulator/sample_input/sample/example1.s,
379 | currDirectory : /Users/junghaejune/simulator
380 | outputFolderPath : sample_input/sample
381 | outputFileName: example1.o
382 | content : ['01010', '01010']
383 | */
384 |
385 | const currDirectory: string = process.cwd();
386 | const outputFilePath: string = path.join(
387 | currDirectory,
388 | outputFolderPath,
389 | outputFileName,
390 | );
391 |
392 | try {
393 | if (fs.existsSync(outputFilePath) === true) {
394 | fs.unlink(outputFilePath, err => {
395 | err
396 | ? console.error(err)
397 | : log(0, `Output file ${outputFileName} exists. Remake the file`);
398 | });
399 | } else throw 'OUTPUT_NOT_EXISTS';
400 | const fd: number = fs.openSync(outputFilePath, 'a');
401 |
402 | content.forEach(item => {
403 | fs.appendFileSync(fd, item + '\n', 'utf-8');
404 | });
405 |
406 | fs.closeSync(fd);
407 | } catch (err) {
408 | if (err === 'OUTPUT_NOT_EXISTS') {
409 | log(0, `Output file ${outputFileName} does not exists. Make the file`);
410 | } else console.error(err);
411 | exit(1);
412 | }
413 | }
414 |
415 | export function makeMappingDetail(
416 | assemblyFile: string[],
417 | dataSeg: string[],
418 | textSeg: string[],
419 | mappingTable: number[][],
420 | output: string[],
421 | ) {
422 | const mappingDetail: IMapDetail[] | null = [] as IMapDetail[];
423 | let textCounter = 0;
424 |
425 | assemblyFile.forEach((assemblyLine, i) => {
426 | const binaryInstructionNumbers: number[] = [];
427 | let binaryInstructions: string[] = [];
428 |
429 | if (assemblyLine.includes('.data')) {
430 | binaryInstructionNumbers.push(0);
431 | binaryInstructions = [output[0]];
432 | } else if (assemblyLine.includes('.word')) {
433 | const dataSegIndex = dataSeg.indexOf(assemblyLine.split('\t')[2]);
434 | const dataIndex = output.length - dataSeg.length + dataSegIndex;
435 | binaryInstructionNumbers.push(dataIndex);
436 | binaryInstructions = [output[dataIndex]];
437 | } else if (assemblyLine.includes('.text')) {
438 | binaryInstructionNumbers.push(1);
439 | binaryInstructions = [output[1]];
440 | } else {
441 | const binaryIndexes = mappingTable[textCounter];
442 | binaryInstructions = binaryIndexes.map(index => {
443 | binaryInstructionNumbers.push(index + 2);
444 | return output[index + 2];
445 | });
446 | assemblyLine === textSeg[textCounter] && textCounter++;
447 | }
448 |
449 | const binaryData: IBinaryData[] = [];
450 |
451 | binaryInstructions.forEach((inst, j) => {
452 | const binaryInstructionIndex = binaryInstructionNumbers[j];
453 | const temp: IBinaryData = {
454 | lineNumber: binaryInstructionIndex,
455 | data: inst,
456 | };
457 |
458 | binaryData.push(temp);
459 | });
460 |
461 | mappingDetail.push({
462 | key: i,
463 | assembly: assemblyLine,
464 | binary: binaryData,
465 | });
466 | });
467 |
468 | return mappingDetail;
469 | }
470 |
471 | /*
472 | assignment2 util
473 | */
474 |
475 | /*
476 | Procedure: fromBinary
477 | Purpose: From binary to integer
478 | */
479 |
480 | export function fromBinary(bits: string): number {
481 | return parseInt(bits, 2);
482 | }
483 |
484 | /*
485 | Procedure: memRead
486 | Purpose: read a 32-bit word from memory
487 | */
488 |
489 | export function memRead(address: number): number {
490 | for (let i = 0; i < MEM_NREGIONS; ++i) {
491 | if (
492 | address >= memRegions[i].start &&
493 | address < memRegions[i].start + memRegions[i].size
494 | ) {
495 | const offset = address - memRegions[i].start;
496 | const result =
497 | (memRegions[i].mem[offset + 3] << 24) |
498 | (memRegions[i].mem[offset + 2] << 16) |
499 | (memRegions[i].mem[offset + 1] << 8) |
500 | (memRegions[i].mem[offset + 0] << 0);
501 | return result;
502 | }
503 | }
504 | }
505 |
506 | /*
507 | Procedure: memWrite
508 | Purpose: Write a 32-bit word to memory
509 | */
510 |
511 | export function memWrite(address: number, value: number): void {
512 | memRegions.forEach(memRegion => {
513 | if (
514 | address >= memRegion.start &&
515 | address < memRegion.start + memRegion.size
516 | ) {
517 | const offset = address - memRegion.start;
518 |
519 | memRegion.mem[offset + 3] = (value >> 24) & 0xff;
520 | memRegion.mem[offset + 2] = (value >> 16) & 0xff;
521 | memRegion.mem[offset + 1] = (value >> 8) & 0xff;
522 | memRegion.mem[offset + 0] = (value >> 0) & 0xff;
523 |
524 | /* set_offBound */
525 | memRegion.dirty = true;
526 | if (memRegion.type === MEM_GROW_UP) {
527 | memRegion.offBound =
528 | offset + 4 > memRegion.offBound ? offset + 4 : memRegion.offBound;
529 | } else
530 | memRegion.offBound =
531 | offset + 4 < memRegion.offBound ? offset + 4 : memRegion.offBound;
532 | }
533 | });
534 | }
535 |
536 | /*
537 | Procedure: memWriteHalf
538 | Purpose: Write a half of 32-bit word to memory
539 | */
540 |
541 | export function memWriteHalf(address: number, value: number): void {
542 | memRegions.forEach(memRegion => {
543 | if (
544 | address >= memRegion.start &&
545 | address < memRegion.start + memRegion.size
546 | ) {
547 | const offset = address - memRegion.start;
548 |
549 | memRegion.mem[offset + 1] = (value >> 8) & 0xff;
550 | memRegion.mem[offset + 0] = (value >> 0) & 0xff;
551 |
552 | /* set_offBound */
553 | memRegion.dirty = true;
554 | if (memRegion.type === MEM_GROW_UP)
555 | memRegion.offBound =
556 | offset + 2 > memRegion.offBound ? offset + 2 : memRegion.offBound;
557 | else
558 | memRegion.offBound =
559 | offset + 2 < memRegion.offBound ? offset + 2 : memRegion.offBound;
560 | }
561 | });
562 | }
563 |
564 | /*
565 | Procedure: initMemory
566 | */
567 | export function initMemory(): void {
568 | for (let i = 0; i < MEM_NREGIONS; ++i) {
569 | memRegions[i].mem = Array.from({length: memRegions[i].size}, () => 0);
570 | }
571 | }
572 |
573 | /*
574 | Procedure: initInstInfo
575 | */
576 | export function initInstInfo(
577 | NUM_INST: number,
578 | INST_INFO: Iinstruction[],
579 | ): void {
580 | for (let i = 0; i < NUM_INST; ++i) {
581 | INST_INFO[i].value = 0;
582 | INST_INFO[i].opcode = 0;
583 | INST_INFO[i].funcCode = 0;
584 | INST_INFO[i].rs = 0;
585 | INST_INFO[i].rt = 0;
586 | INST_INFO[i].rd = 0;
587 | INST_INFO[i].imm = 0;
588 | INST_INFO[i].shamt = 0;
589 | INST_INFO[i].target = 0;
590 | }
591 | }
592 |
593 | /*
594 | Procedure: get_inst_info
595 | Purpose: Read instruction information
596 | */
597 | export function getInstInfo(pc: number): instruction {
598 | return INST_INFO[(pc - MEM_TEXT_START) >> 2];
599 | }
600 |
601 | /*
602 | Procedure: main process
603 | */
604 |
605 | export async function mainProcess(
606 | INST_INFO: instruction[],
607 | cycles: number,
608 | CYCLES: simulatorOutputType[],
609 | ): Promise {
610 | let i = cycles;
611 | let result = '';
612 |
613 | return new Promise((resolve, reject) => {
614 | try {
615 | if (DEBUG_SET) {
616 | console.log(`Simulating for ${cycles} cycles...!\n`);
617 | console.log('MAIN PROCESS', CYCLES);
618 | while (i > 0) {
619 | cycle();
620 | rdump();
621 |
622 | if (MEM_DUMP_SET) dumpMemory();
623 |
624 | i -= 1;
625 |
626 | if (RUN_BIT === 0) break;
627 | }
628 | } else {
629 | running(i, CYCLES);
630 | result += rdump();
631 |
632 | if (MEM_DUMP_SET) {
633 | result += dumpMemory();
634 | }
635 |
636 | let EachCycle: string = rdump();
637 | if (MEM_DUMP_SET) EachCycle += dumpMemory();
638 | CYCLES.push(parseSimulatorOutput(EachCycle));
639 | }
640 | const returnObject = parseSimulatorOutput(result);
641 | resolve(returnObject);
642 | } catch (error) {
643 | reject(error);
644 | }
645 | });
646 | }
647 |
648 | /*
649 | Procedure: cycle
650 | Purpose: Execute a cycle
651 | */
652 |
653 | export function cycle(): void {
654 | process_instruction();
655 | instAddOne();
656 | }
657 |
658 | export function dumpMemory(): string {
659 | let dump_string = '';
660 | if (memData.dirty) {
661 | const dstart = memData.start;
662 | const dstop = memData.start + memData.offBound;
663 | dump_string += `Data section\n`;
664 | dump_string += mdump(dstart, dstop);
665 | dump_string += '\n';
666 | }
667 |
668 | if (memStack.dirty) {
669 | const dstart = memStack.start + memStack.offBound;
670 | const dstop = memStack.start + memStack.size - 4;
671 | dump_string += `Stack section\n`;
672 | dump_string += mdump(dstart, dstop);
673 | dump_string += '\n';
674 | }
675 |
676 | return dump_string;
677 | }
678 |
679 | /*
680 | Procedure: mdump
681 | Purpose: Dump a word-aligned region of memory to the output file.
682 | */
683 | export function mdump(start: number, stop: number): string {
684 | let mdump_string = '';
685 | for (let i = start; i < stop + 1; i += 4) {
686 | mdump_string += `0x${toHexAndPad(i)}: 0x${toHexAndPad(memRead(i))}\n`;
687 | }
688 | mdump_string += '\n';
689 | return mdump_string;
690 | }
691 |
692 | /*
693 | Procedure: rdump
694 | Purpose: Dump current register and bus values to the output file.
695 | */
696 | export function rdump(): string {
697 | let rdump_string = '';
698 | rdump_string += 'Program Counter\n';
699 | rdump_string += `PC: 0x${toHexAndPad(currentState.PC)}\n`;
700 | rdump_string += `Registers\n`;
701 |
702 | for (let k = 0; k < MIPS_REGS; ++k) {
703 | rdump_string += `R${k}: 0x${toHexAndPad((currentState.REGS[k] >>>= 0))}\n`;
704 | }
705 |
706 | rdump_string += '\n';
707 | return rdump_string;
708 | }
709 |
710 | /*
711 | Procedure: run n
712 | Purpose: Simulate MIPS for n cycles
713 | */
714 | export function running(num_cycles: number, CYCLES: simulatorOutputType[]) {
715 | let running_string = '';
716 | if (RUN_BIT === 0) {
717 | running_string = "Can't simulate, Simulator is halted\n";
718 | }
719 |
720 | running_string = `Simulating for ${num_cycles} cycles...!\n\n`;
721 |
722 | for (let i = 0; i < num_cycles; ++i) {
723 | if (RUN_BIT === 0) {
724 | running_string += `Simulator halted ${i}th cycle.\n\n`;
725 | break;
726 | }
727 | let EachCycle: string = rdump();
728 | if (MEM_DUMP_SET) EachCycle += dumpMemory();
729 | CYCLES.push(parseSimulatorOutput(EachCycle));
730 | cycle();
731 | }
732 | console.log(running_string);
733 | }
734 |
--------------------------------------------------------------------------------
/tests/integration/assembler.test.ts:
--------------------------------------------------------------------------------
1 | import {assemble} from '@root/index';
2 | import {resetSymbolTable} from '@utils/constants';
3 | import {makeInput, makeOutput, IMapDetail} from '@utils/functions';
4 |
5 | const mappingDetailOutput: IMapDetail[] = [
6 | {
7 | key: 0,
8 | assembly: '\t.data',
9 | binary: [{lineNumber: 0, data: '00000000000000000000000001011000'}],
10 | },
11 | {
12 | key: 1,
13 | assembly: 'data1:\t.word\t100',
14 | binary: [{lineNumber: 24, data: '00000000000000000000000001100100'}],
15 | },
16 | {
17 | key: 2,
18 | assembly: 'data2:\t.word\t200',
19 | binary: [{lineNumber: 25, data: '00000000000000000000000011001000'}],
20 | },
21 | {
22 | key: 3,
23 | assembly: 'data3:\t.word\t0x12345678',
24 | binary: [{lineNumber: 26, data: '00010010001101000101011001111000'}],
25 | },
26 | {
27 | key: 4,
28 | assembly: '\t.text',
29 | binary: [{lineNumber: 1, data: '00000000000000000000000000001100'}],
30 | },
31 | {
32 | key: 5,
33 | assembly: 'main:',
34 | binary: [{lineNumber: 2, data: '00000010001000001000100000100100'}],
35 | },
36 | {
37 | key: 6,
38 | assembly: '\tand\t$17, $17, $0',
39 | binary: [{lineNumber: 2, data: '00000010001000001000100000100100'}],
40 | },
41 | {
42 | key: 7,
43 | assembly: '\tand\t$18, $18, $0',
44 | binary: [{lineNumber: 3, data: '00000010010000001001000000100100'}],
45 | },
46 | {
47 | key: 8,
48 | assembly: '\tla\t$8, data1',
49 | binary: [{lineNumber: 4, data: '00111100000010000001000000000000'}],
50 | },
51 | {
52 | key: 9,
53 | assembly: '\tla\t$9, data2',
54 | binary: [
55 | {lineNumber: 5, data: '00111100000010010001000000000000'},
56 | {lineNumber: 6, data: '00110101001010010000000000000100'},
57 | ],
58 | },
59 | {
60 | key: 10,
61 | assembly: '\tand\t$10, $10, $0',
62 | binary: [{lineNumber: 7, data: '00000001010000000101000000100100'}],
63 | },
64 | {
65 | key: 11,
66 | assembly: 'lab1:',
67 | binary: [{lineNumber: 8, data: '00000001011000000101100000100100'}],
68 | },
69 | {
70 | key: 12,
71 | assembly: '\tand\t$11, $11, $0',
72 | binary: [{lineNumber: 8, data: '00000001011000000101100000100100'}],
73 | },
74 | {
75 | key: 13,
76 | assembly: 'lab2:',
77 | binary: [{lineNumber: 9, data: '00100010001100010000000000000001'}],
78 | },
79 | {
80 | key: 14,
81 | assembly: '\taddi\t$17, $17, 0x1',
82 | binary: [{lineNumber: 9, data: '00100010001100010000000000000001'}],
83 | },
84 | {
85 | key: 15,
86 | assembly: '\taddi\t$11, $11, 0x1',
87 | binary: [{lineNumber: 10, data: '00100001011010110000000000000001'}],
88 | },
89 | {
90 | key: 16,
91 | assembly: '\tor\t$9, $9, $0',
92 | binary: [{lineNumber: 11, data: '00000001001000000100100000100101'}],
93 | },
94 | {
95 | key: 17,
96 | assembly: '\tbne\t$11, $8, lab2',
97 | binary: [{lineNumber: 12, data: '00010101011010001111111111111100'}],
98 | },
99 | {
100 | key: 18,
101 | assembly: 'lab3:',
102 | binary: [{lineNumber: 13, data: '00100010010100100000000000000010'}],
103 | },
104 | {
105 | key: 19,
106 | assembly: '\taddi\t$18, $18, 0x2',
107 | binary: [{lineNumber: 13, data: '00100010010100100000000000000010'}],
108 | },
109 | {
110 | key: 20,
111 | assembly: '\taddi\t$11, $11, 1',
112 | binary: [{lineNumber: 14, data: '00100001011010110000000000000001'}],
113 | },
114 | {
115 | key: 21,
116 | assembly: '\tsll\t$18, $17, 1',
117 | binary: [{lineNumber: 15, data: '00000000000100011001000001000000'}],
118 | },
119 | {
120 | key: 22,
121 | assembly: '\tsrl\t$17, $18, 1',
122 | binary: [{lineNumber: 16, data: '00000000000100101000100001000010'}],
123 | },
124 | {
125 | key: 23,
126 | assembly: '\tand\t$19, $17, $18',
127 | binary: [{lineNumber: 17, data: '00000010001100101001100000100100'}],
128 | },
129 | {
130 | key: 24,
131 | assembly: '\tbne\t$11, $9, lab3',
132 | binary: [{lineNumber: 18, data: '00010101011010011111111111111010'}],
133 | },
134 | {
135 | key: 25,
136 | assembly: 'lab4:',
137 | binary: [{lineNumber: 19, data: '00000000101111110010100000100000'}],
138 | },
139 | {
140 | key: 26,
141 | assembly: '\tadd\t$5, $5, $31',
142 | binary: [{lineNumber: 19, data: '00000000101111110010100000100000'}],
143 | },
144 | {
145 | key: 27,
146 | assembly: '\tnor\t$16, $17, $18',
147 | binary: [{lineNumber: 20, data: '00000010001100101000000000100111'}],
148 | },
149 | {
150 | key: 28,
151 | assembly: '\tbeq\t$10, $8, lab5',
152 | binary: [{lineNumber: 21, data: '00010001010010000000000000000001'}],
153 | },
154 | {
155 | key: 29,
156 | assembly: '\tj\tlab1',
157 | binary: [{lineNumber: 22, data: '00001000000100000000000000000110'}],
158 | },
159 | {
160 | key: 30,
161 | assembly: 'lab5:',
162 | binary: [{lineNumber: 23, data: '00110110000100001111000011110000'}],
163 | },
164 | {
165 | key: 31,
166 | assembly: '\tori\t$16, $16, 0xf0f0',
167 | binary: [{lineNumber: 23, data: '00110110000100001111000011110000'}],
168 | },
169 | ];
170 |
171 | test(`testing example 1 for [mapping detail]`, () => {
172 | resetSymbolTable();
173 |
174 | const input = makeInput('sample_input', `example1.s`);
175 | const {mappingDetail} = assemble(input, true);
176 |
177 | expect(mappingDetail).toEqual(mappingDetailOutput);
178 | });
179 |
180 | for (let i = 1; i <= 7; i++) {
181 | test(`testing example ${i} for [array output]`, () => {
182 | resetSymbolTable();
183 |
184 | const input = makeInput('sample_input', `example${i}.s`);
185 | const {output} = assemble(input, true);
186 | const testOutput = makeOutput('sample_output', `example${i}.o`)
187 | .split('\n') // string -> string[] 전환
188 | .filter(ele => ele); // "" deleted, "" == false
189 |
190 | expect(output).toEqual(testOutput);
191 | });
192 | }
193 |
--------------------------------------------------------------------------------
/tests/integration/simulator.test.ts:
--------------------------------------------------------------------------------
1 | import {simulator} from '@root/index';
2 | import {initializeMem} from '@utils/constants';
3 | import {makeInput, makeOutput, parseSimulatorOutput} from '@utils/functions';
4 |
5 | for (let i = 1; i <= 7; i++) {
6 | initializeMem();
7 | test(`testing example ${i}`, async () => {
8 | const input = makeInput('sample_input', `example${i}.s`);
9 | const {history} = await simulator(input, 10000, true);
10 | const expectOutput = parseSimulatorOutput(
11 | makeOutput('simulator_sample_output', `example0${i}.o`),
12 | );
13 | const testOutput = history[history.length - 1];
14 | expect(expectOutput.toString()).toBe(testOutput.toString());
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/tests/unit/makeSymbolTable.test.ts:
--------------------------------------------------------------------------------
1 | import {makeSymbolTable} from '@src/simulator/assembler';
2 | import {resetSymbolTable, SYMBOL_TABLE} from '@src/utils/constants';
3 |
4 | const testInput = [
5 | '\t.data',
6 | 'data1:\t.word\t100',
7 | 'data2:\t.word\t200',
8 | 'data3:\t.word\t0x12345678',
9 | '\t.text',
10 | 'main:',
11 | '\tand\t$17, $17, $0',
12 | '\tand\t$18, $18, $0',
13 | '\tla\t$8, data1',
14 | '\tla\t$9, data2',
15 | '\tand\t$10, $10, $0',
16 | 'lab1:',
17 | '\tand\t$11, $11, $0',
18 | 'lab2:',
19 | '\taddi\t$17, $17, 0x1',
20 | '\taddi\t$11, $11, 0x1',
21 | '\tor\t$9, $9, $0',
22 | '\tbne\t$11, $8, lab2',
23 | 'lab3:',
24 | '\taddi\t$18, $18, 0x2',
25 | '\taddi\t$11, $11, 1',
26 | '\tsll\t$18, $17, 1',
27 | '\tsrl\t$17, $18, 1',
28 | '\tand\t$19, $17, $18',
29 | '\tbne\t$11, $9, lab3',
30 | 'lab4:',
31 | '\tadd\t$5, $5, $31',
32 | '\tnor\t$16, $17, $18',
33 | '\tbeq\t$10, $8, lab5',
34 | '\tj\tlab1',
35 | 'lab5:',
36 | '\tori\t$16, $16, 0xf0f0',
37 | ];
38 |
39 | const symbolTableCase = {
40 | symbolTable: {
41 | data1: 268435456,
42 | data2: 268435460,
43 | data3: 268435464,
44 | main: 4194304,
45 | lab1: 4194328,
46 | lab2: 4194332,
47 | lab3: 4194348,
48 | lab4: 4194372,
49 | lab5: 4194388,
50 | },
51 | dataSeg: ['100', '200', '0x12345678'],
52 | textSeg: [
53 | '\tand\t$17, $17, $0',
54 | '\tand\t$18, $18, $0',
55 | '\tla\t$8, data1',
56 | '\tla\t$9, data2',
57 | '\tand\t$10, $10, $0',
58 | '\tand\t$11, $11, $0',
59 | '\taddi\t$17, $17, 0x1',
60 | '\taddi\t$11, $11, 0x1',
61 | '\tor\t$9, $9, $0',
62 | '\tbne\t$11, $8, lab2',
63 | '\taddi\t$18, $18, 0x2',
64 | '\taddi\t$11, $11, 1',
65 | '\tsll\t$18, $17, 1',
66 | '\tsrl\t$17, $18, 1',
67 | '\tand\t$19, $17, $18',
68 | '\tbne\t$11, $9, lab3',
69 | '\tadd\t$5, $5, $31',
70 | '\tnor\t$16, $17, $18',
71 | '\tbeq\t$10, $8, lab5',
72 | '\tj\tlab1',
73 | '\tori\t$16, $16, 0xf0f0',
74 | ],
75 | dataSectionSize: 12,
76 | textSectionSize: 88,
77 | };
78 |
79 | test('testing makeSymbolTable', () => {
80 | resetSymbolTable();
81 |
82 | const {dataSeg, textSeg, dataSectionSize, textSectionSize} =
83 | makeSymbolTable(testInput);
84 |
85 | expect(dataSeg).toEqual(symbolTableCase.dataSeg);
86 | expect(textSeg).toEqual(symbolTableCase.textSeg);
87 | expect(dataSectionSize).toBe(symbolTableCase.dataSectionSize);
88 | expect(textSectionSize).toBe(symbolTableCase.textSectionSize);
89 | expect(SYMBOL_TABLE).toEqual(symbolTableCase.symbolTable);
90 |
91 | resetSymbolTable();
92 | });
93 |
--------------------------------------------------------------------------------
/tests/unit/recordDataSection.test.ts:
--------------------------------------------------------------------------------
1 | import {makeSymbolTable, recordDataSection} from '@src/simulator/assembler';
2 | import {resetSymbolTable} from '@src/utils/constants';
3 |
4 | const testInput = [
5 | '\t.data',
6 | 'data1:\t.word\t100',
7 | 'data2:\t.word\t200',
8 | 'data3:\t.word\t0x12345678',
9 | '\t.text',
10 | 'main:',
11 | '\tand\t$17, $17, $0',
12 | '\tand\t$18, $18, $0',
13 | '\tla\t$8, data1',
14 | '\tla\t$9, data2',
15 | '\tand\t$10, $10, $0',
16 | 'lab1:',
17 | '\tand\t$11, $11, $0',
18 | 'lab2:',
19 | '\taddi\t$17, $17, 0x1',
20 | '\taddi\t$11, $11, 0x1',
21 | '\tor\t$9, $9, $0',
22 | '\tbne\t$11, $8, lab2',
23 | 'lab3:',
24 | '\taddi\t$18, $18, 0x2',
25 | '\taddi\t$11, $11, 1',
26 | '\tsll\t$18, $17, 1',
27 | '\tsrl\t$17, $18, 1',
28 | '\tand\t$19, $17, $18',
29 | '\tbne\t$11, $9, lab3',
30 | 'lab4:',
31 | '\tadd\t$5, $5, $31',
32 | '\tnor\t$16, $17, $18',
33 | '\tbeq\t$10, $8, lab5',
34 | '\tj\tlab1',
35 | 'lab5:',
36 | '\tori\t$16, $16, 0xf0f0',
37 | ];
38 |
39 | const recordDataCase = ['100', '200', '0x12345678'];
40 |
41 | const recordDataOutput = [
42 | '00000000000000000000000001100100',
43 | '00000000000000000000000011001000',
44 | '00010010001101000101011001111000',
45 | ];
46 |
47 | test('testing recordDataSection', () => {
48 | resetSymbolTable();
49 | makeSymbolTable(testInput);
50 |
51 | const output = recordDataSection(recordDataCase);
52 | expect(output).toEqual(recordDataOutput);
53 | });
54 |
--------------------------------------------------------------------------------
/tests/unit/recordTextSection.test.ts:
--------------------------------------------------------------------------------
1 | import {makeSymbolTable, recordTextSection} from '@src/simulator/assembler';
2 | import {resetSymbolTable} from '@src/utils/constants';
3 |
4 | const testInput = [
5 | '\t.data',
6 | 'data1:\t.word\t100',
7 | 'data2:\t.word\t200',
8 | 'data3:\t.word\t0x12345678',
9 | '\t.text',
10 | 'main:',
11 | '\tand\t$17, $17, $0',
12 | '\tand\t$18, $18, $0',
13 | '\tla\t$8, data1',
14 | '\tla\t$9, data2',
15 | '\tand\t$10, $10, $0',
16 | 'lab1:',
17 | '\tand\t$11, $11, $0',
18 | 'lab2:',
19 | '\taddi\t$17, $17, 0x1',
20 | '\taddi\t$11, $11, 0x1',
21 | '\tor\t$9, $9, $0',
22 | '\tbne\t$11, $8, lab2',
23 | 'lab3:',
24 | '\taddi\t$18, $18, 0x2',
25 | '\taddi\t$11, $11, 1',
26 | '\tsll\t$18, $17, 1',
27 | '\tsrl\t$17, $18, 1',
28 | '\tand\t$19, $17, $18',
29 | '\tbne\t$11, $9, lab3',
30 | 'lab4:',
31 | '\tadd\t$5, $5, $31',
32 | '\tnor\t$16, $17, $18',
33 | '\tbeq\t$10, $8, lab5',
34 | '\tj\tlab1',
35 | 'lab5:',
36 | '\tori\t$16, $16, 0xf0f0',
37 | ];
38 |
39 | const recordTextCase = [
40 | ' and $17, $17, $0',
41 | ' and $18, $18, $0',
42 | ' la $8, data1',
43 | ' la $9, data2',
44 | ' and $10, $10, $0',
45 | ' and $11, $11, $0',
46 | ' addi $17, $17, 0x1',
47 | ' addi $11, $11, 0x1',
48 | ' or $9, $9, $0',
49 | ' bne $11, $8, lab2',
50 | ' addi $18, $18, 0x2',
51 | ' addi $11, $11, 1',
52 | ' sll $18, $17, 1',
53 | ' srl $17, $18, 1',
54 | ' and $19, $17, $18',
55 | ' bne $11, $9, lab3',
56 | ' add $5, $5, $31',
57 | ' nor $16, $17, $18',
58 | ' beq $10, $8, lab5',
59 | ' j lab1',
60 | ' ori $16, $16, 0xf0f0',
61 | ];
62 |
63 | const recordTextOutput = [
64 | '00000010001000001000100000100100',
65 | '00000010010000001001000000100100',
66 | '00111100000010000001000000000000',
67 | '00111100000010010001000000000000',
68 | '00110101001010010000000000000100',
69 | '00000001010000000101000000100100',
70 | '00000001011000000101100000100100',
71 | '00100010001100010000000000000001',
72 | '00100001011010110000000000000001',
73 | '00000001001000000100100000100101',
74 | '00010101011010001111111111111100',
75 | '00100010010100100000000000000010',
76 | '00100001011010110000000000000001',
77 | '00000000000100011001000001000000',
78 | '00000000000100101000100001000010',
79 | '00000010001100101001100000100100',
80 | '00010101011010011111111111111010',
81 | '00000000101111110010100000100000',
82 | '00000010001100101000000000100111',
83 | '00010001010010000000000000000001',
84 | '00001000000100000000000000000110',
85 | '00110110000100001111000011110000',
86 | ];
87 |
88 | const mappingTableOutput = [
89 | [0],
90 | [1],
91 | [2],
92 | [3, 4],
93 | [5],
94 | [6],
95 | [7],
96 | [8],
97 | [9],
98 | [10],
99 | [11],
100 | [12],
101 | [13],
102 | [14],
103 | [15],
104 | [16],
105 | [17],
106 | [18],
107 | [19],
108 | [20],
109 | [21],
110 | ];
111 |
112 | test('testing recordTextSection [binary Text]', () => {
113 | resetSymbolTable();
114 | makeSymbolTable(testInput);
115 |
116 | const [binaryText, _] = recordTextSection(recordTextCase);
117 | expect(binaryText).toEqual(recordTextOutput);
118 | });
119 |
120 | test('testing recordTextSection [mapping Table]', () => {
121 | resetSymbolTable();
122 | makeSymbolTable(testInput);
123 |
124 | const [_, mappingTable] = recordTextSection(recordTextCase);
125 | expect(mappingTable).toEqual(mappingTableOutput);
126 | });
127 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // "types": ["jest"],
4 | "module": "CommonJS",
5 | "allowJs": true,
6 | "declaration": true,
7 | "target": "ES5",
8 | "outDir": "./dist",
9 | "rootDir": "./",
10 | "moduleResolution": "Node",
11 | "lib": ["ES2015", "DOM", "DOM.Iterable"],
12 | "esModuleInterop": true,
13 | "allowSyntheticDefaultImports": false,
14 | "baseUrl": "./",
15 | "paths": {
16 | "@root/*": ["./*"],
17 | "@src/*": ["src/*"],
18 | "@utils/*": ["src/utils/*"],
19 | "@simulator/*": ["src/simulator/*"]
20 | }
21 | },
22 | "include": ["./src/**/*.ts", "./tests/**/*.ts"],
23 | "files": ["index.ts"],
24 | "exclude": ["node_modules", "dist", ".github"]
25 | }
26 |
--------------------------------------------------------------------------------
/typescript.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["jest"]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------