1;;; asm-mode.el --- mode for editing assembler code
2
3;; Copyright (C) 1991, 2001, 2002, 2003, 2004, 2005, 2006, 2007
4;; Free Software Foundation, Inc.
5
6;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
7;; Maintainer: FSF
8;; Keywords: tools, languages
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING.  If not, write to the
24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25;; Boston, MA 02110-1301, USA.
26
27;;; Commentary:
28
29;; This mode was written by Eric S. Raymond <esr@snark.thyrsus.com>,
30;; inspired by an earlier asm-mode by Martin Neitzel.
31
32;; This minor mode is based on text mode.  It defines a private abbrev table
33;; that can be used to save abbrevs for assembler mnemonics.  It binds just
34;; five keys:
35;;
36;;	TAB		tab to next tab stop
37;;	:		outdent preceding label, tab to tab stop
38;;	comment char	place or move comment
39;;			asm-comment-char specifies which character this is;
40;;			you can use a different character in different
41;;			Asm mode buffers.
42;;	C-j, C-m	newline and tab to tab stop
43;;
44;; Code is indented to the first tab stop level.
45
46;; This mode runs two hooks:
47;;   1) An asm-mode-set-comment-hook before the part of the initialization
48;; depending on asm-comment-char, and
49;;   2) an asm-mode-hook at the end of initialization.
50
51;;; Code:
52
53(defgroup asm nil
54  "Mode for editing assembler code."
55  :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
56  :group 'languages)
57
58(defcustom asm-comment-char ?\;
59  "*The comment-start character assumed by Asm mode."
60  :type 'character
61  :group 'asm)
62
63(defvar asm-mode-syntax-table
64  (let ((st (make-syntax-table)))
65    (modify-syntax-entry ?\n "> b" st)
66    (modify-syntax-entry ?/  ". 124b" st)
67    (modify-syntax-entry ?*  ". 23" st)
68    st)
69  "Syntax table used while in Asm mode.")
70
71(defvar asm-mode-abbrev-table nil
72  "Abbrev table used while in Asm mode.")
73(define-abbrev-table 'asm-mode-abbrev-table ())
74
75(defvar asm-mode-map
76  (let ((map (make-sparse-keymap)))
77    ;; Note that the comment character isn't set up until asm-mode is called.
78    (define-key map ":"		'asm-colon)
79    (define-key map "\C-c;"	'comment-region)
80    (define-key map "\C-j"	'newline-and-indent)
81    (define-key map "\C-m"	'newline-and-indent)
82    map)
83  "Keymap for Asm mode.")
84
85(defconst asm-font-lock-keywords
86  (append
87   '(("^\\(\\(\\sw\\|\\s_\\)+\\)\\>:?[ \t]*\\(\\sw+\\(\\.\\sw+\\)*\\)?"
88      (1 font-lock-function-name-face) (3 font-lock-keyword-face nil t))
89     ;; label started from ".".
90     ("^\\(\\.\\(\\sw\\|\\s_\\)+\\)\\>:"
91      1 font-lock-function-name-face)
92     ("^\\((\\sw+)\\)?\\s +\\(\\(\\.?\\sw\\|\\s_\\)+\\(\\.\\sw+\\)*\\)"
93      2 font-lock-keyword-face)
94     ;; directive started from ".".
95     ("^\\(\\.\\(\\sw\\|\\s_\\)+\\)\\>[^:]?"
96      1 font-lock-keyword-face)
97     ;; %register
98     ("%\\sw+" . font-lock-variable-name-face))
99   cpp-font-lock-keywords)
100  "Additional expressions to highlight in Assembler mode.")
101
102;;;###autoload
103(defun asm-mode ()
104  "Major mode for editing typical assembler code.
105Features a private abbrev table and the following bindings:
106
107\\[asm-colon]\toutdent a preceding label, tab to next tab stop.
108\\[tab-to-tab-stop]\ttab to next tab stop.
109\\[asm-newline]\tnewline, then tab to next tab stop.
110\\[asm-comment]\tsmart placement of assembler comments.
111
112The character used for making comments is set by the variable
113`asm-comment-char' (which defaults to `?\\;').
114
115Alternatively, you may set this variable in `asm-mode-set-comment-hook',
116which is called near the beginning of mode initialization.
117
118Turning on Asm mode runs the hook `asm-mode-hook' at the end of initialization.
119
120Special commands:
121\\{asm-mode-map}"
122  (interactive)
123  (kill-all-local-variables)
124  (setq mode-name "Assembler")
125  (setq major-mode 'asm-mode)
126  (setq local-abbrev-table asm-mode-abbrev-table)
127  (make-local-variable 'font-lock-defaults)
128  (setq font-lock-defaults '(asm-font-lock-keywords))
129  (set (make-local-variable 'indent-line-function) 'asm-indent-line)
130  ;; Stay closer to the old TAB behavior (was tab-to-tab-stop).
131  (set (make-local-variable 'tab-always-indent) nil)
132
133  (run-hooks 'asm-mode-set-comment-hook)
134  ;; Make our own local child of asm-mode-map
135  ;; so we can define our own comment character.
136  (use-local-map (nconc (make-sparse-keymap) asm-mode-map))
137  (local-set-key (vector asm-comment-char) 'asm-comment)
138  (set-syntax-table (make-syntax-table asm-mode-syntax-table))
139  (modify-syntax-entry	asm-comment-char "< b")
140
141  (make-local-variable 'comment-start)
142  (setq comment-start (string asm-comment-char))
143  (make-local-variable 'comment-add)
144  (setq comment-add 1)
145  (make-local-variable 'comment-start-skip)
146  (setq comment-start-skip "\\(?:\\s<+\\|/[/*]+\\)[ \t]*")
147  (make-local-variable 'comment-end-skip)
148  (setq comment-end-skip "[ \t]*\\(\\s>\\|\\*+/\\)")
149  (make-local-variable 'comment-end)
150  (setq comment-end "")
151  (setq fill-prefix "\t")
152  (run-mode-hooks 'asm-mode-hook))
153
154(defun asm-indent-line ()
155  "Auto-indent the current line."
156  (interactive)
157  (let* ((savep (point))
158	 (indent (condition-case nil
159		     (save-excursion
160		       (forward-line 0)
161		       (skip-chars-forward " \t")
162		       (if (>= (point) savep) (setq savep nil))
163		       (max (asm-calculate-indentation) 0))
164		   (error 0))))
165    (if savep
166	(save-excursion (indent-line-to indent))
167      (indent-line-to indent))))
168
169(defun asm-calculate-indentation ()
170  (or
171   ;; Flush labels to the left margin.
172   (and (looking-at "\\(\\sw\\|\\s_\\)+:") 0)
173   ;; Same thing for `;;;' comments.
174   (and (looking-at "\\s<\\s<\\s<") 0)
175   ;; Simple `;' comments go to the comment-column.
176   (and (looking-at "\\s<\\(\\S<\\|\\'\\)") comment-column)
177   ;; The rest goes at the first tab stop.
178   (or (car tab-stop-list) tab-width)))
179
180(defun asm-colon ()
181  "Insert a colon; if it follows a label, delete the label's indentation."
182  (interactive)
183  (let ((labelp nil))
184    (save-excursion
185      (skip-syntax-backward "w_")
186      (skip-syntax-backward " ")
187      (if (setq labelp (bolp)) (delete-horizontal-space)))
188    (call-interactively 'self-insert-command)
189    (when labelp
190      (delete-horizontal-space)
191      (tab-to-tab-stop))))
192
193;; Obsolete since Emacs-22.1.
194(defalias 'asm-newline 'newline-and-indent)
195
196(defun asm-comment ()
197  "Convert an empty comment to a `larger' kind, or start a new one.
198These are the known comment classes:
199
200   1 -- comment to the right of the code (at the comment-column)
201   2 -- comment on its own line, indented like code
202   3 -- comment on its own line, beginning at the left-most column.
203
204Suggested usage:  while writing your code, trigger asm-comment
205repeatedly until you are satisfied with the kind of comment."
206  (interactive)
207  (comment-normalize-vars)
208  (let (comempty comment)
209    (save-excursion
210      (beginning-of-line)
211      (with-no-warnings
212	(setq comment (comment-search-forward (line-end-position) t)))
213      (setq comempty (looking-at "[ \t]*$")))
214
215  (cond
216
217   ;; Blank line?  Then start comment at code indent level.
218   ;; Just like `comment-dwim'.  -stef
219   ((save-excursion (beginning-of-line) (looking-at "^[ \t]*$"))
220    (indent-according-to-mode)
221    (insert asm-comment-char asm-comment-char ?\ ))
222
223   ;; Nonblank line w/o comment => start a comment at comment-column.
224   ;; Also: point before the comment => jump inside.
225   ((or (null comment) (< (point) comment))
226    (indent-for-comment))
227
228   ;; Flush-left or non-empty comment present => just insert character.
229   ((or (not comempty) (save-excursion (goto-char comment) (bolp)))
230    (insert asm-comment-char))
231
232   ;; Empty code-level comment => upgrade to next comment level.
233   ((save-excursion (goto-char comment) (skip-chars-backward " \t") (bolp))
234    (goto-char comment)
235    (insert asm-comment-char)
236    (indent-for-comment))
237
238   ;; Empty comment ends non-empty code line => new comment above.
239   (t
240    (goto-char comment)
241    (skip-chars-backward " \t")
242    (delete-region (point) (line-end-position))
243    (beginning-of-line) (insert "\n") (backward-char)
244    (asm-comment)))))
245
246(provide 'asm-mode)
247
248;;; arch-tag: 210e695f-f338-4376-8913-a4c5c72ac848
249;;; asm-mode.el ends here
250