diff options
| -rw-r--r-- | asm/ext_std.py (renamed from src/ext_std.py) | 0 | ||||
| -rw-r--r-- | asm/fibo.atk16 | 13 | ||||
| -rw-r--r-- | asm/ram_offset.atk16 | 2 | ||||
| -rw-r--r-- | atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix | bin | 2321 -> 0 bytes | |||
| -rw-r--r-- | atk16-syntax/README.md (renamed from atk16-syntax-highlighting/README.md) | 0 | ||||
| -rw-r--r-- | atk16-syntax/atk16-syntax-0.0.1.vsix | bin | 0 -> 4725 bytes | |||
| -rw-r--r-- | atk16-syntax/extension.js | 197 | ||||
| -rw-r--r-- | atk16-syntax/language-configuration.json (renamed from atk16-syntax-highlighting/language-configuration.json) | 0 | ||||
| -rw-r--r-- | atk16-syntax/package.json (renamed from atk16-syntax-highlighting/package.json) | 19 | ||||
| -rw-r--r-- | atk16-syntax/syntaxes/atk16.tmLanguage.json (renamed from atk16-syntax-highlighting/syntaxes/atk16.tmLanguage.json) | 6 | ||||
| -rw-r--r-- | atk16-syntax/themes/atk16-color-theme.json | 41 | ||||
| -rw-r--r-- | src/asm_pass0.py | 48 | ||||
| -rw-r--r-- | src/asm_pass1.py | 13 | ||||
| -rw-r--r-- | src/asm_pass2.py | 2 | ||||
| -rw-r--r-- | src/asm_pass3.py | 2 | ||||
| -rw-r--r-- | src/asm_pass4.py | 5 | ||||
| -rwxr-xr-x | src/assembler.py | 4 |
17 files changed, 331 insertions, 21 deletions
diff --git a/src/ext_std.py b/asm/ext_std.py index 11da8aa..11da8aa 100644 --- a/src/ext_std.py +++ b/asm/ext_std.py diff --git a/asm/fibo.atk16 b/asm/fibo.atk16 index 8bb7a6e..198ae8b 100644 --- a/asm/fibo.atk16 +++ b/asm/fibo.atk16 @@ -6,12 +6,11 @@ jpi program ; data segment -@label ram_offset - 0x8000 +@include ram_offset @label program ; set up stack pointer to point to beginning of RAM - ldi ram_offset RA + ldi ram_offset_addr RA ldr RA RF ; call fibo subroutine with parameter 10 @@ -20,11 +19,9 @@ hlt @label fibo -; function fibo -; parameters: -; RA n : u16 -; return value: -; RA fibo(n) : u16 +;? subroutine fibo +;? param RA N +;? return RA Nth fibonacci number ; store RB, RC on stack spu RB diff --git a/asm/ram_offset.atk16 b/asm/ram_offset.atk16 new file mode 100644 index 0000000..e8c9255 --- /dev/null +++ b/asm/ram_offset.atk16 @@ -0,0 +1,2 @@ +@label ram_offset_addr + 0x8000 diff --git a/atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix b/atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix Binary files differdeleted file mode 100644 index 5d4a42d..0000000 --- a/atk16-syntax-highlighting/atk16-syntax-highlighting-0.0.1.vsix +++ /dev/null diff --git a/atk16-syntax-highlighting/README.md b/atk16-syntax/README.md index da81144..da81144 100644 --- a/atk16-syntax-highlighting/README.md +++ b/atk16-syntax/README.md diff --git a/atk16-syntax/atk16-syntax-0.0.1.vsix b/atk16-syntax/atk16-syntax-0.0.1.vsix Binary files differnew file mode 100644 index 0000000..b6aa9dc --- /dev/null +++ b/atk16-syntax/atk16-syntax-0.0.1.vsix diff --git a/atk16-syntax/extension.js b/atk16-syntax/extension.js new file mode 100644 index 0000000..e49dd07 --- /dev/null +++ b/atk16-syntax/extension.js @@ -0,0 +1,197 @@ +const vscode = require('vscode'); +const path = require('path'); + +class ATK16HoverProvider { + async provideHover(document, position, token) { + const range = document.getWordRangeAtPosition(position, /[\w-/.]+/); + const labelText = document.getText(range); + const subroutineDefinition = await findSubroutineInDocument(document, labelText); + if (subroutineDefinition) { + const hoverText = new vscode.MarkdownString(subroutineDefinition); + hoverText.isTrusted = true; + return new vscode.Hover(hoverText); + } + + // Search for labels in included files + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i); + const includeMatch = line.text.match(/@include\s+(\S+)/); + if (includeMatch) { + const includedFilePath = path.join( + path.dirname(document.fileName), + `${includeMatch[1]}.atk16` + ); + + if (await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath))) { + const includedDocument = await vscode.workspace.openTextDocument( + includedFilePath + ); + const subroutineDefinition = await findSubroutineInDocument(includedDocument, labelText); + if (subroutineDefinition) { + const hoverText = new vscode.MarkdownString(subroutineDefinition); + hoverText.isTrusted = true; + return new vscode.Hover(hoverText); + } + } + } + } + + return null; + } +} + +class ATK16DefinitionProvider { + async provideDefinition(document, position, token) { + const range = document.getWordRangeAtPosition(position, /[\w-/.]+/); + const text = document.getText(range); + + const line = document.lineAt(position); + const includeMatch = line.text.match(/@include\s+(\S+)/); + const useMatch = line.text.match(/@use\s+(\S+):/); + + if (includeMatch && includeMatch[1] === text) { + const includedFilePath = path.join( + path.dirname(document.fileName), + `${includeMatch[1]}.atk16` + ); + + try { + await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath)); + return new vscode.Location( + vscode.Uri.file(includedFilePath), + new vscode.Position(0, 0) + ); + } catch (err) { + vscode.window.showErrorMessage( + `ATK16: File not found: ${includedFilePath}` + ); + } + } else if (useMatch && useMatch[1] === text) { + const includedFilePath = path.join( + path.dirname(document.fileName), + `${useMatch[1]}.py` + ); + + try { + await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath)); + return new vscode.Location( + vscode.Uri.file(includedFilePath), + new vscode.Position(0, 0) + ); + } catch (err) { + vscode.window.showErrorMessage( + `ATK16: File not found: ${includedFilePath}` + ); + } + } else { + const labelText = document.getText(range); + let labelLocation = await findLabelInDocument(document, labelText); + if (labelLocation) { + return labelLocation; + } + + // Search for labels in included files + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i); + const includeMatch = line.text.match(/@include\s+(\S+)/); + if (includeMatch) { + const includedFilePath = path.join( + path.dirname(document.fileName), + `${includeMatch[1]}.atk16` + ); + + if (await vscode.workspace.fs.stat(vscode.Uri.file(includedFilePath))) { + const includedDocument = await vscode.workspace.openTextDocument( + includedFilePath + ); + labelLocation = await findLabelInDocument(includedDocument, labelText); + if (labelLocation) { + return labelLocation; + } + } + } + } + } + + return null; + } +} + +async function findLabelInDocument(document, labelText) { + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i); + if (line.text.includes(`@label ${labelText}`)) { + return new vscode.Location(document.uri, line.range.start); + } + } + return null; +} + +async function findSubroutineInDocument(document, labelText) { + let name = null; + const params = []; + const returns = []; + let extra = ""; + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i); + if (line.text.includes(`@label ${labelText}`)) { + let labelDefinition = ''; + for (let j = i + 1; j < document.lineCount; j++) { + const docLine = document.lineAt(j); + const docCommentMatch = docLine.text.match(/;\? (.*)/); + if (docCommentMatch) { + const lineText = docCommentMatch[1]; + const parts = lineText.split(/\s/); + if (lineText.startsWith('subroutine')) { + name = parts[parts.length - 1]; + } else if (lineText.startsWith('param')) { + const [_, reg, ...desc] = parts; + params.push([reg, desc.join(" ")]) + } else if (lineText.startsWith('return')) { + const [_, reg, ...desc] = parts; + returns.push([reg, desc.join(" ")]) + } else { + extra += `${lineText}\n`; + } + } else { + break; + } + } + } + } + + if (name === null) return null; + + let definition = `### ${name}\n_Subroutine_\n\n`; + definition += extra; + if (params.length > 0) { + definition += "\n\n|Reg|Parameter|\n"; + definition += "|:---|:---|\n"; + } + for (const [reg, desc] of params) { + definition += `|${reg}|${desc}|\n`; + } + + if (returns.length > 0) { + definition += "\n|Reg|Return|\n"; + definition += "|:---|:---|\n"; + } + for (const [reg, desc] of returns) { + definition += `|${reg}|${desc}|\n`; + } + return definition; +} + +function activate(context) { + context.subscriptions.push( + vscode.languages.registerDefinitionProvider('atk16', new ATK16DefinitionProvider()), + vscode.languages.registerHoverProvider('atk16', new ATK16HoverProvider()) + ); +} + +function deactivate() {} + +module.exports = { + activate, + deactivate, +};
\ No newline at end of file diff --git a/atk16-syntax-highlighting/language-configuration.json b/atk16-syntax/language-configuration.json index c186b1b..c186b1b 100644 --- a/atk16-syntax-highlighting/language-configuration.json +++ b/atk16-syntax/language-configuration.json diff --git a/atk16-syntax-highlighting/package.json b/atk16-syntax/package.json index ff6be2b..bdfbb74 100644 --- a/atk16-syntax-highlighting/package.json +++ b/atk16-syntax/package.json @@ -1,13 +1,17 @@ { - "name": "atk16-syntax-highlighting", - "displayName": "ATK16 Syntax Highlighting", - "description": "Syntax highlighting for ATK16 assembly language", + "name": "atk16-syntax", + "displayName": "ATK16 Syntax", + "description": "Syntax highlighting and support for ATK16 assembly language", "version": "0.0.1", - "publisher": "Jan Tuomi", + "publisher": "JanTuomi", "engines": { "vscode": "^1.60.0" }, "categories": ["Programming Languages"], + "activationEvents": [ + "onLanguage:atk16" + ], + "main": "./extension.js", "contributes": { "languages": [ { @@ -23,6 +27,13 @@ "scopeName": "source.atk16", "path": "./syntaxes/atk16.tmLanguage.json" } + ], + "themes": [ + { + "label": "ATK16 Color Theme", + "uiTheme": "vs-dark", + "path": "./themes/atk16-color-theme.json" + } ] } } diff --git a/atk16-syntax-highlighting/syntaxes/atk16.tmLanguage.json b/atk16-syntax/syntaxes/atk16.tmLanguage.json index 6028be3..1faadf9 100644 --- a/atk16-syntax-highlighting/syntaxes/atk16.tmLanguage.json +++ b/atk16-syntax/syntaxes/atk16.tmLanguage.json @@ -3,12 +3,16 @@ "name": "ATK16", "patterns": [ { + "name": "comment.documentation.atk16", + "match": ";\\?.*$" + }, + { "name": "comment.line.semicolon.atk16", "match": ";.*" }, { "name": "keyword.control.directive.atk16", - "match": "@\\w+" + "match": "^@\\w+" }, { "name": "variable.other.register.atk16", diff --git a/atk16-syntax/themes/atk16-color-theme.json b/atk16-syntax/themes/atk16-color-theme.json new file mode 100644 index 0000000..d1aebb9 --- /dev/null +++ b/atk16-syntax/themes/atk16-color-theme.json @@ -0,0 +1,41 @@ +{ + "name": "ATK16", + "type": "dark", + "colors": { + "editor.background": "#1E1E1E", + "editor.foreground": "#D4D4D4", + "editor.lineHighlightBackground": "#2D2D30" + }, + "tokenColors": [ + { + "scope": "keyword.control.directive.atk16", + "settings": {} + }, + { + "scope": "variable.parameter.register.atk16", + "settings": {} + }, + { + "scope": "constant.numeric.binary.atk16", + "settings": {} + }, + { + "scope": "keyword.control.instruction.atk16", + "settings": {} + }, + { + "scope": "support.function.atk16", + "settings": {} + }, + { + "scope": "entity.name.label.atk16", + "settings": {} + }, + { + "scope": "comment.documentation.atk16", + "settings": { + "fontStyle": "italic" + } + } + ] +}
\ No newline at end of file diff --git a/src/asm_pass0.py b/src/asm_pass0.py new file mode 100644 index 0000000..be6c46a --- /dev/null +++ b/src/asm_pass0.py @@ -0,0 +1,48 @@ +from dataclasses import dataclass +from asm_ops import * +from asm_eval import * + +@dataclass +class Result0Line: + line_num: int + src_file: str + line: str + +@dataclass +class Result0: + lines: list[Result0Line] + options: Options + operations: OpExpansionDict + +def pass_0(lines: list[str], file_name: str) -> Result0: + options = Options() + result_lines: list[Result0Line] = [] + operations: OpExpansionDict = default_expansions.copy() + + for (line_num, line) in enumerate(lines): + line = line.split(";")[0].strip() + if line == "": continue + keyword, *args = line.lower().split() + match keyword: + case "@include": + asm_file_name = args[0] + with open(asm_file_name, "r") as f: + for incl_line in f.readlines(): + result_lines.append(Result0Line( + src_file=asm_file_name, + line_num=line_num, + line=incl_line + )) + + case _: + result_lines.append(Result0Line( + src_file=file_name, + line_num=line_num, + line=line + )) + + return Result0( + operations=operations, + options=options, + lines=result_lines + ) diff --git a/src/asm_pass1.py b/src/asm_pass1.py index 606a1a1..fee72ff 100644 --- a/src/asm_pass1.py +++ b/src/asm_pass1.py @@ -2,10 +2,12 @@ import importlib from dataclasses import dataclass from asm_ops import * from asm_eval import * +from asm_pass0 import * @dataclass class Result1Line: line_num: int + src_file: str parts: list[str] @dataclass @@ -14,15 +16,13 @@ class Result1: options: Options operations: OpExpansionDict -def pass_1(lines: list[str]) -> Result1: +def pass_1(result0: Result0) -> Result1: options = Options() result_lines: list[Result1Line] = [] operations: OpExpansionDict = default_expansions.copy() - for (line_num, line) in enumerate(lines): - line = line.split(";")[0].strip() - if line == "": continue - keyword, *args = line.lower().split() + for line in result0.lines: + keyword, *args = line.line.lower().split() match keyword: case "@opt": opt_name, opt_value = args @@ -41,7 +41,8 @@ def pass_1(lines: list[str]) -> Result1: operations[op] = expansion case _: result_lines.append(Result1Line( - line_num=line_num, + src_file=line.src_file, + line_num=line.line_num, parts=[keyword, *args] )) diff --git a/src/asm_pass2.py b/src/asm_pass2.py index e7f48ac..7272dcf 100644 --- a/src/asm_pass2.py +++ b/src/asm_pass2.py @@ -6,6 +6,7 @@ from asm_pass1 import * @dataclass class Result2Line: line_num: int + src_file: str parts: list[str] original_parts: list[str] @@ -30,6 +31,7 @@ def pass_2(result1: Result1) -> Result2: for (idx, parts) in enumerate(output): result_lines.append(Result2Line( line_num=line.line_num, + src_file=line.src_file, parts=parts, original_parts=line.parts if idx == 0 else ["..."] )) diff --git a/src/asm_pass3.py b/src/asm_pass3.py index 038e3f7..76e7b84 100644 --- a/src/asm_pass3.py +++ b/src/asm_pass3.py @@ -6,6 +6,7 @@ from asm_pass2 import * @dataclass class Result3Line: line_num: int + src_file: str address: int parts: list[str] original_parts: list[str] @@ -34,6 +35,7 @@ def pass_3(result2: Result2) -> Result3: case _: result_lines.append(Result3Line( line_num=line.line_num, + src_file=line.src_file, parts=line.parts, address=address, original_parts=line.original_parts, diff --git a/src/asm_pass4.py b/src/asm_pass4.py index 3d4f45c..dc65b65 100644 --- a/src/asm_pass4.py +++ b/src/asm_pass4.py @@ -6,6 +6,7 @@ from asm_pass3 import * @dataclass class Result4Line: line_num: int + src_file: str address: int word: int text: str @@ -43,6 +44,7 @@ def pass_4(result3: Result3) -> Result4: word = fn(meta, result3.labels, *args) result_lines.append(Result4Line( line_num=line.line_num, + src_file=line.src_file, address=line.address, word=word, text=text, @@ -52,13 +54,14 @@ def pass_4(result3: Result3) -> Result4: try: result_lines.append(Result4Line( line_num=line.line_num, + src_file=line.src_file, address=line.address, word=eval_expr(result3.labels, keyword), text=text, original_text=original_text, )) except: - raise Exception(f"Invalid assembly at {line.line_num + 1}\n\n{line}") + raise Exception(f"Invalid assembly at {line.src_file}:{line.line_num + 1}\n\n{line}") address += 1 diff --git a/src/assembler.py b/src/assembler.py index 3f0c8bc..8f84288 100755 --- a/src/assembler.py +++ b/src/assembler.py @@ -4,6 +4,7 @@ import sys from asm_ops import * from asm_eval import * +from asm_pass0 import pass_0 from asm_pass1 import pass_1 from asm_pass2 import pass_2 from asm_pass3 import pass_3 @@ -49,7 +50,8 @@ def parse(line: str) -> list[str]: result.append(acc) return list(filter(lambda x: len(x) > 0, result)) -result1 = pass_1(src_lines) +result0 = pass_0(src_lines, infile_path) +result1 = pass_1(result0) result2 = pass_2(result1) result3 = pass_3(result2) result4 = pass_4(result3) |
