;;; -*- Mode: Lisp -*- ;;; ;;; $Header: /home/gene/library/website/docsrc/icfp2006/RCS/icfp2006.lisp,v 395.1 2008/04/20 17:25:51 gene Exp $ ;;; (setq *random-state* (make-random-state t)) (defpackage "COM.CYBERTIGGYR.GENE.ICFP2006" (:use "COMMON-LISP") (:import-from "CYBERTIGGYR-TEST" "DEFTEST" "RUN")) (in-package "COM.CYBERTIGGYR.GENE.ICFP2006") (defvar *um-input* *terminal-io* "The universal machine reads from this.") (defvar *um-output* *standard-output* "The universal machine writes to this.") (defvar *um-log* nil "The execution, debug, trace log goes here.") (defconstant PLATTER-RANGE (expt 2 32)) (defconstant PLATTER-MAXIMUM-VALUE (1- PLATTER-RANGE)) (deftype platter () "This is what we'd call a WORD in the sandstone CPU." '(unsigned-byte 32)) (defun read-platter (strm) "Consume & return the next PLATTER value from the binary input stream. If the stream has only part of a platter value, you get an error. If the stream is already at its end, doesn't have even a part of a platter, you get STRM." (let ((b0 (read-byte strm nil strm))) (if (eq b0 strm) strm ; end of input ;; else (+ (ash b0 24) (ash (read-byte strm) 16) (ash (read-byte strm) 8) (read-byte strm))))) (defun platter-op (platter) "Return the OPeration part of a platter value. The operation is always the high four bits. We conver them from a number into a symbol." (declare (type platter platter)) (case (ash platter -28) (0 :conditional-move) (1 :array-index) (2 :array-amendment) (3 :addition) (4 :multiplication) (5 :division) (6 :nand) (7 :halt) (8 :allocation) (9 :abandonment) (10 :output) (11 :input) (12 :load-program) (13 :orthography) (otherwise (format t "~&~A: warning: Decoding an instruction of x~X." 'platter-op platter) (format t " Assuming it's :NOP.") :nop))) (defun platter-a (platter) "Return the A register part of a platter value. The A register is a number, 0 through 7." (declare (type platter platter)) (mod (ash platter -6) 8)) (defun platter-b (platter) "Return the B register part of a platter value. The B register is a number, 0 through 7." (declare (type platter platter)) (mod (ash platter -3) 8)) (defun platter-c (platter) "Return the C register part of a platter value. The C register is a number, 0 through 7." (declare (type platter platter)) (mod platter 8)) (defun platter-orthor (platter) "Return the orthography REGISTER from an instruction." (declare (type platter platter)) (mod (ash platter -25) 8)) (defun platter-orthov (platter) "Return the orthography LITERAL from an instruction." (declare (type platter platter)) (mod platter (expt 2 25))) (defun decode (platter) "Decode a platter value into an instruction. Return the instruction as a list. The list always has five elements. They are: OPeration, A register, B register, C register, ORTHO register, & ORTHO value." (declare (type platter platter)) (list (platter-op platter) (platter-a platter) (platter-b platter) (platter-c platter) (platter-orthor platter) (platter-orthov platter))) (defun load-scroll (pathname) "Load an entire scroll into memory. Return its contents in a vector of platter values. If there is any problem at all, you get an error." (with-open-file (strm pathname :element-type '(unsigned-byte 8)) (if (zerop (mod (file-length strm) 4)) (let* ((count (/ (file-length strm) 4)) (a (make-array count :element-type 'platter :fill-pointer nil :adjustable nil))) (dotimes (i count) (setf (aref a i) (read-platter strm))) a) ;; else (error "~A: The file's length in octets should be divisible by 4." 'load-scroll)))) (defvar *is-halted* nil) (defvar *clock* 0) (defvar *reg* (make-array 8 :element-type 'platter :initial-element 0 :adjustable nil :fill-pointer nil)) (proclaim '(type (simple-array platter (8)) *reg*)) (defvar *finger* 0) (proclaim '(type platter *finger*)) (defvar *tape* (make-array 1 :adjustable nil :fill-pointer nil :initial-element nil)) (defvar *unused-tapes* ()) (defun setup-um (scrollname) (setq *is-halted* nil *clock* 0 *finger* 0) (dotimes (i (length *tape*)) (setf (aref *tape* i) nil)) (setf (aref *tape* 0) (load-scroll scrollname)) (dotimes (i (length *reg*)) (setf (aref *reg* i) 0))) (defun um-tapen (n) "Return tape number N. If there is no such tape, you get NIL." (declare (type platter n)) (assert (< n (length *tape*))) (aref *tape* n)) (defun um-platter (tapen offset) "Return the platter value at OFFSET of tape N. If the tape does not exist, you get an error." (aref (um-tapen tapen) offset)) (defun set-um-platter (tapen offset value) (declare (type platter value)) (setf (aref (um-tapen tapen) offset) value)) (defun um-reg (n) "Return the value in a register. 0 <= REG < 8." (aref *reg* n)) (defun set-um-reg (n value) (declare (type platter n value)) (setf (aref *reg* n) value)) (defun fetch () "Return the platter value that the finger indicates. Increment *FINGER* & *CLOCK*." (let ((x (um-platter 0 *finger*))) (declare (type platter x)) (incf *finger*) (incf *clock*) x)) (defun um-conditional-move (a b c) (unless (zerop (um-reg c)) (set-um-reg a (um-reg b)))) (defun um-array-index (a b c) (set-um-reg a (um-platter (um-reg b) (um-reg c)))) (defun um-array-amendment (a b c) (set-um-platter (um-reg a) (um-reg b) (um-reg c))) (defun um-addition (a b c) (set-um-reg a (mod (+ (um-reg b) (um-reg c)) PLATTER-RANGE))) (defun um-multiplication (a b c) (set-um-reg a (mod (* (um-reg b) (um-reg c)) PLATTER-RANGE))) (defun um-division (a b c) (set-um-reg a (floor (/ (um-reg b) (um-reg c))))) (defun um-nand (a b c) (let* ((valc (um-reg c)) (valb (um-reg b)) (vala (mod (boole boole-nand valb valc) PLATTER-RANGE))) (set-um-reg a vala))) (defun um-halt () (setq *is-halted* t)) (defun unused-tape-number () "Return a tape number which is not currently used." (when (endp *unused-tapes*) (let ((n (length *tape*))) (setq *tape* (adjust-array *tape* (* 2 n))) (loop for i from n while (< i (length *tape*)) do (setf (aref *tape* i) nil) (push i *unused-tapes*)))) (pop *unused-tapes*)) (defun um-allocation (b c) (let ((n (unused-tape-number))) (declare (type fixnum n)) (setf (aref *tape* n) (make-array (um-reg c) :element-type 'platter :adjustable nil :fill-pointer nil :initial-element 0)) (set-um-reg b n))) (defun um-abandonment (c) (assert (not (zerop (um-reg c)))) ; Can't free tape 0. (assert (um-tapen (um-reg c))) ; Mustn't free an unused tape. (setf (aref *tape* (um-reg c)) nil) (push (um-reg c) *unused-tapes*)) (defun um-output (c) (let ((ascii (um-reg c))) (declare (type platter ascii)) (assert (<= 0 ascii 255)) (if (<= 0 ascii 127) (write-char (code-char ascii) *um-output*) ;; Else, it's not realy an ASCII character, so we ;; write something else. (format *um-output* "#x~2,'0X" ascii)) (force-output *um-output*))) (defun um-input (c) (format *um-log* " INPUT") (force-output *um-log*) (let ((char (read-char *um-input*))) (format *um-log* " ~S" char) (force-output *um-log*) (set-um-reg c (char-code char)))) (defun um-load-program (b c) (setf *finger* (um-reg c)) (unless (zerop (um-reg b)) (setf (aref *tape* 0) (copy-seq (aref *tape* (um-reg b)))))) (defun um-orthography (orthor literal) (set-um-reg orthor literal)) (defun execute (op a b c orthor literal) (ecase op (:conditional-move (um-conditional-move a b c)) (:array-index (um-array-index a b c)) (:array-amendment (um-array-amendment a b c)) (:addition (um-addition a b c)) (:multiplication (um-multiplication a b c)) (:division (um-division a b c)) (:nand (um-nand a b c)) (:halt (um-halt)) (:allocation (um-allocation b c)) (:abandonment (um-abandonment c)) (:output (um-output c)) (:input (um-input c)) (:load-program (um-load-program b c)) (:orthography (um-orthography orthor literal)))) (defun spin () (format *um-log* "~%~%~%*** ~A begins ***" 'spin) (loop until *is-halted* do (apply #'execute (decode (fetch)))) (format *um-log* "~&*** ~A ends ***" 'spin) (force-output *um-log*)) ;;; --- end of file ---