Removed code that dealt with ROM closure, since these don't exist.
[picobit.git] / asm.scm
blob485a496e3af999fbbeac4db503e77d1053ab9cc5
1 ;;; File: "asm.scm"
2 ;;;
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))
32   #f)
34 ;; (asm-end!) must be called to finalize the assembler.
36 (define (asm-end!)
37   (set! asm-code-stream #f)
38   #f)
40 ;; (asm-8 n) adds an 8 bit signed or unsigned integer to the code stream.
42 (define (asm-8 n)
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.
47 (define (asm-16 n)
48   (if asm-big-endian?
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.
54 (define (asm-32 n)
55   (if asm-big-endian?
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.
61 (define (asm-64 n)
62   (if asm-big-endian?
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)))
75     (let loop ((i 0))
76       (if (< i len)
77         (begin
78           (asm-8 (char->integer (string-ref str i)))
79           (loop (+ i 1)))
80         (asm-8 0)))))
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)
89   (vector 'LABEL #f id))
91 ;; (asm-label label-obj) sets the label to the current position in the
92 ;; code stream.
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))
98     (begin
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
115 ;; of the assembler.
117 (define (asm-label-pos label-obj)
118   (let ((pos (vector-ref label-obj 1)))
119     (if pos
120       pos
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)
129   (asm-at-assembly
130     (lambda (self)
131       (modulo (- multiple (- self offset)) multiple))
132     (lambda (self)
133       (let loop ((n (modulo (- multiple (- self offset)) multiple)))
134         (if (> n 0)
135           (begin
136             (asm-8 0)
137             (loop (- n 1))))))))
139 ;; (asm-origin address) adds enough zero bytes to the code stream to move
140 ;; to the address "address".
142 (define (asm-origin address)
143   (asm-at-assembly
144     (lambda (self)
145       (- address self))
146     (lambda (self)
147       (let ((len (- address self)))
148         (if (< len 0)
149           (compiler-internal-error "asm-origin, can't move back")
150           (let loop ((n len))
151             (if (> n 0)
152               (begin
153                 (asm-8 0)
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:
178 ;;  (asm-at-assembly
180 ;;    (lambda (self) ; first check procedure
181 ;;      (let ((dist (- (asm-label-pos x) self)))
182 ;;        (if (and (>= dist -128) (<= dist 127)) ; short branch possible?
183 ;;          2
184 ;;          #f)))
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 0)))
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 (asm-assemble)
216   (let ((fixup-lst (asm-pass1)))
218     (let loop1 ()
219       (let loop2 ((lst fixup-lst)
220                   (pos asm-start-pos))
221         (if (pair? lst)
222             (let* ((fixup (car lst))
223                    (pos (+ pos (car fixup)))
224                    (curr (cdr fixup))
225                    (x (car curr)))
226               (if (eq? (vector-ref x 0) 'LABEL)
227                   ;; LABEL
228                   (loop2 (cdr lst) pos)
229                   ;; DEFERRED
230                   (let ((old-size (vector-ref x 2)))
231                     (let loop3 ()
232                       (let ((new-size ((car (vector-ref x 1)) pos)))
233                         (if new-size
234                             (begin
235                               (vector-set! x 2 new-size)
236                               (loop2 (cdr lst) (+ pos old-size)))
237                             (begin
238                               (vector-set! x 1 (cddr (vector-ref x 1)))
239                               (loop3))))))))
240             (let loop4 ((lst fixup-lst)
241                         (pos asm-start-pos)
242                         (changed? #f))
243               (if (pair? lst)
244                   (let* ((fixup (car lst))
245                          (pos (+ pos (car fixup)))
246                          (curr (cdr fixup))
247                          (x (car curr)))
248                     (if (eq? (vector-ref x 0) 'LABEL)
249                         ;; LABEL
250                         (if (= (vector-ref x 1) pos)
251                             (loop4 (cdr lst) pos changed?)
252                             (begin
253                               (vector-set! x 1 pos)
254                               (loop4 (cdr lst) pos #t)))
255                         ;; DEFERRED
256                         (let ((new-size (vector-ref x 2)))
257                           (loop4 (cdr lst) (+ pos new-size) changed?))))
258                   (if changed?
259                       (loop1)))))))
261     (let loop5 ((prev asm-code-stream)
262                 (curr (cdr asm-code-stream))
263                 (pos asm-start-pos))
264       (if (null? curr)
265           (set-car! asm-code-stream prev)
266           (let ((x (car curr))
267                 (next (cdr curr)))
268             (if (vector? x)
269                 (let ((kind (vector-ref x 0)))
270                   (cond ((eq? kind 'LABEL)
271                          (let ((final-pos (vector-ref x 1)))
272                            (if final-pos
273                                (if (not (= pos final-pos))
274                                    (compiler-internal-error
275                                     "asm-assemble, inconsistency detected"))
276                                (vector-set! x 1 pos))
277                            (set-cdr! prev next)
278                            (loop5 prev next pos)))
279                         ((eq? kind 'DEFERRED)
280                          (let ((temp asm-code-stream))
281                            (set! asm-code-stream (asm-make-stream))
282                            ((cadr (vector-ref x 1)) pos)
283                            (let ((tail (car asm-code-stream)))
284                              (set-cdr! tail next)
285                              (let ((head (cdr asm-code-stream)))
286                                (set-cdr! prev head)
287                                (set! asm-code-stream temp)
288                                (loop5 prev head pos)))))
289                         (else
290                          (loop5 curr next pos))))
291                 (loop5 curr next (+ pos 1))))))))
293 ;; (asm-display-listing port) produces a listing of the code stream
294 ;; on the given output port.  The bytes generated are shown in
295 ;; hexadecimal on the left side of the listing and the right side
296 ;; of the listing contains the text inserted by "asm-listing".
298 (define (asm-display-listing port)
300   (define text-col 24)
301   (define pos-width 6)
302   (define byte-width 2)
304   (define (output text)
305     (cond ((null? text))
306           ((pair? text)
307            (output (car text))
308            (output (cdr text)))
309           (else
310            (display text port))))
312   (define (print-hex n)
313     (display (string-ref "0123456789ABCDEF" n) port))
315   (define (print-byte n)
316     (print-hex (quotient n 16))
317     (print-hex (modulo n 16)))
319   (define (print-pos n)
320     (if (< n 0)
321       (display "      " port)
322       (begin
323         (print-byte (quotient n #x10000))
324         (print-byte (modulo (quotient n #x100) #x100))
325         (print-byte (modulo n #x100)))))
327   (let loop1 ((lst (cdr asm-code-stream)) (pos asm-start-pos) (col 0))
328     (if (null? lst)
329       (if (> col 0)
330         (newline port))
331       (let ((x (car lst)))
332         (if (vector? x)
333           (let ((kind (vector-ref x 0)))
334             (cond ((eq? kind 'LISTING)
335                    (let loop2 ((col col))
336                      (if (< col text-col)
337                        (begin
338                          (display (integer->char 9) port)
339                          (loop2 (* 8 (+ (quotient col 8) 1))))))
340                    (output (vector-ref x 1))
341                    (newline port)
342                    (loop1 (cdr lst) pos 0))
343                   (else
344                    (compiler-internal-error
345                      "asm-display-listing, code stream not assembled"))))
346           (if (or (= col 0) (>= col (- text-col byte-width)))
347             (begin
348               (if (not (= col 0)) (newline port))
349               (print-pos pos)
350               (display " " port)
351               (print-byte x)
352               (loop1 (cdr lst) (+ pos 1) (+ (+ pos-width 1) byte-width)))
353             (begin
354               (print-byte x)
355               (loop1 (cdr lst) (+ pos 1) (+ col byte-width)))))))))
357 ;; (asm-write-code filename) outputs the code stream (i.e. the sequence
358 ;; of bytes produced) on the named file.
360 (define (asm-write-code filename)
361   (with-output-to-file filename
362     (lambda ()
363       (let loop ((lst (cdr asm-code-stream)))
364         (if (not (null? lst))
365           (let ((x (car lst)))
366             (if (vector? x)
367               (let ((kind (vector-ref x 0)))
368                 (if (not (eq? kind 'LISTING))
369                   (compiler-internal-error
370                     "asm-write-code, code stream not assembled"))
371                 (loop (cdr lst)))
372               (begin
373                 (write-char (integer->char x))
374                 (loop (cdr lst))))))))))
376 (define (asm-write-hex-file filename)
377   (with-output-to-file filename
378     (lambda ()
380       (define (print-hex n)
381         (display (string-ref "0123456789ABCDEF" n)))
383       (define (print-byte n)
384         (print-hex (quotient n 16))
385         (print-hex (modulo n 16)))
387       (define (print-line type addr bytes)
388         (let ((n (length bytes))
389               (addr-hi (quotient addr 256))
390               (addr-lo (modulo addr 256)))
391           (display ":")
392           (print-byte n)
393           (print-byte addr-hi)
394           (print-byte addr-lo)
395           (print-byte type)
396           (for-each print-byte bytes)
397           (let ((sum
398                  (modulo (- (apply + n addr-hi addr-lo type bytes)) 256)))
399             (print-byte sum)
400             (newline))))
402       (let loop ((lst (cdr asm-code-stream))
403                  (pos asm-start-pos)
404                  (rev-bytes '()))
405         (if (not (null? lst))
406           (let ((x (car lst)))
407             (if (vector? x)
408               (let ((kind (vector-ref x 0)))
409                 (if (not (eq? kind 'LISTING))
410                   (compiler-internal-error
411                     "asm-write-hex-file, code stream not assembled"))
412                 (loop (cdr lst)
413                       pos
414                       rev-bytes))
415               (let ((new-pos
416                      (+ pos 1))
417                     (new-rev-bytes
418                      (cons x
419                            (if (= (modulo pos 16) 0)
420                                (begin
421                                  (print-line 0
422                                              (- pos (length rev-bytes))
423                                              (reverse rev-bytes))
424                                  '())
425                                rev-bytes))))
426                 (loop (cdr lst)
427                       new-pos
428                       new-rev-bytes))))
429           (begin
430             (if (not (null? rev-bytes))
431                 (print-line 0
432                             (- pos (length rev-bytes))
433                             (reverse rev-bytes)))
434             (print-line 1 0 '())
435             (if #t
436                 (begin
437                   ;;;(pp (- 3447 (- pos asm-start-pos)));;;;;;;;;;;;
439                   (display (- pos asm-start-pos) ##stderr-port)
440                   (display " bytes\n" ##stderr-port)))))))))
442 ;; Utilities.
444 (define asm-start-pos #f)   ; start position of the code stream
445 (define asm-big-endian? #f) ; endianness to use
446 (define asm-code-stream #f) ; current code stream
448 (define (asm-make-stream) ; create an empty stream
449   (let ((x (cons '() '())))
450     (set-car! x x)
451     x))
452      
453 (define (asm-code-extend item) ; add an item at the end of current code stream
454   (let* ((stream asm-code-stream)
455          (tail (car stream))
456          (cell (cons item '())))
457     (set-cdr! tail cell)
458     (set-car! stream cell)))
460 (define (asm-pass1) ; construct fixup list and make first label assignment
461   (let loop ((curr (cdr asm-code-stream))
462              (fixup-lst '())
463              (span 0)
464              (pos asm-start-pos))
465     (if (null? curr)
466       (reverse fixup-lst)
467       (let ((x (car curr)))
468         (if (vector? x)
469           (let ((kind (vector-ref x 0)))
470             (cond ((eq? kind 'LABEL)
471                    (vector-set! x 1 pos) ; first approximation of position
472                    (loop (cdr curr) (cons (cons span curr) fixup-lst) 0 pos))
473                   ((eq? kind 'DEFERRED)
474                    (loop (cdr curr) (cons (cons span curr) fixup-lst) 0 pos))
475                   (else
476                    (loop (cdr curr) fixup-lst span pos))))
477           (loop (cdr curr) fixup-lst (+ span 1) (+ pos 1)))))))
479 ;(##declare (generic))
481 (define (asm-bits-0-to-7 n) ; return bits 0 to 7 of a signed integer
482   (modulo n #x100))
484 (define (asm-bits-8-and-up n) ; return bits 8 and up of a signed integer
485   (if (>= n 0)
486     (quotient n #x100)
487     (- (quotient (+ n 1) #x100) 1)))
489 (define (asm-bits-16-and-up n) ; return bits 16 and up of a signed integer
490   (if (>= n 0)
491     (quotient n #x10000)
492     (- (quotient (+ n 1) #x10000) 1)))
494 (define (asm-bits-32-and-up n) ; return bits 32 and up of a signed integer
495   (if (>= n 0)
496     (quotient n #x100000000)
497     (- (quotient (+ n 1) #x100000000) 1)))
499 ; The following procedures convert floating point numbers into their
500 ; machine representation.  They perform bignum and flonum arithmetic.
502 (define (asm-float->inexact-exponential-format x)
504   (define (exp-form-pos x y i)
505     (let ((i*2 (+ i i)))
506       (let ((z (if (and (not (< asm-ieee-e-bias i*2))
507                         (not (< x y)))
508                  (exp-form-pos x (* y y) i*2)
509                  (cons x 0))))
510         (let ((a (car z)) (b (cdr z)))
511           (let ((i+b (+ i b)))
512             (if (and (not (< asm-ieee-e-bias i+b))
513                      (not (< a y)))
514               (begin
515                 (set-car! z (/ a y))
516                 (set-cdr! z i+b)))
517             z)))))
519   (define (exp-form-neg x y i)
520     (let ((i*2 (+ i i)))
521       (let ((z (if (and (< i*2 asm-ieee-e-bias-minus-1)
522                         (< x y))
523                  (exp-form-neg x (* y y) i*2)
524                  (cons x 0))))
525         (let ((a (car z)) (b (cdr z)))
526           (let ((i+b (+ i b)))
527             (if (and (< i+b asm-ieee-e-bias-minus-1)
528                      (< a y))
529               (begin
530                 (set-car! z (/ a y))
531                 (set-cdr! z i+b)))
532             z)))))
534   (define (exp-form x)
535     (if (< x asm-inexact-+1)
536       (let ((z (exp-form-neg x asm-inexact-+1/2 1)))
537         (set-car! z (* asm-inexact-+2 (car z)))
538         (set-cdr! z (- -1 (cdr z)))
539         z)
540       (exp-form-pos x asm-inexact-+2 1)))
542   (if (negative? x)
543     (let ((z (exp-form (- asm-inexact-0 x))))
544       (set-car! z (- asm-inexact-0 (car z)))
545       z)
546     (exp-form x)))
548 (define (asm-float->exact-exponential-format x)
549   (let ((z (asm-float->inexact-exponential-format x)))
550     (let ((y (car z)))
551       (cond ((not (< y asm-inexact-+2))
552              (set-car! z asm-ieee-+m-min)
553              (set-cdr! z asm-ieee-e-bias-plus-1))
554             ((not (< asm-inexact--2 y))
555              (set-car! z asm-ieee--m-min)
556              (set-cdr! z asm-ieee-e-bias-plus-1))
557             (else
558              (set-car! z
559                (truncate (inexact->exact (* (car z) asm-inexact-m-min))))))
560       (set-cdr! z (- (cdr z) asm-ieee-m-bits))
561       z)))
563 (define (asm-float->bits x) ; returns the 64 bit integer encoding the float "x"
565   (define (bits a b)
566     (if (< a asm-ieee-+m-min)
567       a
568       (+ (- a asm-ieee-+m-min)
569          (* (+ (+ b asm-ieee-m-bits) asm-ieee-e-bias)
570             asm-ieee-+m-min))))
572   (let ((z (asm-float->exact-exponential-format x)))
573     (let ((a (car z)) (b (cdr z)))
574       (if (negative? a)
575         (+ asm-ieee-sign-bit (bits (- 0 a) b))
576         (bits a b)))))
578 ; Parameters for ANSI-IEEE Std 754-1985 representation of
579 ; doubles (i.e. 64 bit floating point numbers):
581 (define asm-ieee-m-bits 52)
582 (define asm-ieee-e-bits 11)
583 (define asm-ieee-+m-min 4503599627370496)    ; (expt 2 asm-ieee-m-bits)
584 (define asm-ieee--m-min -4503599627370496)   ; (- asm-ieee-+m-min)
585 (define asm-ieee-sign-bit #x8000000000000000); (expt 2 (+ asm-ieee-e-bits asm-ieee-m-bits))
587 (define asm-ieee-e-bias         1023) ; (- (expt 2 (- asm-ieee-e-bits 1)) 1)
588 (define asm-ieee-e-bias-plus-1  1024) ; (+ asm-ieee-e-bias 1)
589 (define asm-ieee-e-bias-minus-1 1022) ; (- asm-ieee-e-bias 1)
591 (define asm-inexact-m-min (exact->inexact asm-ieee-+m-min))
592 (define asm-inexact-+2    (exact->inexact 2))
593 (define asm-inexact--2    (exact->inexact -2))
594 (define asm-inexact-+1    (exact->inexact 1))
595 (define asm-inexact-+1/2  (exact->inexact (/ 1 2)))
596 (define asm-inexact-0     (exact->inexact 0))