
/*
 *  PACKET.C
 */

#include "dnet.h"

void BuildPacket();

#define Ascize(foo) (((foo) & 0x3F) | 0x40)

ubyte	RxTmp[MAXPACKET];

void
BuildDataPacket(pkt, win, dbuf, actlen)
PKT	*pkt;
ubyte	win;
ubyte	*dbuf;
uword	actlen;
{
    ubyte *ptr;
    ubyte *pend;
    ubyte range = 0;
    static ubyte BadCtl[32] = {
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 0, 0, 1, 0, 0, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1
    };

    ptr = dbuf;
    pend= dbuf + actlen;

    while (ptr < pend) {
	if (*ptr < 0x20 && BadCtl[*ptr])
	    range |= 1;
	if (*ptr >= 0x80)
	    range |= 2;
	++ptr;
    }
    if (Mode7 && range) {
	uword buflen = Expand6(dbuf, pkt->data, actlen);
	BuildPacket(pkt, PKCMD_WRITE6, win, pkt->data, actlen, buflen);
    } else {
	if (Mode7 || (range & 2)) {
	    BuildPacket(pkt, PKCMD_WRITE, win, dbuf, actlen, actlen);
	} else {
	    uword buflen = Compress7(dbuf, pkt->data, actlen);
	    BuildPacket(pkt, PKCMD_WRITE7, win, pkt->data, actlen, buflen);
	}
    }
}

PKT *
BuildRestartAckPacket(dbuf, bytes)
ubyte *dbuf;
ubyte bytes;
{
    static PKT pkt;
    BuildPacket(&pkt, PKCMD_ACKRSTART, 0, dbuf, bytes, bytes);
    return(&pkt);
}

void
BuildPacket(pkt, ctl, win, dbuf, actlen, buflen)
PKT	*pkt;
ubyte	ctl;
ubyte	*dbuf;
uword	actlen;
uword	buflen;
{
    pkt->buflen = buflen;
    pkt->sync = SYNC;
    pkt->ctl = ctl | win;
    pkt->cchk = Ascize((pkt->sync << 1) ^ pkt->ctl);
    if (actlen) {
	uword chk = chkbuf(dbuf, buflen);
	ubyte dchkh = chk >> 8;
	ubyte dchkl = chk;

	pkt->lenh = Ascize(actlen >> 6);
	pkt->lenl = Ascize(actlen);
	pkt->dchkh= Ascize(dchkh);
	pkt->dchkl= Ascize(dchkl);
	if (dbuf != pkt->data)
	    BMov(dbuf, pkt->data, buflen);
    }
}

void
WritePacket(pkt)
PKT *pkt;
{
    if (DDebug)
	printf("SEND-PACKET %02x %d\n", pkt->ctl, pkt->buflen);

    if (pkt->ctl == PKCMD_WRITE)
	++Packets8Out;
    if (pkt->ctl == PKCMD_WRITE6)
	++Packets6Out;
    if (pkt->ctl == PKCMD_WRITE7)
	++Packets7Out;

    if (pkt->buflen)
	NetWrite(&pkt->sync, 7 + pkt->buflen);
    else
	NetWrite(&pkt->sync, 3);
}

void
WriteCtlPacket(ctl, win)
{
    static CTLPKT pkt;

    NetWrite(NULL, 0);
    BuildPacket(&pkt, ctl, win, NULL, 0, 0);
    WritePacket(&pkt);
}

void
WriteNak(win)
{
    WriteCtlPacket(PKCMD_NAK, win);
}

void
WriteAck(win)
{
    WriteCtlPacket(PKCMD_ACK, win);
}

void
WriteChk(win)
{
    WriteCtlPacket(PKCMD_CHECK, win);
}

void
WriteRestart()
{
    WriteCtlPacket(PKCMD_RESTART, 0);
}

/*
 *	RECEIVE A PACKET
 */

int
RecvPacket(ptr, len)
ubyte *ptr;
long len;
{
    static uword ActLen;    /*	actual # bytes after decoding */
    static uword BufLen;    /*	length of input data buffer   */
    static uword DBLen;     /*	# bytes already in i.d.buf    */
    static ubyte RState;
    static PKT	 Pkt;
    ubyte  *dptr;
    short expect;

    if (ptr == NULL) {
	RState = 0;
	return(3);
    }

    while (len) {
	switch(RState) {
	case 0:
	    --len;
	    Pkt.sync = *ptr++;

	    if (Pkt.sync == SYNC)
		++RState;
	    break;
	case 1: 	/*  CTL */
	    --len;
	    Pkt.ctl = *ptr++;

	    if (Pkt.ctl < 0x20 || Pkt.ctl > 0x7F) {
		RState = 0;
		break;
	    }
	    ++RState;
	    break;
	case 2: 	/*  CCHK    */
	    --len;
	    Pkt.cchk = *ptr++;

	    if (Ascize((SYNC<<1) ^ Pkt.ctl) != Pkt.cchk) {
		RState = 0;
		break;
	    }
	    switch(Pkt.ctl & PKF_MASK) {
	    case PKCMD_ACKRSTART:
	    case PKCMD_WRITE:
	    case PKCMD_WRITE6:
	    case PKCMD_WRITE7:
		++RState;
		break;
	    default:
		do_cmd(Pkt.ctl, NULL, 0);
		RState = 0;
		break;
	    }
	    break;
	case 3: 	/*  LENH    */
	    --len;
	    Pkt.lenh = *ptr++;

	    ++RState;
	    if (Pkt.lenh < 0x20 || Pkt.lenh > 0x7F)
		RState = 0;
	    break;
	case 4: 	/*  LENL    */
	    --len;
	    Pkt.lenl = *ptr++;

	    if (Pkt.lenl < 0x20 || Pkt.lenl > 0x7F) {
		RState = 0;
		break;
	    }

	    ++RState;

	    ActLen = ((Pkt.lenh & 0x3F) << 6) | (Pkt.lenl & 0x3F);
	    DBLen = 0;

	    switch(Pkt.ctl & PKF_MASK) {
	    case PKCMD_ACKRSTART:
	    case PKCMD_WRITE:
		BufLen = ActLen;
		break;
	    case PKCMD_WRITE6:
		BufLen = (ActLen * 8 + 5) / 6;
		break;
	    case PKCMD_WRITE7:
		BufLen = (ActLen * 7 + 7) / 8;
		break;
	    default:
		puts("BaD");
		break;
	    }

	    if (ActLen > MAXPKT || BufLen > MAXPACKET) {
		if (PDebug || DDebug)
		    printf("Packet Length Error %d %d\n", ActLen, BufLen);
		RState = 0;
	    }
	    break;
	case 5: 	/*  DChkH   */
	    --len;
	    Pkt.dchkh = *ptr++;

	    ++RState;
	    if (Pkt.dchkh < 0x20 || Pkt.dchkh > 0x7F)
		RState = 0;
	    break;
	case 6: 	/*  DCHKL   */
	    --len;
	    Pkt.dchkl = *ptr++;

	    ++RState;
	    if (Pkt.dchkl < 0x20 || Pkt.dchkl > 0x7F)
		RState = 0;
	    break;
	case 7: 	/*  -DATA-  */
	    if (DBLen + len < BufLen) {     /*  not enough  */
		BMov(ptr, Pkt.data + DBLen, len);
		DBLen += len;
		len = 0;
		break;
	    }

	    /*
	     *	Enough data, check chk
	     */

	    BMov(ptr, Pkt.data + DBLen, BufLen - DBLen);
	    len -= BufLen - DBLen;
	    ptr += BufLen - DBLen;

	    {
		uword chk;
		ubyte chkh;
		ubyte chkl;

		chk = chkbuf(Pkt.data, BufLen);
		chkh = Ascize(chk >> 8);
		chkl = Ascize(chk);

		if (Pkt.dchkh != chkh || Pkt.dchkl != chkl) {
		    printf("Chksum failure %02x %02x %02x %02x\n",
			Pkt.dchkh, chkh, Pkt.dchkl, chkl
		    );
		    RState = 0;
		    break;
		}
	    }

	    switch(Pkt.ctl & PKF_MASK) {
	    case PKCMD_ACKRSTART:
		dptr = Pkt.data;
		break;
	    case PKCMD_WRITE:
		dptr = Pkt.data;
		++Packets8In;
		break;
	    case PKCMD_WRITE6:
		UnExpand6(Pkt.data, RxTmp, ActLen);
		dptr = RxTmp;
		++Packets6In;
		break;
	    case PKCMD_WRITE7:
		UnCompress7(Pkt.data, RxTmp, ActLen);
		dptr = RxTmp;
		++Packets7In;
		break;
	    default:
		puts("BaD2");
		dptr = Pkt.data;
		break;
	    }

	    do_cmd(Pkt.ctl, dptr, ActLen);

	    RState = 0;
	    break;
	}
    }

    {
	static short ExpAry[] = { 3, 2, 1, 4, 3, 2, 1, 0 };

	expect = ExpAry[RState];

	if (RState == 7)
	    expect = BufLen - DBLen;
    }
    if (Rto_act) {
	AbortIO((IOR *)&Rto);
	WaitIO((IOR *)&Rto);
	Rto_act = 0;
    }
    if (RState == 7) {
	Rto.tr_time.tv_secs = 8;
	Rto.tr_time.tv_micro= 0;
	SendIO((IOR *)&Rto);
	Rto_act = 1;
    }
    do_cmd((uword)-1, NULL, 0);
    return((int)expect);
}

