[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/lmc 2e56da4 02/14: * lmc.el: Add a few more commands, a
From: |
Stefan Monnier |
Subject: |
[elpa] externals/lmc 2e56da4 02/14: * lmc.el: Add a few more commands, and a tool-bar. |
Date: |
Sat, 28 Nov 2020 23:22:14 -0500 (EST) |
branch: externals/lmc
commit 2e56da46e3e8a62edd2ccbffe23db027e0a2a4ce
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
* lmc.el: Add a few more commands, and a tool-bar.
(lmc--assemble): Warn of duplicate labels.
(lmc-disassemble-word): Fix typo.
(lmc-mode-map, lmc-menu): Add bindings for run, set-pc, and set-acc.
(lmc-tool-bar-map): New var.
(lmc-mode); Use it. Make the PC and Acc editable with a click.
(lmc-set-pc, lmc-set-acc, lmc-run): New commands.
(lmc-step): Update tool-bar after hitting HLT.
(lmc-asm-indent-line): Parenthesize if needed.
---
lmc.el | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 125 insertions(+), 20 deletions(-)
diff --git a/lmc.el b/lmc.el
index 39a0759..1455bd1 100644
--- a/lmc.el
+++ b/lmc.el
@@ -24,18 +24,39 @@
;; The simulator uses a plain editable buffer, so you can edit the machine
;; words just like any other text, and every word can be given a name (label)
-;; which can also be edited in the normal way.
-
-;; The assembly uses a slightly different (Lispish) syntax where each
-;; instruction needs to be wrapped in parentheses. Other than hat it's the
-;; same assembly as documented elsewhere (accepts a few mnemonic variants, such
-;; as IN/INP, STA/STO).
-;; The lmc-asm-mode supports all the usual editing features such as label
+;; which can also be edited in the normal way. Additionally to the labels it
+;; shows the disassembled meaning of instruction words. Of course, it can't
+;; always know which words are meant to be code rather than data, so it relies
+;; on information from the assembler to do that, and otherwise just marks every
+;; word it executes as being "code".
+
+;; The assembly uses a slightly different (Lispish) syntax where comments start
+;; with ";", and each instruction needs to be wrapped in parentheses.
+;; Other than that it's the same assembly as documented elsewhere
+;; (accepts a few mnemonic variants, such as IN/INP, STA/STO, BR/BRA).
+;; Another difference is that the DAT mnemonic accepts any number of words
+;; rather than just one.
+;;
+;; So the assembly (stored in files with extension ".elmc") looks like:
+;;
+;; label1
+;; (BR label2) ;Useless extra jump.
+;; label2
+;; (LDA data1) ;Cleverest part of the algorithm.
+;; (ADD data2)
+;; (STO data1)
+;; (BR label1)
+;;
+;; data1 (DAT 0)
+;; data2 (DAT 050 060 070)
+;;
+;; And actually, since the assembler re-uses the Emacs Lisp reader to parse the
+;; code, you can use binary, octal, and hexadecimal constants as well, using
+;; the notations #b101010, #o277, and #x5F respectively.
+;;
+;; The lmc-asm-mode supports the usual editing features such as label
;; completion, mnemonic completion, jumping to a label, automatic indentation,
-;; and even code folding.
-
-;; FIXME:
-;; - can't set lmc-pc and lmc-acc.
+;; and code folding.
;;; Code:
@@ -98,7 +119,9 @@
(setq pos (+ pos (if (eq (car cmd) 'DAT)
(1- (length cmd)) 1)))
;; (assert (symbolp cmd))
- (push (cons cmd pos) labels)))
+ (if (assq cmd labels)
+ (error "Duplicate label %S" cmd)
+ (push (cons cmd pos) labels))))
;; Second pass, do the actual assembly.
(let* ((words ())
(ll nil)
@@ -168,8 +191,8 @@
(let ((code (car (rassq (/ word 100) lmc-mnemonic-1-table))))
(cond
(code (list code (mod word 100)))
- ((rassq word lmc-mnemonic-1-table)
- (list (car (rassq word lmc-mnemonic-1-table)))))))
+ ((rassq word lmc-mnemonic-0-table)
+ (list (car (rassq word lmc-mnemonic-0-table)))))))
(defun lmc-addr->point (addr)
(goto-char (point-min))
@@ -320,13 +343,32 @@
(defvar lmc-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-s" 'lmc-step)
+ (define-key map "\C-c\C-r" 'lmc-run)
(define-key map "\C-c\C-l" 'lmc-load-file)
+ (define-key map "\C-c\C-a" 'lmc-set-acc)
+ (define-key map "\C-c\C-p" 'lmc-set-pc)
map))
(easy-menu-define lmc-menu lmc-mode-map "Menu for LMC-Sim."
'("LMC-Sim"
["Step" lmc-step (not (lmc-stopped-p))]
- ["Load file" lmc-load-file]))
+ ["Run" lmc-run (not (lmc-stopped-p))]
+ ["Load file" lmc-load-file]
+ "--"
+ ["Set Program Counter" lmc-set-pc]
+ ["Set Accumulator" lmc-set-acc]))
+
+(defvar lmc-tool-bar-map
+ (let ((map (make-sparse-keymap)))
+ (tool-bar-local-item "gud/next" 'lmc-step 'step map
+ :label "step" ;; :vert-only t
+ :enable '(not (lmc-stopped-p))
+ )
+ (tool-bar-local-item "gud/run" 'lmc-run 'run map
+ :label "run" ;; :vert-only t
+ :enable '(not (lmc-stopped-p))
+ )
+ map))
(define-derived-mode lmc-mode fundamental-mode "LMC-Sim"
"The simulator of the Little Man Computer."
@@ -337,18 +379,58 @@
'(lmc-font-lock-keywords t))
(set (make-local-variable 'font-lock-extra-managed-props)
'(display help-echo))
+ (set (make-local-variable 'tool-bar-map) lmc-tool-bar-map)
(add-hook 'after-change-functions #'lmc-after-change nil t)
(set (make-local-variable 'lmc-label-table) (make-vector 100 nil))
(set (make-local-variable 'overlay-arrow-position) (point-min-marker))
(lmc-update-pc)
;; (overwrite-mode 1)
(set (make-local-variable 'header-line-format)
- '("LMC-Sim PC=" (:eval (format "%02d" lmc-pc))
- " ACC=" (:eval (format "%03d" lmc-acc))
+ `("LMC-Sim PC="
+ (:eval (format ,(propertize "%02d"
+ 'mouse-face 'mode-line-highlight
+ 'help-echo "mouse-2: set the Program
Counter"
+ 'follow-link t
+ ;; I'm having problems with mouse-2 to
+ ;; mouse-1 remapping in the mode-line and
+ ;; header-line, so I over-do it a bit.
+ 'keymap
+ '(keymap
+ (header-line keymap
+ (down-mouse-1 . ignore)
+ (mouse-2 . lmc-set-pc)
+ (mouse-1 . lmc-set-pc))))
+ lmc-pc))
+ " ACC="
+ (:eval (format ,(propertize "%03d"
+ 'mouse-face 'mode-line-highlight
+ 'help-echo "mouse-2: set the Accumulator"
+ 'follow-link t
+ 'keymap
+ ;; I'm having problems with mouse-2 to
+ ;; mouse-1 remapping in the mode-line and
+ ;; header-line, so I over-do it a bit.
+ '(keymap
+ (header-line keymap
+ (down-mouse-1 . ignore)
+ (mouse-2 . lmc-set-acc)
+ (mouse-1 . lmc-set-acc))))
+ lmc-acc))
" Recent output: "
(:eval (if lmc-output (format "%s" lmc-output) "()"))))
)
+(defun lmc-set-pc (pc)
+ "Set the Program Counter."
+ (interactive (list (read-number "New PC: " lmc-pc)))
+ (setq lmc-pc pc)
+ (lmc-update-pc))
+
+(defun lmc-set-acc (acc)
+ "Set the Accumulator."
+ (interactive (list (read-number "New Accumulator: " lmc-acc)))
+ (setq lmc-acc (mod acc 1000)))
+
(defun lmc-load (words)
(pop-to-buffer "*LMC-Sim*")
(lmc-mode)
@@ -377,7 +459,7 @@
(with-silent-modifications
(put-text-property (match-beginning 1) (point)
'face 'region))
- (sit-for 0.5))
+ (sit-for 0.2))
(replace-match (format " %03d" word) t t nil 1)
(when lmc-store-flash
(sit-for 0.1)
@@ -398,7 +480,9 @@
(case (car code)
(HLT (if (lmc-stopped-p)
(error "Already halted")
- (setq lmc--stopped (lmc--state)) (message "Done.")))
+ (setq lmc--stopped (lmc--state))
+ (force-mode-line-update)
+ (message "Done.")))
(IN (setq lmc-acc (mod (read-number "Enter a number") 1000))
(incf lmc-pc))
(OUT (message "Output: %03d" lmc-acc)
@@ -425,6 +509,14 @@
(t (error "%S not implemented" code))))
(lmc-update-pc))
+(defun lmc-run ()
+ "Run the code until hitting a HLT.
+The machine will also stop if the user presses a key."
+ (interactive)
+ (while (not (or (input-pending-p) (lmc-stopped-p)))
+ (lmc-step)
+ (sit-for 0.05)))
+
;;; The LMC assembly language editor.
(defvar lmc-asm-mode-map
@@ -532,6 +624,19 @@
(t (forward-comment (point-max)) (lmc-asm-indentation)))))
(defun lmc-asm-indent-line (&optional arg)
+ (save-excursion
+ (back-to-indentation)
+ (when (and (zerop (nth 0 (syntax-ppss)))
+ (looking-at (concat lmc-asm-mnemonic-names-re "\\_>")))
+ ;; Apparently the user forgot to parenthesize the instruction.
+ (insert "(")
+ (if (assq (read (current-buffer)) lmc-mnemonic-0-table)
+ (insert ")")
+ (let ((eol (line-end-position)))
+ (ignore-errors
+ (read (current-buffer))
+ (when (<= (point) eol)
+ (insert ")")))))))
(let ((indent (lmc-asm-indentation)))
(cond
((null indent) (lisp-indent-line arg))
@@ -567,7 +672,7 @@
(nreverse prog)))
(defun lmc-asm-load ()
- "Load current buffer into the LMC simluator."
+ "Load current buffer into the LMC simulator."
(interactive)
(let ((initialpos (point))
(window (if (eq (current-buffer) (window-buffer)) (selected-window))))
- [elpa] branch externals/lmc created (now cb110db), Stefan Monnier, 2020/11/28
- [elpa] externals/lmc 2e56da4 02/14: * lmc.el: Add a few more commands, and a tool-bar.,
Stefan Monnier <=
- [elpa] externals/lmc d248525 01/14: New package lmc., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc a5fe8d2 04/14: * lmc.el: Make it work on Emacs-22., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc b7cbf14 07/14: * lmc.el (lmc--sit-for): Fix last change., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc 3b26656 08/14: * lmc.el (lmc-asm-indentation): Indent to tab-width by default., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc 6cd76ac 05/14: * packages/lmc/lmc.el (lmc-store-word): match-data can change in sit-for., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc 0604c74 09/14: Add "rudel" to the list of externals., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc cb110db 14/14: * .gitignore: New file, Stefan Monnier, 2020/11/28
- [elpa] externals/lmc 4e16a02 10/14: * packages/lmc/lmc.el (lmc--load-word): Remove unused vars., Stefan Monnier, 2020/11/28
- [elpa] externals/lmc 47cc7a9 11/14: * lmc.el: Use cl-lib, Stefan Monnier, 2020/11/28
- [elpa] externals/lmc d0ca555 03/14: 2011-09-29 Stefan Monnier <address@hidden>, Stefan Monnier, 2020/11/28