;;;-*- Mode:LISP; Package:LISP-INTERNALS; Base:10; Readtable:CL -*-
;;;
;;; Written by Youcef Bennour.
;;;
;;;  Files contains the equivalents of contents of file UC-TV.
;;;  Select-sheet
;;;  Draw-char
;;;  Draw-rectangle
;;;  Draw-line
;;;  Bitblt
;;;
;;; Buffer stuff
;;;
(defparameter *A-TV-CURRENT-SHEET* nil)
(defparameter *A-TV-SCREEN-BUFFER-ADDRESS* nil)
(defparameter *A-TV-SCREEN-BUFFER-END-ADDRESS* nil)
(defparameter *A-TV-SCREEN-LOCATIONS-PER-LINE* nil)
(defparameter *A-TV-SCREEN-BUFFER-BIT-OFFSET* nil)
(defparameter *A-TV-SCREEN-WIDTH* nil)
(defparameter *A-TV-SCREEN-BUFFER-PIXEL-MROT* nil)	
;;;
;;;  FONT STUFF
;;;
(defparameter *A-FONT-POINTER* nil)
(defparameter *A-FONT-ORIGIN* nil)
(defparameter *A-Font-raster-width* nil)
(defparameter *A-font-raster-height* nil)
(defparameter *a-font-raster-shift* nil)
(defparameter *a-font-rows-per-word* nil)
(defparameter *A-font-words-per-char* nil)

;;;
;;; Validate Sheet Cache
;;;
(defun Validate-Sheet-Cache (sheet)
  ;; Sequence Break is inhibited. Function checks to see if the sheet is the
  ;; same as *A-TV-CURRENT-SHEET*. If it is not, updates the sheet global variables.
  )

;;;
;;; Select-sheet
;;;
(defun select-sheet (sheet)
  (multiple-value-bind (bar array-length foo array-data)
      ;; foo and bar are dummy variables.
      (array:decode-array *screen-address*)
    (values 32.					; number of words per line
	    0					; word offset 
	    array-data				; beginning of screen memory
	    (hw:24+ array-length array-data)	; ending address
	    0)					; weird pixel depth (log(pixel-size))
    )
  )

;;;
;;; Validate Font Cache
;;;

(defun Validate-Font-Cache (font-pointer)
  ;; Sequence Break is inhibited. If *A-FONT-POINTER* is different from font-pointer
  ;; Global font are then updated
  )

;;;
;;;    DISPATCH TABLE FOR DPB INTO 32 BIT WORDS USING AN ALU OPERATION.
;;;


(defafun dpb-alu-dispatch-table (value word)
  ;;; byte spec is already loaded into the status reg and ready for operation.
  (alu field-pass return gr:*all-zero* a1 pw-rr ch-return next-pc-return unboxed)	; boole-clr=0
boole-and
  (alu field-AND return a0 a1 pw-rr ch-return next-pc-return unboxed)			; boole-and=1
  (unconditional-branch boole-and (alu field-not a0 a0 a0 pw-rr unboxed))		; boole-andc1=2
  (alu field-pass return a1 a1 pw-rr ch-return next-pc-return unboxed)			; boole-2=3
  (unconditional-branch boole-and (alu field-not a1 a1 a1 pw-rr unboxed))		; boole-andc2=4
  (alu field-pass return a0 a1 pw-rr ch-return next-pc-return unboxed)			; boole-1=5
  (alu field-XOR return a0 a1 pw-rr ch-return next-pc-return unboxed)			; boole-xor=6
Boole-OR  
  (alu field-OR return a0 a1 pw-rr ch-return next-pc-return unboxed)			; boole-ior=7
  (unconditional-branch boole-not (alu field-OR a2 a0 a1 pw-rr unboxed))		; boole-nor=8
  (unconditional-branch boole-not (alu field-XOR a2 a0 a1 pw-rr unboxed))		; boole-EQV=9
  (alu field-not return a0 a0 pw-rr ch-return next-pc-return unboxed)			; boole-C1=10
  (unconditional-branch boole-or (alu field-not a0 a0 a0 pw-rr unboxed))		; boole-ORC1=11
  (alu field-not return a1 a1 pw-rr ch-return next-pc-return unboxed)			; boole-C2=12
  (unconditional-branch boole-or (alu field-not a1 a1 a1 pw-rr unboxed))		; boole-ORC2=13
  (unconditional-branch boole-not (alu field-AND a2 a0 a1 pw-rr unboxed))		; boole-nor=14
  (alu field-pass return gr:*all-ones* a1 pw-rr ch-return next-pc-return unboxed)	; boole-SET=15
boole-not
  (alu field-not return a2 a2 pw-rr ch-return next-pc-return unboxed)			; boole-not?
  )

(defafun dpb-unboxed-with-aluf (value byte-spec word aluf)
  (movea a4 (dpb-alu-dispatch-table 2) boxed)
  (move O0 a0 CH-TAIL-OPEN)
  (alu L+R nop a3 A4 boxed)
  (alu load-status-r nop a1 a1 bw-16 unboxed)
  (move o1 a2 ch-tail-call next-pc-dispatch)
  )

;;;************************************************************************************
;;;*                                                                                  *
;;;*               Char Drawing Routines.                                             *
;;;*                                                                                  *
;;;************************************************************************************

(defun draw-bit-patterns (tv-screen-buffer-address offset tv-bit-offset field-width bit-position word alu-function)
;  (error "In draw-bit-patterns")
  (Array:%VM-WRITE32
    tv-screen-buffer-address
    offset
    (dpb-unboxed-with-aluf
      (hw:ldb word (byte field-width bit-position) (hw:unboxed-constant 0))
      (byte field-width tv-bit-offset)
      (Array:%VM-READ32 tv-screen-buffer-address offset)
      alu-function))
  )


(defun draw-row-crossing-word-boundary (row-data
					raster-char-width
					tv-screen-buffer-address
					offset
					tv-bit-offset
					alu-function &aux (width-1 (- 32. tv-bit-offset)))
  "font-word fits in window but does not fit in a single word starting at tv-bit-offset. We need
two words of buffer to do it."
  (setq raster-char-width (- raster-char-width width-1))
  ;; first portion of current row
  (draw-bit-patterns tv-screen-buffer-address offset tv-bit-offset width-1 raster-char-width row-data alu-function)
  ;; second portion of current row
  (draw-bit-patterns tv-screen-buffer-address (1+ offset) 0 raster-char-width 0 row-data alu-function)
  )

(defun draw-char-no-check (raster-char-width
			   raster-char-height
			   tv-screen-buffer-address
			   tv-bit-offset
			   tv-screen-locations-per-line
			   font-word-address
			   alu-function)
    "Fast char drawing. At this point character has been found to fit in window to draw in,
row definition of character bit map have been found to fit in the word at tv-screen-buffer-address
starting at tv-bit-offset-position."
  (do ((row 0 (1+ row))
       (offset 0 (+ offset tv-screen-locations-per-line))
       (font-word-offset 0)
       (position 32. (+ position raster-char-width))
       ;; position is initialized to 32. to make it read the first word from the font array
       ;; in which case position is set to 0.
       font-word)
      ((= row raster-char-height))
    (when (> position 31.)
      (setq font-word (array:%VM-READ32 font-word-address font-word-offset))
      (setq position 0)
      (setq font-word-offset (1+ font-word-offset)))
    (draw-bit-patterns tv-screen-buffer-address offset tv-bit-offset raster-char-width position font-word alu-function)
    )
  )

(defun draw-char-in-two-words (raster-char-width
			       raster-char-height
			       tv-screen-buffer-address
			       tv-bit-offset
			       tv-screen-locations-per-line
			       font-word-address
			       alu-function)
  "Character fits in window but does not fit in a single word starting at tv-bit-offset. We need
two words of buffer to do it."
  (do ((row 0 (1+ row))
       (offset 0 (+ offset tv-screen-locations-per-line))
       (font-word-offset 0)
       (position 32. (+ position raster-char-width))
       font-word)
      ((= row raster-char-height))
    (and (> position 31.)
	 (setq font-word (array:%VM-READ32 font-word-address font-word-offset)
	       position 0
	       font-word-offset (1+ font-word-offset)))
    (draw-row-crossing-word-boundary
      font-word
      raster-char-width
      tv-screen-buffer-address
      offset
      tv-bit-offset
      alu-function)
    )
  )

    
(defun draw-char-clipping (raster-char-width
			   remaining-height-to-draw
			   tv-screen-buffer-address
			   tv-bit-offset
			   tv-screen-locations-per-line
			   font-word-address
			   byte-spec			   
			   alu-function
			   &aux (size-to-draw (byte-size byte-spec)))
  "Character drawing with clipping relative to a given window. The clipping parameters must have been
already computed. remaining-height-to-draw is the number of rows that has to be drawn. font-word-address
is the first font word to draw (this take care of top and bottom clipping). Byte-spec specifies which bits
in a row word is getting drawn."
  (do ((row 0 (1+ row))
       (offset 0 (+ offset tv-screen-locations-per-line))
       (font-word-offset 0)
       (crosses-word-boundaryp (> (+ tv-bit-offset size-to-draw) 31.))
       (position 32. (+ position raster-char-width))
       font-word)
      ((= row remaining-height-to-draw))
    (and (> position 31.)
	 (setq font-word (array:%VM-READ32 font-word-address font-word-offset)
	       position 0
	       font-word-offset (1+ font-word-offset)))
    (if crosses-word-boundaryp
	(draw-row-crossing-word-boundary
	  (lisp:ldb byte-spec (lisp:ldb (byte raster-char-width position) font-word))
	  size-to-draw
	  tv-screen-buffer-address
	  offset
	  tv-bit-offset
	  alu-function)
      (draw-bit-patterns
	tv-screen-buffer-address
	offset tv-bit-offset
	size-to-draw
	position
	font-word
	alu-function))
    )
  )

;;;
;;;        Top Level Routines to set char-drawing Parameters.
;;; 

(defun clip-char-relative-to-window (window-x
				     window-y
				     window-width
				     window-height
				     char-x
				     char-y
				     raster-char-width
				     raster-char-height)
  ;; if (or (char-x + raster-char-width < window-x)
  ;;        (char-x > window-width)
  ;;        (char-y + raster-char-height < window-y)
  ;;        (char-y > window-y + window-height)) then
  ;;       Completly-clipped = t
  ;;       return
  ;; else
  ;;   if char-x is negative then
  ;;         bit-position = 0
  ;;         width-to-draw = (+ char-width char-x)
  ;;         Char-x = 0
  ;;
  ;;   if char-x + width-to-draw > window-width then
  ;;         bit-position = (- width-to-draw (setq width-to-draw (- window-width char-x)))
  ;;
  ;;   if char-y is negative then
  ;;         height-to-draw = (+ char-height char-y)
  ;;         offset-row = char-y
  ;;         char-y = 0
  ;;
  ;;   if char-y + height-to-draw > window-height
  ;;        height-to-draw = (- window-height char-y)
  ;;
  (let ((completly-clipped nil)
	(not-clipped nil)
	(offset-row 0)
	(bit-position 0)
	(width-to-draw raster-char-width)
	(height-to-draw raster-char-height))
    (if (or (< (+ char-x raster-char-width) window-x)
	    (> char-x window-width)
	    (< (+ char-y raster-char-height) window-y)
	    (> char-y (+ window-y window-height)))
	(setq completly-clipped T)
      ;; may be no clipped at all.
      (if (or (>= char-x 0)
	      (>= char-y 0)
	      (<= (+ char-x raster-char-width) window-width)
	      (<= (+ char-y raster-char-height) window-height))
	  (setq not-clipped t)
	;; check for Left side clip
	(and (minusp char-x)
	     (setq width-to-draw (+ char-x raster-char-width)
		   char-x 0))
	;; check for right side clip
	(and (> (+ char-x width-to-draw) window-width)
	     (setq bit-position (- width-to-draw (setq width-to-draw (- window-width char-x)))))
	;; check for top side clip
	(and (minusp char-y)
	     (setq offset-row (- char-y)
		   height-to-draw (+ raster-char-height char-y)
		   char-y 0))
	;; check for bottom side clip
	(and (> (+ char-y height-to-draw) window-height)
	     (setq height-to-draw (- window-height char-y)))))
    (values char-x char-y not-clipped completly-clipped offset-row bit-position width-to-draw height-to-draw)
    )
  )

;;;
;;;  Draw Rectangle routine
;;;

(defconstant *q-pointer-width* 31.)

(defun tv-xy-address (x-bitpos y-bitpos
		      tv-screen-locations-per-line
		      tv-screen-buffer-bit-offset
		      tv-screen-buffer-address
		      tv-screen-buffer-pixel-size-mrot)
;  (error "in tv-xy-address")
  (let ((line-address-offset (new-math:multiply-fixnum y-bitpos tv-screen-locations-per-line))
	(bit-offset-in-line (hw:24+ tv-screen-buffer-bit-offset
				    (hw:dpb-boxed x-bitpos (byte (- 24. tv-screen-buffer-pixel-size-mrot)
								 tv-screen-buffer-pixel-size-mrot) 0))))
       (values (hw:24+ (hw:24+ line-address-offset
			  (hw:ldb-boxed bit-offset-in-line (byte (- 24. 5) 5) 0))
		  tv-screen-buffer-address)
	       (hw:ldb-boxed bit-offset-in-line (byte 5 0) 0)))
  )

(defun draw-column (width height word-adr bit-offset
		    tv-screen-buffer-address
		    tv-screen-buffer-end-address
		    tv-screen-locations-per-line
		    alu-function)
;  (error "In draw-column")
  (do* ((row 0 (hw:24+ 1 row))
	(offset 0 (hw:24+ offset tv-screen-locations-per-line))
	(word-address word-adr (hw:24+ offset word-adr)))
       ((or (hw:32< word-address tv-screen-buffer-address)
	    (hw:32> word-address tv-screen-buffer-end-address)
	    (= row height)))
    ;;
    ;; this will read a word from the screen buffer, no matter what the alu function is.
    ;; should improved it to discard the read if whole word is to be mugged up.
    ;; but will do for now.
    ;;
    (draw-bit-patterns word-address 0 bit-offset width bit-offset gr:*all-ones* alu-function)
    )
  )


(defun draw-rectangle-internal (width				; width in bits
				height				; height
				word-adr			; screen address where to start drawing
				bit-offset			; bit offset in word
				tv-screen-buffer-address	; low address bound of screen 
				tv-screen-buffer-end-address	; high address bound of screen
				tv-screen-locations-per-line	; number of word per scan line
				alu-function)			; alu function to use
;  (error "In draw-rectangle-internal")
  (do* ((column 0 (1+ column))
	(remaining-width width (- remaining-width pattern-width))
	(pattern-width (min (hw:24- (hw:24+ 1 *q-pointer-width*) bit-offset) width)
		       (min (hw:24+ 1 *q-pointer-width*) remaining-width))
	(bit-off bit-offset 0)
	;; width in word must be (div width 32) + 1 if the remainder is not zero.
	(width-in-word (hw:24+ (min (hw:ldb-boxed width (byte 5 0) 0) 1)
			       (hw:ldb-boxed width (byte (- 24. 5.) 5.) 0))))
       ((or (>= column width-in-word)
	    (hw:32> word-adr tv-screen-buffer-end-address)))
    (draw-column pattern-width height word-adr bit-off
		 tv-screen-buffer-address
		 tv-screen-buffer-end-address
		 tv-screen-locations-per-line
		 alu-function)
    (setq word-adr (hw:24+ 1 word-adr))
    )
  )


(defun draw-rectangle (width height x-bitpos y-bitpos alu-function sheet)
  (li:%trap-if-not-both-fixnum width height)
  (li:%trap-if-not-both-fixnum x-bitpos y-bitpos)
  (li:%trap-if-not-both-fixnum alu-function alu-function)
  (let (word-adr
	bit-offset 
	tv-screen-locations-per-line
	tv-screen-buffer-bit-offset
	tv-screen-buffer-address
	tv-screen-buffer-end-address
	tv-screen-buffer-pixel-size-mrot
	)
    ;; should turn off sequence breaks in select sheet.
    (setq gr:*allow-sequence-break* (1+ gr:*allow-sequence-break*))
    (multiple-value-setq (tv-screen-locations-per-line
			  tv-screen-buffer-bit-offset
			  tv-screen-buffer-address
			  tv-screen-buffer-end-address
			  tv-screen-buffer-pixel-size-mrot)
      (select-sheet sheet))
    (setq gr:*allow-sequence-break* (1- gr:*allow-sequence-break*))
    (multiple-value-setq (word-adr bit-offset)
      (tv-xy-address x-bitpos y-bitpos
		     tv-screen-locations-per-line
		     tv-screen-buffer-bit-offset
		     tv-screen-buffer-address
		     tv-screen-buffer-pixel-size-mrot))
    ;; compute width in bits. Bug tv-screen-buffer-pixel-size-mrot is the log of pixel size
    (setq width (new-math:multiply-fixnum width (ash 1 tv-screen-buffer-pixel-size-mrot)))
    (if (or (zerop width)
	    (zerop height))
	nil
      (if (> (+ bit-offset width) (hw:24+ 1 *q-pointer-width*))
	  (draw-rectangle-internal
	    width
	    height
	    word-adr
	    bit-offset
	    tv-screen-buffer-address
	    tv-screen-buffer-end-address
	    tv-screen-locations-per-line
	    alu-function)
	(draw-column width
		     height
		     word-adr
		     bit-offset
		     tv-screen-buffer-address
		     tv-screen-buffer-end-address
		     tv-screen-locations-per-line
		     alu-function) 
	))
    )
  )


;;;;
;;;;  DRAW LINE
;;;;


(defun increment-x (adr bit-offset pixel-size)
  (setq bit-offset (+ bit-offset pixel-size))
  (when (> bit-offset *q-pointer-width*)
    (setq adr (hw:24+ 1 adr))
    (setq bit-offset 0))
  (values adr bit-offset)
  )

(defun increment-cell-location (adr bit-offset pixel-size
				direction long-side long-side/2
				delta-x delta-y tv-screen-locations-per-line)
  (setq long-side/2 (- long-side/2 delta-y))
  (cond ((minusp long-side/2)
	 (setq adr (hw:24+ tv-screen-locations-per-line adr)
	       long-side/2 (+ long-side/2 delta-x))
	 (multiple-value-setq (adr bit-offset) (increment-x adr bit-offset pixel-size)))
	((minusp direction)
	 (setq adr (hw:24+ tv-screen-locations-per-line adr)))
	(t (multiple-value-setq (adr bit-offset) (increment-x adr bit-offset pixel-size))))
  (values adr bit-offset long-side/2)
  )
      
			  

(defun draw-line-internal (word-adr bit-offset
			   delta-x delta-y
			   draw-first-point draw-end-point
			   tv-screen-locations-per-line
			   tv-screen-buffer-address
			   tv-screen-buffer-end-address
			   tv-screen-buffer-pixel-size
			   alu-function)
  (let ((long-side delta-x)
	direction long-side/2)
    (setq direction (- delta-x delta-y))
    (when (minusp direction)
      (setq delta-x delta-y)
      (setq delta-y long-side)
      (setq long-side delta-x))
    (setq long-side/2 (ash long-side -1))
    (when (not draw-first-point)
      (setq long-side (1- long-side))
      (multiple-value (word-adr bit-offset long-side/2)
	(increment-cell-location
	  word-adr bit-offset
	  tv-screen-buffer-pixel-size
	  direction long-side long-side/2
	  delta-x delta-y tv-screen-locations-per-line)))
    
    (do ((count long-side (1- count))
	 word)
	((or (zerop count)
	     (and (= count 1)
		  (not draw-end-point))))
      (when (and (hw:32>= word-adr tv-screen-buffer-address)
		 (hw:32<= word-adr tv-screen-buffer-end-address))
	(draw-bit-patterns word-adr 0 bit-offset tv-screen-buffer-pixel-size bit-offset gr:*all-ones* alu-function))
      (multiple-value-setq (word-adr bit-offset long-side/2)
	(increment-cell-location
	  word-adr bit-offset
	  tv-screen-buffer-pixel-size
	  direction long-side long-side/2
	  delta-x delta-y tv-screen-locations-per-line))
      )
    )
  )

(defun draw-line (x0 y0 x1 y1 alu-function draw-end-point sheet)
  (li:%trap-if-not-both-fixnum x0 y0)
  (li:%trap-if-not-both-fixnum x1 y1)
  (li:%trap-if-not-both-fixnum alu-function alu-function)
  (let ((delta-x (hw:24- x1 x0))
	(delta-y (hw:24- y1 y0))
	(draw-first-point t)
	tv-screen-locations-per-line
	tv-screen-bit-offset
	tv-screen-buffer-address
	tv-screen-buffer-end-address
	tv-screen-buffer-pixel-size-mrot 
	)
    (when (minusp delta-x)
      (setq delta-x (hw:24- 0 delta-x))
      (setq delta-y (hw:24- 0 delta-y))
      (setq draw-first-point draw-end-point)
      (setq draw-end-point t)
      (setq tv-screen-locations-per-line x0)	   ; Used as a temp location
      (setq x0 x1)
      (setq x1 tv-screen-locations-per-line)	   ; Used as a temp location
      (setq tv-screen-locations-per-line y0)	   ; Used as a temp location
      (setq y0 y1)
      (setq y1 tv-screen-locations-per-line))	   ; Used as a temp location
    (setq gr:*allow-sequence-break* (1+ gr:*allow-sequence-break*))
    (multiple-value-setq (tv-screen-locations-per-line
			  tv-screen-bit-offset
			  tv-screen-buffer-address
			  tv-screen-buffer-end-address
			  tv-screen-buffer-pixel-size-mrot)
      (select-sheet sheet))
    (setq gr:*allow-sequence-break* (1- gr:*allow-sequence-break*))
    (multiple-value-setq (x0 y0)
      (tv-xy-address
	x0 y0
	tv-screen-locations-per-line
	tv-screen-bit-offset
	tv-screen-buffer-address
	tv-screen-buffer-pixel-size-mrot))
    (when (minusp delta-y)
      (setq tv-screen-locations-per-line (- tv-screen-locations-per-line))
      (setq delta-y (- delta-y)))
    (draw-line-internal
      x0 y0 delta-x delta-y draw-first-point
      draw-end-point tv-screen-locations-per-line
      tv-screen-buffer-address
      tv-screen-buffer-end-address
      (ash 1 tv-screen-buffer-pixel-size-mrot)
      alu-function)
    )
  )

;;;
;;;  BITBLT STUFF
;;;

(defun bitblt-column (dest-adr
		      src-adr
		      column-height
		      src-column-height
		      dest-adr-inc
		      src-adr-inc
		      dest-byte-spec
		      src-byte-spec
		      src-y-offset
		      src-offset
		      alu-function)
  (do ((i 0)
       (number-of-rows-until-wrap-around (hw:24- src-column-height src-offset) src-column-height)
       (src-address (hw:24+ src-y-offset src-adr) src-adr))
      ((>= i column-height))
    (dotimes (j number-of-rows-until-wrap-around)
      (Array:%VM-WRITE32
	dest-adr
	0
	(dpb-unboxed-with-aluf
	  (hw:ldb (Array:%VM-READ32 src-address 0) src-byte-spec 0)
	  dest-byte-spec
	  (Array:%VM-READ32 dest-adr 0)
	  alu-function))
      (setq src-address (hw:24+ src-adr-inc src-address))
      (setq dest-adr (hw:24+ dest-adr-inc dest-adr))
      (setq i (1+ i)))
    )
  )


(defun bitblt-decode-array (array x y)
  (let (dim-x dim-y offset array-origin array-type width-in-bits word-offset byte-spec)
    (multiple-value-setq (offset array-origin array-type dim-y dim-x)
      (array:decode-2d-array array y x))
    (multiple-value-setq (y x) (new-math:divide-fixnum offset dim-x))
    (prims:dispatch (byte 5 0) array-type
      (array:art-1b
	(setq byte-spec (byte 24. 0))
	(setq word-offset (hw:ldb offset (byte 19. 5) 0))
	(setq width-in-bits (hw:dpb dim-x byte-spec 0))
	(setq x (hw:ldb x (byte 5. 0) 0)))
      (array:art-2b
	(setq byte-spec (byte 23. 1))
	(setq word-offset (hw:ldb offset (byte 20. 4) 0))
	(setq width-in-bits (hw:dpb dim-x byte-spec 0))
	(setq x (hw:ldb x (byte 4. 1) 0)))
      (array:art-4b
	(setq byte-spec (byte 22. 2))
	(setq word-offset (hw:ldb offset (byte 21. 3) 0))
	(setq width-in-bits (hw:dpb dim-x byte-spec 0))
	(setq x (hw:dpb x (byte 3. 2.) 0)))
      (array:art-8b
	(setq byte-spec (byte 21. 3))
	(setq word-offset (hw:ldb offset (byte 22. 2) 0))
	(setq width-in-bits (hw:dpb dim-x byte-spec 0))
	(setq x (hw:dpb x (byte 2. 3.) 0)))
      (array:art-16b
	(setq byte-spec (byte 20. 4))
	(setq word-offset (hw:ldb offset (byte 23. 1) 0))
	(setq width-in-bits (hw:dpb dim-x byte-spec 0))
	(setq x (hw:dpb x (byte 1. 4)  0)))
      (array:art-32b
	(setq byte-spec (byte 19. 5))
	(setq word-offset (hw:ldb offset (byte 24. 0) 0))
	(setq width-in-bits (hw:dpb dim-x byte-spec 0))
	)
      (t (li:error "Not a bit array"))
      )
;    (unless (zerop (hw:ldb width-in-bits (byte 5. 0) 0))
;      (li:error "Width must be a multiple of 32."))
    (values (hw:24+ word-offset array-origin)
	    byte-spec
	    width-in-bits
	    dim-y
	    x
	    y)
    )
  )

(defun compute-width-of-column-to-bitblt (
					  bitblt-x-dest-offset
					  bitblt-x-src-offset
;					  bitblt-src-width
;					  bitblt-dest-width
					  remaining-width
					  LEFT-TO-RIGHT-P
					  )
  ;; width of column to bitblt is computed so that it does not cross word boundary in src or dest array.
  (let (bit-offset-in-src-word
	bit-offset-in-dest-word
	src-byte-spec
	dest-byte-spec
	column-width)
    (setq bit-offset-in-src-word (hw:ldb bitblt-x-src-offset (byte 5. 0) 0))
    (setq bit-offset-in-dest-word (hw:ldb bitblt-x-dest-offset (byte 5. 0) 0))
    (if LEFT-TO-RIGHT-P
	(progn
	  (setq column-width (min (min (hw:24- 32. bit-offset-in-src-word)
				       (hw:24- 32. bit-offset-in-dest-word))
				       ;(min (- bitblt-src-width bitblt-x-src-offset)
				       ;(hw:24- 32. bit-offset-in-src-word))
				       ;(min (- bitblt-dest-width bitblt-x-dest-offset)
				       ;(hw:24- 32. bit-offset-in-dest-word)))
				  remaining-width))
	  (setq src-byte-spec (byte column-width bit-offset-in-src-word))
	  (setq dest-byte-spec (byte column-width bit-offset-in-dest-word))
	  )
      (progn
	(setq column-width (min (min (1+ bit-offset-in-src-word)
				     (1+ bit-offset-in-dest-word))
				     ;(min (1+ bitblt-x-src-offset)
				     ;(1+ bit-offset-in-src-word))
				     ;(min (1+ bitblt-x-dest-offset)
				     ;(1+ bit-offset-in-dest-word)))
				remaining-width))
	(setq src-byte-spec (byte column-width (1+ (- bit-offset-in-src-word column-width))))
	(setq dest-byte-spec (byte column-width (1+ (- bit-offset-in-dest-word column-width)))))
      )
    (values column-width
	    src-byte-spec
	    dest-byte-spec
	    )
    )
  )

(defun bitblt-left-to-right (alu-function
			     width
			     column-height
			     src-adr
			     dest-adr
			     word-inc-src-rows
			     word-inc-dest-rows
			     bitblt-x-src-offset
			     bitblt-x-dest-offset
			     bitblt-src-y-offset
			     bitblt-src-y
			     bitblt-src-width
			     bitblt-dest-width
			     bitblt-src-height)
  ;; Use of return registers as globals to avoid having to use the stack for locals.
  ;; gr:*Return-17* offset-in-words-byte-spec
  ;; gr:*Return-18* column-width
  ;; gr:*Return-19* src-byte-spec
  ;; gr:*Return-20* dest-byte-spec
  (setq gr:*Return-17* (byte 19. 5.))
  (do ((width width (- width gr:*Return-18*)))
      ((<= width 0) NIL)
    ;; WrapAround on source array?
    (when (>= bitblt-x-src-offset bitblt-src-width)
      (setq bitblt-x-src-offset 0))
    ;; compute column width to be done and next x offsets for both source and destination arrays.
    (multiple-value-setq (gr:*Return-18* gr:*Return-19* gr:*Return-20*)
      (compute-width-of-column-to-bitblt
	bitblt-x-dest-offset
	bitblt-x-src-offset
;	bitblt-src-width
;	bitblt-dest-width
	width
	T))
    ;; Do column
    (bitblt-column (hw:24+ (hw:ldb bitblt-x-dest-offset gr:*Return-17* 0) dest-adr)
		   (hw:24+ (hw:ldb bitblt-x-src-offset gr:*Return-17* 0) src-adr)
		   column-height
		   bitblt-src-height
		   word-inc-dest-rows
		   word-inc-src-rows
		   gr:*Return-20* ;dest-byte-spec
		   gr:*Return-19* ;src-byte-spec
		   bitblt-src-y-offset
		   bitblt-src-y
		   alu-function)
    ;; update x offsets in both source and destination arrays.
    (setq bitblt-x-dest-offset (hw:24+ bitblt-x-dest-offset gr:*Return-18*))
    (setq bitblt-x-src-offset (hw:24+ bitblt-x-src-offset gr:*Return-18*))
    )
  )

(defun bitblt-right-to-left (alu-function
			     width
			     column-height
			     src-adr
			     dest-adr
			     word-inc-src-rows
			     word-inc-dest-rows
			     bitblt-x-src-offset
			     bitblt-x-dest-offset
			     bitblt-src-y-offset
			     bitblt-src-y
			     bitblt-src-width
			     bitblt-dest-width
			     bitblt-src-height)
  ;; Use of return registers as globals to avoid having to use the stack for locals.
  ;; gr:*Return-17* offset-in-words-byte-spec
  ;; gr:*Return-18* column-width
  ;; gr:*Return-19* src-byte-spec
  ;; gr:*Return-20* dest-byte-spec
  (setq gr:*Return-17* (byte 19. 5.))
  (do ((width width (- width gr:*Return-18*)))
      ((<= width 0))
    ;; WrapAround on source array?
    (when (minusp bitblt-x-src-offset)
      (setq bitblt-x-src-offset (1- bitblt-src-width)))
    ;; compute column width to be done and next x offsets for both source and destination arrays.
    (multiple-value-setq (gr:*Return-18* gr:*Return-19* gr:*Return-20*)
      (compute-width-of-column-to-bitblt
	bitblt-x-dest-offset
	bitblt-x-src-offset
;	bitblt-src-width
;	bitblt-dest-width
	width
	NIL))
    ;; Do column
    (bitblt-column (hw:24+ (hw:ldb bitblt-x-dest-offset gr:*Return-17* 0) dest-adr)
		   (hw:24+ (hw:ldb bitblt-x-src-offset gr:*Return-17* 0) src-adr)
		   column-height
		   bitblt-src-height
		   word-inc-dest-rows
		   word-inc-src-rows
		   gr:*Return-20* ;dest-byte-spec
		   gr:*Return-19* ;src-byte-spec
		   bitblt-src-y-offset
		   bitblt-src-y
		   alu-function)
    ;; update x offsets in both source and destination arrays.
    (setq bitblt-x-dest-offset (- bitblt-x-dest-offset gr:*Return-18*))
    (setq bitblt-x-src-offset (- bitblt-x-src-offset gr:*Return-18*))
    )
  )

(defun bitblt-process-height (height word-inc-src-rows word-inc-dest-rows
			      from-array To-array bitblt-src-height bitblt-dest-height from-y To-y
			      bitblt-src-y-offset)
  (let ((negative-height (minusp height))
	temp1 temp2)
    (when negative-height (setq height (- height)))
    ;; check for range. Trim height to fit destination array.
    (when (> (+ To-y height) bitblt-dest-height)
      (setq height (- bitblt-dest-height To-y)))
    (when negative-height
      ;; Change from top of column to bottom of columns for source and destination.
      (setq from-array (hw:24+ (new-math:multiply-fixnum
				 (hw:24+ -1 bitblt-src-height)
				 word-inc-src-rows)
			       from-array))
      (setq To-array (hw:24+ (new-math:multiply-fixnum
			       (hw:24+ -1 height)
			       word-inc-dest-rows)
			     To-array))
      (multiple-value-setq (temp1 temp2)
	(new-math:divide-fixnum (hw:24+ height from-y)
				bitblt-src-height))
      ;; number of rows is then offset from the bottom of column
      (setq from-y (hw:24- height temp1))
      (setq bitblt-src-y-offset (new-math:multiply-fixnum from-y word-inc-src-rows))
      ;; negate increment for source rows
      (setq word-inc-src-rows (- word-inc-src-rows))
      ;; negate word increment between destination rows.
      (setq word-inc-dest-rows (- word-inc-dest-rows)))
    (values height from-array To-array word-inc-src-rows word-inc-dest-rows from-y bitblt-src-y-offset)
    )
  )

(defun bitblt-process-width (width from-array To-array bitblt-src-width bitblt-dest-width
			     bitblt-src-x-offset bitblt-dest-x-offset)
  (let ((negative-width (minusp width)) temp1)
    ;; if width is negative , make it positive.
    (when negative-width (setq width (- width)))
    ;; check for range. Make width fit destination array.
    (when (> (+ bitblt-dest-x-offset width) bitblt-dest-width)
      (setq width (- bitblt-dest-width bitblt-dest-x-offset)))
    (when negative-width
      (setq bitblt-dest-x-offset (+ bitblt-dest-x-offset width -1))
      ;; computes address of last column.
;      (setq To-array (hw:24+ (hw:ldb bitblt-dest-x-offset (byte 19. 5) 0) To-array))
      ;; corresponding bit offset.
;      (setq bitblt-dest-x-offset (hw:ldb bitblt-dest-x-offset (byte 5. 0) 0))
      (setq bitblt-src-x-offset (+ bitblt-src-x-offset width -1))
      ;; modulo source size.
      (multiple-value-setq (temp1 bitblt-src-x-offset)
	(new-math:divide-fixnum bitblt-src-x-offset bitblt-src-width))
;      (setq From-array (hw:24+ (hw:ldb bitblt-src-x-offset (byte 19. 5) 0) From-array))
;      (setq bitblt-src-x-offset (hw:ldb bitblt-src-x-offset (byte 5 0) 0))
      )
    (values width negative-width from-array To-array bitblt-src-x-offset bitblt-dest-x-offset)
    )
  )
  

(defun bitblt (alu-function width height from-array from-x from-y to-array to-x to-y)
  (li:%trap-if-not-both-fixnum width height)
  (li:%trap-if-not-both-fixnum from-x from-y)
  (li:%trap-if-not-both-fixnum to-x to-y)
  (li:%trap-if-not-both-fixnum alu-function alu-function)
  (or (arrayp from-array)
      (li:error "~S is not an array" from-array))
  (or (arrayp to-array)
      (li:error "~S is not an array" to-array))
  (let (;byte-spec
;	word-inc-dest-rows
;	bitblt-dest-height
;	bitblt-dest-width
	bitblt-src-width
	bitblt-src-height
;	word-inc-src-rows
	bitblt-src-y-offset)
    ;; use global register gr:*Return-9* as the byte-spec
    ;; use global register gr:*Return-10* as word-inc-dest-rows
    ;; use global register gr:*Return-11* as word-inc-src-rows
    ;; use global register gr:*Return-12* as bitblt-dest-height
    ;; use global register gr:*Return-13* as bitblt-dest-width
    ;; use global register gr:*Return-14* as negative-width
    ;; decode source
    (multiple-value-setq (from-array gr:*Return-9* bitblt-src-width bitblt-src-height from-x from-y)
      (bitblt-decode-array from-array from-x from-y))
    ;; shifting down 5 bits to divide by 32.
;    (setq word-inc-src-rows (hw:ldb bitblt-src-width (byte 19. 5.) 0))
    (setq gr:*Return-11* (hw:ldb bitblt-src-width (byte 19. 5.) 0))
    ;; compute offset from top of column.
    (setq bitblt-src-y-offset (new-math:multiply-fixnum from-y gr:*Return-11*))
    ;; get start address of first column assuming that height is positive.
    (setq from-array (hw:24+ (- bitblt-src-y-offset) from-array))
    ;; decode destination
    (multiple-value-setq (To-array gr:*Return-9* gr:*return-13* gr:*Return-12* To-x To-y)
      (bitblt-decode-array To-array To-x To-y))
    ;; word increment between destination rows
;    (setq word-inc-dest-rows (hw:ldb bitblt-dest-width (byte 19. 5.) 0))
    (setq gr:*Return-10* (hw:ldb gr:*Return-13* (byte 19. 5.) 0))    
    (multiple-value-setq (height
			  from-array
			  To-array
			  gr:*Return-11*	;word-inc-src-rows
			  gr:*Return-10*	;word-inc-dest-rows
			  from-y
			  bitblt-src-y-offset
			  )
      (bitblt-process-height
	height
	gr:*Return-11*				;word-inc-src-rows
	gr:*Return-10*				; word-inc-dest-rows
	from-array
	To-array
	bitblt-src-height
	gr:*Return-12*
	from-y
	To-y
	bitblt-src-y-offset)
      )
    ;; transform width from bytes to bits.
    (setq width (hw:dpb width gr:*Return-9* 0))
    ;; process width
    (multiple-value-setq (width gr:*Return-14*
			  from-array
			  To-array
			  From-x
			  To-x)
      (bitblt-process-width width from-array To-array bitblt-src-width gr:*Return-13* from-x To-x))
    (if gr:*Return-14*
	(bitblt-right-to-left alu-function
			      width
			      height
			      From-array
			      To-array
			      gr:*Return-11*	;word-inc-src-rows
			      gr:*Return-10*	;word-inc-dest-rows
			      From-x
			      To-x
			      bitblt-src-y-offset
			      From-y
			      bitblt-src-width	; bitblt-src-width
			      gr:*Return-13*
			      bitblt-src-height)
      (bitblt-left-to-right alu-function
			    width
			    height
			    From-array
			    To-array
			    gr:*Return-11*	;word-inc-src-rows
			    gr:*Return-10*	;word-inc-dest-rows
			    From-x
			    To-x
			    bitblt-src-y-offset
			    From-y
			    bitblt-src-width	; bitblt-src-width
			    gr:*Return-13*
			    bitblt-src-height))
    
    )
  )




