/* $Header:decode.c 12.0$ */
/* $ACIS:decode.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/dbx/ibmrt/RCS/decode.c,v $ */

#ifndef lint
static char *rcsid = "$Header:decode.c 12.0$";
#endif

/* Copyright (c) 1982 Regents of the University of California */

/*
 * Target machine dependent stuff.
 */

#include "defs.h"
#include "decode.h"
#include "process.h"
#include "execute.h"
#include "runtime.h"
#include "events.h"
#include "main.h"
#include "symbols.h"
#include "source.h"
#include "mappings.h"
#include "object.h"
#include "tree.h"
#include "eval.h"
#include "keywords.h"
#include "ops.h"
#include <machine/reg.h>
#include <signal.h>

#ifndef public
typedef unsigned int Address;
typedef unsigned char Byte;
typedef unsigned int Word;

#define NREG 17		/* 16 general purpose registers + PC */
#define NFLREG 64	/* up to 64 words of floating point registers */

#define ARGP 13
#define FRP 13
#define STKP 1
#define PROGCTR 16

#define CODESTART 0
#define FUNCOFFSET 0

#define BITSPERBYTE 8
#define BITSPERWORD (BITSPERBYTE * sizeof(Word))
#define WORDALIGN(a) ((a) & ~03)

#define nargspassed(frame) argn(0, frame)

#define BP_ERRNO    SIGTRAP     /* signal received at a breakpoint */

typedef short Bpinst;

extern Bpinst BP_OP;

#include "source.h"
#include "symbols.h"

Address pc;
Address prtaddr;

#endif

Bpinst BP_OP = BPT;

public int rloc[] = {
    R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, IAR
};

/*
 * Instruction decoder.
 */

#define bits(x,n,m) ((x >> (31-m)) & ((1 << (m-n+1)) - 1))
#define signed(x,n,m) ((x <<n) >> 31-m+n)

static char *buf;	/* current append string for formatted instruction */
extern char *mymalloc();

public Address printop (addr)
Address addr;
{
    struct s_tXX *tp;
    YOpCode inst;
    int info;
    char *cp;
    char *savebp;
    char *bp;
    int ifmt;
    Address newaddr;
    char *zaps();

    bp = mymalloc(20);	/* alloc some space to print into */
    savebp = bp;
    buf = bp;

    iread(&inst, addr, sizeof(inst));
    tp = p_tXX[ info = bits(inst,0,3) ];
    if ( info>7 )
	tp = & tp[ bits(inst,4,7) ];
    info = ltab[ ifmt = tp->fmt];
    newaddr = addr + info;	/* add length of instruction to address */

    printf("%08x  ",addr);
    cp = tp->opc;
    bp = buf+1;
    while( *buf++ = *cp++);
    --buf;

    switch(ifmt){

	case JI:
		zaps(bp,bc[bits(inst,4,7)]);
		varput((long) addr +2*signed(inst,8,15));
		break;

	case RI:
		wreg(bits(inst,8,11));
		addc(',');
		fixput(bits(inst,12,15),0);
		break;

	case RI16:
		wreg(bits(inst,8,11));
		addc(',');
		fixput(bits(inst,12,15)+16,1);
		break;

	case RR:
		wreg(bits(inst,8,11));
		addc(',');
		wreg(bits(inst,12,15));
		break;

	case SRI:
		sreg(bits(inst,8,11));
		addc(',');
		fixput(bits(inst,12,15)+16,1);
		break;

	case SRR:
		sreg(bits(inst,8,11));
		addc(',');
		wreg(bits(inst,12,15));
		break;

	case R0R:
		wreg(bits(inst,8,11));
		addc(',');
		addc('0');
		addc('(');
		wreg(bits(inst,12,15));
		addc(')');
		break;

	case RRR:
		wreg(bits(inst,4,7));
		addc(',');
		wreg(bits(inst,8,11));
		addc(',');
		wreg(bits(inst,12,15));
		break;

	case RC:
		info = bits(inst,9,11);
		if (bits(inst,5,6))
			info += 8;
		if (!bits(inst,8,8))
			info = BUNK;
		bp = zaps(bp,bc[info]);
		*bp++ = 'r';
		if (bits(inst,7,7))
			*bp = 'x';
		wreg(bits(inst,12,15));
		break;

	case BI:
		info = bits(inst,9,11);
		if (bits(inst,5,6))
			info = info+8;
		if (!bits(inst,8,8))
			info = BUNK;
		bp = zaps(bp,bc[info]);
		if (bits(inst,7,7))
			*bp = 'x';
		varput((long) addr+2*signed(inst,12,31));
		break;

	case BRI:
		wreg(bits(inst,8,11));
		addc(',');
		varput((long) addr+2*signed(inst,12,31));
		break;

	case BA:
		wreg(15);
		addc(',');
		varput(2*bits(inst,8,30));
		break;

	case DSC:
	case DSH:
	case DSL:
		wreg(bits(inst,8,11));
		addc(',');
		varput(bits(inst,4,7) << (ifmt-DSC),0);
		if (ireg('(',bits(inst,12,15)))
			addc(')');
		break;

	case DIR:
		varput(bits(inst,16,31));
		if (ireg('(',bits(inst,12,15)))
			addc(')');
		break;

	case DRI:
		wreg(bits(inst,12,15));
		addc(',');
		vsiput(signed(inst,16,31));
		break;

	case DIIR:
		fixput(bits(inst,8,11),0);
		addc(',');
		vsiput(signed(inst,16,31));
		if (ireg('(',bits(inst,12,15)))
			addc(')');
		break;

	case DRIR:
		wreg(bits(inst,8,11));
		addc(',');
		vsiput(signed(inst,16,31));
		if (ireg('(',bits(inst,12,15)))
			addc(')');
		break;

	case DRRI:
		wreg(bits(inst,8,11));
		addc(',');
		wreg(bits(inst,12,15));
		addc(',');
		vsiput(signed(inst,16,31));
		break;

	case DTI:
		zaps(bp,tc[bits(inst,9,11)]);
		wreg(bits(inst,12,15));
		addc(',');
		vsiput(signed(inst,16,31));
		break;

	case R00:		/* unknown instruction... */
	case UNK:
		break;

	}

    addc(0x00);		/* add null at end of string */

    printf("%s\n",savebp);	/* print out the instruction */
    fflush(stdout);	/* flush the output buffer so the guy can see it */
    free(savebp);	/* free the buffer....reclaim some space */
    addr = newaddr;	/* update the address pointer */
    return(addr);
}

static addc(c) register char c; { *buf++ = c; }

static ireg(c,r) register char c; register int r;
{
    if (r){
	addc(c); wreg(r);
    }
    return(r);
}

static wreg(r)
   register int r;
{
   addc('r');
   if (r > 9){
	addc('1');
	r-= 10;
   }
   fixput(r,0);
}

static sreg(r)
   register int r;
{
   register char c, *cp;

   for(cp=sr[r]; (c = *cp) > ' '; ++cp)
	addc(c);
}

static fixput(x,i)	/* output the least significant i+1 hex digits of x */
   register long x;
   register int i;
{
   static char hexdig[17] = "0123456789ABCDEF";

   i *= 4;
   do
	addc(hexdig[(x >> i) & 0xF]);
   while ((i -= 4) >= 0);
}

static varput(x)	/* output all significant hex digits of x */
   register long x;
{
   register long mask = 0xF;
   register int i, digs=0;

   for (i=0; i<2*sizeof(long); ++i){
	if (mask & x)
	   digs = i;
	mask <<= 4;
   }
   fixput(x,digs);
}

static vsiput(x) /* output all significant hex digits of x as a signed # */
   register long x;
{
   if (x<0){
	addc('-');
	x = -x;
   }
   varput(x);
}

static char *zaps(p,q)	/* modify the substring p with the substring q and */
			/* ending with the first blank or the end or q */
   register char *p,*q;
{
   register char c;

   while((c= *q++) > ' '){
	*p++ = c;
   }

   return(p);
}

/*
 * Compute the next address that will be executed from the given one.
 * If "isnext" is true then consider a procedure call as straight line code.
 *
 * We must unfortunately do much of the same work that is necessary
 * to print instructions.  In addition we have to deal with branches.
 * Unconditional branches we just follow, for conditional branches
 * we continue execution to the current location and then single step
 * the machine.  We assume that the last argument in an instruction
 * that branches is the branch address (or relative offset).
 */

private Address dojump (startaddr, offset, delayed)
Address startaddr, offset;
boolean delayed;
{
    Address addr;

    stepto(startaddr);
    pstep(process, DEFSIG);
    addr = reg(PROGCTR);
    if (addr >= 0xe0000000) {
	addr = startaddr + offset;
	if (delayed) {
	    addr += 4;
	}
    }
    pc = addr;
    bpact();
    if (not isbperr()){
	printstatus();
    }
    return addr;
}

public Address nextaddr (startaddr, isnext)
Address startaddr;
Boolean isnext;
{
    Address addr;
    struct s_tXX *tp;
    YOpCode inst;
    int info;
    int opcode, extend;

    addr = usignal(process);
    if (addr != 0 && addr != 1) {
	dread(&addr, addr, sizeof(addr));
    } else {
	iread(&inst, startaddr, sizeof(inst));
	info = bits(inst, 0, 3);
	tp = p_tXX[info];
	opcode = info;
	extend = bits(inst, 4, 7);
	if (info > 7) {
	    tp = &tp[extend];
	}
	info = ltab[tp->fmt];
	addr = startaddr + info;		/* default next instruction */
	switch (opcode) {
	    case 0:			/* jumps - JI format */
		addr = dojump(startaddr, info, false);
		break;
	    case 8:			/* branch - check for calls */
		if (extend < 10 || extend > 13) {
		    addr = dojump(startaddr, info, false);
		} else if (isnext) {
		    if (extend == 11 || extend == 13) {
			addr += 4;
		    }
		} else {
		    addr = dojump(startaddr, info, true);
		}
		break;
	    case 0xe:		/* extended branch - calls? */
		if (extend >= 8 && extend != 11 && extend != 13) {
		    if (extend < 10 || extend == 14 || extend == 15) {
			addr = dojump(startaddr, info, false);
		    }
		} else if (isnext) {
		    if (extend == 13) {
			addr += 4;
		    }
		} else {
		    addr = dojump(startaddr, info, true);
		}
		break;
	    /*
	     * 1 STCS, 2 STHS, 3 STS, 4 LCS, 5 LHAS, 6 CAS,
	     * 7 LS, {9,10,11,15} R format, 12 SVC, 13 CLI
	     */
	    default:
		break;
	}
    }
    return addr;
}

/*
 * Enter a procedure by creating and executing a call instruction.
 */

#define CALLSIZE 4	/* size of call instruction */

public beginproc (p, argc)
Symbol p;
int argc;
{
    Word dest;
    Word save, callinst;
    Address addr;
    Name n;
    Symbol data;
    char buf[100];

    sprintf(buf, ".%s", symname(p));
    n = identname(buf, false);
    data = lookup(n);
    if (data == nil || data->class != VAR) {
	error("no data area for %s\n", symname(p));
    }
    addr = 4;
    iread(&save, addr, CALLSIZE);
    pc = 6;
    dest = codeloc(p);
    callinst = 0xdb00ecff;
    iwrite(&callinst, addr, CALLSIZE);
    setreg(15, dest);
    setreg(PROGCTR, pc);
    setreg(0, data->symvalue.offset);
    pstep(process, DEFSIG);
    iwrite(&save, addr, CALLSIZE);
    pc = reg(PROGCTR);
    if (not isbperr()) {
	printstatus();
    }
}

/*
 * Extract a bit field from an integer.
 */

public integer extractField (s)
Symbol s;
{
    integer nbytes, nbits, n, r, off, len;

    off = s->symvalue.field.offset;
    len = s->symvalue.field.length;
    nbytes = size(s);
    n = 0;
    if (nbytes > sizeof(n)) {
	printf("[bad size in extractField -- word assumed]\n");
	nbytes = sizeof(n);
    }
    popn(nbytes, ((char *) &n) + (sizeof(Word) - nbytes));
    nbits = nbytes * BITSPERBYTE;
    r = n >> (nbits - ((off mod nbits) + len));
    r &= ((1 << len) - 1);
    return r;
}

/*
 * Change the length of a value in memory according to a given difference
 * in the lengths of its new and old types.
 */

public loophole (oldlen, newlen)
integer oldlen, newlen;
{
    integer i, n;
    Stack *oldsp;

    n = newlen - oldlen;
    oldsp = sp - oldlen;
    if (n > 0) {
	for (i = oldlen - 1; i >= 0; i--) {
	    oldsp[n + i] = oldsp[i];
	}
	for (i = 0; i < n; i++) {
	    oldsp[i] = '\0';
	}
    } else {
	for (i = 0; i < newlen; i++) {
	    oldsp[i] = oldsp[i - n];
	}
    }
    sp += n;
}
