; KeyStreamsA.asm -- Interrupt driven keyboard handler
;	companion file is KeyStreamsB.bcpl
; Copyright Xerox Corporation 1979
; Last modified March 15, 1979  4:36 PM by Boggs

	.titl KBHan

; outgoing
.ent kbInterrupt	; Interrupt routine
.bext CursorLink	; non zero => link the mouse to the cursor
.bext kbInterruptReMask
.bext kbUserProc
.bext kbUserProcStack
.bext kbTable

; incoming
.bext OsBuffer

StkMin = 335

	.srel

kbInterrupt:		.kbInterrupt
kbInterruptReMask:	0
CursorLink:		-1	; non zero => link mouse to cursor
kbUserProc:		0	; address of user procedure
kbUserProcStack:	0	; stack for same (non-zero => use proc)
kbTable:		Transition

	.nrel

mouseX:		424		; address of mouse x coord
mouseY:		425		; address of mouse y coord
cursorX:	426		; address of cursor x coord
cursorY:	427		; address of cursor y coord

XMax:		606.-16.	; maximum X value for mouse
YMax:		808.-16.	; maximum Y value for mouse

IMASK:		kbInterruptReMask
.CursorLink:	CursorLink

kbPC:		0		; interrupted program's state
kbCry:		0
kbAC0:		0
kbAC1:		0
kbAC2:		0
kbAC3:		0
kbActive:	0

.Active:	453
.IntPC:		500

;----------------------------------------------------------------------------
.kbInterrupt:			; 16.666 ms vertical field interrupt
;----------------------------------------------------------------------------
	sta 0 kbAC0
	sta 1 kbAC1
	sta 2 kbAC2
	sta 3 kbAC3
	movr 3 3
	sta 3 kbCry
	lda 0 @.IntPC
	sta 0 kbPC		; save interrupt PC
	lda 0 @.Active
	lda 1 @IMASK		; save old active and mask out lower
	and 0 1			; priority interrupts while we run.
	sta 1 @.Active
	sta 0 kbActive
	eir

; Update the cursor from the mouse unless CursorLink is 0.
	lda 0 @.CursorLink
	snz 0 0
	 jmp FL0		; don't update coordinates

	lda 0 @mouseX		; if mouseX < 0 then mouseX = 0
	sp 0 0
	 mkzero 0 0
	lda 1 XMax		; if mouseX > XMax then mouseX = XMax
	 subz# 0 1 snc
	mov 1 0
	sta 0 @cursorX		; store X coordinate for cursor
	sta 0 @mouseX		; keep mouse incremental modulo screen limits
	sta 0 userX

	lda 0 @mouseY		; if mouseY < 0 then mouseY = 0
	sp 0 0
	 mkzero 0 0
	lda 1 YMax		; if mouseY > YMax then mouseY = YMax
	subz# 0 1 snc
	 mov 1 0
	sta 0 @cursorY		; store Y coordinate for cursor
	sta 0 @mouseY		; keep mouse incremental modulo screen limits
	sta 0 userY


; Now begin keyboard processing
FL0:	jsr P			; locate ourselves
P:	lda 2 CM5		; count of times to go through loop
lop:	lda 0 @Addrs+4-P,3	; get keys bits
	com 0 0			; 1's imply keys down
	lda 1 Masks+4-P,3	; ignore some bits
	and 1 0
	lda 1 State+4-P,3	; and current view of down keys
	se 0 1			; see if they differ
	 jmp dif		; yes
	neg 3 3			; move 3 down one
	com 3 3
	inc 2 2 szr		; go around loop 5 times
	 jmp lop
	jmp NoTrans		; no differences

difadr:		.blk 1
CM5:		-5

; -------------------- K E Y B O A R D   T A B L E  --------------------------
; The format of kbTab through userY is published in SysDefs.d
kbTab:		jsr 0,3		; return address
Transition:	.blk 1		; transition index + #100000 if going down,
				;  or #40000 if coming up
State:		.blk 5		; 5 words of current keys state
userX:		.blk 1		; cursor coordinates
userY:		.blk 1

Addrs:		177034		; addresses of keyboard words
		177035
kbdAd2:		177036
kbdAd3:		177037
		177030		; keyset and mouse.

Masks:		177777		; 0 => ignore transition for this key
		177777
		177777
		177777
		377

downind:	100000
upind:		40000

Dismiss:dir
	lda 0 kbPC
	sta 0 @.IntPC
	lda 0 kbActive
	sta 0 @.Active
	lda 0 kbAC0
	lda 1 kbAC1
	lda 2 kbAC2
	lda 3 kbCry
	movl 3 3
	lda 3 kbAC3
	bri

dif:	sta 3 difadr		; remember where we found difference
	com 2 2			; neg and then sub 1 (now range 0 to 4)
	addzl 2 2		; multiply by 16
	addzl 2 2
	mov 0 3			; 1 = old bits; 0 = new bits
	andzl 1 3		; xor function: 0 ← 1 xor 0
	add 1 0
	sub 3 0

	subz 3 3 skp		; 0 has 1's where transitions
lop2:	inc 2 2			; bump bit index
	movr 3 3		; assemble a bit to match
	movl 0 0 snc		; check for changed bit
	 jmp lop2		; not found yet

; AC3 = bit that changed
; AC2 = index of bit that changed
; AC1 = old bits (still) 1's = down keys
	lda 0 downind		; down indicator for transition
	and# 3 1 szr		; check which way key went
	 sub 3 1 skp		; up - turn off bit
	 add 3 1 skp		; down - turn on bit
	 lda 0 upind		; up - up indicator
	add 0 2			; add indication to transition index
	adc 0 0			; for return test below
	lda 3 difadr
	sta 1 State+4-P,3	; save new down bits
NoTrans: sta 2 Transition	; save transition indication
	lda 2 @.UserProcStack
	snz 2 2
	 jmp irecord		; no procedure -- I record it.
	lda 0 StkMin		; save bottom of stack word
	sta 0 StkMinSav
	mkzero 0 0
	sta 0 StkMin
	jsr kbTab		; get table address
	mov 3 0
	lda 3 @.UserProc
	jsr 0,3			; go call him. ac0 => table
	 1
	lda 1 StkMinSav
	sta 1 StkMin
irecord:lda 1 Transition
	movzl 1 1 szc		; if no down transition
	snz 0 0			; or if user returned false,
	 jmp Dismiss		; nothing more to do
	jsr .+1
Q:	movzr 1 1		; get index back again (sans down bit)
	add 1 3

; Get directive about what to do.
; Left byte has shift value; right unshifted value.
; Bit 0 is on if it's a letter.
	lda 0 .kbTransitionTable-Q,3
	snz 0 0
	 jmp Dismiss		; not a key OS cares about!
	lda 1 @kbdAd3
	lda 2 c200		; <Lock>
	and 1 2 szr
	 jmp Shift		; not locked -- check shift
	movl# 0 0 szc		; send shifted character if it's a letter
	 jmp SendSh
Shift:	lda 2 c10		; <Right-Shift>
	and 1 2 snr
	 jmp SendSh
	lda 1 @kbdAd2
	lda 2 c100		; <Left-Shift>
	and 1 2 snr
SendSh:	 movs 0 0		; send character shifted
	lda 3 c177
	and 3 0
	lda 1 @kbdAd2
	lda 2 c4000		; <Control>
	lda 3 c37		; control character (0-37B)
	and 1 2 snr
	 and 3 0
	sta 0 char		; save for a moment
	lda 3 @.OsBuffer	; get pointer to buffer control
	lda 2 2,3		; "in" pointer
	inc 2 1			; 1 = newIn
	lda 0 1,3		; last
	sne 1 0
	 lda 1 0,3		; first
	lda 0 3,3		; out
	sne 1 0
	 jmp Dismiss		; buffer full
	lda 0 char
	sta 0 0,2		; store item
	sta 1 2,3		; new "in" pointer
	jmp Dismiss

.OsBuffer:	OsBuffer
.UserProc:	kbUserProc
.UserProcStack:	kbUserProcStack

StkMinSav:	.blk 1
char:		.blk 1

c4000:		4000		; <Control>
c200:		200		; <Lock>
c177:		177
c100:		100		; <Left-Shift>
c37:		37
c10:		10		; <Right-Shift>


;Tables for char code conversion

.ent kbTransitionTable

	.srel

kbTransitionTable:	.kbTransitionTable

	.nrel

; structure KBKEY:	// in SysDefs.d
; [
; Letter bit		// True if char is a letter (use for lock interp)
; ShiftCode bit 7	// Code to use if shift is on
; NormCode bit 8	// Code to use otherwise
; ]

.kbTransitionTable:
	     45B7+65	; % 5	177034
	     44B7+64	; $ 4
	    176B7+66	; ~ 6
	1B0+105B7+145	; E e
	     46B7+67	; & 7
	1B0+104B7+144	; D d
	1B0+125B7+165	; U u
	1B0+126B7+166	; V v
	     51B7+60	; ) 0
	1B0+113B7+153	; K k
	     30B7+55	; ` -
	1B0+120B7+160	; P p
	     77B7+57	; ? /
	    174B7+134	; | \
	    140B7+12	; ACCENT GRAVE LF
	     10B7+10	; BS

	     43B7+63	; # 3	177035
	    100B7+62	; @ 2
	1B0+127B7+167	; W w
	1B0+121B7+161	; Q q
	1B0+123B7+163	; S s
	1B0+101B7+141	; A a
	     50B7+71	; ( 9
	1B0+111B7+151	; I i
	1B0+130B7+170	; X x
	1B0+117B7+157	; O o
	1B0+114B7+154	; L l
	     74B7+54	; < ,
	     42B7+47	; " '
	    175B7+135	; } ]
		    0	; blank-middle or FR4
		    0	; blank-top or BW

	     41B7+61	; ! 1	177036
	     33B7+33	; ESCAPE
	     11B7+11	; TAB
	1B0+106B7+146	; F f
		    0	; CONTROL
	1B0+103B7+143	; C c
	1B0+112B7+152	; J j
	1B0+102B7+142	; B b
	1B0+132B7+172	; Z z
		    0	; SHIFT
	     76B7+56	; > .
	     72B7+73	; : ;
	     15B7+15	; CR
	    136B7+137	; ↑ ←
	    177B7+177	; DEL (FL1)
		    0	; FL3

	1B0+122B7+162	; R r	177037
	1B0+124B7+164	; T t
	1B0+107B7+147	; G g
	1B0+131B7+171	; Y y
	1B0+110B7+150	; H h
	     52B7+70	; * 8
	1B0+116B7+156	; N n
	1B0+115B7+155	; M m
		    0	; LOCK
	     40B7+40	; SPACE
	    173B7+133	; { [
	     53B7+75	;  +=
		    0	; SHIFT
		    0	; blank-bottom or FR1
		    0	; FL4
		    0	; FR5

	.blk 16.	; zeroes for keyset keys

	.END