1 ;;;;------------------------------------------------------------------
3 ;;;; Copyright (C) 2007 Frode V. Fjeld
5 ;;;; Description: Assembly syntax etc.
6 ;;;; Author: Frode Vatvedt Fjeld <frodef@acm.org>
7 ;;;; Distribution: See the accompanying file COPYING.
9 ;;;; $Id: asm.lisp,v 1.14 2008/02/18 22:30:45 ffjeld Exp $
11 ;;;;------------------------------------------------------------------
15 (:export
#:symbol-reference-p
17 #:symbol-reference-symbol
25 #:retry-symbol-resolve
28 #:disassemble-proglist
31 #:*instruction-compute-extra-prefix-map
*
32 #:*position-independent-p
*
33 #:*sub-program-instructions
*))
37 (defvar *pc
* nil
"Current program counter.")
38 (defvar *symtab
* nil
"Current symbol table.")
39 (defvar *instruction-compute-extra-prefix-map
* nil
)
40 (defvar *position-independent-p
* t
)
41 (defvar *sub-program-instructions
* '(:jmp
:ret
:iretd
)
42 "Instruction operators after which to insert sub-programs.")
44 (defvar *anonymous-sub-program-identities
* nil
)
47 "Is x a symbol (in any package) named 'quote'?"
48 ;; This is required because of Movitz package-fiddling.
52 (deftype simple-symbol-reference
()
53 '(cons (satisfies quotep
) (cons symbol null
)))
55 (deftype sub-program-operand
()
56 '(cons (satisfies quotep
)
58 (cons (eql :sub-program
))
61 (deftype funcall-operand
()
62 '(cons (satisfies quotep
)
67 (deftype symbol-reference
()
68 '(or simple-symbol-reference sub-program-operand
))
70 (defun sub-program-operand-p (expr)
71 (typep expr
'sub-program-operand
))
73 (defun sub-program-label (operand)
74 (let ((x (cadadr operand
)))
77 (cdr (or (assoc operand
*anonymous-sub-program-identities
*)
78 (car (push (cons operand
(gensym "sub-program-"))
79 *anonymous-sub-program-identities
*)))))))
81 (defun sub-program-program (operand)
84 (defun symbol-reference-symbol (expr)
86 (simple-symbol-reference
89 (sub-program-label expr
))))
91 (defun funcall-operand-operator (operand)
94 (defun funcall-operand-operands (operand)
97 (deftype immediate-operand
()
98 '(or integer symbol-reference funcall-operand
))
100 (defun immediate-p (expr)
101 (typep expr
'immediate-operand
))
103 (deftype register-operand
()
106 (defun register-p (operand)
107 (typep operand
'register-operand
))
109 (deftype indirect-operand
()
110 '(and cons
(not (cons (satisfies quotep
)))))
112 (defun indirect-operand-p (operand)
113 (typep operand
'indirect-operand
))
115 (deftype pc-relative-operand
()
118 (defun pc-relative-operand-p (operand)
119 (typep operand
'pc-relative-operand
))
121 (defun pc-relative-operand-offset (operand)
122 (check-type operand pc-relative-operand
)
125 (define-condition unresolved-symbol
()
128 :reader unresolved-symbol
))
129 (:report
(lambda (c s
)
130 (format s
"Unresolved symbol ~S." (unresolved-symbol c
)))))
134 (defun resolve-operand (operand)
139 (let ((s (symbol-reference-symbol operand
)))
140 (loop (with-simple-restart (retry-symbol-resolve "Retry resolving ~S." s
)
141 (return (cdr (or (assoc s
*symtab
*)
142 (error 'unresolved-symbol
145 (apply (funcall-operand-operator operand
)
146 (mapcar #'resolve-operand
147 (funcall-operand-operands operand
))))
152 (defun assemble-proglist (proglist &key
((:symtab incoming-symtab
) *symtab
*) corrections
(start-pc 0) (cpu-package '#:asm-x86
))
153 "Encode a proglist, using instruction-encoder in symbol assemble-instruction from cpu-package."
154 (let ((encoder (find-symbol (string '#:assemble-instruction
) cpu-package
))
156 (*symtab
* (append incoming-symtab corrections
))
157 (*anonymous-sub-program-identities
* *anonymous-sub-program-identities
*)
159 (new-corrections nil
)
161 (flet ((process-instruction (instruction)
162 (etypecase instruction
163 ((or symbol integer
) ; a label?
164 (let ((previous-definition (assoc instruction
*symtab
*)))
166 ((null previous-definition
)
167 (push (cons instruction
*pc
*)
169 ((assoc instruction new-corrections
)
170 (break "prev-def ~S in new-corrections?? new: ~S, old: ~S"
173 (cdr (assoc instruction new-corrections
))))
174 ((member previous-definition assumptions
)
175 (setf (cdr previous-definition
) *pc
*)
176 (setf assumptions
(delete previous-definition assumptions
))
177 (push previous-definition new-corrections
))
178 ((member previous-definition corrections
)
180 ((> *pc
* (cdr previous-definition
))
181 ;; (warn "correcting ~S from ~D to ~D" instruction (cdr previous-definition) *pc*)
182 (setf (cdr previous-definition
) *pc
*)
183 (push previous-definition new-corrections
))
184 ((< *pc
* (cdr previous-definition
))
185 ;; (break "Definition for ~S shrunk from ~S to ~S."
187 ;; (cdr previous-definition)
189 (setf (cdr previous-definition
) *pc
*)
190 (push previous-definition new-corrections
))))
191 (t (error "Label ~S doubly defined. Old value: ~S, new value: ~S"
193 (cdr previous-definition
)
196 (cons ; a bona fide instruction?
197 (let ((code (funcall encoder instruction
)))
198 (incf *pc
* (length code
))
201 ((unresolved-symbol (lambda (c)
202 (let ((a (cons (unresolved-symbol c
) *pc
*)))
203 ;; (warn "assuming ~S for ~S" (unresolved-symbol c) *pc*)
206 (invoke-restart 'retry-symbol-resolve
)))))
207 (let ((code (loop for instruction in proglist
208 for operands
= (when (consp instruction
)
210 for operator
= (when (consp instruction
)
211 (let ((x (pop operands
)))
212 (if (not (listp x
)) x
(pop operands
))))
213 append
(process-instruction instruction
)
214 do
(loop for operand in operands
215 do
(when (sub-program-operand-p operand
)
216 (push (cons (sub-program-label operand
)
217 (sub-program-program operand
))
219 when
(and (not (null sub-programs
))
220 (member operator
*sub-program-instructions
*))
221 append
(loop for sub-program in
(nreverse sub-programs
)
222 append
(mapcan #'process-instruction sub-program
)
223 finally
(setf sub-programs nil
)))))
225 ((not (null assumptions
))
226 (error "Undefined symbol~P: ~{~S~^, ~}"
228 (mapcar #'car assumptions
)))
229 ((not (null new-corrections
))
230 (assemble-proglist proglist
231 :symtab incoming-symtab
233 :cpu-package cpu-package
234 :corrections
(nconc new-corrections corrections
)))
235 (t (values code
*symtab
*))))))))
237 (defun instruction-operands (instruction)
238 (if (listp (car instruction
)) ; skip any instruction prefixes etc.
243 (defun disassemble-proglist (code &key
(cpu-package '#:asm-x86
) (pc (or *pc
* 0)) (symtab *symtab
*))
244 (let* ((instruction-disassembler (find-symbol (string '#:disassemble-instruction
)
246 (proglist0 (loop while code
248 collect
(multiple-value-bind (instruction new-code
)
249 (funcall instruction-disassembler
251 (when (eq code new-code
)
253 (loop until
(eq code new-code
)
255 (setf code
(cdr code
)))
256 (let ((operands (instruction-operands instruction
)))
257 (if (notany #'pc-relative-operand-p operands
)
259 (nconc (loop until
(eq instruction operands
)
260 collect
(pop instruction
))
261 (loop for operand in operands
262 collect
(if (not (pc-relative-operand-p operand
))
264 (let* ((location (+ pc
(pc-relative-operand-offset operand
)))
265 (entry (or (rassoc location symtab
)
266 (car (push (cons (gensym) location
)
268 `(quote ,(car entry
))))))))))))
269 (values (loop for
(pc instruction
) on proglist0 by
#'cddr
270 when
(car (rassoc pc symtab
))