I could not find an Emacs mode for editing Hack ASM so I whipped up the following. Maybe someone else will find it useful.
;;; hack-asm-mode.el --- mode for editing hack assembler code -*- lexical-binding: t; -*-
;; Copyright (C) 2022 Excalamus
;; This file is not part of GNU Emacs.
;; This 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 3, or (at your option) any later
;; version.
;;
;; This is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the 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:
;; `hack-asm-mode' handles indentation and rudimentary font highlighting for
;; Hack assembly programs. Labels are indented to column 0. All
;; other code indents further to the right. A-instructions, jumps,
;; and comments each have a different face.
;; To manually enable `hack-asm-mode':
;; 1. Load file: (load "/path/to/hack-asm-mode.el")
;; 2. Enable mode: M-x hack-asm-mode
;; To automatically enable `hack-asm-mode':
;; Emacs uses `asm-mode' for files ending in ".asm". To preserve this
;; while still enabling `hack-asm-mode', save your Hack assembly programs as
;; ".hack.asm" files. Then have Emacs look for this specific file
;; extension when loading modes:
;; (add-to-list 'auto-mode-alist '("\\.hack.asm\\'" . hack-asm-mode))
;; FIXME
;; - Font lock for strings may override the comment font lock
;; TODO
;; - Define face for dest keywords
;; - Define faces for (a == 0)-type comp keywords
;; - Define faces for (a == 1)-type comp keywords
;;; Code:
(defconst hack-asm-font-lock-keywords
(append
'(;; jumps
("JMP\\|JGT\\|JEQ\\|JGE\\|JLT\\|JNE\\|JLE" . font-lock-warning-face)
;; labels
("(\\(\\sw\\|\\s_\\)+)" . font-lock-function-name-face)
;; comments
("[ \t]*//.*$" . font-lock-comment-face)
;; A-instruction
("@\\sw+" . font-lock-reference-face)))
"Additional expressions to highlight in Hack Assembler mode.")
(defun hack-asm-calculate-indentation ()
"Calculate indentation for line"
(or
;; Flush labels to the left margin.
(and (looking-at "(\\(\\sw\\|\\s_\\)+)") 0)
;; Same thing for comments.
(and (looking-at "\\s<\\s<\\s<") 0)
;; Inline comments go to the comment-column.
(and (looking-at "\\s<\\(\\S<\\|\\'\\)") comment-column)
;; The rest goes at the first tab stop.
(indent-next-tab-stop 0)))
(defun hack-asm-indent-line ()
"Auto-indent the current line."
(interactive)
(let* ((savep (point))
(indent (condition-case nil
(save-excursion
(forward-line 0)
(skip-chars-forward " \t")
(if (>= (point) savep) (setq savep nil))
(max (hack-asm-calculate-indentation) 0))
(error 0))))
(if savep
(save-excursion (indent-line-to indent))
(indent-line-to indent))))
;;;###autoload
(define-derived-mode hack-asm-mode prog-mode "Hack Assembler"
"Major mode for editing Hack assembler code."
(setq-local font-lock-defaults '(hack-asm-font-lock-keywords))
(setq-local indent-line-function #'hack-asm-indent-line)
(setq-local tab-always-indent t)
(setq-local comment-start "//")
(setq-local comment-start-skip "\\(?:\\s<+\\|/[/*]+\\)[ \t]*")
(setq-local comment-end ""))
(provide 'hack-asm-mode)
;;; hack-asm-mode.el ends here
Using the
base16-eighties
theme, it looks like: