├── .gitignore ├── COPYING ├── README.md └── uxntal-mode.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uxntal-mode 2 | 3 | Emacs major mode for the [uxntal](https://wiki.xxiivv.com/site/uxntal.html) assembly language. 4 | 5 | [![MELPA](https://melpa.org/packages/uxntal-mode-badge.svg)](https://melpa.org/#/uxntal-mode) 6 | 7 | ## installing the mode 8 | 9 | ### with use-package 10 | 11 | ```elisp 12 | (use-package uxntal-mode) 13 | ``` 14 | 15 | ### with straight.el 16 | 17 | ```elisp 18 | (straight-use-package 'uxntal-mode) 19 | ``` 20 | 21 | ### manually 22 | 23 | Copy `uxntal-mode.el` to the desired location and then modify `init.el`: 24 | 25 | ```elisp 26 | ;; ensure the directory containing uxntal-mode.el is mentioned 27 | ;; in emacs' load-path variable. 28 | (add-to-list 'load-path "~/.emacs.d/lisp/") 29 | 30 | ;; then load uxntal-mode 31 | (require 'uxntal-mode) 32 | ``` 33 | 34 | ## features 35 | 36 | Currently-supported features: 37 | 38 | * Syntax highlighting 39 | * Invoking `uxnasm` via `M-x compile` 40 | * Explaining Uxntal words (`uxntal-explain-word`) 41 | + Decodes instructions, showing their stack effects 42 | + Shows decimal values of numeric constants 43 | + Explains syntactic category (e.g. "sublabel definition") 44 | * Imenu support for macro definitions and labels 45 | 46 | Future features: 47 | 48 | * Interactive evaluation 49 | * Support for goto-definition (`M-.`) 50 | * Decimal -> hexadecimal conversions 51 | * ASCII conversions 52 | * Input string literal as bytes 53 | 54 | ## settings 55 | 56 | By default `uxntal-mode` is lax about comment highlighting. This means that some 57 | invalid comments such as `(this)` or `(that )` or `( these)` will be highlighted 58 | incorrectly. 59 | 60 | If you would prefer to have stricter comment highlighting which forbids all 61 | invalid comments (but may also forbid valid comments like `( )`) set 62 | `uxntal-mode-strict-comments` to `t`. 63 | 64 | Unfortunately both modes fail on words like `worst-case-(` due to limitations 65 | in how Emacs handles multiline comments. 66 | 67 | ## screenshot 68 | 69 | ![uxntal-mode screenshot](http://plastic-idolatry.com/erik/nxu/tal-mode1.png) 70 | 71 | ## attribution 72 | 73 | Copyright d_m, 2022. 74 | 75 | This code is available to you under the 76 | [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt). 77 | See COPYING for more details. 78 | 79 | ## see also 80 | 81 | * https://github.com/xaderfos/uxntal-mode 82 | * https://github.com/rafapaezbas/uxntal-mode 83 | -------------------------------------------------------------------------------- /uxntal-mode.el: -------------------------------------------------------------------------------- 1 | ;;; uxntal-mode.el --- Major mode for Uxntal assembly -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (c) 2022 d_m 4 | 5 | ;; Author: d_m 6 | ;; Homepage: https://github.com/non/uxntal-mode 7 | ;; Version: 0.2 8 | ;; Package-Requires: ((emacs "27.1")) 9 | ;; SPDX-License-Identifier: Apache-2.0 10 | 11 | ;;; Commentary: 12 | 13 | ;; This major mode supports writing the Uxntal assmembly langauge as documented 14 | ;; at https://wiki.xxiivv.com/site/uxntal.html. 15 | 16 | ;;; Code: 17 | 18 | ;; use rx for regular expressions 19 | (require 'rx) 20 | (require 'seq) 21 | 22 | ;; open .tal files with this mode 23 | ;;;###autoload 24 | (add-to-list 'auto-mode-alist '("\\.tal\\'" . uxntal-mode)) 25 | 26 | ;; macro definitions like %MOD 27 | (defconst uxntal-mode-macro-define-re 28 | (rx (group bow "%" (1+ (not (in space))) eow))) 29 | 30 | ;; includes like ~util.tal 31 | (defconst uxntal-mode-include-re 32 | (rx (group bow "~" (1+ (not (in space))) eow))) 33 | 34 | ;; labels like @foo 35 | (defconst uxntal-mode-label-define-re 36 | (rx (group bow "@" (1+ (not (in space))) eow))) 37 | 38 | ;; subabels like &bar 39 | (defconst uxntal-mode-sublabel-define-re 40 | (rx (group bow "&" (1+ (not (in space))) eow))) 41 | 42 | ;; raw strings like "x or "foo or "a-b-c-d-e 43 | (defconst uxntal-mode-raw-str-re 44 | (rx (group bow "\"" (1+ (in "!-~")) eow))) 45 | 46 | ;; absolute pads like |a0 or |0100 47 | (defconst uxntal-mode-absolute-pad-re 48 | (rx (group 49 | bow "|" 50 | (repeat 2 (in "0-9a-f")) 51 | (\? (repeat 2 (in "0-9a-f"))) 52 | eow))) 53 | 54 | ;; pads like $1 $1f $300 $1000 55 | (defconst uxntal-mode-relative-pad-re 56 | (rx (group bow "$" (repeat 1 4 (in "0-9a-f")) eow))) 57 | 58 | ;; addresses such as .foo ,bar ;baz :qux 59 | (defconst uxntal-mode-addr-zpg-re 60 | (rx (group bow "." (1+ (not (in space))) eow))) 61 | (defconst uxntal-mode-addr-rel-re 62 | (rx (group bow "," (1+ (not (in space))) eow))) 63 | (defconst uxntal-mode-addr-abs-re 64 | (rx (group bow ";" (1+ (not (in space))) eow))) 65 | (defconst uxntal-mode-addr-raw-zpg-re 66 | (rx (group bow "-" (1+ (not (in space))) eow))) 67 | (defconst uxntal-mode-addr-raw-rel-re 68 | (rx (group bow "_" (1+ (not (in space))) eow))) 69 | (defconst uxntal-mode-addr-raw-abs-re 70 | (rx (group bow "=" (1+ (not (in space))) eow))) 71 | (defconst uxntal-mode-addr-raw-abs-re-legacy 72 | (rx (group bow ":" (1+ (not (in space))) eow))) 73 | 74 | ;; literal numbers like #ff or #abcd 75 | (defconst uxntal-mode-number-re 76 | (rx (group 77 | bow "#" 78 | (repeat 2 (in "0-9a-f")) 79 | (\? (repeat 2 (in "0-9a-f"))) 80 | eow))) 81 | 82 | ;; raw numbers like ff or abcd 83 | (defconst uxntal-mode-raw-number-re 84 | (rx (group 85 | bow 86 | (repeat 2 (in "0-9a-f")) 87 | (\? (repeat 2 (in "0-9a-f"))) 88 | eow))) 89 | 90 | ;; jci: jump conditional instant 91 | (defconst uxntal-mode-jci-re 92 | (rx (group bow "?" (1+ (not (in space))) eow))) 93 | 94 | ;; jmi: jump instant 95 | (defconst uxntal-mode-jmi-re 96 | (rx (group bow "!" (1+ (not (in space))) eow))) 97 | 98 | ;; jsi: jump stash return instant 99 | (defconst uxntal-mode-jsi-re 100 | (rx (group bow (1+ (not (in space))) eow))) 101 | 102 | ;; tal instructions like ADD or JMP2r 103 | (defconst uxntal-mode-inst-re 104 | (rx (group bow 105 | (or "BRK" 106 | (group "LIT" (\? "2") (\? "r")) 107 | (group (or "INC" "POP" "DUP" "NIP" "SWP" "OVR" "ROT" 108 | "EQU" "NEQ" "GTH" "LTH" 109 | "JMP" "JCN" "JSR" "STH" 110 | "LDZ" "STZ" "LDR" "STR" "LDA" "STA" 111 | "DEI" "DEO" 112 | "ADD" "SUB" "MUL" "DIV" 113 | "AND" "ORA" "EOR" "SFT") 114 | (\? "2") (\? "k") (\? "r"))) 115 | eow))) 116 | 117 | ;; all previous rules joined together into a list 118 | (defconst uxntal-font-lock-keywords-1 119 | (list 120 | ;; macros (%) 121 | (list uxntal-mode-macro-define-re 1 font-lock-keyword-face) 122 | ;; addresses (. , ; :) 123 | (list uxntal-mode-addr-zpg-re 1 font-lock-variable-name-face) 124 | (list uxntal-mode-addr-rel-re 1 font-lock-variable-name-face) 125 | (list uxntal-mode-addr-abs-re 1 font-lock-variable-name-face) 126 | (list uxntal-mode-addr-raw-zpg-re 1 font-lock-variable-name-face) 127 | (list uxntal-mode-addr-raw-rel-re 1 font-lock-variable-name-face) 128 | (list uxntal-mode-addr-raw-abs-re 1 font-lock-variable-name-face) 129 | (list uxntal-mode-addr-raw-abs-re-legacy 1 font-lock-variable-name-face) 130 | ;; labels (@ &) 131 | (list uxntal-mode-label-define-re 1 font-lock-function-name-face) 132 | (list uxntal-mode-sublabel-define-re 1 font-lock-function-name-face) 133 | ;; padding (| $) 134 | (list uxntal-mode-absolute-pad-re 1 font-lock-preprocessor-face) 135 | (list uxntal-mode-relative-pad-re 1 font-lock-preprocessor-face) 136 | ;; includes (~) 137 | (list uxntal-mode-include-re 1 font-lock-preprocessor-face) 138 | ;; instructions 139 | (list uxntal-mode-inst-re 1 font-lock-builtin-face) 140 | ;; constant numbers (#) 141 | (list uxntal-mode-number-re 1 font-lock-constant-face) 142 | ;; raw values (") 143 | (list uxntal-mode-raw-number-re 1 font-lock-string-face) 144 | (list uxntal-mode-raw-str-re 1 font-lock-string-face) 145 | ;; immediate jumps (? ! []) 146 | (list uxntal-mode-jci-re 1 font-lock-keyword-face) 147 | (list uxntal-mode-jmi-re 1 font-lock-keyword-face) 148 | (list uxntal-mode-jsi-re 1 font-lock-keyword-face)) 149 | "Level one font lock.") 150 | 151 | ;; create the syntax table which powers some highlighting decisions. 152 | ;; 153 | ;; since it is not possible to exactly encode uxntal's rules for 154 | ;; comments, we have to choose between strict and non-strict 155 | ;; highlighting. 156 | ;; 157 | ;; when strict, valid comments such as "( )" or "()" 158 | ;; will be incorrectly rejected. 159 | ;; 160 | ;; when non-strict, invalid comments such as "(hi )" or "( bye)" 161 | ;; will be incorrectly accepted. 162 | (defun uxntal-create-syntax-table (strict) 163 | "Create a syntax table. The STRICT parameter determines whether to use strict or lax parsing for comments." 164 | (let ((table (make-syntax-table)) 165 | (c 0)) 166 | ;; treat characters <= space as whitespace 167 | (while (< c ?!) 168 | (modify-syntax-entry c " " table) 169 | (setq c (1+ c))) 170 | ;; treat almost all printable characters as word characters 171 | (while (< c 127) 172 | (modify-syntax-entry c "w" table) 173 | (setq c (1+ c))) 174 | ;; when strict, we require '(' and ')' to have whitespace padding. 175 | ;; this is typically ok but fails on things like "( )" which 176 | ;; should be valid comments but would not highlight correctly. 177 | (if strict 178 | (progn 179 | (modify-syntax-entry ?\( "()1nb" table) 180 | (modify-syntax-entry ?\) ")(4nb" table) 181 | (modify-syntax-entry ?\s " 123" table) 182 | (modify-syntax-entry ?\t " 123" table) 183 | (modify-syntax-entry ?\n " 123" table)) 184 | (progn 185 | (modify-syntax-entry ?\( "<)nb" table) 186 | (modify-syntax-entry ?\) ">(nb" table))) 187 | ;; generic delimiters, ignored by uxnasm 188 | (modify-syntax-entry ?\[ "(]" table) 189 | (modify-syntax-entry ?\] ")[" table) 190 | (modify-syntax-entry ?\{ "(}" table) 191 | (modify-syntax-entry ?\} "){" table) 192 | ;; return the syntax table 193 | table)) 194 | 195 | (defcustom uxntal-mode-strict-comments nil 196 | "When non-nil, will parse comments strictly, ensuring invalid comments are rejected. Otherwise, comments are parsed permissively, ensuring valid comments are accepted." 197 | :type 'boolean 198 | :safe #'booleanp 199 | :group 'uxntal) 200 | 201 | (defcustom uxntal-uxnasm-path "uxnasm" 202 | "Path to run uxnasm assembler command." 203 | :type 'string 204 | :safe #'stringp 205 | :group 'uxntal) 206 | 207 | (defcustom uxntal-uxnemu-path "uxnemu" 208 | "Path to run uxnemu emulator command." 209 | :type 'string 210 | :safe #'stringp 211 | :group 'uxntal) 212 | 213 | (defcustom uxntal-uxnemu-args nil 214 | "Arguments to pass to the uxnemu command." 215 | :type '(repeat string) 216 | :group 'uxntal) 217 | 218 | (defvar uxntal-mode-syntax-table 219 | (uxntal-create-syntax-table uxntal-mode-strict-comments) 220 | "Syntax table in use in `uxntal-mode' buffers.") 221 | 222 | (defconst uxntal-imenu-generic-expression 223 | (list (list nil uxntal-mode-label-define-re 1) 224 | (list nil uxntal-mode-macro-define-re 1)) 225 | "Expressions for navigating Uxntal with imenu.") 226 | 227 | (defun uxntal-indent-line () 228 | "Indent line by inserting a tab character." 229 | (interactive) 230 | (if indent-tabs-mode 231 | (insert "\t") 232 | (insert (make-string tab-width ?\s)))) 233 | 234 | ;; calculcate a ROM filename from the buffer 235 | (defun uxntal-calculate-rom-path () 236 | (let ((name (file-relative-name buffer-file-name))) 237 | (concat (file-name-sans-extension name) ".rom"))) 238 | 239 | ;; set M-x compile to call uxnasm 240 | (defun uxntal-setup-compile-command () 241 | "Set the current buffer to compile to a ROM using uxnasm." 242 | (let ((loc (file-relative-name buffer-file-name)) 243 | (rom (uxntal-calculate-rom-path))) 244 | (set (make-local-variable 'compile-command) 245 | (concat uxntal-uxnasm-path " " loc " " rom)))) 246 | 247 | ;; run uxnasm and then uxnemu 248 | (defun uxntal-compile-and-run () 249 | (interactive) 250 | (compile compile-command) 251 | (let* ((rom (uxntal-calculate-rom-path)) 252 | (args (append (list "uxnemu" nil uxntal-uxnemu-path) 253 | uxntal-uxnemu-args 254 | (list rom)))) 255 | (apply 'start-process args))) 256 | 257 | ;;;###autoload 258 | (define-derived-mode uxntal-mode prog-mode "Uxntal" 259 | "Major mode for editing Uxntal files." 260 | (set-syntax-table (uxntal-create-syntax-table uxntal-mode-strict-comments)) 261 | (uxntal-setup-compile-command) 262 | (add-hook 'uxntal-mode-hook 263 | (lambda () 264 | (set (make-local-variable 'font-lock-defaults) '(uxntal-font-lock-keywords-1 nil nil)) 265 | (set (make-local-variable 'comment-start) "( ") 266 | (set (make-local-variable 'comment-end) " )") 267 | (set (make-local-variable 'comment-quote-nested) nil) 268 | (set (make-local-variable 'indent-line-function) #'uxntal-indent-line) 269 | (set (make-local-variable 'imenu-generic-expression) uxntal-imenu-generic-expression)))) 270 | 271 | ;; Constructs a table of metadata about every instruction. 272 | ;; This table powers uxntal-decode-instruction. 273 | (defconst uxntal-mode-instructions 274 | (let ((m (make-hash-table :test 'equal :size 32))) 275 | ;; stack opcodes 276 | (puthash "BRK" (vector "Break" '(() . ()) nil "halt the program") m) 277 | (puthash "LIT" (vector "Literal" '(() . ("a")) nil "push the next value onto the stack") m) 278 | (puthash "INC" (vector "Increment" '(("a") . ("b")) nil "adds one to the top of the stack") m) 279 | (puthash "POP" (vector "Pop" '(("a") . ()) nil "remove the top of the stack") m) 280 | (puthash "NIP" (vector "Nip" '(("a" "b") . ("b")) nil "remove the second value (a)") m) 281 | (puthash "SWP" (vector "Swap" '(("a" "b") . ("b" "a")) nil "swap the top two stack values") m) 282 | (puthash "ROT" (vector "Rotate" '(("a" "b" "c") . ("b" "c" "a")) nil "rotate the top three values to the left") m) 283 | (puthash "DUP" (vector "Duplicate" '(("a") . ("a" "a")) nil "duplicate the top of the stack") m) 284 | (puthash "OVR" (vector "Over" '(("a" "b") . ("a" "b" "a")) nil "duplicate the second value (a) to the top of the stack") m) 285 | ;; logic opcodes 286 | (puthash "EQU" (vector "Equal" '(("a" "b") . ("bool^")) nil "push 01 if a == b; push 00 otherwise") m) 287 | (puthash "NEQ" (vector "Not Equal" '(("a" "b") . ("bool^")) nil "push 01 if a != b; push 00 otherwise") m) 288 | (puthash "GTH" (vector "Greater Than" '(("a" "b") . ("bool^")) nil "push 01 if a > b; push 00 otherwise") m) 289 | (puthash "LTH" (vector "Less Than" '(("a" "b") . ("bool^")) nil "push 01 if a < b; push 00 otherwise") m) 290 | (puthash "JMP" (vector "Jump" '(("addr") . ()) nil "modify the pc using addr") m) 291 | (puthash "JCN" (vector "Jump Conditional" '(("bool^" "addr") . ()) nil "if bool != 00, modify the pc using addr") m) 292 | (puthash "JSR" (vector "Jump Stash Return" '(("addr") . ()) '(() . ("pc")) "store pc onto return stack; modify pc using addr") m) 293 | (puthash "STH" (vector "Stash" '(("a") . ()) '(() . ("a")) "move the top of the stack to the return stack") m) 294 | ;; memory opcodes 295 | (puthash "LDZ" (vector "Load Zero-Page" '(("addr^") . ("val")) nil "load value from first 256 bytes of memory onto the stack") m) 296 | (puthash "STZ" (vector "Store Zero-Page" '(("val" "addr^") . ()) nil "write top of stack into the first 256 bytes of memory") m) 297 | (puthash "LDR" (vector "Load Relative" '(("addr^") . ("val")) nil "load relative address onto the stack") m) 298 | (puthash "STR" (vector "Store Relative" '(("val" "addr^") . ()) nil "write top of stack to relative address") m) 299 | (puthash "LDA" (vector "Load Absolute" '(("addr*") . ("val")) nil "load absolute address onto the stack") m) 300 | (puthash "STA" (vector "Store Absolute" '(("val" "addr*") . ()) nil "write top of stack to absolute address") m) 301 | (puthash "DEI" (vector "Device In" '(("addr^") . ("val")) nil "load from the given device onto the stack") m) 302 | (puthash "DEO" (vector "Device Out" '(("val" "addr^") . ()) nil "write top of stack to the given device") m) 303 | ;; arithmetic opcodes 304 | (puthash "ADD" (vector "Add" '(("a" "b") . ("a+b")) nil "addition (a + b)") m) 305 | (puthash "SUB" (vector "Subtract" '(("a" "b") . ("a-b")) nil "subtraction (a - b)") m) 306 | (puthash "MUL" (vector "Multiply" '(("a" "b") . ("a*b")) nil "multiplication (a * b)") m) 307 | (puthash "DIV" (vector "Divide" '(("a" "b") . ("a/b")) nil "division (a / b)") m) 308 | (puthash "AND" (vector "And" '(("a" "b") . ("a&b")) nil "bitwise-and (a & b)") m) 309 | (puthash "ORA" (vector "Or" '(("a" "b") . ("a|b")) nil "bitwise-or (a | b)") m) 310 | (puthash "EOR" (vector "Exclusive Or" '(("a" "b") . ("a^b")) nil "bitwise-xor (a ^ b)") m) 311 | (puthash "SFT" (vector "Shift" '(("a" "b^") . ("c")) nil "bitshift right (b & 0xf) then left (b >> 4)") m) 312 | m)) 313 | 314 | (defun uxntal-format-stack (pair glyph) 315 | "Format the given stack PAIR as stack effects using GLYPH. Stacks are represented as a pair of lists for input and output parameters respectively. We apply GLYPH to any parameter that doesn't already have a suffix denoting its type ('^' for 8-bit values, '*' for 16-bit values)." 316 | (let* ((decorate (lambda (name) (if (or (string-suffix-p "^" name) 317 | (string-suffix-p "*" name)) 318 | name 319 | (concat name glyph)))) 320 | (ins (mapconcat decorate (car pair) " ")) 321 | (outs (mapconcat decorate (cdr pair) " "))) 322 | (format "%s -> %s" ins outs))) 323 | 324 | (defun uxntal-setup-keep (pair) 325 | "Translate the given stack PAIR for keep mode. This involves prepending the input parameters (i.e. the first list) to the output parameters (the second list)." 326 | (let ((in (car pair)) 327 | (out (cdr pair))) 328 | (cons in (append in out)))) 329 | 330 | (defun uxntal-decode-instruction (inst) 331 | "Decode the meaning of the INST instruction. Instructions are always three capital letters followed by a suffix involving '2', 'k', and/or 'r'." 332 | (let ((m (string-match uxntal-mode-inst-re inst))) 333 | (if (not m) 334 | (message "`%s' is not an instruction" inst) 335 | (let* ((base (substring inst 0 3)) 336 | (inst-info (gethash base uxntal-mode-instructions)) 337 | (name (aref inst-info 0)) 338 | (s0 (aref inst-info 1)) 339 | (s1 (aref inst-info 2)) 340 | (doc (aref inst-info 3)) 341 | ;; set up stacks based on the bitflags given 342 | ;; 2 -> use 16-bit values (*) instead of 8-bit (^) 343 | ;; k -> keep inputs on stack 344 | ;; r -> swap working stack (ws) and return stack (rs) 345 | (wsx (if (seq-contains-p inst ?r) s1 s0)) 346 | (ws (if (seq-contains-p inst ?k) (uxntal-setup-keep wsx) wsx)) 347 | (rsx (if (seq-contains-p inst ?r) s0 s1)) 348 | (rs (if (seq-contains-p inst ?k) (uxntal-setup-keep rsx) rsx)) 349 | (glyph (if (seq-contains-p inst ?2) "*" "^")) 350 | (wss (if ws (concat "(" (uxntal-format-stack ws glyph) ") ") "")) 351 | (rss (if rs (concat "{" (uxntal-format-stack rs glyph) "} ") ""))) 352 | ;; create full string representation of stacks, 353 | ;; with delimiters and whitespace 354 | (message "%s %s%s%s: %s" inst wss rss name doc))))) 355 | 356 | (defun uxntal-explain-word () 357 | "Explain the given word's meaning in Uxntal. Depdending on the word, this may decode an instruction, display a numeric constant, or describe the syntactic category for the given word." 358 | (interactive) 359 | (let* ((w (current-word t t)) 360 | (dec (lambda () (string-to-number w 16))) 361 | (dec1 (lambda () (string-to-number (substring w 1) 16)))) 362 | (cond 363 | ((not w) (message "No word selected")) 364 | ((string-match uxntal-mode-macro-define-re w) (message "%s is a macro definition" w)) 365 | ((string-match uxntal-mode-include-re w) (message "%s is an include" w)) 366 | ((string-match uxntal-mode-label-define-re w) (message "%s is a label definition" w)) 367 | ((string-match uxntal-mode-sublabel-define-re w) (message "%s is a sublabel definition" w)) 368 | ((string-match uxntal-mode-raw-str-re w) (message "%s is a raw string" w)) 369 | ((string-match uxntal-mode-absolute-pad-re w) (message "%s is an absolute pad (%d)" w (funcall dec1))) 370 | ((string-match uxntal-mode-relative-pad-re w) (message "%s is a relative pad (+%d)" w (funcall dec1))) 371 | ((string-match uxntal-mode-addr-zpg-re w) (message "%s is a zero-page address" w)) 372 | ((string-match uxntal-mode-addr-rel-re w) (message "%s is a relative address" w)) 373 | ((string-match uxntal-mode-addr-abs-re w) (message "%s is an absolute address" w)) 374 | ((string-match uxntal-mode-addr-raw-zpg-re w) (message "%s is a raw zero-page address" w)) 375 | ((string-match uxntal-mode-addr-raw-rel-re w) (message "%s is a raw relative address" w)) 376 | ((string-match uxntal-mode-addr-raw-abs-re w) (message "%s is a raw absolute address" w)) 377 | ((string-match uxntal-mode-addr-raw-abs-re-legacy w) (message "%s is a legacy raw absolute address" w)) 378 | ((string-match uxntal-mode-number-re w) (message "%s is a number (%d)" w (funcall dec1))) 379 | ((string-match uxntal-mode-raw-number-re w) (message "%s is a raw number (%d)" w (funcall dec))) 380 | ((string-match uxntal-mode-inst-re w) (uxntal-decode-instruction w)) 381 | ((string-match uxntal-mode-jci-re w) (message "%s is an instant conditional jump" w)) 382 | ((string-match uxntal-mode-jmi-re w) (message "%s is an instant jump" w)) 383 | ((string-match uxntal-mode-jsi-re w) (message "%s is an instant jump-stash-return" w)) 384 | (t (message "Unknown word: `%s'" w))))) 385 | 386 | (provide 'uxntal-mode) 387 | 388 | ;;; uxntal-mode.el ends here 389 | --------------------------------------------------------------------------------