;;; -*- Mode:LISP; Package:HACKS; Base:10; Lowercase:T; Readtable:ZL -*-
;;; Created 11/24/81 09:57:32 by CMB
;;; Modified, moved to POINTER, and installed by DLW, 1/9/82

;;; This rotate function was translated from Smalltalk.  It appeared in the August 1981
;;; issue of Byte magazine.  The array must be square and a power of two bits on a side.
;;; The direction of rotation will be clockwise.  To rotate a 512x512 bit array takes
;;; about 5 seconds of solid bitblt time.  Rotate takes 2 + 15*log(N) bitblts.

(defconst .STOR tv:alu-seta)
(defconst .IOR tv:alu-ior)
(defconst .AND tv:alu-and)
(defconst .XOR tv:alu-xor)
(defconst .CLEAR 0)
(defconst .SET 15)
(defconst .NAND 2)

(defmacro copy-all-to (from xoffset yoffset to alu)
  `(bitblt ,alu (- width ,xoffset) (- width ,yoffset) ,from 0 0 ,to ,xoffset ,yoffset))

(defmacro copy-all-from (to xoffset yoffset from alu)
  `(bitblt ,alu (- width ,xoffset) (- width ,yoffset) ,from ,xoffset ,yoffset ,to 0 0))

(defun rotate (myself w)
  (let* ((width (array-dimension myself 0))
	 (mask (make-array (list width width) :element-type 'bit))
	 (temp (make-array (list width width) :element-type 'bit)))
    (copy-all-to mask 0 0 mask .CLEAR)
    (copy-all-from mask (truncate width 2) (truncate width 2) mask .SET)
    (do ((quad (truncate width 2) (truncate quad 2)))
	((< quad 1))
      (copy-all-to mask 0 0 temp .STOR)		; 1
      (copy-all-to mask 0 quad temp .IOR)		; 2
      (copy-all-to myself 0 0 temp .AND)		; 3
      (copy-all-to temp 0 0 myself .XOR)		; 4
      (copy-all-from temp quad 0 myself .XOR)	; 5
      (copy-all-from myself quad 0 myself .IOR)	; 6
      (copy-all-to temp quad 0 myself .XOR)	; 7
      (copy-all-to myself 0 0 temp .STOR)	; 8
      (copy-all-from temp quad quad myself .XOR)	; 9
      (copy-all-to mask 0 0 temp .AND)		; 10
      (copy-all-to temp 0 0 myself .XOR)		; 11
      (copy-all-to temp quad quad myself .XOR)	; 12
      (copy-all-from mask (truncate quad 2) (truncate quad 2) mask .AND)	; 13
      (copy-all-to mask quad 0 mask .IOR)		; 14
      (copy-all-to mask 0 quad mask .IOR)		; 15
      (send w :bitblt tv:alu-seta width width myself 0 0 0 0)))
  myself)

(defvar *rotate-source* nil)
(defvar *rotate-size* 512)

(defun run-rotate ()
  (hof-window)
  (tv:window-call (*hof-window* :deactivate)
    (with-real-time
      (send *hof-window* :set-label "Life Window")
      (if (null *rotate-source*)
	  (setq *rotate-source* (make-array (list *rotate-size* *rotate-size*)
					    :element-type 'bit)))
      (send *hof-window* :clear-window)
      (bitblt tv:alu-xor *rotate-size* *rotate-size* *rotate-source* 0 0 *rotate-source* 0 0) 
      ;; random text
      (princ (documentation 'format 'function) *hof-window*)
      (send *hof-window* :bitblt-from-sheet
			 tv:alu-seta *rotate-size* *rotate-size* 0 0 *rotate-source* 0 0)
      (rotate *rotate-source* *hof-window*)
      (send *hof-window* :tyi))))

(defdemo "Rotate"
	 "A demonstration of an interesting algorithm for rotating a bit array."
  (run-rotate))

;;; This life function was translated from Smalltalk.  It appeared in the
;;; August 1981 issue of Byte magazine.  The array may be any size at all,
;;; as long as it fits on the screen.  Each generation of life takes 65
;;; bitblts.  If the loop was unrolled, some of the initial bitblts could
;;; be deleted since it is unnecessary to calculate the carrys and high order
;;; sums.

(defun life (window)
  (multiple-value-bind (w h)
      (send window :inside-size)
    (let* ((h2 (+ h 2))
	   (w2 (+ w 2))
	   (w32 (* (ceiling w2 32) 32))
	   (myself (make-pixel-array w32 h :element-type 'bit))
	   (nbr1   (make-pixel-array w32 h2 :element-type 'bit))
	   (nbr2   (make-pixel-array w32 h2 :element-type 'bit))
	   (nbr4   (make-pixel-array w32 h2 :element-type 'bit))
	   (carry2 (make-pixel-array w32 h2 :element-type 'bit))
	   (carry4 (make-pixel-array w32 h2 :element-type 'bit)))
      (send window :bitblt-from-sheet tv:alu-seta w h 0 0 myself 0 0)
      (dotimes (generation 100000)
	(bitblt .XOR w2 h2 nbr1   0 0 nbr1   0 0)
	(bitblt .XOR w2 h2 nbr2   0 0 nbr2   0 0)
	(bitblt .XOR w2 h2 nbr4   0 0 nbr4   0 0)
	(bitblt .XOR w2 h2 carry2 0 0 carry2 0 0)
	(bitblt .XOR w2 h2 carry4 0 0 carry4 0 0)
	(dolist (l '((0 0) (0 1) (0 2) (1 0) (1 2) (2 0) (2 1) (2 2)))
	  (bitblt .STOR w2 h2 nbr1   0 0 carry2 0       0       )
	  
	  ;; carry2 = nbr1 .AND carry2
	  (bitblt .AND  w  h  myself   0 0 carry2 (car l) (cadr l))
	  
	  ;; nbr1   = nbr1 .XOR myself
	  (bitblt .XOR  w  h  myself   0 0 nbr1   (car l) (cadr l))
	  (bitblt .STOR w2 h2 nbr2   0 0 carry4 0       0       )
	  
	  ;; carry4 = nbr2 .AND carry4
	  (bitblt .AND  w2 h2 carry2 0 0 carry4 0       0       )
	  
	  ;; nbr2   = nbr2 .XOR carry2
	  (bitblt .XOR  w2 h2 carry2 0 0 nbr2   0       0       )
	  
	  ;; nbr4   = nbr4 .XOR carry4
	  (bitblt .XOR  w2 h2 carry4 0 0 nbr4   0       0       ))
	
	;; myself = myself .AND nbr2
	(bitblt .AND    w  h  nbr2 1 1 myself 0 0)
	
	;; nbr1 = nbr1 .AND nbr2
	(bitblt .AND    w2 h2 nbr2 0 0 nbr1 0 0)
	
	;; myself = (myself .AND nbr2) .IOR (nbr1 .AND nbr2)
	(bitblt .IOR     w  h  nbr1 1 1 myself 0 0)
	
	;; myself = (NOT nbr4) .AND ((myself .AND nbr2) .IOR (nbr1 .AND nbr2))
	(bitblt .NAND w  h  nbr4 1 1 myself 0 0)
	(send window :bitblt tv:alu-seta w h myself 0 0 0 0)
	(send window :home-cursor)
	(format window "~D" generation)
	(if (send window :tyi-no-hang) (return-from life t))
	)))
  window)

(defun run-life ()
  (little-hof-window)
  (tv:window-call (*little-hof-window* :deactivate)
    (with-real-time
      (send *little-hof-window* :set-label "Life Window")
      (multiple-value-bind (width height) (send *little-hof-window* :inside-size)
	(send *little-hof-window* :clear-window)
	(send *little-hof-window*
	      :draw-line 100 (truncate height 2)
			 (- width 100) (truncate height 2)))
      (life *little-hof-window*))))

(defdemo "Life"
	 "Conway's game of /"Life/", a cellular automaton demonstration.  By CMB."
  (run-life))
