
/* PDC Compiler - A Freely Distributable C Compiler for the Amiga
 *                Based upon prior work by Matthew Brandt and Jeff Lydiatt.
 *
 * PDC Compiler release 3.3 Copyright (C) 1989 Paul Petersen and Lionel Hummel.
 * PDC Software Distribution (C) 1989 Lionel Hummel and Paul Petersen.
 *
 * This code is freely redistributable upon the conditions that this 
 * notice remains intact and that modified versions of this file not be 
 * distributed as part of the PDC Software Distribution without the express
 * consent of the copyright holders.
 */

/*
 * Outcode.c
 * 
 * Generates the actual assembly text from the code generator's directions.
 */

#include    <stdio.h>
#include    "C.h"
#include    "Expr.h"
#include    "Gen.h"
#include    "Cglbdec.h"

extern char    *litlate();
extern char    *xalloc();

void    put_mask(), nl(), cseg();

/* variable initialization     */

enum e_gt {
    nogen, bytegen, wordgen, longgen, singlegen, doublegen
};

enum e_sg {
    noseg, codeseg, dataseg, bssseg
};

union double_int {
    double          dval;
    float           fval;
    int             ival[2];
};

enum e_gt       gentype = nogen;
enum e_sg       curseg = noseg;
int             outcol = 0;

static int      opl_len = 0;

struct oplst {
    char           *s;
    enum e_op       ov;
};

struct oplst    opl[] = {
    {"abcd", op_abcd},
    {"add", op_add},
    {"add", op_adda},
    {"add", op_addi},
    {"addq", op_addq},
    {"add", op_addx},
    {"and", op_and},
    {"andi", op_andi},
    {"asl", op_asl},
    {"???", op_asm},
    {"asr", op_asr},
    {"bcc", op_bcc},
    {"bchg", op_bchg},
    {"bclr", op_bclr},
    {"bcs", op_bcs},
    {"beq", op_beq},
    {"bge", op_bge},
    {"bgt", op_bgt},
    {"bhi", op_bhi},
    {"ble", op_ble},
    {"bls", op_bls},
    {"blt", op_blt},
    {"bmi", op_bmi},
    {"bne", op_bne},
    {"bpl", op_bpl},
    {"bra", op_bra},
    {"bset", op_bset},
    {"bsr", op_bsr},
    {"btst", op_btst},
    {"bvc", op_bvc},
    {"bvs", op_bvs},
    {"chk", op_chk},
    {"clr", op_clr},
    {"cmp", op_cmp},
    {"cmp", op_cmpa},
    {"cmp", op_cmpi},
    {"cmp", op_cmpm},
    {";;;", op_comment},
    {"dbcc", op_dbcc},
    {"dbcs", op_dbcs},
    {"dbeq", op_dbeq},
    {"dbf", op_dbf},
    {"dbge", op_dbge},
    {"dbgt", op_dbgt},
    {"dbhi", op_dbhi},
    {"dble", op_dble},
    {"dbls", op_dbls},
    {"dbmi", op_dbmi},
    {"dbne", op_dbne},
    {"dbpl", op_dbpl},
    {"dbra", op_dbra},
    {"dbt", op_dbt},
    {"dbvc", op_dbvc},
    {"dbvs", op_dbvs},
    {"dc", op_dc},
    {"divs", op_divs},
    {"divu", op_divu},
    {"eor", op_eor},
    {"eori", op_eori},
    {"exg", op_exg},
    {"ext", op_ext},
    {"???", op_fadd},
    {"???", op_fdiv},
    {"???", op_fmod},
    {"???", op_fmove},
    {"???", op_fmul},
    {"???", op_fneg},
    {"???", op_fsub},
    {"jmp", op_jmp},
    {"jsr", op_jsr},
    {"???", op_label},
    {"lea", op_lea},
    {"link", op_link},
    {"lsl", op_lsl},
    {"lsr", op_lsr},
    {"???", op_mods},
    {"???", op_modu},
    {"move", op_move},
    {"move", op_movea},
    {"movem", op_movem},
    {"movep", op_movep},
    {"moveq", op_moveq},
    {"muls", op_muls},
    {"mulu", op_mulu},
    {"nbcd", op_nbcd},
    {"neg", op_neg},
    {"negx", op_negx},
    {"nop", op_nop},
    {"not", op_not},
    {"or", op_or},
    {"ori", op_ori},
    {"pea", op_pea},
    {"reset", op_reset},
    {"rol", op_rol},
    {"ror", op_ror},
    {"roxl", op_roxl},
    {"roxr", op_roxr},
    {"rte", op_rte},
    {"rtr", op_rtr},
    {"rts", op_rts},
    {"sbcd", op_sbcd},
    {"scc", op_scc},
    {"scs", op_scs},
    {"seq", op_seq},
    {"sf", op_sf},
    {"sge", op_sge},
    {"sgt", op_sgt},
    {"shi", op_shi},
    {"sle", op_sle},
    {"sls", op_sls},
    {"slt", op_slt},
    {"smi", op_smi},
    {"sne", op_sne},
    {"spl", op_spl},
    {"st", op_st},
    {"stabn", op_stabn},
    {"stabs", op_stabs},
    {"stop", op_stop},
    {"sub", op_sub},
    {"sub", op_suba},
    {"sub", op_subi},
    {"subq", op_subq},
    {"sub", op_subx},
    {"svc", op_svc},
    {"svs", op_svs},
    {"swap", op_swap},
    {"tas", op_tas},
    {"trap", op_trap},
    {"trapv", op_trapv},
    {"tst", op_tst},
    {"unlk", op_unlk},
    {(char *) 0, (enum e_op) 0}
};

static char     buffer[64];

char           *
itoa(x)
    int             x;
{
    char           *ptr;
    int             d, negative;

    ptr = &buffer[sizeof(buffer) - 1];
    *ptr = '\0';

    if (x == 0x80000000)    /* The number where x = -x */
        return ("-2147483648");

    negative = 0;
    if (x < 0) {
        negative = 1;
        x = -x;
    }

    do {
        d = x % 10;
        if (d < 0 || d > 9) {
            fputs("DIAG -- itoa has a problem\n", stderr);
            return (ptr);
        }
        x = x / 10;
        *(--ptr) = '0' + d;
    } while (x > 0);

    if (negative)
        *(--ptr) = '-';
    return (ptr);
}

void
putint(r)
    int             r;
{
    fputs(itoa(r), output);
}

void
putsingle(d)
    double          d;
{
    union double_int temp;

    temp.fval = d;
    fputs(itoa(temp.ival[0]), output);
}

void
putdouble(d)
    double          d;
{
    union double_int temp;

    temp.dval = d;
    fputs(itoa(temp.ival[0]), output);
    fputs(",", output);
    fputs(itoa(temp.ival[1]), output);
}

void
putareg(reg)
    int             reg;
{
    fputs("A", output);
    putint(reg);
}

void
putdreg(reg)
    int             reg;
{
    fputs("D", output);
    putint(reg);
}

int
sortopl()
{
    struct oplst   *p1, *p2, temp;
    int             changed = 0;

    p1 = opl;
    for (p2 = p1 + 1; p2->s != NULL; p1++, p2++) {
        if ((int) p1->ov > (int) p2->ov) {
            changed = 1;
            temp = *p1;
            *p1 = *p2;
            *p2 = temp;
        }
    }
    return changed;
}

void
putop(op)
    enum e_op       op;
{
    int             low, high, mid;
    struct oplst   *ptr;

    if (opl_len == 0) {
        for (ptr = opl; ptr->s != NULL; ptr++)
            ++opl_len;
        while (sortopl());
    }

    low = 0;
    high = opl_len - 1;

    do {
        mid = (low + high) / 2;
        ptr = &opl[mid];
        if (ptr->ov == op) {
            fputs("\t", output);
            fputs(ptr->s, output);
            return;
        }
        else {
            if ((int) op > (int) ptr->ov)
                low = mid + 1;
            else
                high = mid - 1;
        }
    } while (low <= high);

    fputs("DIAG -- illegal opcode.\n", stderr);
}

/*
 * put a constant to the output file.
 */

void
putconst(offset)
    struct enode   *offset;
{
    if (offset == NULL) {
        fputs("DIAG -- NULL argument to putconst.\n", stderr);
        return;
    }
    switch (offset->nodetype) {
    case en_autocon:
        putint(offset->v.i);
        fputs("(", output);
        putareg(Options.Frame);
        fputs(")", output);
        break;
    case en_icon:
        putint(offset->v.i);
        break;
    case en_fcon:
        putdouble(offset->v.f);
        break;
    case en_labcon:
        fputs("L", output);
        putint(offset->v.i);
        break;
    case en_nacon:
        fputs("_", output);
        fputs(offset->v.p[0], output);
        break;
    case en_intrlab:
        fputs(".", output);
        fputs(offset->v.p[0], output);
        break;
    case en_add:
        putconst(offset->v.p[0]);
        fputs("+", output);
        putconst(offset->v.p[1]);
        break;
    case en_sub:
        putconst(offset->v.p[0]);
        fputs("-", output);
        putconst(offset->v.p[1]);
        break;
    case en_uminus:
        fputs("-", output);
        putconst(offset->v.p[0]);
        break;
    default:
        fputs("DIAG -- ", stderr );
        fputs( itoa(offset->nodetype), stderr );
        fputs("-- illegal constant node.\n", stderr);
        break;
    }
}

void
putlen(l)

/*
 * append the length field to an instruction.
 */
    int             l;
{
    switch (l) {
    case 0:
        break;      /* no length field */
    case 1:
        fputs(".b", output);
        break;
    case 2:
        fputs(".w", output);
        break;
    case 4:
        fputs(".l", output);
        break;
    case 8:
        fputs(".f", output);
    default:
        fputs(".x", output);
        fputs("DIAG -- illegal length field.\n", stderr);
        break;
    }
}

void
putamode(ap)

/*
 * output a general addressing mode.
 */
    struct amode   *ap;
{
    if (ap == NULL) {
        fputs("DIAG -- NULL argument to putamode.\n", stderr);
        return;
    }
    switch (ap->mode) {
    case am_immed:
        fputs("#", output);
        putconst(ap->offset);
        break;
    case am_direct:
        putconst(ap->offset);
        break;
    case am_areg:
        putareg(ap->preg);
        break;
    case am_dreg:
        putdreg(ap->preg);
        break;
    case am_freg:
        putdreg((enum e_am) 0);
        break;
    case am_ind:
        fputs("(", output);
        putareg(ap->preg);
        fputs(")", output);
        break;
    case am_ainc:
        fputs("(", output);
        putareg(ap->preg);
        fputs(")+", output);
        break;
    case am_adec:
        fputs("-(", output);
        putareg(ap->preg);
        fputs(")", output);
        break;
    case am_indx:
        putconst(ap->offset);
        fputs("(", output);
        putareg(ap->preg);
        fputs(")", output);
        break;
    case am_xpc:
        putconst(ap->offset);
        fputs("(", output);
        putdreg(ap->preg);
        fputs(",pc)", output);
        break;
    case am_indx2:
        putconst(ap->offset);
        fputs("(", output);
        putareg(ap->preg);
        fputs(",", output);
        putdreg(ap->sreg);
        fputs(".l)", output);
        break;
    case am_indx3:
        putconst(ap->offset);
        fputs("(", output);
        putareg(ap->preg);
        fputs(",", output);
        putareg(ap->sreg);
        fputs(".l)", output);
        break;
    case am_mask:
        put_mask((long) (ap->offset));
        break;
    default:
        fputs("DIAG -- illegal address mode.\n", stderr);
        fputs("<DIAG -- illegal address mode.>", output);
        break;
    }
}

void
put_code(op, len, aps, apd)

/*
 * output a generic instruction.
 */
    struct amode   *aps, *apd;
    enum e_op       op;
    int             len;
{
    char           *comment;
    struct dnode   *dp;

    switch (op) {
    case op_comment:
        comment = aps->offset->v.sp;
        nl();
        fputs(";;;\t", output);
        fputs(comment, output);
        fputs("\n", output);
        return;
    case op_asm:
        comment = aps->offset->v.sp;
        nl();
        fputs(comment, output);
        fputs("\n", output);
        return;
    case op_stabn:
        dp = aps->offset->v.dp;
        fputs("\tSTABN\t", output);
        fputs(itoa(dp->tag), output);
        fputs(",0,", output);
        fputs(itoa(dp->nest), output);
        fputs(",", output);
        putconst(dp->ref);
        fputs("\n", output);
        return;
    case op_stabs:
        dp = aps->offset->v.dp;
        if (dp->ref->nodetype == en_autocon || dp->ref->nodetype == en_tempref)
            dp->ref->nodetype = en_icon;
        comment = dp->sp;
        do {
            fputs("\tSTABS\t\"", output);

            while (*comment && (*comment != '\\')) {
                fputc(*comment, output);
                comment++;
                if (*comment == '\0' && dp->next != NULL) {
                    dp = dp->next;
                    comment = dp->sp;
                }
            }

            if (*comment) {
                fputc(*comment++, output);
                fputc(*comment++, output);
            }

            fputs("\",", output);
            fputs(itoa(dp->tag), output);
            fputs(",0,", output);
            fputs(itoa(dp->nest), output);
            fputs(",", output);
            putconst(dp->ref);
            fputs("\n", output);

            if (*comment == '\0' && dp->next != NULL) {
                dp = dp->next;
                comment = dp->sp;
            }
        } while (*comment);
        return;
    case op_dc:
        switch (len) {
        case 1:
            fputs("\tDC.b", output);
            break;
        case 2:
            fputs("\tDC.w", output);
            break;
        case 4:
            fputs("\tDC.l", output);
            break;
        case 8:
            fputs("\tDC.l", output);
        }
        break;
    default:
        putop(op);
        putlen(len);
        break;
    }

    if (aps != NULL) {
        fputs("\t", output);
        if (op == op_cmp || op == op_cmpi)
            putamode(apd);
        else
            putamode(aps);
        if (apd != NULL) {
            fputs(",", output);
            if (op == op_cmp || op == op_cmpi)
                putamode(aps);
            else
                putamode(apd);
        }
    }
    fputs("\n", output);
}

void
startRange(bit)
    int    bit;
{
    if (bit <= 7)
        putdreg(bit % 8);
    else
        putareg(bit % 8);
}

void
endRange(first, bit)
    int    first, bit;
{
    if (first <= 7 && bit > 7) {
        endRange(first, 7);
        fputs("/", output);
        startRange(8);
        first = 8;
    }
    if (bit > first) {
        fputs("-", output);
        startRange(bit);
    }
}

void
put_mask(mask1)

/*
 * generate a register mask for restore and save.
 */
    long            mask1;
{
    int             mask;
    int             bit;
    int             inrange;
    int             some;

    mask = mask1;
    inrange = -1;
    some = 0;

    for (bit = 0; bit <= 15; ++bit) {
        if ((mask >> bit) & 1) {
            if (inrange < 0) {
                if (some)
                    fputs("/", output);
                startRange(bit);
                some = 1;
                inrange = bit;
            }
        }
        else if (inrange >= 0) {
            endRange(inrange, bit - 1);
            inrange = -1;   /* was -= 1 PMP */
        }
    }
    if (inrange >= 0)
        endRange(inrange, 15);
}

void
putreg(r)

/*
 * generate a register name from a tempref number.
 */
    int             r;
{
    if (r < 8)
        putdreg(r);
    else
        putareg(r - 8);
}

void
gen_strlab(s)

/*
 * generate a named label.
 */
    char           *s;
{
    fputs("_", output);
    fputs(s, output);
    fputs(":\n", output);
}

void
put_label(lab)

/*
 * output a compiler generated label.
 */
    long            lab;
{
    fputs("L", output);
    putint(lab);
    fputs("\n", output);
}

void
genbyte(val)
    int             val;
{
    if (gentype == bytegen && outcol < 60) {
        fputs(",", output);
        putint(val & 0x00ff);
        outcol += 4;
    }
    else {
        nl();
        fputs("\tDC.b\t", output);
        putint(val & 0x00ff);
        gentype = bytegen;
        outcol = 19;
    }
}

void
genword(val)
    long            val;
{
    if (gentype == wordgen && outcol < 58) {
        fputs(",", output);
        putint(val & 0x0ffff);
        outcol += 6;
    }
    else {
        nl();
        fputs("\tDC.w\t", output);
        putint(val & 0x0ffff);
        gentype = wordgen;
        outcol = 21;
    }
}

void
genlong(val)
    long            val;
{
    if (gentype == longgen && outcol < 56) {
        fputs(",", output);
        putint(val);
        outcol += 10;
    }
    else {
        nl();
        fputs("\tDC.l\t", output);
        putint(val);
        gentype = longgen;
        outcol = 25;
    }
}

void
gensingle(val)
    double          val;
{
    if (gentype == singlegen && outcol < 56) {
        fputs(",", output);
        putsingle(val);
        outcol += 10;
    }
    else {
        nl();
        fputs("\tDC.l\t", output);
        putsingle(val);
        gentype = singlegen;
        outcol = 35;
    }
}

void
gendouble(val)
    double          val;
{
    if (gentype == doublegen && outcol < 56) {
        fputs(",", output);
        putdouble(val);
        outcol += 20;
    }
    else {
        nl();
        fputs("\tDC.l\t", output);
        putdouble(val);
        gentype = doublegen;
        outcol = 45;
    }
}

void
genref(sp, offset)
    SYM            *sp;
    int             offset;
{
    char            sign;

    if (sp == NULL) {
        fputs("DIAG -- NULL argument to genref.\n", stderr);
        return;
    }
    if (offset >= 0) 
        sign = '+';
    else {
        sign = '-';
        offset = -offset;
    }

    if (gentype == longgen && outcol < 55 - strlen(sp->name)) {
        if (sp->storage_class == sc_static) {
            fputs(",L", output);
            fputs(itoa(sp->value.i), output);
            if (offset != 0) {
                fputc(sign, output);
                fputs(itoa(offset), output);
            }
        }
        else {
            fputs(",_", output);
            fputs(sp->name, output);
            if (offset != 0) {
                fputc(sign, output);
                fputs(itoa(offset), output);
            }
        }
        outcol += (11 + strlen(sp->name));
    }
    else {
        nl();
        if (sp->storage_class == sc_static) {
            fputs("\tDC.l\tL", output);
            fputs(itoa(sp->value.i), output);
            if (offset != 0) {
                fputc(sign, output);
                fputs(itoa(offset), output);
            }
        }
        else {
            fputs("\tDC.l\t_", output);
            fputs(sp->name, output);
            if (offset != 0) {
                fputc(sign, output);
                fputs(itoa(offset), output);
            }
        }
        outcol = 26 + strlen(sp->name);
        gentype = longgen;
    }
}

int
genalignment(align)
    int             align;
{
    nl();
    switch (align) {
    case 0:
    case 1:
        break;
    case 2:
        fputs("\tCNOP\t0,2\n", output);
        break;
    case 4:
        fputs("\tCNOP\t0,4\n", output);
        break;
    case 8:
        fputs("\tCNOP\t0,8\n", output);
        break;
    default:
        fputs("DIAG -- invalid alignment\n", stderr);
        break;
    }
    return (align);
}

void
genstorage(nbytes)
    int             nbytes;
{
    nl();
    fputs("\tDS.b\t", output);
    putint(nbytes);
    fputs("\n", output);
}

void
gen_labref(n, offset)
    int             n;
    int             offset;
{
    if (gentype == longgen && outcol < 58) {
        fputs(",L", output);
        putint(n);
        if (offset != 0) {
            if (offset > 0) {
                fputs("+", output);
                fputs(itoa(offset), output);
            }
            else {
                fputs("-", output);
                fputs(itoa(-offset), output);
            }
        }
        outcol += 6;
    }
    else {
        nl();
        fputs("\tDC.l\tL", output);
        putint(n);
        if (offset != 0) {
            if (offset > 0) {
                fputs("+", output);
                fputs(itoa(offset), output);
            }
            else {
                fputs("-", output);
                fputs(itoa(-offset), output);
            }
        }
        outcol = 22;
        gentype = longgen;
    }
}


long
stringconcat(index, s)
    int             index;
    char           *s;
{
    char           *buf;
    int             length;
    struct slit    *lp;

    if (s == NULL) {
        fputs("DIAG -- NULL argument to stringconcat.\n", stderr);
        return (0);
    }

    for (lp = strtab; lp != NULL; lp = lp->next)
        if (lp->label == index)
            break;

    if (lp != NULL) {
        length = strlen(s);
        ++global_flag;  /* Allocate from the global space   */
        buf = (char *) xalloc(lp->len + length + 1);
        bcopy(lp->str, buf, lp->len);
        lp->str = buf;
        buf += lp->len;
        bcopy(s, buf, length + 1);
        lp->len += length;
        --global_flag;
        return (index);
    }
    return (0);
}

long
stringlit(s)

/*
 * make s a string literal and return it's label number.
 */
    char           *s;
{
    struct slit    *lp;

    if (s == NULL) {
        fputs("DIAG -- NULL argument to stringlit.\n", stderr);
        return (0);
    }
    ++global_flag;      /* always allocate from global space. */
    lp = (struct slit *) xalloc(sizeof(struct slit));
    lp->label = nextlabel++;
    lp->str = litlate(s);
    lp->len = strlen(s);
    lp->type = sconst;
    lp->next = strtab;
    strtab = lp;
    --global_flag;
    return lp->label;
}

long
floatlit(d)
    double          d;
{
    double         *dptr;
    struct slit    *lp;

    ++global_flag;      /* always allocate from global space. */
    lp = (struct slit *) xalloc(sizeof(struct slit));
    lp->label = nextlabel++;
    lp->str = (char *) xalloc(sizeof(double));
    lp->len = sizeof(double);
    lp->next = strtab;
    lp->type = rconst;
    strtab = lp;
    --global_flag;
    dptr = (double *) lp->str;
    *dptr = d;
    return lp->label;
}

void           *
getlit(num)
    int             num;
{
    struct slit    *lp;
    void           *ptr;

    if (strtab != NULL) {
        lp = strtab;
        for (lp = strtab; lp != NULL; lp = lp->next) {
            if (lp->label == num) {
                ptr = (void *) lp->str;
                return (ptr);
            }
        }
    }
    return (NULL);
}

void           *
remlit(num)
    int             num;
{
    struct slit    *lp, **last;
    void           *ptr;

    if (strtab != NULL) {
        lp = strtab;
        last = &strtab;
        while (lp != NULL) {
            if (lp->label == num) {
                *last = lp->next;
                ptr = (void *) lp->str;
                return (ptr);
            }
            last = &lp->next;
            lp = lp->next;
        }
    }
    return (NULL);
}

void
dumplits()

/*
 * dump the string literal pool.
 */
{
    char           *cp;
    int             i, count;

    while (strtab != NULL) {
        cseg();
        nl();
        put_label((long) (strtab->label));
        cp = strtab->str;
        count = strtab->len;
        for (i = 0; i < strtab->len; i++) {
            genbyte(*cp++);
        }
        if (strtab->type == sconst) {
            ++count;
            genbyte(0);
        }
        if ((count % 2) != 0) {
            genbyte(0);
        }
        strtab = strtab->next;
    }
    nl();
}

void
nl()
{
    if (outcol > 0) {
        fputs("\n", output);
        outcol = 0;
        gentype = nogen;
    }
}

void
cseg()
{
    if (curseg != codeseg) {
        nl();
        fputs("\tSECTION\t", output );
        fputs( code_name, output );
        fputs(",CODE\n", output );
        curseg = codeseg;
    }
}

void
dseg()
{
    if (curseg != dataseg) {
        nl();
        fputs("\tSECTION\t", output );
        fputs( data_name, output );
        fputs(",DATA\n", output );
        curseg = dataseg;
    }
}

void
bseg()
{
    if (curseg != bssseg) {
        nl();
        fputs("\tSECTION\t", output );
        fputs( bss_name, output );
        fputs(",BSS\n", output );
        curseg = bssseg;
    }

}
