├── .cargo
└── config.toml
├── .github
├── dependabot.yml.bak
└── workflows
│ ├── build.yml
│ ├── docs.yml
│ ├── release.yml
│ └── vscode.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── Cross.toml
├── LICENSE
├── README.md
├── docs
├── .gitignore
├── .npmignore
├── package-lock.json
├── package.json
└── src
│ ├── .vuepress
│ ├── config.js
│ ├── enhanceApp.js
│ ├── public
│ │ └── mos.png
│ └── styles
│ │ ├── index.styl
│ │ └── palette.styl
│ ├── guide
│ ├── advanced.md
│ ├── assembler.md
│ ├── cli
│ │ └── index.md
│ ├── ide
│ │ ├── index.md
│ │ ├── mos-vscode.jpg
│ │ └── vscode.md
│ ├── index.md
│ ├── project-setup.md
│ └── unit-testing.md
│ └── index.md
├── examples
├── atari800
│ └── colors
│ │ ├── atari800.asm
│ │ ├── main.asm
│ │ └── mos.toml
└── c64
│ ├── cartridge
│ ├── main.asm
│ └── mos.toml
│ ├── scroller
│ ├── main.asm
│ └── mos.toml
│ ├── shared
│ └── c64.asm
│ └── unit-testing
│ ├── main.asm
│ └── mos.toml
├── mos-core
├── Cargo.toml
├── src
│ ├── cbm
│ │ ├── mod.rs
│ │ └── petscii.rs
│ ├── codegen
│ │ ├── analysis.rs
│ │ ├── config_extractor.rs
│ │ ├── config_validator.rs
│ │ ├── evaluator.rs
│ │ ├── mod.rs
│ │ ├── opcodes.rs
│ │ ├── program_counter.rs
│ │ ├── segment.rs
│ │ ├── source_map.rs
│ │ ├── symbols.rs
│ │ └── text_encoding.rs
│ ├── errors.rs
│ ├── formatting
│ │ └── mod.rs
│ ├── io
│ │ ├── binary_writer.rs
│ │ ├── listing.rs
│ │ ├── mod.rs
│ │ └── vice.rs
│ ├── lib.rs
│ ├── parser
│ │ ├── ast.rs
│ │ ├── code_map.rs
│ │ ├── config_map.rs
│ │ ├── identifier.rs
│ │ ├── mnemonic.rs
│ │ ├── mod.rs
│ │ ├── source.rs
│ │ └── testing.rs
│ └── testing.rs
└── test-data
│ ├── build
│ └── include.bin
│ └── format
│ ├── mos-default-formatting.toml
│ ├── valid-formatted.asm
│ └── valid-unformatted.asm
├── mos-testing
├── Cargo.toml
└── src
│ └── lib.rs
├── mos
├── Cargo.toml
├── src
│ ├── commands
│ │ ├── build.rs
│ │ ├── format.rs
│ │ ├── init.rs
│ │ ├── init.toml
│ │ ├── lsp.rs
│ │ ├── mod.rs
│ │ ├── test.rs
│ │ └── version.rs
│ ├── config.rs
│ ├── debugger
│ │ ├── adapters
│ │ │ ├── mod.rs
│ │ │ ├── test_runner
│ │ │ │ └── mod.rs
│ │ │ └── vice
│ │ │ │ ├── mod.rs
│ │ │ │ └── protocol.rs
│ │ ├── connection.rs
│ │ ├── mod.rs
│ │ ├── protocol.rs
│ │ └── types.rs
│ ├── diagnostic_emitter.rs
│ ├── lsp
│ │ ├── code_lens.rs
│ │ ├── completion.rs
│ │ ├── documents.rs
│ │ ├── formatting.rs
│ │ ├── hover.rs
│ │ ├── mod.rs
│ │ ├── references.rs
│ │ ├── rename.rs
│ │ ├── semantic_highlighting.rs
│ │ ├── symbols.rs
│ │ ├── testing.rs
│ │ └── traits.rs
│ ├── main.rs
│ ├── memory_accessor.rs
│ ├── test_runner
│ │ └── mod.rs
│ └── utils.rs
└── test-data
│ ├── build
│ ├── include.asm
│ ├── include.bin
│ ├── include.prg
│ ├── multiple_segments.asm
│ ├── multiple_segments.prg
│ ├── valid.asm
│ └── valid.prg
│ └── test
│ └── some-tests.asm
└── vscode
├── .gitignore
├── .vscodeignore
├── README.md
├── bundle.js
├── clean.sh
├── icon.png
├── language-configuration.json
├── mos-version.js
├── package-lock.json
├── package.json
├── src
├── auto-update
│ ├── download-binary.ts
│ └── net.ts
├── build-task-provider.ts
├── extension.ts
├── log.ts
└── run-all-tests-task-provider.ts
├── syntaxes
└── asm.tmLanguage.json
└── tsconfig.json
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [profile.release]
2 | lto = true
3 |
4 | [target.x86_64-pc-windows-msvc]
5 | rustflags = ["-Ctarget-feature=+crt-static"]
--------------------------------------------------------------------------------
/.github/dependabot.yml.bak:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: cargo
4 | directory: "/"
5 | schedule:
6 | interval: weekly
7 | open-pull-requests-limit: 99
8 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | pull_request:
5 |
6 | jobs:
7 | check:
8 | name: Check
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - uses: actions-rs/toolchain@v1
13 | with:
14 | profile: minimal
15 | toolchain: stable
16 | override: true
17 | - uses: Swatinem/rust-cache@v1
18 | - uses: actions-rs/cargo@v1
19 | with:
20 | command: check
21 |
22 | fmt:
23 | name: Rustfmt
24 | runs-on: ubuntu-latest
25 | steps:
26 | - uses: actions/checkout@v2
27 | - uses: actions-rs/toolchain@v1
28 | with:
29 | profile: minimal
30 | toolchain: stable
31 | override: true
32 | - run: rustup component add rustfmt
33 | - uses: Swatinem/rust-cache@v1
34 | - uses: actions-rs/cargo@v1
35 | with:
36 | command: fmt
37 | args: --all -- --check
38 |
39 | clippy:
40 | name: Clippy
41 | runs-on: ubuntu-latest
42 | steps:
43 | - uses: actions/checkout@v2
44 | - uses: actions-rs/toolchain@v1
45 | with:
46 | profile: minimal
47 | toolchain: stable
48 | override: true
49 | - run: rustup component add clippy
50 | - uses: Swatinem/rust-cache@v1
51 | - uses: actions-rs/cargo@v1
52 | with:
53 | command: clippy
54 | args: -- -D warnings
55 |
56 | build:
57 | name: build
58 | needs: ['check', 'fmt', 'clippy']
59 | runs-on: ${{ matrix.os }}
60 | env:
61 | # For some builds, we use cross to test on 32-bit and big-endian
62 | # systems.
63 | CARGO: cargo
64 | # When CARGO is set to CROSS, this is set to `--target matrix.target`.
65 | TARGET_FLAGS:
66 | # When CARGO is set to CROSS, TARGET_DIR includes matrix.target.
67 | TARGET_DIR: ./target
68 | # Emit backtraces on panics.
69 | RUST_BACKTRACE: 1
70 | strategy:
71 | matrix:
72 | build: [linux, macos, win-msvc]
73 | include:
74 | - build: linux
75 | os: ubuntu-18.04
76 | rust: stable
77 | target: x86_64-unknown-linux-musl
78 | - build: macos
79 | os: macos-latest
80 | rust: stable
81 | target: x86_64-apple-darwin
82 | - build: win-msvc
83 | os: windows-2019
84 | rust: stable
85 | target: x86_64-pc-windows-msvc
86 |
87 | steps:
88 | - name: Checkout repository
89 | uses: actions/checkout@v2
90 | with:
91 | fetch-depth: 1
92 |
93 | - name: Install Rust
94 | uses: actions-rs/toolchain@v1
95 | with:
96 | toolchain: ${{ matrix.rust }}
97 | profile: minimal
98 | override: true
99 | target: ${{ matrix.target }}
100 |
101 | - uses: Swatinem/rust-cache@v1
102 |
103 | - name: Use Cross
104 | if: matrix.os != 'windows-2019'
105 | run: |
106 | cargo install cross
107 | echo "CARGO=cross" >> $GITHUB_ENV
108 | echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV
109 | echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV
110 |
111 | - name: Show command used for Cargo
112 | run: |
113 | echo "cargo command is: ${{ env.CARGO }}"
114 | echo "target flag is: ${{ env.TARGET_FLAGS }}"
115 | echo "target dir is: ${{ env.TARGET_DIR }}"
116 |
117 | - name: Run tests
118 | run: ${{ env.CARGO }} test --verbose --release ${{ env.TARGET_FLAGS }}
119 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Publish on Netlify
2 | on:
3 | workflow_dispatch:
4 | push:
5 | tags:
6 | - '[0-9]+.[0-9]+.[0-9]+'
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@master
14 | - uses: actions/setup-node@v2
15 | - run: npm install
16 | working-directory: docs
17 |
18 | - name: Build docs
19 | working-directory: docs
20 | run: npm run build
21 |
22 | - name: Deploy to netlify
23 | uses: netlify/actions/cli@master
24 | env:
25 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
26 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
27 | with:
28 | args: deploy --dir=docs/src/.vuepress/dist --prod
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # Shamelessly stolen from https://raw.githubusercontent.com/BurntSushi/ripgrep/master/.github/workflows/release.yml
2 | #
3 | # The way this works is a little weird. But basically, the create-release job
4 | # runs purely to initialize the GitHub release itself. Once done, the upload
5 | # URL of the release is saved as an artifact.
6 | #
7 | # The build-release job runs only once create-release is finished. It gets
8 | # the release upload URL by downloading the corresponding artifact (which was
9 | # uploaded by create-release). It then builds the release executables for each
10 | # supported platform and attaches them as release assets to the previously
11 | # created release.
12 | #
13 | # The key here is that we create the release only once.
14 |
15 | name: Release
16 | on:
17 | push:
18 | branches:
19 | - dev-workflows
20 | tags:
21 | - '[0-9]+.[0-9]+.[0-9]+'
22 | jobs:
23 | create-release:
24 | name: create-release
25 | runs-on: ubuntu-latest
26 | steps:
27 | - name: Force release version number on the dev-workflows branch
28 | run: |
29 | if [[ $GITHUB_REF = 'refs/heads/dev-workflows' ]]; then
30 | echo "MOS_VERSION=TEST-0.0.0" >> "$GITHUB_ENV"
31 | fi
32 |
33 | - name: Create artifacts directory
34 | run: mkdir artifacts
35 |
36 | - name: Get the release version from the tag
37 | if: env.MOS_VERSION == ''
38 | run: |
39 | # Apparently, this is the right way to get a tag name. Really?
40 | #
41 | # See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027
42 | echo "MOS_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
43 | echo "version is: ${{ env.MOS_VERSION }}"
44 |
45 | - name: Create GitHub release
46 | id: release
47 | uses: actions/create-release@v1
48 | env:
49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50 | with:
51 | tag_name: ${{ env.MOS_VERSION }}
52 | release_name: ${{ env.MOS_VERSION }}
53 | body: See [changelog](https://github.com/datatrash/mos/blob/${{ env.MOS_VERSION }}/CHANGELOG.md) for details.
54 |
55 | - name: Save release upload URL to artifact
56 | run: echo "${{ steps.release.outputs.upload_url }}" > artifacts/release-upload-url
57 |
58 | - name: Save version number to artifact
59 | run: echo "${{ env.MOS_VERSION }}" > artifacts/release-version
60 |
61 | - name: Upload artifacts
62 | uses: actions/upload-artifact@v1
63 | with:
64 | name: artifacts
65 | path: artifacts
66 |
67 | build-release:
68 | name: build-release
69 | needs: ['create-release']
70 | runs-on: ${{ matrix.os }}
71 | env:
72 | # For some builds, we use cross to test on 32-bit and big-endian
73 | # systems.
74 | CARGO: cargo
75 | # When CARGO is set to CROSS, this is set to `--target matrix.target`.
76 | TARGET_FLAGS:
77 | # When CARGO is set to CROSS, TARGET_DIR includes matrix.target.
78 | TARGET_DIR: ./target
79 | # Emit backtraces on panics.
80 | RUST_BACKTRACE: 1
81 | strategy:
82 | matrix:
83 | build: [linux, macos, win-msvc]
84 | include:
85 | - build: linux
86 | os: ubuntu-18.04
87 | rust: stable
88 | target: x86_64-unknown-linux-musl
89 | - build: macos
90 | os: macos-latest
91 | rust: stable
92 | target: x86_64-apple-darwin
93 | - build: win-msvc
94 | os: windows-2019
95 | rust: stable
96 | target: x86_64-pc-windows-msvc
97 |
98 | steps:
99 | - name: Checkout repository
100 | uses: actions/checkout@v2
101 | with:
102 | fetch-depth: 1
103 |
104 | - name: Install Rust
105 | uses: actions-rs/toolchain@v1
106 | with:
107 | toolchain: ${{ matrix.rust }}
108 | profile: minimal
109 | override: true
110 | target: ${{ matrix.target }}
111 |
112 | - name: Use Cross
113 | if: matrix.os != 'windows-2019'
114 | run: |
115 | cargo install cross
116 | echo "CARGO=cross" >> $GITHUB_ENV
117 | echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV
118 | echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV
119 |
120 | - name: Show command used for Cargo
121 | run: |
122 | echo "cargo command is: ${{ env.CARGO }}"
123 | echo "target flag is: ${{ env.TARGET_FLAGS }}"
124 | echo "target dir is: ${{ env.TARGET_DIR }}"
125 |
126 | - name: Get release download URL
127 | uses: actions/download-artifact@v1
128 | with:
129 | name: artifacts
130 | path: artifacts
131 |
132 | - name: Set release upload URL and release version
133 | shell: bash
134 | run: |
135 | release_upload_url="$(cat artifacts/release-upload-url)"
136 | echo "RELEASE_UPLOAD_URL=$release_upload_url" >> $GITHUB_ENV
137 | echo "release upload url: $RELEASE_UPLOAD_URL"
138 | release_version="$(cat artifacts/release-version)"
139 | echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
140 | echo "release version: $RELEASE_VERSION"
141 |
142 | - name: Build release binary
143 | run: ${{ env.CARGO }} build --verbose --release ${{ env.TARGET_FLAGS }}
144 |
145 | - name: Strip release binary (linux and macos)
146 | if: matrix.build == 'linux' || matrix.build == 'macos'
147 | run: strip "${{ env.TARGET_DIR }}/release/mos"
148 |
149 | - name: Build archive
150 | shell: bash
151 | run: |
152 | staging="mos-${{ env.RELEASE_VERSION }}-${{ matrix.target }}"
153 | mkdir -p "$staging"
154 |
155 | cp {CHANGELOG.md,LICENSE,README.md} "$staging/"
156 | cp -R examples "$staging/"
157 |
158 | if [ "${{ matrix.os }}" = "windows-2019" ]; then
159 | cp "${{ env.TARGET_DIR }}/release/mos.exe" "$staging/"
160 | 7z a "$staging.zip" .\\"$staging"\\*
161 | echo "ASSET=$staging.zip" >> $GITHUB_ENV
162 | else
163 | cp "${{ env.TARGET_DIR }}/release/mos" "$staging/"
164 | cd "$staging"
165 | tar czf "../$staging.tar.gz" .
166 | cd ..
167 | echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV
168 | fi
169 |
170 | - name: Upload release archive
171 | uses: actions/upload-release-asset@v1.0.1
172 | env:
173 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
174 | with:
175 | upload_url: ${{ env.RELEASE_UPLOAD_URL }}
176 | asset_path: ${{ env.ASSET }}
177 | asset_name: ${{ env.ASSET }}
178 | asset_content_type: application/octet-stream
--------------------------------------------------------------------------------
/.github/workflows/vscode.yml:
--------------------------------------------------------------------------------
1 | name: Publish VSCode Extension
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | mos_version:
6 | description: 'The version of the MOS binary to download'
7 | required: true
8 | extension_version:
9 | description: 'The version of the extension to release'
10 | required: true
11 |
12 | jobs:
13 | publish:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@master
18 | - uses: actions/setup-node@v2
19 | - run: npm install
20 | working-directory: vscode
21 |
22 | - name: Publish
23 | working-directory: vscode
24 | env:
25 | VSCE_PAT: ${{ secrets.VSCE_PAT }}
26 | MOS_VERSION: ${{ github.event.inputs.mos_version }}
27 | run: npm run publish -- ${{ github.event.inputs.extension_version }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /**/target
2 | /**/.vscode
3 | /.idea
4 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.8.2 (2022-10-06)
4 |
5 | ### New features
6 | - Allow setting registers when debugging, once again thanks to [@themkat](https://github.com/themkat)! ([#110](https://github.com/datatrash/mos/issues/110))
7 |
8 | ## 0.8.1 (2022-09-02)
9 |
10 | ### New features
11 | * Autocompletion for mnemonics, thanks again [@themkat](https://github.com/themkat)! ([#196](https://github.com/datatrash/mos/issues/196))
12 |
13 | ## 0.8.0 (2022-08-31)
14 |
15 | ### New features
16 | * There is now also code completion in the debugger, thanks [@themkat](https://github.com/themkat)! ([#166](https://github.com/datatrash/mos/issues/166))
17 |
18 | ## 0.7.5 (2021-11-16)
19 |
20 | ### Bugfixes
21 | * 'Branch too far' errors could trigger based on label positions in previous passes.
22 |
23 | ## 0.7.4 (2021-07-22)
24 |
25 | ### Bugfixes
26 | * `Unknown identifier` errors were emitted too often. ([#211](https://github.com/datatrash/mos/issues/211))
27 |
28 | ## 0.7.3 (2021-07-14)
29 |
30 | ### Bugfixes
31 | * When setting breakpoints, the breakpoint should be set at the location the code is ultimately designed to go to. Usually this is the same as the location it is emitted, but this is not the case for code that is designed to be moved to another location. ([#206](https://github.com/datatrash/mos/issues/206))
32 | * A unexpected closing brace would break parsing of the rest of the file.([#205](https://github.com/datatrash/mos/issues/205))
33 |
34 | ## 0.7.2 (2021-07-08)
35 |
36 | ### New features
37 | * The `mos.path` property may now be quoted, easing use on Windows. ([#204](https://github.com/datatrash/mos/issues/204))
38 |
39 | ### Bugfixes
40 | * When launching the debugger no files have to be open in the editor. ([#203](https://github.com/datatrash/mos/issues/203))
41 |
42 | ## 0.7.1 (2021-07-01)
43 |
44 | ### Bugfixes
45 | * Trying to invoke an undefined macro correctly did not report the macro as undefined ([#195](https://github.com/datatrash/mos/issues/195))
46 |
47 | ## 0.7.0 (2021-06-18)
48 |
49 | ### New features
50 | * "Banks" can be used to control how the final binary is laid out (e.g. to create cartridge files)
51 | * Strings can be used as symbols, with support for string interpolation and concatenation
52 | * Error formatting is improved and made configurable through the `--error-style` option
53 | * A listing file can be generated for every segment by specifying `listing = true` in the `[build]` section of `mos.toml`
54 | * Instead of just generating a Commodore-compatible `.prg` file, it is now also possible to generate a raw binary or a raw binary per segment
55 |
56 | ### Notable bugfixes
57 | * Nested macros are now looked up correctly ([#168](https://github.com/datatrash/mos/issues/168))
58 | * Symbol resolution doesn't return incorrect symbols when lookups fail halfway through
59 | * Error reporting shows correct paths on Windows now, instead of UNC paths ([#192](https://github.com/datatrash/mos/issues/192))
60 | * Files imported from subdirectories are resolved in a platform-independent way (i.e. forward or backslashes may be used)
61 | * Uninvoked macros and untaken if/else paths now get proper refactoring support ([#180](https://github.com/datatrash/mos/issues/180))
62 | * When attempting to debug with an unsupported version of VICE a proper error message is created ([#186](https://github.com/datatrash/mos/issues/186))
63 | * Syntax grammar files were not packaged in the VSCode extension ([#185](https://github.com/datatrash/mos/issues/185))
64 |
65 | For a full list of changes please refer to the [0.7.0 milestone on GitHub](https://github.com/datatrash/mos/milestone/8?closed=1).
66 |
67 | ## 0.6.0 (2021-06-02)
68 |
69 | ### New features
70 | * Support for writing unit tests
71 | * Support for running unit tests in an emulated 6502
72 | * Support for running and debugging unit tests from within Visual Studio Code
73 | * Symbols may be annotated with markdown comments, which will show on hover
74 |
75 | ### Bugfixes
76 | * Various scoping issues have been resolved
77 |
78 | ## 0.5.0 (2021-05-25)
79 |
80 | ### New features
81 | * Support for debugging in Visual Studio Code (via the Debug Adapter protocol)
82 | * Formatting now works with label/code/comment columns
83 | * Can now use - and + to refer to start and end of scopes
84 | * Visual Studio Code: Now provides a 'build' task and associated problem matcher
85 |
86 | ### Bugfixes
87 | * Fixed a number of edge cases related to code completion
88 | * Code completion doesn't seem to work inside a (segment) block
89 | * Fix forward references to other segments
90 | * Fix infinite loop with label before end of block
91 | * Formatting: No empty lines after a non-block label
92 | * Formatting: Empty line after a block
93 | * C/C++-style comments are not highlighted nicely
94 | * LSP not shutting down correctly
95 | * Error output is not sorted by line/file
96 |
97 | ## 0.4.1 (2021-03-26)
98 |
99 | ### Bugfixes
100 | * Formatting now works correctly for projects containing multiple files
101 |
102 | ## 0.4.0 (2021-03-24)
103 |
104 | ## 0.3.1 (2021-03-21)
105 |
106 | ### Bugfixes
107 | * Linux version now shows correct version information
108 |
109 | ## 0.3.0 (2021-03-16)
110 |
111 | ### New features
112 | * Support for macros
113 | * Support for loops
114 | * Can now use `-` and `+` to refer to start and end of scopes
115 | * Visual Studio Code: Now provides a 'build' task and associated problem matcher
116 |
117 | ### Bugfixes
118 | * Rewrote code generation to be both simpler and more accurate
119 | * The `no-color` parameter now works as expected
120 |
121 | ## 0.2.0 (2021-03-11)
122 |
123 | ### New features
124 | * Project configuration file is now required in lieu of command-line arguments
125 | * Language Server support
126 |
127 | ## 0.1.0 (2021-02-22)
128 |
129 | First public alpha release!
130 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "mos",
4 | "mos-core",
5 | "mos-testing"
6 | ]
7 |
8 | [profile.dev]
9 | split-debuginfo = "unpacked"
10 |
--------------------------------------------------------------------------------
/Cross.toml:
--------------------------------------------------------------------------------
1 | [build.env]
2 | passthrough = [
3 | "RELEASE_VERSION"
4 | ]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Roy Jacobs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mos' dev!
2 |
3 |
4 |
5 | **mos** is a tool suite for the MOS 6502 (and compatible) CPU written in Rust. More details on the [official site](https://mos.datatra.sh).
6 |
7 | ***
8 |
9 | ### Building the assembler from source (Linux/MacOS/Windows):
10 |
11 | * Ensure this is green: [](https://github.com/datatrash/mos/actions)
12 | * Install [Rust](https://rustup.rs/)
13 | * Clone this repository
14 | * `cargo build --release`
15 | * The `mos` executable will be in `target/release`
16 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | pids
2 | logs
3 | node_modules
4 | npm-debug.log
5 | coverage/
6 | run
7 | dist
8 | .DS_Store
9 | .nyc_output
10 | .basement
11 | config.local.js
12 | basement_dist
13 |
--------------------------------------------------------------------------------
/docs/.npmignore:
--------------------------------------------------------------------------------
1 | pids
2 | logs
3 | node_modules
4 | npm-debug.log
5 | coverage/
6 | run
7 | dist
8 | .DS_Store
9 | .nyc_output
10 | .basement
11 | config.local.js
12 | basement_dist
13 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mos",
3 | "version": "0.0.1",
4 | "description": "MOS - 6502, modernized",
5 | "main": "index.js",
6 | "authors": {
7 | "name": "Roy Jacobs",
8 | "email": "roy.jacobs@gmail.com"
9 | },
10 | "repository": "https://github.com/datatrash/mos/mos",
11 | "scripts": {
12 | "dev": "vuepress dev src",
13 | "build": "vuepress build src"
14 | },
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@vuepress/plugin-active-header-links": "^1.8.2",
18 | "vuepress": "^1.5.3",
19 | "vuepress-plugin-sitemap": "^2.3.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/src/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | const { description } = require('../../package')
2 |
3 | module.exports = {
4 | /**
5 | * Ref:https://v1.vuepress.vuejs.org/config/#title
6 | */
7 | title: 'MOS',
8 | /**
9 | * Ref:https://v1.vuepress.vuejs.org/config/#description
10 | */
11 | description: description,
12 |
13 | /**
14 | * Extra tags to be injected to the page HTML `
`
15 | *
16 | * ref:https://v1.vuepress.vuejs.org/config/#head
17 | */
18 | head: [
19 | ['meta', { name: 'theme-color', content: '#af3e3e' }],
20 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
21 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }]
22 | ],
23 |
24 | /**
25 | * Theme configuration, here is the default theme configuration for VuePress.
26 | *
27 | * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html
28 | */
29 | themeConfig: {
30 | repo: 'https://github.com/datatrash/mos',
31 | editLinks: false,
32 | docsDir: 'docs/src',
33 | editLinkText: '',
34 | lastUpdated: false,
35 | nav: [
36 | {
37 | text: 'Guide',
38 | link: '/guide/',
39 | }
40 | ],
41 | sidebar: {
42 | '/guide/': [
43 | {
44 | title: 'Getting started',
45 | collapsable: false,
46 | children: [
47 | '',
48 | 'project-setup',
49 | 'assembler',
50 | 'advanced',
51 | 'unit-testing'
52 | ]
53 | },
54 | {
55 | title: 'IDE support',
56 | collapsable: false,
57 | children: [
58 | 'ide/',
59 | 'ide/vscode'
60 | ]
61 | },
62 | {
63 | title: 'CLI',
64 | collapsable: false,
65 | children: [
66 | 'cli/'
67 | ]
68 | }
69 | ],
70 | }
71 | },
72 |
73 | markdown: {
74 | lineNumbers: true
75 | },
76 |
77 | /**
78 | * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/
79 | */
80 | plugins: [
81 | '@vuepress/plugin-back-to-top',
82 | '@vuepress/plugin-medium-zoom',
83 | '@vuepress/active-header-links',
84 | [ 'sitemap', { hostname: 'http://mos.datatra.sh' } ]
85 | ]
86 | }
87 |
--------------------------------------------------------------------------------
/docs/src/.vuepress/enhanceApp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Client app enhancement file.
3 | *
4 | * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements
5 | */
6 |
7 | export default ({
8 | Vue, // the version of Vue being used in the VuePress app
9 | options, // the options for the root Vue instance
10 | router, // the router instance for the app
11 | siteData // site metadata
12 | }) => {
13 | // ...apply enhancements for the site.
14 | }
15 |
--------------------------------------------------------------------------------
/docs/src/.vuepress/public/mos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datatrash/mos/12aa86cb635c89d7a920d36c533dc70ae6ebbf38/docs/src/.vuepress/public/mos.png
--------------------------------------------------------------------------------
/docs/src/.vuepress/styles/index.styl:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Styles here.
3 | *
4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl
5 | */
6 |
7 | .home .hero img
8 | max-width 450px
9 |
--------------------------------------------------------------------------------
/docs/src/.vuepress/styles/palette.styl:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom palette here.
3 | *
4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl
5 | */
6 |
7 | $accentColor = #af3e3e
8 | $textColor = #2c3e50
9 | $borderColor = #eaecef
10 | $codeBgColor = #282c34
11 |
--------------------------------------------------------------------------------
/docs/src/guide/assembler.md:
--------------------------------------------------------------------------------
1 | # Assembler syntax
2 | The assembler allows you to write regular 6502 assembly instructions. However, some more powerful features are of course also available.
3 |
4 | ## Labels
5 | Labels can be defined to make it easier to refer to memory locations. Labels should consist of a valid identifier followed by a colon. A valid identifier starts with a character or underscore and may contain only characters, underscores or numbers.
6 |
7 | For example, a label can be used to loop:
8 | ```asm6502
9 | ldx #$20
10 | label:
11 | dex
12 | bne label
13 | ```
14 |
15 | You can also add braces after the label to aid formatting:
16 | ```asm6502
17 | ldx #$20
18 | label: {
19 | dex
20 | bne label
21 | }
22 | ```
23 |
24 | ## Variables and constants
25 | You can define variables and constants using the `.var` and `.const` directives respectively. They can then be used in expressions.
26 |
27 | For example:
28 | ```asm6502
29 | .const BORDER_COLOUR = $d020
30 |
31 | lda #7
32 | sta BORDER_COLOUR
33 | ```
34 |
35 | Variables may be redefined, constants may not.
36 |
37 | ## Symbol scopes
38 | Labels, variables and constants are _symbols_. Symbols are defined in _scopes_. A scope is defined by a _block_ (starting with `{` and ending with `}`). As long as symbols reside in different scopes they can have duplicate names. You can use the `super` keyword to access symbols in outer scopes.
39 |
40 | Sounds complicated perhaps, so here's an example:
41 | ```asm6502
42 | label: {
43 | label: {
44 | jmp label
45 | jmp super.label
46 | }
47 | }
48 | ```
49 |
50 | The first `jmp` will jump to the innermost label. The second `jmp` will jump to the outermost label.
51 |
52 | ## Automatic symbols
53 | Some symbols are generated for you automatically.
54 |
55 | ### `-` and `+`
56 | You can use `-` or `+` to refer to the start or the end of a block.
57 |
58 | For instance, the following code loops 64 times:
59 | ```asm6502
60 | ldx #$40
61 | {
62 | dex
63 | bne -
64 | }
65 | ```
66 |
67 | ## Loops
68 | Loops may be generated using the `.loop` directive:
69 |
70 | ```asm6502
71 | .loop 5 {
72 | lda #$00
73 | sta $0400 + index
74 | }
75 | ```
76 |
77 | The `index` symbol contains the current loop index (zero-based).
78 |
79 | ## Expressions
80 | Simple calculations may be performed.
81 |
82 | This program:
83 | ```asm6502
84 | lda #5 + 10
85 | ```
86 |
87 | Is equal to:
88 | ```asm6502
89 | lda #15
90 | ```
91 |
92 | ### Operators
93 | Supported operators are:
94 | - `*` Multiplication
95 | - `/` Division
96 | - `%` Modulo
97 | - `<<` Shift left
98 | - `>>` Shift right
99 | - `^` Exclusive or (XOR)
100 | - `+` Addition
101 | - `-` Subtraction
102 |
103 | You can also use parentheses, for example:
104 |
105 | ```asm6502
106 | lda #(3 + 4) * 5
107 | ```
108 |
109 | ### Equality tests
110 | Equality tests may also be performed. They will evaluate to `0` when false and `1` when true:
111 | - `==` Equality
112 | - `!=` Inequality
113 | - `>` Greater than
114 | - `>=` Greater than or equal
115 | - `<` Less than
116 | - `<=` Less than or equal
117 | - `&&` And
118 | - `||` Or
119 |
120 | ### Word operators
121 | Additionally, the high or low byte of 16-bit variables may be accessed using the `<` and `>` modifiers, e.g.:
122 |
123 | ```asm6502
124 | .const ADDRESS = $1234
125 | lda #ADDRESS // x will now contain '$12'
127 | ```
128 |
129 | ### Modifiers
130 | There are two ways to modify a factor in an expression.
131 |
132 | - Prefix with `!` to convert `0` into `1` and any positive number into `0`
133 | - Prefix with `-` to negate the value
134 |
135 | ### Built-in functions
136 | Currently the only built-in function is `defined` which evaluates to `1` if a variable or constant is defined and to `0` otherwise.
137 |
138 | ```asm6502
139 | .const ADDRESS = $1234
140 | lda defined(ADDRESS) // a will now contain '1'
141 | lda !defined(ADDRESS) // a will now contain '0'
142 | ```
143 |
144 | ## String handling
145 | It is possible to use strings in expressions and in variable definitions, e.g.:
146 |
147 | ```asm6502
148 | .const MY_HELLO = "hello"
149 | .const MY_WORLD = " world"
150 | .const GREETING = MY_HELLO + MY_WORLD
151 | ```
152 |
153 | It is also possible to do string interpolation using curly braces, e.g.:
154 |
155 | ```asm6502
156 | .const MY_HELLO = "hello"
157 | .const GREETING = "{MY_HELLO} world" // <-- results in "hello world"
158 | ```
159 |
160 | ## Data definition
161 | You may include data inline like so:
162 |
163 | ```asm6502
164 | .byte 1, 2, 3
165 | ```
166 |
167 | Supported data types are `.byte`, `.word` and `.dword`.
168 |
169 | ## Text definition
170 | You may include text inline like so:
171 |
172 | ```asm6502
173 | .text "hello"
174 | ```
175 |
176 | The default encoding is `ascii`. You may also use the encodings `petscii` or `petscreen`. The latter emits Commodore-compatible screen codes. For example:
177 |
178 | ```asm6502
179 | .text petscreen "abc" // This emits $01, $02, $03
180 | ```
181 |
182 | ### Including from files
183 | It is also possible to include data from files, like so:
184 |
185 | ```asm6502
186 | .file "foo.bin"
187 | ```
188 |
189 | The file is located relative to the source file that contains the `.file` directive.
190 |
191 | ## Comments
192 | Lines may end with a C++-style `//` comment, like so:
193 |
194 | ```asm6502
195 | nop // hello, I am a comment
196 | ```
197 |
198 | C-style comment blocks are also supported:
199 | ```asm6502
200 | /*
201 | hello there!
202 | */
203 | nop
204 | ```
205 |
206 | C-style comments may also be nested.
207 |
208 | ## Conditional assembly
209 | It is possible to conditionally assemble chunks of code by wrapping them in an `.if` block:
210 |
211 | ```asm6502
212 | .const FOO = 1
213 |
214 | .if defined(FOO) {
215 | nop
216 | } else {
217 | brk
218 | }
219 | ```
220 |
221 | The `else` clause is optional.
222 |
223 | ## Program counter
224 | During assembly it is possible to change the current program counter (i.e. the location where instructions are assembled to).
225 |
226 | ### Setting
227 | You can set the program counter with the `*` directive, like so:
228 |
229 | ```asm6502
230 | * = $0800
231 | ```
232 |
233 | ### Aligning
234 | You can move the program counter forward to make it align on a certain number of bytes, using the `.align` directive:
235 |
236 | ```asm6502
237 | .align 256
238 | nop // This will always be assembled to $xx00
239 | ```
240 |
--------------------------------------------------------------------------------
/docs/src/guide/cli/index.md:
--------------------------------------------------------------------------------
1 | # Command-line interface
2 |
3 | The MOS binary has a command-line interface. `mos` can be invoked with various subcommands.
4 |
5 | Many of these subcommands don't have any parameters, since all configuration is read from the `mos.toml` project configuration file.
6 |
7 | ## build
8 | To build your application call `mos build`.
9 |
10 | ## format
11 | To format the source code of your application in-place, call `mos format`.
12 |
13 | ## init
14 | To create an empty project configuration, file, call `mos init`.
15 |
16 | ## lsp
17 | To launch the [Language Server](https://microsoft.github.io/language-server-protocol/), call `mos lsp`. Typically you will only do this if you are developing an IDE plugin.
--------------------------------------------------------------------------------
/docs/src/guide/ide/index.md:
--------------------------------------------------------------------------------
1 | # IDE support
2 |
3 | MOS provides a language server and debug adapter that can be used by IDEs to provide tooling.
4 |
5 | It is used to provide a Visual Studio Code extension, and also [an Emacs package](https://github.com/themkat/mos-mode). The Emacs package provides the same features as the Visual Studio Code extension, and you can refer to the VSCode documentation for details.
6 |
7 | 
--------------------------------------------------------------------------------
/docs/src/guide/ide/mos-vscode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datatrash/mos/12aa86cb635c89d7a920d36c533dc70ae6ebbf38/docs/src/guide/ide/mos-vscode.jpg
--------------------------------------------------------------------------------
/docs/src/guide/ide/vscode.md:
--------------------------------------------------------------------------------
1 | # Visual Studio Code extension
2 |
3 | MOS has a Visual Studio Code extension. Follow the [project setup guide](../#visual-studio-code-extension) to install it.
4 |
5 | Once you have it installed, the following features will become available:
6 | * Building your program
7 | * Launching and debugging your program in VICE
8 | * Syntax highlighting
9 | * Find usages
10 | * Go to definition
11 | * Format document
12 | * Format on-type
13 | * Automatic indentation
14 | * Show function documentation on hover
15 |
16 | The debugger supports breakpoints, local symbols, watches, setting variable values and evaluating expressions.
17 |
18 | ::: danger
19 | The debugger requires VICE 3.5+, which introduces the `-binarymonitor` command line argument.
20 | :::
21 |
22 | ## Building and launching
23 | To build your application from within VSCode, use the `mos: Build` build command.
24 |
25 | You can also automatically create a `launch.json` configuration file, but here is a manual configuration:
26 |
27 | ```json
28 | {
29 | "version": "0.2.0",
30 | "configurations": [
31 | {
32 | "type": "mos",
33 | "request": "launch",
34 | "name": "Launch",
35 | "workspace": "${workspaceFolder}",
36 | "preLaunchTask": "mos: Build",
37 | "vicePath": "",
38 | }
39 | ]
40 | }
41 | ```
42 |
43 | If you specify a valid path to a VICE executable in the `vicePath` option you can launch or debug your application from within Visual Studio Code.
44 |
45 | ## Running tests
46 | The extension provides a "run all tests" extension that allows you to run all tests in your project.
47 |
48 | When you define a test (using the `.test` directive) a CodeLens will appear which will allow you to run or debug that specific test using an emulated 6502.
49 |
50 | ## Showing documentation on hover
51 | You can annotate a symbol with a special `///` comment syntax. When you hover over the symbol somewhere in your code the comment will be shown as markdown.
52 |
53 | So, for instance:
54 | ```asm6502
55 | /// This subroutine is **awesome**.
56 | my_subroutine: {
57 | rts
58 | }
59 |
60 | jsr my_subroutine // <-- hovering over my_subroutine will show the documentation
61 | ```
62 |
63 | ## Watch expressions
64 | You can add watch expressions to keep track of the value of symbols whenever you hit a breakpoint.
65 |
66 | You can also access a few extra symbols that allow you to access CPU registers and memory locations:
67 |
68 | | Key | Description |
69 | |-----------------------------|-------------------------------------------|
70 | | cpu.a | The accumulator (A) register |
71 | | cpu.x | The X register |
72 | | cpu.y | The Y register |
73 | | cpu.sp | The stack pointer |
74 | | * | The program counter |
75 | | cpu.flags.zero | The Z flag |
76 | | cpu.flags.carry | The C flag |
77 | | cpu.flags.interrupt_disable | The I flag |
78 | | cpu.flags.decimal | The D flag |
79 | | cpu.flags.overflow | The V flag |
80 | | cpu.flags.negative | The N flag |
81 | | ram(...) | Read a byte from ram, e.g. `ram($d020)` |
82 | | ram16(...) | Read a word from ram, e.g. `ram16($0314)` |
83 |
84 | ## Set variable values during debugging
85 | ::: danger
86 | Currently you are only able to set register values. Other types of variables will give an error!
87 | :::
88 |
89 | You can set the value of variables, either as decimal-, hexadecimal- or binary value. You specify the type or value with a prefix.
90 |
91 | | Prefix | Type | Example |
92 | |--------|-------------|-----------|
93 | | | Decimal | 123 |
94 | | $ | Hexadecimal | $d01 |
95 | | % | Binary | %00010010 |
96 |
97 |
98 |
99 | ## Options
100 | The following plugin options are available:
101 |
102 | | Key | Type | Description |
103 | | --- | ---- | ----------- |
104 | | `mos.path` | Path | By default, the extension will automatically download and update the `mos` executable. If for some reason you want to use your own `mos` executable you can fill in the path to this executable here. |
--------------------------------------------------------------------------------
/docs/src/guide/index.md:
--------------------------------------------------------------------------------
1 | # Overview
2 | MOS is an assembler targeting the MOS 6502 CPU.
3 |
4 | ::: danger
5 | **This documentation is preliminary.**
6 |
7 | It's just here to make sure current features at least have _some_ documentation. Everything is subject to change and will hopefully improve significantly :smile:
8 | :::
9 |
10 | # Getting started
11 | Let's dive into the **MOS** pit :metal: (...sorry).
12 |
13 | ## Getting the tools
14 | There are two ways to work with MOS. You can download the CLI executable, but a quicker way is to install the Visual Studio Code extension.
15 |
16 | ### Visual Studio Code extension
17 | Installing the extension consists of these steps:
18 | * Open Visual Studio Code
19 | * In the `Extensions` menu, install the extension called `mos`.
20 | * Open a folder and place an empty `mos.toml` in this folder. This is the main configuration file for `mos` and the presence of this file will activate the extension.
21 | * The extension will ask if you want to install the `mos` executable automatically. Allow it to do so.
22 | * Done! If you'd like to know more, you can find more detailed documentation [here](./ide/vscode).
23 |
24 | ### Command line
25 | You can download the latest release of MOS [here](https://github.com/datatrash/mos/releases).
26 |
27 | Extracting the archive will leave you with a binary called `mos`. You can put it in some easily accessible place. The rest of the documentation will assume it's in your `PATH`.
28 |
29 | The current version of MOS only generates Commodore-compatible `.prg` files. So, let's try to build a tiny Commodore program.
30 |
31 | ## Building a sample application
32 | Let's create a new file called `main.asm` and fill it with these contents:
33 | ```asm6502
34 | inc $d020
35 | rts
36 | ```
37 | This is a tiny program that changes the border colour on a Commodore 64.
38 |
39 | Next, you can build it like so:
40 | ```
41 | > mos build
42 | ```
43 |
44 | If everything went well you will see no output, but a `main.prg` file will have been created in a directory called `target`. You can load this `.prg` in your favourite C64 emulator. Run it by typing `sys 8192` in the BASIC prompt. The border colour will change, whoo!
45 |
46 | ::: tip
47 | You may be wondering why MOS is compiling `main.asm` when no input filename was passed in. Well, you can configure the input filename and many other things by creating a configuration file called `mos.toml`. For now, let's just keep the defaults.
48 | :::
49 |
50 | ## Errors in your code
51 | What happens when there is an error :boom:? Well, let's edit `main.asm` and change it to this:
52 | ```asm6502
53 | inx $d020
54 | rts
55 | ```
56 | `inx $d020` is not a valid instruction, and this is what you will see:
57 | ```{2}
58 | > mos build
59 | main.asm:1:5: error: unexpected '$d020'
60 | ```
61 |
62 | The error indicates that it is located in file `main.asm`, on line 1, column 5. In this case, it didn't expect an operand because `inx` does not need any.
63 |
64 | ## What next?
65 | Alright, that was the quickest possible introduction! The remainder of the documentation will go over all the features in greater detail.
--------------------------------------------------------------------------------
/docs/src/guide/project-setup.md:
--------------------------------------------------------------------------------
1 | # Project setup
2 |
3 | You can create a new MOS project by creating a file called `mos.toml` in the root of your project. This is the MOS **configuration file** and it allows you to customize things like how MOS assembles and formats your project.
4 |
5 | The simplest way to create this file is via MOS itself:
6 | ```
7 | > mos init
8 | ```
9 |
10 | This creates a `mos.toml` file with a few basic settings. This is enough to get started, but you can customize these settings too.
11 |
12 | ## Build options
13 | These are the default options under the `build` section in `mos.toml`:
14 |
15 | ```toml
16 | [build]
17 | entry = "main.asm"
18 | target-directory = "target"
19 | listing = false
20 | symbols = []
21 | output-format = "prg"
22 | ```
23 |
24 | | Key | Type | Description |
25 | | --- | ---- | ----------- |
26 | | `entry` | filename | The source file from which MOS should start assembling |
27 | | `target-directory` | directory name | The directory in which all output (binaries, symbols) is placed
28 | | `listing` | boolean | Generate listing files, containing disassembled code? |
29 | | `symbols` | array | Which symbol files to generate. Currently only `"vice"` is supported.
30 | | `output-filename` | string | In case the output format results in a single file, you can specify the output filename here. Otherwise the name of the entry source file will be used. (with a different extension). |
31 | | `output-format` | `"prg`" or `"bin`" | "prg" adds a 2-byte header that contains the loading position (used for Commodore emulators) |
32 |
33 | So, if you want to leave all defaults as-is, but would want to generate symbols for Vice, the `build` section in your `mos.toml` would look like this:
34 |
35 | ```toml
36 | [build]
37 | symbols = ["vice"]
38 | ```
39 |
40 | ## Formatting options
41 | The formatter has a few options you can tweak, but it is not extensive yet. The following `mos.toml` represents the default formatting options:
42 |
43 | ```toml
44 | [formatting]
45 | mnemonics.casing = 'lowercase'
46 | mnemonics.register-casing = 'lowercase'
47 | braces.position = 'same-line'
48 | whitespace.indent = 4
49 | whitespace.label-margin = 20
50 | whitespace.label-alignment = right
51 | whitespace.code-margin = 30
52 | listing.num-bytes-per-line = 8
53 | ```
54 |
55 | | Key | Type | Description |
56 | | --- | ---- | ----------- |
57 | | mnemonics.casing | `uppercase`, `lowercase` | The casing of mnemonics (e.g. `NOP`) |
58 | | mnemonics.register-casing | `uppercase`, `lowercase` | The casing of register suffixes (e.g. the `x` in `lda $fb,x`) |
59 | | braces.position | `same_line`, `new_line` | Where to place the braces in things like if statements |
60 | | whitespace.indent | number | How many spaces (no tabs yet... :innocent:) to indent in block statements |
61 | | whitespace.label-margin | number | How many characters to reserve for labels |
62 | | whitespace.label-alignment | `left`, `right` | How to align labels |
63 | | whitespace.code-margin | number | How many characters to reserve for code (the rest is reserved for comments) |
64 | | listing.num-bytes-per-line | number | When generating a listing file, this specifies how many bytes should be emitted per line |
--------------------------------------------------------------------------------
/docs/src/guide/unit-testing.md:
--------------------------------------------------------------------------------
1 | # Unit testing
2 |
3 | When writing code, it can be very convenient to be able to test individual subroutines in isolation. For instance, when writing a sorting routine, it is nice to be able to test that in a few different scenarios. This will give you extra confidence when refactoring the sorting algorithm, since if the tests still pass you know you didn't break anything.
4 |
5 | ## Defining a test
6 | A test can be defined anywhere in your source files, and looks like a bit like this:
7 |
8 | ```asm6502
9 | .test "test_name" {
10 | // Do some setup
11 | lda #123
12 | sta foo
13 |
14 | jsr my_subroutine
15 |
16 | // Check the output
17 | .assert cpu.x == 7
18 | .assert ram(foo) = 123
19 |
20 | // Exit the test
21 | brk
22 | }
23 | ```
24 |
25 | ## Running tests
26 | You can run tests by invoking the `mos test` command. Every test will be assembled individually and run on an emulated 6502.
27 |
28 | Please note that if you have defined [banks](advanced.html#banks) then the unit test will only have access to the bank it is defined in.
29 |
30 | ## Assertions
31 | In every part of your test (and even the subroutines you are testing) you can add `.assert` directives. The expression provided should be true, otherwise the test fails.
32 |
33 | ### Available flags
34 | Since the tests are run on an emulated 6502 you can access CPU and memory information in your assertions as you well:
35 |
36 | | Key | Description |
37 | |-----------------------------|-------------------------------------------|
38 | | cpu.a | The accumulator (A) register |
39 | | cpu.x | The X register |
40 | | cpu.y | The Y register |
41 | | cpu.sp | The stack pointer |
42 | | * | The program counter |
43 | | cpu.flags.zero | The Z flag |
44 | | cpu.flags.carry | The C flag |
45 | | cpu.flags.interrupt_disable | The I flag |
46 | | cpu.flags.decimal | The D flag |
47 | | cpu.flags.overflow | The V flag |
48 | | cpu.flags.negative | The N flag |
49 | | ram(...) | Read a byte from ram, e.g. `ram($d020)` |
50 | | ram16(...) | Read a word from ram, e.g. `ram16($0314)` |
51 |
52 | ### Custom failure message
53 | When an assertion fails, it will log the expression that was being tested. You can optionally also provide a custom message, like so:
54 |
55 | ```asm6502
56 | .assert cpu.y == 123 "this is a custom message"
57 | ```
58 |
59 | ## Tracing
60 | You can add `.trace` directives to simply log some information during the test run, e.g.:
61 |
62 | ```asm6502
63 | .trace (cpu.x, cpu.y, ram($d020))
64 | ```
65 |
66 | Tracing only appears for failing tests, so if your test succeeds the tracing will not pollute your output.
67 |
68 | If you don't pass in any parameters you will get a detailed CPU dump, e.g.:
69 |
70 | ```asm6502
71 | .trace
72 | ```
73 |
74 | Will print something like: `* = $2000, SP = $FD, flags = -----I--, A = $00, X = $00, Y = $00`
--------------------------------------------------------------------------------
/docs/src/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | heroText: 6502, modernized.
4 | heroImage: mos.png
5 | tagline: MOS helps you to build applications that target a MOS 6502 CPU.
6 | actionText: Get started →
7 | actionLink: /guide/
8 | features:
9 | - title: Assembler
10 | details: A very fast multipass macro assembler that can quickly rip through any amount of code.
11 | - title: Formatter
12 | details: Automatic source code formatting to make sure your code always looks its shiniest.
13 | - title: IDE-ready
14 | details: Debugging, syntax highlighting, unit testing, automatic indentation, refactoring support.
15 | footer: MIT Licensed | Copyright © 2021-present datatra.sh
16 | ---
--------------------------------------------------------------------------------
/examples/atari800/colors/atari800.asm:
--------------------------------------------------------------------------------
1 | .macro xex_load_header() {
2 | .define bank {
3 | name = "xex-load-header"
4 | create-segment = true
5 | }
6 |
7 | .segment "xex-load-header" {
8 | .byte $ff, $ff
9 | }
10 | }
11 |
12 | .macro xex_segment_header(name, start, end) {
13 | // the end of a segment is inclusive, so the last emitted byte is actually at end - 1
14 | .const end_offset = end - 1
15 |
16 | .define bank {
17 | name = "${name}-header"
18 | create-segment = true
19 | }
20 |
21 | .segment "${name}-header" {
22 | .byte start, end_offset
23 | }
24 | }
25 |
26 | .macro xex_segment_ini(name, addr) {
27 | .define bank {
28 | name = "${name}-ini"
29 | create-segment = true
30 | }
31 |
32 | .segment "${name}-ini" {
33 | .byte $e2, $02, $e3, $02, addr
34 | }
35 | }
36 |
37 | .macro xex_segment_run(name, addr) {
38 | .define bank {
39 | name = "${name}-run"
40 | create-segment = true
41 | }
42 |
43 | .segment "${name}-run" {
44 | .byte $e0, $02, $e1, $02, addr
45 | }
46 | }
--------------------------------------------------------------------------------
/examples/atari800/colors/main.asm:
--------------------------------------------------------------------------------
1 | /// Thanks to F#READY for help in preparing this example
2 | /// After loading, hit 'start', then wait two seconds and hit 'select'. Colors should appear.
3 | .import * from "atari800.asm"
4 |
5 | xex_load_header()
6 |
7 | ///////////////////////////////////////////////////
8 | // First XEX segment
9 | ///////////////////////////////////////////////////
10 | xex_segment_header("first", segments.first.start, segments.first.end)
11 |
12 | .define bank {
13 | name = "first"
14 | }
15 |
16 | .define segment {
17 | name = "first"
18 | bank = "first"
19 | start = $0600
20 | }
21 |
22 | .segment "first" {
23 | first: lda #0
24 | sta 710
25 |
26 | wait_start: lda $d01f
27 | cmp #6
28 | bne wait_start
29 | rts // continue loading
30 | }
31 |
32 | xex_segment_ini("first", first)
33 |
34 | ///////////////////////////////////////////////////
35 | // Second XEX segment
36 | ///////////////////////////////////////////////////
37 | xex_segment_header("second", segments.second.start, segments.second.end)
38 |
39 | .define bank {
40 | name = "second"
41 | }
42 |
43 | .define segment {
44 | name = "second"
45 | bank = "second"
46 | start = segments.first.end
47 | }
48 |
49 | .segment "second" {
50 | second: lda #34
51 | sta 710
52 |
53 | lda #0
54 | sta 20
55 | wait_2sec: lda 20
56 | cmp #100
57 | bne wait_2sec
58 |
59 | wait_select: lda $d01f
60 | cmp #5
61 | bne wait_select
62 | rts // continue loading
63 | }
64 |
65 | xex_segment_ini("second", second)
66 |
67 | ///////////////////////////////////////////////////
68 | // Main XEX segment
69 | ///////////////////////////////////////////////////
70 | xex_segment_header("main", segments.main.start, segments.main.end)
71 |
72 | .define bank {
73 | name = "main"
74 | }
75 |
76 | .define segment {
77 | name = "main"
78 | bank = "main"
79 | start = segments.second.end
80 | }
81 |
82 | .segment "main" {
83 | main: lda $d40b
84 | adc 20
85 | asl
86 | sta $d40a
87 | sta $d018
88 | jmp main
89 | }
90 |
91 | xex_segment_run("main", main)
--------------------------------------------------------------------------------
/examples/atari800/colors/mos.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | entry = "main.asm"
--------------------------------------------------------------------------------
/examples/c64/cartridge/main.asm:
--------------------------------------------------------------------------------
1 | .import * from "../shared/c64.asm"
2 |
3 | .macro cart_header() {
4 | .define bank {
5 | name = "cart_header"
6 | create-segment = true
7 | size = 64
8 | }
9 |
10 | .segment "cart_header" {
11 | // signature
12 | .text "C64 CARTRIDGE "
13 | // header size
14 | .byte 0, 0, 0, $40
15 | // version
16 | .word 1
17 | // hardware type 'MAGIC DESK'
18 | .byte 0, 19
19 | // EXROM line status
20 | .byte 1
21 | // GAME line status
22 | .byte 0
23 | // reserved
24 | .byte 0, 0, 0, 0, 0, 0
25 | // cartridge name and padding
26 | .text "EXAMPLE CARTRIDGE FOR MOS"
27 | .byte 0, 0, 0, 0, 0, 0, 0
28 | }
29 | }
30 |
31 | .macro bank_header(bank_idx) {
32 | .define bank {
33 | name = "bank_header_{bank_idx}"
34 | size = 16
35 | create-segment = true
36 | }
37 |
38 | .segment "bank_header_{bank_idx}" {
39 | // signature
40 | .text "CHIP"
41 | // packet length
42 | .byte 0, 0, $20, $10
43 | // chip type (0 = ROM, 1 = RAM / no ROM data, 2 = Flash ROM)
44 | .byte 0, 0
45 | // bank number
46 | .byte 0, bank_idx
47 | // starting load address
48 | .byte $80, $00
49 | // ROM image size
50 | .byte $20, $00
51 | }
52 |
53 | .define bank {
54 | name = "bank_{bank_idx}"
55 | size = 8192
56 | fill = 0
57 | }
58 |
59 | .define segment {
60 | name = "bank_{bank_idx}"
61 | bank = "bank_{bank_idx}"
62 | start = $8000
63 | }
64 | }
65 |
66 | cart_header()
67 | bank_header(0)
68 |
69 | .segment "bank_0" {
70 | .word coldstart
71 | .word warmstart
72 | .byte $C3, $C2, $CD, $38, $30
73 |
74 | coldstart: sei
75 | stx $d016
76 | jsr $fda3
77 | jsr $fd50
78 | jsr $fd15
79 | jsr $ff5b
80 | cli
81 |
82 | warmstart: inc $d020
83 | jmp warmstart
84 | }
85 |
86 | .segment "bank_0" {
87 | .test "header_is_in_the_right_place" {
88 | .assert ram($8004) == $C3
89 |
90 | }
91 | }
--------------------------------------------------------------------------------
/examples/c64/cartridge/mos.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | entry = "main.asm"
3 | output-format = "bin"
4 | output-filename = "cart.crt"
--------------------------------------------------------------------------------
/examples/c64/scroller/main.asm:
--------------------------------------------------------------------------------
1 | .import * from "../shared/c64.asm"
2 |
3 | basic_start(start)
4 |
5 | start: lda #colors.gray
6 | sta cursor_color
7 | jsr kernal.clrscr
8 | lda #0
9 | sta vic.background
10 | sta vic.foreground
11 |
12 | // Set 38-column mode
13 | lda vic.xscroll
14 | and #%11110111
15 | sta vic.xscroll
16 |
17 | {
18 | // Run all this code once per frame
19 | lda vic.raster_pos
20 | cmp #$80
21 | bne -
22 |
23 | // Do per-pixel soft-scroll
24 | dec xscroll
25 | bpl apply_xscroll
26 |
27 | // the xscroll has wrapped around, so reset it
28 | // and shift the screen by one char
29 | lda #7
30 | sta xscroll
31 |
32 | ldx #0
33 |
34 | {
35 | lda $0401, x
36 | sta $0400, x
37 | inx
38 | cpx #39
39 | bne -
40 | }
41 |
42 | // Read a new character from our scroller.
43 | // If it's $ff we should wrap around.
44 | ldx text_pos
45 | lda text, x
46 | cmp #$ff
47 | bne write_char
48 |
49 | lda #$00
50 | sta text_pos
51 | jmp apply_xscroll
52 |
53 | write_char: sta $0427
54 | inc text_pos
55 |
56 | // Apply our soft-scroll value to VIC's xscroll register
57 | apply_xscroll: lda vic.xscroll
58 | and #%11111000
59 | ora xscroll
60 | sta vic.xscroll
61 |
62 | // Back to top
63 | jmp -
64 | }
65 |
66 | xscroll: .byte 0
67 | text_pos: .byte 0
68 |
69 | text: {
70 | .text petscreen "mos says hello! "
71 | .byte $ff
72 | }
--------------------------------------------------------------------------------
/examples/c64/scroller/mos.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | entry = "main.asm"
3 | listing = true
4 | output-format = "prg"
5 |
--------------------------------------------------------------------------------
/examples/c64/shared/c64.asm:
--------------------------------------------------------------------------------
1 | /// Color of the cursor (taken into effect when clearing screen)
2 | .const cursor_color = $0286
3 |
4 | vic: {
5 | /// The low-byte of the raster position
6 | .const raster_pos = $d012
7 |
8 | /// Bits 7-6: Unused
9 | ///
10 | /// Bit 5: Reset-Bit
11 | ///
12 | /// Bit 4: Multi-Color Mode
13 | ///
14 | /// Bit 3: 38/40 column (1 = 40 cols)
15 | ///
16 | /// Bit 2-0: Smooth scroll
17 | .const xscroll = $d016
18 |
19 | /// Background color
20 | .const background = $d020
21 |
22 | /// Foreground color
23 | .const foreground = $d021
24 | }
25 |
26 | colors: {
27 | .const black = 0
28 | .const white = 1
29 | .const red = 2
30 | .const cyan = 3
31 | .const purple = 4
32 | .const green = 5
33 | .const blue = 6
34 | .const yellow = 7
35 | .const orange = 8
36 | .const brown = 9
37 | .const light_red = 10
38 | .const dark_gray = 11
39 | .const gray = 12
40 | .const light_green = 13
41 | .const light_blue = 14
42 | .const light_gray = 15
43 | }
44 |
45 | kernal: {
46 | /// Clears the screen in the current cursor color
47 | .const clrscr = $e544
48 | }
49 |
50 | /// Constructs a `0 sys*` basic line
51 | .macro basic_start(address) {
52 | * = $0801
53 |
54 | .byte $0c, $08, $00, $00, $9e
55 |
56 | .if address >= 10000 {
57 | .byte $30 + (address / 10000) % 10
58 | }
59 |
60 | .if address >= 1000 {
61 | .byte $30 + (address / 1000) % 10
62 | }
63 |
64 | .if address >= 100 {
65 | .byte $30 + (address / 100) % 10
66 | }
67 |
68 | .if address >= 10 {
69 | .byte $30 + (address / 10) % 10
70 | }
71 |
72 | .byte $30 + address % 10
73 | .byte 0, 0, 0
74 | }
--------------------------------------------------------------------------------
/examples/c64/unit-testing/main.asm:
--------------------------------------------------------------------------------
1 | // Based on a c64unit example
2 | .test "stack_pointer" {
3 | lda #6
4 | pha
5 | lda #4
6 | pha
7 |
8 | pla
9 | pla
10 |
11 | tsx
12 |
13 | .assert cpu.sp == $fd
14 |
15 | brk
16 | }
17 |
18 | .test "will_fail" {
19 | .loop 2 {
20 | .trace (index, *, ram($2000))
21 | }
22 | .trace
23 | .assert * == $1234
24 |
25 | // will never be reached
26 | nop
27 | }
28 |
29 | .test "will_succeed" {
30 | brk
31 | }
--------------------------------------------------------------------------------
/examples/c64/unit-testing/mos.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | entry = "main.asm"
--------------------------------------------------------------------------------
/mos-core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "mos-core"
3 | version = "0.0.0" # unused
4 | authors = ["Roy Jacobs "]
5 | edition = "2018"
6 | repository = "https://github.com/datatrash/mos"
7 | license = "MIT"
8 | keywords = ["retro", "6502", "assembler"]
9 |
10 | [lib]
11 |
12 | [dependencies]
13 | ansi_term = "0.12"
14 | bitflags = "1"
15 | codespan-reporting = "0.11"
16 | derive_more = "0.99"
17 | fs-err = "2"
18 | indexmap = "1.7"
19 | itertools = "0.10"
20 | log = { version = "0.4", features = ["max_level_trace", "release_max_level_info"] }
21 | loggerv = "0.7"
22 | nom = "7"
23 | nom_locate = "4"
24 | once_cell = "1.8"
25 | path-absolutize = "3"
26 | path-dedot = "3"
27 | pathdiff = "0.2"
28 | petgraph = "0.6"
29 | serde = { version = "1", features = ["derive"] }
30 | serde_json = "1"
31 | smallvec = "1"
32 | strum = { version = "0.23", features = ["derive"] }
33 | toml = "0.5"
34 |
35 | [dev-dependencies]
36 | mos-testing = { path = "../mos-testing" }
37 | tempfile = "3"
--------------------------------------------------------------------------------
/mos-core/src/cbm/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod petscii;
2 |
--------------------------------------------------------------------------------
/mos-core/src/codegen/config_extractor.rs:
--------------------------------------------------------------------------------
1 | use crate::codegen::CodegenContext;
2 | use crate::errors::CoreResult;
3 | use crate::parser::code_map::Span;
4 | use crate::parser::{Expression, Located, Token};
5 | use codespan_reporting::diagnostic::Diagnostic;
6 |
7 | pub struct ConfigExtractor<'a> {
8 | config_span: Span,
9 | kvps: Vec<(&'a Located, &'a Located)>,
10 | }
11 |
12 | impl<'a> ConfigExtractor<'a> {
13 | pub fn new(config_span: Span, kvps: &[(&'a Located, &'a Located)]) -> Self {
14 | Self {
15 | config_span,
16 | kvps: kvps.to_vec(),
17 | }
18 | }
19 |
20 | pub fn get_string(&self, ctx: &mut CodegenContext, key: &str) -> CoreResult {
21 | if let Some(str) = self.try_get_string(ctx, key)? {
22 | Ok(str)
23 | } else {
24 | let span = self
25 | .try_get_kvp(key)
26 | .map(|(k, v)| k.span.merge(v.span))
27 | .unwrap_or(self.config_span);
28 | Err(Diagnostic::error()
29 | .with_message(format!("could not evaluate configuration key '{}'", key))
30 | .with_labels(vec![span.to_label()])
31 | .into())
32 | }
33 | }
34 |
35 | pub fn try_get_string(
36 | &self,
37 | ctx: &mut CodegenContext,
38 | key: &str,
39 | ) -> CoreResult