;;; ;;; icfp2006.lisp ;;; ;;; implementation of the icfp2006 VM ;;; (cl:defpackage :icfp2006 (:use :cl)) (in-package :icfp2006) (declaim (type (simple-array (unsigned-byte 32) (8)) *registers*)) (defparameter *registers* (make-array 8 :element-type '(unsigned-byte 32))) (defvar *arrays*) (declaim (type (simple-array (unsigned-byte 32) (*)) *array-0*)) (defvar *array-0*) (defparameter *machine-halted* nil) (defparameter *instruction-pointer* 0) (defparameter *next-array* 1) (declaim (inline find-array)) (defun find-array (index) (if (zerop index) *array-0* (gethash index *arrays*))) (defun alloc-array (size) (loop while (find-array *next-array*) do (setf *next-array* (logand #xffffffff (1+ *next-array*)))) (setf (gethash *next-array* *arrays*) (make-array size :element-type '(unsigned-byte 32))) *next-array*) (defun free-array (index) (remhash index *arrays*)) (defun load-file (filename) (with-open-file (in filename :direction :input :element-type '(unsigned-byte 8)) (let ((data (make-array (truncate (file-length in) 4) :element-type '(unsigned-byte 32)))) (dotimes (i (length data)) (let ((b0 (read-byte in)) (b1 (read-byte in)) (b2 (read-byte in)) (b3 (read-byte in))) (setf (aref data i) (dpb b0 (byte 8 24) (dpb b1 (byte 8 16) (dpb b2 (byte 8 8) b3)))))) (setf *array-0* data))) (values)) (defun reset-machine () (setf *arrays* (make-hash-table)) (setf *registers* (make-array 8 :element-type '(unsigned-byte 32) :initial-element 0)) (setf *instruction-pointer* 0)) (defun machine-step () ;; FIXME: May want to special-case array 0. (let ((instruction (aref *array-0* *instruction-pointer*))) (incf *instruction-pointer*) (let ((opcode (ldb (byte 4 28) instruction)) (%reg-a (ldb (byte 3 6) instruction)) (%reg-b (ldb (byte 3 3) instruction)) (%reg-c (ldb (byte 3 0) instruction))) (symbol-macrolet ((reg-a (aref *registers* %reg-a)) (reg-b (aref *registers* %reg-b)) (reg-c (aref *registers* %reg-c))) (case opcode (0 ;; conditional move #+(or)(format t "~4,'0X: cmov ~A ~A (#x~X) ~A (#x~X)~%" (1- *instruction-pointer*) %reg-a %reg-b reg-b %reg-c reg-c) (unless (zerop reg-c) (setf reg-a reg-b))) (1 ;; array index (let ((array (find-array reg-b))) (if array (setf reg-a (aref array reg-c)) (progn (format t "aset of inactive array #x~X~%" reg-b) (setf *machine-halted* t))))) (2 ;; array ammendment #+(or)(format t "~4,'0X: aset ~A (#x~X) ~A (#x~X) ~A (#x~X)~%" (1- *instruction-pointer*) %reg-a reg-a %reg-b reg-b %reg-c reg-c) (let ((array (find-array reg-a))) (if array (setf (aref array reg-b) reg-c) (progn (format t "aset of inactive array #x~X~%" reg-a) (setf *machine-halted* t))))) (3 ;; addition #+(or)(format t "~4,'0X: add ~A ~A (#x~X) ~A #x~X)~%" (1- *instruction-pointer*) %reg-a %reg-b reg-b %reg-c reg-c) (setf reg-a (logand #xffffffff (+ reg-b reg-c)))) (4 ;; multiplication (setf reg-a (logand #xffffffff (* reg-b reg-c)))) (5 ;; division (if (zerop reg-c) (progn (format t "division by zero~%") (setf *machine-halted* t)) (setf reg-a (logand #xffffffff (truncate reg-b reg-c))))) (6 ;; not-and #+(or)(format t "~4,'0X: nand ~A ~A (#x~X) ~A (#x~X)~%" (1- *instruction-pointer*) %reg-a %reg-b reg-b %reg-c reg-c) (setf reg-a (logand #xffffffff (lognand reg-b reg-c)))) (7 ;; halt #+(or)(format t "~4,'0X: halt~%" (1- *instruction-pointer*)) (setf *machine-halted* t)) (8 ;; allocation #+(or)(format t "~4,'0X: aloc ~A ~A (#x~X)~%" (1- *instruction-pointer*) %reg-b %reg-c reg-c) (setf reg-b (alloc-array reg-c))) (9 ;; abandonment #+(or)(format t "~4,'0X: aban ~A (#x~X)~%" (1- *instruction-pointer*) %reg-c reg-c) (free-array reg-c)) (10 ;; output #+(or)(format t "~4,'0X: outp ~A~%" (1- *instruction-pointer*) %reg-c) #+(or)(format t "output: #x~X (~S)~%" reg-c (code-char reg-c)) (write-char (code-char reg-c))) (12 ;; load program #+(or)(format t "~4,'0X: load ~A (#x~X) ~A (#x~X)~%" (1- *instruction-pointer*) %reg-b reg-b %reg-c reg-c) #+(or) (unless (zerop reg-b) (format t "loading array not handled") (setf *machine-halted* t)) (unless (zerop reg-b) (setf *array-0* (copy-seq (find-array reg-b)))) (setf *instruction-pointer* reg-c)) (13 ;; orthography #+(or)(format t "~4,'0X: li ~A #x~X~%" (1- *instruction-pointer*) (ldb (byte 3 25) instruction) (ldb (byte 25 0) instruction)) (setf (aref *registers* (ldb (byte 3 25) instruction)) (ldb (byte 25 0) instruction))) (otherwise (format t "~4,'0X: unknown opcode ~A~%" (1- *instruction-pointer*) opcode) (decf *instruction-pointer*) (setf *machine-halted* t)))))) (values)) (defun machine-run () (let ((*machine-halted* nil)) (loop while (not *machine-halted*) do (machine-step)))) (defun reload-machine (filename) (reset-machine) (load-file filename)) #| (reload-machine "sandmark.umz") (time (machine-run)) |# ;;; EOF