/*
 *	pdp8/e emulator
 *
 *	Author:
 *		Bill Haygood
 *		41832 Ernest Road
 *		Loon Lake, WA 99148-9607
 *		Internet: billh@comtch.iea.com
 *		Voice/AnsMach/FAX \
 *			or	   509-233-2555
 *		  Cellular/Pager  /
 *
 *	Copyright 1992,1993 by the author with all rights reserved.
 *
 *	Miscellaneous routines.
 */
#include <math.h>
#include "pdp8regs.h"

/* -------------------------------------------------------------------- */
/*
 *	Print a header to display the registers.
 */
INT header (INT newline)
{
/*
            1  1 1    2     2                4    5     5    6    6   7   7
0      7    2  5 7    2     8                5    0     6    1    6   0   4
DI PC  Code Instruction  (Dir) ((Indir))    Flags L AC   MQ   SR  EAE SC GTF
  FPC                              INDX STAT FAC
107606 7763 CLA MQL MQA DAD 61234 (23456701) 0010 00000 0000 0000  A  00 F
 */
	if (!newline)
	    printf ("\n") ;
	printf ("Flags Bits:  0:Link  1:GTF  2:IReq  3:IInh"
				"  4:IEna  5:UF  6-8:IF  9-11:DF\n") ;
	strcpy (chrsout, "DI PC  Code Instruction  (Dir) ((Indir))"
					"    Flags L AC   MQ   SR") ;
	if (EAE)
	    strcat (chrsout, "  EAE SC GTF") ;
	printf ("%s\n", chrsout) ;
	if (FPP)
	    printf ("  FPC                              INDX STAT FAC\n") ;
	return TRUE ;
}
/* -------------------------------------------------------------------- */
/*
 *	Print a header to display the registers.
 */
VOID outline (VOID)
{
/*
            1  1 1    2     2                4    5     5    6    6   7   7
0      7    2  5 7    2     8                5    0     6    1    6   0   4
ID PC  Code Instruction  (Dir) ((Indir))    Flags L AC   MQ   SR  EAE SC GTF
107600 6211 CDF 1                            0010 00000 0000 0000  B  00  F
107601 1776 TADI 7776 (2345)((6712))         0010 06712 0000 0000  B  00  F
107602 7776 SPA SNA SZL CLA OSR HLT          0010 00000 0000 0000  B  00  F
107603 7767 CLA MQL MQA SCA MUY 1234         0010 00000 0000 0000  B  00  F
107604 7765 CLA MQL MQA DST 61234            0010 00000 0000 0000  B  00  F
107606 7763 CLA MQL MQA DAD 61234 (23456701) 0010 00000 0000 0000  B  00  F
 */
    INT i = strlen (chrsout) ;
    while (i < 45)
	chrsout [i++] = ' ' ;
    sprintf (&chrsout[45], "%04o %05o %04o %04o",
			(((AC & 010000) >> 1)
			| (GTF << 10)
			| ((io_flags & int_mask) ? BIT2 : 0)
			| (int_inh << 8)
			| (int_ena << 7)
			| (UF >> 6)
			| (IF >> 9)
			| (DF >> 12)),
			  AC, MQ, SR) ;
    if (EAE)
    {
	sprintf (pdp8exec, "  %c  %02o", EAE, SC) ;
	strcat (chrsout, pdp8exec) ;
	if (EAE == 'B')
	    strcat (chrsout, GTF ? "  T" : "  F") ;
    }
    printf ("%s\n", chrsout) ;
}
/* -------------------------------------------------------------------- */
/*
 *	Store double to UWORD array w.
 */
VOID storefpp (DOUBLE d, UWORD *w)
{
    LONG y0, y1 ;
    INT exp ;

    if (d)
    {
	d = frexp (d, &exp) ;
	*w = exp & 07777 ;
	d = ldexp (d, 31) ;
	y0 = d ;
	*(w + 1) = (y0 >> 20) & 07777 ;
	*(w + 2) = (y0 >> 8) & 07777 ;
	if (fpp_data == EP)
	{
	    if (d -= y0)
	    {
		d = ldexp (d, 28) ;
		y1 = d ;
		*(w + 3) = ((y0 << 4) & 07760) | ((y1 >> 24) & 017) ;
		*(w + 4) = (y1 >> 12) & 07777 ;
		*(w + 5) = y1 & 07777 ;
	    }
	    else
		*(w + 3) = *(w + 4) = *(w + 5) = 0 ;
	}
    }
    else
    {
	*w++ = 0, *w++ = 0, *w = 0 ;
	if (fpp_data == EP)
	    *++w = 0, *++w = 0, *++w = 0 ;
    }
}
/* -------------------------------------------------------------------- */
/*
 *	Convert a pdp8 fpp floating point datum to double.
 *	w points to fpp exponent word.
 */
DOUBLE fpp2d (UWORD *w)
{
    LONG y0, y1 ;
    INT nz ;
    DOUBLE d ;

    nz = *(w + 1) & 06000 ;
    if (nz == 02000 || nz == 04000)
    {
	y0 = (*(w + 1) << 20) | (*(w + 2) << 8) ;
	if (fpp_data == EP)
	{
	    y0 |= *(w + 3) >> 4 ;
	    y1 = ((*(w + 3) & 0xf) << 24) | (*(w + 4) << 12) | *(w + 5) ;
	}
	else
	    y1 = 0 ;
	d = ldexp ((DOUBLE) y0, -31) ;
	if (y1)
	{
	    if (y0 < 0)
		y1 |= ~01777777777 ;
	    d += ldexp ((DOUBLE) y1, -59) ;
	}
	nz = *w ;
	if (nz & 04000)
	    nz |= ~07777 ;
	d = ldexp (d, nz) ;
    }
    else
	d = 0.0 ;
    order.d = d ;
    return d ;
}
/* --------------------------------------------------------------------	*/
/*
 *	Output an FPP datum in decimal format.
 *	e.g. : -3.14159265358979
 *
 *	If 'w' is NULL, output content of the FAC, otherwise
 *	use address supplied.
 */
VOID outfpp (UWORD *w, INT posit)
{
    LONG j ;
    INT i, norm = TRUE ;
/*
 *	Print to 'chrsout' and delete trailing zeroes.
 */
    if ((*(w + 1) & 04000) == 0)
	chrsout [posit++] = ' ' ;
    if (fpp_data == DP)
    {
	j = (((LONG) *(w + 1)) << 12) | *(w + 2) ;
	if (j & 040000000)
	    j |= ~077777777 ;
	posit += sprintf (&chrsout [posit], "%d", j) ;
    }
    else				/* FP || EP			*/
    {
	DOUBLE doub = fpp2d (w) ;
	j = *(w + 1) | *(w + 2) ;
	if (fpp_data == EP)
	    j |= *(w + 3) | *(w + 4) | *(w + 5) ;
	if (j == 0)
	    j = 02000 ;
	else
	{
	    if ((j = *(w + 1)) & 04000)
		j = (- j) & 07777 ;
	}
	if (j & 02000)			/* w is normalized		*/
	{
	    if (!doub || (abs (doub) < 10.0))
	    {
		posit += sprintf (&chrsout [posit], (fpp_data == EP)
					? "%17.15f" : "%8.6f", doub) ;
		for (i = posit - 1 ; i && chrsout [i] == '0' ; i--)
		    chrsout [i] = '\0' ;
	    }
	    else
	    {
		posit += sprintf (&chrsout [posit], (fpp_data == EP)
					? "%18.15e" : "%8.6e", doub) ;
		i = posit - 1 ;
		while (chrsout [i] != 'E' && chrsout [i] != 'e')
		    i-- ;
		j = i - 1 ;
		while (chrsout [j] == '0')
		    j-- ;
		j++ ;
		while (chrsout [j] != '\0')
		    chrsout [j++] = chrsout [i++] ;
	    }
	    posit = strlen (chrsout) ;
	}
	else
	{
	    if (*(w + 1) & 04000)
		posit += sprintf (&chrsout [posit], " ") ;
	    posit += sprintf (&chrsout [posit], "unnormalized") ;
	    norm = FALSE ;
	}
    }
    if (fpp_run && norm)
	sprintf (&chrsout [posit], (fpp_data == DP) ?  "(DP)"
				: (fpp_data == FP) ? "(FP)" : "(EP)") ;
    printf ("%s\n", chrsout) ;
}
/* --------------------------------------------------------------------	*/
/*
 *	Output byte(s) to output file.
 */
#define ULBUFSIZ	(1 << 10)
VOID out_ul (ULONG ulong, INT writeflag)
{
    STATIC UBYTE outbuff [ULBUFSIZ] ;
    STATIC INT outptr = 0 ;

    if (writeflag)
    {
	switch (writeflag & 0xf)
	{
	    case 4 :
		outbuff [outptr++] = ulong >> 24 ;
		outbuff [outptr++] = ulong >> 16 ;
		outbuff [outptr++] = ulong >> 8 ;
		outbuff [outptr++] = ulong ;
		fwrite (outbuff, 1, outptr, pdp8file) ;
		break ;
	    case 3 :
		outbuff [outptr++] = ulong >> 24 ;
		outbuff [outptr++] = ulong >> 16 ;
		outbuff [outptr++] = ulong >> 8 ;
		fwrite (outbuff, 1, outptr, pdp8file) ;
		break ;
	    case 2 :
		outbuff [outptr++] = ulong >> 24 ;
		outbuff [outptr++] = ulong >> 16 ;
		fwrite (outbuff, 1, outptr, pdp8file) ;
		break ;
	    case 1 :
		outbuff [outptr++] = ulong >> 24 ;
		fwrite (outbuff, 1, outptr, pdp8file) ;
		break ;
	    case 0 :
		if (outptr)
		    fwrite (outbuff, 1, outptr, pdp8file) ;
		break ;
	    default :
		break ;
	}
    }
    else
    {
	outbuff [outptr++] = ulong >> 24 ;
	outbuff [outptr++] = ulong >> 16 ;
	outbuff [outptr++] = ulong >> 8 ;
	outbuff [outptr++] = ulong ;
	if (!(outptr &= ULBUFSIZ - 1))
	    fwrite (outbuff, 1, ULBUFSIZ, pdp8file) ;
    }
}
/* -------------------------------------------------------------------- */
/*
 *	Output a bit stream to a file.
 */
VOID outbits (ULONG bits, INT length)
{
    STATIC INT avail = 32 ;
    STATIC ULONG ulong = 0 ;

    if (length)
    {
	if (avail == length)
	{
	    ulong |= bits ;
	    out_ul (ulong, FALSE) ;
	    avail = 32 ;
	    ulong = 0 ;
	}
	else
	{
	    if (avail > length)
	    {
		ulong |= bits << (avail - length) ;
		avail -= length ;
	    }
	    else
	    {
		length -= avail ;
		ulong |= bits >> length ;
		out_ul (ulong, FALSE) ;
		bits &= (1 << length) - 1 ;
		avail = 32 - length ;
		ulong = bits << avail ;
	    }
	}
    }
    else
    {
	if (avail == 32)
	    length = 0x80 ;
	else
	{
	    if (avail)
		length = 1 ;
	    if (avail < 25)
		length = 2 ;
	    if (avail < 17)
		length = 3 ;
	    if (avail < 9)
		length = 4 ;
	}
	out_ul (ulong, length) ;
    }
}
/* --------------------------------------------------------------------	*/
/*
 *	Input four bytes from a file.
 */
ULONG in_ul (VOID)
{
    STATIC ULONG ulong ;
    STATIC UBYTE inbuff [ULBUFSIZ] ;
    STATIC INT inptr = 0 ;

    if (!inptr)
	fread (inbuff, 1, ULBUFSIZ, pdp8file) ;
    ulong = (inbuff [inptr++] << 24) | (inbuff [inptr++] << 16)
		| (inbuff [inptr++] << 8) | inbuff [inptr++] ;
    inptr &= ULBUFSIZ - 1 ;
    return ulong ;
}
/* -------------------------------------------------------------------- */
/*
 *	Input a bit stream from a file.
 */
UWORD inbits (INT length)
{
    STATIC INT avail = 0 ;
    STATIC ULONG ulong = 0 ;
    UWORD mask = (1 << length) - 1 ;
    UWORD val ;

    if (!avail)
    {
	ulong = in_ul () ;
	avail = 32 ;
    }
    if (avail == length)
    {
	val = ulong & mask ;
	avail = 0 ;
    }
    else if (avail > length)
    {
	avail -= length ;
	val = (ulong >> avail) & mask ;
    }
    else
    {
	val = (ulong << (length - avail)) & mask ;
	length -= avail ;
	ulong = in_ul () ;
	avail = 32 - length ;
	val |= ulong >> (32 - length) ;
    }
    return val ;
}
/* -------------------------------------------------------------------- */
/*
 *	Do RK8-E write cylinder.
 */
VOID rk8writ (INT unit)
{
    if (fseek (rk8 [unit].drive, rk8 [unit].cyl * RK8BUFSIZ, SEEK_SET))
	rk8status |= BIT3 ;
    if (fwrite ((UBYTE *) rk8 [unit].buff, 1, RK8BUFSIZ,
					 rk8 [unit].drive) != RK8BUFSIZ)
	rk8status |= BIT10 ;
    rk8 [unit].dirty = FALSE ;
}
/* -------------------------------------------------------------------- */
/*
 *	Do RK8-E read cylinder.
 */
VOID rk8read (INT unit)
{
    if (rk8 [unit].cyl >= rk8 [unit].maxcyl)
    {
	ULONG *l = (ULONG *) rk8 [unit].buff ;
	INT i = RK8BUFSIZ / 16 ;
/*
 *	Clear the unit's cylinder buffer.
 */
	while (i--)
	    *l++ = 0 , *l++ = 0 , *l++ = 0 , *l++ = 0 ;
/*
 *	Write zeroed cylinders until desired cylinder reached.
 */
	i = rk8 [unit].cyl ;
	rk8 [unit].cyl = rk8 [unit].maxcyl ;
	rk8 [unit].maxcyl = i ;
	while (rk8 [unit].cyl < rk8 [unit].maxcyl)
	    rk8writ (unit) , rk8 [unit].cyl++ ;
	rk8 [unit].maxcyl++ ;
    }
    else
    {
	if (fseek (rk8 [unit].drive, rk8 [unit].cyl * RK8BUFSIZ, SEEK_SET))
	    rk8status |= BIT3 ;
	if (fread ((UBYTE *) rk8 [unit].buff, 1, RK8BUFSIZ,
					rk8 [unit].drive) != RK8BUFSIZ)
	    rk8status |= BIT4 ;
    }
}
/* -------------------------------------------------------------------- */
/*
 *	Do RK8-E I/O (read or write an rk8e sector or half-sector).
 */
INT rk8io (INT unit, INT block, UBYTE half, UBYTE rwflag, UINT memadr)
{
    UBYTE *b = rk8 [unit].buff + (block & 0x1f) * 384 ;
    UWORD *w = base + memadr ;
    INT retval = (half) ? 128 : 256 ;
    INT words = retval >> 3 ;
/*
 *	Get specified cylinder into cylinder buffer.
 */
    if (rk8 [unit].cyl != (block >> 5))
    {
	if (rk8 [unit].dirty)
	    rk8writ (unit) ;
	rk8 [unit].cyl = block >> 5 ;
	rk8read (unit) ;
    }
    if (rwflag)
    {
/*
 *	Write a sector to the rk8e disk file.  The file packing scheme:
 *		<- 8 bits->
 *		+---------+	0H => High order of pdp8 word 0
 *		|   0H    |	0L => Low order of pdp8 word 0
 *		+----+----+	1H => High order of pdp8 word 1
 *		| 0L | 1H |	1L => Low order of pdp8 word 1
 *		+----+----+
 *		|   1L    |	In other words, pdp8 words are written
 *		+----+----+	in bit order (high to low).
 */
	while (words--)
	{
	    *b++ = *w >> 4 ;
	    *b   = *w++ << 4 , *b++ |= *w >> 8 ;
	    *b++ = *w++ ;
	    *b++ = *w >> 4 ;
	    *b   = *w++ << 4 , *b++ |= *w >> 8 ;
	    *b++ = *w++ ;
	    *b++ = *w >> 4 ;
	    *b   = *w++ << 4 , *b++ |= *w >> 8 ;
	    *b++ = *w++ ;
	    *b++ = *w >> 4 ;
	    *b   = *w++ << 4 , *b++ |= *w >> 8 ;
	    *b++ = *w++ ;
	}
	if (half)
	{
	    ULONG *l = (ULONG *) b ;
	    for (words = 12 ; words ; words--)
		*l++ = 0 , *l++ = 0 , *l++ = 0 , *l++ = 0 ;
	}
	rk8 [unit].dirty = TRUE ;
    }
    else
    {
/*
 *	Read a sector from the rk8e disk file.
 */
	while (words--)
	{
	    *w = *b++ << 4 , *w++ |= *b >> 4 ;
	    *w = (*b++ & 0xf) << 8 , *w++ |= *b++ ;
	    *w = *b++ << 4 , *w++ |= *b >> 4 ;
	    *w = (*b++ & 0xf) << 8 , *w++ |= *b++ ;
	    *w = *b++ << 4 , *w++ |= *b >> 4 ;
	    *w = (*b++ & 0xf) << 8 , *w++ |= *b++ ;
	    *w = *b++ << 4 , *w++ |= *b >> 4 ;
	    *w = (*b++ & 0xf) << 8 , *w++ |= *b++ ;
	}
    }
    return retval ;
}
/* -------------------------------------------------------------------- */
