; Spruce Ethernet "Ear" functions

	.titl	SpruceEarMl

;This module contains two accessible functions providing a very specific,
;minimal, Ethernet "ear" for Spruce.
;
;	hostId = ResetEther(mask)
;
;simply resets any current Ethernet activities and enables Ethernet interrupts to use mask
; (disabled if mask is 0, of course).
;It clears EPLoc and sets EHLoc to the program's host Alto's ID, which it also returns.
;
;	PollEther()
;
;must be called frequently from other parts of the system. When first called, it turns
;on the receiver to allow receipt of one packet (good or bad). When subsequently
;called, it examines any received packets, records spooling requests, and responds to
;status requests. On receipt of a status request, it starts the transmitter to send a
;pre-packaged packet to the immediate sender of the request, placing the request's
;source port in the reply's destination port. If a packet is in transit, it simply returns.
;In all other cases, it resets the interface then restarts the receiver. If a spool request is
;received, PollEther sets the static knockResult to true. The function will also set knockResult to other
;non-zero values if the ESC key is down during the call. This allows manual suspension of printing.
;
;Specifics:
;	status request: Pup, addressed to socket #21,  Pup type #200
;	spooling request: Pup, addressed to socket #20, Pup type #30
;	inPacket: external that must point to a 14 word vector
;	outPacket: external that must point to a lenOut word vector, whose fleece
;		is white... no, which contains an Ether-encapsulated status reply Pup
;		(from socket #21, Pup type #201)
;	lenOut: external that must contain the length of outPacket
;	knockResult: external, cleared with interrupts off by non-interrupt code, set here
;		when somebody knocks
;	tickCount: if non-zero, receiver will not be restarted after processing interrupt

; PollEther() may be the target of an interrupt channel. After processing each interrupt, it will
; either start the receiver (if tickCount le 0) or the transmitter. Or it may be called directly
; by non-interrupt code
; to determine recent Ether activity. Interleaving both methods is not suggested.

;	Ticker()
; Responsible for recording the presence of the "suspend printing" key (currently ESC)
; at 60 hz. intervals; when EarOn(2) has been called, responsible for provoking the Ether Ear
; to restart the receiver (see SpruceUtilsRes), by calling PollEther with tickCount le 0
; (See SpruceMeasure) if monitoring Dover engine control, record events via TickMeasure on
; every tick.

	EPLoc = 600
	EBLoc = 601
	EELoc = 602
	ELLoc = 603
	EICLoc = 604
	EIPLoc = 605
	EOCLoc = 606
	EOPLoc = 607
	EHLoc = 610

	.bext PollEther, ResetEther, Ticker
	.bext inPacket, outPacket, lenOut, knockResult, tickCount, tickCount0
	.bext measuring, TickMeasure

.srel
PollEther: .poll
ResetEther: .reset
Ticker: .ticker
.nrel

.ePLoc: EPLoc
.eBLoc: EBLoc
.eELoc: EELoc
.eLLoc: ELLoc
.eICLoc: EICLoc
.eIPLoc: EIPLoc
.eOCLoc: EOCLoc
.eOPLoc: EOPLoc
.eHLoc: EHLoc
.inPacket: inPacket
.outPacket: outPacket
.lenOut: lenOut

.reset:	inc	3,3
	sta	3,save3
	subc	1,1
	sta	1,@.eBLoc		;inhibit interrupts while resetting interface
	mov	0,1			;save mask
	lda	0,c3
	sio				;reset ether
	sta	1,@.eBLoc		;enable channel corresponding to mask
	lda	1,mrh
	and	1,0			;rh(startio 3 result) is host
	sta	0,@.eHLoc
	subc	1,1
	sta	1,@.ePLoc
	lda	3,@.inPacket
	sta	1,0,3
	jmp	@save3

.read:	sta	3,save3
	lda	0,d14			;read no more than 2+10+1 (+1?) words
	sta	0,@.eICLoc
	lda	3,@.inPacket
	sta	3,@.eIPLoc
	subc	0,0
	sta	0,@.ePLoc		;clear post location
	sta	0,0,3			;zero 1st packet word, as in-progress flag
	lda	0,c2
	sio				;turn on input interface
	jmp	@save3

.write:	mknil	0,0
	sta	3,save3
	lda	3,@.inPacket
	sta	0,0,3
	subc	0,0
	sta	0,@.ePLoc
	sta	0,@.eLLoc
	lda	0,@.lenOut
	sta	0,@.eOCLoc
	lda	0,@.outPacket
	sta	0,@.eOPLoc
	mkone	0,0
	sio				;write a packet
	jmp	@save3

; bring into range
.ticking: tickCount
.tickCount0: tickCount0
.knock: knockResult
mrh: m377: 377
save3: 0
c3:	3
c2: 	2
c1000:	1000
; end of range group

.poll:	inc	3,3
	sta	3,1,2
; will accept input done w/ good status, or input overrun w/ any status
	lda	0,@.ePLoc
	lda	1,mlh
	ands	0,1			;firmware status
	lda	3,c2
	se	3,1			;input overrun?
	 jmp	 ckdone		;no, check all done
	mknil	0,0			;overrun flag
	 jmp	posted
ckdone:
	snz	0,0			;has anything been posted?
	  jmp	 noPost		; no, check input in progress
	lda	1,m377
	sub	1,0,szr			;input done and status ok?
	  jmp	 restart		; no, forget it
posted:
	sta	0,overrun
	lda	3,@.inPacket
	lda	0,1,3			; packet type
	lda	1,c1000
	se	0,1			; a pup?
	 jmp	 restart		; no
	lda	0,7,3			; dest socket = 20 or 21?
	sz	0,0
	 jmp	 restart		; no
	lda	0,10,3
	lda	1,c20
	se	0,1			; dest socket = 20?
	 jmp	 ck21			; no, check for status request (socket 21)
	lda	0,3,3			; pup type = 30?
	lda	1,mrh
	and	1,0
	lda	1,c30
	mknil	3,3			; resultis true
	sne	0,1
	 sta	 3,@.knock		;main result: someone knocked
	jmp	restart
ck21:
	inc	1,1			; (c21)
	isz	overrun		; (input overflow test)
	se	0,1			; (dest socket = 21 test)
	 jmp	 restart		; input overflowed, or dest socket ne 21
	lda	0,3,3			; pup type = 200?
	lda	1,mrh			; (already loaded)
	and	1,0
	lda	1,c200
	se	0,1
	 jmp	 restart		; no
	sta	2,save2
	lda	2,@.outPacket
	lda	0,4,3			; id of out = id of in
	sta	0,4,2
	lda	0,5,3
	sta	0,5,2
	lda	0,11,3			; dest sock of out = source sock of in
	sta	0,6,2
	lda	0,12,3
	sta	0,7,2
	lda	0,13,3
	sta	0,10,2
	lda	0,0,3			; this host,, directly connected host
	lda	1,mrh			; reject if sender was anonymous
	and#	1,0,snr
	 jmp	 load2
	movs	0,0			; directly connected host,, this host
	and#	1,0,snr		; reject if request was broadcast
	 jmp	 load2
	sta	0,0,2			; output ether encapsulation
	jsr	.write
load2:	lda	2,save2
	jmp	ret			; resultis false
noPost:
	lda	3,@.inPacket
	lda	0,0,3
	sz	0,0			;input in progress?
	 jmp	 ret			;yes, just go away
; start or restart receiver unless inhibited by tickCount
restart:
	lda	0,@.ticking
	negl#	0,0,snc		;sgz -- don't restart if tickCount positive
	jsr	.read			;Read a packet
ret:
	jmp	@1,2

c200:	200
c20:	20
d14:	16
c30:	30
mlh: 177400
save2: 0
tsave3: 0
overrun: 0
mb1: 40000				; ESC is bit 1
kbadr2: 177036				; address of kbadr+2
.measuring: measuring
.TickMeasure: TickMeasure

.ticker:	sta	3,tsave3
	lda	0,@kbadr2		;if ESC down, record as a "knock" -- should suspend
	lda	1,mb1			;mask for bit 1
	and#	0,1,snr		;key down == bit 0
	 dsz	 @.knock		;will become other than -1 quickly, distinguished from ether kind
	 mov#	 0,0			; in case?
	lda	0,@.measuring
	mov#	0,0,snr
	 jmp	 tickTest
	jsrii	.TickMeasure		;take measurements
	0
tickTest:
	dsz	@.ticking		;time to query ether?
	 jmp	 tQuit			;no, quit
	jsr	.poll			;query, and restart receiver
	0
	lda	3,@.tickCount0
	sta	3,@.ticking
tQuit:
	lda	3,tsave3
;;	sub	0,0		; reinstate if any question about progress thru interrupt code
;;	sta	0,tsave3
	jmp	1,3
	
;; DCS, September 2, 1977  4:22 PM, created
;; September 6, 1977  9:48 PM, status request -> reply, data packet 0 -> true return
;; May 11, 1978  6:24 PM, modify to allow running at interrupt level (result to knockResult)
;; May 12, 1978  11:20 AM, add tickCount control of ear duty cycle
;; July 23, 1978  3:34 PM, repair bug: status request responses broadcast anonymously!
;; August 28, 1978  4:20 PM, ESC key down during poll yields positive knock response
;; August 28, 1978  8:10 PM, put Ticker in ASM, for .3% print efficiency gain during scan/show
;; September 4, 1978  5:23 PM, ESC key causes non- -1 result -- more urgent
;; March 12, 1979  10:16 PM, calls TickMeasure if measuring, see SpruceMeasure, by Swinehart
;; 
	.end