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
)
72 (when (equal (length operands
) (length constraints
))
73 (do ((op-> operands
(cdr op-
>))
74 (co-> constraints
(cdr co-
>)))
75 ((and (null op-
>) (null co-
>)) t
)
76 (unless (operand-type-matches-constraint-type-p (car op-
>)
80 (dolist (v-constraint (cdar co-
>))
81 (format t
"~&v-constraint: ~A" v-constraint
)))))
83 (defun satisfies-modifier-constraints-p (modifier mod-list
)
84 (cond ((null mod-list
) (null modifier
))
85 ((null modifier
) t
) ;; XXX for the moment
86 (t (member modifier mod-list
))))
89 (defun find-matching-entry (opcode operands modifier
)
90 (dolist (entry (cdr (find opcode
*asm-opcode-table
* :key
#'car
91 :test
#'string-equal
)))
92 (cond ((stringp entry
) ; redirect.
93 (awhen (find-matching-entry entry operands modifier
)
94 (format t
"~&redirecting: ~A ~A => ~A" opcode modifier entry
)
97 (when (and (satisfies-modifier-constraints-p modifier
99 (satisfies-operand-constraints-p operands
103 (t (error "Bad entry in opcode table: ~A: ~A" opcode entry
)))))
106 ;;;; CODE GENERATION HELPERS
108 ;;; Note that basically anything here wants to work with
109 ;;; pre-simplified parse trees.
111 (defun register-idx (operand &key both-sets
)
112 "If operand is a register or contains in some way a single register,
113 returns only the numeric index of that register, and the number of
114 bits that takes up, as a second value. Otherwise, an error is
118 (t (setf operand
(indirect-base-register operand
))))
120 (let ((idx (position (caadr operand
) *asm-register-table
*
121 :test
#'string-equal
:key
#'car
)))
122 (assert (<= 0 idx
15))
125 (values (logand idx
7) 3))))
127 (defun register-modifier (operand)
128 (assert (eql (car operand
) 'register
))
132 (defun data-register-p (operand)
133 (char-equal (char (caadr operand
) 0) #\D
))
134 (defun address-register-p (operand)
135 (char-equal (char (caadr operand
) 0) #\A
))
136 (defun pc-register-p (operand)
137 (string-equal (caadr operand
) "PC"))
139 (defun indirect-base-register (operand)
140 (find 'register
(cdr operand
) :key
#'carat
))
141 (defun indirect-index-register (operand)
142 (assert (eql (car operand
) 'indexed-indirect
))
143 (find 'register
(cdr operand
) :from-end t
:key
#'carat
))
144 (defun indirect-displacement (operand modifier
)
145 (let ((length (ecase modifier
(byte 8) (word 16))))
146 (cond ((= (length operand
) 2) 0)
147 (t (prog1 (resolve-expression (cadr operand
))
148 (fix-relocation-size length
))))))
150 (defun register-mask-list (operand &optional alterand
)
151 (let ((bitmask (make-array '(16) :element-type
'bit
:initial-element
0))
152 (flipped-p (and alterand
153 (eq (car alterand
) 'predecrement-indirect
))))
154 ;; iterate over operands
155 (dolist (r (if (eql (car operand
) 'register
)
158 (if (eql (car r
) 'register
)
159 (setf (aref bitmask
(register-idx r
:both-sets t
)) 1)
160 (warn "register-mask-list: ignoring ~A" r
)))
161 (values (bit-vector->int
(if flipped-p
(nreverse bitmask
) bitmask
))
164 (defun effective-address-mode (operand modifier
&key
(flipped-p nil
))
165 "Calculates the classic six-bit effective address mode and register
166 values. If FLIPPED-P, returns the mode and register values swapped."
167 (flet ((combine (m r
)
168 (logior (if flipped-p
(ash r
3) r
) (if flipped-p m
(ash m
3)))))
172 (cond ((data-register-p operand
) (combine 0 (register-idx operand
)))
173 ((address-register-p operand
)
174 (combine #b001
(register-idx operand
)))
175 ;; XXX I'm a bit iffy on this one, but so the book says...
176 ((string-equal (caadr operand
) "SR") (combine #b111
#b100
))))
177 (displacement-indirect
178 (let ((base (indirect-base-register operand
)))
179 (cond ((pc-register-p base
) (combine #b111
#b010
))
180 ((eql (refine-type operand modifier
) 'vanilla-indirect
)
181 (combine #b010
(register-idx base
)))
182 (t (combine #b101
(register-idx base
))))))
184 (let ((base (indirect-base-register operand
)))
185 (cond ((pc-register-p base
) (combine #b111
#b011
))
186 (t (combine #b110
(register-idx base
))))))
188 (ecase (refine-type operand modifier
)
189 (absolute-short (combine #b111
#b000
))
190 (absolute-long (combine #b111
#b001
))))
191 (immediate (combine #b111
#b100
))
192 (postincrement-indirect
193 (combine #b011
(register-idx (indirect-base-register operand
))))
194 (predecrement-indirect
195 (combine #b100
(register-idx (indirect-base-register operand
)))))
198 (defun effective-address-extra (operand modifier
)
199 "Returns the extra data required for addressing OPERAND, along with
200 the length of that data."
202 (register (values 0 0))
203 (displacement-indirect
204 (if (eql (refine-type operand modifier
) 'vanilla-indirect
)
206 (values (indirect-displacement operand
'word
) 16)))
208 (let ((index (indirect-index-register operand
)))
209 (if (not (pc-register-p (indirect-base-register operand
)))
210 ;; rrrr l000 dddd dddd
211 (values (logior (ash (register-idx index
:both-sets t
) 12)
212 (if (aand (register-modifier index
)
215 (logand (indirect-displacement operand
'byte
)
218 (error "Not sure how to encode this!")))) ;XXX
220 (ecase (refine-type operand modifier
)
221 (absolute-short (values (absolute-value operand
'word
) 16))
222 (absolute-long (values (absolute-value operand
'long
) 32))))
223 ;; XXX test extent of value
224 (immediate (values (immediate-value operand
)
225 (if (eql modifier
'long
) 32 16)))
226 (postincrement-indirect (values 0 0))
227 (predecrement-indirect (values 0 0))))
229 ;; XXX this name sucks (too much like ABS)
230 (defun absolute-value (operand &optional
(modifier 'word
))
231 (let ((length (ecase modifier
(byte 8) (long 32) (word 16))))
232 (when (and (consp operand
) (eql (first operand
) 'absolute
))
233 (setf operand
(second operand
)))
234 (values (prog1 (resolve-expression operand
)
235 (fix-relocation-size length
))
238 ;;; XXX defaults to nil.
239 (defun absolute-definitely-needs-long-p (operand)
240 (let ((v (resolve-expression (second operand
))))
241 (and (integerp v
) (or (< v -
32768)
244 (defun immediate-value (operand &optional modifier
)
245 "Returns a certain number of bits from the immediate value of
246 OPERAND, based on MODIFIER, if specified."
247 ;; XXX default length is long?
248 (let ((length (case modifier
((byte word
) 16) (long 32) (t 32))))
249 (values (prog1 (resolve-expression (second operand
))
250 (fix-relocation-size length
))
253 (defun addq-immediate-value (operand &optional modifier
)
254 "Special hack for ADDQ/SUBQ. Returns OPERAND mod 8 and the rest as
255 per IMMEDIATE-VALUE."
256 (multiple-value-bind (v l
) (immediate-value operand modifier
)
257 (values (mod v
8) l
)))
259 (defun modifier-bits (modifier)
260 (values (ecase modifier
(byte #b00
) (word #b01
) (long #b10
)) 2))
262 (defun modifier-bits-for-move (modifier)
263 (values (ecase modifier
(byte #b01
) (word #b11
) (long #b10
)) 2))
266 (defun branch-displacement-bits (operand modifier
&key db-p
)
267 "Returns displacement of OPERAND relative to *PROGRAM-COUNTER*. If
268 :DB-P is T, calculate displacement as per the DBcc opcodes (always
269 16-bit). Otherwise, calculate displacement as per Bcc opcodes, where
270 displacement is either 8 bits or 16 padded to 24."
271 (let ((value (absolute-value operand
(or modifier
272 (if db-p
'word
'byte
))))
273 (length (cond (db-p 16)
274 ((eq modifier
'byte
) 8)
276 ;; if there's a reloc at this pc, change to pc-relative
277 (pc-relativise-relocation)
278 (fix-relocation-size length
)
280 ;; Note PC+2 -- this is due to the way the m68k fetches
282 (values (or (and (integerp value
)
284 (- value
(if (oddp *program-counter
*)
285 (1+ *program-counter
*)
292 ;;;; TEMPLATE EVALUATOR
295 (defun make-codegen-sublis (operands modifier
)
296 (let ((sub (list (cons 'modifier modifier
))))
297 ;; Adjust as necessary for the number of operands possible.
298 (do ((a-> operands
(cdr a-
>))
299 (b-> '(first-operand second-operand third-operand
) (cdr b-
>)))
300 ((or (null a-
>) (null b-
>)) sub
)
301 (push (cons (car b-
>) (car a-
>)) sub
))))
303 (defun fill-codegen-template (list)
304 (do* ((item-> list
(cdr item-
>))
305 (fake-pc *program-counter
* (+ fake-pc
(/ length
8)))
306 (length (caar item-
>) (caar item-
>))
307 (formula (cadar item-
>) (cadar item-
>))
309 ((null item-
>) done-p
)
310 (when (consp formula
)
311 ;; We don't floor fake-pc because it should point out
312 ;; interesting bugs if we ever have to make use of it when it's
314 (let ((*program-counter
* fake-pc
))
315 (multiple-value-bind (val val-len
) (apply (car formula
)
317 (unless (integerp length
)
318 (setf length val-len
) ; for fake-pc
319 (setf (caar item-
>) val-len
))
320 ;; XXX shouldn't have to comment this out, but I do due to
321 ;; silly immediate-value hacks.
322 ;;(when (integerp val-len) (assert (= (caar item->) val-len)))
324 (setf (cadar item-
>) val
)
325 (setf done-p nil
)))))))
327 ;;; first do the sublis, then walk through each item doing the
328 ;;; evaluation when we can. if everything's reduced to an atom,
329 ;;; compile it and return it. otherwise, return the half-completed
332 ;; if the first is an integer, add it to the result-length. if the
333 ;; second is an atom, or it into the result. if the second is a list,
334 ;; evaluate it with first-operand, second-operand, and modifier bound.
336 (defun generate-code (template operands modifier
)
339 (list (sublis (make-codegen-sublis operands modifier
) template
)))
341 (cond ((fill-codegen-template list
)
343 (destructuring-bind (len val
) item
344 (setf result
(logior (ldb (byte len
0) val
)
346 (incf result-len len
)))
347 (values result result-len
))
348 (t (dolist (item list
) (incf result-len
(first item
)))
349 (values list result-len
)))))
351 ;;;; EOF codegen.lisp