3 ;;; This module implements the generic assembler.
5 ;(##declare (standard-bindings) (fixnum) (block))
7 (define compiler-internal-error error)
9 ;; (asm-begin! start-pos big-endian?) initializes the assembler and
10 ;; starts a new empty code stream at address "start-pos". It must be
11 ;; called every time a new code stream is to be built. The argument
12 ;; "big-endian?" indicates the byte ordering to use for 16, 32 and 64
13 ;; bit values. After a call to "asm-begin!" the code stream is built
14 ;; by calling the following procedures:
16 ;; asm-8 to add an 8 bit integer to the code stream
17 ;; asm-16 to add a 16 bit integer to the code stream
18 ;; asm-32 to add a 32 bit integer to the code stream
19 ;; asm-64 to add a 64 bit integer to the code stream
20 ;; asm-float64 to add a 64 bit IEEE float to the code stream
21 ;; asm-string to add a null terminated string to the code stream
22 ;; asm-label to set a label to the current position in the code stream
23 ;; asm-align to add enough zero bytes to force alignment
24 ;; asm-origin to add enough zero bytes to move to a particular address
25 ;; asm-at-assembly to defer code production to assembly time
26 ;; asm-listing to add textual information to the listing
28 (define (asm-begin! start-pos big-endian?)
29 (set! asm-start-pos start-pos)
30 (set! asm-big-endian? big-endian?)
31 (set! asm-code-stream (asm-make-stream))
34 ;; (asm-end!) must be called to finalize the assembler.
37 (set! asm-code-stream #f)
40 ;; (asm-8 n) adds an 8 bit signed or unsigned integer to the code stream.
43 (asm-code-extend (asm-bits-0-to-7 n)))
45 ;; (asm-16 n) adds a 16 bit signed or unsigned integer to the code stream.
49 (begin (asm-8 (asm-bits-8-and-up n)) (asm-8 n))
50 (begin (asm-8 n) (asm-8 (asm-bits-8-and-up n)))))
52 ;; (asm-32 n) adds a 32 bit signed or unsigned integer to the code stream.
56 (begin (asm-16 (asm-bits-16-and-up n)) (asm-16 n))
57 (begin (asm-16 n) (asm-16 (asm-bits-16-and-up n)))))
59 ;; (asm-64 n) adds a 64 bit signed or unsigned integer to the code stream.
63 (begin (asm-32 (asm-bits-32-and-up n)) (asm-32 n))
64 (begin (asm-32 n) (asm-32 (asm-bits-32-and-up n)))))
66 ;; (asm-float64 n) adds a 64 bit IEEE floating point number to the code stream.
68 (define (asm-float64 n)
69 (asm-64 (asm-float->bits n)))
71 ;; (asm-string str) adds a null terminated string to the code stream.
73 (define (asm-string str)
74 (let ((len (string-length str)))
78 (asm-8 (char->integer (string-ref str i)))
82 ;; (asm-make-label id) creates a new label object. A label can
83 ;; be queried with "asm-label-pos" to obtain the label's position
84 ;; relative to the start of the code stream (i.e. "start-pos").
85 ;; The argument "id" gives a name to the label (not necessarily
86 ;; unique) and is only needed for debugging purposes.
88 (define (asm-make-label id #!optional (pos #f))
89 (vector 'LABEL pos id))
91 ;; (asm-label label-obj) sets the label to the current position in the
94 (define (asm-label label-obj)
95 (if (vector-ref label-obj 1)
96 (compiler-internal-error
97 "asm-label, label multiply defined" (asm-label-id label-obj))
99 (vector-set! label-obj 1 0)
100 (asm-code-extend label-obj))))
102 ;; (asm-label-id label-obj) returns the identifier of the label object.
104 (define (asm-label-id label-obj)
105 (vector-ref label-obj 2))
107 ;; (asm-label-pos label-obj) returns the position of the label
108 ;; relative to the start of the code stream (i.e. "start-pos").
109 ;; This procedure can only be called at assembly time (i.e.
110 ;; within the call to "asm-assemble") or after assembly time
111 ;; for labels declared prior to assembly time with "asm-label".
112 ;; A label declared at assembly time can only be queried after
113 ;; assembly time. Moreover, at assembly time the position of a
114 ;; label may vary from one call to the next due to the actions
117 (define (asm-label-pos label-obj)
118 (let ((pos (vector-ref label-obj 1)))
121 (compiler-internal-error
122 "asm-label-pos, undefined label" (asm-label-id label-obj)))))
124 ;; (asm-align multiple offset) adds enough zero bytes to the code
125 ;; stream to force alignment to the next address congruent to
126 ;; "offset" modulo "multiple".
128 (define (asm-align multiple offset)
131 (modulo (- multiple (- self offset)) multiple))
133 (let loop ((n (modulo (- multiple (- self offset)) multiple)))
139 ;; (asm-origin address) adds enough zero bytes to the code stream to move
140 ;; to the address "address".
142 (define (asm-origin address)
147 (let ((len (- address self)))
149 (compiler-internal-error "asm-origin, can't move back")
154 (loop (- n 1))))))))))
156 ;; (asm-at-assembly . procs) makes it possible to defer code
157 ;; production to assembly time. A useful application is to generate
158 ;; position dependent and span dependent code sequences. This
159 ;; procedure must be passed an even number of procedures. All odd
160 ;; indexed procedures (including the first procedure) are called "check"
161 ;; procedures. The even indexed procedures are the "production"
162 ;; procedures which, when called, produce a particular code sequence.
163 ;; A check procedure decides if, given the current state of assembly
164 ;; (in particular the current positioning of the labels), the code
165 ;; produced by the corresponding production procedure is valid.
166 ;; If the code is not valid, the check procedure must return #f.
167 ;; If the code is valid, the check procedure must return the length
168 ;; of the code sequence in bytes. The assembler will try each check
169 ;; procedure in order until it finds one that does not return #f
170 ;; (the last check procedure must never return #f). For convenience,
171 ;; the current position in the code sequence is passed as the single
172 ;; argument of check and production procedures.
174 ;; Here is a sample call of "asm-at-assembly" to produce the
175 ;; shortest branch instruction to branch to label "x" for a
176 ;; hypothetical processor:
180 ;; (lambda (self) ; first check procedure
181 ;; (let ((dist (- (asm-label-pos x) self)))
182 ;; (if (and (>= dist -128) (<= dist 127)) ; short branch possible?
186 ;; (lambda (self) ; first production procedure
187 ;; (asm-8 #x34) ; branch opcode for 8 bit displacement
188 ;; (asm-8 (- (asm-label-pos x) self)))
190 ;; (lambda (self) 5) ; second check procedure
192 ;; (lambda (self) ; second production procedure
193 ;; (asm-8 #x35) ; branch opcode for 32 bit displacement
194 ;; (asm-32 (- (asm-label-pos x) self))))
196 (define (asm-at-assembly . procs)
197 (asm-code-extend (vector 'DEFERRED procs)))
199 ;; (asm-listing text) adds text to the right side of the listing.
200 ;; The atoms in "text" will be output using "display" (lists are
201 ;; traversed recursively). The listing is generated by calling
202 ;; "asm-display-listing".
204 (define (asm-listing text)
205 (asm-code-extend (vector 'LISTING text)))
207 ;; (asm-assemble) assembles the code stream. After assembly, the
208 ;; label objects will be set to their final position and the
209 ;; alignment bytes and the deferred code will have been produced. It
210 ;; is possible to extend the code stream after assembly. However, if
211 ;; any of the procedures "asm-label", "asm-align", and
212 ;; "asm-at-assembly" are called, the code stream will have to be
213 ;; assembled once more.
215 (define symbol-table (make-table)) ; associates addresses to labels
216 (define (asm-assemble)
217 (let ((fixup-lst (asm-pass1)))
220 (let loop2 ((lst fixup-lst)
224 (if changed? (loop1))
225 (let* ((fixup (car lst))
226 (pos (+ pos (car fixup)))
229 (if (eq? (vector-ref x 0) 'LABEL)
231 (if (= (vector-ref x 1) pos)
232 (loop2 (cdr lst) changed? pos)
234 (table-set! symbol-table pos (asm-label-id x))
235 (vector-set! x 1 pos)
236 (loop2 (cdr lst) #t pos)))
239 (let ((n ((car (vector-ref x 1)) pos)))
241 (loop2 (cdr lst) changed? (+ pos n))
243 (vector-set! x 1 (cddr (vector-ref x 1)))
246 (let loop4 ((prev asm-code-stream)
247 (curr (cdr asm-code-stream))
250 (set-car! asm-code-stream prev)
254 (let ((kind (vector-ref x 0)))
255 (cond ((eq? kind 'LABEL)
256 (let ((final-pos (vector-ref x 1)))
258 (if (not (= pos final-pos))
259 (compiler-internal-error
260 "asm-assemble, inconsistency detected"))
261 (vector-set! x 1 pos))
263 (loop4 prev next pos)))
264 ((eq? kind 'DEFERRED)
265 (let ((temp asm-code-stream))
266 (set! asm-code-stream (asm-make-stream))
267 ((cadr (vector-ref x 1)) pos)
268 (let ((tail (car asm-code-stream)))
270 (let ((head (cdr asm-code-stream)))
272 (set! asm-code-stream temp)
273 (loop4 prev head pos)))))
275 (loop4 curr next pos))))
276 (loop4 curr next (+ pos 1))))))))
278 ;; (asm-display-listing port) produces a listing of the code stream
279 ;; on the given output port. The bytes generated are shown in
280 ;; hexadecimal on the left side of the listing and the right side
281 ;; of the listing contains the text inserted by "asm-listing".
283 (define (asm-display-listing port)
287 (define byte-width 2)
289 (define (output text)
295 (display text port))))
297 (define (print-hex n)
298 (display (string-ref "0123456789ABCDEF" n) port))
300 (define (print-byte n)
301 (print-hex (quotient n 16))
302 (print-hex (modulo n 16)))
304 (define (print-pos n)
308 (print-byte (quotient n #x10000))
309 (print-byte (modulo (quotient n #x100) #x100))
310 (print-byte (modulo n #x100)))))
312 (let loop1 ((lst (cdr asm-code-stream)) (pos asm-start-pos) (col 0))
318 (let ((kind (vector-ref x 0)))
319 (cond ((eq? kind 'LISTING)
320 (let loop2 ((col col))
323 (display (integer->char 9) port)
324 (loop2 (* 8 (+ (quotient col 8) 1))))))
325 (output (vector-ref x 1))
327 (loop1 (cdr lst) pos 0))
329 (compiler-internal-error
330 "asm-display-listing, code stream not assembled"))))
331 (if (or (= col 0) (>= col (- text-col byte-width)))
333 (if (not (= col 0)) (newline port))
337 (loop1 (cdr lst) (+ pos 1) (+ (+ pos-width 1) byte-width)))
340 (loop1 (cdr lst) (+ pos 1) (+ col byte-width)))))))))
342 ;; (asm-write-code filename) outputs the code stream (i.e. the sequence
343 ;; of bytes produced) on the named file.
345 (define (asm-write-code filename)
346 (with-output-to-file filename
348 (let loop ((lst (cdr asm-code-stream)))
349 (if (not (null? lst))
352 (let ((kind (vector-ref x 0)))
353 (if (not (eq? kind 'LISTING))
354 (compiler-internal-error
355 "asm-write-code, code stream not assembled"))
358 (write-char (integer->char x))
359 (loop (cdr lst))))))))))
361 (define (asm-write-hex-file filename)
362 (with-output-to-file filename
365 (define (print-hex n)
366 (display (string-ref "0123456789ABCDEF" n)))
368 (define (print-byte n)
369 (print-hex (quotient n 16))
370 (print-hex (modulo n 16)))
372 (define (print-line type addr bytes)
373 (let ((n (length bytes))
374 (addr-hi (quotient addr 256))
375 (addr-lo (modulo addr 256)))
381 (for-each print-byte bytes)
383 (modulo (- (apply + n addr-hi addr-lo type bytes)) 256)))
387 (let loop ((lst (cdr asm-code-stream))
390 (if (not (null? lst))
393 (let ((kind (vector-ref x 0)))
394 (if (not (eq? kind 'LISTING))
395 (compiler-internal-error
396 "asm-write-hex-file, code stream not assembled"))
404 (if (= (modulo pos 16) 0)
407 (- pos (length rev-bytes))
415 (if (not (null? rev-bytes))
417 (- pos (length rev-bytes))
418 (reverse rev-bytes)))
422 (display pos ##stderr-port)
423 (display " ROM bytes\n" ##stderr-port)))))))))
427 (define asm-start-pos #f) ; start position of the code stream
428 (define asm-big-endian? #f) ; endianness to use
429 (define asm-code-stream #f) ; current code stream
431 (define (asm-make-stream) ; create an empty stream
432 (let ((x (cons '() '())))
436 (define (asm-code-extend item) ; add an item at the end of current code stream
437 (let* ((stream asm-code-stream)
439 (cell (cons item '())))
441 (set-car! stream cell)))
443 (define (asm-pass1) ; construct fixup list and make first label assignment
444 (let loop ((curr (cdr asm-code-stream))
450 (let ((x (car curr)))
452 (let ((kind (vector-ref x 0)))
453 (cond ((eq? kind 'LABEL)
454 (vector-set! x 1 pos) ; first approximation of position
455 (loop (cdr curr) (cons (cons span curr) fixup-lst) 0 pos))
456 ((eq? kind 'DEFERRED)
457 (loop (cdr curr) (cons (cons span curr) fixup-lst) 0 pos))
459 (loop (cdr curr) fixup-lst span pos))))
460 (loop (cdr curr) fixup-lst (+ span 1) (+ pos 1)))))))
462 ;(##declare (generic))
464 (define (asm-bits-0-to-7 n) ; return bits 0 to 7 of a signed integer
467 (define (asm-bits-8-and-up n) ; return bits 8 and up of a signed integer
470 (- (quotient (+ n 1) #x100) 1)))
472 (define (asm-bits-16-and-up n) ; return bits 16 and up of a signed integer
475 (- (quotient (+ n 1) #x10000) 1)))
477 (define (asm-bits-32-and-up n) ; return bits 32 and up of a signed integer
479 (quotient n #x100000000)
480 (- (quotient (+ n 1) #x100000000) 1)))
482 ; The following procedures convert floating point numbers into their
483 ; machine representation. They perform bignum and flonum arithmetic.
485 (define (asm-float->inexact-exponential-format x)
487 (define (exp-form-pos x y i)
489 (let ((z (if (and (not (< asm-ieee-e-bias i*2))
491 (exp-form-pos x (* y y) i*2)
493 (let ((a (car z)) (b (cdr z)))
495 (if (and (not (< asm-ieee-e-bias i+b))
502 (define (exp-form-neg x y i)
504 (let ((z (if (and (< i*2 asm-ieee-e-bias-minus-1)
506 (exp-form-neg x (* y y) i*2)
508 (let ((a (car z)) (b (cdr z)))
510 (if (and (< i+b asm-ieee-e-bias-minus-1)
518 (if (< x asm-inexact-+1)
519 (let ((z (exp-form-neg x asm-inexact-+1/2 1)))
520 (set-car! z (* asm-inexact-+2 (car z)))
521 (set-cdr! z (- -1 (cdr z)))
523 (exp-form-pos x asm-inexact-+2 1)))
526 (let ((z (exp-form (- asm-inexact-0 x))))
527 (set-car! z (- asm-inexact-0 (car z)))
531 (define (asm-float->exact-exponential-format x)
532 (let ((z (asm-float->inexact-exponential-format x)))
534 (cond ((not (< y asm-inexact-+2))
535 (set-car! z asm-ieee-+m-min)
536 (set-cdr! z asm-ieee-e-bias-plus-1))
537 ((not (< asm-inexact--2 y))
538 (set-car! z asm-ieee--m-min)
539 (set-cdr! z asm-ieee-e-bias-plus-1))
542 (truncate (inexact->exact (* (car z) asm-inexact-m-min))))))
543 (set-cdr! z (- (cdr z) asm-ieee-m-bits))
546 (define (asm-float->bits x) ; returns the 64 bit integer encoding the float "x"
549 (if (< a asm-ieee-+m-min)
551 (+ (- a asm-ieee-+m-min)
552 (* (+ (+ b asm-ieee-m-bits) asm-ieee-e-bias)
555 (let ((z (asm-float->exact-exponential-format x)))
556 (let ((a (car z)) (b (cdr z)))
558 (+ asm-ieee-sign-bit (bits (- 0 a) b))
561 ; Parameters for ANSI-IEEE Std 754-1985 representation of
562 ; doubles (i.e. 64 bit floating point numbers):
564 (define asm-ieee-m-bits 52)
565 (define asm-ieee-e-bits 11)
566 (define asm-ieee-+m-min 4503599627370496) ; (expt 2 asm-ieee-m-bits)
567 (define asm-ieee--m-min -4503599627370496) ; (- asm-ieee-+m-min)
568 (define asm-ieee-sign-bit #x8000000000000000); (expt 2 (+ asm-ieee-e-bits asm-ieee-m-bits))
570 (define asm-ieee-e-bias 1023) ; (- (expt 2 (- asm-ieee-e-bits 1)) 1)
571 (define asm-ieee-e-bias-plus-1 1024) ; (+ asm-ieee-e-bias 1)
572 (define asm-ieee-e-bias-minus-1 1022) ; (- asm-ieee-e-bias 1)
574 (define asm-inexact-m-min (exact->inexact asm-ieee-+m-min))
575 (define asm-inexact-+2 (exact->inexact 2))
576 (define asm-inexact--2 (exact->inexact -2))
577 (define asm-inexact-+1 (exact->inexact 1))
578 (define asm-inexact-+1/2 (exact->inexact (/ 1 2)))
579 (define asm-inexact-0 (exact->inexact 0))