├── .editorconfig
├── .github
├── FUNDING.yml
└── workflows
│ ├── release.yml
│ └── update-release-branch.yml
├── .gitignore
├── .vscode
├── extensions.json
└── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── _redirects
├── astro.config.mjs
├── package.json
├── pnpm-lock.yaml
├── public
├── FaSolidUser.svg
├── favicon-dark.svg
├── favicon-with-background.svg
├── favicon.ico
├── favicon.svg
├── images
│ └── screen.png
├── logo-with-green-background.svg
└── tips
│ ├── Comments.html
│ ├── Labels.html
│ └── Number.html
├── pwa-assets.config.js
├── release.config.mjs
├── scripts
└── build-emulator.sh
├── src
├── components
│ ├── ActionButton.jsx
│ ├── Actions.jsx
│ ├── ActionsLoader.jsx
│ ├── App.jsx
│ ├── Assembled.jsx
│ ├── Assembled.module.css
│ ├── Card.astro
│ ├── CodeFormatter.jsx
│ ├── CodeFormatterLoader.jsx
│ ├── CodingArea.jsx
│ ├── CopyComponent.jsx
│ ├── DocumentationFooter.astro
│ ├── DocumentationHead.astro
│ ├── DonationPlea.jsx
│ ├── FAQs.astro
│ ├── FileActions.jsx
│ ├── FileNode.jsx
│ ├── Flags.jsx
│ ├── FolderNode.jsx
│ ├── Footer.astro
│ ├── Header.astro
│ ├── IOPorts.jsx
│ ├── InterruptState.jsx
│ ├── KeyboardShortcuts.jsx
│ ├── KeyboardShortcuts.module.css
│ ├── LEDArray.jsx
│ ├── LambdaClassesPoster.jsx
│ ├── LeftPanel.css
│ ├── LeftPanel.jsx
│ ├── MemoryGrid.jsx
│ ├── MemoryList.jsx
│ ├── NatoryAd.jsx
│ ├── PlusDialog.jsx
│ ├── Registers.jsx
│ ├── RightPanel.jsx
│ ├── Settings.jsx
│ ├── StoreContext.js
│ ├── Terminal.jsx
│ ├── TextToolip.module.css
│ ├── TextTooltip.jsx
│ ├── ThemeSwitcher.astro
│ ├── Tips.astro
│ ├── Toolbox.jsx
│ ├── Workspace.jsx
│ ├── WorkspaceTree.jsx
│ ├── analytics
│ │ ├── CookieBanner.astro
│ │ ├── PostHogInit.astro
│ │ ├── PostHogInitialize.astro
│ │ ├── identify.js
│ │ └── tracker.js
│ ├── codemirror
│ │ ├── 8085.js
│ │ ├── CodeMirror.css
│ │ └── CodeMirror.jsx
│ ├── generic
│ │ ├── DelayedComponent.jsx
│ │ ├── Dialog.css
│ │ ├── Dialog.jsx
│ │ ├── DropdownMenu.css
│ │ ├── DropdownMenu.jsx
│ │ ├── Select.css
│ │ ├── Select.jsx
│ │ ├── Switch.css
│ │ ├── Switch.jsx
│ │ ├── Tabs.css
│ │ ├── Tabs.jsx
│ │ ├── Toast.css
│ │ ├── Toast.jsx
│ │ ├── Tooltip.css
│ │ ├── Tooltip.jsx
│ │ └── wrapWithClass.js
│ ├── pwa.js
│ ├── styles.css
│ └── toaster.jsx
├── content
│ ├── changelog
│ │ ├── 2024-11-11.md
│ │ ├── 2024-11-16.md
│ │ ├── 2025-04-24.md
│ │ ├── 2025-05-03.md
│ │ ├── 2025-05-05.md
│ │ ├── 2025-05-06.md
│ │ ├── 2025-05-07.md
│ │ ├── 2025-05-08.md
│ │ ├── 2025-05-14.md
│ │ └── images
│ │ │ ├── android-sim8085-install-2024-11-16.jpeg
│ │ │ ├── doc-section-2024-11-11.png
│ │ │ ├── full-memory-search-top-2025-05-03.png
│ │ │ ├── instruction-timing-setting-2025-05-03.png
│ │ │ ├── interrupt-controls-2025-05-08.png
│ │ │ ├── interrupt-panel-2025-05-08.png
│ │ │ ├── ios-sim8085-install-2024-11-16.jpeg
│ │ │ ├── keyboard-shortcuts-2024-11-11.png
│ │ │ ├── led-array-2025-05-05.png
│ │ │ ├── sim8085-mobile-layout-2024-11-16.jpeg
│ │ │ └── toolbox-panel-2025-05-05.png
│ ├── config.js
│ └── docs
│ │ └── docs
│ │ ├── en
│ │ ├── 404.md
│ │ ├── 8085.mdx
│ │ ├── app-install.mdx
│ │ ├── assembly.mdx
│ │ ├── directives
│ │ │ └── end.md
│ │ ├── guides
│ │ │ └── example.md
│ │ ├── index.mdx
│ │ ├── infinite-loop-reasons.mdx
│ │ ├── instructions
│ │ │ ├── aci.md
│ │ │ └── adc.md
│ │ ├── interrupts.mdx
│ │ ├── reference
│ │ │ ├── ascii.md
│ │ │ └── instruction-summary.md
│ │ └── unsupported.mdx
│ │ └── images
│ │ ├── install-using-chrome.png
│ │ ├── install-using-edge.png
│ │ ├── interrupt-controls-2025-05-08.png
│ │ └── interrupt-panel-2025-05-08.png
├── core
│ ├── 8085.c
│ ├── 8085.js
│ ├── 8085.pegjs
│ ├── 8085.wasm
│ ├── cpuState.js
│ ├── parserTests
│ │ └── label.test.js
│ ├── sdk85-monitor.asm
│ ├── sdk85-monitor.lst
│ ├── simulator-library.js
│ ├── simulator.js
│ └── simulator.test.js
├── env.d.ts
├── icons
│ ├── bmc.svg
│ ├── computer-desktop.svg
│ ├── github.svg
│ ├── hand-holding-heart.svg
│ ├── kofi.svg
│ ├── lock.svg
│ ├── logo-with-green-background.svg
│ ├── logo.svg
│ ├── menu.svg
│ ├── moon.svg
│ ├── paypal.svg
│ ├── razorpay.svg
│ ├── refresh.svg
│ ├── regular-dock-left.svg
│ ├── regular-dock-right.svg
│ ├── solid-dock-left.svg
│ ├── solid-dock-right.svg
│ ├── sponsor.svg
│ ├── sun.svg
│ ├── triangle-info.svg
│ └── youtube.svg
├── images
│ ├── lambda-classes-poster.png
│ ├── natory-light.png
│ ├── natory.png
│ ├── pc-start-value-textbox.png
│ └── project-track-poster.jpg
├── layouts
│ └── Layout.astro
├── lib
│ └── supabase.js
├── pages
│ ├── changelog.astro
│ ├── donate.astro
│ ├── index.astro
│ ├── login.astro
│ ├── privacy-policy.astro
│ └── tos.astro
├── pwa.js
├── store
│ └── store.js
├── tailwind.docs.config.mjs
├── tailwind.docs.css
├── tailwind.main.config.mjs
├── tailwind.main.css
├── tests
│ ├── aci.test.js
│ ├── adi.test.js
│ ├── ani.test.js
│ ├── call.test.js
│ ├── cma.test.js
│ ├── cmc.test.js
│ ├── cpi.test.js
│ ├── daa.test.js
│ ├── dad.test.js
│ ├── dcr.test.js
│ ├── dcx.test.js
│ ├── inr.test.js
│ ├── inx.test.js
│ ├── jump.test.js
│ ├── lda.test.js
│ ├── ldax.test.js
│ ├── lhld.test.js
│ ├── lxi.test.js
│ ├── mov.test.js
│ ├── mvi.test.js
│ ├── ori.test.js
│ ├── pchl.test.js
│ ├── ral.test.js
│ ├── rar.test.js
│ ├── rc.test.js
│ ├── return.test.js
│ ├── rlc.test.js
│ ├── rnc.test.js
│ ├── rnz.test.js
│ ├── rp.test.js
│ ├── rrc.test.js
│ ├── rz.test.js
│ ├── sbb.test.js
│ ├── sbi.test.js
│ ├── shld.test.js
│ ├── sphl.test.js
│ ├── sta.test.js
│ ├── stax.test.js
│ ├── stc.test.js
│ ├── sui.test.js
│ ├── test-utils.js
│ ├── xchg.test.js
│ ├── xri.test.js
│ └── xthl.test.js
├── themes
│ ├── dark.css
│ └── light.css
└── utils
│ ├── NumberFormat.js
│ ├── deepMerge.js
│ └── lens.js
├── tailwind.config.mjs
├── tsconfig.json
└── vitest.config.mjs
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | max_line_length = 120
10 | tab_width = 4
11 |
12 | [*.yml]
13 | indent_size = 2
14 | tab_width = 2
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: debjitbis08
2 | buy_me_a_coffee: debjit.biswas
3 | ko_fi: debjitbiswas
4 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - master # or main
6 |
7 | permissions:
8 | contents: write # for checkout
9 |
10 | jobs:
11 | release:
12 | name: Release
13 | runs-on: ubuntu-latest
14 | permissions:
15 | contents: write # to be able to publish a GitHub release
16 | issues: write # to be able to comment on released issues
17 | pull-requests: write # to be able to comment on released pull requests
18 | id-token: write # to enable use of OIDC for npm provenance
19 | strategy:
20 | matrix:
21 | node-version: [20] # Use the desired Node.js version
22 | steps:
23 | - uses: actions/checkout@v4 # Checkout the repository
24 | - name: Install pnpm
25 | uses: pnpm/action-setup@v4 # Use pnpm's official GitHub Action
26 | with:
27 | version: 9 # Specify the pnpm version to use
28 | - name: Use Node.js ${{ matrix.node-version }}
29 | uses: actions/setup-node@v4
30 | with:
31 | node-version: ${{ matrix.node-version }}
32 | cache: "pnpm" # Enable pnpm cache
33 | - name: Install dependencies
34 | run: pnpm install --frozen-lockfile
35 | # - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
36 | # run: pnpm audit
37 | - name: Release
38 | env:
39 | GITHUB_TOKEN: ${{ secrets.PAT }}
40 | run: pnpm semantic-release
41 |
--------------------------------------------------------------------------------
/.github/workflows/update-release-branch.yml:
--------------------------------------------------------------------------------
1 | name: Update Release Branch
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | update-release-branch:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout the repository
14 | uses: actions/checkout@v4
15 | with:
16 | fetch-depth: 0 # Fetch all history for tags and branches
17 |
18 | - name: Update release branch
19 | run: |
20 | # Get the latest tag
21 | LATEST_TAG=$(git describe --tags --abbrev=0)
22 | echo "Latest Tag: $LATEST_TAG"
23 |
24 | # Update the release branch to point to the latest tag
25 | git checkout release
26 | git reset --hard "$LATEST_TAG"
27 |
28 | # Push the updated release branch
29 | git push origin release --force
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
23 | # jetbrains setting folder
24 | .idea/
25 |
26 | sim8085_ed25519*
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013-present, Debjit Biswas
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/_redirects:
--------------------------------------------------------------------------------
1 | https://v2.sim8085.com/* https://www.sim8085.com/:splat 301!
2 |
--------------------------------------------------------------------------------
/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "astro/config";
2 | import solidJs from "@astrojs/solid-js";
3 | import tailwind from "@astrojs/tailwind";
4 | import peggy from "vite-plugin-peggy-loader";
5 | import AstroPWA from "@vite-pwa/astro";
6 | import starlight from "@astrojs/starlight";
7 | import alpinejs from "@astrojs/alpinejs";
8 |
9 | // https://astro.build/config
10 | export default defineConfig({
11 | trailingSlash: "always",
12 | integrations: [
13 | starlight({
14 | title: "Sim8085 Docs",
15 | defaultLocale: "en",
16 | locales: {
17 | en: {
18 | label: "English",
19 | },
20 | },
21 | customCss: ["./src/tailwind.docs.css"],
22 | components: {
23 | Head: "./src/components/DocumentationHead.astro",
24 | // Footer: './src/components/DocumentationFooter.astro'
25 | },
26 | social: {
27 | github: "https://github.com/debjitbis08/sim8085",
28 | },
29 | disable404Route: true,
30 | sidebar: [
31 | { slug: "docs/en" },
32 | { slug: "docs/en/assembly" },
33 | { slug: "docs/en/unsupported" },
34 | { slug: "docs/en/interrupts" },
35 | { slug: "docs/en/infinite-loop-reasons" },
36 | { slug: "docs/en/app-install" },
37 | /*
38 | {
39 | label: 'Instructions',
40 | items: [
41 | 'docs/en/instructions/aci',
42 | 'docs/en/instructions/adc',
43 | ]
44 | },
45 | */
46 | {
47 | label: "References",
48 | items: ["docs/en/reference/ascii", "docs/en/reference/instruction-summary"],
49 | },
50 | ],
51 | }),
52 | solidJs({
53 | devtools: true,
54 | }),
55 | tailwind({
56 | applyBaseStyles: false,
57 | }),
58 | alpinejs(),
59 | AstroPWA({
60 | registerType: "autoUpdate",
61 | manifest: {
62 | name: "Sim8085",
63 | short_name: "Sim8085",
64 | theme_color: "#ffffff",
65 | },
66 | includeAssets: ["favicon.svg", "favicon-dark.svg", "favicon.ico", "favicon-with-background.svg"],
67 | pwaAssets: {
68 | config: true,
69 | },
70 | workbox: {
71 | globIgnores: [
72 | "tips/**", // Exclude all files in the `tips` folder
73 | ],
74 | },
75 | }),
76 | ],
77 | output: "static",
78 | vite: {
79 | plugins: [peggy()],
80 | ssr: {
81 | noExternal: ["nanoid"],
82 | },
83 | },
84 | });
85 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sim8085",
3 | "description": "Fast, accurate, and offline-capable Intel 8085 simulator with assembler and debugger. Works on desktop and mobile, no installation needed.",
4 | "type": "module",
5 | "version": "2.15.4",
6 | "private": true,
7 | "license": "BSD-3-Clause",
8 | "author": "Debjit Biswas",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/debjitbis08/sim8085"
12 | },
13 | "scripts": {
14 | "dev": "astro dev",
15 | "test:dev": "vitest",
16 | "test": "vitest run",
17 | "start": "astro dev",
18 | "build": "astro build && cp _redirects dist/",
19 | "preview": "astro preview",
20 | "astro": "astro",
21 | "build-emulator": "./scripts/build-emulator.sh"
22 | },
23 | "dependencies": {
24 | "@astrojs/alpinejs": "^0.4.0",
25 | "@astrojs/solid-js": "^4.4.1",
26 | "@astrojs/tailwind": "^5.1.0",
27 | "@codemirror/commands": "^6.6.2",
28 | "@codemirror/language": "^6.10.3",
29 | "@codemirror/state": "^6.4.1",
30 | "@codemirror/view": "^6.33.0",
31 | "@kobalte/core": "^0.13.6",
32 | "@kobalte/tailwindcss": "^0.9.0",
33 | "@lezer/highlight": "^1.2.1",
34 | "@solid-primitives/keyboard": "^1.3.0",
35 | "@supabase/supabase-js": "^2.46.1",
36 | "@tanstack/solid-virtual": "^3.10.8",
37 | "@thumbmarkjs/thumbmarkjs": "^0.20.2",
38 | "alpinejs": "^3.14.1",
39 | "codemirror": "^6.0.1",
40 | "debounce": "^2.2.0",
41 | "posthog-js": "^1.239.1",
42 | "solid-devtools": "^0.30.1",
43 | "solid-icons": "^1.1.0",
44 | "solid-js": "^1.9.5",
45 | "tailwindcss": "^3.4.11",
46 | "uuid": "^11.0.1"
47 | },
48 | "devDependencies": {
49 | "@astrojs/starlight": "^0.28.3",
50 | "@astrojs/starlight-tailwind": "^2.0.3",
51 | "@semantic-release/changelog": "^6.0.3",
52 | "@semantic-release/git": "^10.0.1",
53 | "@tailwindcss/typography": "^0.5.16",
54 | "@types/alpinejs": "^3.13.10",
55 | "@vite-pwa/assets-generator": "^0.2.6",
56 | "@vite-pwa/astro": "^0.4.3",
57 | "astro": "^4.16.6",
58 | "fast-check": "^3.22.0",
59 | "nanoid": "^5.0.7",
60 | "peggy": "^4.0.3",
61 | "semantic-release": "^24.2.0",
62 | "sharp": "^0.33.5",
63 | "vite-plugin-peggy-loader": "^2.0.1",
64 | "vitest": "^2.1.1",
65 | "workbox-window": "^7.1.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/public/FaSolidUser.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/favicon-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/favicon-with-background.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
35 |
38 |
39 |
41 |
46 |
54 |
55 |
59 |
63 |
71 |
74 |
77 |
80 |
83 |
86 |
89 |
92 |
95 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/public/favicon.ico
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/images/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/public/images/screen.png
--------------------------------------------------------------------------------
/public/tips/Comments.html:
--------------------------------------------------------------------------------
1 |
Comment Syntax
2 |
3 |
4 |
5 | Comments always start with the semicolon character
6 | ;
. Everything after this character on the same line
7 | is ignored by the assembler.
8 |
9 |
LABEL: MVI A , 0AH
12 |
13 |
--------------------------------------------------------------------------------
/public/tips/Labels.html:
--------------------------------------------------------------------------------
1 | Label Syntax
2 |
3 |
4 |
5 | Labels must consist of one to six alphanumeric characters, with the first character being either alphabetic or
6 | one of the special characters ?
or
7 | @
. Each label must be followed by a colon
8 | :
to indicate that it is a label. There should be
9 | no space between the word and the
10 | :
character. Additionally, a label can only be
11 | defined once in the program, and it must be unique.
12 |
13 |
LABEL: MVI A , 0AH
16 |
17 |
--------------------------------------------------------------------------------
/public/tips/Number.html:
--------------------------------------------------------------------------------
1 | Number Syntax
2 |
3 |
4 |
5 |
6 |
7 | Each hexadecimal number must begin with a numeric digit (0 through 9) and must be followed by the letter
8 | H
.
9 |
10 |
HERE: MVI C, 0BAH ; LOAD REG C WITH HEX BA
13 |
14 |
15 |
16 |
17 |
18 | Each decimal number may be identified by the letter
19 | D
immediately after its last digit or may
20 | stand alone. Any number not specifically identified as hexadecimal, octal, or binary is assumed to be
21 | decimal.
22 |
23 |
ABC: MVI E, 15D ; LOAD E WITH 15 IN DECIMAL
26 |
27 |
28 |
29 |
30 |
31 | Each octal number must be followed by the letter
32 | O
or the letter
33 | Q
.
34 |
35 |
LABEL: MVI A, 72Q ; LOAD OCTAL 72 INTO ACCUM
38 |
39 |
40 |
41 |
42 |
43 | Each binary number must be followed by the letter
44 | B
.
45 |
46 |
NOW: MVI D, 1111011OB ; LOAD REGISTER D WITH 0F6H
49 |
50 |
51 |
--------------------------------------------------------------------------------
/pwa-assets.config.js:
--------------------------------------------------------------------------------
1 | import {
2 | createAppleSplashScreens,
3 | defineConfig,
4 | minimal2023Preset as preset,
5 | } from '@vite-pwa/assets-generator/config';
6 |
7 | export default defineConfig({
8 | preset,
9 | images: [
10 | 'public/favicon-with-background.svg',
11 | ]
12 | });
13 |
--------------------------------------------------------------------------------
/release.config.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('semantic-release').GlobalConfig}
3 | */
4 | export default {
5 | branches: ["master"],
6 | plugins: [
7 | "@semantic-release/commit-analyzer",
8 | "@semantic-release/release-notes-generator",
9 | [
10 | "@semantic-release/changelog",
11 | {
12 | changelogFile: "CHANGELOG.md",
13 | },
14 | ],
15 | "@semantic-release/npm", // Updates package.json version
16 | [
17 | "@semantic-release/git",
18 | {
19 | assets: ["package.json", "package-lock.json", "CHANGELOG.md"],
20 | message: "release: Release ${nextRelease.version} [skip actions]\n\n${nextRelease.notes}",
21 | },
22 | ],
23 | "@semantic-release/github", // Creates GitHub release
24 | ],
25 | };
--------------------------------------------------------------------------------
/scripts/build-emulator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set +x
4 |
5 | emcc \
6 | src/core/8085.c \
7 | -o src/core/8085.js \
8 | -Oz --closure 0 \
9 | -s EXPORTED_FUNCTIONS="['_malloc', '_free','_Init8085', '_getMemory', '_getIO', '_ExecuteProgram', '_ExecuteProgramSlice', '_Emulate8085Op', '_LoadProgram', '_UnloadProgram', '_ExecuteProgramUntil', '_set_timing_enabled', '_InterruptToHalt', '_triggerInterrupt']" \
10 | -s EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap', 'getValue', 'setValue']" \
11 | -s NO_EXIT_RUNTIME=1 \
12 | -s NO_FILESYSTEM=1 \
13 | -s MODULARIZE=1 \
14 | -s WASM=0 \
15 | -s EXPORT_ES6=1 \
16 | -s ENVIRONMENT="web,worker" \
17 | --js-library src/core/simulator-library.js
18 |
19 | # --memory-init-file 0 \
20 |
--------------------------------------------------------------------------------
/src/components/ActionButton.jsx:
--------------------------------------------------------------------------------
1 | import { Tooltip } from "./generic/Tooltip.jsx";
2 |
3 | export default function ActionButton(props) {
4 | return (
5 |
6 |
11 | {props.icon}
12 |
13 |
14 |
15 |
16 |
17 |
{props.title}
18 | {props.shortcut ? (
19 |
{props.shortcut}
20 | ) : null}
21 |
22 |
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/ActionsLoader.jsx:
--------------------------------------------------------------------------------
1 | import { VsLoading } from "solid-icons/vs";
2 | import { createSignal, lazy, Suspense, onMount } from "solid-js";
3 | import DelayedComponent from "./generic/DelayedComponent.jsx";
4 |
5 | const Actions = lazy(() => import("./Actions.jsx"));
6 |
7 | export default function ActionsLoader() {
8 | return (
9 | import("./Actions.jsx")}
12 | fallback={
13 |
14 |
15 | Loading Action Buttons...
16 |
17 | }
18 | />
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import { Registers } from "./Registers";
2 | import { createStore } from "solid-js/store";
3 | import { StoreContext } from "./StoreContext";
4 | import { Flags } from "./Flags";
5 | import { MemoryGrid } from "./MemoryGrid";
6 | import { CodeMirror } from "./codemirror/CodeMirror";
7 | import { HiOutlineCpuChip } from "solid-icons/hi";
8 | import { BsMemory } from "solid-icons/bs";
9 | import Actions from "./Actions";
10 | import MemoryList from "./MemoryList";
11 | import { LeftPanel } from "./LeftPanel";
12 | import "./styles.css";
13 | import { createEffect } from "solid-js";
14 |
15 | const INITIAL_CODE = `
16 | ;
17 |
18 | jmp start
19 |
20 | ;data
21 |
22 | ;code
23 | start: nop
24 |
25 |
26 | hlt
27 | `;
28 |
29 | export function App() {
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | ASM
42 | main.asm
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
59 |
Coming Soon!
60 |
61 |
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/src/components/Assembled.module.css:
--------------------------------------------------------------------------------
1 | .radiatingBorder {
2 | }
3 |
4 | .radiatingBorder::after {
5 | content: '';
6 | position: absolute;
7 | top: 0;
8 | left: 0;
9 | width: 100%;
10 | height: 100%;
11 | border-radius: 50%; /* Creates the circular border */
12 | border: 2px solid rgb(var(--sm-primary)); /* Replace with your terminal color */
13 | animation: radiate 1.5s infinite;
14 | opacity: 0;
15 | }
16 |
17 | /* Keyframes for radiating effect */
18 | @keyframes radiate {
19 | 0% {
20 | transform: scale(1);
21 | opacity: 1;
22 | }
23 | 100% {
24 | transform: scale(3); /* Expands outward */
25 | opacity: 0; /* Fades out */
26 | }
27 | }
28 |
29 | .machineCodeTable tr:nth-child(odd) {
30 | background-color: rgb(var(--sm-main-background));
31 | }
32 |
33 | .machineCodeTable tr:nth-child(even) {
34 | background-color: rgb(var(--sm-secondary-background));
35 | }
36 |
37 | .machineCodeTable thead tr {
38 | background-color: rgb(var(--sm-secondary-background)) !important;
39 | }
40 |
41 | .machineCodeTable tr:hover {
42 | background-color: rgb(var(--sm-active-background));
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Card.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | title: string;
4 | body: string;
5 | href: string;
6 | }
7 |
8 | const { href, title, body } = Astro.props;
9 | ---
10 |
11 |
12 |
13 |
14 | {title}
15 | →
16 |
17 |
18 | {body}
19 |
20 |
21 |
22 |
62 |
--------------------------------------------------------------------------------
/src/components/CodeFormatterLoader.jsx:
--------------------------------------------------------------------------------
1 | import { VsLoading } from "solid-icons/vs";
2 | import DelayedComponent from "./generic/DelayedComponent.jsx";
3 |
4 | export default function CodeFormatterLoader() {
5 | return (
6 | import("./CodeFormatter.jsx")}
9 | fallback={
10 |
11 |
12 | Loading Code Formatter...
13 |
14 | }
15 | />
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/CodingArea.jsx:
--------------------------------------------------------------------------------
1 | import { onMount } from "solid-js";
2 | import { INITIAL_CODE, setStore, store } from "../store/store.js";
3 | import { CodeMirror } from "./codemirror/CodeMirror.jsx";
4 |
5 | export function CodingArea() {
6 | onMount(() => {
7 | const savedFileStr = localStorage.getItem("activeFile");
8 |
9 | if (savedFileStr) {
10 | const savedFile = JSON.parse(savedFileStr);
11 | setStore("activeFile", savedFile);
12 | } else {
13 | const mainAsmCode = localStorage.getItem("main.asm");
14 | setStore("activeFile", {
15 | name: "untitled-1.asm",
16 | workspaceItemId: null,
17 | content: mainAsmCode || INITIAL_CODE,
18 | });
19 | localStorage.setItem("activeFile", JSON.stringify(store.activeFile));
20 | if (mainAsmCode) {
21 | localStorage.removeItem("main.asm");
22 | }
23 | }
24 | });
25 |
26 | const handleContentChange = (newContent) => {
27 | setStore("activeFile", "content", newContent);
28 | if (store.assembled.length) {
29 | setStore("assembled", []);
30 | }
31 | localStorage.setItem("activeFile", JSON.stringify(store.activeFile));
32 | };
33 |
34 | return (
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/CopyComponent.jsx:
--------------------------------------------------------------------------------
1 | import { createSignal } from "solid-js";
2 | import { VsCopy } from "solid-icons/vs";
3 | import { FaSolidCheck } from "solid-icons/fa";
4 | import { HiSolidCheckCircle } from "solid-icons/hi";
5 |
6 | function CopyComponent(props) {
7 | const [copied, setCopied] = createSignal(false);
8 |
9 | const copyOutputAsText = () => {
10 | navigator.clipboard.writeText(props.getTextToCopy())
11 | .then(() => {
12 | setCopied(true); // Set copied to true
13 | setTimeout(() => setCopied(false), 2000);
14 | })
15 | .catch(err => console.error("Copy failed: ", err));
16 | };
17 |
18 | return (
19 | copyOutputAsText()}>
20 | {copied() ? (
21 |
22 |
23 | Copied
24 |
25 | ) : (
26 |
27 | )}
28 |
29 | );
30 | }
31 |
32 | export default CopyComponent;
33 |
--------------------------------------------------------------------------------
/src/components/DocumentationFooter.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { Props } from '@astrojs/starlight/props'
3 | import Default from '@astrojs/starlight/components/Footer.astro'
4 | import CookieBanner from './analytics/CookieBanner.astro'
5 | ---
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/DocumentationHead.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { Props } from '@astrojs/starlight/props';
3 | import Default from '@astrojs/starlight/components/Head.astro';
4 | import PostHogInit from './analytics/PostHogInit.astro';
5 | ---
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/components/FAQs.astro:
--------------------------------------------------------------------------------
1 |
2 |
FAQs
7 |
8 |
18 |
23 |
34 |
35 |
FAQs
36 |
37 |
38 |
39 |
40 | Where is the program loaded by default?
41 |
42 |
43 | If you do not specify an ORG
instruction at the start, the program
44 | will be loaded at memory location starting at 0.
45 |
46 |
47 |
48 |
49 | How to load the program at a different location?
50 |
51 |
52 | You can add the ORG
directive at the start of the program and provide the desired load address.
53 |
54 |
55 | org 0800h
56 |
57 | jmp start
58 |
59 | ;data
60 |
61 | ;code
62 | start: nop
63 |
64 |
65 | hlt
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/components/Flags.jsx:
--------------------------------------------------------------------------------
1 | // import { useContext } from "solid-js";
2 | // import { StoreContext } from "./StoreContext";
3 | import { AiOutlineClear } from "solid-icons/ai";
4 | import { produce } from "solid-js/store";
5 | import { store, setStore } from "../store/store";
6 | // import { setFlags } from "../core/simulator";
7 |
8 | export function Flags() {
9 | let lazySetFlags;
10 |
11 | const callSetFlags = async () => {
12 | if (!lazySetFlags) {
13 | const module = await import("../core/simulator");
14 | lazySetFlags = module.setFlags;
15 | }
16 | lazySetFlags(store);
17 | };
18 |
19 | const updateFlag = (flagId, isChecked) => {
20 | setStore("flags", flagId, isChecked);
21 | callSetFlags(store);
22 | };
23 |
24 | const clearFlags = () => {
25 | setStore(
26 | "flags",
27 | produce((flags) => {
28 | Object.keys(flags).forEach((flagId) => (flags[flagId] = false));
29 | }),
30 | );
31 | callSetFlags(store);
32 | };
33 |
34 | return (
35 |
36 |
37 |
Flags
38 |
39 |
40 |
41 |
42 |
43 | {Object.keys(store.flags).map((flagId) => (
44 | updateFlag(flagId, isChecked)}
49 | />
50 | ))}
51 |
52 |
53 | );
54 | }
55 |
56 | function Flag(props) {
57 | const id = `flag-${props.id}`;
58 | return (
59 |
60 |
61 |
62 | {props.name}
63 |
64 |
65 |
66 | props.onSave(e.target.checked)}
72 | />
73 |
74 |
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/FolderNode.jsx:
--------------------------------------------------------------------------------
1 | import {createSignal} from "solid-js";
2 | import {supabase} from "../lib/supabase.js";
3 | import {FileNode} from "./FileNode";
4 | import {FaRegularFolder, FaRegularFolderClosed, FaRegularFolderOpen} from "solid-icons/fa";
5 | import {WorkspaceTree} from "./WorkspaceTree.jsx";
6 | import {store} from "../store/store.js";
7 |
8 | export function FolderNode(props) {
9 | const [children, setChildren] = createSignal([]);
10 | const [expanded, setExpanded] = createSignal(false);
11 | const [loading, setLoading] = createSignal(false);
12 |
13 | async function fetchFolderContents(folderId) {
14 | try {
15 | setLoading(true);
16 |
17 | const {data, error} = await supabase
18 | .from("workspace_items")
19 | .select("id, name, parent_folder_id, status_id, created_at, updated_at")
20 | .eq("parent_folder_id", folderId);
21 |
22 | if (error) throw error;
23 |
24 | setChildren(data);
25 | } catch (err) {
26 | console.error("Error fetching folder contents:", err.message);
27 | } finally {
28 | setLoading(false);
29 | }
30 | }
31 |
32 | function toggleExpand() {
33 | if (!expanded()) {
34 | fetchFolderContents(props.folder.id);
35 | }
36 | setExpanded(!expanded());
37 | }
38 |
39 | return (
40 |
41 |
43 |
44 | {expanded() ? (
45 |
46 | ) : (
47 |
48 | )}
49 |
50 | {props.folder.name}
51 |
52 | {expanded() && (
53 |
54 | {loading() &&
Loading...
}
55 |
56 |
57 | )}
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/InterruptState.jsx:
--------------------------------------------------------------------------------
1 | import { store } from "../store/store";
2 | import { FaSolidCircle } from "solid-icons/fa";
3 |
4 | export default function InterruptState() {
5 | const statusColor = () =>
6 | store.interruptsEnabled
7 | ? "text-green-foreground bg-terminal-100 border-terminal-300"
8 | : "text-red-foreground bg-red-100 border-red-300";
9 |
10 | return (
11 |
12 |
13 |
Interrupts
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
31 |
36 |
37 |
38 | );
39 | }
40 |
41 | function Item(props) {
42 | const statusText = () => (props.status ? "Enabled" : "Disabled");
43 | const statusColor = () =>
44 | props.status
45 | ? "text-green-foreground bg-terminal-100 border-terminal-300"
46 | : "text-red-foreground bg-red-100 border-red-300";
47 |
48 | return (
49 |
50 |
{props.name}
51 |
52 | {props.pending && (
53 | Pending
54 | )}
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | function Indicator(props) {
62 | return (
63 |
64 |
65 |
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/KeyboardShortcuts.module.css:
--------------------------------------------------------------------------------
1 | .sectionHeader {
2 | @apply text-lg font-bold mt-6 mb-3 pb-1 border-b border-gray-300;
3 | @apply text-gray-500;
4 | @apply dark:border-gray-700 dark:text-gray-500;
5 | }
6 |
7 | .shortcutsGrid {
8 | @apply grid grid-cols-2 gap-y-3 text-sm;
9 | }
10 |
11 | .keyCombination {
12 | @apply text-right space-x-1 text-gray-900 dark:text-gray-200;
13 | }
14 |
15 | .key {
16 | @apply inline-block px-2 py-1 rounded bg-gray-200 text-gray-900 dark:bg-gray-600 dark:text-gray-200;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/LambdaClassesPoster.jsx:
--------------------------------------------------------------------------------
1 | import LambdaClassesPoster from "../images/lambda-classes-poster.png";
2 | import ProjectTrackPoster from "../images/project-track-poster.jpg";
3 | import { createEffect, createSignal, onMount } from "solid-js";
4 |
5 | export default function lambdaClassesPoster(props) {
6 | let lambdaClassesPoster = null;
7 | let [isHidden, setIsHidden] = createSignal(true);
8 |
9 | const hidePoster = () => {
10 | localStorage.setItem("lambda-classes-poster-hide", "true");
11 | setIsHidden(true);
12 | if (window.posthog) {
13 | posthog.capture("lc dismissed");
14 | }
15 | };
16 |
17 | const onClickthrough = () => {
18 | localStorage.setItem("lambda-classes-poster-hide", "true");
19 | setIsHidden(true);
20 | if (window.posthog) {
21 | posthog.capture("lc clickthrough");
22 | }
23 | };
24 |
25 | onMount(() => {
26 | const value = localStorage.getItem("lambda-classes-poster-hide");
27 | if (!value || value === "false") {
28 | console.log("Showing poster");
29 | setIsHidden(false);
30 | }
31 | });
32 |
33 | createEffect(() => {
34 | const value = localStorage.getItem("lambda-classes-poster-hide");
35 | if (props.isHidden || value === "true") {
36 | console.log("Hiding Poster");
37 | setIsHidden(true);
38 | } else {
39 | setIsHidden(false);
40 | }
41 | });
42 |
43 | return (
44 | (lambdaClassesPoster = el)}>
45 |
46 |
47 |
48 |
53 | ×
54 |
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/LeftPanel.css:
--------------------------------------------------------------------------------
1 | .scroll-shadow {
2 | --bg-color: rgb(32, 35, 39);
3 | --bg-color-transparent: rgba(32, 35, 39, 1);
4 | --shadow-color: rgba(0, 0, 0, 0.6);
5 | --shadow-fade: rgba(0, 0, 0, 0);
6 |
7 | background:
8 | /* Shadow Cover TOP */
9 | linear-gradient(var(--bg-color) 30%, var(--bg-color-transparent)) center top,
10 | /* Shadow Cover BOTTOM */ linear-gradient(var(--bg-color-transparent), var(--bg-color) 70%) center bottom,
11 | /* Shadow TOP */ radial-gradient(farthest-side at 50% 0, var(--shadow-color), var(--shadow-fade)) center top,
12 | /* Shadow BOTTOM */ radial-gradient(farthest-side at 50% 100%, var(--shadow-color), var(--shadow-fade)) center
13 | bottom rgb(var(--sm-secondary-background));
14 |
15 | background-repeat: no-repeat;
16 | background-size:
17 | 100% 40px,
18 | 100% 40px,
19 | 100% 14px,
20 | 100% 14px;
21 | background-attachment: local, local, scroll, scroll;
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/StoreContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from "solid-js"
2 |
3 | export const StoreContext = createContext();
4 |
--------------------------------------------------------------------------------
/src/components/Terminal.jsx:
--------------------------------------------------------------------------------
1 | export function Terminal() {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/TextToolip.module.css:
--------------------------------------------------------------------------------
1 | .tooltipContent {
2 | @apply border;
3 | @apply rounded-sm;
4 | @apply border-gray-400;
5 | @apply dark:border-gray-600;
6 | @apply p-2;
7 | @apply bg-gray-400;
8 | @apply dark:bg-gray-800;
9 | @apply dark:text-gray-200;
10 | @apply text-sm;
11 | @apply z-50;
12 | max-width: min(calc(100vw - 16px), 380px);
13 | box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
14 | animation: contentHide 150ms ease-in forwards;
15 | }
16 |
17 | .tooltipContent[data-expanded] {
18 | animation: contentShow 150ms ease-out;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/TextTooltip.jsx:
--------------------------------------------------------------------------------
1 | import { Tooltip } from "@kobalte/core/tooltip";
2 | import styles from "./TextToolip.module.css";
3 |
4 | export function TextTooltip(props) {
5 | return (
6 |
7 | {props.children}
8 |
9 |
10 |
11 | {props.message}
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/analytics/CookieBanner.astro:
--------------------------------------------------------------------------------
1 |
7 |
8 | This site uses cookies to ensure the best experience for our users.
9 |
10 |
11 | By continuing, you agree to the use of cookies.
12 |
13 |
14 | Got it
15 | Skip
16 |
17 |
18 |
19 |
49 |
57 |
--------------------------------------------------------------------------------
/src/components/analytics/PostHogInit.astro:
--------------------------------------------------------------------------------
1 |
34 |
--------------------------------------------------------------------------------
/src/components/analytics/PostHogInitialize.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const { useTracking, apiKey } = Astro.props;
3 | ---
4 | {useTracking && (
5 |
14 | )}
15 |
--------------------------------------------------------------------------------
/src/components/analytics/identify.js:
--------------------------------------------------------------------------------
1 | import { getSession } from "../../lib/supabase.js";
2 |
3 | export async function identifyUser() {
4 | const cookieConsent = localStorage.getItem("cookie_consent");
5 |
6 | console.log("cookieConsent", cookieConsent);
7 |
8 | const { session, error } = await getSession();
9 |
10 | if (!error && session?.user) {
11 | const name = session.user?.user_metadata?.name || "";
12 |
13 | posthog.identify(session.user.id, {
14 | email: session.user.email,
15 | name,
16 | });
17 | } else if (cookieConsent === "no") {
18 | const { getFingerprint } = await import("@thumbmarkjs/thumbmarkjs");
19 | const fingerprint = await getFingerprint();
20 |
21 | posthog.identify(fingerprint, {
22 | anon: true,
23 | });
24 | } else if (cookieConsent === "yes") {
25 | let id = localStorage.getItem("anon_id");
26 |
27 | if (!id) {
28 | const bytes = new Uint8Array(16);
29 | crypto.getRandomValues(bytes);
30 | id = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
31 | localStorage.setItem("anon_id", id);
32 | }
33 |
34 | posthog.identify(id, {
35 | anon: true,
36 | });
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/analytics/tracker.js:
--------------------------------------------------------------------------------
1 | import posthog from 'posthog-js';
2 |
3 | export function trackEvent(eventName, data) {
4 | posthog.capture(eventName, data);
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/codemirror/CodeMirror.css:
--------------------------------------------------------------------------------
1 | .cm-editor {
2 | height: 100%;
3 | }
4 |
5 | .cm-line.cm-activeLine {
6 | background-color: rgba(var(--sm-editor-active-line), 0.3);
7 | }
8 |
9 | .cm-debug-line-highlight,
10 | .cm-line.cm-activeLine.cm-debug-line-highlight {
11 | background-color: rgb(var(--sm-editor-debug-line));
12 | }
13 |
14 | .cm-editor .cm-gutters {
15 | @apply border-0;
16 | background-color: rgb(var(--sm-main-background));
17 | }
18 |
19 | .cm-editor .cm-activeLineGutter {
20 | background-color: rgb(var(--sm-secondary-background));
21 | }
22 |
23 | .cm-editor .cm-cursor {
24 | border-left: 1px solid rgb(var(--sm-editor-cursor));
25 | }
26 |
27 | .cm-editor .cm-content {
28 | font-size: 0.85rem;
29 | }
30 |
31 | .cm-editor > .cm-scroller > .cm-selectionLayer .cm-selectionBackground,
32 | .cm-editor.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground {
33 | background-color: rgb(var(--sm-editor-selection-background));
34 | }
35 |
36 | .cm-editor.cm-focused {
37 | outline: none;
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/generic/DelayedComponent.jsx:
--------------------------------------------------------------------------------
1 | import { VsLoading } from "solid-icons/vs";
2 | import { createSignal, lazy, Suspense, onMount } from "solid-js";
3 |
4 | export default function DelayedComponent(props) {
5 | const [isReady, setIsReady] = createSignal(false);
6 |
7 | const Comp = lazy(props.fn);
8 |
9 | onMount(() => {
10 | setTimeout(() => {
11 | setIsReady(true);
12 | }, props.delayInMs);
13 | });
14 |
15 | return (
16 |
17 | {isReady() ? (
18 |
19 |
20 |
21 | ) : (
22 | props.fallback
23 | )}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/generic/Dialog.css:
--------------------------------------------------------------------------------
1 | .dialog__overlay {
2 | @apply fixed;
3 | @apply inset-0;
4 | @apply z-50;
5 | @apply bg-gray-600;
6 | @apply bg-opacity-20;
7 | @apply backdrop-blur-sm;
8 | animation: overlayHide 250ms ease 100ms forwards;
9 | }
10 |
11 | .dialog__overlay[data-expanded] {
12 | animation: overlayShow 250ms ease;
13 | }
14 |
15 | .dialog__positioner {
16 | @apply fixed;
17 | @apply inset-0;
18 | @apply z-50;
19 | @apply flex;
20 | @apply items-center;
21 | @apply justify-center;
22 | }
23 |
24 | .dialog__content {
25 | @apply z-50;
26 | @apply min-w-full md:min-w-[500px] min-h-svh md:min-h-max;
27 | background-color: rgb(var(--sm-secondary-background));
28 | border: 1px solid hsl(240 5% 84%);
29 | border-radius: 6px;
30 | padding: 16px;
31 | box-shadow:
32 | 0 10px 15px -3px rgb(0 0 0 / 0.1),
33 | 0 4px 6px -4px rgb(0 0 0 / 0.1);
34 | animation: contentHide 300ms ease-in forwards;
35 | }
36 |
37 | .dialog__content[data-expanded] {
38 | animation: contentShow 300ms ease-out;
39 | }
40 |
41 | .dialog__header {
42 | @apply flex;
43 | @apply items-baseline;
44 | @apply justify-between;
45 | @apply mb-4;
46 | }
47 |
48 | .dialog__close-button {
49 | height: 16px;
50 | width: 16px;
51 | }
52 |
53 | .dialog__title {
54 | @apply text-lg;
55 | @apply font-semibold;
56 | color: rgb(var(--sm-secondary-foreground));
57 | }
58 |
59 | .dialog__description {
60 | }
61 |
62 | @keyframes overlayShow {
63 | from {
64 | opacity: 0;
65 | }
66 | to {
67 | opacity: 1;
68 | }
69 | }
70 | @keyframes overlayHide {
71 | from {
72 | opacity: 1;
73 | }
74 | to {
75 | opacity: 0;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/generic/Dialog.jsx:
--------------------------------------------------------------------------------
1 | export { Dialog } from "@kobalte/core/dialog";
2 | import "./Dialog.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/DropdownMenu.jsx:
--------------------------------------------------------------------------------
1 | export { DropdownMenu } from "@kobalte/core/dropdown-menu";
2 | import "./DropdownMenu.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/Select.css:
--------------------------------------------------------------------------------
1 | .select__trigger {
2 | transition:
3 | border-color 250ms,
4 | color 250ms;
5 | color: rgb(var(--sm-active-foreground));
6 | background-color: rgb(var(--sm-page-background));
7 | border-color: rgb(var(--sm-active-border));
8 | @apply flex items-center gap-2 justify-between rounded py-3 px-2 text-sm h-5 outline-none border;
9 | }
10 | .select__trigger:hover {
11 | border-color: hsl(240 5% 65%);
12 | }
13 | .select__trigger:focus-visible {
14 | outline: 2px solid hsl(200 98% 39%);
15 | outline-offset: 2px;
16 | }
17 | .select__trigger[data-invalid] {
18 | border-color: hsl(0 72% 51%);
19 | color: hsl(0 72% 51%);
20 | }
21 | .select__value {
22 | text-overflow: ellipsis;
23 | white-space: nowrap;
24 | overflow: hidden;
25 | }
26 | .select__value[data-placeholder-shown] {
27 | color: hsl(240 4% 46%);
28 | }
29 |
30 | .select__icon {
31 | }
32 |
33 | .select__description {
34 | margin-top: 8px;
35 | color: hsl(240 5% 26%);
36 | font-size: 12px;
37 | user-select: none;
38 | }
39 | .select__error-message {
40 | margin-top: 8px;
41 | color: hsl(0 72% 51%);
42 | font-size: 12px;
43 | user-select: none;
44 | }
45 | .select__content {
46 | border-radius: 6px;
47 | border: 1px solid hsl(240 6% 90%);
48 | box-shadow:
49 | 0 4px 6px -1px rgb(0 0 0 / 0.1),
50 | 0 2px 4px -2px rgb(0 0 0 / 0.1);
51 | transform-origin: var(--kb-select-content-transform-origin);
52 | animation: contentHide 250ms ease-in forwards;
53 | }
54 | .select__content[data-expanded] {
55 | animation: contentShow 250ms ease-out;
56 | }
57 | .select__listbox {
58 | overflow-y: auto;
59 | max-height: 360px;
60 | padding: 8px;
61 | }
62 | .select__item {
63 | font-size: 16px;
64 | line-height: 1;
65 | border-radius: 4px;
66 | display: flex;
67 | align-items: center;
68 | justify-content: space-between;
69 | height: 32px;
70 | padding: 0 8px;
71 | position: relative;
72 | user-select: none;
73 | outline: none;
74 | }
75 | .select__item[data-disabled] {
76 | color: hsl(240 5% 65%);
77 | opacity: 0.5;
78 | pointer-events: none;
79 | }
80 | .select__item[data-highlighted] {
81 | outline: none;
82 | background-color: hsl(200 98% 39%);
83 | color: white;
84 | }
85 | .select__section {
86 | padding: 8px 0 0 8px;
87 | font-size: 14px;
88 | line-height: 32px;
89 | color: hsl(240 4% 46%);
90 | }
91 | .select__item-indicator {
92 | height: 20px;
93 | width: 20px;
94 | display: inline-flex;
95 | align-items: center;
96 | justify-content: center;
97 | }
98 |
--------------------------------------------------------------------------------
/src/components/generic/Select.jsx:
--------------------------------------------------------------------------------
1 | export { Select } from "@kobalte/core/select";
2 | import "./Select.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/Switch.css:
--------------------------------------------------------------------------------
1 | .switch {
2 | display: inline-flex;
3 | align-items: center;
4 | }
5 |
6 | .switch__control {
7 | display: inline-flex;
8 | align-items: center;
9 | height: 24px;
10 | width: 44px;
11 | border: 1px solid rgb(var(--sm-gray-200));
12 | border-radius: 12px;
13 | padding: 0 2px;
14 | background-color: rgb(var(--sm-gray-300));
15 | transition: 250ms background-color;
16 | @apply cursor-pointer;
17 | }
18 |
19 | .switch__input:focus-visible + .switch__control {
20 | outline: 2px solid hsl(200 98% 39%);
21 | outline-offset: 2px;
22 | }
23 |
24 | .switch__control[data-checked] {
25 | @apply border;
26 | border-color: rgb(var(--sm-primary));
27 | background-color: rgb(var(--sm-primary));
28 | }
29 |
30 | .switch__thumb {
31 | height: 20px;
32 | width: 20px;
33 | border-radius: 10px;
34 | background-color: white;
35 | transition: 250ms transform;
36 | box-shadow: 0 2px 5px rgb(var(--sm-gray-600));
37 | }
38 |
39 | .switch__thumb[data-checked] {
40 | transform: translateX(calc(100% - 1px));
41 | }
42 |
43 | .switch__label {
44 | margin-right: 6px;
45 | font-size: 14px;
46 | user-select: none;
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/generic/Switch.jsx:
--------------------------------------------------------------------------------
1 | export { Switch } from "@kobalte/core/switch";
2 | import "./Switch.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/Tabs.css:
--------------------------------------------------------------------------------
1 | .tabs {
2 | width: 100%;
3 | }
4 | .tabs[data-orientation="vertical"] {
5 | display: flex;
6 | }
7 | .tabs__list {
8 | position: relative;
9 | display: flex;
10 | }
11 | .tabs__list[data-orientation="horizontal"] {
12 | align-items: center;
13 | border-bottom-color: rgb(var(--sm-inactive-border));
14 | @apply border-b;
15 | }
16 | .tabs__list[data-orientation="vertical"] {
17 | flex-direction: column;
18 | align-items: stretch;
19 | border-right-color: rgb(var(--sm-inactive-border));
20 | @apply border-r;
21 | }
22 | .tabs__indicator {
23 | transition: all 250ms;
24 | background-color: rgb(var(--sm-primary));
25 | @apply absolute;
26 | }
27 | .tabs__indicator[data-orientation="horizontal"] {
28 | bottom: -1px;
29 | height: 2px;
30 | }
31 | .tabs__indicator[data-orientation="vertical"] {
32 | right: -1px;
33 | width: 2px;
34 | }
35 | .tabs__trigger {
36 | display: inline-block;
37 | padding: 8px 16px;
38 | outline: none;
39 | }
40 | .tabs__trigger:hover {
41 | color: rgb(var(--sm-active-foreground));
42 | background-color: rgb(var(--sm-active-background));
43 | }
44 | .tabs__trigger:focus-visible {
45 | background-color: hsl(240 5% 96%);
46 | }
47 | .tabs__trigger[data-disabled],
48 | .tabs__trigger[data-disabled]:hover {
49 | opacity: 0.5;
50 | background-color: transparent;
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/generic/Tabs.jsx:
--------------------------------------------------------------------------------
1 | export { Tabs } from "@kobalte/core/tabs";
2 | import "./Tabs.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/Toast.css:
--------------------------------------------------------------------------------
1 | .toast__list {
2 | --viewport-padding: 16px;
3 | position: fixed;
4 | bottom: 0;
5 | right: 0;
6 | display: flex;
7 | flex-direction: column;
8 | padding: var(--viewport-padding);
9 | gap: 8px;
10 | width: 400px;
11 | max-width: 100vw;
12 | margin: 0;
13 | list-style: none;
14 | z-index: 9999;
15 | outline: none;
16 | }
17 |
18 | .toast {
19 | box-shadow:
20 | 0 10px 15px -3px rgb(0 0 0 / 0.1),
21 | 0 4px 6px -4px rgb(0 0 0 / 0.1);
22 | }
23 |
24 | .toast[data-opened] {
25 | animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
26 | }
27 |
28 | .toast[data-closed] {
29 | animation: hide 100ms ease-in;
30 | }
31 |
32 | .toast[data-swipe="move"] {
33 | transform: translateX(var(--kb-toast-swipe-move-x));
34 | }
35 |
36 | .toast[data-swipe="cancel"] {
37 | transform: translateX(0);
38 | transition: transform 200ms ease-out;
39 | }
40 |
41 | .toast[data-swipe="end"] {
42 | animation: swipeOut 100ms ease-out;
43 | }
44 |
45 | .toast__progress-fill {
46 | width: var(--kb-toast-progress-fill-width);
47 | transition: width 250ms linear;
48 | }
49 |
50 | @keyframes hide {
51 | from {
52 | opacity: 1;
53 | }
54 | to {
55 | opacity: 0;
56 | }
57 | }
58 |
59 | @keyframes slideIn {
60 | from {
61 | transform: translateX(calc(100% + var(--viewport-padding)));
62 | }
63 | to {
64 | transform: translateX(0);
65 | }
66 | }
67 |
68 | @keyframes swipeOut {
69 | from {
70 | transform: translateX(var(--kb-toast-swipe-end-x));
71 | }
72 | to {
73 | transform: translateX(calc(100% + var(--viewport-padding)));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/generic/Toast.jsx:
--------------------------------------------------------------------------------
1 | export { Toast, toaster } from "@kobalte/core/toast";
2 | import "./Toast.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/Tooltip.css:
--------------------------------------------------------------------------------
1 | @keyframes contentShow {
2 | from {
3 | opacity: 0;
4 | transform: scale(0.96);
5 | }
6 | to {
7 | opacity: 1;
8 | transform: scale(1);
9 | }
10 | }
11 | @keyframes contentHide {
12 | from {
13 | opacity: 1;
14 | transform: scale(1);
15 | }
16 | to {
17 | opacity: 0;
18 | transform: scale(0.96);
19 | }
20 | }
21 |
22 | .tooltip__content {
23 | @apply border;
24 | @apply rounded-sm;
25 | @apply p-2;
26 | @apply text-sm;
27 | @apply z-50;
28 | background-color: rgb(var(--sm-main-background));
29 | border-color: rgb(var(--sm-main-border));
30 | max-width: min(calc(100vw - 16px), 380px);
31 | box-shadow:
32 | 0 10px 15px -3px rgb(0 0 0 / 0.1),
33 | 0 4px 6px -4px rgb(0 0 0 / 0.1);
34 | animation: contentHide 150ms ease-in forwards;
35 | }
36 |
37 | .tooltip__content[data-expanded] {
38 | animation: contentShow 150ms ease-out;
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/generic/Tooltip.jsx:
--------------------------------------------------------------------------------
1 | export { Tooltip } from "@kobalte/core/tooltip";
2 | import "./Tooltip.css";
3 |
--------------------------------------------------------------------------------
/src/components/generic/wrapWithClass.js:
--------------------------------------------------------------------------------
1 | export function withDefaultClass(Component, defaultClass) {
2 | return (props) => ;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/pwa.js:
--------------------------------------------------------------------------------
1 | import { registerSW } from 'virtual:pwa-register'
2 |
3 | window.addEventListener('load', () => {
4 | const pwaToast = document.querySelector('#pwa-toast')
5 | const pwaToastMessage = pwaToast.querySelector('.message #toast-message')
6 | const pwaCloseBtn = pwaToast.querySelector('#pwa-close')
7 | const pwaRefreshBtn = pwaToast.querySelector('#pwa-refresh')
8 |
9 | let refreshSW; // : ((reloadPage?: boolean) => Promise) | undefined
10 |
11 | const refreshCallback = () => refreshSW?.(true)
12 |
13 | const hidePwaToast = (raf = false) => {
14 | if (raf) {
15 | requestAnimationFrame(() => hidePwaToast(false))
16 | return
17 | }
18 | if (pwaToast.classList.contains('refresh'))
19 | pwaRefreshBtn.removeEventListener('click', refreshCallback)
20 |
21 | pwaToast.classList.remove('show', 'refresh')
22 | }
23 | const showPwaToast = (offline) => {
24 | if (!offline)
25 | pwaRefreshBtn.addEventListener('click', refreshCallback)
26 | requestAnimationFrame(() => {
27 | hidePwaToast(false)
28 | if (!offline)
29 | pwaToast.classList.add('refresh')
30 | pwaToast.classList.add('show')
31 | })
32 | }
33 |
34 | pwaCloseBtn.addEventListener('click', () => hidePwaToast(true))
35 |
36 | refreshSW = registerSW({
37 | immediate: true,
38 | onOfflineReady() {
39 | pwaToastMessage.innerHTML = 'App ready to work offline'
40 | showPwaToast(true)
41 | },
42 | onNeedRefresh() {
43 | pwaToastMessage.innerHTML = 'New content available, click on reload button to update'
44 | showPwaToast(false)
45 | },
46 | onRegisteredSW(swScriptUrl) {
47 | console.log('SW registered: ', swScriptUrl)
48 | }
49 | })
50 | })
51 |
--------------------------------------------------------------------------------
/src/components/styles.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/components/styles.css
--------------------------------------------------------------------------------
/src/components/toaster.jsx:
--------------------------------------------------------------------------------
1 | import { Toast, toaster } from "./generic/Toast.jsx";
2 | import { VsClose } from "solid-icons/vs";
3 | import { FaSolidCircleCheck, FaSolidCircleInfo, FaSolidCircleXmark } from "solid-icons/fa";
4 |
5 | export function showToaster(type, title, message) {
6 | toaster.show((props) => {
7 | const color = type === "info" ? "blue" : type === "success" ? "green" : type === "error" ? "red" : "blue";
8 | return (
9 |
13 |
14 |
15 |
16 | {type === "success" ? (
17 |
18 | ) : type === "info" ? (
19 |
20 | ) : type === "error" ? (
21 |
22 | ) : (
23 |
24 | )}
25 |
26 | {title}
27 |
28 |
29 |
30 |
31 |
{message}
32 |
33 |
44 |
45 |
46 |
47 | );
48 | });
49 | }
50 |
--------------------------------------------------------------------------------
/src/content/changelog/2024-11-11.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.1.0"
4 | date: "2024-11-11"
5 | ---
6 |
7 | ### Keyboard shortcuts are here!
8 |
9 | 
10 |
11 | Use your keyboard to assemble, run, and control other parts of the application.
12 |
13 | ### Add documentation page
14 |
15 | 
16 |
17 | Sim8085 now has a [documentation section](/docs/en). Documents about Sim8085 and
18 | 8085 microprocessor in general will be available in this section.
19 |
20 | ### Support for END Directive
21 |
22 | The `END` directive is used for marking the end of assembling process
23 | and to set the starting address to start execution. [Read the docs](/docs/en/directives/end).
24 |
--------------------------------------------------------------------------------
/src/content/changelog/2024-11-16.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.2.0"
4 | - "2.2.1"
5 | - "2.3.0"
6 | - "2.3.1"
7 | - "2.3.2"
8 | - "2.3.3"
9 | date: "2024-11-16"
10 | ---
11 |
12 | ### Mobile & Offline Support Has Arrived!
13 |
14 | Sim8085 is now more accessible than ever! Use it seamlessly on your mobile device or desktop, whether you're online or offline.
15 |
16 | 
17 |
18 | ### What’s New?
19 |
20 | - **Mobile-Friendly**: Run Sim8085 on your phone to quickly try out programs, learn, and experiment with 8085 assembly programming—anytime, anywhere.
21 | - **Offline Access**: Enjoy Sim8085 even without an internet connection. Yes, you could technically debug an assembly program on a plane (not that you’d want to… or would you?).
22 | - **Installable as an App**: Add Sim8085 to your home screen or desktop and use it like a native app for a smoother experience.
23 |
24 | ### How to Install
25 |
26 | - **On iOS**:
27 | 1. Open [https://www.sim8085.com](https://www.sim8085.com) in Safari.
28 | 2. Tap the **Share** icon and select **Add to Home Screen**.
29 |
30 | 
31 |
32 | - **On Android**:
33 | 1. Open [https://www.sim8085.com](https://www.sim8085.com) in your browser.
34 | 2. Tap the menu (three dots) and select **Add to Home Screen** or **Install App**.
35 |
36 | 
37 |
38 | - **On Desktop**:
39 | 1. Open [https://www.sim8085.com](https://www.sim8085.com).
40 | 2. Look for the **Install** option in your browser's URL bar.
41 |
42 | For detailed instructions, check out Mozilla’s [Installing and Uninstalling Web Apps](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Installing) guide.
43 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-04-24.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.8.0"
4 | date: "2025-04-24"
5 | ---
6 |
7 | ### Added support for multiple labels
8 |
9 | Programs such as the one below failed to assemble earlier,
10 |
11 | ```asm
12 |
13 | ; Program with multiple labels
14 | ; for single statement
15 |
16 | JMP INIT
17 |
18 | ;data
19 | STR: DB 'HELLO'
20 |
21 | INIT:
22 | START:
23 | MVI A, STR
24 |
25 | HLT
26 | ```
27 |
28 | These programs are now supported by the assembler.
29 |
30 | Note that you **cannot** add multiple labels to data definitions such as
31 | `DB`, `EQU`, `SET` and others.
32 |
33 | ```
34 |
35 | ```
36 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-05-03.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.9.0"
4 | date: "2025-05-03"
5 | ---
6 |
7 | ### ✨ Added Support for Long-Running Programs
8 |
9 | Sim8085 can now handle programs with infinite loops, such as those that generate square waves:
10 |
11 | ```asm
12 | ORG 00H
13 |
14 | WHILE:
15 | MVI A, 00H
16 | OUT 02H
17 | MVI D, 0FFH
18 | MVI E, 0FFH
19 | CALL DELAY
20 | MVI A, 01H
21 | OUT 02H
22 | MVI D, 0FFH
23 | MVI E, 0FFH
24 | CALL DELAY
25 | JMP WHILE
26 | HLT
27 |
28 | DELAY:
29 | DCX D
30 | MOV A,D
31 | ORA E
32 | JNZ DELAY
33 | RET
34 | ```
35 |
36 | Previously, such programs could freeze the browser due to tight loops. This update introduces **Instruction Timing Mode**, a new simulator setting that emulates instruction execution delays more realistically.
37 |
38 | 
39 |
40 | When enabled, the simulator inserts delays based on actual 8085 timing, allowing the host browser to remain responsive — even for infinite or long-running loops.
41 |
42 | > **Note**: Instruction Timing Mode is **off by default**. You must enable it before running time-sensitive or infinite loop programs.
43 |
44 | This lays the groundwork for exciting features like:
45 |
46 | - Interactive programs (e.g., waiting for keyboard input)
47 | - Monitor programs that rely on polling loops
48 | - Realistic peripheral emulation in future updates
49 |
50 | ---
51 |
52 | ### 🛠️ Minor Fixes
53 |
54 | - When using the **Full Memory View**, searched rows now appear at the **top** of the view instead of the bottom — making them easier to find.
55 |
56 | 
57 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-05-05.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.11.2"
4 | - "2.11.1"
5 | - "2.11.0"
6 | - "2.10.3"
7 | - "2.10.2"
8 | - "2.10.1"
9 | - "2.10.0"
10 | date: "2025-05-05"
11 | ---
12 |
13 | ### 💡 LED Array Connected to J3
14 |
15 | Sim8085 now includes a **virtual LED Array** connected to **port J3**, letting you simulate LED output visually:
16 |
17 | ```asm
18 | MVI A, 55H
19 | OUT 03H ; lights up alternating LEDs
20 | HLT
21 | ```
22 |
23 | This is perfect for writing I/O programs that display patterns or signals, such as binary counters or blinking sequences.
24 |
25 | 
26 |
27 | ---
28 |
29 | ### 🧰 New Toolbox Panel
30 |
31 | Introducing the **Toolbox Panel** — a handy sidebar with utilities to help you convert and inspect values while programming.
32 |
33 | It currently includes:
34 |
35 | - **Hex ⇄ Decimal Converter**
36 | - **Binary ⇄ Decimal Converter**
37 | - **SIM ⇄ Hex Helper** to visualize SIM instruction bits
38 |
39 | 
40 |
41 | > This is just the beginning — more tools are planned for upcoming releases.
42 |
43 | ---
44 |
45 | ### ⚡ Performance Improvements
46 |
47 | - Improved loading performance with smarter asset loading
48 | - Removed unused Google Fonts to reduce page size
49 |
50 | ---
51 |
52 | ### 🐞 Bug Fixes
53 |
54 | #### 📱 Mobile Layouts
55 |
56 | - Fixed layout of the **Actions** panel on small screens
57 | - Reordered icons in the Actions list for better clarity
58 | - Fixed login page logo distortion on **iPhone Safari**
59 | - Improved **changelog layout** on mobile screens
60 |
61 | #### 🧱 General UI
62 |
63 | - Fixed CSS build issues
64 | - Corrected documentation path in sidebar
65 | - Fixed Tips section styling for consistency
66 |
67 | ---
68 |
69 | Let me know if you'd like to split these into separate posts for each major feature or keep them combined.
70 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-05-06.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.12.1"
4 | - "2.12.0"
5 | - "2.11.3"
6 | date: "2025-05-06"
7 | ---
8 |
9 | ### 🧠 RIM & SIM Instruction Support
10 |
11 | Sim8085 now supports the **RIM** (Read Interrupt Mask) and **SIM** (Set Interrupt Mask) instructions — enabling simulation of advanced interrupt behavior and control flags.
12 |
13 | - You can now write programs that **read and modify interrupt masks** using `RIM` and `SIM`.
14 | - Useful for implementing software-based interrupt control and serial I/O logic.
15 |
16 | ```asm
17 | SIM ; Configure interrupt masks or SOD
18 | RIM ; Read interrupt masks and pending RSTs
19 | ```
20 |
21 | ---
22 |
23 | ### 🚨 RST Instruction and Interrupts
24 |
25 | Support for `RST n` instructions (software interrupts) is now included!
26 |
27 | You can now write monitor-style programs and test interrupt service routines:
28 |
29 | ```asm
30 | RST 7 ; Jump to 0038H
31 | ```
32 |
33 | This opens the door to more **realistic system-level programming** and educational use cases.
34 |
35 | ---
36 |
37 | ### 🧰 SIM/RIM ↔ Number Converter Tool
38 |
39 | A new section has been added to the **Toolbox Panel** to decode and encode values for SIM and RIM instructions.
40 |
41 | - Visualize the meaning of each bit in SIM/RIM values
42 | - Easily toggle flags and copy the result into your program
43 |
44 | > This is especially helpful when setting up SOD, interrupt masks, and checking pending interrupts.
45 |
46 | ---
47 |
48 | ### 🐞 Bug Fixes
49 |
50 | - Fixed a bugs related to extra scrollbars on some screens.
51 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-05-07.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.12.3"
4 | - "2.12.2"
5 | date: "2025-05-07"
6 | ---
7 |
8 | ### ⚙️ Improved Memory and I/O Access Reliability
9 |
10 | This update ensures that **memory and I/O access remain stable and consistent**, even as internal optimizations are made.
11 |
12 | You can now rely on your programs working as expected — without surprises caused by behind-the-scenes changes.
13 |
14 | ---
15 |
16 | ### ⚡ Performance Improvements
17 |
18 | - Simulator is now **lazy-loaded**, meaning it's only fetched when needed — improving initial load times significantly.
19 | - Additional load time optimizations applied to reduce startup delays, especially on slower connections.
20 |
21 | ---
22 |
23 | ### 🎨 UI & Styling Fixes
24 |
25 | - Fixed styling issues in the **left and right panels**, including spacing, overflow, and alignment.
26 | - Fixed layout problems caused by **large footers**, especially in documentation or tutorial pages.
27 | - Polished the **changelog page styling** for better readability and consistency.
28 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-05-08.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.13.0"
4 | date: "2025-05-08"
5 | ---
6 |
7 | ### 🆕 Interrupt Controls & Indicators
8 |
9 | Sim8085 now supports **realistic interrupt simulation** — not just the CPU behavior, but also the **hardware lines and pending states**:
10 |
11 | - 🟡 **Interrupt Control Panel**
12 | You can now toggle **TRAP**, **RST 5.5**, **RST 6.5**, and **RST 7.5** lines from the new lightning bolt (⚡) menu — just like pressing hardware buttons. Each toggle represents the state of the actual interrupt line.
13 |
14 | 
15 |
16 | - 🟢 **Live Interrupt Status Panel**
17 | A new sidebar section displays the **enabled/masked state** of each interrupt, as well as any **pending interrupts**.
18 | TRAP is always enabled (as it is in real 8085), and RST 7.5 now includes proper **latch behavior**.
19 |
20 | 
21 |
22 | These improvements make it easier to understand and debug interrupt-driven programs — and bring Sim8085 even closer to how real 8085 hardware behaves.
23 |
--------------------------------------------------------------------------------
/src/content/changelog/2025-05-14.md:
--------------------------------------------------------------------------------
1 | ---
2 | versions:
3 | - "2.15.0"
4 | date: "2025-05-14"
5 | ---
6 |
7 | ### 🧹 Code Formatter
8 |
9 | Sim8085 now includes a built-in **code formatting tool** to clean up and align your 8085 assembly code — making it more readable and consistent with traditional 3-column style:
10 |
11 | - 🧠 **Smart Label-Aware Formatting**
12 | Labels are aligned to the left, and if a label is too long, the instruction is moved to the next line automatically.
13 |
14 | - 🧱 **Mnemonic and Operand Alignment**
15 | Instructions and operands are neatly spaced into consistent columns, making the structure of your code easier to follow.
16 |
17 | - ✍️ **Comment Preservation**
18 | Comments are retained exactly where you typed them — whether inline or on standalone lines.
19 |
20 | - ✨ **Blank Line Preservation**
21 | Your original blank lines are preserved, so logical sections of your program remain visually distinct.
22 |
23 | 🧪 **Note:** This feature is still in testing. If you notice any formatting issues or edge cases, please [report them on GitHub](https://github.com/debjitbis08/sim8085/issues).
24 |
--------------------------------------------------------------------------------
/src/content/changelog/images/android-sim8085-install-2024-11-16.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/android-sim8085-install-2024-11-16.jpeg
--------------------------------------------------------------------------------
/src/content/changelog/images/doc-section-2024-11-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/doc-section-2024-11-11.png
--------------------------------------------------------------------------------
/src/content/changelog/images/full-memory-search-top-2025-05-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/full-memory-search-top-2025-05-03.png
--------------------------------------------------------------------------------
/src/content/changelog/images/instruction-timing-setting-2025-05-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/instruction-timing-setting-2025-05-03.png
--------------------------------------------------------------------------------
/src/content/changelog/images/interrupt-controls-2025-05-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/interrupt-controls-2025-05-08.png
--------------------------------------------------------------------------------
/src/content/changelog/images/interrupt-panel-2025-05-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/interrupt-panel-2025-05-08.png
--------------------------------------------------------------------------------
/src/content/changelog/images/ios-sim8085-install-2024-11-16.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/ios-sim8085-install-2024-11-16.jpeg
--------------------------------------------------------------------------------
/src/content/changelog/images/keyboard-shortcuts-2024-11-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/keyboard-shortcuts-2024-11-11.png
--------------------------------------------------------------------------------
/src/content/changelog/images/led-array-2025-05-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/led-array-2025-05-05.png
--------------------------------------------------------------------------------
/src/content/changelog/images/sim8085-mobile-layout-2024-11-16.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/sim8085-mobile-layout-2024-11-16.jpeg
--------------------------------------------------------------------------------
/src/content/changelog/images/toolbox-panel-2025-05-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/changelog/images/toolbox-panel-2025-05-05.png
--------------------------------------------------------------------------------
/src/content/config.js:
--------------------------------------------------------------------------------
1 | import { defineCollection, z } from 'astro:content';
2 | import { docsSchema } from '@astrojs/starlight/schema';
3 |
4 | export const collections = {
5 | docs: defineCollection({ schema: docsSchema() }),
6 | changes: defineCollection({
7 | versions: z.array(z.string()),
8 | date: z.string()
9 | })
10 | };
11 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/404.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Not found
3 | template: splash
4 | editUrl: false
5 | hero:
6 | title: '404'
7 | tagline: Houston, we have a problem. We couldn’t find that page. Check the URL or try using the search bar.
8 | actions:
9 | - text: Go home
10 | icon: right-arrow
11 | link: /
12 | variant: primary
13 | ---
14 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/8085.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: 8085 Microprocessor
3 | description: Sim8085 CPU features.
4 | ---
5 |
6 | The processor in Sim8085 is obviously the Intel 8085A microprocessor. You can read about it in detail
7 | in the [8085 datasheet](https://ia801807.us.archive.org/3/items/intel-8085-datasheet/8085_datasheet.pdf).
8 |
9 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/app-install.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installing Sim8085
3 | description: Sim8085 can be installed as a desktop or mobile app and can be used offline.
4 | ---
5 |
6 | Sim8085 is a **Progressive Web App (PWA)**, which means you can install it like a native desktop
7 | application directly from your Chrome browser on Windows. Once installed, it behaves like a
8 | standalone app — with offline support, a cleaner UI, and faster load times.
9 |
10 | * 🪟 A standalone window (no address bar or tabs)
11 | * 🛫 Full offline access after installation
12 | * 📌 Launch from Start Menu or taskbar
13 | * ⚡ Faster access and cleaner experience
14 |
15 | ---
16 |
17 | ## 🧭 Option 1: Google Chrome (Windows)
18 |
19 | ### ✅ Steps
20 |
21 | 1. Open **Chrome** and go to [https://www.sim8085.com](https://www.sim8085.com).
22 | 2. Click the **three-dot menu** in the top-right corner of the browser.
23 | 3. Hover over **"Cast, save, and share"**.
24 | 4. Click **"Install page as app…"**
25 | *(as shown in the screenshot below)*
26 |
27 | 
28 |
29 | 5. Confirm the prompt by clicking **Install**.
30 | 6. Sim8085 will launch in its own window, and you can pin it to the **taskbar**, **desktop**, or **Start menu**.
31 |
32 | ---
33 |
34 | ## 🧭 Option 2: Microsoft Edge (Windows)
35 |
36 | ### ✅ Steps
37 |
38 | 1. Open **Edge** and navigate to [https://www.sim8085.com](https://www.sim8085.com).
39 | 2. Click the **three-dot menu** in the top-right corner.
40 | 3. Go to **"Apps"** → **"Install this site as an app"**.
41 |
42 | 
43 |
44 | 4. Click **Install** in the confirmation dialog.
45 | 5. Sim8085 will open in a new window. You’ll also find it listed in the Start Menu, and you can pin it to your taskbar or desktop.
46 |
47 | ---
48 |
49 | ## 📡 Offline Support
50 |
51 | Once installed, Sim8085 will work **offline**. You can:
52 |
53 | * Open and use it without internet.
54 | * Load programs, edit, and simulate 8085 code offline.
55 | * Updates will automatically apply when you're online next.
56 |
57 | ---
58 |
59 | ## 🧽 How to Uninstall
60 |
61 | 1. Right-click the **Sim8085** icon in your Start menu or taskbar.
62 | 2. Select **Uninstall** or **Remove from this device**.
63 | 3. Alternatively, open Chrome, go to `chrome://apps`, right-click Sim8085, and choose **Remove from Chrome**.
64 |
65 | ---
66 |
67 | ## 🔍 Need More Help?
68 |
69 | See [Chrome's official PWA install guide](https://support.google.com/chrome/answer/9658361).
70 |
71 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/directives/end.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: END
3 | description: END Directive
4 | ---
5 |
6 | The END directive marks the conclusion of the source program and signals the assembler to terminate each pass.
7 |
8 | Format:
9 |
10 | | Label | Opcode | Operand (Optional) |
11 | |-----------|--------|--------------------|
12 | | Optional: | `END` | `expression` |
13 |
14 | * Only one END statement is allowed in a source program, and it must be the final statement.
15 | * If an optional expression is provided, its value determines the starting address for program execution.
16 | * If no expression is specified, the assembler defaults the starting address to zero.
17 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/guides/example.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Example Guide
3 | description: A guide in my new Starlight docs site.
4 | ---
5 |
6 | Guides lead a user through a specific task they want to accomplish, often with a sequence of steps.
7 | Writing a good guide requires thinking about what your users are trying to do.
8 |
9 | ## Further reading
10 |
11 | - Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework
12 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/instructions/aci.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ACI - Add Immediate with Carry
3 | description: Add Immediate with Carry
4 | ---
5 |
6 | **Add Immediate with Carry**
7 |
8 | The ACI instruction adds the immediate operand (data) and the current carry bit to the accumulator. The result is stored back in the accumulator. The carry flag is added if it's set.
9 |
10 | ## Instruction Format
11 |
12 | | Opcode | Operand |
13 | |--------|---------|
14 | | `ACI` | `data` |
15 |
16 | ## Operand Details
17 |
18 | The operand specifies the actual data to be added to the accumulator, except for the carry bit. The data may be in one of the following forms:
19 | - A number
20 | - An ASCII constant
21 | - The label of a previously defined value
22 | - An expression
23 |
24 | The data must not exceed **one byte**.
25 |
26 | ## Operation
27 |
28 | The operation performed by `ACI` is:
29 |
30 | ```
31 | Accumulator = Accumulator + Immediate Data + Carry
32 | ```
33 |
34 | - **Operand**: The immediate data to be added to the accumulator. It must be a single byte (8 bits) and can be in the form of a number, an ASCII constant, or an expression.
35 | - **Carry Bit**: This instruction includes the carry bit in its addition.
36 |
37 |
38 | ## Flags Affected
39 |
40 | - **Z (Zero)**: Set if the result is zero.
41 | - **S (Sign)**: Set if the result is negative.
42 | - **P (Parity)**: Set if the result has even parity.
43 | - **CY (Carry)**: Set if there is a carry out.
44 | - **AC (Auxiliary Carry)**: Set if there is a carry out from the lower nibble.
45 |
46 |
47 | ## Execution Details
48 |
49 | | **Cycles** | **States** | **Addressing Mode** | **Flags** |
50 | |------------|------------|---------------------|---------------------|
51 | | 2 | 7 | Immediate | Z, S, P, CY, AC |
52 |
53 | ## Example
54 |
55 | **Before Execution**:
56 | - **Accumulator**: `24H` (00100100)
57 | - **Immediate Data**: `36H` (00110110)
58 | - **Carry Bit**: `1`
59 |
60 | **After Execution**:
61 | - **Accumulator**: `(24H + 36H + 1)` = `5BH` (01011011)
62 |
63 | ### Breakdown of Addition (Binary)
64 |
65 | ```
66 | Accumulator = 00100100 (24H)
67 | Immediate = 00110110 (36H)
68 | Carry = 00000001
69 | --------
70 | Result = 01011011 (5BH)
71 | ```
72 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/instructions/adc.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ADC — Add with Carry
3 | ---
4 |
5 | The `ADC` instruction adds the contents of a register or memory location, along
6 | with the carry flag, to the accumulator. The result is stored back in the
7 | accumulator. This instruction also updates the carry flag and other condition
8 | flags based on the result of the operation.
9 |
10 | The `ADC` instruction enables multi-byte addition by utilizing the carry flag,
11 | allowing you to chain multiple additions together.
12 |
13 | ## Instruction Format
14 |
15 | | Opcode | Operand |
16 | |--------|-------------|
17 | | `ADC` | `register` |
18 | | `ADC` | `M` |
19 |
20 | **Operand**: The operand can be one of the registers `A` through `E`, `H`, or `L`, or the
21 | memory location (`M`) addressed by the contents of the `H` and `L` registers.
22 |
23 | ## Operation
24 |
25 | ```
26 | Accumulator = Accumulator + Register/Memory + Carry
27 | ```
28 |
29 | **Carry Bit**: If the carry bit is set (CY=1), it is added to the result along with
30 | the contents of the specified register or memory.
31 |
32 | ## Flags Affected
33 |
34 | - **Z (Zero)**: Set if the result is zero.
35 | - **S (Sign)**: Set if the result is negative (most significant bit is 1).
36 | - **P (Parity)**: Set if the result has even parity.
37 | - **CY (Carry)**: Set if there is a carry out of the most significant bit.
38 | - **AC (Auxiliary Carry)**: Set if there is a carry from bit 3 to bit 4.
39 |
40 | ## Execution Details
41 |
42 | ### Add Register to Accumulator with Carry
43 |
44 | This adds the contents of the specified register and the carry bit to the
45 | accumulator, and stores the result in the accumulator.
46 |
47 | | **Cycles** | **States** | **Addressing Mode** | **Flags** |
48 | |------------|------------|---------------------|---------------------|
49 | | 1 | 4 | Register | Z, S, P, CY, AC |
50 |
51 | ### Add Memory to Accumulator with Carry
52 |
53 | This adds the contents of the memory location addressed by the `H` and `L`
54 | registers, along with the carry bit, to the accumulator. `M` refers to the memory
55 | location (`H` and `L` registers).
56 |
57 | | **Cycles** | **States** | **Addressing Mode** | **Flags** |
58 | |------------|------------|---------------------|---------------------|
59 | | 2 | 7 | Register Indirect | Z, S, P, CY, AC |
60 |
61 | ## Example 1: (Carry = 0)
62 |
63 | **Initial State**
64 | - Register C: 3DH (00111101)
65 | - Accumulator: 42H (01000010)
66 | - Carry Bit: 0
67 |
68 | **After Execution (ADC C)**
69 |
70 | Result:
71 |
72 | ```
73 | Accumulator = 42H + 3DH + 0 = 7FH (01111111)
74 | ```
75 |
76 | The condition flags are set as follows:
77 |
78 | - Carry: `0`
79 | - Sign: `0`
80 | - Zero: `0`
81 | - Parity: `0` (odd parity)
82 | - Auxiliary Carry: `0`
83 |
84 | ## Example 2: (Carry = 1)
85 |
86 | **Initial State**
87 | - Register C: 3DH (00111101)
88 | - Accumulator: 42H (01000010)
89 | - Carry Bit: 1
90 |
91 | **After Execution (ADC C)**
92 |
93 | Result:
94 |
95 | ```
96 | Accumulator = 42H + 3DH + 1 = 80H (10000000)
97 | ```
98 |
99 | The condition flags are set as follows:
100 |
101 | - Carry: `0`
102 | - Sign: `1` (most significant bit is 1)
103 | - Zero: `0`
104 | - Parity: `0` (odd parity)
105 | - Auxiliary Carry: `1` (carry from bit 3 to bit 4)
106 |
--------------------------------------------------------------------------------
/src/content/docs/docs/en/unsupported.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Unsupported Features
3 | description: Sim8085 does not support some instructions and advanced assembler features yet.
4 | ---
5 |
6 | We have tried our best to implement as many features specified in the original
7 | 8080/85 Assembly Language Programming Manual but due to time constraints and
8 | not been able to implement all of them. We want the application to be useful
9 | to users as soon as possible and thus we have released the application with
10 | these missing features.
11 |
12 | We will be implementing all of these in due time.
13 |
14 | If you believe any of these are essential, please raise a request through the
15 | GitHub [discussions](https://github.com/debjitbis08/sim8085/discussions/categories/ideas) page.
16 |
17 | ### Unsupported Directives
18 |
19 | The application does not support the following,
20 |
21 | - `DS`
22 | - `IF`, `ELSE` & `ENDIF`
23 | - `EOT`
24 | - Some of the relocation directives, `CSEG`, `DSEG`, `PUBLIC`, `EXTRN`, `NAME`, `STKLN` & the `STACK` and `MEMORY`
25 | reserved words.
26 | - None of the macro directives, `MACRO`, `ENDM`, `LOCAL`, `REPT`, `IRP`, `IRPC`, `EXITM`
27 |
28 | ### Unsupported Expressions
29 |
30 | Apart of the arithmetic expression, no other kind of expression are supported right now.
31 |
--------------------------------------------------------------------------------
/src/content/docs/docs/images/install-using-chrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/docs/docs/images/install-using-chrome.png
--------------------------------------------------------------------------------
/src/content/docs/docs/images/install-using-edge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/docs/docs/images/install-using-edge.png
--------------------------------------------------------------------------------
/src/content/docs/docs/images/interrupt-controls-2025-05-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/docs/docs/images/interrupt-controls-2025-05-08.png
--------------------------------------------------------------------------------
/src/content/docs/docs/images/interrupt-panel-2025-05-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/content/docs/docs/images/interrupt-panel-2025-05-08.png
--------------------------------------------------------------------------------
/src/core/8085.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/core/8085.wasm
--------------------------------------------------------------------------------
/src/core/parserTests/label.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test, expect } from 'vitest';
2 |
3 | describe('8085 Simulator Functions', () => {
4 | test('label with colon and op should be parsed', () => {
5 | expect(1).toBe(1);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/core/simulator-library.js:
--------------------------------------------------------------------------------
1 | addToLibrary({
2 | io_write: function (address, value) {
3 | io_write(address, value);
4 | },
5 | });
6 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | interface Window {
5 | posthog: object;
6 | }
7 |
--------------------------------------------------------------------------------
/src/icons/bmc.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/icons/computer-desktop.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/icons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/icons/hand-holding-heart.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/kofi.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/lock.svg:
--------------------------------------------------------------------------------
1 |
3 |
5 |
--------------------------------------------------------------------------------
/src/icons/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/icons/moon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/icons/paypal.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/razorpay.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/icons/regular-dock-left.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/src/icons/regular-dock-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/solid-dock-left.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/solid-dock-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/sponsor.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/sun.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/icons/triangle-info.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/youtube.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/images/lambda-classes-poster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/images/lambda-classes-poster.png
--------------------------------------------------------------------------------
/src/images/natory-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/images/natory-light.png
--------------------------------------------------------------------------------
/src/images/natory.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/images/natory.png
--------------------------------------------------------------------------------
/src/images/pc-start-value-textbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/images/pc-start-value-textbox.png
--------------------------------------------------------------------------------
/src/images/project-track-poster.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debjitbis08/sim8085/fed030ab927293272254c0c5e81b079f4f8c29a3/src/images/project-track-poster.jpg
--------------------------------------------------------------------------------
/src/lib/supabase.js:
--------------------------------------------------------------------------------
1 | import { createClient } from "@supabase/supabase-js";
2 |
3 | const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL;
4 | const supabaseAnonKey = import.meta.env.PUBLIC_SUPABASE_ANON_KEY;
5 |
6 | export let supabase = null;
7 |
8 | if (supabaseUrl && supabaseAnonKey) {
9 | supabase = createClient(supabaseUrl, supabaseAnonKey);
10 | }
11 |
12 | export const getUser = async () => {
13 | if (!supabase) return { user: null, error: new Error("Supabse is not initialized") };
14 |
15 | const {
16 | data: { user },
17 | error,
18 | } = await supabase.auth.getUser();
19 |
20 | return { user, error };
21 | };
22 |
23 | export const getSession = async () => {
24 | if (!supabase) return { session: null, error: new Error("Supabse is not initialized") };
25 |
26 | const {
27 | data: { session },
28 | error,
29 | } = await supabase.auth.getSession();
30 |
31 | return { session, error };
32 | };
33 |
34 | export const getUserFromSession = async () => {
35 | if (!supabase) return { user: null, error: new Error("Supabse is not initialized") };
36 |
37 | const {
38 | data: { session },
39 | error,
40 | } = await supabase.auth.getSession();
41 |
42 | return { user: session?.user, error };
43 | };
44 |
45 | export const signOut = async () => {
46 | if (supabase) {
47 | await supabase.auth.signOut();
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/src/pages/changelog.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from '../layouts/Layout.astro';
3 | import { getCollection, getEntries } from 'astro:content';
4 |
5 | const dateFormatter = new Intl.DateTimeFormat('en-US', {
6 | year: 'numeric',
7 | month: 'long',
8 | day: 'numeric',
9 | })
10 |
11 | const entries = await Promise.all(
12 | (await getCollection("changelog"))
13 | .sort((entryA, entryB) =>
14 | Number(new Date(entryB.data.date)) - Number(new Date(entryA.data.date))
15 | )
16 | .map((entry) => {
17 | return entry.render().then(({ Content }) => ({
18 | ...entry,
19 | Content,
20 | date: dateFormatter.format(new Date(entry.data.date)),
21 | })).catch((err) => console.error(err))
22 | })
23 | );
24 | ---
25 |
26 |
27 |
28 |
29 |
30 |
31 |
December 00, 0000
class
32 |
33 |
34 |
Changelog
35 |
New updates and improvements for Sim8085
36 |
39 |
40 |
41 |
42 | {entries.map((entry, index) => (
43 |
44 |
45 |
46 | {entry.date}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Detailed Changelogs
55 |
56 | {entry.data.versions.map((version) => (
57 |
58 | v{version}
59 |
60 | ))}
61 |
62 |
63 |
64 |
65 | ))}
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/pwa.js:
--------------------------------------------------------------------------------
1 | import { registerSW } from 'virtual:pwa-register';
2 |
3 | registerSW({
4 | immediate: true,
5 | onRegisteredSW(swScriptUrl) {
6 | console.log('SW registered: ', swScriptUrl)
7 | },
8 | onOfflineReady() {
9 | console.log('PWA application ready to work offline')
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/src/store/store.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "solid-js/store";
2 |
3 | export const INITIAL_CODE = `;
4 |
5 | JMP START
6 |
7 | ;data
8 |
9 | ;code
10 | START: NOP
11 |
12 | ;Start writing your code here
13 |
14 | HLT
15 | `;
16 |
17 | export const REGISTER_KEYS = ["bc", "de", "hl"];
18 |
19 | const initialRegisterState = () => {
20 | return REGISTER_KEYS.reduce(
21 | (o, registerId) => ({
22 | ...o,
23 | [registerId]: { high: 0, low: 0, isEditing: false },
24 | }),
25 | {},
26 | );
27 | };
28 |
29 | export const DEFAULT_SETTINGS = {
30 | run: {
31 | enableTiming: false,
32 | clockFrequency: "3072000",
33 | },
34 | beforeRun: {
35 | clearFlags: true,
36 | clearRegisters: true,
37 | clearAllMemoryLocations: false,
38 | },
39 | alert: {
40 | afterSuccessfulRun: true,
41 | afterClearAll: true,
42 | afterDebugStop: true,
43 | },
44 | editor: {
45 | fontSize: 16,
46 | },
47 | };
48 |
49 | export const [store, setStore] = createStore({
50 | code: INITIAL_CODE,
51 | codeWithError: "",
52 | programState: "Idle", // Idle, Loaded, Running, Paused
53 | assembled: [],
54 | loadAddress: 0,
55 | accumulator: 0,
56 | isEditingAccumulator: false,
57 | registers: initialRegisterState(),
58 | stackPointer: 0xffff,
59 | programCounter: 0,
60 | pcStartValue: 0,
61 | statePointer: null,
62 | flags: {
63 | s: false,
64 | z: false,
65 | ac: false,
66 | p: false,
67 | c: false,
68 | },
69 | memory: Array(65536).fill(0),
70 | io: Array(256).fill(0),
71 | interruptsEnabled: false,
72 | interruptMasks: {
73 | rst55: false,
74 | rst65: false,
75 | rst75: false,
76 | },
77 | pendingInterrupts: {
78 | trap: false,
79 | rst55: false,
80 | rst65: false,
81 | rst75: false,
82 | },
83 | breakpoints: [],
84 | errors: [],
85 | activeFile: {
86 | name: "untitled-1.asm",
87 | workspaceItemId: null,
88 | currentVersionId: null,
89 | content: INITIAL_CODE,
90 | },
91 | homeFolderId: null,
92 | settings: structuredClone(DEFAULT_SETTINGS),
93 | });
94 |
--------------------------------------------------------------------------------
/src/tailwind.docs.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 |
3 | import starlightPlugin from "@astrojs/starlight-tailwind";
4 |
5 | export default {
6 | content: {
7 | relative: true,
8 | files: ["./content/docs/**/*.{md,mdx}"],
9 | },
10 | darkMode: ["class", '[data-theme="dark"]'],
11 | theme: {
12 | extend: {},
13 | },
14 | plugins: [starlightPlugin()],
15 | };
16 |
--------------------------------------------------------------------------------
/src/tailwind.docs.css:
--------------------------------------------------------------------------------
1 | @config "./tailwind.docs.config.mjs";
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
--------------------------------------------------------------------------------
/src/tailwind.main.css:
--------------------------------------------------------------------------------
1 | @config './tailwind.main.config.mjs';
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 | @layer base {
8 | :root {
9 | --sm-primary-50: 238 251 243;
10 | --sm-primary-100: 214 245 225;
11 | --sm-primary-200: 177 233 200;
12 | --sm-primary-300: 126 215 169;
13 | --sm-primary-400: 73 190 132;
14 | --sm-primary-500: 38 162 105;
15 | --sm-primary-600: 24 131 84;
16 | --sm-primary-700: 19 105 69;
17 | --sm-primary-800: 18 83 57;
18 | --sm-primary-900: 15 69 48;
19 | --sm-primary-950: 8 38 27;
20 |
21 | --sm-primary: var(--sm-primary-500);
22 |
23 | --sm-gray-50: 251 251 251; /* #fbfbfb */
24 | --sm-gray-100: 239 239 239; /* #efefef */
25 | --sm-gray-200: 220 220 220; /* #dcdcdc */
26 | --sm-gray-300: 189 189 189; /* #bdbdbd */
27 | --sm-gray-400: 152 152 152; /* #989898 */
28 | --sm-gray-500: 124 124 124; /* #7c7c7c */
29 | --sm-gray-600: 101 101 101; /* #656565 */
30 | --sm-gray-700: 82 82 82; /* #525252 */
31 | --sm-gray-800: 70 70 70; /* #464646 */
32 | --sm-gray-900: 61 61 61; /* #3d3d3d */
33 | --sm-gray-950: 41 41 41; /* #292929 */
34 |
35 | --sm-page-background: 232 232 232; /* #e8e8e8 */
36 | --sm-main-background: var(--sm-gray-50);
37 | --sm-secondary-background: var(--sm-gray-100);
38 | --sm-main-border: var(--sm-gray-300);
39 |
40 | --sm-primary-foreground: var(--sm-gray-700);
41 | --sm-primary-border: var(--sm-gray-600);
42 |
43 | --sm-secondary-foreground: var(--sm-gray-500);
44 | --sm-secondary-border: var(--sm-gray-600);
45 |
46 | --sm-active-foreground: var(--sm-gray-700);
47 | --sm-active-background: var(--sm-gray-200);
48 | --sm-active-border: var(--sm-gray-300);
49 |
50 | --sm-inactive-foreground: var(--sm-gray-400);
51 | --sm-inactive-border: var(--sm-gray-400);
52 |
53 | --sm-red-foreground: 185 28 28; /* #b91c1c */
54 | --sm-green-foreground: 21 128 61; /* #15803d */
55 | --sm-yellow-foreground: 234 179 8; /* #eab308 */
56 | --sm-blue-foreground: 37 99 235; /* #2563eb */
57 | --sm-orange-foreground: 234 88 12; /* #ea580c */
58 |
59 | /* Editor colors */
60 | --sm-editor-active-line: 191 219 254; /* #bfdbfe */
61 | --sm-editor-debug-line: 96 165 250; /* #60a5fa */
62 | --sm-editor-active-line-gutter: 255 255 255;
63 | --sm-editor-cursor: 75 85 99; /* #4b5563 */
64 | --sm-editor-selection-background: 191 219 254; /* #bfdbfe */
65 |
66 | --sm-editor-opcode: 28 128 61;
67 | --sm-editor-directive: 29 78 216;
68 | --sm-editor-label: 168 91 7;
69 | --sm-editor-comment: 107 114 128;
70 | --sm-editor-register: 185 28 28;
71 | --sm-editor-number: 194 65 12;
72 | --sm-editor-string: 234 88 12;
73 | --sm-editor-operator: 190 24 ;
74 | --sm-editor-punctuation: 156 163 175;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/tests/adi.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import { runTest } from "./test-utils";
3 |
4 | describe("ADI Instruction Tests", () => {
5 | test("ADI: Sets AC flag", async () => {
6 | const code = `
7 | mvi a, 8h
8 | adi 8h
9 | hlt
10 | `;
11 | const expectedCpuState = {
12 | accumulator: 0x10,
13 | flags: {
14 | z: false,
15 | s: false,
16 | p: false,
17 | c: false,
18 | ac: true,
19 | },
20 | programCounter: 0x0005,
21 | };
22 | await runTest(code, {}, expectedCpuState);
23 | });
24 |
25 | test("ADI: Resets AC flag", async () => {
26 | const code = `
27 | mvi a, 14h
28 | adi 42h
29 | hlt
30 | `;
31 | const expectedCpuState = {
32 | accumulator: 0x56,
33 | flags: {
34 | z: false,
35 | s: false,
36 | p: true,
37 | c: false,
38 | ac: false,
39 | },
40 | programCounter: 0x0005,
41 | };
42 | await runTest(code, {}, expectedCpuState);
43 | });
44 | });
--------------------------------------------------------------------------------
/src/tests/ani.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("ANI Instruction Tests", () => {
6 | test("ANI: Performs logical AND between accumulator and immediate data, resets carry flag", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit accumulator value
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit immediate value
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (accumulator, immediateValue, flags) => {
19 | const result = accumulator & immediateValue;
20 |
21 | // Calculate expected flags
22 | const zeroFlag = result === 0;
23 | const signFlag = (result & 0x80) !== 0;
24 | const parityFlag =
25 | result
26 | .toString(2)
27 | .split("")
28 | .filter((bit) => bit === "1").length %
29 | 2 ===
30 | 0;
31 |
32 | const code = `
33 | org 0x0000
34 | ani ${immediateValue}
35 | hlt
36 | `;
37 |
38 | // Define the initial state of the CPU
39 | const initialCpuState = {
40 | accumulator,
41 | registers: {
42 | bc: { high: 0x00, low: 0x00 },
43 | de: { high: 0x00, low: 0x00 },
44 | hl: { high: 0x00, low: 0x00 },
45 | },
46 | flags,
47 | };
48 |
49 | // Define the expected state of the CPU after execution
50 | const expectedCpuState = {
51 | ...initialCpuState,
52 | accumulator: result, // Result of the AND operation
53 | flags: {
54 | z: zeroFlag,
55 | s: signFlag,
56 | p: parityFlag,
57 | c: false, // Carry flag is always reset to 0 by ANI
58 | ac: false, // Auxiliary carry is always reset to 0 by ANI
59 | },
60 | programCounter: 0x0003,
61 | };
62 |
63 | await runTest(code, initialCpuState, expectedCpuState);
64 | },
65 | ),
66 | { verbose: true, numRuns: 100 }, // Run 100 variations for ANI instruction
67 | );
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/src/tests/call.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | // Define the condition functions for each CALL instruction
6 | const callConditions = {
7 | CALL: () => true, // Always call
8 | CC: (flags) => flags.c, // Call if carry flag set
9 | CNC: (flags) => !flags.c, // Call if carry flag not set
10 | CZ: (flags) => flags.z, // Call if zero flag set
11 | CNZ: (flags) => !flags.z, // Call if zero flag not set
12 | CM: (flags) => flags.s, // Call if minus (sign flag set)
13 | CP: (flags) => !flags.s, // Call if positive (sign flag not set)
14 | CPE: (flags) => flags.p, // Call if parity even
15 | CPO: (flags) => !flags.p, // Call if parity odd
16 | };
17 |
18 | describe("CALL and RET Instructions with Dynamically Generated Labels Tests", () => {
19 | Object.entries(callConditions).forEach(([instruction, condition]) => {
20 | test(`${instruction}: Conditional call and return with dynamically generated labels`, async () => {
21 | await fc.assert(
22 | fc.asyncProperty(
23 | fc.record({
24 | z: fc.boolean(),
25 | s: fc.boolean(),
26 | p: fc.boolean(),
27 | c: fc.boolean(),
28 | ac: fc.boolean(),
29 | }), // Random flag states
30 | fc.stringMatching(/^[a-zA-Z?@][a-zA-Z0-9]{0,5}$/),
31 | async (flags, labelName) => {
32 | const code = `
33 | org 0x0000
34 | ${instruction} ${labelName}
35 | hlt
36 |
37 | org 0x1000
38 | ${labelName}:
39 | pop b
40 | dcx sp
41 | dcx sp
42 | ret
43 | `;
44 |
45 | const initialCpuState = {
46 | accumulator: 0x00,
47 | registers: {
48 | bc: { high: 0x00, low: 0x00 },
49 | de: { high: 0x00, low: 0x00 },
50 | },
51 | flags, // Randomized flags
52 | programCounter: 0x0000, // Initial PC
53 | };
54 |
55 | const shouldCall = condition(flags);
56 |
57 | await runTest(code, initialCpuState, {
58 | registers: {
59 | bc: shouldCall
60 | ? {
61 | high: 0,
62 | low: 3,
63 | }
64 | : { high: 0, low: 0 },
65 | },
66 | programCounter: 4,
67 | });
68 | },
69 | ),
70 | { verbose: true, numRuns: 100 }, // Run 100 variations for each instruction
71 | );
72 | });
73 | });
74 | });
--------------------------------------------------------------------------------
/src/tests/cma.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('CMA Instruction Property-Based Tests', () => {
6 | test('CMA: Complements the accumulator without modifying flags', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Generate an initial accumulator value and flags
10 | fc.integer({ min: 0x00, max: 0xFF }), // Accumulator can hold 8-bit values
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }),
18 | async (initialAccumulator, initialFlags) => {
19 | const code = `
20 | cma
21 | hlt
22 | `;
23 |
24 | const initialCpuState = {
25 | accumulator: initialAccumulator,
26 | registers: {
27 | bc: { high: 0x00, low: 0x00 },
28 | de: { high: 0x00, low: 0x00 },
29 | hl: { high: 0x00, low: 0x00 },
30 | },
31 | flags: initialFlags,
32 | programCounter: 0x0000,
33 | memory: {},
34 | };
35 |
36 | const expectedCpuState = {
37 | ...initialCpuState,
38 | accumulator: ~initialAccumulator & 0xFF, // Compute one's complement (ensure 8-bit wrap)
39 | programCounter: 0x0002,
40 | };
41 |
42 | await runTest(code, initialCpuState, expectedCpuState);
43 | }
44 | ),
45 | {
46 | verbose: true,
47 | numRuns: 100, // Test with 100 random variations
48 | }
49 | );
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/src/tests/cmc.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import { runTest } from './test-utils';
3 |
4 | describe('CMC Instruction Tests', () => {
5 | test('CMC: Toggles carry flag from 0 to 1', async () => {
6 | const code = `
7 | cmc
8 | hlt
9 | `;
10 |
11 | const initialCpuState = {
12 | accumulator: 0x00,
13 | registers: {
14 | bc: { high: 0x00, low: 0x00 },
15 | de: { high: 0x00, low: 0x00 },
16 | hl: { high: 0x00, low: 0x00 },
17 | },
18 | flags: {
19 | z: false,
20 | s: false,
21 | p: false,
22 | c: false, // Carry flag starts at 0
23 | ac: false,
24 | },
25 | };
26 |
27 | const expectedCpuState = {
28 | ...initialCpuState,
29 | flags: {
30 | ...initialCpuState.flags,
31 | c: true, // Carry flag toggled to 1
32 | },
33 | programCounter: 0x0002,
34 | };
35 |
36 | await runTest(code, initialCpuState, expectedCpuState);
37 | });
38 |
39 | test('CMC: Toggles carry flag from 1 to 0', async () => {
40 | const code = `
41 | cmc
42 | hlt
43 | `;
44 |
45 | const initialCpuState = {
46 | accumulator: 0x00,
47 | registers: {
48 | bc: { high: 0x00, low: 0x00 },
49 | de: { high: 0x00, low: 0x00 },
50 | hl: { high: 0x00, low: 0x00 },
51 | },
52 | flags: {
53 | z: false,
54 | s: false,
55 | p: false,
56 | c: true, // Carry flag starts at 1
57 | ac: false,
58 | },
59 | };
60 |
61 | const expectedCpuState = {
62 | ...initialCpuState,
63 | flags: {
64 | ...initialCpuState.flags,
65 | c: false, // Carry flag toggled to 0
66 | },
67 | programCounter: 0x0002,
68 | };
69 |
70 | await runTest(code, initialCpuState, expectedCpuState);
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/src/tests/cpi.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("CPI Instruction Tests", () => {
6 | test("CPI: Compares immediate data with accumulator and sets zero and carry flags", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit accumulator value
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit immediate value
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (accumulator, immediateValue, flags) => {
19 | const result = accumulator - immediateValue;
20 |
21 | // Calculate expected flags
22 | const zeroFlag = result === 0;
23 | const carryFlag = accumulator < immediateValue;
24 |
25 | const code = `
26 | org 0x0000
27 | cpi ${immediateValue}
28 | hlt
29 | `;
30 |
31 | // Define the initial state of the CPU
32 | const initialCpuState = {
33 | accumulator,
34 | registers: {
35 | bc: { high: 0x00, low: 0x00 },
36 | de: { high: 0x00, low: 0x00 },
37 | hl: { high: 0x00, low: 0x00 },
38 | },
39 | flags, // Randomized flags from fast-check
40 | };
41 |
42 | // Define the expected state of the CPU after execution
43 | const expectedCpuState = {
44 | ...initialCpuState,
45 | flags: {
46 | z: zeroFlag,
47 | s: (result & 0x80) !== 0, // Sign flag based on the most significant bit of the result
48 | p:
49 | (result & 0xff)
50 | .toString(2)
51 | .split("")
52 | .filter((bit) => bit === "1").length %
53 | 2 ===
54 | 0, // Parity flag based on even/odd 1 bits
55 | c: carryFlag, // Carry flag based on the comparison
56 | ac: false, // Auxiliary carry flag is reset
57 | },
58 | programCounter: 0x0003, // PC should increment by 2 after CPI (2-byte instruction)
59 | };
60 |
61 | await runTest(code, initialCpuState, expectedCpuState);
62 | },
63 | ),
64 | { verbose: true, numRuns: 100 }, // Run 100 variations for CPI instruction
65 | );
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/src/tests/lda.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("LDA Instruction Tests", () => {
6 | test("LDA: Loads value from specified memory location into accumulator", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit memory value
10 | fc.integer({ min: 0x0005, max: 0xffff }), // Random 16-bit memory address
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (memoryValue, memoryAddress, flags) => {
19 | const code = `
20 | org 0x0000
21 | lda ${memoryAddress}
22 | hlt
23 | `;
24 |
25 | // Define the initial state of the CPU
26 | const initialCpuState = {
27 | accumulator: 0x00, // Initial accumulator value
28 | memory: {
29 | [memoryAddress]: memoryValue, // Memory contains the value at the specified address
30 | },
31 | registers: {
32 | bc: { high: 0x00, low: 0x00 },
33 | de: { high: 0x00, low: 0x00 },
34 | hl: { high: 0x00, low: 0x00 },
35 | },
36 | flags, // Randomized flags from fast-check
37 | };
38 |
39 | // Define the expected state of the CPU after execution
40 | const expectedCpuState = {
41 | ...initialCpuState,
42 | accumulator: memoryValue, // Accumulator is updated with the value from memory
43 | memory: {
44 | [memoryAddress]: memoryValue,
45 | },
46 | programCounter: 0x0004,
47 | };
48 |
49 | await runTest(code, initialCpuState, expectedCpuState);
50 | },
51 | ),
52 | { verbose: true, numRuns: 100 }, // Run 100 variations for LDA instruction
53 | );
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/src/tests/lhld.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("LHLD Instruction Tests", () => {
6 | test("LHLD: Loads L and H register values from specified memory locations", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x0005, max: 0xfffe }), // Random 16-bit memory address
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit memory value for L
11 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit memory value for H
12 | fc.record({
13 | z: fc.boolean(),
14 | s: fc.boolean(),
15 | p: fc.boolean(),
16 | c: fc.boolean(),
17 | ac: fc.boolean(),
18 | }), // Random initial flag states
19 | async (memoryAddress, lValue, hValue, flags) => {
20 | const code = `
21 | org 0x0000
22 | lhld ${memoryAddress}
23 | hlt
24 | `;
25 |
26 | // Define the initial state of the CPU
27 | const initialCpuState = {
28 | accumulator: 0x00, // Initial accumulator value
29 | memory: {
30 | [memoryAddress]: lValue, // L value stored at specified memory address
31 | [memoryAddress + 1]: hValue, // H value stored at the next memory address
32 | },
33 | registers: {
34 | bc: { high: 0x00, low: 0x00 },
35 | de: { high: 0x00, low: 0x00 },
36 | hl: { high: 0x00, low: 0x00 }, // HL initially zero
37 | },
38 | flags, // Randomized flags from fast-check
39 | };
40 |
41 | // Define the expected state of the CPU after execution
42 | const expectedCpuState = {
43 | ...initialCpuState,
44 | registers: {
45 | ...initialCpuState.registers,
46 | hl: { high: hValue, low: lValue }, // HL registers updated with memory values
47 | },
48 | programCounter: 0x0004, // PC should increment by 3 after LHLD (3-byte instruction)
49 | };
50 |
51 | await runTest(code, initialCpuState, expectedCpuState);
52 | },
53 | ),
54 | { verbose: true, numRuns: 100 }, // Run 100 variations for LHLD instruction
55 | );
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/src/tests/lxi.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("LXI Instruction Tests", () => {
6 | const registerPairs = ["B", "D", "H", "SP"];
7 |
8 | registerPairs.forEach((pair) => {
9 | test(`LXI ${pair}: Load immediate data into ${pair} register pair`, async () => {
10 | await fc.assert(
11 | fc.asyncProperty(
12 | fc.integer({ min: 0x0000, max: 0xffff }), // Random 16-bit immediate data
13 | async (immediateValue) => {
14 | const lowByte = immediateValue & 0xff;
15 | const highByte = (immediateValue >> 8) & 0xff;
16 |
17 | const code = `
18 | org 0x0000
19 | lxi ${pair.toLowerCase()}, 0x${immediateValue.toString(16).padStart(4, "0")}
20 | hlt
21 | `;
22 |
23 | // Define the initial state of the CPU
24 | const initialCpuState = {
25 | accumulator: 0x00,
26 | registers: {
27 | bc: { high: 0x00, low: 0x00 },
28 | de: { high: 0x00, low: 0x00 },
29 | hl: { high: 0x00, low: 0x00 },
30 | },
31 | stackPointer: 0xffff,
32 | flags: {
33 | z: false,
34 | s: false,
35 | p: false,
36 | c: false,
37 | ac: false,
38 | },
39 | programCounter: 0x0000, // Initial PC
40 | };
41 |
42 | // Define the expected state of the CPU after execution
43 | const expectedCpuState = {
44 | ...initialCpuState,
45 | registers: {
46 | ...initialCpuState.registers,
47 | ...(pair === "SP"
48 | ? {}
49 | : {
50 | [registerPairNames[pair.toLowerCase()]]: { high: highByte, low: lowByte },
51 | }),
52 | },
53 | stackPointer: pair === "SP" ? immediateValue : initialCpuState.stackPointer,
54 | programCounter: 0x0004, // PC should increment by 3 after LXI (3-byte instruction)
55 | };
56 |
57 | await runTest(code, initialCpuState, expectedCpuState);
58 | },
59 | ),
60 | { verbose: true, numRuns: 100 }, // Run 100 variations for each register pair
61 | );
62 | });
63 | });
64 | });
65 |
66 | const registerPairNames = {
67 | b: "bc",
68 | d: "de",
69 | h: "hl",
70 | };
--------------------------------------------------------------------------------
/src/tests/mvi.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import { runTest } from './test-utils';
3 |
4 | describe('MVI Instruction Tests', () => {
5 |
6 | // Test 1: MVI A with a specific value
7 | test('MVI A, 5Ah', async () => {
8 | const code = `
9 | MVI A, 5Ah
10 | HLT
11 | `;
12 | const expectedCpuState = {
13 | accumulator: 0x5A, // Verifying the accumulator value
14 | programCounter: 0x0003, // After HLT, PC should increment by 3
15 | };
16 | await runTest(code, {}, expectedCpuState);
17 | });
18 |
19 | // Test 2: MVI B with a specific value
20 | test('MVI B, 1Eh', async () => {
21 | const code = `
22 | MVI B, 1Eh
23 | HLT
24 | `;
25 | const expectedCpuState = {
26 | registers: {
27 | bc: { high: 0x1E } // Verifying the B register (high part of BC)
28 | },
29 | programCounter: 0x0003, // After HLT, PC should increment by 3
30 | };
31 | await runTest(code, {}, expectedCpuState);
32 | });
33 |
34 | // Test 3: MVI C with a specific value
35 | test('MVI C, 5Ah', async () => {
36 | const code = `
37 | MVI C, 5Ah
38 | HLT
39 | `;
40 | const expectedCpuState = {
41 | registers: {
42 | bc: { low: 0x5A } // Verifying the C register (low part of BC)
43 | },
44 | programCounter: 0x0003, // After HLT, PC should increment by 3
45 | };
46 | await runTest(code, {}, expectedCpuState);
47 | });
48 |
49 | // Test 4: MVI D with a specific value
50 | test('MVI D, 20h', async () => {
51 | const code = `
52 | MVI D, 20h
53 | HLT
54 | `;
55 | const expectedCpuState = {
56 | registers: {
57 | de: { high: 0x20 } // Verifying the D register (high part of DE)
58 | },
59 | programCounter: 0x0003, // After HLT, PC should increment by 3
60 | };
61 | await runTest(code, {}, expectedCpuState);
62 | });
63 |
64 | // Test 5: MVI E with a specific value
65 | test('MVI E, 3Ch', async () => {
66 | const code = `
67 | MVI E, 3Ch
68 | HLT
69 | `;
70 | const expectedCpuState = {
71 | registers: {
72 | de: { low: 0x3C } // Verifying the E register (low part of DE)
73 | },
74 | programCounter: 0x0003, // After HLT, PC should increment by 3
75 | };
76 | await runTest(code, {}, expectedCpuState);
77 | });
78 |
79 | // Test 6: MVI H with a specific value
80 | test('MVI H, 4Ah', async () => {
81 | const code = `
82 | MVI H, 4Ah
83 | HLT
84 | `;
85 | const expectedCpuState = {
86 | registers: {
87 | hl: { high: 0x4A } // Verifying the H register (high part of HL)
88 | },
89 | programCounter: 0x0003, // After HLT, PC should increment by 3
90 | };
91 | await runTest(code, {}, expectedCpuState);
92 | });
93 |
94 | // Test 7: MVI L with a specific value
95 | test('MVI L, 2Bh', async () => {
96 | const code = `
97 | MVI L, 2Bh
98 | HLT
99 | `;
100 | const expectedCpuState = {
101 | registers: {
102 | hl: { low: 0x2B } // Verifying the L register (low part of HL)
103 | },
104 | programCounter: 0x0003, // After HLT, PC should increment by 3
105 | };
106 | await runTest(code, {}, expectedCpuState);
107 | });
108 |
109 | });
--------------------------------------------------------------------------------
/src/tests/ori.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("ORI Instruction Tests", () => {
6 | test("ORI: Performs logical OR between accumulator and immediate data, resets carry and auxiliary carry flags", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit accumulator value
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit immediate value
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (accumulator, immediateValue, flags) => {
19 | const result = accumulator | immediateValue;
20 |
21 | // Calculate expected flags
22 | const zeroFlag = result === 0;
23 | const signFlag = (result & 0x80) !== 0;
24 | const parityFlag =
25 | result
26 | .toString(2)
27 | .split("")
28 | .filter((bit) => bit === "1").length %
29 | 2 ===
30 | 0;
31 |
32 | const code = `
33 | org 0x0000
34 | ori ${immediateValue}
35 | hlt
36 | `;
37 |
38 | // Define the initial state of the CPU
39 | const initialCpuState = {
40 | accumulator,
41 | registers: {
42 | bc: { high: 0x00, low: 0x00 },
43 | de: { high: 0x00, low: 0x00 },
44 | hl: { high: 0x00, low: 0x00 },
45 | },
46 | flags, // Randomized flags from fast-check
47 | };
48 |
49 | // Define the expected state of the CPU after execution
50 | const expectedCpuState = {
51 | ...initialCpuState,
52 | accumulator: result, // Result of the OR operation
53 | flags: {
54 | z: zeroFlag,
55 | s: signFlag,
56 | p: parityFlag,
57 | c: false, // Carry flag is always reset to 0 by ORI
58 | ac: false, // Auxiliary carry flag is always reset to 0 by ORI
59 | },
60 | programCounter: 0x0003,
61 | };
62 |
63 | await runTest(code, initialCpuState, expectedCpuState);
64 | },
65 | ),
66 | { verbose: true, numRuns: 100 }, // Run 100 variations for ORI instruction
67 | );
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/src/tests/pchl.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('PCHL Instruction Property-Based Tests', () => {
6 | test('PCHL: Sets PC to the value in HL', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Generate values for H and L registers
10 | fc.integer({ min: 0x01, max: 0xFE }),
11 | fc.integer({ min: 0x01, max: 0xFE }),
12 | async (high, low) => {
13 | const code = `
14 | pchl
15 | org ${(high<<8)|low}
16 | hlt
17 | `;
18 |
19 | const initialCpuState = {
20 | accumulator: 0x00,
21 | registers: {
22 | bc: { high: 0x00, low: 0x00 },
23 | de: { high: 0x00, low: 0x00 },
24 | hl: { high, low }, // Random values for H and L
25 | },
26 | flags: {
27 | z: false,
28 | s: false,
29 | p: false,
30 | c: false,
31 | ac: false,
32 | },
33 | programCounter: 0x0000, // Initial PC is 0
34 | memory: {},
35 | };
36 |
37 | const expectedCpuState = {
38 | ...initialCpuState,
39 | programCounter: ((high << 8) | low) + 1, // PC = (H << 8) | L
40 | };
41 |
42 | await runTest(code, initialCpuState, expectedCpuState);
43 | }
44 | ),
45 | {
46 | verbose: true,
47 | numRuns: 100, // Test with 100 random combinations of H and L
48 | }
49 | );
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/src/tests/ral.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import { runTest } from "./test-utils";
3 |
4 | describe("RAL Instruction Tests", () => {
5 | test("RAL: Rotate accumulator left with carry flag 0", async () => {
6 | const code = `
7 | mvi a, 0aah ; A = 10101010
8 | ral
9 | hlt
10 | `;
11 | const expectedCpuState = {
12 | accumulator: 0x54, // A = 01010100 after rotation
13 | flags: {
14 | z: false,
15 | s: false,
16 | p: false,
17 | c: true, // Carry flag set because of the bit shifted out from A
18 | },
19 | programCounter: 0x0004,
20 | };
21 | await runTest(code, {}, expectedCpuState);
22 | });
23 |
24 | test("RAL: Rotate accumulator left with carry flag 1", async () => {
25 | const code = `
26 | mvi a, 0aah ; A = 10101010
27 | stc ; Set carry flag to 1
28 | ral
29 | hlt
30 | `;
31 | const expectedCpuState = {
32 | accumulator: 0x55, // A = 01010101 after rotation
33 | flags: {
34 | z: false,
35 | s: false,
36 | p: false,
37 | c: true,
38 | },
39 | programCounter: 0x0005,
40 | };
41 | await runTest(code, {}, expectedCpuState);
42 | });
43 | });
--------------------------------------------------------------------------------
/src/tests/rar.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import { runTest } from "./test-utils";
3 |
4 | describe("RAR Instruction Tests", () => {
5 | test("RAR: Rotate accumulator left with carry flag 0", async () => {
6 | const code = `
7 | mvi a, 0aah ; A = 10101010
8 | rar
9 | hlt
10 | `;
11 | const expectedCpuState = {
12 | accumulator: 0x55, // A = 01010101 after rotation
13 | flags: {
14 | z: false,
15 | s: false,
16 | p: false,
17 | c: false,
18 | },
19 | programCounter: 0x0004,
20 | };
21 | await runTest(code, {}, expectedCpuState);
22 | });
23 |
24 | test("RAR: Rotate accumulator left with carry flag 1", async () => {
25 | const code = `
26 | mvi a, 0aah ; A = 10101010
27 | stc ; Set carry flag to 1
28 | rar
29 | hlt
30 | `;
31 | const expectedCpuState = {
32 | accumulator: 0xd5, // A = 11010101 after rotation
33 | flags: {
34 | z: false,
35 | s: false,
36 | p: false,
37 | c: false,
38 | },
39 | programCounter: 0x0005,
40 | };
41 | await runTest(code, {}, expectedCpuState);
42 | });
43 | });
--------------------------------------------------------------------------------
/src/tests/rc.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RC Instruction Property-Based Tests', () => {
6 | test('RC: Pops two bytes and updates program counter only if carry flag is set', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Max has to be 2 less than 0xffff because there cannot be an RC without a call
10 | fc.integer({ min: 0x1000, max: 0xFFFD }), // Stack pointer range (valid memory locations)
11 | fc.integer({ min: 0x0005, max: 0xFFFE }), // Random return address
12 | fc.boolean(), // Random carry flag state
13 | async (stackPointer, returnAddress, carryFlag) => {
14 | const lowByte = returnAddress & 0xFF;
15 | const highByte = (returnAddress >> 8) & 0xFF;
16 |
17 | const code = `
18 | org 0x0000
19 | lxi sp, ${stackPointer}
20 | rc
21 | hlt
22 | org ${returnAddress}
23 | hlt
24 | `;
25 |
26 | const initialCpuState = {
27 | accumulator: 0x00,
28 | registers: {
29 | bc: { high: 0x00, low: 0x00 },
30 | de: { high: 0x00, low: 0x00 },
31 | },
32 | flags: {
33 | z: false,
34 | s: false,
35 | p: false,
36 | c: carryFlag, // Randomized carry flag state
37 | ac: false,
38 | },
39 | programCounter: 0x0000, // Initial PC
40 | memory: {
41 | [stackPointer]: lowByte, // Low byte of return address
42 | [stackPointer + 1]: highByte, // High byte of return address
43 | },
44 | };
45 |
46 | const expectedCpuState = {
47 | ...initialCpuState,
48 | stackPointer: carryFlag ? stackPointer + 2 : stackPointer, // SP incremented only if carry flag is set
49 | programCounter: carryFlag ? returnAddress + 1 : 0x0005, // PC set to return address if carry; otherwise, next instruction
50 | };
51 |
52 | await runTest(code, initialCpuState, expectedCpuState);
53 | }
54 | ),
55 | { verbose: true, numRuns: 100 } // Run 100 variations of stack pointer, return address, and carry flag
56 | );
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/tests/rlc.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RLC Instruction Property-Based Tests', () => {
6 | test('RLC: Rotates accumulator and modifies only carry flag', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Generate an 8-bit value for the accumulator and random flag states
10 | fc.integer({ min: 0x00, max: 0xFF }),
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | ac: fc.boolean(),
16 | }),
17 | async (initialAccumulator, initialFlags) => {
18 | const code = `
19 | rlc
20 | hlt
21 | `;
22 |
23 | // Calculate the expected results
24 | const carryFlag = (initialAccumulator >> 7) & 0x1; // High-order bit becomes the carry flag
25 | const rotatedAccumulator = ((initialAccumulator << 1) | carryFlag) & 0xFF; // Rotate left
26 |
27 | const initialCpuState = {
28 | accumulator: initialAccumulator,
29 | registers: {
30 | bc: { high: 0x00, low: 0x00 },
31 | de: { high: 0x00, low: 0x00 },
32 | hl: { high: 0x00, low: 0x00 },
33 | },
34 | flags: {
35 | ...initialFlags,
36 | c: false, // Carry flag starts undefined
37 | },
38 | programCounter: 0x0000,
39 | memory: {},
40 | };
41 |
42 | const expectedCpuState = {
43 | ...initialCpuState,
44 | accumulator: rotatedAccumulator,
45 | flags: {
46 | ...initialFlags, // Other flags should remain unchanged
47 | c: !!carryFlag, // Carry flag equals the original high-order bit
48 | },
49 | programCounter: 0x0002,
50 | };
51 |
52 | await runTest(code, initialCpuState, expectedCpuState);
53 | }
54 | ),
55 | {
56 | verbose: true,
57 | numRuns: 100, // Test with 100 random accumulator and flag states
58 | }
59 | );
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/src/tests/rnc.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RNC Instruction Property-Based Tests', () => {
6 | test('RNC: Pops two bytes and updates program counter only if carry flag is not set', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Max has to be 2 less than 0xffff because there cannot be an RNC without a call
10 | fc.integer({ min: 0x1000, max: 0xFFFD }), // Stack pointer range (valid memory locations)
11 | fc.integer({ min: 0x0005, max: 0xFFFE }), // Random return address
12 | fc.boolean(), // Random carry flag state
13 | async (stackPointer, returnAddress, carryFlag) => {
14 | const lowByte = returnAddress & 0xFF;
15 | const highByte = (returnAddress >> 8) & 0xFF;
16 |
17 | const code = `
18 | org 0x0000
19 | lxi sp, ${stackPointer}
20 | rnc
21 | hlt
22 | org ${returnAddress}
23 | hlt
24 | `;
25 |
26 | const initialCpuState = {
27 | accumulator: 0x00,
28 | registers: {
29 | bc: { high: 0x00, low: 0x00 },
30 | de: { high: 0x00, low: 0x00 },
31 | },
32 | flags: {
33 | z: false,
34 | s: false,
35 | p: false,
36 | c: carryFlag, // Randomized carry flag state
37 | ac: false,
38 | },
39 | programCounter: 0x0000, // Initial PC
40 | memory: {
41 | [stackPointer]: lowByte, // Low byte of return address
42 | [stackPointer + 1]: highByte, // High byte of return address
43 | },
44 | };
45 |
46 | const expectedCpuState = {
47 | ...initialCpuState,
48 | stackPointer: carryFlag ? stackPointer : stackPointer + 2, // SP incremented only if carry flag is not set
49 | programCounter: carryFlag ? 0x0005 : returnAddress + 1, // PC set to return address if no carry; otherwise, next instruction
50 | };
51 |
52 | await runTest(code, initialCpuState, expectedCpuState);
53 | }
54 | ),
55 | { verbose: true, numRuns: 100 } // Run 100 variations of stack pointer, return address, and carry flag
56 | );
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/tests/rnz.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RNZ Instruction Property-Based Tests', () => {
6 | test('RNZ: Pops two bytes and updates program counter only if zero flag is not set', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Max has to be 2 less than 0xffff because there cannot be an RNZ without a call
10 | fc.integer({ min: 0x1000, max: 0xFFFD }), // Stack pointer range (valid memory locations)
11 | fc.integer({ min: 0x0005, max: 0xFFFE }), // Random return address
12 | fc.boolean(), // Random zero flag state
13 | async (stackPointer, returnAddress, zeroFlag) => {
14 | const lowByte = returnAddress & 0xFF;
15 | const highByte = (returnAddress >> 8) & 0xFF;
16 |
17 | const code = `
18 | org 0x0000
19 | lxi sp, ${stackPointer}
20 | rnz
21 | hlt
22 | org ${returnAddress}
23 | hlt
24 | `;
25 |
26 | const initialCpuState = {
27 | accumulator: 0x00,
28 | registers: {
29 | bc: { high: 0x00, low: 0x00 },
30 | de: { high: 0x00, low: 0x00 },
31 | },
32 | flags: {
33 | z: zeroFlag, // Randomized zero flag state
34 | s: false,
35 | p: false,
36 | c: false,
37 | ac: false,
38 | },
39 | programCounter: 0x0000, // Initial PC
40 | memory: {
41 | [stackPointer]: lowByte, // Low byte of return address
42 | [stackPointer + 1]: highByte, // High byte of return address
43 | },
44 | };
45 |
46 | const expectedCpuState = {
47 | ...initialCpuState,
48 | stackPointer: zeroFlag ? stackPointer : stackPointer + 2, // SP incremented only if zero flag is not set
49 | programCounter: zeroFlag ? 0x0005 : returnAddress + 1, // PC set to return address if not zero; otherwise, next instruction
50 | };
51 |
52 | await runTest(code, initialCpuState, expectedCpuState);
53 | }
54 | ),
55 | { verbose: true, numRuns: 100 } // Run 100 variations of stack pointer, return address, and zero flag
56 | );
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/tests/rp.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RP Instruction Property-Based Tests', () => {
6 | test('RP: Pops two bytes and updates program counter only if sign flag is not set', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Max has to be 2 less than 0xffff because there cannot be an RP without a call
10 | fc.integer({ min: 0x1000, max: 0xFFFD }), // Stack pointer range (valid memory locations)
11 | fc.integer({ min: 0x0005, max: 0xFFFE }), // Random return address
12 | fc.boolean(), // Random sign flag state
13 | async (stackPointer, returnAddress, signFlag) => {
14 | const lowByte = returnAddress & 0xFF;
15 | const highByte = (returnAddress >> 8) & 0xFF;
16 |
17 | const code = `
18 | org 0x0000
19 | lxi sp, ${stackPointer}
20 | rp
21 | hlt
22 | org ${returnAddress}
23 | hlt
24 | `;
25 |
26 | const initialCpuState = {
27 | accumulator: 0x00,
28 | registers: {
29 | bc: { high: 0x00, low: 0x00 },
30 | de: { high: 0x00, low: 0x00 },
31 | },
32 | flags: {
33 | z: false,
34 | s: signFlag, // Randomized sign flag state
35 | p: false,
36 | c: false,
37 | ac: false,
38 | },
39 | programCounter: 0x0000, // Initial PC
40 | memory: {
41 | [stackPointer]: lowByte, // Low byte of return address
42 | [stackPointer + 1]: highByte, // High byte of return address
43 | },
44 | };
45 |
46 | const expectedCpuState = {
47 | ...initialCpuState,
48 | stackPointer: signFlag ? stackPointer : stackPointer + 2, // SP incremented only if sign flag is not set
49 | programCounter: signFlag ? 0x0005 : returnAddress + 1, // PC set to return address if positive; otherwise, next instruction
50 | };
51 |
52 | await runTest(code, initialCpuState, expectedCpuState);
53 | }
54 | ),
55 | { verbose: true, numRuns: 100 } // Run 100 variations of stack pointer, return address, and sign flag
56 | );
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/tests/rrc.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RRC Instruction Property-Based Tests', () => {
6 | test('RRC: Rotates the accumulator right and sets the carry flag', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xFF }), // Generate 8-bit accumulator values
10 | async (initialAccumulator) => {
11 | const code = `
12 | rrc
13 | hlt
14 | `;
15 |
16 | // Calculate the expected results
17 | const carryFlag = initialAccumulator & 0x1; // Low-order bit becomes the carry flag
18 | const rotatedAccumulator = ((initialAccumulator >> 1) | (initialAccumulator << 7)) & 0xFF; // Rotate right
19 |
20 | const initialCpuState = {
21 | accumulator: initialAccumulator,
22 | registers: {
23 | bc: { high: 0x00, low: 0x00 },
24 | de: { high: 0x00, low: 0x00 },
25 | hl: { high: 0x00, low: 0x00 },
26 | },
27 | flags: {
28 | z: false,
29 | s: false,
30 | p: false,
31 | c: false, // Carry flag starts undefined
32 | ac: false,
33 | },
34 | programCounter: 0x0000,
35 | memory: {},
36 | };
37 |
38 | const expectedCpuState = {
39 | ...initialCpuState,
40 | accumulator: rotatedAccumulator,
41 | flags: {
42 | ...initialCpuState.flags,
43 | c: !!carryFlag, // Carry flag equals the original low-order bit
44 | },
45 | programCounter: 0x0002, // Program counter increments by 1
46 | };
47 |
48 | await runTest(code, initialCpuState, expectedCpuState);
49 | }
50 | ),
51 | { verbose: true, numRuns: 100 } // Test with 100 random accumulator values
52 | );
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/src/tests/rz.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('RZ Instruction Property-Based Tests', () => {
6 | test('RZ: Pops two bytes and updates program counter only if zero flag is set', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Max has to be 2 less than 0xffff because there cannot be an RZ without a call
10 | fc.integer({ min: 0x1000, max: 0xFFFD }), // Stack pointer range (valid memory locations)
11 | fc.integer({ min: 0x0005, max: 0xFFFE }), // Random return address
12 | fc.boolean(), // Random zero flag state
13 | async (stackPointer, returnAddress, zeroFlag) => {
14 | const lowByte = returnAddress & 0xFF;
15 | const highByte = (returnAddress >> 8) & 0xFF;
16 |
17 | const code = `
18 | org 0x0000
19 | lxi sp, ${stackPointer}
20 | rz
21 | hlt
22 | org ${returnAddress}
23 | hlt
24 | `;
25 |
26 | const initialCpuState = {
27 | accumulator: 0x00,
28 | registers: {
29 | bc: { high: 0x00, low: 0x00 },
30 | de: { high: 0x00, low: 0x00 },
31 | },
32 | flags: {
33 | z: zeroFlag, // Randomized zero flag state
34 | s: false,
35 | p: false,
36 | c: false,
37 | ac: false,
38 | },
39 | programCounter: 0x0000, // Initial PC
40 | memory: {
41 | [stackPointer]: lowByte, // Low byte of return address
42 | [stackPointer + 1]: highByte, // High byte of return address
43 | },
44 | };
45 |
46 | const expectedCpuState = {
47 | ...initialCpuState,
48 | stackPointer: zeroFlag ? stackPointer + 2 : stackPointer, // SP incremented only if zero flag is set
49 | programCounter: zeroFlag ? returnAddress + 1 : 0x0005, // PC set to return address if zero; otherwise, next instruction
50 | };
51 |
52 | await runTest(code, initialCpuState, expectedCpuState);
53 | }
54 | ),
55 | { verbose: true, numRuns: 100 } // Run 100 variations of stack pointer, return address, and zero flag
56 | );
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/tests/sbb.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import { runTest } from './test-utils';
3 |
4 | describe('SBB Instruction Tests', () => {
5 |
6 | // Test 1: SBB with carry, A = 4, B = 2, carry set
7 | test('SBB: Subtract with Carry', async () => {
8 | const code = `
9 | MVI A, 4h ; Load 4 into the accumulator (A)
10 | MVI B, 2h ; Load 2 into register B
11 | STC ; Set carry flag
12 | SBB B ; Subtract B and carry from A (A = A - B - CY)
13 | HLT
14 | `;
15 | const expectedCpuState = {
16 | accumulator: 0x01, // Result: 4 - 2 - 1 (carry) = 1
17 | flags: {
18 | z: false,
19 | s: false,
20 | p: false,
21 | c: false,
22 | ac: true
23 | },
24 | programCounter: 0x0007 // After HLT, PC should increment by 7
25 | };
26 | await runTest(code, {}, expectedCpuState);
27 | });
28 | });
--------------------------------------------------------------------------------
/src/tests/shld.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("SHLD Instruction Tests", () => {
6 | test("SHLD: Stores L and H register values into specified memory locations", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x0005, max: 0xfffe }), // Random 16-bit memory address
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit L register value
11 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit H register value
12 | fc.record({
13 | z: fc.boolean(),
14 | s: fc.boolean(),
15 | p: fc.boolean(),
16 | c: fc.boolean(),
17 | ac: fc.boolean(),
18 | }), // Random initial flag states
19 | async (memoryAddress, lValue, hValue, flags) => {
20 | const code = `
21 | org 0x0000
22 | shld ${memoryAddress}
23 | hlt
24 | `;
25 |
26 | // Define the initial state of the CPU
27 | const initialCpuState = {
28 | accumulator: 0x00, // Initial accumulator value
29 | memory: {}, // Empty memory
30 | registers: {
31 | bc: { high: 0x00, low: 0x00 },
32 | de: { high: 0x00, low: 0x00 },
33 | hl: { high: hValue, low: lValue }, // HL register pair values
34 | },
35 | flags, // Randomized flags from fast-check
36 | };
37 |
38 | // Define the expected state of the CPU after execution
39 | const expectedCpuState = {
40 | ...initialCpuState,
41 | memory: {
42 | ...initialCpuState.memory,
43 | [memoryAddress]: lValue, // L register value at specified memory address
44 | [memoryAddress + 1]: hValue, // H register value at next memory address
45 | },
46 | programCounter: 0x0004, // PC should increment by 3 after SHLD (3-byte instruction)
47 | };
48 |
49 | await runTest(code, initialCpuState, expectedCpuState);
50 | },
51 | ),
52 | { verbose: true, numRuns: 100 }, // Run 100 variations for SHLD instruction
53 | );
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/src/tests/sphl.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('SPHL Instruction Property-Based Tests', () => {
6 | test('SPHL: Sets SP to the value in HL', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Generate values for H and L registers
10 | fc.integer({ min: 0x00, max: 0xFF }),
11 | fc.integer({ min: 0x00, max: 0xFF }),
12 | async (high, low) => {
13 | const code = `
14 | sphl
15 | hlt
16 | `;
17 |
18 | const initialCpuState = {
19 | accumulator: 0x00,
20 | registers: {
21 | bc: { high: 0x00, low: 0x00 },
22 | de: { high: 0x00, low: 0x00 },
23 | hl: { high, low }, // Random values for H and L
24 | },
25 | flags: {
26 | z: false,
27 | s: false,
28 | p: false,
29 | c: false,
30 | ac: false,
31 | },
32 | stackPointer: 0x0000, // Initial SP is 0
33 | programCounter: 0x0000,
34 | memory: {},
35 | };
36 |
37 | const expectedCpuState = {
38 | ...initialCpuState,
39 | stackPointer: (high << 8) | low, // SP = (H << 8) | L
40 | programCounter: 0x0002,
41 | };
42 |
43 | await runTest(code, initialCpuState, expectedCpuState);
44 | }
45 | ),
46 | {
47 | verbose: true,
48 | numRuns: 100, // Test with 100 random combinations of H and L
49 | }
50 | );
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/src/tests/sta.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("STA Instruction Tests", () => {
6 | test("STA: Stores accumulator value in specified memory location", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit accumulator value
10 | fc.integer({ min: 0x0005, max: 0xffff }), // Random 16-bit memory address
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (accumulator, memoryAddress, flags) => {
19 | const code = `
20 | org 0x0000
21 | sta ${memoryAddress}
22 | hlt
23 | `;
24 |
25 | // Define the initial state of the CPU
26 | const initialCpuState = {
27 | accumulator,
28 | memory: {}, // Empty memory
29 | registers: {
30 | bc: { high: 0x00, low: 0x00 },
31 | de: { high: 0x00, low: 0x00 },
32 | hl: { high: 0x00, low: 0x00 },
33 | },
34 | flags, // Randomized flags from fast-check
35 | };
36 |
37 | // Define the expected state of the CPU after execution
38 | const expectedCpuState = {
39 | ...initialCpuState,
40 | memory: {
41 | ...initialCpuState.memory,
42 | [memoryAddress]: accumulator, // Memory at the address is updated with the accumulator value
43 | },
44 | programCounter: 0x0004, // PC should increment by 3 after STA (3-byte instruction)
45 | };
46 |
47 | await runTest(code, initialCpuState, expectedCpuState);
48 | },
49 | ),
50 | { verbose: true, numRuns: 100 }, // Run 100 variations for STA instruction
51 | );
52 | });
53 | });
--------------------------------------------------------------------------------
/src/tests/stax.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import { runTest } from "./test-utils";
3 |
4 | describe("STAX Instruction Tests", () => {
5 | test("STAX B: Stores value from A register into memory using BC pair", async () => {
6 | const code = `
7 | mvi a, 9h
8 | mvi b, 3fh
9 | mvi c, 16h
10 | stax b
11 | hlt
12 | `;
13 | const expectedCpuState = {
14 | accumulator: 0x09,
15 | flags: {
16 | z: false,
17 | s: false,
18 | p: false,
19 | c: false,
20 | ac: false,
21 | },
22 | programCounter: 0x0008,
23 | memory: {
24 | 0x3f16: 0x09,
25 | },
26 | };
27 | await runTest(code, {}, expectedCpuState);
28 | });
29 |
30 | test("STAX D: Stores value from A register into memory using DE pair", async () => {
31 | const code = `
32 | mvi a, 12h
33 | mvi d, 4fh
34 | mvi e, 22h
35 | stax d
36 | hlt
37 | `;
38 | const expectedCpuState = {
39 | accumulator: 0x12,
40 | flags: {
41 | z: false,
42 | s: false,
43 | p: false,
44 | c: false,
45 | ac: false,
46 | },
47 | programCounter: 0x0008,
48 | memory: {
49 | 0x4f22: 0x12,
50 | },
51 | };
52 | await runTest(code, {}, expectedCpuState);
53 | });
54 | });
--------------------------------------------------------------------------------
/src/tests/stc.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import { runTest } from './test-utils';
3 |
4 | describe('STC Instruction Tests', () => {
5 | test('STC: Sets carry flag without modifying other flags or registers', async () => {
6 | const code = `
7 | stc
8 | hlt
9 | `;
10 |
11 | const initialCpuState = {
12 | flags: {
13 | z: false,
14 | s: false,
15 | p: false,
16 | c: false,
17 | ac: false,
18 | },
19 | };
20 |
21 | const expectedCpuState = {
22 | flags: {
23 | ...initialCpuState.flags,
24 | c: true, // Carry flag should be set to 1
25 | },
26 | programCounter: 0x0002,
27 | };
28 |
29 | await runTest(code, initialCpuState, expectedCpuState);
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/src/tests/sui.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("SUI Instruction Tests", () => {
6 | test("SUI: Subtracts immediate data from accumulator and updates flags", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit accumulator value
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit immediate value
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (accumulator, immediateValue, flags) => {
19 | const result = accumulator - immediateValue;
20 |
21 | // Calculate expected flags
22 | const zeroFlag = (result & 0xff) === 0;
23 | const signFlag = (result & 0x80) !== 0;
24 | const parityFlag =
25 | (result & 0xff)
26 | .toString(2)
27 | .split("")
28 | .filter((bit) => bit === "1").length %
29 | 2 ===
30 | 0;
31 | const carryFlag = accumulator < immediateValue;
32 | const auxCarryFlag = (accumulator & 0x0f) + ((~immediateValue + 1) & 0x0f) > 0x0f;
33 |
34 | const code = `
35 | org 0x0000
36 | sui ${immediateValue}
37 | hlt
38 | `;
39 |
40 | // Define the initial state of the CPU
41 | const initialCpuState = {
42 | accumulator,
43 | registers: {
44 | bc: { high: 0x00, low: 0x00 },
45 | de: { high: 0x00, low: 0x00 },
46 | hl: { high: 0x00, low: 0x00 },
47 | },
48 | flags, // Randomized flags from fast-check
49 | };
50 |
51 | // Define the expected state of the CPU after execution
52 | const expectedCpuState = {
53 | ...initialCpuState,
54 | accumulator: result & 0xff, // Result masked to 8 bits
55 | flags: {
56 | z: zeroFlag,
57 | s: signFlag,
58 | p: parityFlag,
59 | c: carryFlag, // Carry flag indicates borrow
60 | ac: auxCarryFlag, // Auxiliary carry flag indicates lower nibble borrow
61 | },
62 | programCounter: 0x0003, // PC should increment by 2 after SUI (2-byte instruction)
63 | };
64 |
65 | await runTest(code, initialCpuState, expectedCpuState);
66 | },
67 | ),
68 | { verbose: true, numRuns: 100 }, // Run 100 variations for SUI instruction
69 | );
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/src/tests/xchg.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('XCHG Instruction Property-Based Tests', () => {
6 | test('XCHG: Exchanges contents of H/L with D/E without modifying flags', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Generate 8-bit values for H, L, D, and E
10 | fc.integer({ min: 0x00, max: 0xFF }), // High byte of HL
11 | fc.integer({ min: 0x00, max: 0xFF }), // Low byte of HL
12 | fc.integer({ min: 0x00, max: 0xFF }), // High byte of DE
13 | fc.integer({ min: 0x00, max: 0xFF }), // Low byte of DE
14 | fc.record({
15 | z: fc.boolean(),
16 | s: fc.boolean(),
17 | p: fc.boolean(),
18 | c: fc.boolean(),
19 | ac: fc.boolean(),
20 | }), // Randomized initial flags
21 | async (h, l, d, e, initialFlags) => {
22 | const code = `
23 | xchg
24 | hlt
25 | `;
26 |
27 | const initialCpuState = {
28 | accumulator: 0x00,
29 | registers: {
30 | bc: { high: 0x00, low: 0x00 },
31 | de: { high: d, low: e },
32 | hl: { high: h, low: l },
33 | },
34 | flags: initialFlags, // Randomized initial flags
35 | programCounter: 0x0000,
36 | memory: {},
37 | };
38 |
39 | const expectedCpuState = {
40 | ...initialCpuState,
41 | registers: {
42 | ...initialCpuState.registers,
43 | de: { high: h, low: l }, // D = H, E = L
44 | hl: { high: d, low: e }, // H = D, L = E
45 | },
46 | flags: initialFlags, // Flags should remain unchanged
47 | programCounter: 0x0002, // PC advances to the next instruction
48 | };
49 |
50 | await runTest(code, initialCpuState, expectedCpuState);
51 | }
52 | ),
53 | { verbose: true, numRuns: 100 }
54 | );
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/src/tests/xri.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from "vitest";
2 | import * as fc from "fast-check";
3 | import { runTest } from "./test-utils";
4 |
5 | describe("XRI Instruction Tests", () => {
6 | test("XRI: Performs logical XOR between accumulator and immediate data, resets carry and auxiliary carry flags", async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit accumulator value
10 | fc.integer({ min: 0x00, max: 0xff }), // Random 8-bit immediate value
11 | fc.record({
12 | z: fc.boolean(),
13 | s: fc.boolean(),
14 | p: fc.boolean(),
15 | c: fc.boolean(),
16 | ac: fc.boolean(),
17 | }), // Random initial flag states
18 | async (accumulator, immediateValue, flags) => {
19 | const result = accumulator ^ immediateValue;
20 |
21 | // Calculate expected flags
22 | const zeroFlag = result === 0;
23 | const signFlag = (result & 0x80) !== 0;
24 | const parityFlag =
25 | result
26 | .toString(2)
27 | .split("")
28 | .filter((bit) => bit === "1").length %
29 | 2 ===
30 | 0;
31 |
32 | const code = `
33 | org 0x0000
34 | xri ${immediateValue}
35 | hlt
36 | `;
37 |
38 | // Define the initial state of the CPU
39 | const initialCpuState = {
40 | accumulator,
41 | registers: {
42 | bc: { high: 0x00, low: 0x00 },
43 | de: { high: 0x00, low: 0x00 },
44 | hl: { high: 0x00, low: 0x00 },
45 | },
46 | flags, // Randomized flags from fast-check
47 | };
48 |
49 | // Define the expected state of the CPU after execution
50 | const expectedCpuState = {
51 | ...initialCpuState,
52 | accumulator: result, // Result of the XOR operation
53 | flags: {
54 | z: zeroFlag,
55 | s: signFlag,
56 | p: parityFlag,
57 | c: false, // Carry flag is always reset to 0 by XRI
58 | ac: false, // Auxiliary carry flag is always reset to 0 by XRI
59 | },
60 | programCounter: 0x0003,
61 | };
62 |
63 | await runTest(code, initialCpuState, expectedCpuState);
64 | },
65 | ),
66 | { verbose: true, numRuns: 100 }, // Run 100 variations for XRI instruction
67 | );
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/src/tests/xthl.test.js:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import * as fc from 'fast-check';
3 | import { runTest } from './test-utils';
4 |
5 | describe('XTHL Instruction Property-Based Tests', () => {
6 | test('XTHL: Exchanges contents of H/L with top of stack without modifying flags', async () => {
7 | await fc.assert(
8 | fc.asyncProperty(
9 | // Generate 8-bit values for H, L, SP contents, and flags
10 | fc.integer({ min: 0x00, max: 0xFF }), // High byte of HL (H)
11 | fc.integer({ min: 0x00, max: 0xFF }), // Low byte of HL (L)
12 | fc.integer({ min: 0x00, max: 0xFF }), // Top of stack (SP)
13 | fc.integer({ min: 0x00, max: 0xFF }), // Top of stack + 1 (SP + 1)
14 | fc.record({
15 | z: fc.boolean(),
16 | s: fc.boolean(),
17 | p: fc.boolean(),
18 | c: fc.boolean(),
19 | ac: fc.boolean(),
20 | }), // Randomized initial flags
21 | fc.integer({ min: 0x1000, max: 0xFFFF - 1 }), // Stack pointer (SP)
22 | async (h, l, spLow, spHigh, initialFlags, stackPointer) => {
23 | const code = `
24 | lxi sp, ${stackPointer}
25 | xthl
26 | hlt
27 | `;
28 |
29 | const initialCpuState = {
30 | accumulator: 0x00,
31 | registers: {
32 | bc: { high: 0x00, low: 0x00 },
33 | de: { high: 0x00, low: 0x00 },
34 | hl: { high: h, low: l }, // HL register contents
35 | },
36 | flags: initialFlags, // Randomized initial flags
37 | stackPointer,
38 | programCounter: 0x0000,
39 | memory: {
40 | [stackPointer]: spLow, // Top of stack (low byte)
41 | [stackPointer + 1]: spHigh, // Top of stack + 1 (high byte)
42 | },
43 | };
44 |
45 | const expectedCpuState = {
46 | ...initialCpuState,
47 | registers: {
48 | ...initialCpuState.registers,
49 | hl: { high: spHigh, low: spLow }, // HL gets the stack's top values
50 | },
51 | memory: {
52 | ...initialCpuState.memory,
53 | [stackPointer]: l, // Stack top gets L (low byte of HL)
54 | [stackPointer + 1]: h, // Stack top + 1 gets H (high byte of HL)
55 | },
56 | flags: initialFlags, // Flags should remain unchanged
57 | programCounter: 0x0005, // PC advances to the next instruction
58 | };
59 |
60 | await runTest(code, initialCpuState, expectedCpuState);
61 | }
62 | ),
63 | { verbose: true, numRuns: 100 }
64 | );
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/src/themes/dark.css:
--------------------------------------------------------------------------------
1 | html[data-theme="dark"] {
2 | --sm-primary-50: 238 251 243;
3 | --sm-primary-100: 214 245 225;
4 | --sm-primary-200: 177 233 200;
5 | --sm-primary-300: 126 215 169;
6 | --sm-primary-400: 73 190 132;
7 | --sm-primary-500: 38 162 105;
8 | --sm-primary-600: 24 131 84;
9 | --sm-primary-700: 19 105 69;
10 | --sm-primary-800: 18 83 57;
11 | --sm-primary-900: 15 69 48;
12 | --sm-primary-950: 8 38 27;
13 |
14 | --sm-primary: var(--sm-primary-600);
15 |
16 | --sm-gray-50: 248 248 248; /* #f8f8f8 */
17 | --sm-gray-100: 240 240 240; /* # */
18 | --sm-gray-200: 220 220 220; /* #dcdcdc */
19 | --sm-gray-300: 240 240 240; /* # */
20 | --sm-gray-400: 115 115 115; /* #989898 */
21 | --sm-gray-500: 125 126 130; /* #7c7c7c */
22 | --sm-gray-600: 101 101 101; /* #656565 */
23 | --sm-gray-700: 82 82 82; /* #525252 */
24 | --sm-gray-800: 69 74 84; /* #464646 */
25 | --sm-gray-900: 32 35 39; /* #3d3d3d */
26 | --sm-gray-950: 21 24 30; /* #151814 */
27 |
28 | --sm-page-background: 46 49 56; /* #2e3138 */
29 | --sm-main-background: var(--sm-gray-950);
30 | --sm-secondary-background: var(--sm-gray-900);
31 | --sm-main-border: var(--sm-gray-800);
32 |
33 | --sm-primary-foreground: var(--sm-gray-100);
34 | --sm-primary-border: var(--sm-gray-600);
35 |
36 | --sm-secondary-foreground: var(--sm-gray-500);
37 | --sm-secondary-border: var(--sm-gray-600);
38 |
39 | --sm-active-foreground: var(--sm-gray-100);
40 | --sm-active-background: var(--sm-gray-800);
41 | --sm-active-border: var(--sm-gray-700);
42 |
43 | --sm-inactive-foreground: var(--sm-gray-400);
44 | --sm-inactive-border: var(--sm-gray-800);
45 |
46 | --sm-red-foreground: 248 113 113; /* #*/
47 | --sm-green-foreground: 21 128 61; /* #15803d */
48 | --sm-yellow-foreground: 234 179 8; /* #eab308 */
49 | --sm-blue-foreground: 96 165 250; /* #60a5fa */
50 | /* --sm-orange-foreground: 251 146 60; /* #fb923c */
51 | --sm-orange-foreground: 253 186 116; /* #fdba74 */
52 |
53 | /* Editor colors */
54 | --sm-editor-active-line: 23 42 81; /* #1f2937 */
55 | --sm-editor-debug-line: 37 99 235; /* #2563eb */
56 | --sm-editor-active-line-gutter: 255 255 255;
57 | --sm-editor-cursor: 209 213 219; /* #d1d5db */
58 | --sm-editor-selection-background: 30 58 138; /* #1e3a8a */
59 |
60 | --sm-editor-opcode: 74 222 128;
61 | --sm-editor-directive: 96 165 250;
62 | --sm-editor-label: 250 204 21;
63 | --sm-editor-comment: 107 114 128;
64 | --sm-editor-register: 248 113 113;
65 | --sm-editor-number: 251 146 60;
66 | --sm-editor-string: 234 88 12;
67 | --sm-editor-operator: 244 114 182;
68 | --sm-editor-punctuation: 75 85 99;
69 | }
70 |
--------------------------------------------------------------------------------
/src/themes/light.css:
--------------------------------------------------------------------------------
1 | html[data-theme="light"] {
2 | --sm-primary-50: 238 251 243;
3 | --sm-primary-100: 214 245 225;
4 | --sm-primary-200: 177 233 200;
5 | --sm-primary-300: 126 215 169;
6 | --sm-primary-400: 73 190 132;
7 | --sm-primary-500: 38 162 105;
8 | --sm-primary-600: 24 131 84;
9 | --sm-primary-700: 19 105 69;
10 | --sm-primary-800: 18 83 57;
11 | --sm-primary-900: 15 69 48;
12 | --sm-primary-950: 8 38 27;
13 |
14 | --sm-primary: var(--sm-primary-500);
15 |
16 | --sm-gray-50: 251 251 251; /* #fbfbfb */
17 | --sm-gray-100: 239 239 239; /* #efefef */
18 | --sm-gray-200: 220 220 220; /* #dcdcdc */
19 | --sm-gray-300: 189 189 189; /* #bdbdbd */
20 | --sm-gray-400: 152 152 152; /* #989898 */
21 | --sm-gray-500: 124 124 124; /* #7c7c7c */
22 | --sm-gray-600: 101 101 101; /* #656565 */
23 | --sm-gray-700: 82 82 82; /* #525252 */
24 | --sm-gray-800: 70 70 70; /* #464646 */
25 | --sm-gray-900: 61 61 61; /* #3d3d3d */
26 | --sm-gray-950: 41 41 41; /* #292929 */
27 |
28 | --sm-page-background: 232 232 232; /* #e8e8e8 */
29 | --sm-main-background: var(--sm-gray-50);
30 | --sm-secondary-background: var(--sm-gray-100);
31 | --sm-main-border: var(--sm-gray-300);
32 |
33 | --sm-primary-foreground: var(--sm-gray-700);
34 | --sm-primary-border: var(--sm-gray-600);
35 |
36 | --sm-secondary-foreground: var(--sm-gray-500);
37 | --sm-secondary-border: var(--sm-gray-600);
38 |
39 | --sm-active-foreground: var(--sm-gray-700);
40 | --sm-active-background: var(--sm-gray-200);
41 | --sm-active-border: var(--sm-gray-300);
42 |
43 | --sm-inactive-foreground: var(--sm-gray-400);
44 | --sm-inactive-border: var(--sm-gray-200);
45 |
46 | --sm-red-foreground: 185 28 28; /* #b91c1c */
47 | --sm-green-foreground: 21 128 61; /* #15803d */
48 | --sm-yellow-foreground: 234 179 8; /* #eab308 */
49 | --sm-blue-foreground: 96 165 250; /* #60a5fa */
50 | --sm-orange-foreground: 234 88 12; /* #ea580c */
51 |
52 | /* Editor colors */
53 | --sm-editor-active-line: 191 219 254; /* #bfdbfe */
54 | --sm-editor-debug-line: 96 165 250; /* #60a5fa */
55 | --sm-editor-active-line-gutter: 255 255 255;
56 | --sm-editor-cursor: 75 85 99; /* #4b5563 */
57 | --sm-editor-selection-background: 191 219 254; /* #bfdbfe */
58 |
59 | --sm-editor-opcode: 28 128 61;
60 | --sm-editor-directive: 29 78 216;
61 | --sm-editor-label: 168 91 7;
62 | --sm-editor-comment: 107 114 128;
63 | --sm-editor-register: 185 28 28;
64 | --sm-editor-number: 194 65 12;
65 | --sm-editor-string: 234 88 12;
66 | --sm-editor-operator: 190 24 93;
67 | --sm-editor-punctuation: 75 85 99;
68 | }
69 |
--------------------------------------------------------------------------------
/src/utils/NumberFormat.js:
--------------------------------------------------------------------------------
1 | export function toByteString(n) {
2 | if (n == null) return "";
3 |
4 | if (n < 16) return `0${toRadix(16, n)}`;
5 | else return toRadix(16, n);
6 | }
7 |
8 | export function toRadix(r, n) {
9 | if (n == null || r == null) return "";
10 |
11 | function getChr(c) {
12 | return c < 10 ? c.toString() : String.fromCharCode(87 + c);
13 | }
14 |
15 | function getStr(b) {
16 | return n < b ? getChr(n) : toRadix(r, Math.floor(n / b)) + getChr(n % b);
17 | }
18 |
19 | if (r >= 2 && r <= 16) {
20 | return getStr(r).toUpperCase();
21 | } else {
22 | return n.toString().toUpperCase();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/deepMerge.js:
--------------------------------------------------------------------------------
1 | function isObject(item) {
2 | return (item && typeof item === 'object' && !Array.isArray(item));
3 | }
4 |
5 | /**
6 | * Deep merge two objects.
7 | * @param target
8 | * @param ...sources
9 | */
10 | export function deepMerge(target, ...sources) {
11 | if (!sources.length) return target;
12 | const source = sources.shift();
13 |
14 | if (isObject(target) && isObject(source)) {
15 | for (const key in source) {
16 | if (isObject(source[key])) {
17 | if (!target[key]) Object.assign(target, { [key]: {} });
18 | deepMerge(target[key], source[key]);
19 | } else {
20 | Object.assign(target, { [key]: source[key] });
21 | }
22 | }
23 | }
24 |
25 | return deepMerge(target, ...sources);
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/lens.js:
--------------------------------------------------------------------------------
1 | export function createLens(path) {
2 | return {
3 | get: (obj) => path.reduce((acc, key) => acc[key], obj), // Dynamically traverse the path for the getter
4 | set: (obj, value) => {
5 | // Traverse to the second-last key
6 | const lastKey = path.pop();
7 | const target = path.reduce((acc, key) => acc[key], obj);
8 | target[lastKey] = value; // Set the value at the last key
9 | }
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/tailwind.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | darkMode: ["class", '[data-theme="dark"]'],
4 | };
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/base",
3 | "compilerOptions": {
4 | "jsx": "preserve",
5 | "jsxImportSource": "solid-js",
6 | "strictNullChecks": true,
7 | "allowJs": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/vitest.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config'
2 | import peggy from 'vite-plugin-peggy-loader';
3 |
4 | export default defineConfig({
5 | plugins: [peggy()],
6 | test: {},
7 | })
8 |
--------------------------------------------------------------------------------