home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #6
/
amigamamagazinepolishissue1998.iso
/
coders
/
jËzyki_programowania
/
clisp
/
src
/
archive
/
clisp.faslsp.lha
/
affi1.lsp
< prev
next >
Wrap
Lisp/Scheme
|
1996-08-12
|
11KB
|
290 lines
;;;; Simple Foreign Function Interface support
;;;; Jörg Höhle 7.8.1996
(in-package "AFFI")
(eval-when (eval load compile)
(dolist (symbol '("MEM-READ" "MEM-WRITE" "MEM-WRITE-VECTOR" "NZERO-POINTER-P"))
(import (intern symbol "SYS"))))
(export '(declare-library-base require-library-functions
open-library close-library with-open-library
defflibfun declare-library-function flibcall mlibcall
nzero-pointer-p mem-read mem-write mem-write-vector))
;; The libraries a-list associates the symbol used to denote the library
;; (e.g. SysBase for exec.library) with an opencount and the OS name (as a
;; string). The symbol's value field contains the library address.
(defvar *libraries-alist* '((SysBase 0 "exec.library")))
(defun reset-libraries-at-init ()
;; reset all libray base pointers when starting a new Lisp
(dolist (library *libraries-alist*)
(makunbound (first library))))
(pushnew 'reset-libraries-at-init sys::*init-hooks*)
(defun declare-library-base (symbol name)
"Associate the SYMBOL referencing library NAME and import it."
(unless (and (keywordp symbol) (stringp name))
(error
#L{
DEUTSCH "Base ~S kein Schlüsselwort oder Libraryname ~S kein String"
ENGLISH "Basename ~S not a keyword or libraryname ~S not a string"
FRANCAIS "La base ~S n'est pas un mot-clé ou alors le nom de librairie ~S n'est pas une chaîne"
}
symbol name))
(setq symbol (intern (symbol-name symbol) '#.*package*)) ;intern into AFFI
(let ((found (assoc symbol *libraries-alist* :test #'eq)))
(cond (found
(unless (string-equal name (third found))
;; how possibly continue with cerror?
(error
#L{
DEUTSCH "Libraryredefinition: alt ~S, neu ~S"
ENGLISH "Library redefinition: old ~S, new ~S"
FRANCAIS "Redéfinition d'une librarie : avant ~S, maintenant ~S"
}
(third found) name)))
(t (push (list symbol 0 name) *libraries-alist*))))
(proclaim `(special ,symbol))
(import symbol)
symbol)
(defun check-library-base (symbol)
(or (assoc symbol *libraries-alist* :test #'eq)
(error
#L{
DEUTSCH "Unbekannte Library: ~S"
ENGLISH "Unknown library: ~S"
FRANCAIS "Librairie inconnue : ~S"
}
symbol)))
;;TODO? library version
(defun open-library (symbol)
"Returns library address or NIL if it failed. The library must be known."
;;CLISP won't close libraries for you...
(let ((found (check-library-base symbol)))
(cond
((boundp symbol)
(incf (second found)) ;open count
(symbol-value symbol))
(t ;first open
(let ((base
(sys::%libcall
(locally (declare (special SysBase))
(if (boundp 'SysBase) SysBase (sys::mem-read 4 '*)))
'#((-552 . #x1A) * string 2)
(third found) 0)))
(when (nzero-pointer-p base)
(prog1 (set (first found) base)
(setf (second found) 1)))))))) ; field might have been invalid
(defun close-library (symbol)
(let ((found (check-library-base symbol)))
(cond
((not (boundp symbol))
(error
#L{
DEUTSCH "Library ~S ist gar nicht geöffnet"
ENGLISH "Library ~S is not open"
FRANCAIS "La librairie ~S n'est pas ouverte"
}
symbol))
;;TODO? count<0 Test
((zerop (decf (second found)))
(sys::%libcall
(locally (declare (special SysBase))
(if (boundp 'SysBase) SysBase (sys::mem-read 4 '*)))
'#((-414 . #xA) () *)
(symbol-value symbol))
(makunbound symbol))))
t)
(defun check-library-name (name)
;; same as above, but allows base symbol or library name
(cond ((when (stringp name)
(rassoc name *libraries-alist* :key #'second
:test #'string-equal))) ;case-sensitive like Exec
(t (check-library-base name)))) ;find it or error
(defmacro with-open-library ((name) &body body)
"If necessary opens library NAME, and executes BODY, finally closing it.
Returns NIL if the library can't be opened. Unlike OPEN-LIBRARY, NAME may
be a string, which must be the name of a known library."
(when (stringp name) ; name must be known at compile-time
(setq name (car (check-library-name name))))
`(when (open-library ',name)
(unwind-protect (multiple-value-prog1 (progn ,@body))
(close-library ',name))))
;; All known functions symbols are stored in a single hash-table. Each
;; function is associated with its library and call information in a cons
;; pair: <function> -> ( <library> . <call information> )
(defvar *library-functions* (make-hash-table :test 'eq))
(defun import-or-loose (symbol)
(let ((new (find-symbol (symbol-name symbol))))
(when (and new (not (eq new symbol)))
(error "Another symbol ~A already exists" symbol))
(cond (new)
(t (import symbol) symbol))))
(defun require-library-functions (name &key (import t))
"Loads foreign function definitions for library NAME if necessary."
(let* ((entry (check-library-name name))
(base (first entry))
(name (third entry)))
(require name (make-pathname :type "affi" :defaults name)) ;"exec.affi"
;; If require worked, name must be correct, now export symbols
(typecase import
(LIST ; import only requested functions
(values
base
(loop for fname in import
collect
(etypecase fname
(STRING
(let* ((common (intern (string-upcase fname) '#.*package*)) ;TODO? match *fd-readtable* case
(info (gethash common *library-functions*)))
(if (and info (eq (car info) base))
(import-or-loose common)
(error
#L{
DEUTSCH "Funktion in Library ~S unbekannt: ~S"
ENGLISH "Unknown function of library ~S: ~S"
FRANCAIS "Fonction inconnue dans la librarie ~S : ~S"
}
name common))))))))
((EQL T)
;; as we don't store functions on a per-library basis, walk over all
(maphash
#'(lambda (function info)
(when (and (eq (car info) base)
;; Assume we only need to import AFFI symbols (others are imported from Lisp)
;; possible problem with "LISP" vs. "COMMON-LISP"
(eq (symbol-package function) '#.*package*))
(import-or-loose function)))
*library-functions*)
base))))
;; The function call information is used by the C part. It's a simple-
;; vector of n+2 elements for an n-ary function. The first element may be
;; nil or a cons of a library offset and a mask indicating a
;; register-based call. The second element indicates the function return
;; type. The other elements indicate the argument types.
;; ATTENTION: AFFI.D depends on this format!
(defun defflibfun (name library offset mask result-type &rest arg-types)
(check-library-base library)
(unless (typep offset 'fixnum) ;TODO not only reg calls
(error
#L{
DEUTSCH "Offset ist kein FIXNUM: ~S"
ENGLISH "Offset must be a fixnum: ~S"
FRANCAIS "Le déplacement ~S n'est pas de type FIXNUM"
}
offset))
(let ((old (gethash name *library-functions*))
(new (cons library
(concatenate
'simple-vector
(list (cons offset mask) result-type)
arg-types))))
(unless (equalp old new)
(when old
(format *error-output*
#L{
DEUTSCH "~&;; Definitionsänderung der foreign-library Funktion ~S~%;; von ~S nach ~S.~%"
ENGLISH "~&;; redefining foreign library function ~S~%;; from ~S to ~S~%"
FRANCAIS "~&;; Redéfinition de la fonction étrangère ~S~%;; de ~S en ~S~%"
}
name old new))
;; TODO check types
(setf (gethash name *library-functions*) new)))
name)
(defun calc-register-mask (regs test)
(labels
((calc (regs accu)
(if (null regs) accu
(calc (rest regs)
(logior
(ash accu 4)
(1+ (or (position
(first regs)
'(:D0 :D1 :D2 :D3 :D4 :D5 :D6 :D7
:A0 :A1 :A2 :A3 :A4 :A5 :A6)
:test test)
(error
#L{
DEUTSCH "Unbekanntes Register: ~S"
ENGLISH "Unknown register: ~S"
FRANCAIS "Registre inconnu : ~S"
}
(first regs)))))))))
(calc (reverse regs) 0)))
;; better-looking definitions (closer to FFI)
(defmacro declare-library-function (name library &rest options)
(let ((base (first (check-library-name library)))
(arguments (rest (assoc :arguments options))))
(dolist (arg arguments)
(unless (and (symbolp (first arg)) ; variable name
#+AMIGA (keywordp (third arg)) ; register
(null (nthcdr 3 arg))) ; nothing more
(error
#L{
DEUTSCH "Ungültige Parameterspezifikation ~S in ~S"
ENGLISH "Invalid parameter specification ~S in function ~S"
FRANCAIS "Spécification invalide d'argument ~S pour la fonction ~S"
}
arg name)))
`(defflibfun ',name ',base
',(second (assoc :offset options))
',(calc-register-mask (mapcar #'third arguments) #'eq)
',(second (assoc :return-type options))
,.(mapcar #'(lambda (arg) (list 'quote (second arg))) arguments))))
(flet
((function-info (name)
(or (gethash name *library-functions*)
(error
#L{
DEUTSCH "Unbekannte Libraryfunktion: ~S"
ENGLISH "Unknown library function: ~S"
FRANCAIS "Fonction d'une librairie inconnue : ~S"
}
name))))
(defun flibcall (name &rest args)
"Call library function NAME with any number of ARGS."
(let ((info (function-info name)))
(apply #'sys::%libcall
(symbol-value (car info)) ; library
(cdr info)
args)))
(defmacro mlibcall (name &rest args)
"Call library function NAME with ARGS."
(let ((info (function-info name)))
(if (= (length args) (- (length (cdr info)) 2))
`(sys::%libcall ,(car info) ',(cdr info) . ,args)
(sys::error-of-type 'program-error
#L{
DEUTSCH "Falsche anzahl an Argumenten für ~S: ~S"
ENGLISH "Bad number of arguments for ~S: ~S"
FRANCAIS "Mauvais nombre d'arguments pour ~S : ~S"
}
name (length args)))))
) ; flet