[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


From: Heike C. Zimmerer
Subject: gas-mode.el
Date: Sat, 26 May 2007 15:09:47 +0200
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/23.0.0 (gnu/linux)

A major mode for editing assemble files in gas syntax.

Expect some (hopefully minor) bugs from this first public release.

Although the code should be able to handle any character for
gas-comment-char (the character that introduces comments), this has
not been tested at all until now.

;; gas-mode.el --- mode for editing assembler code

;; Copyright (C) 2007 Heike C. Zimmerer

;; Author: Heike C. Zimmerer <address@hidden>
;; Created: 20 Feb 2007
;; Version: 1.07  (2007-05-26)
;; Keywords: languages

;; This file is written for GNU Emacs, and uses the same license
;; terms; however, it is an add-on and not part of it.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; To use this mode, put gas-mode.el somewhere on your load-path.
;; Then add this to your .emacs:
;;    (require 'gas-mode)
;;    (add-to-list 'auto-mode-alist '("\\.S\\'" . gas-mode))

;; gas-mode recognizes gas syntax (including embedded C preprocessor
;; directives).  It does a limited amount of parsing, so it can do
;; some fancy things with syntactic elements (like labels).  It,
;; however does not know about the peculiarities of the special
;; processor you're writing code for (there are just too many of
;; them), so, for example, it can't tell register names from labels.
;; Also, its scope is limited to the file you are editing.
;; Symbol highlighting: For a symbol to be recognized as such, it must
;; be in a field where (as per gas syntax) symbols can be placed
;; (i.e. label field, argument field, some directives).  If point
;; rests on such a symbol and there are more of it in the current
;; buffer, it is highlighted and you can move forward and backward
;; between all places where that symbol is referenced or defined with
;; forward-sexp and backward-sexp.  
;; This is different from a simple string search in that only those
;; places are considered where the symbol is actually used.  For local
;; labels, gas-mode resolves which references are associated with
;; which location and only highlights those that fit.  The
;; highlighting is different for different types of symbols; see the
;; customization buffer for the gas-symbol-* faces for short
;; explanations of their meanings.
;; Special forms of local labels (like `55$') are not (yet?)
;; supported.

;; A special feature may need some explanation: C passthroughs.
;; Assembler code is often used to write functions that are later
;; called by C programs.  For this to work, you usually have to
;; maintain two files, one containing the assembler source, the other
;; holding the C interface declaration.  C passthroughs allow you to
;; move the C declaration part into the assembler file next to the
;; function it belongs to.
;; From the assembler's point of view, C passthroughs are just C syntax
;; comments with some small syntactic sugar added, like:
;; /*C
;;     int a_declaration(void);
;;     extern volatile int another_declaration;
;;     /# and this will be passed as comment #/
;; */
;; Note the `/*C' at the beginning and the `/# ... #/' for the nested
;; comment.
;; It is then up to the Make process to generate a .h file, which
;; carries the declarations and the comment, changing the "/#" and "#/"
;; into "/*" and "*/".
;; This may be done by including a line similar to the following into
;; your Makefile (assuming $(ASFILES) is a list of your assembly
;; language files) (and don't forget to use a TAB for the white space
;; which introduces the action lines (<TAB> echo ..., <TAB>sed -n ...):
;; asm-C-defs.h: $(ASFILES)
;;      echo '/* Definitions of assembly language functions */' > $@
;;      echo '/* (automatically created by make)            */' >> $@
;;      sed -n '/[/][*]C/,/[*][/]/{s|/[*]C||;s|[*]/||;s|/#|/*|;s|#/|*/|;p}' \
;;                $^ >> $@
;; gas-mode recognizes this kind of comment by proper syntax
;; highlighting.  Symbol highlighting is also supported.  For a symbol
;; to be highlighted within C passthrough code, it must be defined to
;; be global (because only then it is visible to an external C
;; program).

;; This mode runs `gas-mode-hook' when initialization is complete.

;; Bugs:
;;    Most probably, yes.  You'll tell me (address@hidden).
;;    This code is *not tested at all* for syntaxes where
;;    `gas-commant-char' differs from `?;'.

;;; Change Log:
;;    2007-05-26  1st public release (hcz).

;;; Code:

(defgroup gas nil
  "Mode for editing gas syntax assembler code."
  :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
  :group 'languages)

(defcustom gas-comment-char ?\;
  "The comment start character assumed by gas mode."

  :type 'character
;  :set 'gas-set-gas-comment-char
  :group 'gas)

(defcustom gas-opcode-column 14
  "The opcode column."
  :type 'integer
  :group 'gas)

(defcustom gas-argument-column 20
  "The column for the arguments (if any)."
  :type 'integer
  :group 'gas)

(defcustom gas-comment-column 36
  "The column where end-of-line asm comments go to."
  :type 'integer
  :group 'gas)

(defcustom gas-comment-char-starts-comment nil
  "Always jump to comment column when a `gas-comment-char' is typed.

It t, starts/expands a comment if appropriate.  When
unset (nil), use comment-dwim (usually bound to M-;) to get there."
  :type 'boolean
  :group 'gas)

(defcustom gas-indent-current-field-only nil
  "If nil, 'indent' indents all fileds on the current line.
Else only the current field is affected."
  :type 'boolean
  :group 'gas)

;; (defcustom gas-preserve-trailing-whitespace nil
;;   "If nil, (re-)indenting removes trailing white space."
;;   :type 'boolean
;;   :group 'gas)
(setq gas-preserve-trailing-whitespace nil) ; (currently?) non-functional.

(defcustom gas-enable-symbol-highlight t
  "Enable symbol recognition and highlighing.

When t and if point is on a symbol, some limited parsing data is
collected and all occurences of this symbol in the buffer get
highlighted according to the results (defined, global, etc.).  As
long as a symbol is highlighted, forward-sexp and backward-sexp
move to the next/previous occurence of the same symbol in the
same buffer."
  :type 'boolean
  :group 'gas)

(defcustom gas-use-C-passthrough t
  "When true, C passthrough comments are recognized.

This kind of comment is introduced by the starting sequence
\"/*C\" and is meant to be processed later by an external
program \(see the introducing comment in gas-mode.el for an
example) into C source code.  Within these passthrough-comments,
the combination /# ... #/ is available for nested comments which
will be later changed into real C comments (/* ... */) by the
same external program."
  :type 'boolean
  :group 'gas)

(defcustom gas-defun-regexp "\n\\([;#].*\\|.*[*]/[ \t]*\\|\\|[ \t]*\\|[ 
\t]+\\..*\\)\n\\([^ \t\n;]+:\\)"
  "Regexp used to recognize the beginning of a defun.

The default value describes a line which is either empty, a
full-line left-justified comment or a directive, followed by a
line starting with a label.  Note that the character \";\" in the
regexp will be replaced by the actual comment character described
by `gas-comment-char'."
  :type 'regexp
;  :set 'gas-set-comment-regexp
  :group 'gas)


(defcustom gas-defun-regexp-subexp 2
  "The subexp in `gas-defun-regexp to jump to."
  :type 'integer
  :group 'gas)

(defcustom gas-C-indent 3
  "Indent to use with C style comments."
  :group 'gas
  :type 'integer)

(defcustom gas-C-comment-end-column 0
  "Where to indent a C comment end (\"*/\") if it starts a line."
  :group 'gas
  :type 'integer)

(defcustom gas-symbol-highlight-delay 0.5
  "After this many seconds symbols get highlighted.

Number of seconds of idle time (a float) to wait before a symbol
gets highlighted."
  :group 'gas
  :type 'float)

(defgroup gas-faces nil
  "Faces used by gas-mode."
  :group 'gas)

(defface gas-builtin
  '((((min-colors 88)) (:foreground "maroon"))
    (t (:foreground "yellow")))
  "Face to use for Gas buitins."
  :group 'gas-faces)

(defface gas-symbol-ok
  '((((min-colors 88)) (:background "#e0ffe0" :foreground "black"))
    (t (:foreground "yellow" :background "blue" :weight bold)))
  "Face to use for symbols where exactly 1 definition was found."
  :group 'gas-faces)

(defface gas-symbol-error
  '((((min-colors 88)) (:background "#ffffe8" :foreground "red" :weight bold))
    (t (:foreground "yellow" :background "red" :weight bold)))
  "Face to use when highlighting symbols with more than 1 definition."
  :group 'gas-faces)

(defface gas-symbol-global
  '((((min-colors 88)) (:background "#d0f8ff" :foreground "black"))
    (t (:foreground "yellow" :background "blue" :weight bold)))
  "Face to use when highlighting global symbols."
  :group 'gas-faces)

(defface gas-symbol-undef
  '((((min-colors 88)) (:background "#ffffe8" :foreground "maroon"))
    (t (:foreground "yellow" :background "red" :weight bold)))
  "Face to use for symbols defined as global when no definition
was found."
  :group 'gas-faces)

(defface gas-symbol-global-undef
  '((((min-colors 88)) (:background "#d0f8ff" :foreground "red"))
    (t (:foreground "yellow" :background "red" :weight bold)))
  "Face to use for symbols when no definition is found."
  :group 'gas-faces)

(defface gas-passthrough-code
  '((((min-colors 88)) (:foreground "magenta4"))
    (t (:foreground "magenta1" :background "cyan")))
  "Marks passthrough code."
  :group 'gas-faces)

(defface gas-passthrough-comment
  '((((min-colors 88)) (:foreground "turquoise4"))
    (t (:foreground "turquoise1")))
  "Marks passthrough comments."
  :group 'gas-faces)

(defvar gas-builtin-face 'gas-builtin)
(defvar gas-symbol-ok-face 'gas-symbol-ok)
(defvar gas-symbol-error-face 'gas-symbol-error)
(defvar gas-symbol-global-face 'gas-symbol-global)
(defvar gas-symbol-undef-face 'gas-symbol-undef)
(defvar gas-symbol-global-undef-face 'gas-symbol-global-undef)
(defvar gas-passthrough-code-face 'gas-passthrough-code)
(defvar gas-passthrough-comment-face 'gas-passthrough-comment)

(defconst gas-max-lines-in-cache 500
  "Maximum number of parsed lines in cache.  

I don't expect much impact from this on performance (the line
cache is emptied on any buffer change anyway), so any rough
estimate of how often the same label may be mentioned within a
buffer should be more than enough.")

(defconst gas-max-labels-in-cache 300
  "Maximum number of symbols in highlight cache.  
Reduce this if memory footprint grows too high (very unlikely).")

(defconst gas-re-sym "\\([_$A-Za-z][_0-9$A-Za-z]*\\)"
  "Regexp defining a valid symbol as a subexpression.")

(defconst gas-skip-sym "_0-9$A-Za-z"
  "The valid characters for a symbol as used in `skip-chars-*'

(defconst gas-re-nosym "[^_0-9$A-Za-z]"
  "Regexp defining the character set not allowed in a symbol.")

(defvar gas-mode-syntax-table
  (let ((st (make-syntax-table)))
    (modify-syntax-entry ?\n "> b" st)
    (modify-syntax-entry ?. "_" st)
    (modify-syntax-entry ?/  ". 124b" st)
    (modify-syntax-entry ?*  ". 23" st)
  "Syntax table used while in gas mode.")

(defvar gas-mode-abbrev-table nil
  "Abbrev table used while in Gas mode.")
(define-abbrev-table 'gas-mode-abbrev-table ())

(defvar gas-mode-map
  (let ((map (make-sparse-keymap)))
    ;; Note that the comment character isn't set up until gas-mode is called.
    (define-key map ":"         'gas-colon)
    (define-key map "\M-;"      'gas-comment)
    (define-key map ";"         'gas-comment-char)
    (define-key map "#"         'gas-hash)
    (define-key map (kbd "<S-iso-lefttab>") 'gas-indent-backward)
    (define-key map "\C-c;"     'comment-region)
    (define-key map "\C-j"      'newline-and-indent)
    (define-key map "\C-m"      'newline-and-indent)
  "Keymap for Gas mode.")

(defconst gas-equ (regexp-opt '(".equ" ".set" ".eqv" ".equiv" ".set"))
  "Regex matching all operators which define a symbol.")

(defconst gas-indents
  '((C-comment-end . gas-C-comment-end-column)
    (C-comment-start . 0)
    (C-comment . gas-get-C-relative-indent)
    (cpp-macro-def . 0)
    (cpp-argument . 0)
    (label . 0)
    (opcode . gas-opcode-column)
    (argument . gas-argument-column)
    (asm-comment . gas-get-asm-comment-column))
  "Fields and their indents.  The cdr (the indent) may either be
  a number, a symbol bound to a number, or a symbol bound to a
  function yielding the value.")

(defconst gas-parse-sequences
     . ((cpp-macro-def . cpp-macro-def)
        (label . label)
        (empty-label . label)
        (asm-comment . asm-comment)
        (opcode . opcode)))
     . ((opcode . opcode)
        (asm-comment . asm-comment)
        (garbage . garbage)))
     . ((asm-comment . asm-comment)
        (argument . argument)
        (eol-ws . eol-ws)
        (garbage . garbage)))
     . ((asm-comment . asm-comment)
        (eol-ws . eol-ws)
        (garbage . garbage)))
     . ((eol-ws . eol-ws)))
     . ((eol-ws . eol-ws)
        (garbage . garbage)))
     . ((cpp-argument . cpp-argument)
        (garbage . garbage)))
     . ((eol-ws)
        (garbage . garbage)))
     . ((C-comment . C-comment)))
     . ((C-comment-end . C-comment-end)
        (C-comment . C-comment)))
     . ((C-comment-end . C-comment-end)
        (eol-ws . eol-ws)
        (garbage . garbage)))
     . ((eol-ws . eol-ws)))
     . fini))
  "Mapping from the field type we're on to the field types to
check next:
 car - type of field we're on (IOW, the one just handled)
 cdr - ordered list of (first (most special) check first): 
    car - token to match 
    cdr - next field type iff match." )

(defconst gas-patterns
     "[ \t]*\\(\\(#[^ \t\n]*\\)\\)"
      (0        1                  1 1)
     "[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
      (0        1                              1 1)
     "[ \t]*\\([^ :\t\n]+:\\)"
      (0        1              1 1)
     "[ \t]*\\([^ \t\n]+\\)"
      (0       1            1 1)
     "[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
      (0        1                              1 1)
     "[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
      (0        1                              1 1)
     "[ \t]*\\(\\(;+\\)\\([ \t]*[^ \t\n]+\\)*\\)"
      (0       1                               1 1)
     "[ \t]*\\(/[*].*?[*]/\\)"
      (0       1                  1 1)
     "[ \t]*\\(/[*]C?\\)"
      (0       1      1 1)
     ;; a field of at least 1 white space: no fill at start, nil
     ;; text field at start, fill at end
     " [ \t]*"
      (0 0 0 nil)
     "[ \t]*\\([*]/\\)"
      (0       1      1 1)
     ;; match the entire line (except for trailing whitespace)
     "[ \t]*\\(\\([ \t]*[^ \t]+\\)*\\)"
      (0 1 1 1)
    ;; fill only: zero or more white space, nil text field at
    ;; end
     "[ \t]+\\( ?\\)"
      (0 1 1 1)                         ; last element always empty
  "An alist of parse patterns.

Each entry holds 4 elements (SYMBOL REGEXP SUBEXPS TERMINATE-RE):

  SYMBOL - designator (a symbol)

  REGEXP - the regexp to match against,

  SUBEXPS - a list (BEG-COL TEXT-COL END-COL) of at which
    subexpression of REGEXP to find beg-col, text-col, end-col
    and the end of field,

  TERMINATE-RE - a regexp, the start of which (if it matches)
    unconditionally terminates the field.  

Every occurence of the character \";\" in both regexes is
replaced by `gas-comment-char' before use.")

(defconst gas-elmt-types
  '(type subtype beg-col text-col end-col text modified)
  "The elements of a gas syntax field.")

(defconst gas-builtin-keywords (concat "^\\(\\(\\sw\\|\\s_\\)+:?\\)?[ \t]+\\(" 
                                       (regexp-opt '(
               ".Abort" ".ABORT" ".Align" ".Altmacro" ".Ascii" ".Asciz" 
               ".Balign" ".Byte" ".Comm" 
               ".Data" ".Def" ".Desc" ".Dim" ".Double" ".Eject" 
               ".Else" ".Elseif" ".End" ".Endef" ".Endfunc" ".Endif" 
               ".Equ" ".Equiv" ".Eqv"
               ".Err" ".Error" ".Exitm" ".Extern" ".Fail"
               ".File" ".Fill" ".Float" ".Func"
               ".Global" ".Hidden" ".hword" ".Ident" 
               ".If" ".ifb" ".ifc" ".ifeq" ".ifeqs" 
               ".ifge" ".ifle" ".ifgt" ".iflt"
               ".ifnb" ".ifnc" ".ifndef" ".ifdef" ".ifnotdef" ".ifne" ".ifnes" 
               ".Incbin" ".Include" ".Int"
               ".Internal" ".Irp" ".Irpc" ".Lcomm" ".Lflags" ".Line" 
               ".Linkonce" ".List" ".Ln" 
               ".Long" ".Macro" ".MRI" ".Noaltmacro" 
               ".Nolist" ".Octa" ".Org" ".P2align" ".PopSection" ".Previous" 
               ".Print" ".Protected" ".Psize" ".Purgem" ".PushSection" 
               ".Quad" ".Rept" ".Sbttl" ".Scl" ".Section" ".Set" ".Short" 
               ".Single" ".Size" ".Skip" ".Sleb128" ".Space" ".Stab" ".String" 
               ".Struct" ".SubSection" ".Symver" ".Tag" ".Text" ".Title" 
               ".Uleb128" ".Val" ".Version" ".VTableEntry" ".VTableInherit" 
               ".Warning" ".Weak" ".Weakref" ".Word" ".Deprecated"))
                                       "\\)[ \t\n]"))

(defvar gas-font-lock-keywords
    '(gas-return-passthrough-code-hi . (0 gas-passthrough-code-face t))
    '(gas-return-passthrough-comment-hi . (0 gas-passthrough-comment-face t))
    '(gas-return-gas-hi-ok . (0 gas-symbol-ok-face t))
    '(gas-return-gas-hi-global . (0 gas-symbol-global-face t))
    '(gas-return-gas-hi-error . (0 gas-symbol-error-face t))
    '(gas-return-gas-hi-undef . (0 gas-symbol-undef-face t))
    '(gas-return-gas-hi-global-undef . (0 gas-symbol-global-undef-face t))
    (list gas-builtin-keywords   3 'gas-builtin-face)
    '("^\\(\\(\\sw\\|\\s_\\)+\\)\\>:?"  1 font-lock-function-name-face))
  "Additional expressions to highlight in gas mode.")
(put 'gas-mode 'font-lock-defaults '(gas-font-lock-keywords))


(defun gas-mode ()
  "Major mode for editing assembler code.


\(Some of these commands may exhibit slightly different behaviour if point
is on a C syntax line.)

\\[indent-for-tab-command]  indent the field(s) point is on.  If it already is 
in its position, 
  move on to the next field on the line.

\\[gas-indent-backward]  Move to the previous field.

\\[gas-comment]  When no region is active, starts a comment sequence:
  - If a comment is present and point is not at its start, jump there.
  - Else start a comment. If there already is one, increase its comment level.
    What that is and what it does, is best explained when you try
    it out: Move to en empty line, then type \\[gas-comment] and
    then repeatedly \\[gas-comment] or \\[gas-comment-char].
  Calls \\[comment-dwim] (see below) if the region is active.

\\[forward-sexp]  If you're on a highlighted symbol, jump to its next 
  occurence. Else do forward-sexp like in text mode.

\\[backward-sexp]  If you're on a highlighted symbol, jump to its previous
  occurence. Else do backward-sexp like in text mode.

\\[comment-dwim] If the region starts at the leading white space
  before a comment, all full-line comments in region will be
  removed.  If the region starts on a comment, comments will be
  removed, but comments with leading white space will be left
  untouched.  Else insert triple `gas-comment-char's before all
  lines in region.

\\[fill-paragraph] beautyfies the paragraph around
  point, i.e. it adjusts all assembly syntax fields to their
  standard positions.

\\[indent-region]  beautifies the region, i.e., adjusts all fields in region.

The following characters have a special meaning in special cases:

\\[gas-colon]  if it terminates a label:  outdent the label and
  move to opcode column.  Else, just insert \\[gas-colon] as usual.

\\[gas-comment-char]  The value which introduces an asm style comment.
  If typed in in a row after \\[gas-comment], behaves as an alias to 
\\[gas-comment]. else
  just insert \\[gas-comment-char].
  Can be customized to always act as alias (`gas-comment-char-starts-comment').

  Alternatively, you may use a File Variable to make it buffer local
  (which allows you to use different syntaxes in the same session).
  Note:  Setting it to a value other than ?\; has not yet been tested.

\\[gas-hash]  If it starts a preprocessor directive:  Outdent it to first

Customization:  Entry on this mode runs `gas-mode-hook'.
The customization group is called 'gas'.

Special commands:
  (setq mode-name "gas")
  (setq major-mode 'gas-mode)
  (setq local-abbrev-table gas-mode-abbrev-table)
  (setq gas-comment-string (string gas-comment-char))
  (setq gas-comment-re (regexp-quote gas-comment-string))
  (set (make-local-variable 'indent-line-function) 'gas-indent)
  (set (make-local-variable 'indent-region-function) 'gas-indent-region)
  (set (make-local-variable 'forward-sexp-function) 'gas-forward-sexp)
  (set (make-local-variable 'fill-paragraph-function) 'gas-fill-paragraph)
  (set (make-local-variable 'font-lock-defaults) '(gas-font-lock-keywords))
  (set (make-local-variable 'gas-local-comment-char) gas-comment-char)
  (set (make-local-variable 'beginning-of-defun-function)
  (set (make-local-variable 'end-of-defun-function)
  (set (make-local-variable 'font-lock-keywords-case-fold-search) t)
  (use-local-map (nconc (make-sparse-keymap) gas-mode-map))
  (local-set-key (vector gas-comment-char) 'gas-comment-char)
  (set-syntax-table (make-syntax-table gas-mode-syntax-table))
  (modify-syntax-entry  gas-comment-char "< b")
  (dolist (var '(line-cache label-cache local-label-cache
        gas-hi-valid gas-hi-sym-list gas-hi-global gas-hi-undef
        gas-hi-error gas-hi-ok gas-changed gas-highlights gas-hi
        gas-pass-code-hi gas-pass-comment-hi gas-symbol-timer
        gas-highlights-error gas-symbol-highlight-beg
        gas-symbol-highlight-end gas-doing-comment
    (set (make-local-variable var) nil))
  (add-to-list 'after-change-functions 'gas-after-change)
  (add-hook 'pre-command-hook 'gas-symbol-pre-command)
  (run-mode-hooks 'gas-mode-hook)
  ;; scan buffer for extra regions to highlight:

(defun gas-dbg ()
  "You won't need this unless you're debugging gas-mode."
  (setq debug-on-error t)
  ;(setq debug-on-quit t)
  (setq debug-items '(indent))
  (switch-to-buffer-other-frame "*Messages*")
  (switch-to-buffer-other-frame "*scratch*")
  (info "Elisp")
  (switch-to-buffer-other-frame "*scratch*")
  (find-file-other-frame "x.S")
  (switch-to-buffer-other-frame "x.S")
  (switch-to-buffer-other-frame (get-buffer-create "*gas-dbg*"))
  (find-file-other-frame "gas-mode.el")
  (column-number-mode t)
  (switch-to-buffer-other-frame "gas-mode.el"))

(defun dmsg (condition &rest args)
  "Execute the form (message args) if debug-items is defined as a
symbol or a list and `condition (symbol or list)' is one of them.
Currently defined symbols are: 'wip (work in progress),
'hi (highlighting), 'parser, 'cursor."
  (when (and (boundp 'debug-items) debug-items)
    (when (not (listp condition))
      (setq condition (list condition)))
    (when (not (listp debug-items))
      (setq debug-items (list debug-items)))
    (when (or (eq condition '(all))
              (catch 'found
                (dolist (c condition)
                  (when (member c debug-items)
                    (throw 'found t)))))
;       (eval (cons 'message args)))))
      (let ((contents (apply 'format args)))
          (set-buffer (get-buffer-create "*gas-dbg*"))
          (goto-char (point-max))
          (insert contents)
          (goto-char (point-max))
          ;(recenter -1)
(defun gas-change-comment-regexp (str)
  "Return STR with all \";\"s replaced by (regexp-quote `gas-comment-char')."
  (when str
    (setq str (replace-regexp-in-string
                ".*\\[[^]]*\\(;\\)" gas-comment-string str t t 1))
    (replace-regexp-in-string ";" (regexp-quote gas-comment-string) str t t)))

(defun gas-change-comment-string (str)
  "Return STR with all \";\"s replaced by `gas-comment-char'."
  (when str
    (replace-regexp-in-string ";" gas-comment-string str t t)))

(defun gas-set-patterns (comment-char)
  "Replace `gas-patterns' by a copy, replacing ?; by `gas-comment-char'."
  ;(kill-local-variable 'gas-patterns)
  ;(set (make-local-variable 'gas-comment-string) (string val)))
  (set (make-local-variable 'gas-comment-char) comment-char)
  (set (make-local-variable 'gas-comment-string) (string gas-comment-char))
  (set (make-local-variable 'gas-comment-re) (regexp-quote gas-comment-string))
  (kill-local-variable 'gas-patterns)
  (let (result)
    (dolist (pattern gas-patterns)
      (add-to-list 'result
         (car pattern)
         (gas-change-comment-regexp (nth 1 pattern))
         (nth 2 pattern)
         (gas-change-comment-regexp (nth 3 pattern)))))
  (set (make-local-variable 'gas-patterns) result)
  (dolist (sym '(gas-defun-regexp))
    (kill-local-variable sym)
    (let ((result (gas-change-comment-regexp (eval sym))))
      (set (make-local-variable sym) result)))))


(defun gas-after-change (beg end len)
  "Invalidate saved parser state."
  (setq label-cache nil)
  (setq gas-hi-valid nil)
  (setq local-label-cache nil)
  (setq gas-changed (append gas-changed (list (current-buffer) beg end len))))

(defun gas-symbol-pre-command()
  (setq gas-hi nil))

(defun gas-start-symbol-timer (&optional stop)
  "Schedule a timer for symbol highlighting (if not already
scheduled).  Optional STOP, if non-nil,  means remove from schedule."
  (if (and stop gas-symbol-timer)
        (cancel-timer gas-symbol-timer)
        (setq gas-symbol-timer nil))
    (unless (and gas-symbol-timer
                 (memq gas-symbol-timer timer-idle-list))
      (setq gas-symbol-timer
            (run-with-idle-timer gas-symbol-highlight-delay

(defun gas-return-passthrough-hi (pos what)
  "called by highlight.el.  Returns matches for passthrough code
if appropriate."
  (let ((curpoint (point)))
    (catch 'found
      (dolist (match what)
        (when (and (< curpoint (cdr match))
                   (> pos (car match)))
          (when (< curpoint (car match))
            (goto-char (car match)))
          (when (re-search-forward ".+" (min pos (cdr match)) t)
            (dmsg 'hi "match: %s, data: '%s'" match (match-string 0))
            (throw 'found t)))
        (when (>= (car match) pos)
          (throw 'found nil))))))

(defun gas-return-passthrough-code-hi (pos)
  (gas-return-passthrough-hi pos gas-pass-code-hi))

(defun gas-return-passthrough-comment-hi (pos)
  (gas-return-passthrough-hi pos gas-pass-comment-hi))

(defun gas-passthrough-highlight ()
  (goto-char (point-min))
  (setq gas-pass-code-hi nil)
  (setq gas-pass-comment-hi nil)
  (let (beg end limit)
    (while (and (re-search-forward "/[*]C" nil t)
                (setq beg (point))
                (setq limit 
                        (and (re-search-forward "[*]/" nil t) 
                             (- (point) 2)))))
      (while (and beg
                  (setq end (and (re-search-forward "/#" limit t)
        (add-to-list 'gas-pass-code-hi (cons beg (- end 2)) t)
        (forward-char 2)
            (setq beg (and (re-search-forward "#/" limit t) (point)))
          (add-to-list 'gas-pass-comment-hi (cons (- end 2) beg) t)))
      (when beg
        (add-to-list 'gas-pass-code-hi (cons beg limit) t)))))

(defun gas-return-highlight (pos hi-list)
  "called by highlight.el.  Returns matches for symbols
if appropriate."
  (when gas-hi-valid
    (dmsg 'hi "gas-return-highlight, pos: %s, point: %s, list: %s" pos (point) 
    (catch 'found
      (dolist (match hi-list)
        (if (< (car match) pos)
            (when (>= (car match) (point))
              (dmsg 'hi "gas-return-highlight, match: %s" match)
              (goto-char (car match))
              (throw 'found (re-search-forward ".+" (min pos (cadr match)) t)))
          (dmsg 'hi "gas-return-highlight: nope.")
          (throw 'found nil)

(defun gas-return-gas-hi-global (pos)
  (gas-return-highlight pos gas-hi-global))

(defun gas-return-gas-hi-undef (pos)
  (gas-return-highlight pos gas-hi-undef))

(defun gas-return-gas-hi-error (pos)
  (gas-return-highlight pos gas-hi-error))

(defun gas-return-gas-hi-ok (pos)
  (gas-return-highlight pos gas-hi-ok))

(defun gas-return-gas-hi-global-undef (pos)
  (gas-return-highlight pos gas-hi-global-undef))

(defun gas-qualify-symbol (sym-re slist lflags)
  "The common part of `gas-scan-global-symbol and gas-scan-local-symbol.
Gets called with point on a line where a label match may be
found.  It expects SYM-RE to be a regexp describing the
label. Adds what it finds to SLIST (a symbol bound to a
    (let ((eol (line-end-position))
          (bol (line-beginning-position)))
      (setq case-fold-search nil)
      (if (gas-C-comment-p)
            (while (re-search-forward 
                    (format "%s\\(%s\\)\\(%s\\|$\\)"
                            gas-re-nosym sym-re gas-re-nosym)
                    eol t)
              (let ((beg (match-beginning 1))
                    (end (match-end 1)))
                (when (gas-C-passthrough-code-p)
                  (add-to-list lflags 'C-ref t)
                  (add-to-list slist (list 'C-ref beg end) t)))))
        ;; not C style:
        (let* ((fields (gas-parsed))
               (lbl (gas-nth 'text 'label fields))
               (arg (gas-nth 'text 'argument fields))
               (type 'ref)
               (argno 0))
          (when (and lbl 
                     (string-match (concat "^" sym-re ":?$") lbl))
            (setq nlabels (1+ nlabels))
            (if (member 'def (eval lflags))
                (add-to-list lflags 'duplicate t)
              (add-to-list lflags 'def t))
            (move-to-column 0)
            (looking-at "[^:]+:")
            (add-to-list slist  
                         (list 'def (match-beginning 0) (match-end 0)) t))
          (when arg
            (move-to-column (- (gas-nth 'text-col 'argument fields) 1))
            (let ((type 'ref)
                  (eo-arg (+ bol (gas-nth 'end-col 'argument fields) 2))
                  (opcode (gas-nth 'text 'opcode fields)))
              (while (re-search-forward 
                      (format "%s\\(%s\\)\\(%s\\|$\\)"
                              gas-re-nosym sym-re gas-re-nosym)
                      eo-arg t)
                (let ((beg (match-beginning 1))
                      (end (match-end 1)))
                  ;; first argument? 
                  (if (and (= argno 0)                  ; yes
                           (string-match gas-equ opcode)) ; assignment?
                        (setq type 'def)  ; yes
                        (if (member 'def (eval lflags))
                            (add-to-list lflags 'duplicate t)
                          (add-to-list lflags 'def t)))
                    (add-to-list lflags 'ref t)) ; no
                  (when (string-match ".global" opcode)
                    (add-to-list lflags 'global t))
                  (add-to-list slist (list type beg end) t)
                  (setq argno (1+ argno))))))))))
    ;; skip past parsed part:

(defun gas-scan-global-symbol (sym)
  "Scans the buffer vor valid occurences of sym."
  (let* (sym-list 
         (nlabels 0)
         (sym-re (regexp-quote sym))
         (re (format "\\(^\\|%s\\)\\(%s\\)\\(%s\\|$\\)"
                     gas-re-nosym sym-re gas-re-nosym)))
    (while (re-search-forward re nil t)
      (when (input-pending-p)
        (throw 'event-abort nil))
      (goto-char (match-beginning 2))
      (dmsg 'sym "global: qualified1: %s" sym-list)
      (gas-qualify-symbol sym-re 'sym-list 'flags))
    (dmsg 'sym "global: sym-list: %s" sym-list)
    (unless (equal sym-list '(nil))
      (list sym flags sym-list))))

(defun gas-scan-local-symbol (orig-sym)
  (when (string-match "^\\(.*\\)\\([:bf]\\)" orig-sym)
    (let* ((search-lo (point-min)) ; location of previous duplicate         
           search-mid                   ; label pos
           (search-hi (point-max))      ; location of next duplicate
           (sym (match-string 1 orig-sym))
           (sym-kind (match-string 2 orig-sym))
           (sym-re (regexp-quote sym))
           (lbl-re (concat "^\\(" sym-re ":" "\\)" ))
           (nlabels 0)
      ;; determine region where the label is valid
        (when (equal sym-kind "b")
          (re-search-backward lbl-re nil t)) ; skip label
        (when (re-search-backward lbl-re nil t)
          (setq search-lo (match-end 1)))
        (goto-char search-lo)
        (when (re-search-forward lbl-re nil t)
          (setq search-mid (match-beginning 1)))
        (when (re-search-forward lbl-re nil t)
          (setq search-hi (match-beginning 1))))
      (let ((lo search-hi)              ; first match
            (hi search-lo)              ; end of last match
             (if search-mid
                  (list (concat  sym-re "f") search-lo search-mid)
                  (list (concat  sym-re ":")
                        search-mid  (+ search-mid (length sym) 2))
                  (list (concat  sym-re "b") search-mid search-hi))
                (list (concat  sym-re "f") search-lo search-hi)
                (list (concat  sym-re "b") search-lo search-hi)))))
        (dolist (param search-params)
          (when (input-pending-p)
            (throw 'event-abort nil))
          (let* ((sym-re (car param))
                 (search-re (concat gas-re-nosym
                                    "\\(" sym-re "\\)" gas-re-nosym))
                 (limit (caddr param)))
            (goto-char (- (cadr param) 1))
            (while (and (< (point) limit)
                        (re-search-forward search-re limit t))
              (setq lo (min lo (match-beginning 1)))
              (setq hi (max hi (match-end 1)))
              (goto-char (match-beginning 1))
              (gas-qualify-symbol sym-re 'sym-list 'flags))))
        (dmsg 'sym "local: sym-list: %s" sym-list)
        (unless (equal sym-list '(nil))
          (list sym lo hi flags sym-list))))))
(defun gas-sym-invalidate ()
  (setq gas-hi-sym-list nil)
  (setq gas-hi-global nil)
  (setq gas-hi-global-undef nil)
  (setq gas-hi-undef nil)
  (setq gas-hi-error nil)
  (setq gas-hi-ok nil))

(defun gas-symbol-highlight ()
  "Get symbol point is on, highlight it if appropriate."
      (let* ((fields (gas-parsed))
             (curpoint (point))
             (case-fold-orig case-fold-search)
             (pointpos (gas-get-pointpos fields))
        (catch 'event-abort
          (dmsg 'hi "pointpos=%s, fields=%s" pointpos fields)
          ;; skip to the symbol's start
          (skip-chars-backward gas-skip-sym)
          (when (member (car pointpos) '(label argument C-comment))
            (setq case-fold-search nil)
            (setq sym-list
                  (catch 'found
                    ;; local label?
                    (if (and (not (eq (car pointpos) 'C-comment))
                             (looking-at (concat "\\(\\([0-9]+\\)\\([bf:]\\)\\)"
                        (let ((lbl (match-string 2)))
                          ;; local label
                          (dolist (entry local-label-cache)
                            (when (input-pending-p)
                              (throw 'event-abort nil))
                            (and (equal (car entry) lbl)
                                 (>= (nth 1 entry) curpoint)
                                 (<= (nth 2 entry) curpoint)
                                 (dolist (sym (nthcdr 3 entry))
                                   (and (>= (nth 1 entry) curpoint)
                                        (<= (nth 2 entry) curpoint)
                                        (throw 'found (nthcdr 3 entry)))))
                          ;; not found in cache
                          (let ((entry (gas-scan-local-symbol (match-string 
                            (when entry
                              (when (> (length local-label-cache)
                                (dmsg 'sym "local-label-cache truncated.")
                                (nbutlast local-label-cache
                                          (/ (* gas-max-labels-in-cache 3) 4)))
                              (add-to-list 'local-label-cache entry)
                              (throw 'found (nthcdr 3 entry)))))
                      ;; global label:
                      (when (looking-at (format "\\(%s+\\)%s"
                                                gas-re-sym gas-re-nosym))
                        (setq lbl (match-string 1))
                        (when (setq entry (assoc lbl label-cache))
                          (throw 'found (cdr entry)))
                        ;; not found in cache
                        (let ((entry (gas-scan-global-symbol lbl)))
                          (when entry
                            (when (> (length label-cache)
                              (dmsg 'sym "label-cache truncated.")
                              (nbutlast label-cache
                                        (/ (* gas-max-labels-in-cache 3) 4)))
                            (add-to-list 'label-cache entry)
                            (throw 'found  (cdr entry))))))))
            (dmsg 'sym "gas-symbol-highlight: Matched: %s" sym-list)
            (setq gas-hi-sym-list sym-list)
            (let ((flags (car sym-list))
              (dolist (sym (cadr sym-list))
                (when (input-pending-p)
                  (throw 'event-abort nil))
                (setq target-list
                       ((member 'duplicate flags) 'gas-hi-error)
                       ((and (member 'global flags) (member 'def flags))
                       ((member 'global flags) 'gas-hi-global-undef)
                       ((eq (car sym) 'C-ref) nil)
                       ((and (member 'def flags) (member 'ref flags))
                       ((> (length (cadr sym-list)) 1) 'gas-hi-undef)))
                (dmsg 'sym "target-list, sym: %s, list: %s" sym target-list)
                (when target-list
                  (add-to-list target-list (cdr sym) t))))
            (setq gas-hi-valid t)
        (setq case-fold-search case-fold-orig)))))

(defun gas-symbol-highlight-maybe ()
  (unless (or (not gas-enable-symbol-highlight ) (input-pending-p))
    (undo-boundary)                     ; probably redundant
    (undo-boundary)))                   ;   -"-

(defun gas-C-comment-p ()
  "true if we're editing a C syntax comment (the one enclosed in /* */)"
    (let ((current (point)))
      (cond ((not (re-search-backward "/\\*" 0 t)) nil)
            ((not (re-search-forward "\\*/" current t)))))))

;; format of a C passthrough:
;; --- asm
;; /*C
;;   --- C passthrough code
;;   /#
;;      --- C passthrough comment
;;   #/
;;   ---- C passthrough code
;;   [... passthrough code, comment as above ...]
;; */  
;; --- asm
(defun gas-C-passthrough-p ()
  "true if we're editing a C passthrough (a C style comment
enclosed in /*C ... */)"
  (when gas-use-C-passthrough
      (let ((current (point)))
        (and (re-search-backward "/\\*C" 0 t)
             (not (re-search-forward "\\*/" current t)))))))

(defun gas-C-passthrough-comment-p ()
  "true if we're editing a C passthrough comment (/# ... #/
within a /*C ... */ comment)"
  (when gas-use-C-passthrough
      (let ((current (point)))
        (when (gas-C-passthrough-p)
                (and (re-search-backward "/\\#" 0 t)
                     (not (re-search-forward "[#*]/" current t))))))))))

(defun gas-C-passthrough-code-p ()
  "true if we're editing C passthrough code (within a /*C ... */
comment but not within /# ... #/)"
  (when gas-use-C-passthrough
    (and (gas-C-passthrough-p)
         (not (gas-C-passthrough-comment-p)))))

(defun gas-C-comment-really-p ()
  (and (gas-C-comment-p)
       (not (gas-C-passthrough-code-p))))

(defun gas-comment-p ()
  "true if we're editing some kind of comment"
  (or (looking-back (concat gas-comment-string ".*"))

(defun gas-narrow-to-C-comment ()
  "narrow region to the C comment point resides in."
  (unless (gas-C-comment-p)
    (error "not within C comment"))
    (let* ((beg (re-search-backward ".*/[*]"))
           (end (re-search-forward "[*]/")))
      (narrow-to-region beg end))))

(defun gas-token-pattern (cur-check)
  "return a list how to parse for token `cur-check'.
The returned list holds three elements, see the documentation
of `gas-patterns' for an explanation."
  (let ((pattern (cdr (assq cur-check gas-patterns))))
    (when (not pattern)
      (t (error "gas internal: illegal pattern %s requested"

(defun gas-next-token (checks)
  "Parse for one of the tokens in list `checks'.
`checks' is a list of cons cells '(token_symbol . resulting_type).

Valid token_symbols are the ones listed in the body of
gas-token-pattern.  Valid resulting_types are those recognised
for f-type at the end of the body of gas-indent-asm-line.

Eat the next matching token, returning its parameters. If none
matches, return nil."
  (catch 'got-token
    (dolist (cur-check checks)
      (let* ((pattern-list (gas-token-pattern (car cur-check)))
             (regexp (pop pattern-list))
             (subexps (pop pattern-list))
             (limit-re (pop pattern-list))
             (subtype nil))
          (setq limit cureol) ; text end
          (when (and limit-re
                     (re-search-forward limit-re limit t))
            (re-search-backward limit-re)
            (setq limit (point))))
          (narrow-to-region (point) limit)
          (when (looking-at regexp)      ; match pattern
                 ((eq (cdr cur-check) 'asm-comment)
                  ;; set subtype to the number of consecutive comment-chars
                  (setq subtype (- (match-end 2) (match-beginning 2))))
                 ((eq (cdr cur-check) 'argument)
                  ;; set subtype to be a list holding the positions of the
                  ;; individual subexpressions (relative to start of text)
                  (let ((arg-beg (point))
                        (arg-end (match-end 0)))
                    (setq subtype '(0))
                    (while (re-search-forward "," arg-end t)
                      (add-to-list 'subtype (- (point) arg-beg 1) t)))))))
            (goto-char (min limit (match-end 0)))
            (throw 'got-token
                    (list (cdr cur-check)
                          (- (min limit (match-beginning (pop subexps))) curbol)
                          (- (min limit (match-beginning (pop subexps))) curbol)
                          (- (min limit (match-end (pop subexps))) curbol)
                          (let ((subexp (pop subexps)))
                            (and subexp
                                 (not (equal (match-string subexp) ""))
                                 (match-string subexp)))

(defun gas-parse-line-really ()
  "Parse the line point is on, element by element.
The returned list holds a list of syntactic elements found, in
the order of appearance.  Each syntactic element is
represented by a list holding 7 elements:

'(type subtype beg-col text-col end-col text modified)
    0 - 'type: ('label, 'opcode ...)
    1 - 'subtype: (additional information required by some types)
    2 - 'beg-col: first slot (column #) (usually occupied by white 
                  space before text)
    3 - 'text-col: first text slot (column #)
    4 - 'end-col: first free slot after field (column #)
    5 - 'text: text contents (nil if empty)
    6 - 'modified: nil (will later reflect if rearrangement (indentation) 
        is required / was done).

Note that text-col may lie outside the range beg-col ... end-col if
there's no actual text (it is the column where text would have to go)."

  ;; We use syntax tables neither here nor much throughout the whole
  ;; gas-mode, since assembler code, being line oriented and based on
  ;; number and position of elements on the line, fits regexps better
  ;; than syntax tables (IMHO).  The only drawback is that this makes
  ;; dealing with inline C-style comments (who the hell uses that?)
  ;; somewhat clumsy, but not by much.
  (let (field
        (f-type 'start)
        (C-inline-comment-level 0)

      (setq cureol (line-end-position))
      (setq curbol (point))

      (setq f-type
            (if (gas-C-comment-p)
                  (setq pushed-f-type 'starting-asm-line)
      (while f-type
        ;; always check for C-comment first (except we're already
        ;; inside)
        (setq field 
              (and (not (gas-C-comment-p))
                    '((C-inline-comment . C-inline-comment)
                      (C-comment-start . C-comment-start)))))
        (unless field
          ;; dispatch based on previous field
          (let ((next-check (cdr (assq f-type gas-parse-sequences))))
            (when (not next-check)
              (error "gas-mode internal: gas-parse-line: %s" f-type))
            (when (not (eq next-check 'fini))
              (setq field (gas-next-token next-check)))
            (when (eq (car field) 'garbage)
              (error "gas-mode internal: garbage: %s" field))
            (dmsg 'parser "parsed a field: %s->%s" f-type field)))
        (when (eq (car field) 'C-inline-comment)
          (setcar field (list 'C-inline-comment C-inline-comment-level))
          (setq C-inline-comment-level (1+ C-inline-comment-level)))
        (when (and (eq 'asm-comment (car field))
                   (assq 'opcode fields))
          (gas-set-nth 'subtype field 1))
        (when field
          (add-to-list 'fields field t))
        (when (eq (car field) 'C-comment-start) ; push state
          (setq pushed-f-type f-type))
        (unless (and field (listp (car field))) ; not C-inline-comment
          (setq f-type (car field)))
        (when (eq f-type 'C-comment-end)   ; pop state
          (setq f-type pushed-f-type))
        (when (listp f-type)
          (setq f-type (car f-type)))))
    (dmsg '(parser cursor) "parsed all fields: %s" fields)

(defun gas-get-field (field-type field-list &optional offset)
  "Return field (a list) with first element (syntactic type) eq
to field-type from field-list, nil if no such field.  

offset >= 0: return next field, offset < 0: return previous field.
If offset is given. both fields (field-type and one before/after)
must exist, else nil is returned."
  (if offset
      (let (f-types
        (dolist (field field-list)
          (add-to-list 'f-types (car field) t))
        (if (> offset 0)
            (while (not (eq (pop ftypes) field-type)))
          (while (and (setq f-type (nth 1 f-types))
                      (not (eq f-type field-type)))
            (pop f-types)))
        (gas-get-field (car f-types) field-list))
    (assq field-type field-list)))

(defun gas-nth (component field-from &optional field-list)
  "Get one of the components of a field containing a line
  (let ((field-from (if field-list
                        (gas-get-field field-from field-list)
    (nth (gas-elmt-n component) field-from)))

(defun gas-elmt-n (elmt)
  "Return index of a field element."
  (when (symbolp elmt)
    (- (length gas-elmt-types) (length (memq elmt gas-elmt-types)))))
(defun gas-set-nth (elmt field val-or-fields &optional val)
  (let ((curfield field))
    (if val
        (setq curfield (assq field val-or-fields))
      (setq val val-or-fields))
    (setcar (nthcdr (gas-elmt-n elmt) curfield) val)))

(defun gas-rearrange (fields &optional elmt f-type val)
  "Rearrange fields so they fit together
without gaps or overlapping."
  (let ((field-slot 0)                   ; next free slot for a field
        (text-slot 0)            ; next column where text should go to
        (end-slot 0)
    (dolist (field fields)
      (let ((old-col (gas-nth 'text-col field))
            (text (gas-nth 'text field)))

        (gas-set-nth 'beg-col field field-slot)
        ;; insert new value if needed
        (when (and val (eq f-type (gas-nth 'type field)))
          (gas-set-nth elmt field val))
        ;; determine text column
        (let ((tcol (gas-nth 'text-col field)))
          ;; if the current text column is nil and we are reordering:
          ;; put in its default value.
          (when (or (eq elmt 'all)
                    (and (not val) (eq f-type (car field))))
            (setq tcol (cdr (assq (car field) gas-indents))))
          ;; if column is a symbol: replace it by its value
          (when (and tcol (symbolp tcol))
            (setq tcol (if (fboundp tcol)
                           (funcall tcol)
                         (eval tcol))))
          ;; if we still have got no target value: use the next free
          ;; text slot
          (unless tcol
            (setq tcol text-slot))
          ;; set text col to max (current, next-free-slot)
          (setq tcol (max tcol text-slot))
          (gas-set-nth 'text-col field tcol)
          ;; update `modified' flags:
          (when (and text
                     (not (eq tcol old-col)))
            (unless did-change
              (setq did-change (car field)))
            (gas-set-nth 'modified field t))
          (when text
            (setq field-slot (+ (gas-nth 'text-col field) (length text))))
          (setq end-slot (max end-slot (or (gas-nth 'end-col field) 0)))
          (gas-set-nth 'end-col field field-slot)
          ;; determine next free slot
          (setq text-slot (max tcol text-slot))
          (when (/= 0 field-slot)
            (setq text-slot (max text-slot  (1+ field-slot)))))))
    (gas-set-nth 'end-col 'eol-ws fields end-slot)

(defun gas-reset-modified (fields)
  "Clears the `modified' flag on all fields."
  (dolist (field fields)
    (gas-set-nth 'modified field nil)))

(defun gas-get-pointpos (ffields)
  "determine the field in fields point is on.
Returns a list (f-type subtype offset_to_field_text-begin abs-column eol).
EOL may bei either the symbol 'eol or nil"
  (let* ((curcol (current-column))
         (fields (copy-tree ffields))
         (ppos (list (caar fields) curcol)))
    (catch 'pos-found
      (dolist (field fields)
        ;; first, look for a field match
        (when (and (gas-nth 'text field)
                   (>= curcol (gas-nth 'beg-col field))  ; field start
                   (<= curcol (gas-nth 'end-col field))) ; field end
          (setq ppos (list (car field) (- curcol (gas-nth 'text-col field))))
          (throw 'pos-found t)))
      ;; no field match: look at position relative to text slot
      (gas-rearrange fields 'all)
      (dolist (field fields)
        (when (or (<= curcol (gas-nth 'text-col field))
                  (eq (car field) 'eol-ws))
          (setq ppos (list (car field) (- curcol (gas-nth 'text-col field))))
          (throw 'pos-found t))))
    (setq ppos (append ppos (list curcol)))
    (if (>= curcol (gas-nth 'end-col 'eol-ws fields))
        (append ppos '(eol))

(defun gas-get-C-relative-indent ()
  "Return the indent of the closest previous non-blank line
between point and the C comment start seqence."
  (if (not (gas-C-comment-p))
      (let ((col
               (catch 'found
                 (if (looking-at "[ \t]*#/")
                     ;; end passthrough:  same indent as start
                     (while (= 0 (forward-line -1))
                       (when (looking-at "[ \t]*/#")
                         (throw 'found (current-indentation))))
                   (while (= 0 (forward-line -1))
                     (when (looking-at "[ \t]*/[*#]")
                       (throw 'found (+ gas-C-indent (current-indentation))))
                     (when (looking-at "[ \t]*[^ \t\n]")
                       (throw 'found (current-indentation)))))
        (when (gas-C-passthrough-code-p)
          (if (looking-at "[ \t\n]*}[ \t\n]*;")
              (setq col (max 0 (- col gas-C-indent)))))

(defun gas-add-missing-fields (parsed-fields)
  "Determine line syntax, then add fields which are missing but
allowed by the syntax."
  ;; Let's qualify what we've got
  (let (existing-types mandatory line-syntax new-fields f-type cur-type)
    ;; determine mandatory fields:
    (setq mandatory
          (cond ((not parsed-fields)    ; empty line
                 '(label opcode argument asm-comment eol-ws))
                ((assq 'C-comment parsed-fields)
                 '(C-comment-start C-comment C-comment-end eol-ws))
                ((assq 'cpp-macro-def parsed-fields)
                 '(cpp-macro-def cpp-argument eol-ws))
                 (let ((subtype (gas-nth 'subtype 'asm-comment parsed-fields)))
                    ((eq subtype 3) '(asm-comment eol-ws))
                    ((eq subtype 2) '(label asm-comment eol-ws))
                    (t '(label opcode argument asm-comment eol-ws)))))))
    (dmsg '(parser cursor) "mandatory: %s" mandatory)
    ;; We're going through all fields in `fields', inserting mandatory
    ;; elements (from `mandatory') if not present
    (dolist (field parsed-fields)
      (setq f-type (car field))
      (if (member f-type mandatory)
          ;; we have a mandatory field.  Insert missing fields (if
          ;; any) before it, then insert the field at its place.
            ;; insert an empty field for each mandatory but
            ;; not already exisiting element before the one
            ;; we're dealing with.
            (while (progn
                     (setq cur-type (pop mandatory))
                     (not (eq cur-type f-type)))
              (dmsg 'parser "adding missing mandatory: %s" cur-type)
              (add-to-list 'new-fields
                           (list cur-type nil nil nil nil nil nil) t))
            ;; now insert field
            (dmsg 'parser "adding existing mandatory: %s" field)
            (add-to-list 'new-fields field t))
        ;; we have a non-mandatory field.  Pass it through,
        ;; keeping its place if possible.
        (add-to-list 'new-fields field t)))
    ;; done with parsed fields.
    ;; add mandatory elements left out at right
    (while (setq cur-type (pop mandatory))
      (dmsg 'rearranged "adding left-over: %s" cur-type)
      (add-to-list 'new-fields
                           (list cur-type nil nil nil nil nil nil) t))
    (dmsg 'parser "new-fields=%s" new-fields)

(defun gas-parsed (&optional lineno)  
  "Return fields on line LINENO or, if nil, on current line.
Calls `gas-parse-line-really' if new parsing is reqired.
See there for more."
  (dmsg 'parser "gas-changed: %s, lenght line-cache: %s"
        gas-changed (length line-cache))
  (unless (and (eq gas-comment-char ?\;)
               (eq gas-local-comment-char gas-comment-char))
       (setq gas-local-comment-char gas-comment-char)
       (gas-set-patterns gas-comment-char))
  (when gas-changed
    (setq line-cache nil)
    (setq gas-changed nil))
  (when (> (length line-cache) gas-max-lines-in-cache)
    (dmsg 'parser "line-cache truncated.")
    (nbutlast line-cache (/ (* gas-max-lines-in-cache 3) 4)))
  (let* ((curline (or lineno (line-number-at-pos)))
         (cached (assq curline line-cache)))
    (if cached
        (cadr cached)
      (let (fields)
          (goto-line curline)
          (setq fields (gas-parse-line-really))
          (setq fields (gas-add-missing-fields fields))
          (gas-rearrange fields)
          (dmsg 'parser "->line-cache: %s" fields)
          (add-to-list 'line-cache (list curline fields)))

(defun gas-put-parsed (fields lineno)
  "put FIELDS into line-cache at LINENO, overwriting the previous
entry for that line (if any)."
  (gas-reset-modified fields)
  (let ((current (assq lineno line-cache)))
    (if current
        (setcdr current (list fields))
      (add-to-list 'line-cache (list curline fields)))))
(defun gas-put-out (&optional fields)
  "Write the current line onto screen."
  (when (not fields)
    (setq fields (gas-parsed)))
  (dmsg 'indent "put-out, fields: %s" fields)
      (delete-region (point) (line-end-position))
      (dolist (field fields)
        (when (and (gas-nth 'text field)
                   (or gas-preserve-trailing-whitespace
                       (not (eq (car field) 'eol-ws))))
          (indent-to (gas-nth 'text-col field))
          (insert (gas-nth 'text field)))))))

(defun gas-get-asm-comment-column ()
  "determine from the current comment field (from FIELDS) which
column to use for an assembler style comment.  The caller must
provide the list fields (bound to the fields in question)."
  (let ((subtype (gas-nth 'subtype 'asm-comment fields)))
    (cond ((not subtype) gas-comment-column)
          ((= subtype 1) gas-comment-column)
          ((= subtype 2) gas-opcode-column)
          (t 0))))

(defun gas-set-point-to-field (f-type fields)
  (let ((target-col (gas-nth 'text-col f-type fields)))
    (move-to-column target-col)
    (indent-to target-col)
    (when (eq f-type 'asm-comment)
      (skip-chars-forward (format " %c" gas-comment-char))
      (when (not (looking-back " "))
        (insert-char ?\s 1)))))

(defun gas-set-point (pointpos fields &optional direction always)
  "set point to a field of the current line.

POINTPOS is the current field as returned by a call to
gas-get-pointpos.  If DIRECTION is nil or 'stay, set it to the
beginning of the current field.  If DIRECTION is 'end-of-field,
set it to the end of the current field. If DIRECTION equals
'backward or 'forward, and ALWAYS is nil, set it to the beginning
of the next/previous field only if the field is unchanged, else
to the beginning of the current.  Finally, if DIRECTION equals
'backward or'forward and ALWAYS is non-nil, move point
unconditionally to the beginning of the next/previous field."
    (let ((existing '(line-1))
      (dmsg 'indent "(gas-set-point %s %s %s)" pointpos direction always)
      (dolist (field fields)
        (setq existing (append existing (list (car field)))))      
      (add-to-list 'existing 'line+1 t)
      (when (eq direction 'backward)
        (setq existing (nreverse existing)))
      (setq f-type (car pointpos))
      (when (and (eq direction 'forward)
                 (eq (nth 3 pointpos) 'eol))
        ;; special forward skipping rules for certain fields:
        (cond ((and (eq f-type 'C-comment-start) (= (nth 2 pointpos) 0))
               ;; move on:
              ((memq f-type '(label opcode argument)) 
               (cond ((>= (nth 2 pointpos) gas-comment-column)
                      (setq f-type 'eol-ws)) ; next: line+1
                     ((and (not (gas-nth 'text 'opcode fields))
                           (>= (nth 2 pointpos) gas-opcode-column))
                      (setq f-type 'argument)))) ; next: comment
              (t (setq f-type 'eol-ws)))) ; next: line+1
      ;; rules for skipping backward:
      (when (eq direction 'backward)
        (when (= (nth 2 pointpos) 0)
          (setq f-type (car (last existing 2)))) ;next: line-1
        (and (> (nth 1 pointpos) 0)
             (or (not (eq f-type 'asm-comment))
                 (not (looking-back ";[ \t]")))
             (setq direction 'stay)))
      ;; move to next field if told to do so:
      (when (and (not (memq direction '(pos stay end-of-field))) ; no - 
                 (or always             ; yes - do always
                     (and direction     ; maybe - do only if field unchanged
                          (not (gas-nth 'modified f-type fields)))))
        (setq f-type (cadr (member f-type existing))))
      (dmsg 'indent "gas-set-point next:%s, fields: %s" f-type fields)
      ;; set point
      (if (eq f-type 'line-1)
            (if (bobp)
        (when (eq f-type 'line+1)
          (when (eobp) 
            (error "End of buffer"))
          (setq f-type 'eol-ws)
          (setq direction 'end-of-field))
        ;; and set point there:
        (let ((where (gas-nth 'text-col f-type fields)))
          (cond ((eq direction 'pos) 
                 (setq where (+ (gas-nth 'text-col f-type fields)
                                (cadr pointpos))))
                ((eq direction 'end-of-field)
                 (setq where (max where (gas-nth 'end-col f-type fields)))))
          (move-to-column where t)
          (indent-to where))
        (when (eq f-type 'asm-comment)
          (skip-chars-forward (format " %c" gas-comment-char))
          (when (not (looking-back " "))
            (insert-char ?\s 1))))))

(defun gas-indent (&optional direction always ffields)
  "Indent current line.

For lines carrying asm syntax, `gas-indent-current-field-only'
determines if only the current field or the entire line is

See `gas-set-point' for a description of DIRECTION and ALWAYS.
FFIELDS, if present, is a list describing the fields on the
current line as returned by `gas-parsed'."
  (let* ((fields (or ffields (gas-parsed)))
    (setq pointpos (gas-get-pointpos fields))
    (dmsg 'indent "gas-indent, fields: %s" fields)
    (dmsg 'indent "gas-indent, pointpos: %s" pointpos)
    (when (and (not direction) 
               (not (gas-C-comment-p))
               (eq this-command 'newline-and-indent))
      ;; go to first non-empty field.  If none, to opcode.
      (setq pointpos
             (or (catch 'field-found
                   (dolist (field fields)
                     (when (gas-nth 'text field)
                       (throw 'field-found (car field)))))
             0 gas-opcode-column))
      (setq direction 'stay)
      (setq always t))    
    (setq rearranged-type
           ((and (eq (caar fields) 'C-comment-start)
                 (or (save-excursion (beginning-of-line)(looking-at "[ \t]*$"))
                     (and (eq (car pointpos) 'C-comment) (= (nth 1 pointpos) 
            ;; at start of C comment text (poosibly empty): in/outdent
            (let ((n_indents (/ (nth 2 pointpos) gas-C-indent)))
              (if (eq direction 'backward)
                  (setq n_indents (max 0 (1- n_indents)))
                (setq n_indents (1+ n_indents)))
              (gas-rearrange fields 'text-col 'C-comment
                             (* gas-C-indent n_indents))
              (setq direction 'stay)
            (gas-rearrange fields 'text-col (car pointpos) 
                           (cdr (assq (car pointpos) gas-indents))))
           (t (gas-rearrange fields 'all))))
    (dmsg 'indent "re-indented, fields: %s" fields)
    (gas-put-parsed fields (line-number-at-pos))
    (cond (always (gas-set-point pointpos fields direction always))
          (rearranged-type (gas-set-point-to-field rearranged-type fields))
          ((and (eq (car pointpos) 'C-comment) (not direction)
                (> (cadr pointpos) 0))
           (gas-set-point pointpos fields 'pos always))
          ((and (not direction) (>= (cadr pointpos) 0))
           (gas-set-point pointpos fields 'forward always))
          (t (gas-set-point pointpos fields direction always)))

(defun gas-indent-region (&optional from to)
  "indent all fields in region."
  (unless from (setq from (min (point) (mark))))
  (unless to (setq to (max (point) (mark))))
      (goto-char from)
      (while (and (not (eobp)) (< (point) to))
        (dmsg 'indent "indent-region, line:%s" (line-number-at-pos))
        (if (gas-C-comment-p)
            (beginning-of-line 2)         ; skip C comments
          (let ((fields (gas-parsed)))
            (gas-rearrange fields 'all)
            (gas-put-out fields)
            (beginning-of-line 2)))))))

(defun gas-indent-backward ()
  "Indent, then move to previous field.  While in C-comment,
remove one level of indentation."
  (gas-indent 'backward 'always))

(defun gas-forward-sexp (n)
  "move to corresponding label/argument"
  (if gas-hi-sym-list
          (catch 'found-one
            (if (< 0 n)
                (dolist (match (cadr gas-hi-sym-list))
                  (when (> (cadr match) (point))
                    (goto-char (cadr match))
                    (throw 'found-one t)))
              (dolist (match (reverse (cadr gas-hi-sym-list)))
                (when (< (nth 2 match)  (point))
                  (goto-char (cadr match))
                  (throw 'found-one t)))))
    (let (forward-sexp-function)
      (forward-sexp n))))

(defun gas-beginning-of-defun ()
  (let* ((beg (save-excursion
                (re-search-backward gas-defun-regexp nil t))))
    (when beg
      (goto-char (match-beginning gas-defun-regexp-subexp)))))

(defun gas-end-of-defun ()
  (let ((beg (save-excursion
              (re-search-forward gas-defun-regexp nil t))))
    (when beg
      (goto-char (match-beginning gas-defun-regexp-subexp)))))

(defun gas-hash () 
  "Insert a hash mark.  If it starts a macro, delete the indentaion."
  (when (and (not (gas-comment-p))
             (looking-back "^[ \t]*"))
  (call-interactively 'self-insert-command))

(defun gas-colon ()
  "Insert a colon; if it follows a label, delete the label's indentation." ;
  (call-interactively 'self-insert-command)
  ;; check if colon belongs to a label field
  (let* ((fields (gas-parsed))
         (pointpos (gas-get-pointpos fields)))
    (dmsg 'indent "gas-colon, fields: %s" fields)
    (dmsg 'indent "gas-colon, pointpos: %s" pointpos)
    (when (eq (car pointpos) 'label)
      (gas-indent 'forward t))))

;; Obsolete since Emacs-22.1.
(defalias 'gas-newline 'newline-and-indent)

(defun gas-comment-region-dwim (&optional from to)
  "de-comment region if at start of comment,
make comment out of region else."
  (unless from
    (setq from (min (point) (mark)))
    (setq to (max (point) (mark))))
  (when (save-excursion (goto-char to) (bolp))
    (setq to (1- to)))
    (let ((mmax (progn (goto-char to) (point-marker)))
          (mmin (progn (goto-char from) (point-marker))))
      (if (gas-C-passthrough-p)
          (let* (beg end end-match
                 (pos (looking-at "[ \t\n]*/#"))
                 (de-comment (or pos (gas-C-passthrough-comment-p))))
              (if de-comment
                    (when (gas-C-passthrough-comment-p)
                      (insert " #/\n"))
                    (while (re-search-forward
                            "\\(\n[ \t]*\\)?/#\\(\n[ \t]*\\)?" mmax t)
                      (replace-match "")
                      (when (re-search-forward
                             "\\(\n[ \t]*\\)?[#]/\\(\n[ \t]*\\)?" mmax t)
                        (replace-match "")))
                    (let* ((end (save-excursion
                                  (re-search-forward "[#*]/" nil t)))
                           (end-match (match-string 0))
                           (beg (save-excursion
                                  (re-search-forward "/#" nil t))))
                      (when (and (equal end-match "#/")
                                 (or (not beg)
                                     (> beg end)))
                        ;; we have a comment close outside the selcted
                        ;; area which misses the matching comment open
                        (goto-char mmax)
                        (insert "\n/#\n"))))
                ;; do C en-comment
                (insert " /#\n ")    ;
                (while (and (save-excursion 
                              (setq end (re-search-forward "[*#]/" nil t)))
                            (setq beg (re-search-forward "/#" mmax t)))
                  (replace-match "#/ /# ")
                  (re-search-forward "[#*]/" mmax t)
                  (if (equal end-match "*/")
                      (replace-match "   #/\n*/")
                    (replace-match "#/ /#")))
                (let* ((end (save-excursion
                              (re-search-forward "[#*]/" nil t)))
                       (end-match (match-string 0))
                       (beg (save-excursion
                              (re-search-forward "/#" nil t))))
                  (when (and (equal end-match "*/")
                             (or (not beg)
                                 (> beg end)))
                    ;; we have no comment close outside the selcted
                    ;; area but comment is open
                    (goto-char mmax)
                    (backward-char 2)
                    (insert "\n   #/\n"))))))
        ;; skip to the start of the asm comment if  we're on one
        (when (looking-back (format "%c+[ \t]*" gas-comment-char))
          (goto-char (match-beginning 0))
          (skip-chars-backward gas-comment-string))
          (narrow-to-region (point) to)
          (let ((triple-comment (concat (make-string 3 gas-comment-char) " "))
                 ;; de-comment if there's a comment after point or
                 ;; at the start of the next non-empty line
                 (looking-at (format "[ \t]*%c\\|.*\n[ \t\n]*\%c"
                                     gas-comment-char gas-comment-char))))
            (when de-comment
              (goto-char (match-end 0))
              (skip-chars-backward gas-comment-string)
              (looking-at (format "\n?\\([ \t]\\)*\\(%c+\\)\\([ \t]?\\)"
              (let ((pre-pattern (match-string 1))
                    (mid-pattern (regexp-quote (match-string 2)))
                    (post-pattern (match-string 3)))
                (setq pre-pattern
                      (if (and pre-pattern
                               (string-match "[ \t]" pre-pattern))
                          "\\([ \t]*"
                (setq post-pattern
                      (if (and post-pattern
                               (string-match "[ \t]" post-pattern))
                          "[ \t]?\\)"
                        (format "\\)\\([^%c]\\|$\\)" gas-comment-char)))
                (setq comment-pattern
                      (concat pre-pattern mid-pattern post-pattern))))
            (catch 'fini
              (while t
                (cond ((gas-C-comment-p) nil)
                      ((looking-at "[ \t]/[*]") nil)
                      ((not de-comment) (insert triple-comment))
                      (t (when (looking-at comment-pattern)
                           (replace-match "" nil t nil 1))))
                ;; forward-line returns t even after it just
                ;; moved to eol (end of narrowed region):
                (unless (and (= 0 (forward-line)) (bolp))
                  (throw 'fini t)))))))
      (setq mmax nil)
      (setq mmin nil))))

(defun gas-backward-indent ()
  (gas-indent 'backward 'always))

(defun gas-comment ()
  "Start/expand a comment.
Suggested usage:  while writing your code, trigger gas-comment
repeatedly until you are satisfied with the kind of comment."
  (setq gas-doing-comment t)
  (if mark-active
    (let* ((fields (save-excursion (gas-parsed)))
           (curcol (current-column))
           (pointpos (gas-get-pointpos fields))
           (subtype (gas-nth 'subtype 'asm-comment fields)))
      (dmsg 'indent "gas-comment, pointpos: %s, fields. %s"
            pointpos fields)
       ((gas-C-passthrough-comment-p) (insert-char gas-comment-char 1))
        (when (looking-back "/[*]C[ \t]*")
          (insert "\n"))
        (if (looking-back "[ \t]")
            (insert "/#   #/ ")
          (insert " /#   #/ "))
        (backward-char 5))
        (if (and gas-use-C-passthrough
                 (looking-back "\\(/[*] ?\\)[ \t\n]*")            )
            (save-excursion (replace-match "/*C" nil nil nil 1))
          (insert-char gas-comment-char 1)))
       ((and subtype (>= subtype 3))
        (move-to-column (gas-nth 'text-col 'asm-comment fields))
        (looking-at ";+ ?")
        (replace-match "")
        (insert "/*")
        (gas-indent 'forward 'always)
        (let ((curindent (current-indentation)))
          (insert "\n\n")
          (indent-to curindent)
          (insert "*/")
          (forward-line -1)
          (indent-to (+ curindent gas-C-indent))))
        (move-to-column (gas-nth 'text-col 'asm-comment fields))
        (when (or (not subtype)
                  (and (<= (current-column) curcol)
                         (skip-chars-forward (format "%c \t" gas-comment-char))
                         (>= (current-column) curcol))))
          (insert-char gas-comment-char 1)
          (setq fields (gas-parsed)))
        (gas-rearrange fields 'text-col 'asm-comment)
        (gas-put-out fields)
        (gas-set-point-to-field 'asm-comment fields))))))

(defun gas-fill-paragraph (arg)
  (let ((curcol (current-column))
        (fill-paragraph-function nil)
        ; non-empty comment:
        (asm-comment-re (format "\\([ \t]*\\(%c+\\)\\)[ \t]*[^ \t%c\n]\n?"
                                gas-comment-char gas-comment-char))
        (empty-line-re "\\([^ \t]+:\\)?[ \t]*$"))
    (cond ((gas-C-comment-really-p) 
           (let* ((beg (save-excursion 
                         (re-search-backward "/#\\|/[*][^C]\\([ \t]*\n\\)?")
                         (match-end 0)))
                  (end (save-excursion
                         (re-search-forward "\\(\n[ \t]*\\)?[*#]/")
                         (match-beginning 0))))
               (narrow-to-region beg end)
               (fill-paragraph arg))))
           (let* ((beg (save-excursion 
                         (re-search-backward "\\(#/\\|/[*]C\\)\\([ \t]*\n\\)?")
                         (match-end 0)))
                  (end (save-excursion
                         (re-search-forward "\\(\n[ \t]*\\)?[*#]/")
                         (match-beginning 0))))
             (goto-char beg)
             (while (< (point) end)
               (let ((fields (gas-parsed)))
                 (gas-rearrange fields 'all)
                 (gas-put-out fields)
                 (beginning-of-line 2)))))
          ((save-excursion (beginning-of-line) (looking-at asm-comment-re))
           (let* ((fill-prefix (concat (match-string 1) " "))
                  (re (format "[^%c]*\\%s[ \t]*[^ \t%c\n]\n?" gas-comment-char
                               (match-string 2) gas-comment-char))
                  (beg (save-excursion
                        (while (save-excursion
                                 (looking-at re))
                          (end-of-line 0))
                 (end (save-excursion
                        (while (save-excursion
                                 (beginning-of-line 2) 
                                 (looking-at re))
                          (end-of-line 2))
               (narrow-to-region beg end)
               (fill-paragraph arg))))
           (let ((beg (save-excursion
                        (while (save-excursion
                                 (beginning-of-line 0)
                                 (not (or (looking-at asm-comment-re)
                                          (looking-at empty-line-re)
                          (beginning-of-line 0))
                 (end (save-excursion
                        (while (save-excursion
                                 (beginning-of-line 2)
                                 (not (or (looking-at asm-comment-re)
                                          (looking-at empty-line-re)
                                 (end-of-line 2))
             (gas-indent-region beg end))))
    (move-to-column curcol)) t)

(defun gas-comment-char ()
  "Handle comment character.  If the last command was gas-comment
usually bound to M-;), increment the level of the comment
just started, else insert the character without extra action."
  (if (and gas-comment-char-starts-comment
           (not (gas-C-comment-p)))
    (if (and gas-doing-comment
             (member last-command '(gas-comment-char gas-comment)))
      (setq gas-doing-comment nil)
      (insert-char gas-comment-char 1))))

(provide 'gas-mode)

;;; gas-mode.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]