(FILECREATED "16-Sep-85 14:43:58" {DSK}<DSK>HTHOMPSON>DSL>BUSMASTER.;14 38057  

      changes to:  (FNS \FUATran)

      previous date: "13-Sep-85 10:58:27" {DSK}<DSK>HTHOMPSON>DSL>BUSMASTER.;13)


(* 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 - declares are this way so 
	   loadcomp of the compiled file will see these)
	(DECLARE: DOCOPY (DECLARE: EVAL@LOAD EVAL@COMPILE
				   (PROP DOPVAL \BUSBLTIN \BUSBLTINBYTES \BUSBLTINSWAPBYTES 
					 \BUSBLTOUT \BUSBLTOUTBYTES \BUSBLTOUTNYBBLES 
					 \BUSBLTOUTSWAPBYTES \BUSBLTCHECK \BUSBLTCHECKBYTES 
					 \BUSBLTCHECKSWAPBYTES)
				   (MACROS BUSDMA.FASTUPDATEADDR BUSDMA.SLOWUPDATEADDR)
				   (PROP ARGNAMES BUSDMA.FASTUPDATEADDR BUSDMA.SLOWUPDATEADDR)))
	(FNS \FUATran \SUATran)
	[DECLARE: DOCOPY (DECLARE: EVAL@COMPILE DONTEVAL@LOAD (* this is in here because the 
								 BUSDMA.FASTUPDATEADDR macro includes 
								 calls to BUSEXTENDER-defined macros 
								 in its output)
				   (FILES (LOADCOMP)
					  BUSEXTENDER.DCOM)
				   (* this next is a horrible kludge to allow LOADCOMPing of the 
				      compiled file to work properly)
				   (P (COND ((NULL (GETD '\FUATran))
					     (LOADFNS '(\FUATran \SUATran)
						      (OR (FINDFILE 'BUSMASTER.DCOM)
							  'BUSMASTER]
	(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)
	(* 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 (P (RESETSAVE DWIMIFYCOMPFLG T)
							   (COND ([NOT (OR (GETP 'ARRAYBASE
										 'DMACRO)
									   (GETP 'ARRAYBASE
										 'MACRO]
								  (HELP 
			       "ARRAYBASE needed - load macro def'n from somewhere and/or RETURN"])



(* 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 - declares are this way so loadcomp 
of the compiled file will see these)

(DECLARE: DOCOPY 
(DECLARE: EVAL@LOAD EVAL@COMPILE 

(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))

(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: "16-Sep-85 14:41")
    (if (AND 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.)


	 (LET ((chanCode (CONSTANTEXPRESSIONP dmaChannel))
	    [form (BQUOTE (PROGN (PROGN (BX.OUTPUT 12 7)
					(BX.OUTPUT 8 4)
					(BX.OUTPUT 0 5))
				 (SETQ , currentAddress (LOGOR (LRSH (PROGN (BX.OUTPUT chanCode 7)
									    (BX.OUTPUT 4 4)
									    (BX.OUTPUT 0 5)
									    (BX.INPUT 5))
								     1)
							       (LLSH (PROGN (BX.OUTPUT chanCode 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))
								  chanCode)))
					      then (SETQ , wrapped 'DoubleWrap)
					    else , wrapped)
				   else (if [SETQ , wrapped
					      (NOT (ZEROP (LOGAND (PROGN (BX.OUTPUT 8 7)
									 (BX.OUTPUT 4 4)
									 (BX.OUTPUT 0 5)
									 (BX.INPUT 5))
								  chanCode]
					    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 chanCode 7)
								       (BX.OUTPUT 4 4)
								       (BX.OUTPUT 0 5)
								       (BX.INPUT 5))
								1)
							  (LLSH (PROGN (BX.OUTPUT chanCode 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))
									 chanCode)))
						     then (ERROR "double wrap 2" chanCode)
						   else T]
	    else
	    (ERROR "Invalid arguments - must be a number and two atoms" (LIST dmaChannel 
									      currentAddress wrapped))
	    )
	   (if chanCode
	       then (SUBST (LLSH chanCode:1 1)
			   'chanCode
			   form)
	     else (BQUOTE (LET ((chanCode (LLSH , dmaChannel 1)))
			    , form])

(\SUATran
  [LAMBDA (dmaChannel currentAddress wrapped)                (* ht: "12-Sep-85 12:09")

          (* * update the address and wrapped flags safely)


    (if (AND currentAddress wrapped (LITATOM currentAddress)
	     (LITATOM wrapped))
	then [BQUOTE (LET ((channel , dmaChannel)
			tcBit)
		       (BUSDMA.MASK channel)
		       (SETQ , currentAddress (BUSDMA.READADDRESS channel))
		       (SETQ tcBit (BUSDMA.READTCBIT channel T))
		       (PROG1 (SETQ , wrapped (if (AND , wrapped tcBit)
						  then 'DoubleWrap
						else (OR , wrapped tcBit)))
			      (BUSDMA.UNMASK channel]
      else (ERROR "Invalid arguments - currentAddress and wrapped must be atoms" (LIST currentAddress 
										       wrapped])
)
(DECLARE: DOCOPY 
(DECLARE: EVAL@COMPILE DONTEVAL@LOAD 
(FILESLOAD (LOADCOMP)
	   BUSEXTENDER.DCOM)

[COND ((NULL (GETD '\FUATran))
       (LOADFNS '(\FUATran \SUATran)
		(OR (FINDFILE 'BUSMASTER.DCOM)
		    'BUSMASTER]
)
)
(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 (ILEQ NBYTES 0) or (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)
)



(* 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: "13-Sep-85 10:41")

          (* * 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)


    (if (AND (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))
      else (SELECTQ (LOGAND (BX.INPUT 4)
			    63679)
		    ((47287 14519 14518 47286))
		    (printout T .PARA 0 0
			      '(There is some indication that the failure indicated above results 
				      from the fact that 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])

(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: "13-Sep-85 10:48")

          (* * some simple loop back checks to see if the busmaster is cabled to a working receiver card on a PC backplane -
	  assumes BUS.CHECKOUT has been run successfully.)


    (BUS.RESET)
    (if (AND (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))
      else (PCBUS.OUTPUT 532 0)
	   (if (PCBUS.INPUT 532)=255
	       then (printout T .PARA 0 0
			      '(There is some indication that the failure indicated above results 
				      from the fact that the PC is powered down or not connected - 
				      please check and try again)
			      T))
	   NIL])

(PCMEM.PRELIMCHECK
  [LAMBDA (pageNumber quietFlg)                              (* ht: "13-Sep-85 10:57")

          (* * 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 .PARA 0 0
			 '(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 - page numbers " .PPVTL pages T))
	     pages])

(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 
(RESETSAVE DWIMIFYCOMPFLG T)
(COND ([NOT (OR (GETP 'ARRAYBASE
		      'DMACRO)
		(GETP 'ARRAYBASE
		      'MACRO]
       (HELP "ARRAYBASE needed - load macro def'n from somewhere and/or RETURN")))
)
(PUTPROPS BUSMASTER COPYRIGHT ("Xerox Corporation" 1984 1985))
(DECLARE: DONTCOPY
  (FILEMAP (NIL (2712 9422 (BUS.RESET 2722 . 3340) (MBUS.INPUT 3342 . 3799) (MBUS.OUTPUT 3801 . 4215) (
MBUS.READ 4217 . 4484) (MBUS.READHL 4486 . 4996) (MBUS.WRITE 4998 . 5278) (MBUS.WRITEHL 5280 . 5747) (
PCBUS.INPUT 5749 . 6143) (PCBUS.OUTPUT 6145 . 6497) (PCBUS.READ 6499 . 6766) (PCBUS.READHL 6768 . 7216
) (PCBUS.READWORD 7218 . 7941) (PCBUS.WRITE 7943 . 8224) (PCBUS.WRITEHL 8226 . 8631) (PCBUS.WRITEWORD 
8633 . 9420)) (9497 16166 (PCBUS.READARRAY 9507 . 11807) (PCBUS.WRITEARRAY 11809 . 14126) (
PCBUS.TESTARRAY 14128 . 16164)) (17323 21239 (\FUATran 17333 . 20413) (\SUATran 20415 . 21237)) (21464
 22027 (\BUSBLT.UFN 21474 . 22025)) (22088 25572 (BUSDMA.SETMODE 22098 . 22523) (BUSDMA.SETPAGE 22525
 . 22923) (BUSDMA.SETADDRESS 22925 . 23403) (BUSDMA.READADDRESS 23405 . 23867) (BUSDMA.SETCOUNTER 
23869 . 24552) (BUSDMA.READCOUNTER 24554 . 25062) (BUSDMA.MASK 25064 . 25316) (BUSDMA.UNMASK 25318 . 
25570)) (25626 27870 (BUSDMA.INIT 25636 . 27111) (BUSDMA.READTCBIT 27113 . 27868)) (28022 37686 (
BUS.CHECKADDR 28032 . 28479) (BUS.CHECKCNT 28481 . 28947) (BUS.CHECKOUT 28949 . 31930) (BM16LOOP 31932
 . 32364) (BM8LOOP 32366 . 32802) (BMSTATLOOP 32804 . 33583) (MB.CHECKOUT 33585 . 33720) (
MBRCVR.CHECKOUT 33722 . 33861) (MBMEM.PRELIMCHECK 33863 . 34004) (PC.CHECKOUT 34006 . 34323) (
PCRCVR.CHECKOUT 34325 . 36083) (PCMEM.PRELIMCHECK 36085 . 37400) (STATPRINT 37402 . 37684)))))
STOP