; Utility.asm.  Assembly language utility routines
; Last change July 21, 1981  12:38 PM by Beau Sheil
; Tone change March 17, 1981  6:35 PM by Beau Sheil
; Last change February 4, 1981  10:46 PM by Beau Sheil
; Chord change November 18, 1980  10:30 AM by Beau Sheil
; Previous last change November 11, 1980  5:36 PM by Beau Sheil

	.ENT	VP, VP2, Bytes2, EqNIL, EmAddr
	.ENT	MkSmallPos, MkSmallNeg, Smallp, SmallUnbox, SmallNegUnbox
	.ENT	UCase, ReadClock, TimeSince
	.ENT	MachineType, Version, Serial, DoBitBlt
; procedures used
	.EXTN	RAIDCode
; statics used
	.EXTD	RMSK, lvVPtr, VPtr0, VPtr1
	.EXTN	EmulatorSpace
; new opcodes defined
	.DUSR	SWAT = #77400		; For where one shouldnt go

	.SREL
VP:		.vp
VP2:		.vp2
Bytes2:		.by2
EqNIL:		.eqn
EmAddr:		.ea
MkSmallPos:	.msp
MkSmallNeg:	.msn
Smallp:		.smp
SmallUnbox:	.smunb
SmallNegUnbox:	.smnunb
UCase:		.uc
ReadClock:	.rclk
TimeSince:	.tme
MachineType:	.mt
Version:	.vers
Serial:		.ser
DoBitBlt:	.dbbt

	.NREL

; VP(lvAddr) -> virtual page in ac0
.vp:	sta	3,1,2
	mov	0,3
	lda	0,0,3
	lda	1,1,3
	jmp	vp

; VP2(vahi, valo) -> virtual page in ac0
.vp2:	sta	3,1,2
vp:	lda	3,RMSK
	ands	3,0
	lda	3,LMSK
	ands	3,1
	add	1,0
	lda	3,1,2
	jmp	1,3
LMSK:	177400

; Bytes2(hibyte, lobyte) -> word {hibyte,lobyte} in ac0
.by2:	sta	3,1,2
	lda	3,RMSK
	ands	3,0
	and	3,1
	add	1,0
	lda	3,1,2
	jmp	1,3

; Smallp(lvN) -> whether lvN is a SMALLP
.smp:	sta	3,1,2
	mov	0,3
	lda	0,0,3
	lda	1, .smallp
	sub#	0,1,snr
	jmp	.true
	lda	1, .smalln
	sub#	0,1,snr
	jmp	.true
	jmp	.false		; .true and .false restore ac3

; SmallNegUnbox(lvN) unbox if smallnegp, otherwise error
.smnunb: lda	1, .smalln
	 jmp	.smun2

; SmallUnbox(lvN) unbox if smallposp, otherwise error
.smunb:	lda	1, .smallp
.smun2: sta	3,1,2
	mov	0,3
	lda	0,0,3
	sub#	0,1,szr
	jmp	.notsm
	lda	0,1,3
	lda	3,1,2
	jmp	1,3
.notsm: mov	3, 1		; original arg
	jsr	.smerr		; get addr of error msg into ac3
	.TXTM	B
	.TXT	"Not a small posp"
.smerr:	mov	3, 0		; addr of error msg
	jsrii	.raid
		2
	SWAT			; cant return

.raid:		RAIDCode
.smallp:	16		;hi order word of small positive numbers
.smalln:	17		;hi order word of small negative numbers
.es:		EmulatorSpace

; EmAddr(n)			; makes an emulator ptr of n
.ea:	lda	1,@.es
	jmp	.ms

; MkSmallNeg(n)			;n is treated as UNSIGNED
.msn:	lda	1,.smalln
	jmp	.ms

; MkSmallPos(n)
.msp:	lda	1,.smallp
.ms:	sta	0,VPtr1
	sta	1,VPtr0
	lda	0,lvVPtr
	jmp	1,3

; EqNIL(ptr) - NIL is <0><0> by definition
.eqn:	sta	3,1,2
	mov	0,3
	lda	0,0,3
	mov	0,0,szr
	jmp	.false
	lda	0,1,3
	mov	0,0,szr
.false:	sub	0,0,skp		; false is 0
.true:	adc	0,0		; true is -1
	lda	3,1,2
	jmp	1,3

; UCase(cc)			;returns Upper Case version of character code
.uc:	lda	1,.uca		;range check
	adcz#	0,1 szc		;skip if ac0 ge 'a
	jmp	1,3		;other characters are OK
	lda	1,.ucz
	adcz#	1,0 szc		;skip if ac0 le 'z
	jmp	1,3		;other characters are OK
	lda	1,.uc0
	and	1,0		;turn off high bit
	jmp	1,3
.uc0:	#337			; select only low order byte, turn off bit 2
.uca:	"a
.ucz:	"z

; ReadClock(ptr) Reads ucode clock into ptr!0 and ptr!1
.rclk:	sta	3,1,2
	mov	0,3
	RCLK
	sta	0,0,3
	sta	1,1,3
	lda	3,1,2
	jmp	1,3

; TimeSince(ptr) -> time in msec since time in ptr!0 and ptr!1
.tme:	sta	3,1,2
	mov	0,3
	RCLK			; AC0/1 ← current time
;	sta	0, savAC0	; *** Strictly for debugging ***
;	sta	1, savAC1	; *** Strictly for debugging ***
	sta	2, savAC2
	lda	2,0,3		; AC2/3 ← start time
	lda	3,1,3
	subz	3,1 szc		;
	sub	2,0 skp		; AC0/1 ← AC0/1 - AC2/3
	adc	2,0		;
	lda	2, msc1
	div			; convert to msec
	SWAT			; interval too long
	lda	2, msc2
	subz#	2,0 szc
	inc	1,1		; round up
	mov	1,0
	lda	2, savAC2
	lda	3,1,2
	jmp	1,3
;savAC0:	0			; *** Strictly for debugging ***
;savAC1:	0			; *** Strictly for debugging ***
savAC2:	0
msc1:	1681.			; 1msec / .595 usec
msc2:	840.			; half the above

; MachineType() = Version() rshift 12 = Dorado or Dolphin
.mt:	VERS
	lda	1, hi4
	and	1, 0
	60004			; cycle4 - moves [0:3] -> [12:15]
	jmp	1,3
hi4:	170000			; selects bits [0:3]

; Version()
.vers:	VERS
	jmp	1,3

; Serial()	returns the Alto Serial number
.ser:	sub	0,0
	SIO
	lda	1, RMSK		; we want the right 8 bits only
	and	1, 0
	jmp	1,3

; DoBitBlt(Ebbt)
.dbbt:	sta	3,1,2
	sta	2, bbtAC2	; *** Strictly for debugging ***
	mov	2,1
	mov	0,2		; AC2← BitBltTable
	sta	1,1,2		; old AC2 into word 1 of BBT
	sub	1,1		; AC1← 0
	BITBLT
	lda	2,1,2		; restore stack
	sub	1,1		; *** Strictly for debugging ***	
	sta	1, bbtAC2	; *** Strictly for debugging ***
	lda	3,1,2
	jmp	1,3
bbtAC2:	0			; *** Strictly for debugging ***

; The lines labelled as debugging save the old stack pointer to help us
; chase an outstanding BitBlt bug. They should be removed when it is found.
; Beau 27/Jun/80

; The following are arithmetic routines used ONLY by RAID number printing
; They are here for convenience as I dont want to use a whole file for them

	.ENT	Divide, Negate

	.SREL
Divide:		DIVD
Negate:		NEGTE

	.NREL

; NEGATE (VEC)
NEGTE:	STA	3,1,2
	MOV	0,3
	LDA	0,0,3
	LDA	1,1,3
	NEG	1,1,SNR
	NEG	0,0,SKP
	COM	0,0
	STA	0,0,3
	STA	1,1,3
	MOV	3,0
	LDA	3,1,2
	JMP	1,3

; DIVIDE(a2, b) -> r, leaves q in a

DIVD:	STA	3,1,2
	STA	2,TEMP
	MOV	0,3
	MOV	1,2
	SUB	0,0
	LDA	1,0,3
	DIV
	JMP	.+1
	STA	1,0,3	; Q0
	LDA	1,1,3
	DIV
	JMP	.+1
	STA	1,1,3	; Q1
	LDA	2,TEMP
	LDA	3,1,2
	JMP	1,3

TEMP:	0
	.END