; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*- ; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING. ; Scalar primitives (define-primitive eq? (any-> any->) vm-eq? return-boolean) (define-primitive char? (any->) vm-char? return-boolean) (define-primitive char=? (vm-char-> vm-char->) vm-char=? return-boolean) (define-primitive char vm-char->) vm-charascii (char->) char->ascii return-fixnum) (define-primitive ascii->char (fixnum->) (lambda (x) (if (or (> x 255) (< x 0)) (raise-exception wrong-type-argument 0 (enter-fixnum x)) (goto return (enter-char (ascii->char x)))))) (define-primitive eof-object? (any->) (lambda (x) (vm-eq? x eof-object)) return-boolean) ;---------------- (define-primitive stored-object-has-type? (any->) (lambda (x) (goto continue-with-value (enter-boolean (stob-of-type? x (code-byte 0))) 1))) (define-primitive stored-object-length (any->) (lambda (stob) (let ((type (code-byte 0))) (if (stob-of-type? stob type) (goto continue-with-value (enter-fixnum (d-vector-length stob)) 1) (raise-exception wrong-type-argument 1 stob (enter-fixnum type)))))) ; Fixed sized objects (define-primitive stored-object-ref (any->) (lambda (stob) (let ((type (code-byte 0)) (offset (code-byte 1))) (if (stob-of-type? stob type) (goto continue-with-value (d-vector-ref stob offset) 2) (raise-exception wrong-type-argument 2 stob (enter-fixnum type) (enter-fixnum offset)))))) (define-primitive stored-object-set! (any-> any->) (lambda (stob value) (let ((type (code-byte 0)) (offset (code-byte 1))) (cond ((and (stob-of-type? stob type) (not (immutable? stob))) (d-vector-set! stob offset value) (goto continue-with-value unspecific-value 2)) (else (raise-exception wrong-type-argument 2 stob (enter-fixnum type) (enter-fixnum offset) value)))))) ; Indexed objects (define-primitive stored-object-indexed-ref (any-> any->) (lambda (stob index) (let ((type (code-byte 0))) (cond ((or (not (fixnum? index)) (not (stob-of-type? stob type))) (raise-exception wrong-type-argument 1 stob (enter-fixnum type) index)) ((valid-index? (extract-fixnum index) (d-vector-length stob)) (goto continue-with-value (d-vector-ref stob (extract-fixnum index)) 1)) (else (raise-exception index-out-of-range 1 stob (enter-fixnum type) index)))))) (define-primitive stored-object-indexed-set! (any-> any-> any->) (lambda (stob index value) (let ((type (code-byte 0))) (cond ((or (not (fixnum? index)) (not (stob-of-type? stob type)) (immutable? stob)) (raise-exception wrong-type-argument 1 stob (enter-fixnum type) index value)) ((valid-index? (extract-fixnum index) (d-vector-length stob)) (d-vector-set! stob (extract-fixnum index) value) (goto continue-with-value unspecific-value 1)) (else (raise-exception index-out-of-range 1 stob (enter-fixnum type) index value)))))) ; Hacko record handlers done for speed. (define-primitive checked-record-ref (any-> any-> fixnum->) (lambda (record type index) (cond ((not (and (stob-of-type? record (enum stob record)) (vm-eq? type (record-ref record 0)))) (raise-exception wrong-type-argument 0 record type (enter-fixnum index))) ((valid-index? index (record-length record)) (goto return (record-ref record index))) (else (raise-exception index-out-of-range 0 record type (enter-fixnum index)))))) (define-primitive checked-record-set! (any-> any-> fixnum-> any->) (lambda (record type index value) (cond ((not (and (stob-of-type? record (enum stob record)) (vm-eq? type (record-ref record 0)) (not (immutable? record)))) (raise-exception wrong-type-argument 0 record type (enter-fixnum index) value)) ((valid-index? index (record-length record)) (record-set! record index value) (goto no-result)) (else (raise-exception index-out-of-range 0 record type (enter-fixnum index) value))))) ; Constructors (define-primitive make-stored-object () (lambda () (let* ((len (code-byte 0)) (key (ensure-space (cells->bytes (+ stob-overhead len)))) (new (make-d-vector (code-byte 1) len key))) (cond ((>= len 1) (d-vector-init! new (- len 1) *val*) (do ((i (- len 2) (- i 1))) ((> 0 i) (unspecific)) ; for the type checker! (d-vector-init! new i (pop))))) (goto continue-with-value new 2)))) ; This is for the closed compiled versions of VECTOR and RECORD. ; *stack* = arg0 arg1 ... argN rest-list N+1 total-nargs (define-primitive closed-make-stored-object () (lambda () (let* ((len (extract-fixnum (pop))) (key (ensure-space (cells->bytes (+ stob-overhead len)))) (new (make-d-vector (code-byte 0) len key)) (stack-nargs (extract-fixnum (pop))) (rest-list (pop))) (do ((i (- stack-nargs 1) (- i 1))) ((> 0 i) (unspecific)) ; for the type checker! (d-vector-init! new i (pop))) (do ((i stack-nargs (+ i 1)) (rest-list rest-list (vm-cdr rest-list))) ((vm-eq? rest-list null) (unspecific)) ; for the type checker! (d-vector-init! new i (vm-car rest-list))) (goto continue-with-value new 1)))) (define-primitive make-vector-object (any-> any->) (lambda (len init) (let ((type (code-byte 0))) (if (fixnum? len) (let* ((len (extract-fixnum len)) (size (vm-vector-size len))) (if (or (< len 0) (> size max-stob-size-in-cells)) (raise-exception wrong-type-argument 1 (enter-fixnum type) (enter-fixnum len) init) (receive (okay? key init) (maybe-ensure-space-saving-temp size init) (if (not okay?) (raise-exception heap-overflow 1 (enter-fixnum type) (enter-fixnum len) init) (let ((v (make-d-vector type len key))) (do ((i (- len 1) (- i 1))) ((< i 0)) (d-vector-set! v i init)) (goto continue-with-value v 1)))))) (raise-exception wrong-type-argument 1 (enter-fixnum type) len init))))) ; Strings and byte vectors (define-primitive string-length (string->) (lambda (string) (goto return-fixnum (vm-string-length string)))) (define-primitive byte-vector-length (code-vector->) (lambda (byte-vector) (goto return-fixnum (code-vector-length byte-vector)))) (define (make-byte-ref ref length returner) (lambda (vector index) (if (valid-index? index (length vector)) (goto returner (ref vector index)) (raise-exception index-out-of-range 0 vector (enter-fixnum index))))) (let ((proc (make-byte-ref vm-string-ref vm-string-length return-char))) (define-primitive string-ref (string-> fixnum->) proc)) (let ((proc (make-byte-ref code-vector-ref code-vector-length return-fixnum))) (define-primitive byte-vector-ref (code-vector-> fixnum->) proc)) (define (make-byte-setter setter length enter-elt) (lambda (vector index char) (cond ((immutable? vector) (raise-exception wrong-type-argument 0 vector (enter-fixnum index) (enter-elt char))) ((valid-index? index (length vector)) (setter vector index char) (goto no-result)) (else (raise-exception index-out-of-range 0 vector (enter-fixnum index) (enter-elt char)))))) (let ((proc (make-byte-setter vm-string-set! vm-string-length enter-char))) (define-primitive string-set! (string-> fixnum-> char->) proc)) (let ((proc (make-byte-setter code-vector-set! code-vector-length enter-fixnum))) (define-primitive byte-vector-set! (code-vector-> fixnum-> fixnum->) proc)) (define (byte-vector-maker size maker setter enter-elt) (lambda (len init) (let ((size (size len))) (if (or (< len 0) (> size max-stob-size-in-cells)) (raise-exception wrong-type-argument 0 (enter-fixnum len) (enter-elt init)) (receive (okay? key) (maybe-ensure-space size) (if (not okay?) (raise-exception heap-overflow 0 (enter-fixnum len) (enter-elt init)) (let ((vector (maker len key))) (do ((i (- len 1) (- i 1))) ((< i 0)) (setter vector i init)) (goto return vector)))))))) (let ((proc (byte-vector-maker vm-string-size vm-make-string vm-string-set! enter-char))) (define-primitive make-string (fixnum-> char->) proc)) (let ((proc (byte-vector-maker code-vector-size make-code-vector code-vector-set! enter-fixnum))) (define-primitive make-byte-vector (fixnum-> fixnum->) proc)) ; Locations & mutability (define-primitive location-defined? (location->) (lambda (loc) (return-boolean (or (not (undefined? (contents loc))) (= (contents loc) unassigned-marker))))) (define-primitive set-location-defined?! (location-> boolean->) (lambda (loc value) (cond ((not value) (set-contents! loc unbound-marker)) ((undefined? (contents loc)) (set-contents! loc unassigned-marker)))) return-unspecific) (define-primitive immutable? (any->) immutable? return-boolean) (define-primitive make-immutable! (any->) (lambda (thing) (make-immutable! thing) (goto return thing))) ;---------------- ; Misc (define-primitive false () (lambda () (goto return false))) (define-primitive eof-object () (lambda () (goto return eof-object))) (define-primitive trap (any->) (lambda (arg) (raise-exception trap 0 arg))) (define-primitive find-all (fixnum->) (lambda (type) (let loop ((first? #t)) (let ((vector (s48-find-all type))) (cond ((not (false? vector)) (goto return vector)) (first? (collect) (loop #f)) (else (raise-exception heap-overflow 0 (enter-fixnum type)))))))) (define-primitive find-all-records (any->) (lambda (type) (let loop ((first? #t) (type type)) (let ((vector (s48-find-all-records type))) ; only one call site (cond ((not (false? vector)) (goto return vector)) (first? (loop #f (collect-saving-temp type))) (else (raise-exception heap-overflow 0 type))))))) (define-primitive collect () (lambda () (set! *val* unspecific-value) (collect) (goto continue 0))) (define-consing-primitive add-finalizer! (any-> any->) (lambda (n) (* 2 vm-pair-size)) (lambda (stob proc key) (cond ((not (and (stob? stob) (closure? proc))) (raise-exception wrong-type-argument 0 stob proc)) ; This would be useful but could get quite expensive ; ((vm-assq stob *finalizer-alist*) ; (raise-exception has-finalizer 0 stob proc)) (else (set! *finalizer-alist* (vm-cons (vm-cons stob proc key) *finalizer-alist* key)) (goto no-result))))) (define-primitive memory-status (fixnum-> any->) (lambda (key other) (enum-case memory-status-option key ((pointer-hash) (goto return (descriptor->fixnum other))) ((available) (goto return-fixnum (s48-available))) ((heap-size) (goto return-fixnum (bytes->cells (s48-heap-size)))) ((stack-size) (goto return-fixnum (stack-size))) ((gc-count) (goto return-fixnum (s48-gc-count))) ((expand-heap!) (raise-exception unimplemented-instruction 0 (enter-fixnum key) other)) (else (raise-exception bad-option 0 (enter-fixnum key) other))))) (define-primitive time (fixnum-> any->) (lambda (option other) (enum-case time-option option ((cheap-time) (goto return-fixnum (cheap-time))) ((run-time) (receive (seconds mseconds) (run-time) (goto return-time-value option seconds mseconds))) ((real-time) (receive (seconds mseconds) (real-time) (goto return-time-value option seconds mseconds))) (else (raise-exception bad-option 0 (enter-fixnum option) other))))) ; The largest number of seconds that can be converted into a fixnum number ; of milliseconds. (define maximum-seconds (quotient (- greatest-fixnum-value 1000) 1000)) (define (return-time-value option seconds mseconds) (if (> seconds maximum-seconds) (raise-exception arithmetic-overflow 0 (enter-fixnum option) (enter-fixnum seconds) (enter-fixnum mseconds)) (goto return-fixnum (+ (* seconds 1000) mseconds)))) (define-primitive schedule-interrupt (fixnum->) (lambda (delta) (clear-interrupt! (enum interrupt alarm)) (goto return-fixnum (schedule-interrupt delta)))) ; Convert from the user's exponent to the system's. ;(define (adjust-time mantissa exponent) ; (let ((system (clock-exponent))) ; (cond ((= exponent system) ; mantissa) ; ((> system exponent) ; (quotient mantissa (expt 10 (- system exponent)))) ; (else ; (* mantissa (expt 10 (- exponent system))))))) (define-enumeration vm-extension-status (okay exception )) (define s48-*extension-value*) (define-primitive vm-extension (fixnum-> any->) (lambda (key value) (let ((status (extended-vm key value))) (cond ((vm-eq? status (enum vm-extension-status okay)) (goto return s48-*extension-value*)) ((vm-eq? status (enum vm-extension-status exception)) (raise-exception extension-exception 0 (enter-fixnum key) value)) (else (raise-exception extension-return-error 0 (enter-fixnum key) value)))))) ; This is exported to keep s48-*EXTENSION-VALUE* from being eliminated by the ; compiler. (define (s48-set-extension-value! value) (set! s48-*extension-value* value)) ; Used to indicate which stack block we are returning to. Set to FALSE if we are ; returning from the VM as a whole. (define s48-*callback-return-stack-block* false) (define-primitive return-from-callback (any-> any->) (lambda (stack-block value) (enable-interrupts!) ; Disabled to ensure that we return to the right ; stack block. (set! s48-*callback-return-stack-block* stack-block) value)) ; the interpreter returns this value (define-primitive current-thread () (lambda () *current-thread*) return-any) (define-primitive set-current-thread! (any->) (lambda (state) (set! *current-thread* state)) return-unspecific) (define-primitive session-data () (lambda () *session-data*) return-any) (define-primitive set-session-data! (any->) (lambda (state) (set! *session-data* state)) return-unspecific) ; Unnecessary primitives (define-primitive string=? (string-> string->) vm-string=? return-boolean) ; Special primitive called by the reader. ; Primitive for the sake of speed. Probably should be flushed. (define-consing-primitive reverse-list->string (any-> fixnum->) (lambda (n) (vm-string-size (extract-fixnum n))) (lambda (l n k) (if (not (or (vm-pair? l) (vm-eq? l null))) (raise-exception wrong-type-argument 0 l (enter-fixnum n)) (let ((obj (vm-make-string n k))) (do ((l l (vm-cdr l)) (i (- n 1) (- i 1))) ((< i 0) (goto return obj)) (vm-string-set! obj i (extract-char (vm-car l)))))))) (define-primitive string-hash (string->) vm-string-hash return-fixnum) ; Messy because we have to detect circular lists (alternatively we ; could check for interrupts and then pclsr). *** (define-primitive assq (any-> any->) (lambda (thing list) (let ((lose (lambda () (raise-exception wrong-type-argument 0 thing list)))) (let loop ((list list) (slow list) (move-slow? #t)) (cond ((vm-eq? list null) (goto return-boolean #f)) ((not (vm-pair? list)) (lose)) (else (let ((head (vm-car list))) (cond ((not (vm-pair? head)) (lose)) ((vm-eq? (vm-car head) thing) (goto return head)) (else (let ((list (vm-cdr list))) (cond ((eq? list slow) (lose)) (move-slow? (loop list (vm-cdr slow) #f)) (else (loop list slow #t))))))))))))) ; Eventually add make-table, table-ref, table-set! as primitives? ; No -- write a compiler instead. ; *** Our entry for the obscure comment of the year contest. ; ; Pclsring is the term in ITS for the mechanism that makes the operating system ; appear to be a virtual machine. The paradigm is that of the BLT instruction ; on the PDP-10: its arguments are in a set of registers, and if the instruction ; gets interrupted in the middle, the registers reflect the intermediate state; ; the PC is set to the BLT instruction itself, and the process can be resumed ; in the usual way. ; For more on pclsring see `Pclsring: Keeping Process State Modular' by Alan ; Bawden (ftp.ai.mit.edu:pub/alan/pclsr.memo).