;----------------------------------------------------------------------------
;File name:	SLC_UNCO.S			Revision date:	1997.07.27
;C->S Port by:	Ulf Ronald Andersson		Port begun:	1996.08.02
;Original C by:	Dan Ackerman & Steve Adam
;----------------------------------------------------------------------------
;
	include	"uran\STRUCT.SH"
	include	"uran\URAn_SYS.SH"
	include	"stik\CSLIP_HD.SH"
	include	"stik\GLOBDEFS.SH"
;
slc_unco_defined	set	1
;
;----------------------------------------------------------------------------
;
	XREF	calc_sum	;extern uint16 calc_sum(char *, uint16, uint16);
;
;----------------------------------------------------------------------------
;	Macro definitions
;
;URAn NB:  In assembly it was more convenient to use a common DECODE macro
;          to replace the three C macros originally used.  Adapting code
;          has been added as/if needed at each use of this macro.
;
	.MACRO	DECODE	ax,dx
	.LOCAL	.done
	clr	dx
	move.b	(ax)+,dx
	bne.s	.done
	move.b	(ax)+,dx
	rol	#8,dx
	move.b	(ax)+,dx
.done:
	.ENDM
;
;
;;;#define DECODEL(f) { \
;;;	if (*cp == 0) {\
;;;		(f) = ((f) + ((cp[1] << 8) | cp[2])); \
;;;		cp += 3; \
;;;	} else { \
;;;		(f) = ((f) + (uint32)*cp++); \
;;;	} \
;;;}
;;;
;;;#define DECODES(f) { \
;;;	if (*cp == 0) {\
;;;		f = f + ((cp[1] << 8) | cp[2]); \
;;;		cp += 3; \
;;;	} else { \
;;;		f = f + (uint16)*cp++; \
;;;	} \
;;;}
;;;
;;;#define DECODEU(f) { \
;;;	if (*cp == 0) {\
;;;		(f) = (cp[1] << 8) | cp[2]; \
;;;		cp += 3; \
;;;	} else { \
;;;		(f) = (uint16)*cp++; \
;;;	} \
;;;}
;----------------------------------------------------------------------------
;
slc_unco:			;long slc_uncompress (b, type, comp)
;				;GPKT *b; octet type; struct slcompress *comp; {
	XDEF	slc_unco	;extern long slc_uncompress (GPKT *b, octet type, struct slcompress *comp);
	lv_init	a6			;link local variables through a6
	movem.l	d3-d7/a2-a5,-(sp)	;protect entry registers for Pure_C
;Pure_C_def;	movea.l	8(a6),a0	;reg a0 has 'b'
;Pure_C_def;	movea.l	12(a6),d0	;reg d0 has 'type'
;Pure_C_def;	move.l	16(a6),a1	;reg a1 has 'comp'
;
;erase each ';Pure_C_def;' sequence above for CDECL function
;
	movea.l	a0,a5			;reg a5 is GPKT *b                  (arg 1)
	movea.l a1,a4			;reg a4 is struct slcompress *comp  (arg 3)
;---------------------------------------;reg a3 is char *cp	
	move.l	d0,d7			;reg d7 is octet type               (arg 2)
;---------------------------------------;reg d6 is unsigned long hlen;
;---------------------------------------;reg d5 is long len;
;---------------------------------------;reg d4 is int changes;
;
;register variables above leave d0-d3/a0-a2 free for calculations
;
	lvar.l	.th,1			;TCP_HDR *th;
	lvar.l	.cs,1			;struct cstate *cs;
	lvar.l	.ip,1			;IP_HDR *ip;
;
;;;	lvar.w	.tmp_hlen,1		;uint16 tmp_hlen;  NB: temp reg in ASM version
;
;;;	lvar.l	.newbuf			;char *newbuf; NB: redundant with new methods
;
	move	gpkt_ip_len(a5),d5
	ext.l	d5			;len = b->ip_len;
;
;	switch (type) {
;
	cmp.b	#TYPE_UNCOMPRESSED_TCP,d7
	bne	.past_UNCOMPRESSED_TCP	;case TYPE_UNCOMPRESSED_TCP:
;
;		/*
;		 *  Here we grab all the header info for later decompression
;		 *  rstate stands for recieve state.
;		 */
;
	movea.l	gpkt_pip(a5),a2
	move.l	a2,.ip(a6)			;ip = (IP_HDR *)b->pip;
;
	cmpi.l	#40,d5
	blt	.bad				;if (len < 40 ||
	cmpi.b	#MAX_STATES,iph_ptcl(a2)
	bhs	.bad				;    ip->ptcl >= MAX_STATES)  goto bad;
;
;		/*
;		 * clear out TYPE_UNCOMPRESSED_TCP
;		 */
;
.ip_valid:
	andi.b	#((255&!amask_iph_ver_bs)|(4<<iph_ver_bs)),iph_ver(a2)	;ip->ver &= 0x4;
	move.b	iph_ptcl(a2),slcm_last_recv(a4)	;comp->last_recv = ip->ptcl;
	movea.l	slcm_rstate(a4),a1
	clr	d0
	move.b	slcm_last_recv(a4),d0
	asl	#2,d0				;NB: index scaled for (struct *)[]
	add	d0,a1
	move.l	(a1),.cs(a6)			;cs = comp->rstate[comp->last_recv];
	andi	#~SLF_TOSS,slcm_flags(a4)	;comp->flags &= ~SLF_TOSS;
	move.b	#IPPROTO_TCP,iph_ptcl(a2)	;ip->ptcl = IPPROTO_TCP;
	move.b	iph_ihl(a2),d6
	and.l	#mask_iph_ihl_bs,d6			;hlen = ip->ihl;
	move	d6,d0
	asl	#4,d0				;NB: 2 for index + 2 to scale for (long *)[]
	move.b	tcph_ofst(a2,d0),d0
	lsr	#tcph_ofst_bs,d0
	and	#mask_tcph_ofst_bs,d0
	add	d0,d6				;hlen += ((TCP_HDR *)&((long *)ip)[hlen << 2])->ofst;
	asl	#2,d6				;hlen <<= 2;
;
	move.l	a2,a0				;a0 = ip
	lea	cs_ip(a1),a1			;a1 = &cs->cs_ip
	move	d6,d0				;d0 = byte count
	beq.s	.done_copy_1
	subq	#1,d0				;d0 = dbra count
.copy_loop_1:					;BCOPY(ip, &cs->cs_ip, hlen);
	move.b	(a0)+,(a1)+
	dbra	d0,.copy_loop_1
.done_copy_1:
	move.l	.cs(a6),a1
	clr	iph_sum+cs_ip(a1)		;cs->cs_ip.sum = 0;
	move	d6,cs_hlen(a1)			;cs->cs_hlen = hlen;
	move.l	d5,d0
	bra	.exit				;return (len);
;
;----------------------------------------------------------------------------
;
.past_UNCOMPRESSED_TCP:
	cmp.b	#TYPE_COMPRESSED_TCP,d7
	beq.s	.break_switch_1		;case TYPE_COMPRESSED_TCP:
;					;	break;
.case_default:				;default:
	bra	.bad				;goto bad; }
;
.break_switch_1:
;
;	/* We've got a compressed packet. */
;
	move.l	gpkt_fp(a5),a3			;cp = b->fp;
	move.b	(a3)+,d4
	ext	d4				;changes = *cp++;
	btst	#NEW_C_bs,d4
	beq.s	.not_NEW_C			;if (changes & NEW_C) {
;
;		/* Make sure the state index is in range, then grab the state.
;		 * If we have a good state index, clear the 'discard' flag. */
;
	cmpi.b	#MAX_STATES,(a3)
	bhs	.bad				;  if (*cp >= MAX_STATES) goto bad;
	andi	#~SLF_TOSS,slcm_flags(a4)	;  comp->flags &= ~SLF_TOSS;
	move.b	(a3)+,slcm_last_recv(a4)	;  comp->last_recv = *cp++;
	bra.s	.done_NEW_C_implicit		;}
;						;else {
.not_NEW_C:
;
;		/* this packet has an implicit state index.  If we've
;		 * had a line error since the last time we got an
;		 * explicit state index, we have to toss the packet. */
;
	move	slcm_flags(a4),d0
	andi	#SLF_TOSS,d0
	beq.s	.implicit_ok			;  if (comp->flags & SLF_TOSS)
	clr.l	d0
	bra	.exit				;    return (0);
;
;----------------------------------------------------------------------------
;
.implicit_ok:
.done_NEW_C_implicit:				;}
	movea.l	slcm_rstate(a4),a0
	clr	d0
	move.b	slcm_last_recv(a4),d0
	asl	#2,d0				;NB: index scaled for (struct *)[]
	lea	(a0,d0),a1
	move.l	(a1),.cs(a6)			;cs = comp->rstate[comp->last_recv];
;
	move.b	iph_ihl+cs_ip(a1),d6
	and.l	#mask_iph_ihl_bs,d6
	asl	#2,d6				;hlen = cs->cs_ip.ihl << 2;
;
	lea	cs_ip(a1,d6),a0
	move.l	a0,.th(a6)			;th = (TCP_HDR *)&((unsigned char *)&cs->cs_ip)[hlen];
;
	move.b	(a3)+,tcph_sum(a0)		;th->sum = (*cp << 8) | cp[1];
	move.b	(a3)+,tcph_sum+1(a0)		;cp += 2;
;
	btst	#TCP_PUSH_BIT_bs,d4
	beq.s	.not_PUSH			;if (changes & TCP_PUSH_BIT)
	bset	#tcph_f_psh_bs,tcph_f_psh(a0)	;  th->f_psh = 1;
	bra.s	.done_PUSH
;							;else
.not_PUSH:
	bclr	#tcph_f_psh_bs,tcph_f_psh(a0)	;  th->f_psh = 0;
.done_PUSH:
;
	move	d4,d2
	andi	#SPECIALS_MASK,d2		;switch (changes & SPECIALS_MASK) {
;
	cmpi	#SPECIAL_I,d2			;case SPECIAL_I: {
	bne.s	.past_SPECIAL_I
	clr.l	d0
	move	iph_len+cs_ip(a1),d0
	sub	cs_hlen(a1),d0			;    uint32 i = cs->cs_ip.len - cs->cs_hlen;
	ext.l	d0			;URAn NB: 'ack' & 'seq' are now signed long
	add.l	d0,tcph_ack(a0)			;    th->ack = th->ack + i;
	add.l	d0,tcph_seq(a0)			;    th->seq = th->seq + i; }
	bra	.break_switch_2			;  break;
;
.past_SPECIAL_I:
	cmpi	#SPECIAL_D,d2			;case SPECIAL_D:
	bne.s	.past_SPECIAL_D
	clr.l	d0
	move	iph_len+cs_ip(a1),d0
	sub	cs_hlen(a1),d0			;  d0 = cs->cs_ip.len - cs->cs_hlen;
	ext.l	d0			;URAn NB: 'seq' is now signed long
	add.l	d0,tcph_seq(a0)			;  th->seq = th->seq + cs->cs_ip.len - cs->cs_hlen;
	bra.s	break_switch_2			;  break;
;
.past_SPECIAL_D:
.default_2:					;default:
	btst	#NEW_U_bs,d4
	beq.s	not_NEW_U				;  if (changes & NEW_U) {
	bset	#tcph_f_urg_bs,tcph_f_urg(a0)		;    th->f_urg = 1;
	DECODE	a3,d0
	move	d0,tcph_urgent(a0)			;    DECODEU(th->urgent); /*DECODEU*/
	bra.s	done_NEW_U				;  }
;							;  else
not_NEW_U:
	bclr	#tcph_f_urg_bs,tcph_f_urg(a0)	;    th->f_urg = 0;
done_NEW_U:
	btst	#NEW_W_bs,d4
	beq.s	done_NEW_W				;  if (changes & NEW_W)
	DECODE	a3,d0
	add	d0,tcph_window(a0)			;    DECODES (th->window);
done_NEW_W:
	btst	#NEW_A_bs,d4
	beq.s	done_NEW_A				;  if (changes & NEW_A)
	DECODE	a3,d0
	ext.l	d0				;URAn NB: 'ack' is now signed long
	add.l	d0,tcph_ack(a0)				;    DECODEL (th->ack);
done_NEW_A:
	btst	#NEW_S_bs,d4
	beq.s	done_NEW_S				;  if (changes & NEW_S)
	DECODE	a3,d0
	ext.l	d0				;URAn NB: 'seq' is now signed long
	add.l	d0,tcph_seq(a0)				;    DECODEL (th->seq);
done_NEW_S:
	bra	.break_switch_2
;							;}
break_switch_2:
;
	btst	#NEW_I_bs,d4
	beq.s	not_NEW_I				;if (changes & NEW_I) {
	DECODE	a3,d0
	add	d0,iph_id+cs_ip(a1)			;    DECODES (cs->cs_ip.id);
	bra.s	done_NEW_I
;							;} else
not_NEW_I:
	addq	#1,iph_id+cs_ip(a1)			;cs->cs_ip.id = cs->cs_ip.id++; NB: Bug
done_NEW_I:
;
;	/*
;	 * At this point, cp points to the first byte of data in the
;	 * packet.  If we're not aligned on a 4-byte boundary, copy the
;	 * data down so the ip & tcp headers will be aligned.  Then back up
;	 * cp by the tcp/ip header length to make room for the reconstructed
;	 * header (we assume the packet we were handed has enough space to
;	 * prepend 128 bytes of header).  Adjust the length to account for
;	 * the new header & fill in the IP total length.
;	 */
;
	move.l	a3,d0
	sub.l	gpkt_fp(a5),d0
	sub.l	d0,d5				;len -= (cp - (char *)b->fp); /* was fp */
	blt	.bad				;if (len < 0) goto bad;
;						;/* we must have dropped some characters (crc should detect
;						; * this but the old slip framing won't) */
;
;	/* URAn addition 1996.09.23
;	 * Here the packet is almost ready for unpacking the header,
;	 * so this is a good place to add the tcp header initialization.
;	 */
;
	movea.l	gpkt_pip(a5),a1		;a1 = b->pip;
	move.b	iph_ihl(a1),d1
	and	#mask_iph_ihl_bs,d1
	asl	#2,d1			;d1 = tmp_hlen = ((IP_HDR *)b->pip)->ihl * 4;  /* Number of octets in ip header    */
	lea	(a1,d1),a0
	move.l	a0,gpkt_mp(a5)		;b->mp = &b->pip[tmp_hlen];
	move	gpkt_ip_len(a5),d0
	sub	d1,d0
	move	d0,gpkt_mlen(a5)	;b->mlen = b->ip_len - tmp_hlen;
;
	move.b	tcph_ofst(a2,d0),d1
	lsr	#tcph_ofst_bs,d1
	and	#mask_tcph_ofst_bs,d1
	asl	#2,d1			;d1 = tmp_hlen = th->ofst * 4;  /* Calc header len  */
	move.l	gpkt_mp(a5),a1		;a1 = b->mp;
	lea	(a1,d1),a0
	move.l	a0,gpkt_data(a5)	;b->data = &b->mp[tmp_hlen];     /* set generic data pointer to first char */
	move	gpkt_mlen(a5),d0
	sub	d1,d0
	move	d0,gpkt_dlen(a5)	;b->dlen = b->mlen - tmp_hlen;   /* after TCP header, and set the data size  */
;
;
;	/* OK it doesn't like adjusting the beginning of the packet 
;		which is what we seem to be doing.  So lets try making space
;		building it there and moving it back - worth a try */
;
;URAn NB:	I will use in-situ transfers instead.
;
	movea.l	.cs(a6),a2
	tst	cs_hlen(a2)
	ble.s	.bad				;trash packet if header size is impossible
	cmpi	#128,cs_hlen(a2)
	bhi.s	.bad				;trash packet if header size is too large
;
	add	cs_hlen(a2),d5			;len += cs->cs_hlen;
	move	d5,iph_len+cs_ip(a2)		;cs->cs_ip.len = (int16)len;
;
;	We must now move the packet data up in 'b' RAM to allow for the new header size
;	so this move must start at the end and work downwards in RAM to handle overlap.
;	Then we must move the new cstate header into the room made.
;	All data needed for both moves will be read before moving any, for overlap safety.
;
;NB:	This routine has been amended so at to handle overlap moves in either direction
;
	move	gpkt_dlen(a5),d0	;d0 = data byte count
	move	cs_hlen(a2),d1		;d1 = header byte count
	movea.l	gpkt_data(a5),a0	;a0 -> old data start
	lea	(a5,d1),a1		;a1 -> new data start
	cmpa.l	a0,a1
	beq.s	.done_copy_data
	blo.s	.copy_data_down
.copy_data_up:
	add	d0,a0			;a0 -> old data end
	add	d0,a1			;a1 -> new data end
	subq	#1,d0			;d0 = data dbra count
	blt.s	.done_copy_data
.copy_data_up_loop:
	move.b	-(a0),-(a1)
	dbra	d0,.copy_data_up_loop
	bra.s	.done_copy_data
;
.copy_data_down:
	subq	#1,d0			;d0 = data dbra count
	blt.s	.done_copy_data
.copy_data_down_loop:
	move.b	(a0)+,(a1)+
	dbra	d0,.copy_data_down_loop
;
.done_copy_data:
	lea	cs_ip(a2),a0		;a0 -> new header in 'cs' cstate structure
	lea	(a5),a1			;a1 -> header base of 'b' GPKT structure
	subq	#1,d1			;d1 = head dbra count
.copy_head_loop:
	move.b	(a0)+,(a1)+
	dbra	d1,.copy_head_loop
;
;;;	this was the C version (which never worked)
;;;	newbuf = KRmalloc((long)b->ip_len);
;;;	BCOPY(&cs->cs_ip, newbuf, cs->cs_hlen);
;;;	BCOPY(&b->data, newbuf + cs->cs_hlen, b->dlen);
;;;	BCOPY(&newbuf, b, b->ip_len);
;;;	KRfree(newbuf);
;
;	/* recompute the ip header checksum */
;
	clr	iph_sum(a5)		;((IP_HDR *)b)->sum = 0; /* Zero sum before new calculation  */
;
	movea.l	gpkt_pip(a5),a0
	moveq	#0,d0
	move	d6,d1
	asr	#1,d1
	ext.l	d1
	jsr	calc_sum.l
	move	d0,iph_sum(a5)		;((IP_HDR *)b)->sum = calc_sum(b->pip, 0, (int16)hlen>>1);
;
	move.l	d5,d0
	bra.s	.exit				;return (len);
;
;----------------------------------------------------------------------------
;
.bad:						;bad:
	ori	#SLF_TOSS,slcm_flags(a4)	;comp->flags |= SLF_TOSS;
	clr.l	d0				;return (0)
.exit:
	movem.l	(sp)+,d3-d7/a2-a5	;restore entry registers for Pure_C
	lv_exit	a6			;unlink local variables through a6
	rts
;
;----------------------------------------------------------------------------
;
VJ_deco:				;uint16 VJ_deco(cp)
;					;void **cp
	XREF	VJ_deco			;extern uint16 VJ_deco(void **cpp)
;Pure_C_def;	movea.l	4(sp),a0	;reg a0 has 'cpp'
;
;erase the ';Pure_C_def;' sequence above for CDECL function
;
	move.l	(a0),a1			;a1 -> packed data
	clr.l	d0
	DECODE	a1,d0
	move.l	a1,(a0)			;store updated pointer
	rts
;
;----------------------------------------------------------------------------
;End of file:	SLC_UNCO.S
;----------------------------------------------------------------------------
