	.title	getput
; vers. 1.0 Glenn C. Everhart 1989
; get_in and put_out functions for LZW compress/decompress
; These are the "hard" codes.
; They assume a structure passed to them of form:
;stream:  .word nextbyte
;	.word size_array-used
;	.byte nextbit,sizbit	;so nextbyte-nextbit should be less than size-arr-sizbit
;	.blkb	nnnn	;nnnn big "enough"
;When a clear code is put out (code bigger than fits in outbits) then put
; zeroes nextbyte and nextbit. These represent the starting BITS in the array
; to be used for the next in or out data move.
;
; Calls:
;  status=put_out(outbits,stream,code)
;  status=get_in(outbits,stream,code)
;
; get_in will return error when attempting to grab a char beyond end of nextbyte/nextbit
; n.b. - ALWAYS call with big enough buffers not to overflow!
nxtbyt:	.long	0	;next byte local store
nxtbit:	.long	0	;next bit local store
code:	.long	0	;code local store
bits:	.long	0	;char size local store
strma:	.long	0	;address of "Stream" arg local copy
siz:	.long	0	;array size
; table of max char sizes for powers of 2
bitmax:	.long	1,2,4,8,16,32,64
	.long	128,256,512,1024,2048,4096,8192,16384,32768,65536
	.globl	put_out,get_in
;put a character code of bits size into data stream
	.entry put_out,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10>
puto:	movl	@4(ap),bits	;get bit count
	movl	8(ap),strma	;save address of stream
	movl	@12(Ap),code	;get code
	movl	strma,r3
	movzwl	(r3)+,r4	;get next byte within array
	movzwl	(r3)+,siz	;save size
	movzbl	(r3)+,r5	;get byte number
	movl	r5,nxtbit	;save bit number also
	tstb	(r3)+		;pass size-bit
	addl2	r3,r4		;get abs address
	movl	r4,nxtbyt	;save locally
;first test for code larger than fits into bits
	movl	bits,r6		;prepare to index
	cmpl	code,bitmax[r6]	;see if code is = or > max
	blssu	1$		;if lower, normal
; this is a clear operation. Zero the offsets and return
	movl	strma,r3	;get context address
	clrl	(r3)+		;clear byte offset/size max
	clrw	(r3)+		;zero bit offset/size bit offset
	movl	#ss$_normal,r0	;say all well
	ret
1$:
;use bit-field instructions to store data.
	movl	nxtbyt,r7
	insv	code,nxtbit,bits,(r7)
	addl3	bits,nxtbit,r7	;compute new next bit
2$:
	cmpl	r7,#8		;.ge.8 bits off?
	blss	3$		;if not, go finish up
	incw	@strma		;bump context byte offset word
	incl	nxtbyt		;bump next-byte counter
	subl2	#8,r7		;subtract off 8 bits
	brb	2$		;go for it again
3$:	movl	r7,nxtbit	;save next bit now
;store context info for next time
	movl	strma,r3
	movw	(r3)+,(r3)+	;Copy size used to array size area
	movb	nxtbit,(r3)+	;store next bit for next time.
	movb	nxtbit,(r3)+	;store next bit for next time.
	movl	#ss$_normal,r0	;say all well
	ret
;get a character code of bits size from data stream
	.entry get_in,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10>
geti:	movl	@4(ap),bits	;get bit count
	movl	8(ap),strma	;save address of stream
;	movl	@12(Ap),code	;get code
	movl	strma,r3
	movzwl	(r3)+,r4	;get next byte within array
	movzwl	(r3)+,siz	;store size of array locally
	addl2	r3,r4		;get abs address
	addl2	#2,r4		;(pass bit counter bytes)
	movl	r4,nxtbyt	;save locally
	movzbl	(r3)+,r5	;get bit number
	movl	r5,nxtbit	;save bit number also

;use bit-field instructions to store data.
	movl	nxtbyt,r7
;see if we got to the end of the buffer
	movl	strma,r3
	cmpw	(r3)+,(r3)+	;use pointer at or past end?
	blssu	100$		;if not, normal, so branch
	cmpb	(r3)+,(r3)+	;bit number before end?
	blssu	100$		;yes, branch
;past end of data. Return error flag
	movl	#4,r0
	ret
100$:
	extzv	nxtbit,bits,(r7),code
	movl	code,@12(ap)	;send code to caller
	addl3	bits,nxtbit,r7	;compute new next bit
2$:
	cmpl	r7,#8		;.ge.8 bits off?
	blss	3$		;if not, go finish up
	incw	@strma		;bump context byte offset word
	incl	nxtbyt		;bump next-byte counter
	subl2	#8,r7		;subtract off 8 bits
	brb	2$		;go for it again
3$:	movl	r7,nxtbit	;save next bit now
;store context info for next time
	movl	strma,r3
	tstw	(r3)+		;pass next-byte
	tstw	(r3)+		;pass size in bytes
	movb	nxtbit,(r3)	;store next bit for next time.
	movl	#ss$_normal,r0	;say all well
	ret
	.entry get_out,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10>
; get_out(stream,char) returns next character from its' stream
;(which is just a byte array)
;First word of the array is current position, second is size
geto:	movl	4(ap),r3	;get context
	movzwl	(r3)+,r4	;r4 = offset in bytes
	movzwl	(r3)+,r5	;r5 = size
	cmpl	r4,r5		;past end or at end?
	bgeq	10$		;if so error return
	addl2	r3,r4		;form address
	movb	(r4),@8(ap)	;return the char
	incw	@4(ap)		;bump current offset
	movl	#ss$_normal,r0
	ret
10$:	movl	#4,r0		;error
	clrw	@4(ap)	;zero pointer then too
	ret
	.entry put_in,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10>
; put_in(stream,char) puts char into stream
;(which is just a byte array)
;First word of the array is current position, second is size
puti:	movl	4(ap),r3	;get context
	movzwl	(r3)+,r4	;r4 = offset in bytes
	movzwl	(r3)+,r5	;r5 = size
	cmpl	r4,r5		;past end or at end?
	bgeq	10$		;if so error return
	addl2	r3,r4		;form address
	movb	@8(ap),(r4)	;store the char
	incw	@4(ap)		;bump current offset
	movl	#ss$_normal,r0
	ret
10$:	movl	#4,r0		;error
	clrw	@4(ap)	;zero pointer then too
	ret
	.globl	get,put
	.entry put,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10>
	cmpb	(ap),#2		;call to put_in???
	bneq	30$		;no,branch
	jmp	puti
30$:	cmpb	(ap),#3
	bneq	40$		;put_out call?
	jmp	puto
40$:
	movl	#4,r0
	ret			;else error
	.entry get,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10>
	cmpb	(ap),#2		;call to get_out???
	bneq	30$		;no,branch
	jmp	geto
30$:	cmpb	(ap),#3
	bneq	40$		;get_in call?
	jmp	geti
40$:
	movl	#4,r0
	ret			;else error
	.end

