Recently Written · git

forthlisp-stm32

STM32F446 target for forthlisp: Thumb-2 Forth kernel built by an SBCL assembler, QEMU netduinoplus2

git clone https://github.com/equwal/forthlisp-stm32

Log | Files | Refs


asm.lisp (11472 bytes)

1 ;;;; asm.lisp -- layer 2: a Thumb-2 (ARMv7-M + FPv4-SP) assembler on SBCL's sb-assem.
2 ;;;;
3 ;;;; sb-assem supplies the ISA-neutral parts: segments and labels; `fixup` resolves references. The encoders
4 ;;;; below are plain functions, one per instruction form, that emit little-endian halfwords
5 ;;;; into *seg*. asm-test.lisp checks every form against arm-none-eabi-as byte for byte.
6 
7 (defpackage :thumb (:use :cl)
8   (:export #:*seg* #:*base* #:new-segment #:segment-bytes #:here #:label #:mark #:label-address
9            #:h16 #:w32 #:abs32 #:align4 #:reg))
10 (in-package :thumb)
11 
12 (defvar *seg*)
13 (defvar *base* #x08000000 "Address of segment position 0.")
14 (defvar *fixups* '() "(position size function): bytes computed after all labels are placed.")
15 
16 (defun new-segment () (setf *fixups* '() *seg* (sb-assem:make-segment)))
17 (defun fixup (size fn)
18   "Reserve SIZE bytes; segment-bytes later calls (FN position) to emit them with labels known.
19 sb-assem's emit-back-patch resolved only the first of several patches in a row here (SBCL
20 2.6.9), so label references go through this list instead."
21   (push (list (sb-assem::segment-current-posn *seg*) size fn) *fixups*)
22   (dotimes (i size) (sb-assem:emit-byte *seg* 0)))
23 (defun segment-bytes ()
24   (sb-assem:finalize-segment *seg*)
25   (let ((v (copy-seq (coerce (sb-assem:segment-contents-as-vector *seg*) '(simple-array (unsigned-byte 8) (*)))))
26         (main *seg*))
27     (unwind-protect
28          (dolist (f *fixups* v)
29            (destructuring-bind (posn size fn) f
30              (setf *seg* (sb-assem:make-segment))
31              (funcall fn posn)
32              (sb-assem:finalize-segment *seg*)
33              (let ((bytes (sb-assem:segment-contents-as-vector *seg*)))
34                (assert (= (length bytes) size) () "fixup at ~d wrote ~d bytes, not ~d" posn (length bytes) size)
35                (replace v bytes :start1 posn))))
36       (setf *seg* main))))
37 (defun here () (sb-assem::segment-current-posn *seg*))
38 (defun label () (sb-assem:gen-label))
39 (defun mark (l) (sb-assem::%emit-label *seg* nil l) l)
40 (defun label-address (l) (+ *base* (sb-assem:label-position l)))
41 
42 (defun h16 (h) (sb-assem:emit-byte *seg* (ldb (byte 8 0) h)) (sb-assem:emit-byte *seg* (ldb (byte 8 8) h)))
43 (defun w32 (w) (h16 (ldb (byte 16 0) w)) (h16 (ldb (byte 16 16) w)))
44 (defun h32 (hw1 hw2) (h16 hw1) (h16 hw2))
45 (defun align4 () (loop until (zerop (mod (here) 4)) do (sb-assem:emit-byte *seg* 0)))
46 
47 (defun abs32 (l &optional (addend 0))
48   "A 32-bit cell holding label L's absolute address plus ADDEND (resolved at finalize)."
49   (fixup 4 (lambda (posn) (declare (ignore posn)) (w32 (+ (label-address l) addend)))))
50 
51 ;;; ---------- operands ----------
52 
53 (defun reg (r)
54   (let ((n (case r (:sp 13) (:lr 14) (:pc 15) (:ip 4) (:psp 5) (:tos 6) (:w 7)
55                  (t (if (and (symbolp r) (char= (char (symbol-name r) 0) #\R))
56                         (parse-integer (symbol-name r) :start 1)
57                         r)))))
58     (assert (and (integerp n) (<= 0 n 15)) () "bad register ~s" r)
59     n))
60 (defun sreg (s) (let ((n (parse-integer (symbol-name s) :start 1))) (assert (<= 0 n 31)) n))
61 
62 (defparameter *conds* '(:eq 0 :ne 1 :cs 2 :hs 2 :cc 3 :lo 3 :mi 4 :pl 5 :vs 6 :vc 7
63                         :hi 8 :ls 9 :ge 10 :lt 11 :gt 12 :le 13 :al 14))
64 (defun cnd (c) (or (getf *conds* c) (error "bad condition ~s" c)))
65 
66 ;;; ---------- 16-bit forms ----------
67 
68 (defun nop () (h16 #xBF00))
69 (defun bx (rm) (h16 (logior #x4700 (ash (reg rm) 3))))
70 (defun blx (rm) (h16 (logior #x4780 (ash (reg rm) 3))))
71 (defun mov (rd rm) "MOV Rd,Rm (T1, any registers, flags unchanged)."
72   (let ((d (reg rd))) (h16 (logior #x4600 (ash (ldb (byte 1 3) d) 7) (ash (reg rm) 3) (ldb (byte 3 0) d)))))
73 (defun it (c &optional (pattern "")) "IT block: PATTERN (string or keyword) of T/E for the 2nd..4th instructions."
74   (let* ((pattern (string pattern)) (fc (cnd c)) (bit0 (ldb (byte 1 0) fc)) (mask 0) (n (length pattern)))
75     (loop for ch across pattern for i downfrom 3
76           do (setf (ldb (byte 1 i) mask) (if (char-equal ch #\T) bit0 (- 1 bit0))))
77     (setf (ldb (byte 1 (- 3 n)) mask) 1)
78     (h16 (logior #xBF00 (ash fc 4) mask))))
79 (defun cmp-imm8 (rn imm) (assert (and (< (reg rn) 8) (<= 0 imm 255))) (h16 (logior #x2800 (ash (reg rn) 8) imm)))
80 
81 ;;; ---------- loads and stores ----------
82 
83 (defun mem12 (op rt rn off)
84   (assert (<= 0 off 4095))
85   (h32 (logior op (reg rn)) (logior (ash (reg rt) 12) off)))
86 (defun mem8 (op rt rn off mode)
87   "T4 form. MODE :pre (pre-index with writeback), :post (post-index), :neg (offset, no writeback)."
88   (let ((u (if (minusp off) 0 1)) (a (abs off)))
89     (assert (<= a 255))
90     (multiple-value-bind (p w) (ecase mode (:pre (values 1 1)) (:post (values 0 1)) (:neg (values 1 0)))
91       (h32 (logior op (reg rn))
92            (logior (ash (reg rt) 12) #x800 (ash p 10) (ash u 9) (ash w 8) a)))))
93 (macrolet ((def (name op12 op8)
94              `(defun ,name (rt rn &optional (off 0) mode)
95                 (if (and (null mode) (<= 0 off 4095))
96                     (mem12 ,op12 rt rn off)
97                     (mem8 ,op8 rt rn off (or mode :neg))))))
98   (def ldr #xF8D0 #xF850) (def str #xF8C0 #xF840)
99   (def ldrb #xF890 #xF810) (def strb #xF880 #xF800)
100   (def ldrh #xF8B0 #xF830) (def strh #xF8A0 #xF820)
101   (def ldrsb #xF990 #xF910) (def ldrsh #xF9B0 #xF930))
102 (defun push1 (r) (str r :sp -4 :pre))
103 (defun pop1 (r) (ldr r :sp 4 :post))
104 
105 ;;; ---------- data processing ----------
106 
107 (defparameter *shift-types* '(:lsl 0 :lsr 1 :asr 2 :ror 3))
108 (defun dp-reg (op rd rn rm &optional (shift :lsl) (amount 0) s)
109   "Data processing, shifted register (T2/T3 32-bit forms)."
110   (let ((amt (if (and (member shift '(:lsr :asr)) (= amount 32)) 0 amount)))
111     (assert (<= 0 amt 31))
112     (h32 (logior op (if s #x10 0) (reg rn))
113          (logior (ash (ldb (byte 3 2) amt) 12) (ash (reg rd) 8) (ash (ldb (byte 2 0) amt) 6)
114                  (ash (getf *shift-types* shift) 4) (reg rm)))))
115 (macrolet ((def (name op)
116              `(defun ,name (rd rn rm &optional (shift :lsl) (amount 0) s) (dp-reg ,op rd rn rm shift amount s))))
117   (def and-reg #xEA00) (def bic-reg #xEA20) (def orr-reg #xEA40) (def orn-reg #xEA60)
118   (def eor-reg #xEA80) (def add-reg #xEB00) (def adc-reg #xEB40) (def sbc-reg #xEB60)
119   (def sub-reg #xEBA0) (def rsb-reg #xEBC0))
120 (defun adds-reg (rd rn rm) (dp-reg #xEB00 rd rn rm :lsl 0 t))
121 (defun subs-reg (rd rn rm) (dp-reg #xEBA0 rd rn rm :lsl 0 t))
122 (defun mov-shift (rd rm shift amount) (dp-reg #xEA40 rd 15 rm shift amount))
123 (defun mvn-reg (rd rm) (dp-reg #xEA60 rd 15 rm))
124 (defun cmp-reg (rn rm) (dp-reg #xEBA0 15 rn rm :lsl 0 t))
125 (defun tst-reg (rn rm) (dp-reg #xEA00 15 rn rm :lsl 0 t))
126 (defun shift-reg (op rd rn rm) (h32 (logior op (reg rn)) (logior #xF000 (ash (reg rd) 8) (reg rm))))
127 (defun lsl-reg (rd rn rm) (shift-reg #xFA00 rd rn rm))
128 (defun lsr-reg (rd rn rm) (shift-reg #xFA20 rd rn rm))
129 (defun asr-reg (rd rn rm) (shift-reg #xFA40 rd rn rm))
130 
131 (defun imm12-form (op rd rn imm)
132   (assert (<= 0 imm 4095))
133   (h32 (logior op (ash (ldb (byte 1 11) imm) 10) (reg rn))
134        (logior (ash (ldb (byte 3 8) imm) 12) (ash (reg rd) 8) (ldb (byte 8 0) imm))))
135 (defun addw (rd rn imm) (imm12-form #xF200 rd rn imm))
136 (defun subw (rd rn imm) (imm12-form #xF2A0 rd rn imm))
137 (defun mov16-form (op rd imm)
138   (assert (<= 0 imm #xFFFF))
139   (h32 (logior op (ash (ldb (byte 1 11) imm) 10) (ldb (byte 4 12) imm))
140        (logior (ash (ldb (byte 3 8) imm) 12) (ash (reg rd) 8) (ldb (byte 8 0) imm))))
141 (defun movw (rd imm) (mov16-form #xF240 rd imm))
142 (defun movt (rd imm) (mov16-form #xF2C0 rd imm))
143 (defun li (rd value)
144   "Load a 32-bit constant, or a label's absolute address, with MOVW (+ MOVT)."
145   (if (integerp value)
146       (let ((v (ldb (byte 32 0) value)))
147         (movw rd (ldb (byte 16 0) v))
148         (unless (zerop (ldb (byte 16 16) v)) (movt rd (ldb (byte 16 16) v))))
149       (fixup 8 (lambda (posn) (declare (ignore posn))
150                  (let ((v (label-address value)))
151                    (movw rd (ldb (byte 16 0) v)) (movt rd (ldb (byte 16 16) v)))))))
152 (defun mvn-0 (rd) "MVN.W Rd,#0, i.e. Rd = -1." (h32 #xF06F (ash (reg rd) 8)))
153 (defun mul (rd rn rm) (h32 (logior #xFB00 (reg rn)) (logior #xF000 (ash (reg rd) 8) (reg rm))))
154 (defun mls (rd rn rm ra) "Rd = Ra - Rn*Rm" (h32 (logior #xFB00 (reg rn)) (logior (ash (reg ra) 12) (ash (reg rd) 8) #x10 (reg rm))))
155 (defun sdiv (rd rn rm) (h32 (logior #xFB90 (reg rn)) (logior #xF0F0 (ash (reg rd) 8) (reg rm))))
156 (defun udiv (rd rn rm) (h32 (logior #xFBB0 (reg rn)) (logior #xF0F0 (ash (reg rd) 8) (reg rm))))
157 (defun smull (lo hi rn rm) (h32 (logior #xFB80 (reg rn)) (logior (ash (reg lo) 12) (ash (reg hi) 8) (reg rm))))
158 (defun umull (lo hi rn rm) (h32 (logior #xFBA0 (reg rn)) (logior (ash (reg lo) 12) (ash (reg hi) 8) (reg rm))))
159 (defun clz (rd rm) (h32 (logior #xFAB0 (reg rm)) (logior #xF080 (ash (reg rd) 8) (reg rm))))
160 
161 ;;; ---------- branches ----------
162 
163 (defun branch-patch (target encode)
164   (fixup 4 (lambda (posn) (funcall encode (- (sb-assem:label-position target) (+ posn 4))))))
165 (defun enc-b-t4 (off link)
166   (assert (and (evenp off) (<= (- (ash 1 24)) off (1- (ash 1 24)))) () "branch out of range")
167   (let* ((s (ldb (byte 1 24) off)) (i1 (ldb (byte 1 23) off)) (i2 (ldb (byte 1 22) off))
168          (j1 (logxor 1 i1 s)) (j2 (logxor 1 i2 s)))
169     (h32 (logior #xF000 (ash s 10) (ldb (byte 10 12) off))
170          (logior (if link #xD000 #x9000) (ash j1 13) (ash j2 11) (ldb (byte 11 1) off)))))
171 (defun enc-bcond-t3 (c off)
172   (assert (and (evenp off) (<= (- (ash 1 20)) off (1- (ash 1 20)))) () "conditional branch out of range")
173   (h32 (logior #xF000 (ash (ldb (byte 1 20) off) 10) (ash (cnd c) 6) (ldb (byte 6 12) off))
174        (logior #x8000 (ash (ldb (byte 1 18) off) 13) (ash (ldb (byte 1 19) off) 11) (ldb (byte 11 1) off))))
175 (defun b (target) (branch-patch target (lambda (off) (enc-b-t4 off nil))))
176 (defun bl (target) (branch-patch target (lambda (off) (enc-b-t4 off t))))
177 (defun bcc (c target) (branch-patch target (lambda (off) (enc-bcond-t3 c off))))
178 ;;; ---------- FPv4-SP (single-precision VFP) ----------
179 
180 (defun sreg (s) (let ((n (parse-integer (symbol-name s) :start 1))) (assert (<= 0 n 31)) n))
181 (defun vfp-dnm (op1 op2 sd sn sm)
182   "Three-register VFP data processing: OP1/OP2 are the fixed bits of hw1/hw2."
183   (let ((d (sreg sd)) (n (sreg sn)) (m (sreg sm)))
184     (h32 (logior op1 (ash (ldb (byte 1 0) d) 6) (ash n -1))
185          (logior op2 (ash (ash d -1) 12) (ash (ldb (byte 1 0) n) 7) (ash (ldb (byte 1 0) m) 5) (ash m -1)))))
186 (defun vadd (sd sn sm) (vfp-dnm #xEE30 #x0A00 sd sn sm))
187 (defun vsub (sd sn sm) (vfp-dnm #xEE30 #x0A40 sd sn sm))
188 (defun vmul (sd sn sm) (vfp-dnm #xEE20 #x0A00 sd sn sm))
189 (defun vdiv (sd sn sm) (vfp-dnm #xEE80 #x0A00 sd sn sm))
190 (defun vfp-dm (op1 op2 sd sm)
191   (let ((d (sreg sd)) (m (sreg sm)))
192     (h32 (logior op1 (ash (ldb (byte 1 0) d) 6))
193          (logior op2 (ash (ash d -1) 12) (ash (ldb (byte 1 0) m) 5) (ash m -1)))))
194 (defun vcvt-f32-s32 (sd sm) (vfp-dm #xEEB8 #x0AC0 sd sm))
195 (defun vcvt-s32-f32 (sd sm) "Round toward zero." (vfp-dm #xEEBD #x0AC0 sd sm))
196 (defun vsqrt (sd sm) (vfp-dm #xEEB1 #x0AC0 sd sm))
197 (defun vcmp (sd sm) (vfp-dm #xEEB4 #x0A40 sd sm))
198 (defun vmrs-apsr () "VMRS APSR_nzcv, FPSCR" (h32 #xEEF1 #xFA10))
199 (defun vmov-s-r (sn rt) "VMOV Sn, Rt"
200   (let ((n (sreg sn))) (h32 (logior #xEE00 (ash n -1)) (logior (ash (reg rt) 12) #x0A10 (ash (ldb (byte 1 0) n) 7)))))
201 (defun vmov-r-s (rt sn) "VMOV Rt, Sn"
202   (let ((n (sreg sn))) (h32 (logior #xEE10 (ash n -1)) (logior (ash (reg rt) 12) #x0A10 (ash (ldb (byte 1 0) n) 7)))))