/*
* Mimas conversion tools
*
* Copyright (C) 2010 Benjamin Moody
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "mimas.h"
#include "convert.h"
#include "utils.h"
#include "insts.h"
#include "itempl.h"
#include "byvalue.h"
typedef struct _constant_entry {
unsigned int value;
char *str;
} constant_entry;
typedef struct _import_entry {
unsigned char type;
char name[9];
} import_entry;
typedef struct _section_entry {
int type;
unsigned int data_start;
unsigned int data_end;
char name[9];
} section_entry;
/**************** String unpacking ****************/
/* Get length of a packed symbol string */
static int symbol_length(const unsigned char *data, int limit)
{
int i;
for (i = 0; i < limit; i++)
if ((data[i] & 0x0f) == 0)
return i + 1;
return limit;
}
/* Unpack and convert a symbol string (see UnpackSymbolString) */
static char *convert_symbol(const unsigned char *data, int limit)
{
static const char packed_chars[12] =
"`adeilnorst_";
static const char prefix_chars[64] =
"``````9876543210"
"`ABCDEFGHIJKLMNO"
"`z``[ZYXWVUTSRQP"
"`bcfghjkmpquvwxy";
char *str, *converted;
unsigned int n, prefix = 0;
int packedlength, pos, h, length;
packedlength = symbol_length(data, limit);
str = xnew(char, (packedlength * 2) + 1);
pos = 0;
if (data[0] & 0xf0) {
length = 0;
h = 1;
}
else {
str[0] = '.';
length = 1;
h = 0;
}
while (pos < packedlength) {
if (h) {
n = data[pos] >> 4;
h = 0;
}
else {
n = data[pos] & 0x0f;
h = 1;
pos++;
}
if (n == 0)
break;
if (prefix) {
str[length++] = prefix_chars[n + prefix - 0xc0];
prefix = 0;
}
else if (n < 0x0c) {
str[length++] = packed_chars[n];
}
else {
prefix = n << 4;
}
}
if (prefix)
str[length++] = '`';
str[length] = 0;
converted = ti83p_to_ascii(str);
xfree(str);
return converted;
}
/* Unpack and convert a comment string (see UnpackCommentString) */
static char *convert_comment(const unsigned char *data, int packedlength)
{
static const char packed_chars[11] =
" adeilnorst";
static const char prefix_chars[80] =
"\301!\"#$%&'()*+,-./"
"?>=<;:9876543210"
"@ABCDEFGHIJKLMNO"
"_z]\\[ZYXWVUTSRQP"
"^bcfghjkmpquvwxy";
char *str, *converted;
unsigned int n, prefix = 0;
int length, pos, h;
str = xnew(char, (packedlength * 2) + 1);
length = pos = 0;
h = 1;
while (pos < packedlength) {
if (h) {
n = data[pos] >> 4;
h = 0;
}
else {
n = data[pos] & 0x0f;
h = 1;
pos++;
}
if (prefix) {
str[length++] = prefix_chars[n + prefix - 0xb0];
prefix = 0;
}
else if (n < 0x0b) {
str[length++] = packed_chars[n];
}
else {
prefix = n << 4;
}
}
if (prefix)
str[length++] = '`';
while (length > 0 && str[length - 1] == ' ')
length--;
str[length] = 0;
converted = ti83p_to_ascii(str);
xfree(str);
return converted;
}
/**************** Builtins ****************/
static int compare_builtins(const void *a, const void *b)
{
const builtin_symbol *sa = a;
const builtin_symbol *sb = b;
if (sa->value < sb->value)
return -1;
else if (sa->value > sb->value)
return 1;
else
return 0;
}
/* Get name of a builtin symbol */
static const char *find_builtin_by_value(unsigned int type, unsigned int value)
{
const builtin_symbol *builtins, *b;
unsigned int nbuiltins;
builtin_symbol k;
switch (type) {
case 0:
builtins = sysFlagByValueTable;
nbuiltins = sizeof(sysFlagByValueTable) / sizeof(builtin_symbol);
k.value = value;
break;
case X_ROMCALL:
builtins = romCallByValueTable;
nbuiltins = sizeof(romCallByValueTable) / sizeof(builtin_symbol);
k.value = value;
break;
case X_SYSADDR:
builtins = sysAddrByValueTable;
nbuiltins = sizeof(sysAddrByValueTable) / sizeof(builtin_symbol);
k.value = value;
break;
case X_SYSFLAG:
case X_SCANCODE:
case X_DATATYPE:
case X_KEY:
case X_KEY_FB:
case X_KEY_FC:
builtins = sysEnumByValueTable;
nbuiltins = sizeof(sysEnumByValueTable) / sizeof(builtin_symbol);
k.value = (type << 8) | value;
break;
default:
return NULL;
}
b = bsearch(&k, builtins, nbuiltins, sizeof(builtin_symbol), &compare_builtins);
if (b)
return b->name;
else
return NULL;
}
/**************** Expressions ****************/
/* Get length of an expression */
static int expr_length(const unsigned char *exp, int limit)
{
int i, depth;
depth = 1;
i = 0;
while (i < limit && depth != 0) {
if (exp[i] < 0x40) {
/* symbol */
i += 2;
depth--;
}
else if (exp[i] < 0x50) {
/* special */
i++;
depth--;
}
else if (exp[i] < 0x60) {
/* byte */
i += 2;
depth--;
}
else if (exp[i] < 0x70) {
/* word */
i += 3;
depth--;
}
else if (exp[i] < 0x90) {
/* reserved expression codes */
return limit;
}
else if (exp[i] < 0xa0) {
/* unary operator */
i++;
}
else if (exp[i] < 0xc0) {
/* binary operator */
i++;
depth++;
}
else {
/* dec6 constant */
i++;
depth--;
}
}
if (i < limit)
return i;
else
return limit;
}
/* Registers */
static const struct {
unsigned int value;
const char name[5];
} registers[] =
{{ X_REG_B, "b" },
{ X_REG_C, "c" },
{ X_REG_D, "d" },
{ X_REG_E, "e" },
{ X_REG_H, "h" },
{ X_REG_L, "l" },
{ X_REG_iHL, "(hl)" },
{ X_REG_A, "a" },
{ X_REG_BC, "bc" },
{ X_REG_DE, "de" },
{ X_REG_HL, "hl" },
{ X_REG_SP, "sp" },
{ X_REG_IX, "ix" },
{ X_REG_IY, "iy" },
{ X_REG_AF2, "af'" },
{ X_REG_AF, "af" },
{ X_REG_I, "i" },
{ X_REG_R, "r" },
{ X_REG_IXH, "ixh" },
{ X_REG_IXL, "ixl" },
{ X_REG_IYH, "iyh" },
{ X_REG_IYL, "iyl" },
{ X_COND_NZ, "nz" },
{ X_COND_Z, "z" },
{ X_COND_NC, "nc" },
{ X_COND_C, "c" },
{ X_COND_PO, "po" },
{ X_COND_PE, "pe" },
{ X_COND_P, "p" },
{ X_COND_M, "m" }};
#define NUM_REGISTERS 30
/* Binary operators and parenthesization info (see FormatExpr and the
binaryOperatorParenTable) */
static const struct {
const char s[3];
unsigned int pclass;
unsigned int pmask;
} binary_operators[] =
{{ "*", 0x80, 0x7F },
{ "/", 0x40, 0x7F },
{ "%", 0x40, 0x7F },
{ "+", 0x20, 0x1F },
{ "-", 0x20, 0x1F },
{ "<<", 0x10, 0x3F },
{ ">>", 0x10, 0x3F },
{ ">", 0x08, 0x0F },
{ "<", 0x08, 0x0F },
{ ">=", 0x08, 0x0F },
{ "<=", 0x08, 0x0F },
{ "==", 0x08, 0x0F },
{ "!=", 0x08, 0x0F },
{ "&", 0x04, 0xFB },
{ "^", 0x02, 0xFD },
{ "|", 0x01, 0xFE }};
static int print_hex(FILE *outf, unsigned int n, int width)
{
if (n >= 0xa000)
return fprintf(outf, "%05Xh", n);
else if (n >= 0xa00 || width == 4)
return fprintf(outf, "%04Xh", n);
else if (n >= 0xa0)
return fprintf(outf, "%03Xh", n);
else
return fprintf(outf, "%02Xh", n);
}
static int print_oct(FILE *outf, unsigned int n, int width)
{
if (width == 6)
return fprintf(outf, "%06oo", n);
else
return fprintf(outf, "%03oo", n);
}
static int print_bin(FILE *outf, unsigned int n, int width)
{
int i;
for (i = width - 1; i >= 0; i--)
fputc('0' + ((n >> i) & 1), outf);
fputc('b', outf);
return width + 1;
}
static int print_quoted_char(FILE *outf, int c, int q)
{
if (c == q)
return fprintf(outf, "\\%c", c);
else if (c >= ' ' && c <= '~' && c != '[')
return fprintf(outf, "%c", c);
else
return fprintf(outf, "\\x%02x", c);
}
/* Print an expression */
static int print_expr(FILE *outf, const unsigned char *exp,
int length, int islabel, unsigned int parenmask,
const unsigned char *instr, const int *argexprs,
unsigned int nsymbols, char **symbols)
{
const unsigned char *exp_a, *exp_b;
int length_a, length_b;
unsigned int n, pclass, pmask;
const char *s;
int count, j;
if (length < 1)
return fprintf(outf, "{**INCOMPLETE**}");
if (exp[0] < 0x40) {
/* symbol */
if (length < 2)
return fprintf(outf, "{**INCOMPLETE**}");
n = ((exp[0] << 8) | exp[1]);
if (n < nsymbols && symbols[n])
return fprintf(outf, "%s", symbols[n]);
else
return fprintf(outf, "__Invalid_Sym_%d", n);
}
else if (exp[0] < 0x50) {
/* special */
switch (exp[0]) {
case X_EXECPC:
return fprintf(outf, "$");
case X_LOADPC:
return fprintf(outf, "$$");
case X_PREV_ANON:
return fprintf(outf, "@B");
case X_NEXT_ANON:
if (islabel)
return fprintf(outf, "@");
else
return fprintf(outf, "@F");
default:
return fprintf(outf, "{**EXPR-%02X**}", exp[0]);
}
}
else if (exp[0] < 0x60) {
/* byte */
if (length < 2)
return fprintf(outf, "{**INCOMPLETE**}");
n = exp[1];
switch (exp[0]) {
case X_DEC8:
return fprintf(outf, "%d", n);
case X_HEX8:
return print_hex(outf, n, 2);
case X_OCT8:
return print_oct(outf, n, 3);
case X_BIN8:
return print_bin(outf, n, 8);
case X_CHAR:
fputc('\'', outf);
count = print_quoted_char(outf, n, '\'');
fputc('\'', outf);
return count + 2;
default:
s = find_builtin_by_value(exp[0], n);
if (s)
return fprintf(outf, "%s", s);
else
return print_hex(outf, n, 2);
}
}
else if (exp[0] < 0x70) {
/* word */
if (length < 3)
return fprintf(outf, "{**INCOMPLETE**}");
n = ((exp[2] << 8) | exp[1]);
switch (exp[0]) {
case X_DEC16:
return fprintf(outf, "%d", n);
case X_HEX16:
return print_hex(outf, n, 4);
case X_OCT16:
return print_oct(outf, n, 6);
case X_BIN16:
return print_bin(outf, n, 16);
case X_ROMCALL:
s = find_builtin_by_value(exp[0], n);
if (s)
return fprintf(outf, "_%s", s);
else
return print_hex(outf, n, 4);
default:
s = find_builtin_by_value(exp[0], n);
if (s)
return fprintf(outf, "%s", s);
else
return print_hex(outf, n, 2);
}
}
else if (exp[0] < 0x90) {
/* reserved expression codes */
return fprintf(outf, "{**EXPR-%02X**}", exp[0]);
}
else if (exp[0] < 0xa0) {
/* unary operator */
exp_a = exp + 1;
length_a = expr_length(exp_a, length - 1);
switch (exp[0]) {
case X_LSB:
count = fprintf(outf, "lsb(");
count += print_expr(outf, exp_a, length_a, 0, 0,
instr, argexprs, nsymbols, symbols);
fputc(')', outf);
return count + 1;
case X_MSB:
count = fprintf(outf, "msb(");
count += print_expr(outf, exp_a, length_a, 0, 0,
instr, argexprs, nsymbols, symbols);
fputc(')', outf);
return count + 1;
case X_MINUS:
case X_COMPLEMENT:
case X_NOT:
fputc("-~!"[exp[0] - X_MINUS], outf);
count = print_expr(outf, exp_a, length_a, 0, 0xff,
instr, argexprs, nsymbols, symbols);
return count + 1;
case X_PAREN:
fputc('(', outf);
count = print_expr(outf, exp_a, length_a, 0, 0,
instr, argexprs, nsymbols, symbols);
fputc(')', outf);
return count + 2;
case X_REGVAL:
if (length_a == 2 && exp_a[0] == X_ARGVAL && exp_a[1] >= 0xc0
&& argexprs) {
n = exp_a[1] & 3;
exp_a = instr + argexprs[n];
length_a = argexprs[n + 1] - argexprs[n];
}
if (length_a == 1 && exp_a[0] >= 0xc0)
for (j = 0; j < NUM_REGISTERS; j++)
if (registers[j].value == exp_a[0])
return fprintf(outf, "%s", registers[j].name);
count = fprintf(outf, "{**REGISTER**}(");
count += print_expr(outf, exp_a, length_a, 0, 0,
instr, argexprs, nsymbols, symbols);
fputc(')', outf);
return count + 1;
case X_ARGVAL:
if (length_a == 1 && exp_a[0] >= 0xc0 && argexprs) {
n = exp_a[0] & 3;
return print_expr(outf, instr + argexprs[n],
argexprs[n + 1] - argexprs[n],
islabel, parenmask, NULL, NULL,
nsymbols, symbols);
}
else {
fputc('#', outf);
count = print_expr(outf, exp_a, length_a, 0, 0xff,
instr, argexprs, nsymbols, symbols);
return count + 1;
}
default:
count = fprintf(outf, "{**UNARY-%02X**}(", exp[0]);
count += print_expr(outf, exp_a, length_a, 0, 0,
instr, argexprs, nsymbols, symbols);
fputc(')', outf);
return count + 1;
}
}
else if (exp[0] < 0xc0) {
/* binary operator */
exp_a = exp + 1;
length_a = expr_length(exp_a, length - 1);
exp_b = exp_a + length_a;
length_b = expr_length(exp_b, length - 1 - length_a);
if (exp[0] <= X_OR) {
pclass = binary_operators[exp[0] - X_MUL].pclass;
pmask = binary_operators[exp[0] - X_MUL].pmask;
if (pclass & parenmask)
fputc('(', outf);
count = print_expr(outf, exp_a, length_a, 0, pmask,
instr, argexprs, nsymbols, symbols);
count += fprintf(outf, " %s ", binary_operators[exp[0] - X_MUL].s);
count += print_expr(outf, exp_b, length_b, 0, pclass | pmask,
instr, argexprs, nsymbols, symbols);
if (pclass & parenmask) {
fputc(')', outf);
count += 2;
}
}
else {
count = fprintf(outf, "{**BINARY-%02X**}(", exp[0]);
count += print_expr(outf, exp_a, length_a, 0, 0,
instr, argexprs, nsymbols, symbols);
count += fprintf(outf, ", ");
count += print_expr(outf, exp_b, length_b, 0, 0,
instr, argexprs, nsymbols, symbols);
fputc(')', outf);
count++;
}
return count;
}
else {
/* dec6 constant */
return fprintf(outf, "%d", (exp[0] & 0x3f));
}
}
static void print_label_suffix(FILE *outf)
{
fputc(':', outf);
}
static void print_comment(FILE *outf, const char *c)
{
fputc(';', outf);
if (c[0])
fprintf(outf, " %s", c);
}
static void print_mnemonic_noindent(FILE *outf, const char *m)
{
fprintf(outf, "%s", m);
}
static void print_mnemonic(FILE *outf, const char *m)
{
fputc('\t', outf);
print_mnemonic_noindent(outf, m);
}
static void print_argstart(FILE *outf)
{
fputc('\t', outf);
}
static void print_argsep(FILE *outf)
{
fputs(", ", outf);
}
static void print_template(FILE *outf, const unsigned char *template,
const unsigned char *instr, const int *argexprs,
unsigned int nsymbols, char **symbols)
{
int i, n;
const char *s;
if (!template)
return;
i = 0;
while (i < template[0]) {
if (i)
print_argsep(outf);
else
print_argstart(outf);
n = expr_length(template + i + 1, template[0] - i);
if (i == 0
&& (instr[1] == I_BIT_n_iIYpn
|| instr[1] == I_RES_n_iIYpn || instr[1] == I_RES_n_s_iIYpn
|| instr[1] == I_SET_n_iIYpn || instr[1] == I_SET_n_s_iIYpn)
&& instr[argexprs[0]] == X_SYSFLAG
&& instr[argexprs[1]] >= X_DEC6) {
s = find_builtin_by_value(0, (instr[argexprs[0] + 1] << 8)
| instr[argexprs[1]]);
if (s)
fprintf(outf, "%s", s);
else
fprintf(outf, "%d", instr[argexprs[1]] & 0x3f);
}
else {
print_expr(outf, template + i + 1, n, 0, 0,
instr, argexprs, nsymbols, symbols);
}
i += n;
}
}
static void print_var_name(FILE *outf, const unsigned char *vn, int length)
{
int type, i;
if (length == 0)
fprintf(outf, "\"\"");
else {
type = vn[0] & 0x1f;
if (type == 0x05 || type == 0x06 || type == 0x16) {
fprintf(outf, "\"prgm");
for (i = 1; i < length && vn[i]; i++)
print_quoted_char(outf, vn[i], '"');
fprintf(outf, "\"");
}
else if (type == 0x15) {
fprintf(outf, "\"");
for (i = 1; i < length && vn[i]; i++)
print_quoted_char(outf, vn[i], '"');
fprintf(outf, "\"");
}
else if (type == 0x07 && vn[1] == 0x60) {
fprintf(outf, "\"Pic%d\"", (vn[2] == 9 ? 0 : vn[2] + 1));
}
else {
fprintf(outf, "{**VAR-%02X**}", vn[1]);
}
}
}
static unsigned int byte_at(const unsigned char *data, unsigned int length,
unsigned int offset)
{
if (offset >= length) {
fprintf(stderr, "Input file corrupted (offset %x out of bounds)\n", offset);
return 0;
}
return data[offset];
}
static unsigned int word_at(const unsigned char *data, unsigned int length,
unsigned int offset)
{
if (offset + 1 >= length) {
fprintf(stderr, "Input file corrupted (offset %x out of bounds)\n",
offset + 1);
return 0;
}
return (data[offset] | (data[offset + 1] << 8));
}
static const char * const segment_names[] =
{ "HEADER", "CODE", "DATA", "FOOTER" };
static const char * const special_mnemonics[] =
{ "align", "assert", "ds", "db", "else", "endif",
"if", "ifdef", "ifndef", "org", "rorg", "word" };
static void export_asm_file_section(FILE *outf, const unsigned char *data,
unsigned int length, unsigned int stabpos,
unsigned int nsymbols, char **symbols)
{
unsigned int type, datastart, dataend, itype, ilength, j, k, l;
unsigned char instr[65];
int argexprs[4];
const char *mnemonic;
const unsigned char *template;
char buf[16];
char *s;
type = byte_at(data, length, stabpos + SECTHEADER_SECTION_TYPE);
datastart = word_at(data, length, stabpos + SECTHEADER_DATA_START);
dataend = word_at(data, length, stabpos + SECTHEADER_DATA_END);
for (j = 0; j < 8; j++)
buf[j] = byte_at(data, length, stabpos + SECTHEADER_NAME + j);
buf[8] = 0;
s = ti83p_to_ascii(buf);
fprintf(outf, ";#SECTION \"%s\", %s%s\n\n", s,
(type & 0x80 ? "DISABLED_" : ""),
segment_names[type & 3]);
xfree(s);
j = datastart;
while (j < dataend && j < length) {
instr[0] = byte_at(data, length, j);
itype = instr[0] & 0xc0;
ilength = (instr[0] & 0x3f) + 2;
for (k = 1; k < ilength; k++)
instr[k] = byte_at(data, length, j + k);
argexprs[0] = 2;
for (k = 0; k < 3; k++)
argexprs[k + 1] = argexprs[k] + expr_length(instr + argexprs[k],
ilength - argexprs[k]);
switch (itype) {
case T_NORMAL:
mnemonic = normal_mnemonics[instr[1]];
template = normal_templates[instr[1]];
print_mnemonic(outf, mnemonic);
print_template(outf, template, instr, argexprs, nsymbols, symbols);
if (instr[1] == I_RET || instr[1] == I_RETI || instr[1] == I_RETN
|| instr[1] == I_JP_iHL || instr[1] == I_JP_iIX
|| instr[1] == I_JP_iIY || instr[1] == I_JP_n || instr[1] == I_JR_n)
fprintf(outf, "\n");
break;
case T_LABEL:
print_expr(outf, instr + 1, ilength - 1, 1, 0,
NULL, NULL, nsymbols, symbols);
print_label_suffix(outf);
break;
case T_COMMENT:
s = convert_comment(instr + 1, ilength - 1);
print_comment(outf, s);
xfree(s);
break;
case T_SPECIAL:
switch (instr[1]) {
case S_ASCII:
case S_ASCIZ:
print_mnemonic(outf, "db");
print_argstart(outf);
fprintf(outf, "\"");
for (k = 2; k < ilength; k++)
print_quoted_char(outf, instr[k], '"');
fprintf(outf, "\"");
if (instr[1] == S_ASCIZ) {
print_argsep(outf);
fprintf(outf, "0");
}
break;
case S_HEX:
print_mnemonic(outf, "db");
for (k = 2; k < ilength; k++) {
if (k > 2)
print_argsep(outf);
else
print_argstart(outf);
print_hex(outf, instr[k], 2);
}
break;
case S_INCBIN:
print_mnemonic(outf, "incbin");
print_argstart(outf);
print_var_name(outf, instr + 2, ilength - 2);
break;
case S_BCALL:
case S_BJUMP:
print_mnemonic(outf, instr[1] == S_BCALL ? "B_CALL" : "B_JUMP");
print_template(outf, templ__n, instr, argexprs,
nsymbols, symbols);
if (instr[1] == S_BJUMP)
fprintf(outf, "\n");
break;
case S_BREAK:
print_mnemonic(outf, "break");
break;
case S_BREAK_ccc:
print_mnemonic(outf, "break");
print_template(outf, templ__ccc, instr, argexprs,
nsymbols, symbols);
break;
case S_EQU:
print_expr(outf, instr + argexprs[0], argexprs[1] - argexprs[0],
1, 0, NULL, NULL, nsymbols, symbols);
fprintf(outf, " ");
print_mnemonic_noindent(outf, "equ");
print_argstart(outf);
print_expr(outf, instr + argexprs[1], argexprs[2] - argexprs[1],
0, 0, NULL, NULL, nsymbols, symbols);
break;
case S_JQ_n:
print_mnemonic(outf, "jq");
print_template(outf, templ__n, instr, argexprs,
nsymbols, symbols);
fprintf(outf, "\n");
break;
case S_JQ_cc_n:
print_mnemonic(outf, "jq");
print_template(outf, templ__cc_n, instr, argexprs,
nsymbols, symbols);
break;
default:
if (instr[1] >= S_ALIGN && instr[1] <= S_WORD)
print_mnemonic(outf, special_mnemonics[instr[1] - S_ALIGN]);
else {
sprintf(buf, "##SPECIAL-%02X##", instr[1]);
print_mnemonic(outf, buf);
}
k = 2;
while (k < ilength) {
if (k > 2)
print_argsep(outf);
else
print_argstart(outf);
l = expr_length(instr + k, ilength - k);
print_expr(outf, instr + k, l, 0, 0,
NULL, NULL, nsymbols, symbols);
k += l;
}
break;
}
break;
}
fprintf(outf, "\n");
j += ilength;
}
}
static void export_asm_file(FILE *outf, const unsigned char *data,
unsigned int length)
{
char **symbols;
unsigned int symstart, symend, strstart, strend, conststart, constend,
impstart, impend, sectstart, sectend, nsymbols,
i, j, rc, v;
char *s;
symstart = word_at(data, length, PROGHEADER_SYMBOL_START);
symend = word_at(data, length, PROGHEADER_SYMBOL_END);
strstart = word_at(data, length, PROGHEADER_SYMBOL_STRING_START);
strend = word_at(data, length, PROGHEADER_SYMBOL_STRING_END);
if (symstart >= symend || strstart >= strend) {
nsymbols = 0;
symbols = NULL;
}
else {
nsymbols = (symend - symstart) / 3;
symbols = xnew(char*, nsymbols);
for (i = 0; i < nsymbols; i++) {
j = strstart + word_at(data, length, symstart + 3 * i);
rc = byte_at(data, length, symstart + 3 * i + 2);
if (rc)
symbols[i] = convert_symbol(data + j, strend - j);
else
symbols[i] = NULL;
}
}
conststart = word_at(data, length, PROGHEADER_CONSTANT_START);
constend = word_at(data, length, PROGHEADER_CONSTANT_END);
if (conststart != constend) {
fprintf(outf, ";#CONSTANTS\n\n");
i = conststart + 3;
while (i + 2 < constend) {
v = word_at(data, length, i);
s = convert_symbol(data + i + 2, constend - i - 2);
i += 2 + symbol_length(data + i + 2, constend - i - 2);
fprintf(outf, "%-32s ", s);
print_mnemonic_noindent(outf, "equ");
print_argstart(outf);
print_hex(outf, v, 4);
fprintf(outf, "\n");
xfree(s);
}
fprintf(outf, "\n");
}
sectstart = word_at(data, length, PROGHEADER_SECTION_TABLE_START);
sectend = word_at(data, length, PROGHEADER_SECTION_TABLE_END);
for (i = sectstart; i < sectend; i += SECTHEADER_LENGTH)
if (!(byte_at(data, length, i + SECTHEADER_SECTION_TYPE) & 0x7f))
export_asm_file_section(outf, data, length, i, nsymbols, symbols);
for (i = sectstart; i < sectend; i += SECTHEADER_LENGTH)
if ((byte_at(data, length, i + SECTHEADER_SECTION_TYPE) & 0x7f))
export_asm_file_section(outf, data, length, i, nsymbols, symbols);
for (i = 0; i < nsymbols; i++)
xfree(symbols[i]);
xfree(symbols);
impstart = word_at(data, length, PROGHEADER_IMPORT_START);
impend = word_at(data, length, PROGHEADER_IMPORT_END);
if (impend > length)
impend = length;
for (i = impstart; i + 9 <= impend; i += 9) {
fprintf(outf, ";#IMPORT ");
print_var_name(outf, data + i, 9);
fprintf(outf, "\n");
}
}
static int read_word(FILE* f)
{
unsigned char b[2];
if (fread(b, 1, 2, f) != 2)
return -1;
else
return (b[0] | (b[1] << 8));
}
static const char usage[] =
"Usage: %s [-o asm-file] 8xv-file ...\n";
static int checkarg(int argc, char **argv, int *pos, char shortopt,
const char *longopt, const char **arg)
{
const char *a;
if (*pos >= argc || argv[*pos][0] != '-')
return 0;
if (shortopt && argv[*pos][1] == shortopt)
a = argv[*pos][2] ? &argv[*pos][2] : NULL;
else if (argv[*pos][1] != '-')
return 0;
else if (longopt
&& !strcasecmp(&argv[*pos][2], longopt))
a = NULL;
else if (longopt
&& !strncasecmp(&argv[*pos][2], longopt, strlen(longopt))
&& argv[*pos][2 + strlen(longopt)] == '=')
a = &argv[*pos][3 + strlen(longopt)];
else
return 0;
if (!arg)
return 1;
else if (a) {
*arg = a;
return 1;
}
else {
(*pos)++;
if (*pos >= argc) {
fprintf(stderr, "%s: %s: requires an argument\n", argv[0],
argv[*pos - 1]);
exit(1);
}
else {
*arg = argv[*pos];
argv[*pos] = NULL;
return 1;
}
}
}
int main(int argc, char **argv)
{
const char *outfilename = NULL, *arg;
const char *infilename;
FILE *inf;
FILE *outf;
unsigned char hdr[53];
unsigned char *vh = NULL, *data = NULL;
int content_length, vh_length, var_length;
int i, var_ok;
if (argc < 2) {
fprintf(stderr, usage, argv[0]);
return 1;
}
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1]) {
if (checkarg(argc, argv, &i, 'o', "output", &arg))
outfilename = arg;
else if (checkarg(argc, argv, &i, 0, "help", NULL)) {
printf(usage, argv[0]);
return 0;
}
else if (checkarg(argc, argv, &i, 0, "version", NULL)) {
printf("8xvtoasm (Mimas %s)\n"
"Copyright (C) 2010 Benjamin Moody\n"
"This program is free software. "
"There is NO WARRANTY of any kind.\n",
PACKAGE_VERSION);
return 0;
}
else {
fprintf(stderr, "%s: unknown option %s\n", argv[0], argv[i]);
fprintf(stderr, usage, argv[0]);
return 1;
}
}
}
if (outfilename && strcmp(outfilename, "-")) {
outf = fopen(outfilename, "wt");
if (!outf) {
perror(outfilename);
return 4;
}
}
else
outf = stdout;
for (i = 1; i < argc; i++) {
if (!argv[i] || (argv[i][0] == '-' && argv[i][1]))
continue;
else if (argv[i][0] == '-') {
inf = stdin;
infilename = "standard input";
}
else {
infilename = argv[i];
inf = fopen(infilename, "rb");
if (!inf) {
perror(infilename);
fclose(outf);
return 2;
}
}
if (fread(hdr, 1, 53, inf) != 53
|| strncmp((char*) hdr, "**TI83F*", 8)) {
fprintf(stderr, "%s: not a valid 8xv file\n", infilename);
if (outf != stdout)
fclose(outf);
if (inf != stdin)
fclose(inf);
return 2;
}
content_length = read_word(inf);
var_ok = 0;
while (content_length > 0) {
if ((vh_length = read_word(inf)) == -1
|| (int) fread((vh = xnew(unsigned char, vh_length)),
1, vh_length, inf) != vh_length
|| (var_length = read_word(inf)) == -1
|| (int) fread((data = xnew(unsigned char, var_length)),
1, var_length, inf) != var_length) {
xfree(vh);
xfree(data);
fprintf(stderr, "%s: unexpected EOF\n", infilename);
if (outf != stdout)
fclose(outf);
if (inf != stdin)
fclose(inf);
return 2;
}
if (var_length > PROGHEADER_LENGTH + 2
//&& (vh[2] & 0x1f) == 0x15
&& !strncmp((char*) data + 2, "MimS\001", 5)) {
export_asm_file(outf, data + 2, var_length - 2);
var_ok = 1;
}
xfree(vh);
xfree(data);
vh = data = NULL;
content_length -= 2 + vh_length + 2 + var_length;
}
if (inf != stdin)
fclose(inf);
if (!var_ok) {
fprintf(stderr, "%s: not a valid Mimas source file\n", infilename);
if (outf != stdout)
fclose(outf);
return 2;
}
}
if (outf != stdout)
fclose(outf);
return 0;
}