1 ;;; Code generation-related routines for m68k-assembler.
3 ;;; Julian Squires/2005
5 (in-package :m68k-assembler
)
7 ;;;; OPERAND CONSTRAINTS
9 (defparameter *all-ea-modes
*
10 '(immediate pc-index pc-displacement absolute-long absolute-short
11 postincrement-indirect predecrement-indirect displacement-indirect
12 indexed-indirect vanilla-indirect address-register data-register
))
14 (defparameter *constraints-modes-table
*
15 `((all-ea-modes ,*all-ea-modes
*)
16 (alterable-modes ,(set-difference *all-ea-modes
*
17 '(immediate pc-index pc-displacement
)))
18 (data-alterable-modes ,(set-difference *all-ea-modes
*
19 '(immediate pc-index pc-displacement address-register
)))
20 (memory-alterable-modes ,(set-difference *all-ea-modes
*
21 '(immediate pc-index pc-displacement address-register data-register
)))
22 (data-addressing-modes ,(set-difference *all-ea-modes
*
24 (control-addressing-modes ,(set-difference *all-ea-modes
*
25 '(address-register data-register immediate predecrement-indirect
26 postincrement-indirect
)))
27 (movem-pre-modes (vanilla-indirect predecrement-indirect
28 displacement-indirect indexed-indirect absolute-short
30 (movem-post-modes (vanilla-indirect postincrement-indirect
31 displacement-indirect indexed-indirect absolute-short
32 absolute-long pc-displacement pc-index
))
33 (absolute (absolute-short absolute-long
))))
37 ;; displacement-indirect w/o expression => vanilla-indirect
38 ;; register dX => data-register
39 ;; register aX => address-register
40 ;; indirect w/pc => pc-indexed or pc-displacement
42 (defun refine-type (operand modifier
)
44 ;; XXX: should try to evaluate expression if it exists.
45 ((and (eq (car operand
) 'displacement-indirect
)
46 (zerop (indirect-displacement operand
'word
))
47 (address-register-p (indirect-base-register operand
)))
49 ((eq (car operand
) 'register
)
50 (case (char (caadr operand
) 0)
51 ((#\d
#\D
) 'data-register
)
52 ((#\a #\A
) 'address-register
)
54 ;; XXX: not sure if this is reasonable behavior, but it seems ok
56 ((eq (car operand
) 'absolute
)
58 ;(word 'absolute-short) (long 'absolute-long)
59 ;; default to absolute long, to avoid some gemdos
60 ;; relocation-related hassles. probably not the right thing to
65 (defun operand-type-matches-constraint-type-p (operand constraint modifier
)
66 (let ((refined-op (refine-type operand modifier
)))
67 (aif (find (car constraint
) *constraints-modes-table
* :key
#'car
)
68 (member refined-op
(second it
))
69 (eql refined-op
(car constraint
)))))
71 (defun satisfies-operand-constraints-p (operands constraints modifier
)
73 (when (equal (length operands
) (length constraints
))
74 (do ((op-> operands
(cdr op-
>))
75 (co-> constraints
(cdr co-
>)))
76 ((and (null op-
>) (null co-
>)) t
)
77 (unless (operand-type-matches-constraint-type-p (car op-
>)
80 (return-from top nil
))
81 (dolist (v-constraint (cdar co-
>))
82 ;; bind value to the first string or whatever
83 (let ((subbed (subst (cond ((eq (caar op-
>) 'immediate
)
84 (resolve-expression (cadar op-
>)))
85 ((eq (caar op-
>) 'register
)
87 (t (error "Dunno how to apply vconstraints to a ~A." (caar op-
>))))
88 'value v-constraint
)))
89 (unless (apply (car subbed
) (cdr subbed
))
90 (return-from top nil
))))))))
92 (defun satisfies-modifier-constraints-p (modifier mod-list
)
93 (cond ((null mod-list
) (null modifier
))
94 ((null modifier
) t
) ;; XXX for the moment
95 (t (member modifier mod-list
))))
98 (defun find-matching-entry (opcode operands modifier
)
99 (dolist (entry (cdr (find opcode
*asm-opcode-table
* :key
#'car
100 :test
#'string-equal
)))
101 (cond ((stringp entry
) ; redirect.
102 (awhen (find-matching-entry entry operands modifier
)
103 (format t
"~&redirecting: ~A ~A => ~A" opcode modifier entry
)
106 (when (and (satisfies-modifier-constraints-p modifier
108 (satisfies-operand-constraints-p operands
112 (t (error "Bad entry in opcode table: ~A: ~A" opcode entry
)))))
115 ;;;; CODE GENERATION HELPERS
117 ;;; Note that basically anything here wants to work with
118 ;;; pre-simplified parse trees.
120 (defun register-idx (operand &key both-sets
)
121 "If operand is a register or contains in some way a single register,
122 returns only the numeric index of that register, and the number of
123 bits that takes up, as a second value. Otherwise, an error is
127 (t (setf operand
(indirect-base-register operand
))))
129 (let ((idx (position (caadr operand
) *asm-register-table
*
130 :test
#'string-equal
:key
#'car
)))
131 (assert (<= 0 idx
15))
134 (values (logand idx
7) 3))))
136 (defun register-modifier (operand)
137 (assert (eql (car operand
) 'register
))
141 (defun data-register-p (operand)
142 (char-equal (char (caadr operand
) 0) #\D
))
143 (defun address-register-p (operand)
144 (char-equal (char (caadr operand
) 0) #\A
))
145 (defun pc-register-p (operand)
146 (string-equal (caadr operand
) "PC"))
148 (defun indirect-base-register (operand)
149 (find 'register
(cdr operand
) :key
#'carat
))
150 (defun indirect-index-register (operand)
151 (assert (eql (car operand
) 'indexed-indirect
))
152 (find 'register
(cdr operand
) :from-end t
:key
#'carat
))
153 (defun indirect-displacement (operand modifier
)
154 (let ((length (ecase modifier
(byte 8) (word 16))))
155 (cond ((= (length operand
) 2) 0)
156 ((and (consp (second operand
))
157 (eq (caadr operand
) 'register
)) 0)
158 (t (prog1 (resolve-expression (cadr operand
))
159 (fix-relocation-size length
))))))
161 (defun register-mask-list (operand &optional alterand
)
162 (let ((bitmask (make-array '(16) :element-type
'bit
:initial-element
0))
163 (flipped-p (and alterand
164 (eq (car alterand
) 'predecrement-indirect
))))
165 ;; iterate over operands
166 (dolist (r (if (eql (car operand
) 'register
)
169 (if (eql (car r
) 'register
)
170 (setf (aref bitmask
(register-idx r
:both-sets t
)) 1)
171 (warn "register-mask-list: ignoring ~A" r
)))
172 (values (bit-vector->int
(if flipped-p
(nreverse bitmask
) bitmask
))
175 (defun effective-address-mode (operand modifier
&key
(flipped-p nil
))
176 "Calculates the classic six-bit effective address mode and register
177 values. If FLIPPED-P, returns the mode and register values swapped."
178 (flet ((combine (m r
)
179 (logior (if flipped-p
(ash r
3) r
) (if flipped-p m
(ash m
3)))))
183 (cond ((data-register-p operand
) (combine 0 (register-idx operand
)))
184 ((address-register-p operand
)
185 (combine #b001
(register-idx operand
)))
186 ;; XXX I'm a bit iffy on this one, but so the book says...
187 ((string-equal (caadr operand
) "SR") (combine #b111
#b100
))))
188 (displacement-indirect
189 (let ((base (indirect-base-register operand
)))
190 (cond ((pc-register-p base
) (combine #b111
#b010
))
191 ((eql (refine-type operand modifier
) 'vanilla-indirect
)
192 (combine #b010
(register-idx base
)))
193 (t (combine #b101
(register-idx base
))))))
195 (let ((base (indirect-base-register operand
)))
196 (cond ((pc-register-p base
) (combine #b111
#b011
))
197 (t (combine #b110
(register-idx base
))))))
199 (ecase (refine-type operand modifier
)
200 (absolute-short (combine #b111
#b000
))
201 (absolute-long (combine #b111
#b001
))))
202 (immediate (combine #b111
#b100
))
203 (postincrement-indirect
204 (combine #b011
(register-idx (indirect-base-register operand
))))
205 (predecrement-indirect
206 (combine #b100
(register-idx (indirect-base-register operand
)))))
209 (defun effective-address-extra (operand modifier
)
210 "Returns the extra data required for addressing OPERAND, along with
211 the length of that data."
213 (register (values 0 0))
214 (displacement-indirect
215 (if (eql (refine-type operand modifier
) 'vanilla-indirect
)
217 (values (indirect-displacement operand
'word
) 16)))
219 (let ((index (indirect-index-register operand
)))
220 (if (not (pc-register-p (indirect-base-register operand
)))
221 ;; rrrr l000 dddd dddd
222 (values (logior (ash (register-idx index
:both-sets t
) 12)
223 (if (aand (register-modifier index
)
226 (logand (indirect-displacement operand
'byte
)
229 (error "Not sure how to encode this!")))) ;XXX
231 (ecase (refine-type operand modifier
)
232 (absolute-short (values (absolute-value operand
'word
) 16))
233 (absolute-long (values (absolute-value operand
'long
) 32))))
234 ;; XXX test extent of value
235 (immediate (values (immediate-value operand
)
236 (if (eql modifier
'long
) 32 16)))
237 (postincrement-indirect (values 0 0))
238 (predecrement-indirect (values 0 0))))
240 ;; XXX this name sucks (too much like ABS)
241 (defun absolute-value (operand &optional
(modifier 'word
))
242 (let ((length (ecase modifier
(byte 8) (long 32) (word 16))))
243 (when (and (consp operand
) (eql (first operand
) 'absolute
))
244 (setf operand
(second operand
)))
245 (values (prog1 (resolve-expression operand
)
246 (fix-relocation-size length
))
249 ;;; XXX defaults to nil.
250 (defun absolute-definitely-needs-long-p (operand)
251 (let ((v (resolve-expression (second operand
))))
252 (and (integerp v
) (or (< v -
32768)
255 (defun immediate-value (operand &optional modifier
)
256 "Returns a certain number of bits from the immediate value of
257 OPERAND, based on MODIFIER, if specified."
258 ;; XXX default length is long?
259 (let ((length (case modifier
((byte word
) 16) (long 32) (t 32))))
260 (values (prog1 (resolve-expression (second operand
))
261 (fix-relocation-size length
))
264 (defun addq-immediate-value (operand &optional modifier
)
265 "Special hack for ADDQ/SUBQ. Returns OPERAND mod 8 and the rest as
266 per IMMEDIATE-VALUE."
267 (multiple-value-bind (v l
) (immediate-value operand modifier
)
268 (values (mod v
8) l
)))
270 (defun modifier-bits (modifier)
271 (values (ecase modifier
(byte #b00
) (word #b01
) (long #b10
)) 2))
273 (defun modifier-bits-for-move (modifier)
274 (values (ecase modifier
(byte #b01
) (word #b11
) (long #b10
)) 2))
277 (defun branch-displacement-bits (operand modifier
&key db-p
)
278 "Returns displacement of OPERAND relative to *PROGRAM-COUNTER*. If
279 :DB-P is T, calculate displacement as per the DBcc opcodes (always
280 16-bit). Otherwise, calculate displacement as per Bcc opcodes, where
281 displacement is either 8 bits or 16 padded to 24."
282 (let ((value (absolute-value operand
(or modifier
283 (if db-p
'word
'byte
))))
284 (length (cond (db-p 16)
285 ((eq modifier
'byte
) 8)
287 ;; if there's a reloc at this pc, change to pc-relative
288 (pc-relativise-relocation)
289 (fix-relocation-size length
)
291 ;; Note PC+2 -- this is due to the way the m68k fetches
293 (values (or (and (integerp value
)
295 (- value
(if (oddp *program-counter
*)
296 (1+ *program-counter
*)
303 ;;;; TEMPLATE EVALUATOR
306 (defun make-codegen-sublis (operands modifier
)
307 (let ((sub (list (cons 'modifier modifier
))))
308 ;; Adjust as necessary for the number of operands possible.
309 (do ((a-> operands
(cdr a-
>))
310 (b-> '(first-operand second-operand third-operand
) (cdr b-
>)))
311 ((or (null a-
>) (null b-
>)) sub
)
312 (push (cons (car b-
>) (car a-
>)) sub
))))
314 (defun fill-codegen-template (list)
315 (do* ((item-> list
(cdr item-
>))
316 (fake-pc *program-counter
* (+ fake-pc
(/ length
8)))
317 (length (caar item-
>) (caar item-
>))
318 (formula (cadar item-
>) (cadar item-
>))
320 ((null item-
>) done-p
)
321 (when (consp formula
)
322 ;; We don't floor fake-pc because it should point out
323 ;; interesting bugs if we ever have to make use of it when it's
325 (let ((*program-counter
* fake-pc
))
326 (multiple-value-bind (val val-len
) (apply (car formula
)
328 (unless (integerp length
)
329 (setf length val-len
) ; for fake-pc
330 (setf (caar item-
>) val-len
))
331 ;; XXX shouldn't have to comment this out, but I do due to
332 ;; silly immediate-value hacks.
333 ;;(when (integerp val-len) (assert (= (caar item->) val-len)))
335 (setf (cadar item-
>) val
)
336 (setf done-p nil
)))))))
338 ;;; first do the sublis, then walk through each item doing the
339 ;;; evaluation when we can. if everything's reduced to an atom,
340 ;;; compile it and return it. otherwise, return the half-completed
343 ;; if the first is an integer, add it to the result-length. if the
344 ;; second is an atom, or it into the result. if the second is a list,
345 ;; evaluate it with first-operand, second-operand, and modifier bound.
347 (defun generate-code (template operands modifier
)
350 (list (sublis (make-codegen-sublis operands modifier
) template
)))
352 (cond ((fill-codegen-template list
)
354 (destructuring-bind (len val
) item
355 (setf result
(logior (ldb (byte len
0) val
)
357 (incf result-len len
)))
358 (values result result-len
))
359 (t (dolist (item list
) (incf result-len
(first item
)))
360 (values list result-len
)))))
362 ;;;; EOF codegen.lisp