/* qbe.c
 *
 * Andy Goth <unununium@openverse.com>
 *
 * Derived from, and hopefully compatible with, GIMP's gqbist.c, originally by
 * Jens Ch. Restemeier <jrestemeier@currantbun.com>.  gqbist.c is available
 * under the GPL (GNU General Public License), version 2 or later.  Therefore,
 * this file is also available under the GPL.  And there was much rejoicing. */

#include <stdio.h>
#include <stdlib.h>

#include "qbism.h"

/* Reads a QBE file into an algorithm structure. */
void algo_read_qbe(algo_t* algo, FILE* in)
{
    int i, j;
    int ret;
    char buf[8];
    int* data = NULL;
    int data_len = 0;
    int data_cap = 0;
    algo->num_steps = 0;
    algo->num_regs = 0;

    /* Read all data. */
    while (1) {
        /* If necessary, grow the sequence buffer. */
        if (data_len == data_cap) {
            if (data_cap == 0) {
                data_cap = 36 * 4;
            } else {
                data_cap *= 2;
            }
            data = xrealloc(data, sizeof(*data) * data_cap);
        }

        /* Read some bits! */
        ret = fread(buf, 1, sizeof(buf), in);
        if (ret != sizeof(buf)) {
            if (ferror(in)) {
                perror("fread");
                exit(EXIT_FAILURE);
            }
            if (ret == 0) {
                break;
            } else {
                fprintf(stderr, "Malformatted QBE input file.\n");
                exit(EXIT_FAILURE);
            }
        }

        /* Store into the temporary data buffer. */
        for (i = 0; i < 4; ++i) {
            data[data_len + i] = (buf[i * 2 + 0] << 8) | (buf[i * 2 + 1] << 0);
        }
        data_len += 4;
        ++algo->num_steps;
    }

    /* Store into the algorithm structure.  Also, identify the highest-numbered
     * register. */
    algo->seq = xmalloc(sizeof(*algo->seq) * algo->num_steps);
    for (i = 0; i < algo->num_steps; ++i) {
        algo->seq[i].opcode  = data[algo->num_steps * 0 + i];
        algo->seq[i].source  = data[algo->num_steps * 1 + i];
        algo->seq[i].control = data[algo->num_steps * 2 + i];
        algo->seq[i].dest    = data[algo->num_steps * 3 + i];

        for (j = 1; j <= 3; ++j) {
            if (data[algo->num_steps * j + i] > algo->num_regs) {
                algo->num_regs = data[algo->num_steps * j + i];
            }
        }
    }

    ++algo->num_regs;
    free(data);
}

/* Wirtes an algorithm structure to a QBE file. */
void algo_write_qbe(algo_t* algo, FILE* out)
{
    int i;

    for (i = 0; i < algo->num_steps; ++i) {
        fputc(algo->seq[i].opcode >> 8, out);
        fputc(algo->seq[i].opcode >> 0, out);
    }
    for (i = 0; i < algo->num_steps; ++i) {
        fputc(algo->seq[i].source >> 8, out);
        fputc(algo->seq[i].source >> 0, out);
    }
    for (i = 0; i < algo->num_steps; ++i) {
        fputc(algo->seq[i].control >> 8, out);
        fputc(algo->seq[i].control >> 0, out);
    }
    for (i = 0; i < algo->num_steps; ++i) {
        fputc(algo->seq[i].dest >> 8, out);
        fputc(algo->seq[i].dest >> 0, out);
    }
}

/* vim: set ts=4 sts=4 sw=4 tw=80 et: */

