3 (in-package :cl-x86-asm
)
5 ;; x86 can assemble in one of two modes BITS32 or BITS16
6 (defparameter *current-bits-size
* 32)
8 ;; switch between modes
10 "(switch-bits) switch assembly mode between 32 bits and 16"
11 (if (= *current-bits-size
* 32)
12 (setf *current-bits-size
* 16)
13 (setf *current-bits-size
* 32)))
15 ;; -- the actual assembler -----------------------------------------------------
19 ;; -- the actual assembler -----------------------------------------------------
21 ;; to do -- lookup across different segments?
22 ;; to do -- character operands?
23 (defun operand-value (operand)
24 "(operand-value operand) - given operand which may be a number or a symbol
25 then reduce to it's numeric value"
31 (find-symbol (symbol-name operand
)
32 *current-segment-package
*)))
37 (symbol-name operand
) 'keyword
)))
40 (error "Invalid operand ~A " operand
))))))
41 (t (error "Invalid operand ~A " operand
))))
43 (defun operands-match-types-p (operands types
)
44 "(is-operand-type-p operand-type operand) Given an operand type (eg :reg32)
45 and an operand (eg :EAX), returns t if that operand is of the correct type"
47 ((operand-matches-type-p (op ty
)
49 (cadr (assoc ty
*operand-type-checkers
*))
52 #'(lambda (a b
) (and a b
))
54 #'operand-matches-type-p operands types
))))
56 (defun encoding-length (encoding)
57 "(encoding-length encoding) -- return the 'length' or weight of an encoding
58 used when disambiguating potentially ambigous encodings"
60 (if (eq :ib
(first (last encoding
))) 1 0)
61 (if (eq :iw
(first (last encoding
))) 2 0)
62 (if (eq :id
(first (last encoding
))) 4 0)))
64 (defun one-description-p
66 "(one-description-p descriptions-list) Quickie predicate to
67 check if we have one description left in our candidate list"
68 (= (length descriptions
) 1))
70 (defun filter-descriptions-by-number (operands descriptions
)
71 "(filter-descriptions-by-number operands descriptions) Given an
72 operand list and a list of possible descriptions: pairs then
73 return only those where the number of operands in the
74 descriptions matches the number of operands given"
76 for
(types encoding
) in descriptions
77 when
(or (= (length types
) (length operands
))
78 (and (zerop (length operands
)) (eq (first types
) :|none|
)))
79 collect
(list types encoding
)))
81 (defun filter-descriptions-by-type (operands descriptions
)
82 "(filter-descriptions-by-type operands descriptions) Return
83 only those descriptions that match the types of the operands"
85 for
(types encoding
) in descriptions
86 when
(operands-match-types-p operands types
)
87 collect
(list types encoding
)))
89 (defun filter-descriptions-by-simplicity (operands descriptions
)
90 "(pick-simplest-description descriptions) Look for a description with an operand
91 type that is a literal register keyword - an automatic win"
92 (declare (ignore operands
) (optimize (debug 3) (safety 1) (compilation-speed 0) (speed 0)))
95 for
(types encoding
) in descriptions
96 for index
= 0 then
(1+ index
)
97 when
(contains-keyword '(:STO
:CX
:ECX
:DX
:CL
:AL
:AX
:EAX
) encoding
)
98 collect
(list types encoding
))
99 descriptions
)) ;; if we don't find anything, pass along to the next filter
102 (defun pick-smallest-description (descriptions)
103 "(filter-descriptions-by-type operands descriptions)
104 Return the description that is likely to result in the
105 smallest possible encoding"
107 ((candidate-index 0))
109 for
(types encoding
) in descriptions
110 for index
= 0 then
(1+ index
)
111 when
(< (encoding-length encoding
)
113 (second (nth index descriptions
))))
115 (setf candidate-index index
))
116 (nth candidate-index descriptions
)))
118 ;; to do ? what if we *still* have alternate encodings
119 ;; adfter going through *those* filters?
121 (defun find-correct-description (operands
122 candidate-descriptions
)
123 "(find-correct-description operands candidate-descrptions)
124 Given a list of operands for an instruction and a list of possible
125 descriptions as a list of (types encoding) pair lists then find
126 the description which most likely satisfies the operands"
127 (macrolet ((with-description-filter (filterfn &rest body
)
128 `(let ((candidate-descriptions
129 (apply ,filterfn
(list operands candidate-descriptions
))))
130 (format *debug-io
* "Filtered Descriptions ~A~&"
131 candidate-descriptions
)
132 (if (one-description-p candidate-descriptions
)
133 (first candidate-descriptions
)
136 "Operands ~A ~&Possible Descriptions ~A~&"
137 operands candidate-descriptions
)
138 (with-description-filter
139 #'filter-descriptions-by-number
140 (with-description-filter
141 #'filter-descriptions-by-type
142 (with-description-filter
143 #'filter-descriptions-by-simplicity
144 (first candidate-descriptions
))))))
147 (defun process-opcodes (insn-name operands types encoding instruction
)
148 (format *debug-io
* "Processin instruction ~A~&" insn-name
)
149 (format *debug-io
* "With operands ~A~&" operands
)
150 (format *debug-io
* "Matching types ~A~&" types
)
151 (format *debug-io
* "Using encoding ~A~&" encoding
)
156 (setf (get instruction
:opcodes
)
157 (append (get instruction
:opcodes
) (list code
)))
158 (progn (format *debug-io
* "~A~&" (gethash code
*opcode-encoder-table
*))
159 (funcall (gethash code
*opcode-encoder-table
*)
160 insn-name operands types encoding instruction
)))))
162 ;; turn an encoding into a sequcnce of bytes with no symbols
164 (defun process-encoding (insn-name operands description
)
165 "(process-encoding instruction operand operand-description) Given an instruction,
166 it's operands and it's description produce a stream of bytes for assembly, taking
167 it's encoding into account"
168 (let ((result (make-instruction)))
170 "Processing encoding of instruction ~A w operands ~A using description ~A~&"
171 insn-name operands description
)
172 (process-opcodes insn-name operands
173 (first description
) (second description
)
175 (print-instruction "Resulting " result
)
176 (flatten-instruction result
)))
178 ;; to do -- need to put instructions in keyword package,
179 ;; hash-table to lookup
180 (defun assemble-instruction-or-directive (instruction operands
)
181 "(assemble-instruction-or-directive instruction operand) Given the itstruction
182 and operand portion of an assembler form, assemble it in the current segment"
183 ;; deal with instruction
184 (format *debug-io
* "Looking up instruction ~A~&" instruction
)
185 (multiple-value-bind (instruction operand-descriptions
)
186 (lookup-instruction instruction
)
187 (when operand-descriptions
188 (format *debug-io
* "Found instruction ~A~&" instruction
)
189 (let ((candidate-description
190 (find-correct-description operands operand-descriptions
)))
191 (emit-bytes-to-segment
195 candidate-description
))))))
199 (defun assemble-form (form)
200 "(assemble-form form) -- assemble a single line of assembler code
201 The form may be one of the following:
202 a number - a literal to be emitted
203 a symbol - if it is in the keyword packaage it is an instruction with no operands
204 - if it is begins with a dot it is a symbol definition
205 - if does not begin with a dot it is a symbol reference
206 a list - it is a list of the above, plus possibly operands"
208 ;; remove dot from the front of a symbol
210 (subseq x
1 (length x
)))
211 ;; test to see if a symbol begins with a dot
213 (char= (char (symbol-name x
) 0) #\.
)))
214 (format *debug-io
* "--~&Assembling form ~A~%" form
)
216 ;; value is supposed to be a literal to be poked in
217 (number (emit-data-to-segment form
))
218 ;; symbol is supposed to be a reference to a symbol,
219 ;; or a definition of a symbol unless its a keyword
220 ;; in which case it's an opcode-less directive
222 (if (symbol-is-in-package form
"KEYWORD")
223 (assemble-instruction-or-directive form nil
)
224 (if (begins-with-dot form
)
225 (add-symbol-definition (remove-dot (symbol-name form
)))
226 (add-symbol-reference (symbol-name form
)))))
228 ;; if the list begins with a keyword, the rest of it
230 (if (and (symbolp (first form
))
231 (symbol-is-in-package (first form
) "KEYWORD"))
232 (assemble-instruction-or-directive (first form
) (rest form
))
234 (assemble-form (first form
))
236 (assemble-form (rest form
)))))))))
239 (defun assemble-forms (&rest forms
)
240 "(assemble-forms forms)
241 Assemble a list of forms in the current segment"
242 (mapcar #'assemble-form forms
))
246 ;; ;; ;; our first test program
247 ;;(make-instruction-hash-table)
249 (make-segment "text" :segment-type
'data-segment
:set-to-current t
)
252 ;; '((.Entry :ADD :EAX 56)
259 ;; (:SUB :EAX #XFFFEA)
268 ;; '((.Entry JMP Exit)