(FILECREATED "29-Jun-85 18:18:44" {DSK}<DSK>HTHOMPSON>DSL>BUSMASTER.;12 36369  

      changes to:  (FNS PCBUS.READARRAY PCBUS.WRITEARRAY)

      previous date: "24-Jun-85 09:12:06" {DSK}<DSK>HTHOMPSON>DSL>BUSMASTER.;10)


(* Copyright (c) 1984, 1985 by Xerox Corporation. All rights reserved.)

(PRETTYCOMPRINT BUSMASTERCOMS)

(RPAQQ BUSMASTERCOMS ((* peek and poke functions)
	(FNS BUS.RESET MBUS.INPUT MBUS.OUTPUT MBUS.READ MBUS.READHL MBUS.WRITE MBUS.WRITEHL 
	     PCBUS.INPUT PCBUS.OUTPUT PCBUS.READ PCBUS.READHL PCBUS.READWORD PCBUS.WRITE 
	     PCBUS.WRITEHL PCBUS.WRITEWORD)
	(* block transfer and test functions (words <-> PC only to date))
	(FNS PCBUS.READARRAY PCBUS.WRITEARRAY PCBUS.TESTARRAY)
	(* define the \BUSBLT subopcodes in terms of the basic one)
	(PROP DOPVAL \BUSBLTIN \BUSBLTINBYTES \BUSBLTINSWAPBYTES \BUSBLTOUT \BUSBLTOUTBYTES 
	      \BUSBLTOUTNYBBLES \BUSBLTOUTSWAPBYTES \BUSBLTCHECK \BUSBLTCHECKBYTES 
	      \BUSBLTCHECKSWAPBYTES)
	(FNS \BUSBLT.UFN)
	(* dma controller%'s low level (command)
	   functions)
	(FNS BUSDMA.SETMODE BUSDMA.SETPAGE BUSDMA.SETADDRESS BUSDMA.READADDRESS BUSDMA.SETCOUNTER 
	     BUSDMA.READCOUNTER BUSDMA.MASK BUSDMA.UNMASK)
	(* dma controller%'s higher level functions)
	(FNS BUSDMA.INIT BUSDMA.READTCBIT)
	(VARS (BUSDMA.TCBITS 0))
	(GLOBALVARS BUSDMA.TCBITS)
	(MACROS BUSDMA.FASTUPDATEADDR BUSDMA.SLOWUPDATEADDR)
	(PROP ARGNAMES BUSDMA.FASTUPDATEADDR BUSDMA.SLOWUPDATEADDR)
	(FNS \FUATran \SUATran)
	(* Busmaster and PC/Multibus connection checkout)
	(FNS BUS.CHECKADDR BUS.CHECKCNT BUS.CHECKOUT BM16LOOP BM8LOOP BMSTATLOOP MB.CHECKOUT 
	     MBRCVR.CHECKOUT MBMEM.PRELIMCHECK PC.CHECKOUT PCRCVR.CHECKOUT PCMEM.PRELIMCHECK 
	     STATPRINT)
	(FILES (SYSLOAD)
	       BUSEXTENDER)
	(DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY (FILES (LOADCOMP)
							       BUSEXTENDER))))



(* peek and poke functions)

(DEFINEQ

(BUS.RESET
  [LAMBDA NIL                                                (* ht: "25-May-85 17:33")

          (* * reset the BusMaster and do a reset cycle on the external bus -- emit a control code then write a dummy datum to
	  trigger the cycle)


    (BX.OUTPUT 32 4)
    (BX.OUTPUT 0 5)

          (* * this amounts to the core of PCDAC.STOP if it is at the standard address -
	  this shouldn:t be necessary but apparently it is, and I hope it won%'t screw up if what%'s attached is a multibus 
	  instead of a pc bus.)


    (PCBUS.OUTPUT 748+1 15)
    (PCBUS.INPUT 748])

(MBUS.INPUT
  [LAMBDA (IOADR)                                            (* ht: "25-May-85 13:18")

          (* * input a word datum from an i/o address on the external multibus -- latch address, latch command, write dummy 
	  datum to trigger the cycle, read the resulting datum)


    (BX.OUTPUT IOADR 7)
    (BX.OUTPUT 34308 4)                                      (* "8604 hex")
    (BX.OUTPUT 0 5)
    (BX.INPUT 5])

(MBUS.OUTPUT
  [LAMBDA (IOADR DATUM)                                      (* ht: "25-May-85 13:19")

          (* * output a word datum to an i/o address on the external multibus -- latch address, latch command, write datum 
	  (which triggers the cycle))


    (BX.OUTPUT IOADR 7)
    (BX.OUTPUT 34056 4)                                      (* "8508 hex")
    (BX.OUTPUT DATUM 5])

(MBUS.READ
  [LAMBDA (MEMADDR)                                          (* ht: "25-May-85 13:10")

          (* * read a word datum from a memory address on the external multibus)


    (MBUS.READHL (LRSH MEMADDR 16)
		 (LOGAND MEMADDR 65535])

(MBUS.READHL
  [LAMBDA (MEMADRH MEMADRL)                                  (* ht: "25-May-85 13:20")

          (* * read a word datum from a memory address on the external multibus -- latch page number, latch address, latch 
	  command, write dummy datum to trigger the cycle, read the resulting datum)


    (BX.OUTPUT MEMADRH 6)
    (BX.OUTPUT MEMADRL 7)
    (BX.OUTPUT 33281 4)                                      (* "8201 hex")
    (BX.OUTPUT 0 5)
    (BX.INPUT 5])

(MBUS.WRITE
  [LAMBDA (MEMADDR DATUM)                                    (* ht: "25-May-85 13:10")

          (* * write a word datum to a memory address on the external multibus)


    (MBUS.WRITEHL (LRSH MEMADDR 16)
		  (LOGAND MEMADDR 65535)
		  DATUM])

(MBUS.WRITEHL
  [LAMBDA (MEMADRH MEMADRL DATUM)                            (* ht: "25-May-85 13:17")

          (* * write a word datum to a memory address on the external multibus -- latch page number, latch address, latch 
	  command, write datum (which triggers the cycle))


    (BX.OUTPUT MEMADRH 6)
    (BX.OUTPUT MEMADRL 7)
    (BX.OUTPUT 33026 4)                                      (* "8102 hex")
    (BX.OUTPUT DATUM 5])

(PCBUS.INPUT
  [LAMBDA (IOADR)                                            (* ht: "25-May-85 13:23")

          (* * input a byte datum from an i/o address on the external pcbus -- latch address, latch command, write dummy datum
	  to trigger the cycle, read the resulting datum)


    (BX.OUTPUT IOADR 7)
    (BX.OUTPUT 4 4)
    (BX.OUTPUT 0 5)
    (BX.INPUT 5])

(PCBUS.OUTPUT
  [LAMBDA (IOADR DATUM)                                      (* ht: "25-May-85 13:24")

          (* * output a byte datum to an i/o address on the external pcbus -- latch address, latch command, write datum 
	  (which triggers the cycle))


    (BX.OUTPUT IOADR 7)
    (BX.OUTPUT 8 4)
    (BX.OUTPUT DATUM 5])

(PCBUS.READ
  [LAMBDA (MEMADDR)                                          (* ht: "25-May-85 13:24")

          (* * read a byte datum from a memory address on the external pcbus)


    (PCBUS.READHL (LRSH MEMADDR 16)
		  (LOGAND MEMADDR 65535])

(PCBUS.READHL
  [LAMBDA (MEMADRH MEMADRL)                                  (* ht: "25-May-85 13:25")

          (* * read a byte datum from a memory address on the external pcbus -- latch page number, latch address, latch 
	  command, write dummy datum to trigger the cycle, read the resulting datum)


    (BX.OUTPUT MEMADRH 6)
    (BX.OUTPUT MEMADRL 7)
    (BX.OUTPUT 1 4)
    (BX.OUTPUT 0 5)
    (BX.INPUT 5])

(PCBUS.READWORD
  [LAMBDA (hiAddr loWordAddr mode)                           (* ht: "26-May-85 18:44")

          (* * read a word from the external pc memory -
	  hiAddr is pcmem page number, loWordAddr is word address on page. mode is either STRAIGHT (or NIL) meaning bytes are 
	  stored hiOrder/loOrder, or SWAP meaning bytes are stored loOrder/hiOrder)


    (LET*[(loAddr (LLSH loWordAddr 1))
       (first (PCBUS.READHL hiAddr loAddr))
       (second (PCBUS.READHL hiAddr (IPLUS loAddr 1]
      (SELECTQ mode
	       ((NIL STRAIGHT)
		 (IPLUS (LLSH first 8)
			second))
	       (SWAP (IPLUS (LLSH second 8)
			    first))
	       (SHOULDNT "bad mode"])

(PCBUS.WRITE
  [LAMBDA (MEMADDR DATUM)                                    (* ht: "25-May-85 13:24")

          (* * write a byte datum to a memory address on the external pcbus)


    (PCBUS.WRITEHL (LRSH MEMADDR 16)
		   (LOGAND MEMADDR 65535)
		   DATUM])

(PCBUS.WRITEHL
  [LAMBDA (MEMADRH MEMADRL DATUM)                            (* ht: "25-May-85 13:25")

          (* * write a byte datum to a memory address on the external pcbus -- latch page number, latch address, latch 
	  command, write datum (which triggers the cycle))


    (BX.OUTPUT MEMADRH 6)
    (BX.OUTPUT MEMADRL 7)
    (BX.OUTPUT 2 4)
    (BX.OUTPUT DATUM 5])

(PCBUS.WRITEWORD
  [LAMBDA (hiAddr loWordAddr word mode)                      (* ht: "26-May-85 18:48")

          (* * write a word to the external pc memory -
	  hiAddr is pcmem page number, loWordAddr is word address on page. mode is either STRAIGHT (or NIL) meaning bytes are 
	  stored hiOrder/loOrder, or SWAP meaning bytes are stored loOrder/hiOrder)


    (LET ((lo (LOGAND word 255))
       (hi (LRSH word 8))
       (loAddr (LLSH loWordAddr 1)))
      (SELECTQ mode
	       ((NIL STRAIGHT)
		 (PCBUS.WRITEHL hiAddr loAddr hi)
		 (PCBUS.WRITEHL hiAddr (IPLUS loAddr 1)
				lo))
	       (SWAP (PCBUS.WRITEHL hiAddr loAddr lo)
		     (PCBUS.WRITEHL hiAddr (IPLUS loAddr 1)
				    hi))
	       (SHOULDNT "bad mode"])
)



(* block transfer and test functions (words <-> PC only to date))

(DEFINEQ

(PCBUS.READARRAY
  [LAMBDA (array loWordAddr count mode arrayIndex wrap? hiAddr)
                                                             (* ht: "29-Jun-85 18:18")

          (* * read words from the external pc memory to an array -
	  hiAddr is pcmem page number (defaults to 1), loWordAddr is word address on page. mode is either STRAIGHT 
	  (or NIL) meaning bytes are stored hiOrder/loOrder, or SWAP meaning bytes are stored loOrder/hiOrder -
	  count is the number of words to transfer, and arrayIndex is the first element of the array to store into 
	  (defaults to first element of array) -
	  Works for either 0 or 1 origin arrays. Checks array references are in bounds, but not PC ones because it will wrap 
	  around in case of overflow, which is probably what one wants. (If wrap? is non-NIL, will wrap around with respect to
	  the array if necessary.))


    (if (IGREATERP count 0)
	then (if (ARRAYTYP array)= 'SMALLPOSP
		 then (LET ((size (ARRAYSIZE array))
			 (orig (ARRAYORIG array))
			 firstCount)
			(if (OR (ILEQ (OR arrayIndex orig)+count size+orig)
				(AND wrap? (ILEQ count size)
				     (PCBUS.READARRAY array loWordAddr firstCount←size+orig-(OR
							arrayIndex orig)
						      mode arrayIndex NIL hiAddr)
				     (add loWordAddr firstCount)
				     count←count-firstCount
				     arrayIndex←orig))
			    then (SELECTQ mode
					  ((NIL STRAIGHT)
					    (\BUSBLTINBYTES (if arrayIndex
								then (\ADDBASE (ARRAYBASE array)
									       (IDIFFERENCE 
										       arrayIndex 
											    orig))
							      else (ARRAYBASE array))
							    (OR hiAddr 1)
							    (LLSH loWordAddr 1)
							    count))
					  (SWAP (\BUSBLTINSWAPBYTES (if arrayIndex
									then (\ADDBASE (ARRAYBASE
											 array)
										       (IDIFFERENCE
											 arrayIndex 
											 orig))
								      else (ARRAYBASE array))
								    (OR hiAddr 1)
								    (LLSH loWordAddr 1)
								    count))
					  (SHOULDNT "bad mode"))
			  else (SETERRORN 27 count)
			       (ERRORX)))
	       else (SETERRORN 27 array)
		    (ERRORX])

(PCBUS.WRITEARRAY
  [LAMBDA (array loWordAddr count mode arrayIndex wrap? hiAddr)
                                                             (* ht: "29-Jun-85 18:18")

          (* * write words to the external pc memory from an array of WORDs -
	  hiAddr is pcmem page number (defaults to 1), loWordAddr is word address on page. mode is either STRAIGHT 
	  (or NIL) meaning bytes are stored hiOrder/loOrder, or SWAP meaning bytes are stored loOrder/hiOrder -
	  count is the number of words to transfer, and arrayIndex is the first element of the array to store into 
	  (defaults to first element of array) -
	  Works for either 0 or 1 origin arrays. Checks array references are in bounds, but not PC ones because it will wrap 
	  around in case of overflow, which is probably what one wants. If wrap? is non-NIL, will wrap around with respect to 
	  the array if necessary.)


    (if (IGREATERP count 0)
	then (if (ARRAYTYP array)= 'SMALLPOSP
		 then (LET ((size (ARRAYSIZE array))
			 (orig (ARRAYORIG array))
			 firstCount)
			(if (OR (ILEQ (OR arrayIndex orig)+count size+orig)
				(AND wrap? (ILEQ count size)
				     (PCBUS.WRITEARRAY array loWordAddr firstCount←size+orig-(OR
							 arrayIndex orig)
						       mode arrayIndex NIL hiAddr)
				     (add loWordAddr firstCount)
				     count←count-firstCount
				     arrayIndex←orig))
			    then (SELECTQ mode
					  ((NIL STRAIGHT)
					    (\BUSBLTOUTBYTES (if arrayIndex
								 then (\ADDBASE (ARRAYBASE array)
										(IDIFFERENCE 
										       arrayIndex 
											     orig))
							       else (ARRAYBASE array))
							     (OR hiAddr 1)
							     (LLSH loWordAddr 1)
							     count))
					  (SWAP (\BUSBLTOUTSWAPBYTES (if arrayIndex
									 then (\ADDBASE (ARRAYBASE
											  array)
											(IDIFFERENCE
											  arrayIndex 
											  orig))
								       else (ARRAYBASE array))
								     (OR hiAddr 1)
								     (LLSH loWordAddr 1)
								     count))
					  (SHOULDNT "bad mode"))
			  else (SETERRORN 27 count)
			       (ERRORX)))
	       else (SETERRORN 27 array)
		    (ERRORX])

(PCBUS.TESTARRAY
  [LAMBDA (array loWordAddr count mode arrayIndex hiAddr)    (* ht: "26-May-85 18:52")

          (* * read and compare words from the external pc memory to an array -
	  hiAddr is pcmem page number (defaults to 1), loWordAddr is word address on page. mode is either STRAIGHT 
	  (or NIL) meaning bytes are stored hiOrder/loOrder, or SWAP meaning bytes are stored loOrder/hiOrder -
	  count is the number of words to transfer, and arrayIndex is the first element of the array to store into 
	  (defaults to first element of array) -
	  Works for either 0 or 1 origin arrays. Works backwords until it finds an error or completes, and returns the offset 
	  of the failing word -
	  i.e. 0 means success, n means (ELT array (PLUS (SUB1 n) arrayIndex)) is bad. Checks array references are in bounds, 
	  but not PC ones because it will wrap around in case of overflow, which is probably what one wants.)


    (if (count gt 0)
	then (ELT array (IPLUS count -1)+(IPLUS (OR arrayIndex 1)
						-1)+(ARRAYORIG array))
                                                             (* cheap bounds check)
	     (if (ARRAYTYP array)= 'SMALLPOSP
		 then (SELECTQ mode
			       ((NIL STRAIGHT)
				 (\BUSBLTCHECKBYTES (if arrayIndex
							then (\ADDBASE (ARRAYBASE array)
								       (IDIFFERENCE arrayIndex
										    (ARRAYORIG array))
								       )
						      else (ARRAYBASE array))
						    (OR hiAddr 1)
						    (LLSH loWordAddr 1)
						    count))
			       (SWAP (\BUSBLTCHECKSWAPBYTES (if arrayIndex
								then (\ADDBASE (ARRAYBASE array)
									       (IDIFFERENCE
										 arrayIndex
										 (ARRAYORIG array)))
							      else (ARRAYBASE array))
							    (OR hiAddr 1)
							    (LLSH loWordAddr 1)
							    count))
			       (SHOULDNT "bad mode"))
	       else (ERROR "array must be of WORDs"])
)



(* define the \BUSBLT subopcodes in terms of the basic one)


(PUTPROPS \BUSBLTIN DOPVAL (4 BUSBLT 4))

(PUTPROPS \BUSBLTINBYTES DOPVAL (4 BUSBLT 5))

(PUTPROPS \BUSBLTINSWAPBYTES DOPVAL (4 BUSBLT 6))

(PUTPROPS \BUSBLTOUT DOPVAL (4 BUSBLT 0))

(PUTPROPS \BUSBLTOUTBYTES DOPVAL (4 BUSBLT 1))

(PUTPROPS \BUSBLTOUTNYBBLES DOPVAL (4 BUSBLT 3))

(PUTPROPS \BUSBLTOUTSWAPBYTES DOPVAL (4 BUSBLT 2))

(PUTPROPS \BUSBLTCHECK DOPVAL (4 BUSBLT 20))

(PUTPROPS \BUSBLTCHECKBYTES DOPVAL (4 BUSBLT 21))

(PUTPROPS \BUSBLTCHECKSWAPBYTES DOPVAL (4 BUSBLT 22))
(DEFINEQ

(\BUSBLT.UFN
  [LAMBDA (DLADR BUSADRH BUSADRL NWORDS ALPHABYTE)           (* ht: "26-May-85 17:25")

          (* * error UFN for the busblt opcode -- we do not need more because (1) machines without the BUSBLT microcode also 
	  dont have the BX.INPUT etc microcode one would need to simulate the BUSBLT, (2) we have not publicly documented the 
	  BUSBLT subopcodes that are not implemented in microcode, and (3) the BUSBLT opcode doesn%'t punt)


    (ERROR "Attempt to use busmaster on a machine which doesn't support it"])
)



(* dma controller%'s low level (command) functions)

(DEFINEQ

(BUSDMA.SETMODE
  [LAMBDA (CHAN WRITEMEMORY? AUTOINIT? DECADDR?)             (* ht: "25-May-85 12:55")

          (* * set the channel%'s mode register. Assumes "single" mode)


    (PCBUS.OUTPUT 11 (LOGOR CHAN (if WRITEMEMORY?
				     then 4
				   else 8)
			    (if AUTOINIT?
				then 16
			      else 0)
			    (if DECADDR?
				then 32
			      else 0)
			    64])

(BUSDMA.SETPAGE
  [LAMBDA (CHAN PAGE)                                        (* ht: "25-May-85 12:55")

          (* * write the channel%'s page register. Note that the page register for channel 0 (the memory refresh channel) is 
	  of no use.)


    (PCBUS.OUTPUT (SELECTQ CHAN
			   (1 131)
			   (2 129)
			   (3 130)
			   (HELP "bad channel# arg:" CHAN))
		  PAGE])

(BUSDMA.SETADDRESS
  [LAMBDA (CHAN ADDR)                                        (* ht: "25-May-85 12:55")

          (* * write low-16-bit part of base address to a channel%'s base and current address registers -- presumes the 
	  channel is masked, but does clear the byte-select flipflop first.)


    (PROG ((CMD (LLSH CHAN 1)))
          (PCBUS.OUTPUT 12 0)
          (PCBUS.OUTPUT CMD ADDR)
          (PCBUS.OUTPUT CMD (LRSH ADDR 8])

(BUSDMA.READADDRESS
  [LAMBDA (CHAN)                                             (* ht: "26-May-85 18:53")

          (* * read and return a channel%'s current address register (that is, the low-16-bit part) -- presumes the channel is
	  masked, but does clear the byte-select flipflop first.)


    (LET ((CMD (LLSH CHAN 1)))
      (PCBUS.OUTPUT 12 0)
      (LOGOR (PCBUS.INPUT CMD)
	     (LLSH (PCBUS.INPUT CMD)
		   8])

(BUSDMA.SETCOUNTER
  [LAMBDA (CHAN NBYTES)                                      (* ht: "25-May-85 12:55")

          (* * write 16-bit base byte count for dma to a channel%'s base and current byte count registers, as 
	  byte-count-less-1 mod 64K -- presumes the channel is masked, but does clear the byte-select flipflop first.)


    (PROG [(NBYTESLESS1 (NBYTES-1))
	   (CMD (1+(LLSH CHAN 1]
          (if (OR (ILEQ NBYTES 0)
		  (IGREATERP NBYTES 65536))
	      then (HELP "arg must be 1..64K:" NBYTES))
          (PCBUS.OUTPUT 12 0)
          (PCBUS.OUTPUT CMD NBYTESLESS1)
          (PCBUS.OUTPUT CMD (LRSH NBYTESLESS1 8])

(BUSDMA.READCOUNTER
  [LAMBDA (CHAN)                                             (* ht: "26-May-85 18:56")

          (* * read a channel%'s current byte-count-remaining-less-1 register, and return as byte count, mod 64K -- presumes 
	  the channel is masked, but does clear the byte-select flipflop first.)


    (LET [(CMD (IPLUS 1 (LLSH CHAN 1]
      (PCBUS.OUTPUT 12 0)
      (IPLUS (LOGOR (PCBUS.INPUT CMD)
		    (LLSH (PCBUS.INPUT CMD)
			  8))
	     1])

(BUSDMA.MASK
  [LAMBDA (CHAN)                                             (* ht: "25-May-85 12:55")

          (* * prevent dma activity on a channel, by setting its bit in the controller%'s mask register)


    (PCBUS.OUTPUT 10 4+CHAN])

(BUSDMA.UNMASK
  [LAMBDA (CHAN)                                             (* ht: "25-May-85 12:55")

          (* * allow dma activity on a channel, by clearing its bit in the controller%'s mask register)


    (PCBUS.OUTPUT 10 CHAN])
)



(* dma controller%'s higher level functions)

(DEFINEQ

(BUSDMA.INIT
  [LAMBDA NIL                                                (* ht: "25-May-85 12:55")

          (* * initialize the bus%'s dma controller AND start memory refresh in the bus%'s memory, using channel 0 -- so it 
	  also has to initialize the timer used for memory refresh -- leaves channels 1-3 masked but uninitialized)



          (* * TIMER is the base i/o command address for the memory refresh timer -- MASTERCLEAR is the dma chip%'s 
	  "master clear" command address -- BUSDMA.TCBITS is memory of status register, see BUSDMA.READTCBIT)


    (PROG ((TIMER 64)
	   (MASTERCLEAR 13))

          (* * initialize timer --)


          (PCBUS.OUTPUT 3+TIMER 84)
          (PCBUS.OUTPUT 1+TIMER 18)

          (* * dma in general -- master clear, including in particular: clear command register (enable dma controller, no 
	  specials), set mask register (disable all channels), and clear high-byte-select flipflop -- the datum argument is 
	  ignored by the hadware)


          (PCBUS.OUTPUT MASTERCLEAR 0)
          (BUSDMA.TCBITS←0)

          (* * memory refresh via channel 0 --)


          (BUSDMA.SETCOUNTER 0 8192)                         (* max wordcount)
          (BUSDMA.SETMODE 0 NIL T NIL)                       (* read, autoinit, increment address 
							     (and "single" mode))
          (BUSDMA.UNMASK 0)                                  (* go)
      ])

(BUSDMA.READTCBIT
  [LAMBDA (CHANNEL CLEARTHEBIT?)                             (* ht: "26-May-85 18:58")

          (* * return value of TC bit for channel -- hardware remembers all bits, in status register, until init or read from 
	  status register, either of which clears them all -- we want to remember each bit separately until init or that bit 
	  is explicitly cleared via this function -- so use global variable BUSDMA.TCBITS -- BUSDMA.INIT clears BUSDMA.TCBITS)


    (LET ((MASK (LLSH 1 CHANNEL)))
      BUSDMA.TCBITS←(LOGOR BUSDMA.TCBITS (PCBUS.INPUT 8))
      (PROG1 (LOGAND BUSDMA.TCBITS MASK)
	     ~=0
	     (if CLEARTHEBIT?
		 then BUSDMA.TCBITS←(LOGAND BUSDMA.TCBITS (LOGNOT MASK])
)

(RPAQQ BUSDMA.TCBITS 0)
(DECLARE: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS BUSDMA.TCBITS)
)
(DECLARE: EVAL@COMPILE 

(PUTPROPS BUSDMA.FASTUPDATEADDR MACRO (X (\FUATran (CAR X)
						   (CADR X)
						   (CADDR X))))

(PUTPROPS BUSDMA.SLOWUPDATEADDR MACRO (X (\SUATran (CAR X)
						   (CADR X)
						   (CADDR X))))
)

(PUTPROPS BUSDMA.FASTUPDATEADDR ARGNAMES (dmaChannel currentAddress wrapped))

(PUTPROPS BUSDMA.SLOWUPDATEADDR ARGNAMES (dmaChannel currentAddress wrapped))
(DEFINEQ

(\FUATran
  [LAMBDA (dmaChannel currentAddress wrapped)                (* ht: "22-Jun-85 18:01")
    (if (AND (OR (NUMBERP dmaChannel)
		 (NUMBERP (EVAL dmaChannel)))
	     currentAddress wrapped (LITATOM currentAddress)
	     (LITATOM wrapped))
	then 

          (* * Open coded all the way -
	  This is the fast, unsafe version -
	  For channel, which must be an explicit number updates currentAddress and wrapped, which must be variable names.
	  Generates an error if a double wrap has happened. See documentation of BUSMASTER for description of what this is 
	  for.)


	     [BQUOTE (PROGN (PROGN (BX.OUTPUT 12 7)
				   (BX.OUTPUT 8 4)
				   (BX.OUTPUT 0 5))
			    (SETQ , currentAddress
			      (LOGOR (LRSH (PROGN (BX.OUTPUT (CONSTANT (LLSH , dmaChannel 1))
							     7)
						  (BX.OUTPUT 4 4)
						  (BX.OUTPUT 0 5)
						  (BX.INPUT 5))
					   1)
				     (LLSH (PROGN (BX.OUTPUT (CONSTANT (LLSH , dmaChannel 1))
							     7)
						  (BX.OUTPUT 4 4)
						  (BX.OUTPUT 0 5)
						  (BX.INPUT 5))
					   7)))

          (* * Check the TC bit for this channel -
	  open coded for speed)


			    (if , wrapped
				then (if [NOT (ZEROP (LOGAND (PROGN (BX.OUTPUT 8 7)
								    (BX.OUTPUT 4 4)
								    (BX.OUTPUT 0 5)
								    (BX.INPUT 5))
							     (CONSTANT (LLSH 1 , dmaChannel]
					 then (SETQ , wrapped 'DoubleWrap)
				       else T)
			      else (if [SETQ , wrapped
					 (NOT (ZEROP (LOGAND (PROGN (BX.OUTPUT 8 7)
								    (BX.OUTPUT 4 4)
								    (BX.OUTPUT 0 5)
								    (BX.INPUT 5))
							     (CONSTANT (LLSH 1 , dmaChannel]
				       then 

          (* * read address again to be sure to get past the end)


					    (PROGN (BX.OUTPUT 12 7)
						   (BX.OUTPUT 8 4)
						   (BX.OUTPUT 0 5))
					    (SETQ , currentAddress
					      (LOGOR (LRSH (PROGN (BX.OUTPUT (CONSTANT (LLSH , 
										       dmaChannel 1))
									     7)
								  (BX.OUTPUT 4 4)
								  (BX.OUTPUT 0 5)
								  (BX.INPUT 5))
							   1)
						     (LLSH (PROGN (BX.OUTPUT (CONSTANT (LLSH , 
										       dmaChannel 1))
									     7)
								  (BX.OUTPUT 4 4)
								  (BX.OUTPUT 0 5)
								  (BX.INPUT 5))
							   7)))

          (* * if we%'ve wrapped again already, that means we were not paying attention for a long time before the current 
	  round, and things are in bad shape)


					    (if [NOT (ZEROP (LOGAND (PROGN (BX.OUTPUT 8 7)
									   (BX.OUTPUT 4 4)
									   (BX.OUTPUT 0 5)
									   (BX.INPUT 5))
								    (CONSTANT (LLSH 1 , dmaChannel]
						then (ERROR "double wrap 2" , dmaChannel)
					      else T]
      else (ERROR "Invalid arguments - must be a number and two atoms" (LIST dmaChannel 
									     currentAddress wrapped])

(\SUATran
  [LAMBDA (dmaChannel currentAddress wrapped)                (* ht: "24-Jun-85 09:11")

          (* * update the address and wrapped flags safely)


    (if (AND currentAddress wrapped (LITATOM currentAddress)
	     (LITATOM wrapped))
	then [BQUOTE (LET ((channel , dmaChannel))
		       (BUSDMA.MASK channel)
		       (SETQ , currentAddress (BUSDMA.READADDRESS channel))
		       (PROG1 (if , wrapped
				  then (if (BUSDMA.READTCBIT channel T)
					   then 'DoubleWrap
					 else T)
				else (SETQ , wrapped (BUSDMA.READTCBIT channel T)))
			      (BUSDMA.UNMASK channel]
      else (ERROR "Invalid arguments - currentAddress and wrapped must be atoms" (LIST currentAddress 
										       wrapped])
)



(* Busmaster and PC/Multibus connection checkout)

(DEFINEQ

(BUS.CHECKADDR
  [LAMBDA (chan pat)                                         (* ht: "26-May-85 19:00")

          (* * loopback on dma address register)


    (LET (result (cmd (LLSH chan 1)))
      (PCBUS.OUTPUT 12 0)
      (PCBUS.OUTPUT cmd pat)
      (PCBUS.OUTPUT cmd (LRSH pat 8))
      (if pat~=result←(LOGOR (PCBUS.INPUT cmd)
			     (LLSH (PCBUS.INPUT cmd)
				   8))
	  then result])

(BUS.CHECKCNT
  [LAMBDA (chan pat)                                         (* ht: "26-May-85 19:00")

          (* * loopback on dma count register)


    (LET (result (cmd (IPLUS (LLSH chan 1)
			     1)))
      (PCBUS.OUTPUT 12 0)
      (PCBUS.OUTPUT cmd pat)
      (PCBUS.OUTPUT cmd (LRSH pat 8))
      (if pat~=result←(LOGOR (PCBUS.INPUT cmd)
			     (LLSH (PCBUS.INPUT cmd)
				   8))
	  then result])

(BUS.CHECKOUT
  [LAMBDA (quietFlg)                                         (* ht: "26-May-85 18:16")

          (* * Do an escalating series of loop-backs to check out the Busmaster card)


    (BUS.RESET)

          (* * the following is just my best guess to date at how to tell whether there is anything out there -
	  may be wrong e.g. for case where a multibus is attached or under other circumstances)


    (AND (SELECTQ (LOGAND (BX.INPUT 4)
			  63679)
		  ((47287 14519 14518 47286)
		    T)
		  (PROGN (printout T 
"Either the busmaster is powered down, or there isn't anything connected to the parallel I/O port where the busmaster should be - please check that the busmaster is indeed cabled up to the bottom socket on the CPE (3) board of the Dandetiger and that it has power."
				   T)
			 NIL))
	 (if (bind result for i from 0 to 15 never (if result←(BMSTATLOOP i)
						       then (printout T "Status loopback failure: " #
								      (STATPRINT i)
								      "in," 25 # (STATPRINT result)
								      "out." T)
							    T))
	     then (if (NOT quietFlg)
		      then (printout T "Status loopback OK" T))
		  T)
	 (if (bind result for i from 0 to 255
		never (if result←(BM8LOOP i)
			  then (printout T "8-bit data loopback failure: " .I2.16 i " in," 29 .I2.16 
					 result " out." T)
			       T))
	     then (if (NOT quietFlg)
		      then (printout T "8-bit data loopback OK" T))
		  T)
	 (if (bind result for i from 0 to 255 never (if result←(BM16LOOP (LOGOR i (LLSH i 8)))
							then (printout T 
								 "16-bit data loopback failure: "
								       .I4.16
								       (LOGOR i (LLSH i 8))
								       " in," 30 .I4.16 result 
								       " out."
								       T)
							     T))
	     then (if (NOT quietFlg)
		      then (printout T "16-bit data loopback OK" T))
		  T)
	 (if [bind result for chan from 0 to 3
		always (for pat in '(0 65535)
			  never (OR (if result←(BUS.CHECKADDR chan pat)
					then (printout T 
					       "DMA channel address loopback failure on channel "
						       chan ": " .I4.16 pat " in," 51 .I4.16 result 
						       " out."
						       T)
					     T)
				    (if result←(BUS.CHECKCNT chan pat)
					then (printout T 
					       "DMA channel counter loopback failure on channel "
						       chan ": " .I4.16 pat " in," 51 .I4.16 result 
						       " out."
						       T)
					     T]
	     then (if (NOT quietFlg)
		      then (printout T "DMA loopback OK" T))
		  T])

(BM16LOOP
  [LAMBDA (D)                                                (* ht: "22-Apr-85 16:19")

          (* * loop back check of the busmaster data register -
	  write to MB memory and read back data register)


    (LET (result)
      (BX.OUTPUT 15 6)
      (BX.OUTPUT 0 7)
      (BX.OUTPUT 33026 4)
      (BX.OUTPUT D 5)
      result←(BX.INPUT 5)
      (if D~=result
	  then result])

(BM8LOOP
  [LAMBDA (D)                                                (* ht: "22-Apr-85 15:42")

          (* * loop back check of the busmaster data register low half -
	  write to PC memory and read back data register)


    (LET (result)
      (BX.OUTPUT 15 6)
      (BX.OUTPUT 0 7)
      (BX.OUTPUT 2 4)
      (BX.OUTPUT D 5)
      result←(BX.INPUT 5)
      (if D~=result
	  then result])

(BMSTATLOOP
  [LAMBDA (pat)                                              (* ht: "26-May-85 19:11")

          (* * do a simple loop-back from the command and address registers back thru the status register -
	  pat is interpreted as Test:Addr16:Addr8:Addr0)


    (LET (result status)
      (BX.OUTPUT (LRSH pat 2)
		 6)
      (BX.OUTPUT (LOGOR (LLSH pat 7)
			pat)
		 7)
      (BX.OUTPUT (LOGOR (LLSH (LOGAND 8 pat)
			      3)
			1)
		 4)
      (BX.OUTPUT 0 5)
      status←(BX.INPUT 4)
      (if pat~=result←(LOGOR (LRSH (LOGAND 256 status)
				   5)
			     (LRSH (LOGAND (IPLUS 1024 512)
					   status)
				   8)
			     (LRSH (LOGAND 64 status)
				   6))
	  then result])

(MB.CHECKOUT
  [LAMBDA (quietFlg)                                         (* ht: "26-May-85 18:37")
    "Not implemented yet"])

(MBRCVR.CHECKOUT
  [LAMBDA (quietFlg)                                         (* ht: "26-May-85 18:37")
    "Not implemented yet"])

(MBMEM.PRELIMCHECK
  [LAMBDA (pageNumber quietFlg)                              (* ht: "26-May-85 18:37")
    "Not implemented yet"])

(PC.CHECKOUT
  [LAMBDA (quietFlg)                                         (* ht: "26-May-85 18:22")
    (BUS.RESET)
    (if (AND (BUS.CHECKOUT quietFlg)
	     (PCRCVR.CHECKOUT quietFlg)
	     (PCMEM.PRELIMCHECK NIL quietFlg))
	then (BUS.RESET)
	     (BUSDMA.INIT)
	     T])

(PCRCVR.CHECKOUT
  [LAMBDA (quietFlg)                                         (* ht: "26-May-85 18:16")

          (* * some simple loop back checks to see if the busmaster is cabled to a working receiver card on a PC backplane)


    (BUS.RESET)
    (PCBUS.OUTPUT 532 0)
    (AND (if (PCBUS.INPUT 532)=255
	     then (printout T 
		"The PC appears to be powered down or not connected -
please check and try again"
			    T)
		  NIL
	   else T)
	 (if (bind result for i from 0 to 255
		never (PCBUS.OUTPUT 532 i)
		      (if i~=result←(PCBUS.INPUT 532)
			  then (printout T "Receiver data register loopback failure: " .I2.16 i 
					 " in,"
					 41 .I2.16 result " out." T)
			       T))
	     then (if (NOT quietFlg)
		      then (printout T "Receiver data register loopback OK" T))
		  T)
	 (if (bind result for i from 0 to 255
		never (PCBUS.WRITEHL 15 (LOGOR (LLSH i 8)
					       i)
				     255)
		      (OR (if i~=result←(PCBUS.INPUT 533)
			      then (printout T "Receiver addr-hi register loopback failure: " .I2.16 
					     i " in," 44 .I2.16 result " out." T)
				   T)
			  (if i~=result←(PCBUS.INPUT 534)
			      then (printout T "Receiver addr-lo register loopback failure: " .I2.16 
					     i " in," 44 .I2.16 result " out." T)
				   T)))
	     then (if (NOT quietFlg)
		      then (printout T "Receiver address registers loopback OK" T))
		  T])

(PCMEM.PRELIMCHECK
  [LAMBDA (pageNumber quietFlg)                              (* ht: "26-May-85 18:17")

          (* * run some simple tests to validate the 1109-Busmaster-PCmemory paths and contents -
	  assumes that BUS.CHECKOUT and PCRCVR.CHECKOUT have been run successfully)


    (LET ((pages (for i from 0 to (IMAX (OR pageNumber 0)
					15)
		    when (AND (PROGN (PCBUS.WRITEHL i 0 0)
				     (PCBUS.READHL i 0)=0)
			      (PROGN (PCBUS.WRITEHL i 0 255)
				     (PCBUS.READHL i 0)=255))
		    collect i)))
      (if pages=NIL
	  then (printout T 
	  "There does not appear to be any memory connected to the PC in page address range 0 - "
			 (IMAX (OR pageNumber 0)
			       15)
			 "." T 
"Please check that the busmaster and PC are powered up and running correctly by calling (BUS.CHECKOUT) and (PCRCVR.CHECKOUT), and check that there is at least one memory board installed in the PC.  If that doesn't help, check the page address switches on the memory board to see that they are in the indicated range."
			 T)
	       NIL
	else (if (NOT quietFlg)
		 then (printout T "Memory there OK" T))
	     T])

(STATPRINT
  [LAMBDA (statbits)                                         (* ht: "22-Apr-85 15:33")
    (for i from 3 to 0 by -1 as label in '(test a16 a8 a0) do (printout NIL label ":"
									(LOGAND 1 (LRSH statbits i))
									,])
)
(FILESLOAD (SYSLOAD)
	   BUSEXTENDER)
(DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY 
(FILESLOAD (LOADCOMP)
	   BUSEXTENDER)
)
(PUTPROPS BUSMASTER COPYRIGHT ("Xerox Corporation" 1984 1985))
(DECLARE: DONTCOPY
  (FILEMAP (NIL (1881 8593 (BUS.RESET 1891 . 2511) (MBUS.INPUT 2513 . 2970) (MBUS.OUTPUT 2972 . 3386) (
MBUS.READ 3388 . 3655) (MBUS.READHL 3657 . 4167) (MBUS.WRITE 4169 . 4449) (MBUS.WRITEHL 4451 . 4918) (
PCBUS.INPUT 4920 . 5314) (PCBUS.OUTPUT 5316 . 5668) (PCBUS.READ 5670 . 5937) (PCBUS.READHL 5939 . 6387
) (PCBUS.READWORD 6389 . 7112) (PCBUS.WRITE 7114 . 7395) (PCBUS.WRITEHL 7397 . 7802) (PCBUS.WRITEWORD 
7804 . 8591)) (8668 15337 (PCBUS.READARRAY 8678 . 10978) (PCBUS.WRITEARRAY 10980 . 13297) (
PCBUS.TESTARRAY 13299 . 15335)) (15931 16494 (\BUSBLT.UFN 15941 . 16492)) (16555 20048 (BUSDMA.SETMODE
 16565 . 16990) (BUSDMA.SETPAGE 16992 . 17390) (BUSDMA.SETADDRESS 17392 . 17870) (BUSDMA.READADDRESS 
17872 . 18334) (BUSDMA.SETCOUNTER 18336 . 19027) (BUSDMA.READCOUNTER 19029 . 19537) (BUSDMA.MASK 19539
 . 19792) (BUSDMA.UNMASK 19794 . 20046)) (20102 22350 (BUSDMA.INIT 20112 . 21591) (BUSDMA.READTCBIT 
21593 . 22348)) (22876 26881 (\FUATran 22886 . 26059) (\SUATran 26061 . 26879)) (26940 36156 (
BUS.CHECKADDR 26950 . 27397) (BUS.CHECKCNT 27399 . 27865) (BUS.CHECKOUT 27867 . 30672) (BM16LOOP 30674
 . 31106) (BM8LOOP 31108 . 31544) (BMSTATLOOP 31546 . 32325) (MB.CHECKOUT 32327 . 32462) (
MBRCVR.CHECKOUT 32464 . 32603) (MBMEM.PRELIMCHECK 32605 . 32746) (PC.CHECKOUT 32748 . 33065) (
PCRCVR.CHECKOUT 33067 . 34638) (PCMEM.PRELIMCHECK 34640 . 35870) (STATPRINT 35872 . 36154)))))
STOP