step 1 to 5
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
out
|
||||
dist
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
*.s
|
||||
5
.vscode-test.mjs
Normal file
5
.vscode-test.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from '@vscode/test-cli';
|
||||
|
||||
export default defineConfig({
|
||||
files: 'out/test/**/*.test.js',
|
||||
});
|
||||
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"ms-vscode.extension-test-runner"
|
||||
]
|
||||
}
|
||||
21
.vscode/launch.json
vendored
Normal file
21
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
20
.vscode/tasks.json
vendored
Normal file
20
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
11
.vscodeignore
Normal file
11
.vscodeignore
Normal file
@@ -0,0 +1,11 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/eslint.config.mjs
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/.vscode-test.*
|
||||
9
CHANGELOG.md
Normal file
9
CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "riscv-asm" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
||||
71
README.md
Normal file
71
README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# riscv-asm README
|
||||
|
||||
This is the README for your extension "riscv-asm". After writing up a brief description, we recommend including the following sections.
|
||||
|
||||
## Features
|
||||
|
||||
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
|
||||
|
||||
For example if there is an image subfolder under your extension project workspace:
|
||||
|
||||
\!\[feature X\]\(images/feature-x.png\)
|
||||
|
||||
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
|
||||
|
||||
## Requirements
|
||||
|
||||
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
|
||||
|
||||
## Extension Settings
|
||||
|
||||
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
|
||||
|
||||
For example:
|
||||
|
||||
This extension contributes the following settings:
|
||||
|
||||
* `myExtension.enable`: Enable/disable this extension.
|
||||
* `myExtension.thing`: Set to `blah` to do something.
|
||||
|
||||
## Known Issues
|
||||
|
||||
Calling out known issues can help limit users opening duplicate issues against your extension.
|
||||
|
||||
## Release Notes
|
||||
|
||||
Users appreciate release notes as you update your extension.
|
||||
|
||||
### 1.0.0
|
||||
|
||||
Initial release of ...
|
||||
|
||||
### 1.0.1
|
||||
|
||||
Fixed issue #.
|
||||
|
||||
### 1.1.0
|
||||
|
||||
Added features X, Y, and Z.
|
||||
|
||||
---
|
||||
|
||||
## Following extension guidelines
|
||||
|
||||
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
|
||||
|
||||
* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
|
||||
|
||||
## Working with Markdown
|
||||
|
||||
You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
|
||||
|
||||
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux).
|
||||
* Toggle preview (`Shift+Cmd+V` on macOS or `Shift+Ctrl+V` on Windows and Linux).
|
||||
* Press `Ctrl+Space` (Windows, Linux, macOS) to see a list of Markdown snippets.
|
||||
|
||||
## For more information
|
||||
|
||||
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
|
||||
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
|
||||
|
||||
**Enjoy!**
|
||||
98
TODO
Normal file
98
TODO
Normal file
@@ -0,0 +1,98 @@
|
||||
# TODO - Extension VSCode RISC-V
|
||||
|
||||
## Tâches terminées
|
||||
|
||||
### Étape 1: Coloration Syntaxique
|
||||
- [x] Configuration du langage RISC-V (.s, .asm, .rv)
|
||||
- [x] Grammaire TextMate pour coloration syntaxique
|
||||
- [x] Support des instructions, registres, directives, commentaires
|
||||
- [x] Support des nombres (décimal, hexadécimal, binaire)
|
||||
- [x] Configuration des brackets et auto-fermeture
|
||||
|
||||
### Étape 2: Snippets de Base
|
||||
- [x] Snippets pour instructions arithmétiques (add, addi, sub, etc.)
|
||||
- [x] Snippets pour instructions mémoire (lw, sw, lb, sb, etc.)
|
||||
- [x] Snippets pour instructions de branchement (beq, bne, blt, etc.)
|
||||
- [x] Snippets pour pseudo-instructions (li, la, mv, j, ret, etc.)
|
||||
- [x] Snippets pour directives d'assembleur (.text, .data, .global, etc.)
|
||||
- [x] Navigation avec Tab entre les placeholders
|
||||
|
||||
### Étape 4: Exécution dans QEMU
|
||||
- [x] Commande de compilation avec riscv64-linux-gnu-gcc
|
||||
- [x] Détection automatique du type de programme (_start vs main)
|
||||
- [x] Options de compilation adaptées (standalone vs avec libc)
|
||||
- [x] Commande d'exécution dans QEMU
|
||||
- [x] Terminal intégré pour l'exécution
|
||||
- [x] Boutons dans la barre d'outils
|
||||
- [x] Support du clic droit et palette de commandes
|
||||
|
||||
### Étape 5: Debugging et Diagnostics
|
||||
- [x] Validation syntaxique en temps réel
|
||||
- [x] Vérification des instructions RISC-V
|
||||
- [x] Validation des noms de registres
|
||||
- [x] Détection des labels non définis
|
||||
- [x] Vérification du nombre d'arguments des instructions
|
||||
- [x] Warnings pour directives inconnues
|
||||
- [x] Hover documentation pour instructions
|
||||
- [x] Hover information pour registres
|
||||
- [x] Hover documentation pour directives
|
||||
- [x] IntelliSense avancé avec auto-complétion contextuelle
|
||||
- [x] Complétion des instructions selon le contexte
|
||||
- [x] Complétion des registres
|
||||
- [x] Complétion des labels définis dans le document
|
||||
- [x] Complétion des directives d'assembleur
|
||||
- [x] Complétion des constantes système courantes
|
||||
- [x] Navigation Go to Definition pour labels
|
||||
- [x] Find All References pour labels
|
||||
- [x] Configuration du debugging avec GDB
|
||||
- [x] Support gdb-multiarch pour RISC-V
|
||||
- [x] Compilation avec informations de debug (-g)
|
||||
- [x] Configuration de debugging avec QEMU
|
||||
- [x] Commandes de debugging intégrées
|
||||
|
||||
## Tâches en cours
|
||||
- [ ] Tests des fonctionnalités de debugging
|
||||
|
||||
## Tâches futures possibles
|
||||
|
||||
### Étape 6: Templates et Boilerplate
|
||||
- [ ] Commande "New RISC-V Program"
|
||||
- [ ] Templates multiples (Hello World, calcul, tableaux)
|
||||
- [ ] Snippets avancés (boucles, fonctions)
|
||||
- [ ] Génération automatique de structure de base
|
||||
|
||||
### Étape 7: Intégration avancée
|
||||
- [ ] Génération automatique de Makefile
|
||||
- [ ] Support des projets multi-fichiers
|
||||
- [ ] Outline view des labels et fonctions
|
||||
- [ ] Folding avancé par sections
|
||||
- [ ] Refactoring de base (renommage de labels)
|
||||
|
||||
### Étape 8: Émulation et Testing
|
||||
- [ ] Terminal QEMU intégré dans VSCode
|
||||
- [ ] Memory viewer pour inspection mémoire
|
||||
- [ ] Register viewer en temps réel
|
||||
- [ ] Step-by-step execution avec interface graphique
|
||||
- [ ] Breakpoints visuels dans l'éditeur
|
||||
|
||||
### Améliorations techniques
|
||||
- [ ] Support des macros assembleur
|
||||
- [ ] Validation sémantique avancée
|
||||
- [ ] Support des différentes variantes RISC-V (RV32, RV64)
|
||||
- [ ] Support des extensions (M, A, F, D)
|
||||
- [ ] Intégration avec simulateurs alternatifs
|
||||
- [ ] Export vers différents formats (Intel HEX, etc.)
|
||||
|
||||
### Distribution et maintenance
|
||||
- [ ] Tests automatisés
|
||||
- [ ] Documentation utilisateur complète
|
||||
- [ ] Empaquetage pour VS Code Marketplace
|
||||
- [ ] CI/CD pour les releases
|
||||
- [ ] Support multi-plateforme (Windows, macOS)
|
||||
|
||||
## Notes techniques
|
||||
- Extension basée sur TypeScript
|
||||
- Utilise l'API VSCode pour language servers
|
||||
- Intégration avec toolchain GNU RISC-V
|
||||
- Compatible avec QEMU user mode
|
||||
- Support debugging via GDB multiarch
|
||||
28
eslint.config.mjs
Normal file
28
eslint.config.mjs
Normal file
@@ -0,0 +1,28 @@
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
|
||||
export default [{
|
||||
files: ["**/*.ts"],
|
||||
}, {
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/naming-convention": ["warn", {
|
||||
selector: "import",
|
||||
format: ["camelCase", "PascalCase"],
|
||||
}],
|
||||
|
||||
curly: "warn",
|
||||
eqeqeq: "warn",
|
||||
"no-throw-literal": "warn",
|
||||
semi: "warn",
|
||||
},
|
||||
}];
|
||||
32
language-configuration.json
Normal file
32
language-configuration.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "#",
|
||||
"blockComment": ["/*", "*/"]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*#\\s*region\\b",
|
||||
"end": "^\\s*#\\s*endregion\\b"
|
||||
}
|
||||
},
|
||||
"wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)"
|
||||
}
|
||||
3376
package-lock.json
generated
Normal file
3376
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
131
package.json
Normal file
131
package.json
Normal file
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"name": "riscv-asm",
|
||||
"displayName": "RISC-V Assembly",
|
||||
"description": "Support pour le développement en assembleur RISC-V",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.101.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages",
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:riscv-asm"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "riscv-asm.helloWorld",
|
||||
"title": "Hello World",
|
||||
"category": "RISC-V"
|
||||
},
|
||||
{
|
||||
"command": "riscv-asm.compile",
|
||||
"title": "Compile RISC-V File",
|
||||
"category": "RISC-V",
|
||||
"icon": "$(tools)"
|
||||
},
|
||||
{
|
||||
"command": "riscv-asm.run",
|
||||
"title": "Run in QEMU",
|
||||
"category": "RISC-V",
|
||||
"icon": "$(play)"
|
||||
},
|
||||
{
|
||||
"command": "riscv-asm.compileAndRun",
|
||||
"title": "Compile & Run",
|
||||
"category": "RISC-V",
|
||||
"icon": "$(play-circle)"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"editor/title": [
|
||||
{
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv",
|
||||
"command": "riscv-asm.compile",
|
||||
"group": "navigation@1"
|
||||
},
|
||||
{
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv",
|
||||
"command": "riscv-asm.run",
|
||||
"group": "navigation@2"
|
||||
},
|
||||
{
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv",
|
||||
"command": "riscv-asm.compileAndRun",
|
||||
"group": "navigation@3"
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv",
|
||||
"command": "riscv-asm.compileAndRun",
|
||||
"group": "risc-v"
|
||||
}
|
||||
],
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "riscv-asm.compile",
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv"
|
||||
},
|
||||
{
|
||||
"command": "riscv-asm.run",
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv"
|
||||
},
|
||||
{
|
||||
"command": "riscv-asm.compileAndRun",
|
||||
"when": "resourceExtname == .s || resourceExtname == .asm || resourceExtname == .rv"
|
||||
}
|
||||
]
|
||||
},
|
||||
"languages": [
|
||||
{
|
||||
"id": "riscv-asm",
|
||||
"aliases": [
|
||||
"RISC-V Assembly",
|
||||
"riscv-asm"
|
||||
],
|
||||
"extensions": [
|
||||
".s",
|
||||
".asm",
|
||||
".rv"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "riscv-asm",
|
||||
"scopeName": "source.riscv-asm",
|
||||
"path": "./syntaxes/riscv-asm.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"snippets": [
|
||||
{
|
||||
"language": "riscv-asm",
|
||||
"path": "./snippets/riscv.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"lint": "eslint src",
|
||||
"test": "vscode-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "^1.101.0",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "22.x",
|
||||
"@typescript-eslint/eslint-plugin": "^8.39.0",
|
||||
"@typescript-eslint/parser": "^8.39.0",
|
||||
"eslint": "^9.32.0",
|
||||
"typescript": "^5.9.2",
|
||||
"@vscode/test-cli": "^0.0.11",
|
||||
"@vscode/test-electron": "^2.5.2"
|
||||
}
|
||||
}
|
||||
289
snippets/riscv.json
Normal file
289
snippets/riscv.json
Normal file
@@ -0,0 +1,289 @@
|
||||
{
|
||||
"Load Immediate": {
|
||||
"prefix": "li",
|
||||
"body": [
|
||||
"li ${1:reg}, ${2:immediate}"
|
||||
],
|
||||
"description": "Load immediate value into register"
|
||||
},
|
||||
"Load Address": {
|
||||
"prefix": "la",
|
||||
"body": [
|
||||
"la ${1:reg}, ${2:label}"
|
||||
],
|
||||
"description": "Load address of label into register"
|
||||
},
|
||||
"Add": {
|
||||
"prefix": "add",
|
||||
"body": [
|
||||
"add ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Add two registers"
|
||||
},
|
||||
"Add Immediate": {
|
||||
"prefix": "addi",
|
||||
"body": [
|
||||
"addi ${1:rd}, ${2:rs1}, ${3:immediate}"
|
||||
],
|
||||
"description": "Add immediate to register"
|
||||
},
|
||||
"Subtract": {
|
||||
"prefix": "sub",
|
||||
"body": [
|
||||
"sub ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Subtract two registers"
|
||||
},
|
||||
"Move": {
|
||||
"prefix": "mv",
|
||||
"body": [
|
||||
"mv ${1:rd}, ${2:rs}"
|
||||
],
|
||||
"description": "Move register (pseudo-instruction)"
|
||||
},
|
||||
"Load Word": {
|
||||
"prefix": "lw",
|
||||
"body": [
|
||||
"lw ${1:rd}, ${2:offset}(${3:rs1})"
|
||||
],
|
||||
"description": "Load word from memory"
|
||||
},
|
||||
"Load Byte": {
|
||||
"prefix": "lb",
|
||||
"body": [
|
||||
"lb ${1:rd}, ${2:offset}(${3:rs1})"
|
||||
],
|
||||
"description": "Load byte from memory"
|
||||
},
|
||||
"Load Half": {
|
||||
"prefix": "lh",
|
||||
"body": [
|
||||
"lh ${1:rd}, ${2:offset}(${3:rs1})"
|
||||
],
|
||||
"description": "Load halfword from memory"
|
||||
},
|
||||
"Store Word": {
|
||||
"prefix": "sw",
|
||||
"body": [
|
||||
"sw ${1:rs2}, ${2:offset}(${3:rs1})"
|
||||
],
|
||||
"description": "Store word to memory"
|
||||
},
|
||||
"Store Byte": {
|
||||
"prefix": "sb",
|
||||
"body": [
|
||||
"sb ${1:rs2}, ${2:offset}(${3:rs1})"
|
||||
],
|
||||
"description": "Store byte to memory"
|
||||
},
|
||||
"Store Half": {
|
||||
"prefix": "sh",
|
||||
"body": [
|
||||
"sh ${1:rs2}, ${2:offset}(${3:rs1})"
|
||||
],
|
||||
"description": "Store halfword to memory"
|
||||
},
|
||||
"Branch Equal": {
|
||||
"prefix": "beq",
|
||||
"body": [
|
||||
"beq ${1:rs1}, ${2:rs2}, ${3:label}"
|
||||
],
|
||||
"description": "Branch if equal"
|
||||
},
|
||||
"Branch Not Equal": {
|
||||
"prefix": "bne",
|
||||
"body": [
|
||||
"bne ${1:rs1}, ${2:rs2}, ${3:label}"
|
||||
],
|
||||
"description": "Branch if not equal"
|
||||
},
|
||||
"Branch Less Than": {
|
||||
"prefix": "blt",
|
||||
"body": [
|
||||
"blt ${1:rs1}, ${2:rs2}, ${3:label}"
|
||||
],
|
||||
"description": "Branch if less than (signed)"
|
||||
},
|
||||
"Branch Greater Equal": {
|
||||
"prefix": "bge",
|
||||
"body": [
|
||||
"bge ${1:rs1}, ${2:rs2}, ${3:label}"
|
||||
],
|
||||
"description": "Branch if greater or equal (signed)"
|
||||
},
|
||||
"Jump": {
|
||||
"prefix": "j",
|
||||
"body": [
|
||||
"j ${1:label}"
|
||||
],
|
||||
"description": "Unconditional jump"
|
||||
},
|
||||
"Jump and Link": {
|
||||
"prefix": "jal",
|
||||
"body": [
|
||||
"jal ${1:rd}, ${2:label}"
|
||||
],
|
||||
"description": "Jump and link"
|
||||
},
|
||||
"Jump and Link Register": {
|
||||
"prefix": "jalr",
|
||||
"body": [
|
||||
"jalr ${1:rd}, ${2:rs1}, ${3:offset}"
|
||||
],
|
||||
"description": "Jump and link register"
|
||||
},
|
||||
"Environment Call": {
|
||||
"prefix": "ecall",
|
||||
"body": [
|
||||
"ecall"
|
||||
],
|
||||
"description": "System call"
|
||||
},
|
||||
"No Operation": {
|
||||
"prefix": "nop",
|
||||
"body": [
|
||||
"nop"
|
||||
],
|
||||
"description": "No operation"
|
||||
},
|
||||
"Return": {
|
||||
"prefix": "ret",
|
||||
"body": [
|
||||
"ret"
|
||||
],
|
||||
"description": "Return from function"
|
||||
},
|
||||
"XOR": {
|
||||
"prefix": "xor",
|
||||
"body": [
|
||||
"xor ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Exclusive OR"
|
||||
},
|
||||
"XOR Immediate": {
|
||||
"prefix": "xori",
|
||||
"body": [
|
||||
"xori ${1:rd}, ${2:rs1}, ${3:immediate}"
|
||||
],
|
||||
"description": "XOR with immediate"
|
||||
},
|
||||
"OR": {
|
||||
"prefix": "or",
|
||||
"body": [
|
||||
"or ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Bitwise OR"
|
||||
},
|
||||
"OR Immediate": {
|
||||
"prefix": "ori",
|
||||
"body": [
|
||||
"ori ${1:rd}, ${2:rs1}, ${3:immediate}"
|
||||
],
|
||||
"description": "OR with immediate"
|
||||
},
|
||||
"AND": {
|
||||
"prefix": "and",
|
||||
"body": [
|
||||
"and ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Bitwise AND"
|
||||
},
|
||||
"AND Immediate": {
|
||||
"prefix": "andi",
|
||||
"body": [
|
||||
"andi ${1:rd}, ${2:rs1}, ${3:immediate}"
|
||||
],
|
||||
"description": "AND with immediate"
|
||||
},
|
||||
"Shift Left Logical": {
|
||||
"prefix": "sll",
|
||||
"body": [
|
||||
"sll ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Shift left logical"
|
||||
},
|
||||
"Shift Left Logical Immediate": {
|
||||
"prefix": "slli",
|
||||
"body": [
|
||||
"slli ${1:rd}, ${2:rs1}, ${3:shamt}"
|
||||
],
|
||||
"description": "Shift left logical immediate"
|
||||
},
|
||||
"Shift Right Logical": {
|
||||
"prefix": "srl",
|
||||
"body": [
|
||||
"srl ${1:rd}, ${2:rs1}, ${3:rs2}"
|
||||
],
|
||||
"description": "Shift right logical"
|
||||
},
|
||||
"Shift Right Logical Immediate": {
|
||||
"prefix": "srli",
|
||||
"body": [
|
||||
"srli ${1:rd}, ${2:rs1}, ${3:shamt}"
|
||||
],
|
||||
"description": "Shift right logical immediate"
|
||||
},
|
||||
"Text Section": {
|
||||
"prefix": ".text",
|
||||
"body": [
|
||||
".text"
|
||||
],
|
||||
"description": "Text section directive"
|
||||
},
|
||||
"Data Section": {
|
||||
"prefix": ".data",
|
||||
"body": [
|
||||
".data"
|
||||
],
|
||||
"description": "Data section directive"
|
||||
},
|
||||
"Global Symbol": {
|
||||
"prefix": ".globl",
|
||||
"body": [
|
||||
".globl ${1:symbol}"
|
||||
],
|
||||
"description": "Make symbol globally visible"
|
||||
},
|
||||
"Word Data": {
|
||||
"prefix": ".word",
|
||||
"body": [
|
||||
".word ${1:value}"
|
||||
],
|
||||
"description": "Define 32-bit word"
|
||||
},
|
||||
"Byte Data": {
|
||||
"prefix": ".byte",
|
||||
"body": [
|
||||
".byte ${1:value}"
|
||||
],
|
||||
"description": "Define byte"
|
||||
},
|
||||
"String Data": {
|
||||
"prefix": ".string",
|
||||
"body": [
|
||||
".string \"${1:text}\""
|
||||
],
|
||||
"description": "Define null-terminated string"
|
||||
},
|
||||
"ASCII String": {
|
||||
"prefix": ".ascii",
|
||||
"body": [
|
||||
".ascii \"${1:text}\""
|
||||
],
|
||||
"description": "Define ASCII string (no null terminator)"
|
||||
},
|
||||
"Space Allocation": {
|
||||
"prefix": ".space",
|
||||
"body": [
|
||||
".space ${1:bytes}"
|
||||
],
|
||||
"description": "Reserve space in bytes"
|
||||
},
|
||||
"Align": {
|
||||
"prefix": ".align",
|
||||
"body": [
|
||||
".align ${1:boundary}"
|
||||
],
|
||||
"description": "Align to boundary"
|
||||
}
|
||||
}
|
||||
264
src/completion.ts
Normal file
264
src/completion.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// Instructions avec leurs signatures
|
||||
const INSTRUCTION_COMPLETIONS: { [key: string]: vscode.CompletionItem } = {};
|
||||
|
||||
// Initialiser les completions d'instructions
|
||||
function initializeInstructionCompletions() {
|
||||
const instructions = [
|
||||
// Arithmétiques
|
||||
{ name: 'add', detail: 'Add two registers', signature: 'add rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'addi', detail: 'Add immediate', signature: 'addi rd, rs1, imm', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sub', detail: 'Subtract', signature: 'sub rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'lui', detail: 'Load upper immediate', signature: 'lui rd, imm', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'auipc', detail: 'Add upper immediate to PC', signature: 'auipc rd, imm', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Logiques
|
||||
{ name: 'xor', detail: 'Exclusive OR', signature: 'xor rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'xori', detail: 'XOR immediate', signature: 'xori rd, rs1, imm', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'or', detail: 'Bitwise OR', signature: 'or rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'ori', detail: 'OR immediate', signature: 'ori rd, rs1, imm', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'and', detail: 'Bitwise AND', signature: 'and rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'andi', detail: 'AND immediate', signature: 'andi rd, rs1, imm', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Décalages
|
||||
{ name: 'sll', detail: 'Shift left logical', signature: 'sll rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'slli', detail: 'Shift left logical immediate', signature: 'slli rd, rs1, shamt', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'srl', detail: 'Shift right logical', signature: 'srl rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'srli', detail: 'Shift right logical immediate', signature: 'srli rd, rs1, shamt', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sra', detail: 'Shift right arithmetic', signature: 'sra rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'srai', detail: 'Shift right arithmetic immediate', signature: 'srai rd, rs1, shamt', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Comparaisons
|
||||
{ name: 'slt', detail: 'Set less than', signature: 'slt rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'slti', detail: 'Set less than immediate', signature: 'slti rd, rs1, imm', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sltu', detail: 'Set less than unsigned', signature: 'sltu rd, rs1, rs2', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sltiu', detail: 'Set less than immediate unsigned', signature: 'sltiu rd, rs1, imm', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Mémoire
|
||||
{ name: 'lb', detail: 'Load byte', signature: 'lb rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'lh', detail: 'Load halfword', signature: 'lh rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'lw', detail: 'Load word', signature: 'lw rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'ld', detail: 'Load doubleword', signature: 'ld rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'lbu', detail: 'Load byte unsigned', signature: 'lbu rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'lhu', detail: 'Load halfword unsigned', signature: 'lhu rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'lwu', detail: 'Load word unsigned', signature: 'lwu rd, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sb', detail: 'Store byte', signature: 'sb rs2, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sh', detail: 'Store halfword', signature: 'sh rs2, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sw', detail: 'Store word', signature: 'sw rs2, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'sd', detail: 'Store doubleword', signature: 'sd rs2, offset(rs1)', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Branchements
|
||||
{ name: 'beq', detail: 'Branch if equal', signature: 'beq rs1, rs2, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'bne', detail: 'Branch if not equal', signature: 'bne rs1, rs2, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'blt', detail: 'Branch if less than', signature: 'blt rs1, rs2, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'bge', detail: 'Branch if greater equal', signature: 'bge rs1, rs2, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'bltu', detail: 'Branch if less than unsigned', signature: 'bltu rs1, rs2, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'bgeu', detail: 'Branch if greater equal unsigned', signature: 'bgeu rs1, rs2, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'jal', detail: 'Jump and link', signature: 'jal rd, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'jalr', detail: 'Jump and link register', signature: 'jalr rd, rs1, offset', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Pseudo-instructions
|
||||
{ name: 'nop', detail: 'No operation', signature: 'nop', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'li', detail: 'Load immediate', signature: 'li rd, imm', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'la', detail: 'Load address', signature: 'la rd, label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'mv', detail: 'Move register', signature: 'mv rd, rs', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'not', detail: 'Bitwise NOT', signature: 'not rd, rs', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'neg', detail: 'Negate', signature: 'neg rd, rs', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'j', detail: 'Jump', signature: 'j label', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'jr', detail: 'Jump register', signature: 'jr rs', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'ret', detail: 'Return', signature: 'ret', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'call', detail: 'Call function', signature: 'call label', kind: vscode.CompletionItemKind.Function },
|
||||
|
||||
// Système
|
||||
{ name: 'ecall', detail: 'Environment call', signature: 'ecall', kind: vscode.CompletionItemKind.Function },
|
||||
{ name: 'ebreak', detail: 'Environment break', signature: 'ebreak', kind: vscode.CompletionItemKind.Function }
|
||||
];
|
||||
|
||||
instructions.forEach(instr => {
|
||||
const item = new vscode.CompletionItem(instr.name, instr.kind);
|
||||
item.detail = instr.detail;
|
||||
item.documentation = new vscode.MarkdownString().appendCodeblock(instr.signature, 'riscv-asm');
|
||||
item.insertText = instr.name;
|
||||
INSTRUCTION_COMPLETIONS[instr.name] = item;
|
||||
});
|
||||
}
|
||||
|
||||
// Créer les registres avec auto-complétion
|
||||
function createRegisterCompletions(): vscode.CompletionItem[] {
|
||||
const completions: vscode.CompletionItem[] = [];
|
||||
|
||||
// Registres temporaires
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const item = new vscode.CompletionItem(`t${i}`, vscode.CompletionItemKind.Variable);
|
||||
item.detail = `Temporary register ${i} (x${i + 5})`;
|
||||
completions.push(item);
|
||||
}
|
||||
|
||||
// Registres sauvegardés
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const item = new vscode.CompletionItem(`s${i}`, vscode.CompletionItemKind.Variable);
|
||||
item.detail = `Saved register ${i} (x${i + 8})`;
|
||||
completions.push(item);
|
||||
}
|
||||
|
||||
// Registres d'arguments
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const item = new vscode.CompletionItem(`a${i}`, vscode.CompletionItemKind.Variable);
|
||||
item.detail = `Argument register ${i} (x${i + 10})`;
|
||||
completions.push(item);
|
||||
}
|
||||
|
||||
// Registres spéciaux
|
||||
const specialRegs = [
|
||||
{ name: 'zero', detail: 'Always zero (x0)' },
|
||||
{ name: 'ra', detail: 'Return address (x1)' },
|
||||
{ name: 'sp', detail: 'Stack pointer (x2)' },
|
||||
{ name: 'gp', detail: 'Global pointer (x3)' },
|
||||
{ name: 'tp', detail: 'Thread pointer (x4)' },
|
||||
{ name: 'fp', detail: 'Frame pointer (x8/s0)' }
|
||||
];
|
||||
|
||||
specialRegs.forEach(reg => {
|
||||
const item = new vscode.CompletionItem(reg.name, vscode.CompletionItemKind.Variable);
|
||||
item.detail = reg.detail;
|
||||
completions.push(item);
|
||||
});
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
const REGISTER_COMPLETIONS = createRegisterCompletions();
|
||||
|
||||
export class RiscvCompletionProvider implements vscode.CompletionItemProvider {
|
||||
constructor() {
|
||||
initializeInstructionCompletions();
|
||||
}
|
||||
|
||||
provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
context: vscode.CompletionContext
|
||||
): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
|
||||
const line = document.lineAt(position).text;
|
||||
const linePrefix = line.substring(0, position.character);
|
||||
|
||||
// Collecter tous les labels définis dans le document
|
||||
const labels = this.collectLabels(document);
|
||||
|
||||
// Détecter le contexte de completion
|
||||
const completionItems: vscode.CompletionItem[] = [];
|
||||
|
||||
// Si on commence la ligne, proposer instructions + directives + labels
|
||||
if (linePrefix.trim() === '' || linePrefix.match(/^\s+$/)) {
|
||||
// Instructions
|
||||
completionItems.push(...Object.values(INSTRUCTION_COMPLETIONS));
|
||||
|
||||
// Directives
|
||||
completionItems.push(...this.getDirectiveCompletions());
|
||||
|
||||
// Labels (pour les sauts)
|
||||
completionItems.push(...labels);
|
||||
}
|
||||
// Si on est après une instruction, proposer registres + labels + constantes
|
||||
else {
|
||||
const instructionMatch = linePrefix.match(/^\s*(\w+)\s+/);
|
||||
if (instructionMatch) {
|
||||
const instruction = instructionMatch[1].toLowerCase();
|
||||
|
||||
// Registres toujours disponibles
|
||||
completionItems.push(...REGISTER_COMPLETIONS);
|
||||
|
||||
// Labels pour instructions de branchement/saut
|
||||
if (['beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu', 'jal', 'j', 'call'].includes(instruction)) {
|
||||
completionItems.push(...labels);
|
||||
}
|
||||
|
||||
// Constantes système courantes
|
||||
completionItems.push(...this.getSystemConstantCompletions());
|
||||
}
|
||||
}
|
||||
|
||||
return completionItems;
|
||||
}
|
||||
|
||||
private collectLabels(document: vscode.TextDocument): vscode.CompletionItem[] {
|
||||
const labels: vscode.CompletionItem[] = [];
|
||||
const text = document.getText();
|
||||
const lines = text.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
const labelMatch = line.match(/^(\w+):\s*/);
|
||||
if (labelMatch) {
|
||||
const labelName = labelMatch[1];
|
||||
const item = new vscode.CompletionItem(labelName, vscode.CompletionItemKind.Reference);
|
||||
item.detail = `Label defined at line ${i + 1}`;
|
||||
item.documentation = `Jump target: ${labelName}`;
|
||||
labels.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
private getDirectiveCompletions(): vscode.CompletionItem[] {
|
||||
const directives = [
|
||||
{ name: '.text', detail: 'Text section', doc: 'Start of code section' },
|
||||
{ name: '.data', detail: 'Data section', doc: 'Start of data section' },
|
||||
{ name: '.bss', detail: 'BSS section', doc: 'Uninitialized data section' },
|
||||
{ name: '.global', detail: 'Global symbol', doc: 'Make symbol globally visible' },
|
||||
{ name: '.globl', detail: 'Global symbol', doc: 'Make symbol globally visible (GNU syntax)' },
|
||||
{ name: '.word', detail: 'Define word', doc: 'Define 32-bit word' },
|
||||
{ name: '.dword', detail: 'Define doubleword', doc: 'Define 64-bit doubleword' },
|
||||
{ name: '.byte', detail: 'Define byte', doc: 'Define 8-bit byte' },
|
||||
{ name: '.half', detail: 'Define halfword', doc: 'Define 16-bit halfword' },
|
||||
{ name: '.string', detail: 'Define string', doc: 'Define null-terminated string' },
|
||||
{ name: '.ascii', detail: 'Define ASCII', doc: 'Define ASCII string (no null terminator)' },
|
||||
{ name: '.space', detail: 'Reserve space', doc: 'Reserve specified bytes' },
|
||||
{ name: '.align', detail: 'Align data', doc: 'Align to boundary' },
|
||||
{ name: '.equ', detail: 'Define constant', doc: 'Define symbolic constant' },
|
||||
{ name: '.set', detail: 'Set value', doc: 'Set symbol value' }
|
||||
];
|
||||
|
||||
return directives.map(dir => {
|
||||
const item = new vscode.CompletionItem(dir.name, vscode.CompletionItemKind.Keyword);
|
||||
item.detail = dir.detail;
|
||||
item.documentation = dir.doc;
|
||||
item.insertText = dir.name;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
private getSystemConstantCompletions(): vscode.CompletionItem[] {
|
||||
const constants = [
|
||||
// Codes d'appel système Linux
|
||||
{ name: '64', detail: 'sys_write', doc: 'Write system call number' },
|
||||
{ name: '63', detail: 'sys_read', doc: 'Read system call number' },
|
||||
{ name: '93', detail: 'sys_exit', doc: 'Exit system call number' },
|
||||
{ name: '1024', detail: 'sys_open', doc: 'Open file system call number' },
|
||||
{ name: '1025', detail: 'sys_close', doc: 'Close file system call number' },
|
||||
|
||||
// File descriptors
|
||||
{ name: '0', detail: 'STDIN', doc: 'Standard input file descriptor' },
|
||||
{ name: '1', detail: 'STDOUT', doc: 'Standard output file descriptor' },
|
||||
{ name: '2', detail: 'STDERR', doc: 'Standard error file descriptor' },
|
||||
|
||||
// Valeurs courantes
|
||||
{ name: '42', detail: 'The answer', doc: 'Common test value' }
|
||||
];
|
||||
|
||||
return constants.map(constant => {
|
||||
const item = new vscode.CompletionItem(constant.name, vscode.CompletionItemKind.Constant);
|
||||
item.detail = constant.detail;
|
||||
item.documentation = constant.doc;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CompletionItem> {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
78
src/definition.ts
Normal file
78
src/definition.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class RiscvDefinitionProvider implements vscode.DefinitionProvider {
|
||||
provideDefinition(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken
|
||||
): vscode.ProviderResult<vscode.Definition | vscode.LocationLink[]> {
|
||||
|
||||
const range = document.getWordRangeAtPosition(position);
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const word = document.getText(range);
|
||||
|
||||
// Chercher la définition du label dans le document
|
||||
const labelDefinition = this.findLabelDefinition(document, word);
|
||||
if (labelDefinition) {
|
||||
return new vscode.Location(document.uri, labelDefinition);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private findLabelDefinition(document: vscode.TextDocument, labelName: string): vscode.Position | undefined {
|
||||
const text = document.getText();
|
||||
const lines = text.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
const labelMatch = line.match(/^(\w+):\s*/);
|
||||
if (labelMatch && labelMatch[1] === labelName) {
|
||||
return new vscode.Position(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class RiscvReferenceProvider implements vscode.ReferenceProvider {
|
||||
provideReferences(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
context: vscode.ReferenceContext,
|
||||
token: vscode.CancellationToken
|
||||
): vscode.ProviderResult<vscode.Location[]> {
|
||||
|
||||
const range = document.getWordRangeAtPosition(position);
|
||||
if (!range) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const word = document.getText(range);
|
||||
const references: vscode.Location[] = [];
|
||||
|
||||
// Chercher toutes les références au label
|
||||
const text = document.getText();
|
||||
const lines = text.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Chercher les occurrences du label
|
||||
let match;
|
||||
const regex = new RegExp(`\\b${word}\\b`, 'g');
|
||||
while ((match = regex.exec(line)) !== null) {
|
||||
const startPos = new vscode.Position(i, match.index);
|
||||
const endPos = new vscode.Position(i, match.index + word.length);
|
||||
const location = new vscode.Location(document.uri, new vscode.Range(startPos, endPos));
|
||||
references.push(location);
|
||||
}
|
||||
}
|
||||
|
||||
return references;
|
||||
}
|
||||
}
|
||||
215
src/diagnostics.ts
Normal file
215
src/diagnostics.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// Instructions RISC-V valides
|
||||
const VALID_INSTRUCTIONS = new Set([
|
||||
// Instructions arithmétiques
|
||||
'add', 'addi', 'sub', 'lui', 'auipc',
|
||||
'xor', 'xori', 'or', 'ori', 'and', 'andi',
|
||||
'sll', 'slli', 'srl', 'srli', 'sra', 'srai',
|
||||
'slt', 'slti', 'sltu', 'sltiu',
|
||||
|
||||
// Instructions de chargement/stockage
|
||||
'lb', 'lh', 'lw', 'ld', 'lbu', 'lhu', 'lwu',
|
||||
'sb', 'sh', 'sw', 'sd',
|
||||
|
||||
// Instructions de branchement
|
||||
'beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu',
|
||||
'jal', 'jalr',
|
||||
|
||||
// Instructions système
|
||||
'ecall', 'ebreak', 'fence', 'fence.i',
|
||||
'csrrw', 'csrrs', 'csrrc', 'csrrwi', 'csrrsi', 'csrrci',
|
||||
|
||||
// Instructions de multiplication/division
|
||||
'mul', 'mulh', 'mulhsu', 'mulhu', 'div', 'divu', 'rem', 'remu',
|
||||
|
||||
// Pseudo-instructions
|
||||
'nop', 'li', 'la', 'mv', 'not', 'neg', 'seqz', 'snez',
|
||||
'sltz', 'sgtz', 'j', 'jr', 'ret', 'call', 'tail'
|
||||
]);
|
||||
|
||||
// Registres RISC-V valides
|
||||
const VALID_REGISTERS = new Set([
|
||||
// Registres numériques
|
||||
'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9',
|
||||
'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19',
|
||||
'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29',
|
||||
'x30', 'x31',
|
||||
|
||||
// Noms ABI
|
||||
'zero', 'ra', 'sp', 'gp', 'tp',
|
||||
't0', 't1', 't2', 't3', 't4', 't5', 't6',
|
||||
's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11',
|
||||
'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7',
|
||||
'fp',
|
||||
|
||||
// Registres flottants
|
||||
'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
|
||||
'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19',
|
||||
'f20', 'f21', 'f22', 'f23', 'f24', 'f25', 'f26', 'f27', 'f28', 'f29',
|
||||
'f30', 'f31',
|
||||
'ft0', 'ft1', 'ft2', 'ft3', 'ft4', 'ft5', 'ft6', 'ft7', 'ft8', 'ft9', 'ft10', 'ft11',
|
||||
'fs0', 'fs1', 'fs2', 'fs3', 'fs4', 'fs5', 'fs6', 'fs7', 'fs8', 'fs9', 'fs10', 'fs11',
|
||||
'fa0', 'fa1', 'fa2', 'fa3', 'fa4', 'fa5', 'fa6', 'fa7'
|
||||
]);
|
||||
|
||||
// Directives valides
|
||||
const VALID_DIRECTIVES = new Set([
|
||||
'.text', '.data', '.bss', '.section', '.global', '.globl', '.extern',
|
||||
'.equ', '.set', '.align', '.balign', '.word', '.dword', '.byte', '.half',
|
||||
'.ascii', '.asciiz', '.string', '.zero', '.space', '.skip', '.org', '.include'
|
||||
]);
|
||||
|
||||
export function validateRiscvDocument(document: vscode.TextDocument): vscode.Diagnostic[] {
|
||||
const diagnostics: vscode.Diagnostic[] = [];
|
||||
const text = document.getText();
|
||||
const lines = text.split('\n');
|
||||
|
||||
const labels = new Set<string>();
|
||||
const usedLabels = new Set<string>();
|
||||
|
||||
for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
|
||||
const line = lines[lineNumber].trim();
|
||||
|
||||
// Ignorer les lignes vides et les commentaires
|
||||
if (!line || line.startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Détecter les labels
|
||||
const labelMatch = line.match(/^(\w+):\s*(.*)/);
|
||||
if (labelMatch) {
|
||||
const labelName = labelMatch[1];
|
||||
labels.add(labelName);
|
||||
|
||||
// Vérifier la ligne après le label
|
||||
const restOfLine = labelMatch[2].trim();
|
||||
if (restOfLine) {
|
||||
validateInstruction(restOfLine, lineNumber, diagnostics, usedLabels);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vérifier les instructions
|
||||
validateInstruction(line, lineNumber, diagnostics, usedLabels);
|
||||
}
|
||||
|
||||
// Vérifier les labels non définis
|
||||
for (const usedLabel of usedLabels) {
|
||||
if (!labels.has(usedLabel)) {
|
||||
// Trouver où ce label est utilisé pour signaler l'erreur
|
||||
for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
|
||||
const line = lines[lineNumber];
|
||||
if (line.includes(usedLabel)) {
|
||||
const range = new vscode.Range(lineNumber, 0, lineNumber, line.length);
|
||||
const diagnostic = new vscode.Diagnostic(
|
||||
range,
|
||||
`Undefined label: ${usedLabel}`,
|
||||
vscode.DiagnosticSeverity.Error
|
||||
);
|
||||
diagnostic.code = 'undefined-label';
|
||||
diagnostics.push(diagnostic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
function validateInstruction(line: string, lineNumber: number, diagnostics: vscode.Diagnostic[], usedLabels: Set<string>) {
|
||||
// Ignorer les directives d'assembleur
|
||||
if (line.startsWith('.')) {
|
||||
const directive = line.split(/\s+/)[0].toLowerCase();
|
||||
if (!VALID_DIRECTIVES.has(directive)) {
|
||||
const range = new vscode.Range(lineNumber, 0, lineNumber, directive.length);
|
||||
const diagnostic = new vscode.Diagnostic(
|
||||
range,
|
||||
`Unknown directive: ${directive}`,
|
||||
vscode.DiagnosticSeverity.Warning
|
||||
);
|
||||
diagnostic.code = 'unknown-directive';
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Parser l'instruction
|
||||
const parts = line.split(/[\s,()]+/).filter(part => part.length > 0);
|
||||
if (parts.length === 0) return;
|
||||
|
||||
const instruction = parts[0].toLowerCase();
|
||||
|
||||
// Vérifier si l'instruction est valide
|
||||
if (!VALID_INSTRUCTIONS.has(instruction)) {
|
||||
const range = new vscode.Range(lineNumber, 0, lineNumber, instruction.length);
|
||||
const diagnostic = new vscode.Diagnostic(
|
||||
range,
|
||||
`Unknown instruction: ${instruction}`,
|
||||
vscode.DiagnosticSeverity.Error
|
||||
);
|
||||
diagnostic.code = 'unknown-instruction';
|
||||
diagnostics.push(diagnostic);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier les arguments
|
||||
const args = parts.slice(1);
|
||||
validateInstructionArgs(instruction, args, lineNumber, diagnostics, usedLabels);
|
||||
}
|
||||
|
||||
function validateInstructionArgs(instruction: string, args: string[], lineNumber: number, diagnostics: vscode.Diagnostic[], usedLabels: Set<string>) {
|
||||
// Vérifier le nombre d'arguments pour certaines instructions
|
||||
const expectedArgCounts: { [key: string]: number } = {
|
||||
'add': 3, 'sub': 3, 'xor': 3, 'or': 3, 'and': 3,
|
||||
'addi': 3, 'xori': 3, 'ori': 3, 'andi': 3,
|
||||
'beq': 3, 'bne': 3, 'blt': 3, 'bge': 3,
|
||||
'lw': 2, 'sw': 2, 'lb': 2, 'sb': 2,
|
||||
'li': 2, 'mv': 2, 'j': 1, 'jal': 2,
|
||||
'ecall': 0, 'ret': 0, 'nop': 0
|
||||
};
|
||||
|
||||
if (expectedArgCounts[instruction] !== undefined) {
|
||||
const expected = expectedArgCounts[instruction];
|
||||
if (args.length !== expected) {
|
||||
const range = new vscode.Range(lineNumber, 0, lineNumber, 100);
|
||||
const diagnostic = new vscode.Diagnostic(
|
||||
range,
|
||||
`${instruction} expects ${expected} arguments, got ${args.length}`,
|
||||
vscode.DiagnosticSeverity.Error
|
||||
);
|
||||
diagnostic.code = 'wrong-arg-count';
|
||||
diagnostics.push(diagnostic);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier les registres et collecter les labels utilisés
|
||||
for (const arg of args) {
|
||||
const cleanArg = arg.replace(/[(),]/g, '');
|
||||
|
||||
// Ignorer les nombres (décimaux, hex, etc.)
|
||||
if (/^-?\d+$/.test(cleanArg) || /^0x[0-9a-fA-F]+$/.test(cleanArg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vérifier si c'est un registre
|
||||
if (cleanArg.startsWith('x') || VALID_REGISTERS.has(cleanArg.toLowerCase())) {
|
||||
if (!VALID_REGISTERS.has(cleanArg.toLowerCase())) {
|
||||
const range = new vscode.Range(lineNumber, 0, lineNumber, 100);
|
||||
const diagnostic = new vscode.Diagnostic(
|
||||
range,
|
||||
`Invalid register: ${cleanArg}`,
|
||||
vscode.DiagnosticSeverity.Error
|
||||
);
|
||||
diagnostic.code = 'invalid-register';
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
// Sinon, c'est probablement un label
|
||||
else if (/^[a-zA-Z_]\w*$/.test(cleanArg)) {
|
||||
usedLabels.add(cleanArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
213
src/extension.ts
Normal file
213
src/extension.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import { validateRiscvDocument } from './diagnostics';
|
||||
import { RiscvHoverProvider } from './hover';
|
||||
import { RiscvCompletionProvider } from './completion';
|
||||
import { RiscvDefinitionProvider, RiscvReferenceProvider } from './definition';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('RISC-V Assembly extension is now active!');
|
||||
|
||||
// Créer une collection de diagnostics
|
||||
const diagnosticCollection = vscode.languages.createDiagnosticCollection('riscv-asm');
|
||||
context.subscriptions.push(diagnosticCollection);
|
||||
|
||||
// Enregistrer le hover provider
|
||||
const hoverProvider = vscode.languages.registerHoverProvider('riscv-asm', new RiscvHoverProvider());
|
||||
context.subscriptions.push(hoverProvider);
|
||||
|
||||
// Enregistrer le completion provider
|
||||
const completionProvider = vscode.languages.registerCompletionItemProvider(
|
||||
'riscv-asm',
|
||||
new RiscvCompletionProvider(),
|
||||
'.', // Trigger sur '.' pour les directives
|
||||
',' // Trigger sur ',' pour les arguments
|
||||
);
|
||||
context.subscriptions.push(completionProvider);
|
||||
|
||||
// Enregistrer les providers de navigation
|
||||
const definitionProvider = vscode.languages.registerDefinitionProvider('riscv-asm', new RiscvDefinitionProvider());
|
||||
const referenceProvider = vscode.languages.registerReferenceProvider('riscv-asm', new RiscvReferenceProvider());
|
||||
context.subscriptions.push(definitionProvider, referenceProvider);
|
||||
|
||||
// Fonction pour mettre à jour les diagnostics
|
||||
function updateDiagnostics(document: vscode.TextDocument) {
|
||||
if (document.languageId === 'riscv-asm') {
|
||||
const diagnostics = validateRiscvDocument(document);
|
||||
diagnosticCollection.set(document.uri, diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
// Écouter les changements de document
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidChangeTextDocument((event) => {
|
||||
updateDiagnostics(event.document);
|
||||
}),
|
||||
vscode.workspace.onDidOpenTextDocument((document) => {
|
||||
updateDiagnostics(document);
|
||||
}),
|
||||
vscode.workspace.onDidCloseTextDocument((document) => {
|
||||
diagnosticCollection.delete(document.uri);
|
||||
})
|
||||
);
|
||||
|
||||
// Valider tous les documents déjà ouverts
|
||||
vscode.workspace.textDocuments.forEach(updateDiagnostics);
|
||||
|
||||
// Commande Hello World existante
|
||||
const helloWorldDisposable = vscode.commands.registerCommand('riscv-asm.helloWorld', () => {
|
||||
vscode.window.showInformationMessage('Hello World from RISC-V Assembly!');
|
||||
});
|
||||
|
||||
// Commande de compilation
|
||||
const compileDisposable = vscode.commands.registerCommand('riscv-asm.compile', async () => {
|
||||
const activeEditor = vscode.window.activeTextEditor;
|
||||
if (!activeEditor) {
|
||||
vscode.window.showErrorMessage('No active file to compile');
|
||||
return;
|
||||
}
|
||||
|
||||
const document = activeEditor.document;
|
||||
if (!document.fileName.match(/\.(s|asm|rv)$/)) {
|
||||
vscode.window.showErrorMessage('Please compile a RISC-V assembly file (.s, .asm, .rv)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sauvegarder le fichier avant compilation
|
||||
await document.save();
|
||||
|
||||
try {
|
||||
await compileFile(document.fileName);
|
||||
vscode.window.showInformationMessage('Compilation successful!');
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(`Compilation failed: ${error}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Commande d'exécution avec QEMU
|
||||
const runDisposable = vscode.commands.registerCommand('riscv-asm.run', async () => {
|
||||
const activeEditor = vscode.window.activeTextEditor;
|
||||
if (!activeEditor) {
|
||||
vscode.window.showErrorMessage('No active file to run');
|
||||
return;
|
||||
}
|
||||
|
||||
const document = activeEditor.document;
|
||||
if (!document.fileName.match(/\.(s|asm|rv)$/)) {
|
||||
vscode.window.showErrorMessage('Please run a RISC-V assembly file (.s, .asm, .rv)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sauvegarder le fichier avant compilation et exécution
|
||||
await document.save();
|
||||
|
||||
try {
|
||||
// D'abord compiler
|
||||
await compileFile(document.fileName);
|
||||
|
||||
// Puis exécuter
|
||||
await runInQemu(document.fileName);
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(`Execution failed: ${error}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Commande de compilation et exécution en une fois
|
||||
const compileAndRunDisposable = vscode.commands.registerCommand('riscv-asm.compileAndRun', async () => {
|
||||
const activeEditor = vscode.window.activeTextEditor;
|
||||
if (!activeEditor) {
|
||||
vscode.window.showErrorMessage('No active file');
|
||||
return;
|
||||
}
|
||||
|
||||
const document = activeEditor.document;
|
||||
if (!document.fileName.match(/\.(s|asm|rv)$/)) {
|
||||
vscode.window.showErrorMessage('Please use a RISC-V assembly file (.s, .asm, .rv)');
|
||||
return;
|
||||
}
|
||||
|
||||
await document.save();
|
||||
|
||||
try {
|
||||
vscode.window.showInformationMessage('Compiling and running...');
|
||||
await compileFile(document.fileName);
|
||||
await runInQemu(document.fileName);
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(`Failed: ${error}`);
|
||||
}
|
||||
});
|
||||
|
||||
context.subscriptions.push(
|
||||
helloWorldDisposable,
|
||||
compileDisposable,
|
||||
runDisposable,
|
||||
compileAndRunDisposable
|
||||
);
|
||||
}
|
||||
|
||||
async function compileFile(sourceFile: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dir = path.dirname(sourceFile);
|
||||
const baseName = path.basename(sourceFile, path.extname(sourceFile));
|
||||
const outputFile = path.join(dir, baseName);
|
||||
|
||||
// Lire le contenu pour détecter le type de programme
|
||||
const content = fs.readFileSync(sourceFile, 'utf8');
|
||||
const hasStart = content.includes('_start');
|
||||
const hasMain = content.includes('main:');
|
||||
|
||||
let compileCommand: string;
|
||||
|
||||
if (hasStart && !hasMain) {
|
||||
// Programme standalone avec _start
|
||||
compileCommand = `riscv64-linux-gnu-gcc -nostdlib -nostartfiles -static -o "${outputFile}" "${sourceFile}"`;
|
||||
} else {
|
||||
// Programme avec main ou standard
|
||||
compileCommand = `riscv64-linux-gnu-gcc -static -o "${outputFile}" "${sourceFile}"`;
|
||||
}
|
||||
|
||||
try {
|
||||
execSync(compileCommand, {
|
||||
cwd: dir,
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
resolve();
|
||||
} catch (error: any) {
|
||||
reject(`Compilation error: ${error.stderr?.toString() || error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function runInQemu(sourceFile: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dir = path.dirname(sourceFile);
|
||||
const baseName = path.basename(sourceFile, path.extname(sourceFile));
|
||||
const executableFile = path.join(dir, baseName);
|
||||
|
||||
// Vérifier que l'exécutable existe
|
||||
if (!fs.existsSync(executableFile)) {
|
||||
reject('Executable not found. Please compile first.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Créer un terminal et exécuter avec QEMU
|
||||
const terminal = vscode.window.createTerminal({
|
||||
name: 'RISC-V QEMU',
|
||||
cwd: dir
|
||||
});
|
||||
|
||||
terminal.show();
|
||||
terminal.sendText(`qemu-riscv64 -L /usr/riscv64-linux-gnu "${executableFile}"`);
|
||||
|
||||
// Afficher un message d'information
|
||||
vscode.window.showInformationMessage(`Running ${baseName} in QEMU terminal`);
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
console.log('RISC-V Assembly extension deactivated');
|
||||
}
|
||||
236
src/hover.ts
Normal file
236
src/hover.ts
Normal file
@@ -0,0 +1,236 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// Documentation des instructions RISC-V
|
||||
const INSTRUCTION_DOCS: { [key: string]: { description: string; syntax: string; example: string } } = {
|
||||
'add': {
|
||||
description: 'Add two registers',
|
||||
syntax: 'add rd, rs1, rs2',
|
||||
example: 'add t0, t1, t2 # t0 = t1 + t2'
|
||||
},
|
||||
'addi': {
|
||||
description: 'Add immediate to register',
|
||||
syntax: 'addi rd, rs1, imm',
|
||||
example: 'addi t0, t1, 10 # t0 = t1 + 10'
|
||||
},
|
||||
'sub': {
|
||||
description: 'Subtract two registers',
|
||||
syntax: 'sub rd, rs1, rs2',
|
||||
example: 'sub t0, t1, t2 # t0 = t1 - t2'
|
||||
},
|
||||
'li': {
|
||||
description: 'Load immediate value (pseudo-instruction)',
|
||||
syntax: 'li rd, imm',
|
||||
example: 'li t0, 42 # t0 = 42'
|
||||
},
|
||||
'la': {
|
||||
description: 'Load address (pseudo-instruction)',
|
||||
syntax: 'la rd, label',
|
||||
example: 'la t0, msg # t0 = address of msg'
|
||||
},
|
||||
'mv': {
|
||||
description: 'Move register (pseudo-instruction)',
|
||||
syntax: 'mv rd, rs',
|
||||
example: 'mv t0, t1 # t0 = t1'
|
||||
},
|
||||
'lw': {
|
||||
description: 'Load word from memory',
|
||||
syntax: 'lw rd, offset(rs1)',
|
||||
example: 'lw t0, 8(sp) # t0 = memory[sp + 8]'
|
||||
},
|
||||
'sw': {
|
||||
description: 'Store word to memory',
|
||||
syntax: 'sw rs2, offset(rs1)',
|
||||
example: 'sw t0, 8(sp) # memory[sp + 8] = t0'
|
||||
},
|
||||
'lb': {
|
||||
description: 'Load byte from memory (sign-extended)',
|
||||
syntax: 'lb rd, offset(rs1)',
|
||||
example: 'lb t0, 0(t1) # t0 = (signed) memory[t1]'
|
||||
},
|
||||
'sb': {
|
||||
description: 'Store byte to memory',
|
||||
syntax: 'sb rs2, offset(rs1)',
|
||||
example: 'sb t0, 0(t1) # memory[t1] = t0[7:0]'
|
||||
},
|
||||
'beq': {
|
||||
description: 'Branch if equal',
|
||||
syntax: 'beq rs1, rs2, label',
|
||||
example: 'beq t0, t1, loop # if t0 == t1 goto loop'
|
||||
},
|
||||
'bne': {
|
||||
description: 'Branch if not equal',
|
||||
syntax: 'bne rs1, rs2, label',
|
||||
example: 'bne t0, zero, skip # if t0 != 0 goto skip'
|
||||
},
|
||||
'blt': {
|
||||
description: 'Branch if less than (signed)',
|
||||
syntax: 'blt rs1, rs2, label',
|
||||
example: 'blt t0, t1, smaller # if t0 < t1 goto smaller'
|
||||
},
|
||||
'bge': {
|
||||
description: 'Branch if greater or equal (signed)',
|
||||
syntax: 'bge rs1, rs2, label',
|
||||
example: 'bge t0, t1, bigger # if t0 >= t1 goto bigger'
|
||||
},
|
||||
'j': {
|
||||
description: 'Unconditional jump (pseudo-instruction)',
|
||||
syntax: 'j label',
|
||||
example: 'j loop # goto loop'
|
||||
},
|
||||
'jal': {
|
||||
description: 'Jump and link',
|
||||
syntax: 'jal rd, label',
|
||||
example: 'jal ra, function # ra = PC+4, goto function'
|
||||
},
|
||||
'jalr': {
|
||||
description: 'Jump and link register',
|
||||
syntax: 'jalr rd, rs1, offset',
|
||||
example: 'jalr ra, t0, 0 # ra = PC+4, goto t0'
|
||||
},
|
||||
'ret': {
|
||||
description: 'Return from function (pseudo-instruction)',
|
||||
syntax: 'ret',
|
||||
example: 'ret # goto ra (return address)'
|
||||
},
|
||||
'ecall': {
|
||||
description: 'Environment call (system call)',
|
||||
syntax: 'ecall',
|
||||
example: 'ecall # invoke system call'
|
||||
},
|
||||
'nop': {
|
||||
description: 'No operation (pseudo-instruction)',
|
||||
syntax: 'nop',
|
||||
example: 'nop # do nothing'
|
||||
},
|
||||
'xor': {
|
||||
description: 'Exclusive OR',
|
||||
syntax: 'xor rd, rs1, rs2',
|
||||
example: 'xor t0, t1, t2 # t0 = t1 ^ t2'
|
||||
},
|
||||
'or': {
|
||||
description: 'Bitwise OR',
|
||||
syntax: 'or rd, rs1, rs2',
|
||||
example: 'or t0, t1, t2 # t0 = t1 | t2'
|
||||
},
|
||||
'and': {
|
||||
description: 'Bitwise AND',
|
||||
syntax: 'and rd, rs1, rs2',
|
||||
example: 'and t0, t1, t2 # t0 = t1 & t2'
|
||||
},
|
||||
'sll': {
|
||||
description: 'Shift left logical',
|
||||
syntax: 'sll rd, rs1, rs2',
|
||||
example: 'sll t0, t1, t2 # t0 = t1 << t2'
|
||||
},
|
||||
'srl': {
|
||||
description: 'Shift right logical',
|
||||
syntax: 'srl rd, rs1, rs2',
|
||||
example: 'srl t0, t1, t2 # t0 = t1 >> t2 (unsigned)'
|
||||
},
|
||||
'sra': {
|
||||
description: 'Shift right arithmetic',
|
||||
syntax: 'sra rd, rs1, rs2',
|
||||
example: 'sra t0, t1, t2 # t0 = t1 >> t2 (signed)'
|
||||
}
|
||||
};
|
||||
|
||||
// Documentation des registres
|
||||
const REGISTER_DOCS: { [key: string]: { description: string; abi_name?: string } } = {
|
||||
'zero': { description: 'Always zero (x0)' },
|
||||
'ra': { description: 'Return address (x1)' },
|
||||
'sp': { description: 'Stack pointer (x2)' },
|
||||
'gp': { description: 'Global pointer (x3)' },
|
||||
'tp': { description: 'Thread pointer (x4)' },
|
||||
't0': { description: 'Temporary register 0 (x5)' },
|
||||
't1': { description: 'Temporary register 1 (x6)' },
|
||||
't2': { description: 'Temporary register 2 (x7)' },
|
||||
's0': { description: 'Saved register 0 / Frame pointer (x8)', abi_name: 'fp' },
|
||||
's1': { description: 'Saved register 1 (x9)' },
|
||||
'a0': { description: 'Argument/return value 0 (x10)' },
|
||||
'a1': { description: 'Argument/return value 1 (x11)' },
|
||||
'a2': { description: 'Argument register 2 (x12)' },
|
||||
'a3': { description: 'Argument register 3 (x13)' },
|
||||
'a4': { description: 'Argument register 4 (x14)' },
|
||||
'a5': { description: 'Argument register 5 (x15)' },
|
||||
'a6': { description: 'Argument register 6 (x16)' },
|
||||
'a7': { description: 'Argument register 7 (x17)' }
|
||||
};
|
||||
|
||||
// Documentation des directives
|
||||
const DIRECTIVE_DOCS: { [key: string]: { description: string; syntax: string; example: string } } = {
|
||||
'.text': {
|
||||
description: 'Start text (code) section',
|
||||
syntax: '.text',
|
||||
example: '.text # Code goes here'
|
||||
},
|
||||
'.data': {
|
||||
description: 'Start data section',
|
||||
syntax: '.data',
|
||||
example: '.data # Data goes here'
|
||||
},
|
||||
'.global': {
|
||||
description: 'Make symbol globally visible',
|
||||
syntax: '.global symbol',
|
||||
example: '.global main # main is visible to linker'
|
||||
},
|
||||
'.string': {
|
||||
description: 'Define null-terminated string',
|
||||
syntax: '.string "text"',
|
||||
example: '.string "Hello World"'
|
||||
},
|
||||
'.word': {
|
||||
description: 'Define 32-bit word',
|
||||
syntax: '.word value',
|
||||
example: '.word 42 # stores 32-bit value 42'
|
||||
},
|
||||
'.byte': {
|
||||
description: 'Define 8-bit byte',
|
||||
syntax: '.byte value',
|
||||
example: '.byte 0xFF # stores 8-bit value 255'
|
||||
}
|
||||
};
|
||||
|
||||
export class RiscvHoverProvider implements vscode.HoverProvider {
|
||||
provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult<vscode.Hover> {
|
||||
const range = document.getWordRangeAtPosition(position);
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const word = document.getText(range).toLowerCase();
|
||||
|
||||
// Vérifier les instructions
|
||||
if (INSTRUCTION_DOCS[word]) {
|
||||
const doc = INSTRUCTION_DOCS[word];
|
||||
const markdown = new vscode.MarkdownString();
|
||||
markdown.appendCodeblock(`${doc.syntax}`, 'riscv-asm');
|
||||
markdown.appendMarkdown(`**${doc.description}**\n\n`);
|
||||
markdown.appendCodeblock(`${doc.example}`, 'riscv-asm');
|
||||
return new vscode.Hover(markdown, range);
|
||||
}
|
||||
|
||||
// Vérifier les registres
|
||||
if (REGISTER_DOCS[word]) {
|
||||
const doc = REGISTER_DOCS[word];
|
||||
const markdown = new vscode.MarkdownString();
|
||||
markdown.appendMarkdown(`**Register:** \`${word}\`\n\n`);
|
||||
markdown.appendMarkdown(`${doc.description}`);
|
||||
if (doc.abi_name) {
|
||||
markdown.appendMarkdown(` (also known as \`${doc.abi_name}\`)`);
|
||||
}
|
||||
return new vscode.Hover(markdown, range);
|
||||
}
|
||||
|
||||
// Vérifier les directives
|
||||
if (DIRECTIVE_DOCS[word]) {
|
||||
const doc = DIRECTIVE_DOCS[word];
|
||||
const markdown = new vscode.MarkdownString();
|
||||
markdown.appendCodeblock(`${doc.syntax}`, 'riscv-asm');
|
||||
markdown.appendMarkdown(`**${doc.description}**\n\n`);
|
||||
markdown.appendCodeblock(`${doc.example}`, 'riscv-asm');
|
||||
return new vscode.Hover(markdown, range);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
15
src/test/extension.test.ts
Normal file
15
src/test/extension.test.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
136
syntaxes/riscv-asm.tmLanguage.json
Normal file
136
syntaxes/riscv-asm.tmLanguage.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "RISC-V Assembly",
|
||||
"scopeName": "source.riscv-asm",
|
||||
"patterns": [
|
||||
{"include": "#comments"},
|
||||
{"include": "#labels"},
|
||||
{"include": "#directives"},
|
||||
{"include": "#instructions"},
|
||||
{"include": "#registers"},
|
||||
{"include": "#numbers"},
|
||||
{"include": "#strings"}
|
||||
],
|
||||
"repository": {
|
||||
"comments": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "comment.line.number-sign.riscv-asm",
|
||||
"begin": "#",
|
||||
"end": "$"
|
||||
},
|
||||
{
|
||||
"name": "comment.block.riscv-asm",
|
||||
"begin": "/\\*",
|
||||
"end": "\\*/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"labels": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "entity.name.function.riscv-asm",
|
||||
"match": "^\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*:"
|
||||
}
|
||||
]
|
||||
},
|
||||
"directives": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.directive.riscv-asm",
|
||||
"match": "\\b(\\.text|\\.data|\\.bss|\\.section|\\.global|\\.globl|\\.extern|\\.equ|\\.set|\\.align|\\.balign|\\.word|\\.dword|\\.byte|\\.half|\\.ascii|\\.asciiz|\\.string|\\.zero|\\.space|\\.skip|\\.org|\\.include)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"instructions": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.mnemonic.arithmetic.riscv-asm",
|
||||
"match": "\\b(add|addi|sub|lui|auipc|xor|xori|or|ori|and|andi|sll|slli|srl|srli|sra|srai|slt|slti|sltu|sltiu)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.mnemonic.load-store.riscv-asm",
|
||||
"match": "\\b(lb|lh|lw|ld|lbu|lhu|lwu|sb|sh|sw|sd)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.mnemonic.branch.riscv-asm",
|
||||
"match": "\\b(beq|bne|blt|bge|bltu|bgeu|jal|jalr)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.mnemonic.system.riscv-asm",
|
||||
"match": "\\b(ecall|ebreak|csrrw|csrrs|csrrc|csrrwi|csrrsi|csrrci|fence|fence\\.i)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.mnemonic.multiplication.riscv-asm",
|
||||
"match": "\\b(mul|mulh|mulhsu|mulhu|div|divu|rem|remu)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.mnemonic.atomic.riscv-asm",
|
||||
"match": "\\b(lr\\.w|sc\\.w|lr\\.d|sc\\.d|amoswap|amoadd|amoxor|amoand|amoor|amomin|amomax|amominu|amomaxu)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.mnemonic.pseudoinstruction.riscv-asm",
|
||||
"match": "\\b(nop|li|la|mv|not|neg|seqz|snez|sltz|sgtz|j|jr|ret|call|tail)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"registers": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "variable.language.register.riscv-asm",
|
||||
"match": "\\b(x[0-9]|x[12][0-9]|x3[01]|zero|ra|sp|gp|tp|t[0-6]|s[0-9]|s1[01]|a[0-7]|fp)\\b"
|
||||
},
|
||||
{
|
||||
"name": "variable.language.register.floating.riscv-asm",
|
||||
"match": "\\b(f[0-9]|f[12][0-9]|f3[01]|ft[0-9]|ft1[01]|fs[0-9]|fs1[01]|fa[0-7])\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"numbers": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.numeric.hex.riscv-asm",
|
||||
"match": "\\b0[xX][0-9a-fA-F]+\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.binary.riscv-asm",
|
||||
"match": "\\b0[bB][01]+\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.octal.riscv-asm",
|
||||
"match": "\\b0[0-7]+\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.decimal.riscv-asm",
|
||||
"match": "\\b[0-9]+\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "string.quoted.double.riscv-asm",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.character.escape.riscv-asm",
|
||||
"match": "\\\\."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "string.quoted.single.riscv-asm",
|
||||
"begin": "'",
|
||||
"end": "'",
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.character.escape.riscv-asm",
|
||||
"match": "\\\\."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "ES2022",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"ES2022"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true, /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
}
|
||||
}
|
||||
44
vsc-extension-quickstart.md
Normal file
44
vsc-extension-quickstart.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Welcome to your VS Code Extension
|
||||
|
||||
## What's in the folder
|
||||
|
||||
* This folder contains all of the files necessary for your extension.
|
||||
* `package.json` - this is the manifest file in which you declare your extension and command.
|
||||
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
|
||||
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
|
||||
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
|
||||
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
|
||||
|
||||
## Get up and running straight away
|
||||
|
||||
* Press `F5` to open a new window with your extension loaded.
|
||||
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
|
||||
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
|
||||
* Find output from your extension in the debug console.
|
||||
|
||||
## Make changes
|
||||
|
||||
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
|
||||
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||
|
||||
## Explore the API
|
||||
|
||||
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
|
||||
|
||||
## Run tests
|
||||
|
||||
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
|
||||
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
|
||||
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
|
||||
* See the output of the test result in the Test Results view.
|
||||
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
|
||||
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
|
||||
* You can create folders inside the `test` folder to structure your tests any way you want.
|
||||
|
||||
## Go further
|
||||
|
||||
* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
|
||||
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
|
||||
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
|
||||
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
|
||||
* Integrate to the [report issue](https://code.visualstudio.com/api/get-started/wrapping-up#issue-reporting) flow to get issue and feature requests reported by users.
|
||||
Reference in New Issue
Block a user