#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util/macros.h"
#include "afuc.h"
#include "asm.h"
#include "parser.h"
#include "util.h"
int gpuver;
static struct asm_instruction instructions[0x2000];
static unsigned num_instructions;
static struct asm_label labels[0x512];
static unsigned num_labels;
struct asm_instruction *
next_instr(int tok)
{
struct asm_instruction *ai = &instructions[num_instructions++];
assert(num_instructions < ARRAY_SIZE(instructions));
ai->tok = tok;
return ai;
}
void
decl_label(const char *str)
{
struct asm_label *label = &labels[num_labels++];
assert(num_labels < ARRAY_SIZE(labels));
label->offset = num_instructions;
label->label = str;
}
static int
resolve_label(const char *str)
{
int i;
for (i = 0; i < num_labels; i++) {
struct asm_label *label = &labels[i];
if (!strcmp(str, label->label)) {
return label->offset;
}
}
fprintf(stderr, "Undeclared label: %s\n", str);
exit(2);
}
static afuc_opc
tok2alu(int tok)
{
switch (tok) {
case T_OP_ADD:
return OPC_ADD;
case T_OP_ADDHI:
return OPC_ADDHI;
case T_OP_SUB:
return OPC_SUB;
case T_OP_SUBHI:
return OPC_SUBHI;
case T_OP_AND:
return OPC_AND;
case T_OP_OR:
return OPC_OR;
case T_OP_XOR:
return OPC_XOR;
case T_OP_NOT:
return OPC_NOT;
case T_OP_SHL:
return OPC_SHL;
case T_OP_USHR:
return OPC_USHR;
case T_OP_ISHR:
return OPC_ISHR;
case T_OP_ROT:
return OPC_ROT;
case T_OP_MUL8:
return OPC_MUL8;
case T_OP_MIN:
return OPC_MIN;
case T_OP_MAX:
return OPC_MAX;
case T_OP_CMP:
return OPC_CMP;
case T_OP_MSB:
return OPC_MSB;
default:
assert(0);
return -1;
}
}
static void
emit_instructions(int outfd)
{
int i;
i = 0;
write(outfd, &i, 4);
for (i = 0; i < num_instructions; i++) {
struct asm_instruction *ai = &instructions[i];
afuc_instr instr = {0};
afuc_opc opc;
if (i == 1) {
assert(ai->is_literal);
ai->literal &= ~0xffff;
ai->literal |= num_instructions;
}
if (ai->is_literal) {
write(outfd, &ai->literal, 4);
continue;
}
switch (ai->tok) {
case T_OP_NOP:
opc = OPC_NOP;
if (gpuver >= 6)
instr.pad = 0x1000000;
break;
case T_OP_ADD:
case T_OP_ADDHI:
case T_OP_SUB:
case T_OP_SUBHI:
case T_OP_AND:
case T_OP_OR:
case T_OP_XOR:
case T_OP_NOT:
case T_OP_SHL:
case T_OP_USHR:
case T_OP_ISHR:
case T_OP_ROT:
case T_OP_MUL8:
case T_OP_MIN:
case T_OP_MAX:
case T_OP_CMP:
case T_OP_MSB:
if (ai->has_immed) {
assert(ai->tok != T_OP_MSB);
if (ai->xmov) {
fprintf(stderr,
"ALU instruction cannot have immediate and xmov\n");
exit(1);
}
opc = tok2alu(ai->tok);
instr.alui.dst = ai->dst;
instr.alui.src = ai->src1;
instr.alui.uimm = ai->immed;
} else {
opc = OPC_ALU;
instr.alu.dst = ai->dst;
instr.alu.src1 = ai->src1;
instr.alu.src2 = ai->src2;
instr.alu.xmov = ai->xmov;
instr.alu.alu = tok2alu(ai->tok);
}
break;
case T_OP_MOV:
if ((ai->has_immed || ai->label) && ai->xmov) {
fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
exit(1);
}
if (ai->has_immed) {
opc = OPC_MOVI;
instr.movi.dst = ai->dst;
instr.movi.uimm = ai->immed;
instr.movi.shift = ai->shift;
} else if (ai->label) {
opc = OPC_MOVI;
instr.movi.dst = ai->dst;
instr.movi.uimm = resolve_label(ai->label);
instr.movi.shift = ai->shift;
} else {
opc = OPC_ALU;
instr.alu.dst = ai->dst;
instr.alu.src1 = 0x00;
instr.alu.src2 = ai->src1;
instr.alu.xmov = ai->xmov;
instr.alu.alu = OPC_OR;
}
break;
case T_OP_CWRITE:
case T_OP_CREAD:
case T_OP_STORE:
case T_OP_LOAD:
if (gpuver >= 6) {
if (ai->tok == T_OP_CWRITE) {
opc = OPC_CWRITE6;
} else if (ai->tok == T_OP_CREAD) {
opc = OPC_CREAD6;
} else if (ai->tok == T_OP_STORE) {
opc = OPC_STORE6;
} else if (ai->tok == T_OP_LOAD) {
opc = OPC_LOAD6;
}
} else {
if (ai->tok == T_OP_CWRITE) {
opc = OPC_CWRITE5;
} else if (ai->tok == T_OP_CREAD) {
opc = OPC_CREAD5;
} else if (ai->tok == T_OP_STORE || ai->tok == T_OP_LOAD) {
fprintf(stderr, "load and store do not exist on a5xx\n");
exit(1);
}
}
instr.control.src1 = ai->src1;
instr.control.src2 = ai->src2;
instr.control.flags = ai->bit;
instr.control.uimm = ai->immed;
break;
case T_OP_BRNE:
case T_OP_BREQ:
if (ai->has_immed) {
opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI;
instr.br.bit_or_imm = ai->immed;
} else {
opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB;
instr.br.bit_or_imm = ai->bit;
}
instr.br.src = ai->src1;
instr.br.ioff = resolve_label(ai->label) - i;
break;
case T_OP_RET:
opc = OPC_RET;
break;
case T_OP_IRET:
opc = OPC_RET;
instr.ret.interrupt = 1;
break;
case T_OP_CALL:
opc = OPC_CALL;
instr.call.uoff = resolve_label(ai->label);
break;
case T_OP_PREEMPTLEAVE:
opc = OPC_PREEMPTLEAVE6;
instr.call.uoff = resolve_label(ai->label);
break;
case T_OP_SETSECURE:
opc = OPC_SETSECURE;
if (resolve_label(ai->label) != i + 3) {
fprintf(stderr, "jump label %s is incorrect for setsecure\n",
ai->label);
exit(1);
}
if (ai->src1 != 0x2) {
fprintf(stderr, "source for setsecure must be $02\n");
exit(1);
}
break;
case T_OP_JUMP:
opc = OPC_BRNEB;
instr.br.bit_or_imm = 0;
instr.br.src = 0x00;
instr.br.ioff = resolve_label(ai->label) - i;
break;
case T_OP_WAITIN:
opc = OPC_WIN;
break;
default:
unreachable("");
}
afuc_set_opc(&instr, opc, ai->rep);
write(outfd, &instr, 4);
}
}
unsigned
parse_control_reg(const char *name)
{
return afuc_control_reg(name + 1);
}
static void
emit_jumptable(int outfd)
{
uint32_t jmptable[0x80] = {0};
int i;
for (i = 0; i < num_labels; i++) {
struct asm_label *label = &labels[i];
int id = afuc_pm4_id(label->label);
if (id < 0) {
if (sscanf(label->label, "UNKN%d", &id) != 1) {
continue;
}
}
jmptable[id] = label->offset;
}
write(outfd, jmptable, sizeof(jmptable));
}
static void
usage(void)
{
fprintf(stderr, "Usage:\n"
"\tasm [-g GPUVER] filename.asm filename.fw\n"
"\t\t-g - specify GPU version (5, etc)\n");
exit(2);
}
int
main(int argc, char **argv)
{
FILE *in;
char *file, *outfile;
int c, ret, outfd;
while ((c = getopt(argc, argv, "g:")) != -1) {
switch (c) {
case 'g':
gpuver = atoi(optarg);
break;
default:
usage();
}
}
if (optind >= (argc + 1)) {
fprintf(stderr, "no file specified!\n");
usage();
}
file = argv[optind];
outfile = argv[optind + 1];
outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outfd < 0) {
fprintf(stderr, "could not open \"%s\"\n", outfile);
usage();
}
in = fopen(file, "r");
if (!in) {
fprintf(stderr, "could not open \"%s\"\n", file);
usage();
}
yyset_in(in);
if (!gpuver) {
if (strstr(file, "a5")) {
gpuver = 5;
} else if (strstr(file, "a6")) {
gpuver = 6;
}
}
ret = afuc_util_init(gpuver, false);
if (ret < 0) {
usage();
}
ret = yyparse();
if (ret) {
fprintf(stderr, "parse failed: %d\n", ret);
return ret;
}
emit_instructions(outfd);
emit_jumptable(outfd);
close(outfd);
return 0;
}