#!@prefix@/lib/scsh/scshvm \
-o @prefix@/lib/scsh/scshvm -h 8000000 -i @prefix@/lib/scsh/scsh.image -lm @prefix@/lib/scsh/vm/ps-interface.scm -lm @prefix@/lib/scsh/vm/interfaces.scm -lm @prefix@/lib/scsh/vm/package-defs.scm -lm @prefix@/lib/scsh/vm/s48-package-defs.scm -dm -m static-heaps -e static-heap-linker -s
!#

#!
For testing load this at a scsh prompt
,config ,load ../vm/ps-interface.scm
,config ,load ../vm/interfaces.scm
,config ,load ../vm/package-defs.scm
,config ,load ../vm/s48-package-defs.scm
,config ,load static.scm
,load-package static-heaps
,in static-heaps
!#

;;; Static heap package for the Scheme Shell
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Copyright (c) 1995-1996 by Brian D. Carlstrom.
;;;
;;; based on Scheme48 implementation.
;;; Copyright (c) 1993, 1994 by Richard Kelsey and Jonathan Rees.
;;;
;;; The business of this package is converting a Scheme 48 bytecode
;;; image as embodied in a .image file to a C representation. This C
;;; code is then compiled and linked in with a virtual machine. One
;;; pleasant side effect of this is reduced startup times. Another
;;; good thing is that immutable parts of the image can be shared
;;; between processes.

(define-structure static-heaps
  (export static-heap-linker)
  (open scheme heap memory data stob struct
	heap-extra
	vm-architecture
	formats
	enumerated
	signals
	tables
	defrec-package
	scsh)
  (begin
    
;;; static-heap-linker
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; the external entry point
;;; real work in static-heap-linker1
;;; argl is a list of the command line arguments
    (define (static-heap-linker argl)
      (static-heap-linker1 (parse-options argl))
      (exit 0))

;;; parse-options
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;    
;;; parses the command line options 
;;; returns them in an options structure
    (define (parse-options argl)
      (let ((options (make-options)))

	(let loop ((args (cdr argl)))
	  (cond ((null? args)
		 (cond ((not (options:output-executable options))
			(display "error: -o is a required argument") 
			(newline)
			(usage (car argl)))
		       ((not (options:input-image options))
			(display "error: -i is a required argument") 
			(newline)
			(usage (car argl)))))
		((equal? (car args) "-i")
		 (cond ((not (null? (cdr args)))
			(set-options:input-image options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: -i requires argument") (newline)
			(usage (car argl)))))
		((equal? (car args) "-o")
		 (cond ((not (null? (cdr args)))
			(set-options:output-executable options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: -o requires argument") (newline)
			(usage (car argl)))))
		((equal? (car args) "--args")
		 (cond ((not (null? (cdr args)))
			(set-options:args-parser options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: --args requires argument") (newline)
			(usage (car argl)))))
		((equal? (car args) "--temp")
		 (cond ((not (null? (cdr args)))
			(set-options:temp-dir options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: --temp requires argument") (newline)
			(usage (car argl)))))
		((equal? (car args) "--cc")
		 (cond ((not (null? (cdr args)))
			(set-options:cc-command options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: --cc requires argument") (newline)
			(usage (car argl)))))
		((equal? (car args) "--ld")
		 (cond ((not (null? (cdr args)))
			(set-options:ld-command options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: --ld requires argument") (newline)
			(usage (car argl)))))
		((equal? (car args) "--libs")
		 (cond ((not (null? (cdr args)))
			(set-options:libraries options (cadr args))
			(loop (cddr args)))
		       (else 
			(display "error: --libs requires argument") (newline)
			(usage (car argl)))))
		(else
		 (format #t "error: unknown argument ~a" (car args)) 
		 (newline)
		 (usage (car argl)))))
	(set-options:args-parser
	 options
	 (if (options:args-parser options)
	     (list (options:args-parser options))
	     '()))
	(set-options:temp-dir 
	 options
	 (or (options:temp-dir options)
	     (getenv "TMPDIR") 
	     "@TMPDIR@"))
	(set-options:cc-command
	 options
	 (or (options:cc-command options)
	     (getenv "CC") 
	     "@CC@ @CFLAGS@"))
	(set-options:ld-flags
	 options
	 (or (options:ld-flags options)
	     (getenv "LDFLAGS") 
	     "@LDFLAGS@"))
	(set-options:libraries
	 options
	 (or (options:libraries options)
	     (getenv "LIBS") 
	     "@LIBS@"))
	options))

;;; usage reporting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (define (usage program-name)
      (format #t 
	      (string-append
	       "usage: ~a ~%" 
	       "          [-i image]~%"
	       "          [-o executable]~%"
	       "          [--args object]~%"
	       "          [--temp directory]~%"
	       "          [--cc command]~%"
	       "          [--ld command]~%"
	       "          [--libs libraries]~%")
	      program-name)
      (exit 1))
	
;;; options structure
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    (define-record options
      (input-image #f)			; the input scheme image file
      (output-executable #f)		; the output executable file
      (args-parser #f)			; .o file for replacement process_args
      (temp-dir #f)			; place for intermediate .c .o files
      (cc-command #f)			; command to compile a .c file
      (ld-flags #f)			; flags needed to link executable
      (libraries #f)			; linbraries need to link executable
      )

;;; heap structure
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    (define-record heap
      (length    0)
      (objects '())
      )

;;; static-heap-linker1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (define (static-heap-linker1 options)

      ;;; munge some options into a more usable form
      (set-options:temp-dir 
       options
       (format #f "~a/scsh~s" (options:temp-dir options) (pid)))
      (set-options:output-executable 
       options
       (string-append (cwd) "/" (options:output-executable options)))

      ;;; Read the image
      (let ((start ; entry point of image
	      (read-heap-image (options:input-image options))))

	;;; Process the image
	(receive (pure impure reloc externs)
	    (create-heaps-and-tables)

	  ;;; Prepare for output
	  ;;; if directory exists blow it away
	  ;;; useful for repeated runs from within same scsh process
	  (if (file-exists? (options:temp-dir options))
	      (cond ((file-directory? (options:temp-dir otions))
		     (with-cwd (options:temp-dir options)
			       (let loop ((files (directory-files 
						  (options:temp-dir options) 
						  #t)))
				 (cond ((not (null? files))
					(delete-file (car files))
					(loop (cdr files))))))
		     (delete-directory (options:temp-dir options)))
		    (else
		     (delete-file (options:temp-dir options)))))
	  (create-directory (options:temp-dir options) #o755 #t)
	  
	  ;;; Process the info we gather to make it the output file
	  (with-cwd (options:temp-dir options)
		    (write-c-header-file pure impure externs)
		    (compile-main-c-file start reloc options)
		    (compile-c-image pure impure reloc externs options)
		    (link-files options)
		    (let loop ((files (directory-files 
				      (options:temp-dir options) #t)))
		      (cond ((not (null? files))
			     (delete-file (car files))
			     (loop (cdr files))))))
	  (delete-directory (options:temp-dir options)))))

;;; read-heap-image
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; reads the scheme48 bytecode image into memory.
;;; returns entry point.
    (define (read-heap-image infile)
      (let ((bytes (file-info:size (file-info infile))))
	(init (inexact->exact (floor (* 1.1 bytes))) infile)))
    ;; XXX the 1.1 is because we need a little extra space for find-all-xs

;;; create-heaps-and-tables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Walks over the in memory scheme 48 heap image. 
;;; Returns
;;;   1.) vector of heaps describing   pure heap objects
;;;   2.) vector of heaps describing impure heap objects
;;;   3.) vector of tables descibing relocations
;;;   4.) table of external references
    (define (create-heaps-and-tables)
      (let* ((n       (nchunks))	; number of chunks we have in image
	     (  pure  (make-vector n))	; immutable bits of each chunk
	     (impure  (make-vector n))	;   mutable bits of each chunk
	     (reloc   (make-vector n))	; relocation information
	     (externs (make-table)))	; external references
	;; create empty heaps for each chunk
	(let loop ((i 0))
	  (cond ((not (= i n))
		 (vector-set!   pure i (make-heap))
		 (vector-set! impure i (make-heap))
		 (vector-set!  reloc i (make-table))
		 (loop (+ i 1)))))
	;; here is where we iterate through all the bits
	;; we construct our own data structures describing the layout
	(scsh-for-each-stored-object
	 (lambda (chunk)
	   (display "."))
	 (lambda (chunk x len)
	   (let* ((heap			; choose the appropriate heap
		   (vector-ref (if (mutable? x) impure pure) chunk)))
	     ;; add the relocation information
	     (table-set! (vector-ref reloc chunk) x (heap:length heap))
	     ;; add object reference to heap chunk
	     (set-heap:objects heap (cons x (heap:objects heap)))
	     ;; update current heap chunk length
	     (set-heap:length  heap (+ len  (heap:length  heap)))
	     ;; if we have an external reference handle add it to the list
	     (if (= (header-type (stob-header x)) (enum stob external))
		 (table-set! externs 
			     (external-value x) 
			     (vm-string->string (external-name x))))))
	 (lambda (chunk) 'foo))
	(newline)
	;; put all the heaps in the correct order
	(let loop ((i 0))
	  (cond ((not (= i n))
		 (let ((p (vector-ref   pure i))
		       (i (vector-ref impure i)))
		   (set-heap:objects p (reverse (heap:objects p)))
		   (set-heap:objects i (reverse (heap:objects i))))
		 (loop (+ i 1)))))
	(values pure impure reloc externs)))

;;; vm-string->string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; converts a vm-string to a scheme one that we can handle
    (define (vm-string->string x)
      (cond ((vm-string? x)
	     (let ((len (vm-string-length x)))
	       (let loop ((i 0) 
			  (l '()))
		 (cond ((= i len) 
			(list->string (reverse l)))
		       (else
			(loop (+ i 1) (cons (vm-string-ref x i) l)))))))
	    (else
	     (message x " is not a vm-string"))))

;;; write-c-header-file
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; declares the c long arrays for each heap chunk 
;;; declares the extern references to other c functions
    (define (write-c-header-file pure impure externs)
      (call-with-output-file "static.h"
	(lambda (port)
	  (format port "/* Static Heap File Automatically Generated~%")
	  (format port " * by scsh/static.scm */~%")
	  ;; declare the long arrays for each heap chunk
	  (let ((n (nchunks)))
	    (do ((i 0 (+ i 1)))
		((= i n))
	      (format port "extern const long p~s[~s];~%" i 
		      (quotient (heap:length (vector-ref   pure i)) 4)))
	    (do ((i 0 (+ i 1)))
		((= i n))
	      (format port "extern long i~s[~s];~%" i
		      (quotient (heap:length (vector-ref impure i)) 4))))
	  ;; declare the external references
	  (table-walk
	   (lambda (address name)
	     (format port "const extern ~a();~%" name))
	   externs)
	  )))

;;; compile-main-c-file
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; creates the top level interfaces that scheme48 wants to see
;;;  p_count i_count
;;;    number of chunks
;;;  p_areas i_areas
;;;    pointers to each chunk
;;;  p_sizes i_sizes
;;;    sizes of each chunk
;;; entry
;;;    the starting entry point
    (define (compile-main-c-file start reloc options)
      (let ((n (nchunks))
	    (cc (append (line->list (options:cc-command options)) '(-c))))
	(call-with-output-file "static.c"
	  (lambda (port)
	    (format port "#include \"static.h\"~%")
	    (format port "const long p_count = ~s;~%" n)
	    (format port "const long i_count = ~s;~%" n)
	    
	    (format port "const long * const p_areas[~s] = {" n)
	    (do ((i 0 (+ i 1)))
		((= i n))
	      (format port "(const long *) &p~s, " i))
	    (format port "};~%")
	    (format port "long * const i_areas[~s] = {" n)
	    (do ((i 0 (+ i 1)))
		((= i n))
	      (format port "(long *) &i~s, " i))
	    (format port "};~%")

	    (format port "const long p_sizes[~s] = {" n)
	    (do ((i 0 (+ i 1)))
		((= i n))
	      (format port "sizeof(p~s), " i))
	    (format port "};~%")
	    (format port "const long i_sizes[~s] = {" n)
	    (do ((i 0 (+ i 1)))
		((= i n))
	      (format port "sizeof(i~s), " i))
	    (format port "};~%")

	    (display "const long entry = " port)
	    (scsh-emit-descriptor start reloc port)
	    (write-char #\; port)
	    (newline port)))
	(let ((command (append cc '("static.c"))))
	  (message command)
	  (run (,@command)))))

;;; compile-c-image
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; responsible for writing and compiling the pure and impure heaps
    (define (compile-c-image pure impure reloc externs options)
      (compile-c-image1 pure   "p" "const " reloc externs options)
      (compile-c-image1 impure "i" ""       reloc externs options))

;;; compile-c-image1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; writes and compiles the c long array
    (define (compile-c-image1 heap name const reloc externs options)
      (let* ((n (nchunks))
	     (process #f)
	     (cc (append (line->list (options:cc-command options)) '(-c))))
	;; iterate over all the chunks for this part of heap
	(let chunk-loop ((c 0))
	  (cond ((not (= c n))
		 (let ((filename (format #f "static-~a~s.c" name c)))
		   (call-with-output-file filename
		     (lambda (port)	     
		       (format port "#include \"static.h\"~%")
		       (format port "~a long ~a~s[]={~%" const name c)
		       (let ((heap (vector-ref heap c)))
			 ;; iterate over each object
			 (let heap-loop ((l (heap:objects heap)))
			   (cond ((not (null? l))
				  (scsh-emit-initializer 
				   (car l) reloc externs port)
				  (heap-loop (cdr l))))))
		       (display "};" port)
		       (newline port)))
		   ;; wait for last compile before starting new one
		   (if process
		       (wait process))
		   (let ((command (append cc (list filename))))
		     (message command)
		     (set! process (& (,@command))))
		   (chunk-loop (+ 1 c))))
		(else
		 (wait process))))))

;;; link-files
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; links the .o's from compile-c-files
;;; uses the provided flags and libraries
;;; produces outfile as executable
    (define (link-files options)
      (let ((n (nchunks))
	    (ld (append (line->list (options:cc-command options))
			(line->list (options:ld-flags options))
			`(-o ,(options:output-executable options))))
	    (libs (line->list (options:libraries options))))
	(let ((command (append ld
			       (let loop ((i 0)
					  (l '()))
				 (cond ((not (= i n))
					(loop (+ i 1)
					      (cons 
					       (format #f "static-i~s.o" i)
					       (cons
						(format #f "static-p~s.o" i)
						l))))
				       (else 
					(reverse 		
					 (cons "static.o"
					       l)))))
			       (options:args-parser options)
			       '("-L" "@prefix@/lib/scsh" "-lscshvm")
			       libs)))
	  (message command)
	  (run (,@command)))))

;;; scsh-emit-initializer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; see scheme48 emit-initialize below
    (define (scsh-emit-initializer x reloc externs port)
      ;; emit the header
      (write-hex port (stob-header x))
      ;; handle descriptor vectors and vm-strings. 
      ;; everything else is a byte vector
      (cond ((d-vector? x)
	     (scsh-emit-d-vector-initializer x reloc port))
	    ((vm-string? x)
	     (scsh-emit-vm-string-initializer x port))
	    (else
	     (scsh-emit-b-vector-initializer x externs port)))
      (if *comments?*
	  (begin (display " /* " port)
		 (writex x port)
		 (display " */" port)))
      (newline port))

;;; scsh-emit-d-vector
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; descriptor vectors are pretty easy given scsh-emit-descriptor
    (define (scsh-emit-d-vector-initializer x reloc port)
      (let ((len (d-vector-length x)))
	(do ((i 0 (+ i 1)))
	    ((= i len))
	  (scsh-emit-descriptor (d-vector-ref x i) reloc port)
	  (write-char #\, port))))

;;; scsh-emit-descriptor
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; for descrriptors we consult the relocation table
    (define (scsh-emit-descriptor x reloc port)
      (if (stob? x)
	  (let ((n (chunk-number x)))
	    (display "(long)(&" port)
	    (if (immutable? x)
		(display "p" port)
		(display "i" port))
	    (display n port)
	    (display "[" port)
	    (display (quotient (table-ref (vector-ref reloc n) x) 4) port)
	    (display "])+7" port))
	  (format port 
		  (if (negative? x) "-0x~a" "0x~a")
		  (number->string (abs x) 16))))

;;; scsh-emit-vm-string-initializer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; vm-strings are converted to numbers and byte order adjusted
    (define (scsh-emit-vm-string-initializer x port)
      (let* ((len (vm-string-length x)) ; end is jawilson style hack
	     (end (- (cells->bytes (bytes->cells (+ len 1))) 4)))
	(do ((i 0 (+ i 4)))
	    ((= i end) 
	     (case (- len end)
	       ((0)
		(write-hex port 0))
	       ((1)
		(write-hex
		 port
		 (net-to-host-32 (arithmetic-shift 
				  (char->ascii (vm-string-ref x i)) 24))))
	       ((2)
		(write-hex 
		 port
		 (net-to-host-32 
		  (bitwise-ior
		   (arithmetic-shift
		    (char->ascii (vm-string-ref x i))       24)
		   (arithmetic-shift
		    (char->ascii (vm-string-ref x (+ i 1))) 16)))))
	       ((3)
		(write-hex
		 port
		 (net-to-host-32
		  (bitwise-ior
		   (bitwise-ior
		    (arithmetic-shift 
		     (char->ascii (vm-string-ref x i))       24)
		    (arithmetic-shift 
		     (char->ascii (vm-string-ref x (+ i 1))) 16))
		   (arithmetic-shift  
		    (char->ascii (vm-string-ref x (+ i 2)))  8)))))))
	  (write-hex port
		     (net-to-host-32 (bitwise-ior
				      (bitwise-ior
				       (arithmetic-shift 
					(char->ascii 
					 (vm-string-ref x i))       24)
				       (arithmetic-shift 
					(char->ascii 
					 (vm-string-ref x (+ i 1))) 16))
				      (bitwise-ior
				       (arithmetic-shift 
					(char->ascii 
					 (vm-string-ref x (+ i 2)))  8)
				       (char->ascii  
					(vm-string-ref x (+ i 3))))))
		     ))))

;;; scsh-emit-b-vector-initializer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; byte vectors are byte order adjusted too
    (define (scsh-emit-b-vector-initializer x externs port)
      (cond ((and (code-vector? x)
		  (table-ref externs x)) =>
		  (lambda (name)
		    (format port "(long) *~a," name)))
	    (else 
	     (let* ((len (b-vector-length x)) ;end is jawilson style hack
		    (end (- (cells->bytes (bytes->cells (+ len 1))) 4)))
	       (do ((i 0 (+ i 4)))
		   ((= i end)
		    (case (- len end)
		      ((1)
		       (write-hex
			port
			(net-to-host-32 
			 (arithmetic-shift (b-vector-ref x i) 24))))
		      ((2)
		       (write-hex 
			port
			(net-to-host-32
			 (bitwise-ior
			  (arithmetic-shift (b-vector-ref x i)       24)
			  (arithmetic-shift (b-vector-ref x (+ i 1)) 16)))))
		      ((3)
		       (write-hex
			port
			(net-to-host-32
			 (bitwise-ior
			  (bitwise-ior
			   (arithmetic-shift (b-vector-ref x i)       24)
			   (arithmetic-shift (b-vector-ref x (+ i 1)) 16))
			  (arithmetic-shift  (b-vector-ref x (+ i 2))  8)))
			))))
		 (write-hex 
		  port
		  (net-to-host-32 (bitwise-ior
				   (bitwise-ior
				    (arithmetic-shift 
				     (b-vector-ref x i)       24)
				    (arithmetic-shift 
				     (b-vector-ref x (+ i 1)) 16))
				   (bitwise-ior
				    (arithmetic-shift 
				     (b-vector-ref x (+ i 2))  8)
				    (b-vector-ref x (+ i 3))))))))
	     )))

;;; scsh-for-each-stored-object
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; see scheme48 for-each-stored-object
;;; Image traversal utility

    (define (scsh-for-each-stored-object chunk-start proc chunk-end)
      (let ((limit (heap-pointer)))
	(let chunk-loop ((addr (newspace-begin))
			 (i 0)
			 (chunk (+ (newspace-begin) *chunk-size*)))
	  (if (addr< addr limit)
	      (begin (chunk-start i)
		     (let loop ((addr addr))
		       (if (and (addr< addr limit)
				(addr< addr chunk))
			   (let* ((d   (fetch addr))
				  (len (addr1+ (header-a-units d))))
			     (if (not (header? d))
				 (warn "heap is in an inconsistent state" d))
			     (proc i 
				   (address->stob-descriptor (addr1+ addr)) 
				   len)
			     (loop (addr+ addr len)))
			   (begin (chunk-end i)
				  (chunk-loop addr
					      (+ i 1)
					      (+ chunk *chunk-size*))))))))))
;;; write-hex
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; utility routine to print a scheme number as a c hex number
    (define (write-hex port x) 
      (format port 
	      (if (negative? x) "-0x~a," "0x~a,")
	      (number->string (abs x) 16)))

;;; line->list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; utility that takes a string and break it into a list at whitespace
;;; rewrite using scsh stuff?
    (define (line->list line)
      (let ((len (string-length line)))
	(let loop ((start 0)
		   (end 0)
		   (l '()))
	  (cond ((>= end len)
		 (if (= start end)
		     l
		     (append l (list (substring line start end)))))
		((and (= start end)
		      (or (char=? (string-ref line start) (ascii->char 32))
			  (char=? (string-ref line start) (ascii->char 9))))
		 (loop (+ 1 start) 
		       (+ 1 end) 
		       l))
		((or (char=? (string-ref line end) (ascii->char 32))
		     (char=? (string-ref line end) (ascii->char 9)))
		 (loop (+ 1 end) 
		       (+ 1 end) 
		       (append l (list (substring line start end)))))
		((< end len)
		 (loop start 
		       (+ 1 end)
		       l))
		(else (error "unexpected case in line->list"))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Debugging
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    (define (bin n)
      (number->string n 2))

    (define (oct n)
      (number->string n 8))

    (define (dec n)
      (number->string n 10))

    (define (hex n)
      (number->string n 16))
    
    ;;; Static Heap Code From Scheme48
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; The Scheme 48 version produced monolithic C files that even
    ;;; the GNU C Compiler couldn't handle, let alone standard vendor
    ;;; compilers...
    ;;; It also relied upon the C compiler to fill in some pointer
    ;;; information. Because I needed to break up the files, I had to
    ;;; calculate this information myself.

					; For example:
					;   (do-it 100000 "~/s48/debug/little.image" "little-heap.c")
					;
					; The first argument to do-it should be somewhat larger than the size,
					; in bytes, of the image file to be converted (which you can obtain with
					; "ls -l").
					;
					; If the image contains 0-length stored objects, then the .c file will
					; have to be compiled by gcc, since 0-length arrays aren't allowed in
					; ANSI C.  This wouldn't be difficult to work around.
    
    (define *comments?* #f)
    
					; 800,000 bytes => 200,000 words => at least 100,000 objects
					;   50 chunks => 16,000 bytes per chunk => 2,000 objects per chunk
    (define *chunk-size* 10000)

    (define (do-it bytes infile outfile)
      (let ((start (init bytes infile)))
	(call-with-output-file outfile
	  (lambda (port)
	    (format port "#define D(x) (long)(&x)+7~%")
	    (format port "#define H unsigned long~%")
	    (emit-area-declarations "p" immutable? "const " port)
	    (emit-area-declarations "i" mutable? "" port)
	    (emit-area-initializers "p" immutable? "const " port)
	    (emit-area-initializers "i" mutable? "" port)
	    (display "const long entry = " port)
	    (emit-descriptor start port)
	    (write-char #\; port)
	    (newline port)))))

    (define (init bytes infile)
      (create-memory (quotient bytes 2) quiescent) ;Output of ls -l
      (initialize-heap (memory-begin) (memory-size))
      (let ((start (read-image infile 0)))
	(message (nchunks)
		 " chunks")
	start))

    (define (nchunks) (+ (chunk-number (heap-pointer)) 1))

					; emit struct declarations for areas

    (define (emit-area-declarations name in-area? const port)
      (for-each-stored-object
       (lambda (chunk)
	 (message name chunk " declaration")
	 (display "struct " port) (display name port) (display chunk port)
	 (display " {" port) (newline port))
       (lambda (x)
	 (if (in-area? x)
	     (emit-declaration x port)))
       (lambda (chunk)
	 (display "};" port)
	 (newline port)
	 (display const port)
	 (display "extern struct " port) (display name port) (display chunk port)
	 (write-char #\space port) (display name port) (display chunk port)
	 (write-char #\; port) (newline port)
	 chunk)))

    (define (emit-declaration x port)
      (display "  H x" port)
      (writex x port)
      (cond ((d-vector? x)
	     (display "; long d" port)
	     (writex x port)
	     (write-char #\[ port)
	     (write (d-vector-length x) port))
	    ((vm-string? x)
	     (display "; char d" port)
	     (writex x port)
	     (write-char #\[ port)
	     ;; Ensure alignment (thanks Ian)
	     (write (cells->bytes (bytes->cells (b-vector-length x)))
		    port))
	    (else
	     (display "; unsigned char d" port)
	     (writex x port)
	     (write-char #\[ port)
	     ;; Ensure alignment
	     (write (cells->bytes (bytes->cells (b-vector-length x)))
		    port)))
      (display "];" port)
      (if *comments?*
	  (begin (display " /* " port)
		 (display (enumerand->name (stob-type x) stob) port)
		 (display " */" port)))
      (newline port))

					; Emit initializers for areas

    (define (emit-area-initializers name in-area? const port)
      (for-each-stored-object
       (lambda (chunk)
	 (message name chunk " initializer")

	 (display const port)
	 (display "struct " port) (display name port) (write chunk port)
	 (write-char #\space port) (display name port) (write chunk port)
	 (display " =" port) (newline port)

	 (write-char #\{ port) (newline port))
       (lambda (x)
	 (if (in-area? x)
	     (emit-initializer x port)))
       (lambda (chunk)
	 (display "};" port) (newline port)))

      (let ((n (nchunks)))
	(format port "const long ~a_count = ~s;~%" name n)
	(format port "~a long * const ~a_areas[~s] = {" const name n)
	(do ((i 0 (+ i 1)))
	    ((= i n))
	  (format port "(~a long *)&~a~s, " const name i))
	(format port "};~%const long ~a_sizes[~s] = {" name n)
	(do ((i 0 (+ i 1)))
	    ((= i n))
	  (format port "sizeof(~a~s), " name i))
	(format port "};~%")))


    (define (message . stuff)
      (for-each display stuff) (newline))

    (define (emit-initializer x port)
      (display "  " port)
      (write (stob-header x) port)
      (write-char #\, port)
      (cond ((d-vector? x)
	     (emit-d-vector-initializer x port))
	    ((vm-string? x)
	     (write-char #\" port)
	     (let ((len (vm-string-length x)))
	       (do ((i 0 (+ i 1)))
		   ((= i len) (write-char #\" port))
		 (let ((c (vm-string-ref x i)))
		   (cond ((or (char=? c #\") (char=? c #\\))
			  (write-char #\\ port))
			 ((char=? c #\newline)
			  (display "\\n\\" port)))
		   (write-char c port)))))
	    (else
	     (write-char #\{ port)
	     (let ((len (b-vector-length x)))
	       (do ((i 0 (+ i 1)))
		   ((= i len) (write-char #\} port))
		 (write (b-vector-ref x i) port)
		 (write-char #\, port)))))
      (write-char #\, port)
      (if *comments?*
	  (begin (display " /* " port)
		 (writex x port)
		 (display " */" port)))
      (newline port))

    (define (emit-d-vector-initializer x port)
      (write-char #\{ port)
      (let ((len (d-vector-length x)))
	(do ((i 0 (+ i 1)))
	    ((= i len) (write-char #\} port))
	  (emit-descriptor (d-vector-ref x i) port)
	  (write-char #\, port))))

    (define (emit-descriptor x port)
      (if (stob? x)
	  (begin (if (immutable? x)
		     (display "D(p" port)
		     (display "D(i" port))
		 (display (chunk-number x) port)
		 (display ".x" port)
		 (writex x port)
		 (write-char #\) port))
	  (write x port)))


					; Foo

    (define (writex x port)
      (write (quotient (- (- x (memory-begin)) 7) 4) port))

    (define (chunk-number x)
      (quotient (- (- x (memory-begin)) 7) *chunk-size*))


					; Image traversal utility

    (define (for-each-stored-object chunk-start proc chunk-end)
      (let ((limit (heap-pointer)))
	(let chunk-loop ((addr (newspace-begin))
			 (i 0)
			 (chunk (+ (newspace-begin) *chunk-size*)))
	  (if (addr< addr limit)
	      (begin (chunk-start i)
		     (let loop ((addr addr))
		       (if (and (addr< addr limit)
				(addr< addr chunk))
			   (let ((d (fetch addr)))
			     (if (not (header? d))
				 (warn "heap is in an inconsistent state" d))
			     (proc (address->stob-descriptor (addr1+ addr)))
			     (loop (addr1+ (addr+ addr (header-a-units d)))))
			   (begin (chunk-end i)
				  (chunk-loop addr
					      (+ i 1)
					      (+ chunk *chunk-size*))))))))))

    (define (mutable? x) (not (immutable? x)))

    ;; End begin
    ))