691 lines
14 KiB
C
691 lines
14 KiB
C
|
/*
|
||
|
* Copyright 2014 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*/
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include "compile_time_macros.h"
|
||
|
#include "ec_commands.h"
|
||
|
#include "lb_common.h"
|
||
|
#include "lightbar.h"
|
||
|
|
||
|
static const char usage[] =
|
||
|
"\n"
|
||
|
"Usage: %s [OPTIONS] [INFILE [OUTFILE]]\n"
|
||
|
"\n"
|
||
|
"This compiles or decompiles the lightbar programmable bytecode.\n"
|
||
|
"\n"
|
||
|
"Options:\n"
|
||
|
" -d Decode binary to ascii\n"
|
||
|
" -v Decode output should be verbose\n"
|
||
|
"\n";
|
||
|
|
||
|
/* globals */
|
||
|
static int hit_errors;
|
||
|
static int opt_verbose;
|
||
|
static int is_jump_target[EC_LB_PROG_LEN]; /* does program jump here? */
|
||
|
static int is_instruction[EC_LB_PROG_LEN]; /* instruction or operand? */
|
||
|
static char *label[EC_LB_PROG_LEN]; /* labels we've seen */
|
||
|
static char *reloc_label[EC_LB_PROG_LEN]; /* put label target here */
|
||
|
|
||
|
static void Error(const char *format, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
va_start(ap, format);
|
||
|
fprintf(stderr, "ERROR: ");
|
||
|
vfprintf(stderr, format, ap);
|
||
|
va_end(ap);
|
||
|
hit_errors++;
|
||
|
}
|
||
|
|
||
|
static void Warning(const char *format, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
va_start(ap, format);
|
||
|
fprintf(stderr, "Warning: ");
|
||
|
vfprintf(stderr, format, ap);
|
||
|
va_end(ap);
|
||
|
}
|
||
|
|
||
|
/* The longest line should have a label, an opcode, and the max operands */
|
||
|
#define LB_PROG_MAX_OPERANDS 4
|
||
|
#define MAX_WORDS (2 + LB_PROG_MAX_OPERANDS)
|
||
|
|
||
|
struct safe_lightbar_program {
|
||
|
struct lightbar_program p;
|
||
|
uint8_t zeros[LB_PROG_MAX_OPERANDS];
|
||
|
} __packed;
|
||
|
|
||
|
#define OP(NAME, BYTES, MNEMONIC) NAME,
|
||
|
#include "lightbar_opcode_list.h"
|
||
|
enum lightbyte_opcode {
|
||
|
LIGHTBAR_OPCODE_TABLE
|
||
|
MAX_OPCODE
|
||
|
};
|
||
|
#undef OP
|
||
|
|
||
|
#define OP(NAME, BYTES, MNEMONIC) BYTES,
|
||
|
#include "lightbar_opcode_list.h"
|
||
|
static const int num_operands[] = {
|
||
|
LIGHTBAR_OPCODE_TABLE
|
||
|
};
|
||
|
#undef OP
|
||
|
|
||
|
#define OP(NAME, BYTES, MNEMONIC) MNEMONIC,
|
||
|
#include "lightbar_opcode_list.h"
|
||
|
static const char * const opcode_sym[] = {
|
||
|
LIGHTBAR_OPCODE_TABLE
|
||
|
};
|
||
|
#undef OP
|
||
|
|
||
|
static const char * const control_sym[] = {
|
||
|
"beg", "end", "phase", "<invalid>"
|
||
|
};
|
||
|
static const char * const color_sym[] = {
|
||
|
"r", "g", "b", "<invalid>"
|
||
|
};
|
||
|
|
||
|
static void read_binary(FILE *fp, struct safe_lightbar_program *prog)
|
||
|
{
|
||
|
int got;
|
||
|
|
||
|
memset(prog, 0, sizeof(*prog));
|
||
|
|
||
|
/* Read up to one more byte than we need, so we know if it's too big */
|
||
|
got = fread(prog->p.data, 1, EC_LB_PROG_LEN + 1, fp);
|
||
|
if (got < 1) {
|
||
|
Error("Unable to read any input: ");
|
||
|
if (feof(fp))
|
||
|
fprintf(stderr, "EOF\n");
|
||
|
else if (ferror(fp))
|
||
|
fprintf(stderr, "%s\n", strerror(errno));
|
||
|
else
|
||
|
fprintf(stderr, "no idea why.\n");
|
||
|
} else if (got > EC_LB_PROG_LEN) {
|
||
|
Warning("Truncating input at %d bytes\n", EC_LB_PROG_LEN);
|
||
|
prog->zeros[0] = 0;
|
||
|
got = EC_LB_PROG_LEN;
|
||
|
} else {
|
||
|
prog->p.size = got;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint32_t val32(uint8_t *ptr)
|
||
|
{
|
||
|
uint32_t val;
|
||
|
val = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static int is_jump(uint8_t op)
|
||
|
{
|
||
|
/* TODO: probably should be a field in the opcode list */
|
||
|
return op >= JUMP && op <= JUMP_IF_CHARGING;
|
||
|
}
|
||
|
|
||
|
static void print_led_set(FILE *fp, uint8_t led)
|
||
|
{
|
||
|
int i, first = 1;
|
||
|
|
||
|
fprintf(fp, "{");
|
||
|
for (i = 0; i < NUM_LEDS; i++)
|
||
|
if (led & BIT(i)) {
|
||
|
if (!first)
|
||
|
fprintf(fp, ",");
|
||
|
fprintf(fp, "%d", i);
|
||
|
first = 0;
|
||
|
}
|
||
|
fprintf(fp, "}");
|
||
|
}
|
||
|
|
||
|
/* returns number of operands consumed */
|
||
|
static int print_op(FILE *fp, uint8_t addr, uint8_t cmd, uint8_t *arg)
|
||
|
{
|
||
|
uint8_t led, color, control;
|
||
|
int i, operands;
|
||
|
|
||
|
operands = num_operands[cmd];
|
||
|
|
||
|
/* assume valid instruction for now */
|
||
|
is_instruction[addr] = 1;
|
||
|
|
||
|
if (opt_verbose) {
|
||
|
fprintf(fp, "%02x: %02x", addr, cmd);
|
||
|
for (i = 0; i < LB_PROG_MAX_OPERANDS; i++)
|
||
|
if (i < operands)
|
||
|
fprintf(fp, " %02x", arg[i]);
|
||
|
else
|
||
|
fprintf(fp, " ");
|
||
|
fprintf(fp, "\t");
|
||
|
}
|
||
|
if (is_jump_target[addr])
|
||
|
fprintf(fp, "L00%02x:", addr);
|
||
|
fprintf(fp, "\t");
|
||
|
|
||
|
if (cmd < MAX_OPCODE)
|
||
|
fprintf(fp, "%s", opcode_sym[cmd]);
|
||
|
|
||
|
switch (cmd) {
|
||
|
case JUMP:
|
||
|
case JUMP_IF_CHARGING:
|
||
|
fprintf(fp, "\tL00%02x\n", arg[0]);
|
||
|
break;
|
||
|
case JUMP_BATTERY:
|
||
|
fprintf(fp, "\tL00%02x L00%02x\n", arg[0], arg[1]);
|
||
|
break;
|
||
|
case SET_WAIT_DELAY:
|
||
|
case SET_RAMP_DELAY:
|
||
|
fprintf(fp, "\t%d\n", val32(arg));
|
||
|
break;
|
||
|
case SET_BRIGHTNESS:
|
||
|
fprintf(fp, "\t%d\n", arg[0]);
|
||
|
break;
|
||
|
case SET_COLOR_SINGLE:
|
||
|
led = arg[0] >> 4;
|
||
|
control = (arg[0] >> 2) & 0x03;
|
||
|
color = arg[0] & 0x03;
|
||
|
fprintf(fp, "\t");
|
||
|
|
||
|
print_led_set(fp, led);
|
||
|
fprintf(fp, ".%s", control_sym[control]);
|
||
|
fprintf(fp, ".%s", color_sym[color]);
|
||
|
fprintf(fp, "\t0x%02x\n", arg[1]);
|
||
|
break;
|
||
|
case SET_COLOR_RGB:
|
||
|
led = arg[0] >> 4;
|
||
|
control = (arg[0] >> 2) & 0x03;
|
||
|
fprintf(fp, "\t");
|
||
|
|
||
|
print_led_set(fp, led);
|
||
|
fprintf(fp, ".%s", control_sym[control]);
|
||
|
fprintf(fp, "\t0x%02x 0x%02x 0x%02x\n", arg[1], arg[2], arg[3]);
|
||
|
break;
|
||
|
case ON:
|
||
|
case OFF:
|
||
|
case WAIT:
|
||
|
case GET_COLORS:
|
||
|
case SWAP_COLORS:
|
||
|
case RAMP_ONCE:
|
||
|
case CYCLE_ONCE:
|
||
|
case CYCLE:
|
||
|
case HALT:
|
||
|
fprintf(fp, "\n");
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(fp, "-- invalid opcode 0x%02x --\n", cmd);
|
||
|
is_instruction[addr] = 0;
|
||
|
hit_errors++;
|
||
|
}
|
||
|
|
||
|
return operands;
|
||
|
}
|
||
|
|
||
|
static void set_jump_target(uint8_t targ)
|
||
|
{
|
||
|
if (targ >= EC_LB_PROG_LEN) {
|
||
|
Warning("program jumps to 0x%02x, "
|
||
|
"which out of bounds\n", targ);
|
||
|
return;
|
||
|
}
|
||
|
is_jump_target[targ] = 1;
|
||
|
}
|
||
|
|
||
|
static void disassemble_prog(FILE *fp, struct safe_lightbar_program *prog)
|
||
|
{
|
||
|
int i;
|
||
|
uint8_t *ptr, op;
|
||
|
|
||
|
/* Scan the program once to identify all the jump targets,
|
||
|
* so we can print the labels when we encounter them. */
|
||
|
for (i = 0; i < prog->p.size; i++) {
|
||
|
ptr = &prog->p.data[i];
|
||
|
op = *ptr;
|
||
|
if (is_jump(op))
|
||
|
set_jump_target(ptr[1]);
|
||
|
if (op == JUMP_BATTERY)
|
||
|
set_jump_target(ptr[2]);
|
||
|
i += num_operands[op];
|
||
|
}
|
||
|
|
||
|
/* Now disassemble */
|
||
|
for (i = 0; i < prog->p.size; i++) {
|
||
|
ptr = &prog->p.data[i];
|
||
|
i += print_op(fp, i, *ptr, ptr + 1);
|
||
|
}
|
||
|
|
||
|
/* Finally, make sure the program doesn't jump to any location other
|
||
|
* than a valid instruction */
|
||
|
for (i = 0; i < EC_LB_PROG_LEN; i++)
|
||
|
if (is_jump_target[i] && !is_instruction[i]) {
|
||
|
Warning("program jumps to 0x%02x, "
|
||
|
"which is not a valid instruction\n", i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* We'll split each line into an array of these. */
|
||
|
struct parse_s {
|
||
|
char *word;
|
||
|
int is_num;
|
||
|
uint32_t val;
|
||
|
};
|
||
|
|
||
|
/* Fills in struct, returns number of words found. Note that pointers are only
|
||
|
* copied. The strings they point to are not duplicated. */
|
||
|
static int split_line(char *buf, char *delim, struct parse_s *elt, int max)
|
||
|
{
|
||
|
char *w, *ptr, *buf_savetok;
|
||
|
int i;
|
||
|
char *e = 0;
|
||
|
|
||
|
memset(elt, 0, max * sizeof(*elt));
|
||
|
|
||
|
for (ptr = buf, i = 0;
|
||
|
i < max && (w = strtok_r(ptr, delim, &buf_savetok)) != 0;
|
||
|
ptr = 0, i++) {
|
||
|
elt[i].word = w;
|
||
|
elt[i].val = (uint32_t)strtoul(w, &e, 0);
|
||
|
if (!e || !*e)
|
||
|
elt[i].is_num = 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
/* Decode led set. Return 0 if bogus, 1 if okay. */
|
||
|
static int is_led_set(char *buf, uint8_t *valp)
|
||
|
{
|
||
|
uint8_t led = 0;
|
||
|
unsigned long int next_led;
|
||
|
char *ptr;
|
||
|
|
||
|
if (!buf)
|
||
|
return 0;
|
||
|
|
||
|
if (*buf != '{')
|
||
|
return 0;
|
||
|
|
||
|
buf++;
|
||
|
for (;;) {
|
||
|
next_led = strtoul(buf, &ptr, 0);
|
||
|
if (buf == ptr) {
|
||
|
if (buf[0] == '}' && buf[1] == 0) {
|
||
|
*valp = led;
|
||
|
return 1;
|
||
|
} else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (next_led >= NUM_LEDS)
|
||
|
return 0;
|
||
|
|
||
|
led |= 1 << next_led;
|
||
|
|
||
|
buf = ptr;
|
||
|
if (*buf == ',')
|
||
|
buf++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Decode color arg based on expected control param sections.
|
||
|
* Return 0 if bogus, 1 if okay.
|
||
|
*/
|
||
|
static int is_color_arg(char *buf, int expected, uint32_t *valp)
|
||
|
{
|
||
|
struct parse_s token[MAX_WORDS];
|
||
|
uint8_t led, control, color;
|
||
|
int i;
|
||
|
|
||
|
if (!buf)
|
||
|
return 0;
|
||
|
|
||
|
/* There should be three terms, separated with '.' */
|
||
|
i = split_line(buf, ".", token, MAX_WORDS);
|
||
|
if (i != expected)
|
||
|
return 0;
|
||
|
|
||
|
if (!is_led_set(token[0].word, &led)) {
|
||
|
Error("Invalid LED set \"%s\"\n", token[0].word);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < LB_CONT_MAX; i++)
|
||
|
if (!strcmp(token[1].word, control_sym[i])) {
|
||
|
control = i;
|
||
|
break;
|
||
|
}
|
||
|
if (i >= LB_CONT_MAX)
|
||
|
return 0;
|
||
|
|
||
|
if (expected == 3) {
|
||
|
for (i = 0; i < ARRAY_SIZE(color_sym); i++)
|
||
|
if (!strcmp(token[2].word, color_sym[i])) {
|
||
|
color = i;
|
||
|
break;
|
||
|
}
|
||
|
if (i >= ARRAY_SIZE(color_sym))
|
||
|
return 0;
|
||
|
} else
|
||
|
color = 0;
|
||
|
|
||
|
|
||
|
*valp = ((led & 0xF) << 4) | ((control & 0x3) << 2) | (color & 0x3);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void fixup_symbols(struct safe_lightbar_program *prog)
|
||
|
{
|
||
|
int i, j;
|
||
|
|
||
|
for (i = 0; i < EC_LB_PROG_LEN; i++) {
|
||
|
if (reloc_label[i]) {
|
||
|
/* Looking for reloc label */
|
||
|
for (j = 0; j < EC_LB_PROG_LEN; j++) {
|
||
|
if (label[j] && !strcmp(label[j],
|
||
|
reloc_label[i])) {
|
||
|
prog->p.data[i] = j;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (j >= EC_LB_PROG_LEN)
|
||
|
Error("Can't find label %s\n", reloc_label[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void compile(FILE *fp, struct safe_lightbar_program *prog)
|
||
|
{
|
||
|
char buf[128];
|
||
|
struct parse_s token[MAX_WORDS];
|
||
|
char *s;
|
||
|
int line = 0, chopping = 0;
|
||
|
uint8_t addr = 0;
|
||
|
int opcode;
|
||
|
int wnum, wordcnt;
|
||
|
int i;
|
||
|
|
||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||
|
|
||
|
/* We truncate lines that are too long */
|
||
|
s = strchr(buf, '\n');
|
||
|
if (chopping) {
|
||
|
if (s)
|
||
|
chopping = 0;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Got something to look at */
|
||
|
line++;
|
||
|
if (!s) {
|
||
|
chopping = 1;
|
||
|
Warning("truncating line %d\n", line);
|
||
|
}
|
||
|
|
||
|
/* Ignore comments */
|
||
|
s = strchr(buf, '#');
|
||
|
if (s)
|
||
|
*s = '\0';
|
||
|
|
||
|
wordcnt = split_line(buf, " \t\n", token, MAX_WORDS);
|
||
|
if (!wordcnt)
|
||
|
continue;
|
||
|
|
||
|
wnum = 0;
|
||
|
|
||
|
/* A label must be the first word, ends with a ':' (no spaces
|
||
|
* before it), and doesn't start with a ':' */
|
||
|
s = strchr(token[0].word, ':');
|
||
|
if (s && s[1] == '\0' && s != token[0].word) {
|
||
|
*s = '\0';
|
||
|
label[addr] = strdup(token[0].word);
|
||
|
wnum++;
|
||
|
}
|
||
|
|
||
|
/* How about an opcode? */
|
||
|
for (opcode = 0; opcode < MAX_OPCODE; opcode++)
|
||
|
if (!strcasecmp(token[wnum].word, opcode_sym[opcode]))
|
||
|
break;
|
||
|
|
||
|
if (opcode >= MAX_OPCODE) {
|
||
|
Error("Unrecognized opcode \"%s\""
|
||
|
" at line %d\n", token[wnum].word, line);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Do we even have a place to write this opcode? */
|
||
|
if (addr >= EC_LB_PROG_LEN) {
|
||
|
Error("out of program space at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Got an opcode. Save it! */
|
||
|
prog->p.data[addr++] = opcode;
|
||
|
wnum++;
|
||
|
|
||
|
/* Now we need operands. */
|
||
|
switch (opcode) {
|
||
|
case JUMP:
|
||
|
case JUMP_IF_CHARGING:
|
||
|
/* a label */
|
||
|
if (token[wnum].word)
|
||
|
reloc_label[addr++] = strdup(token[wnum].word);
|
||
|
else
|
||
|
Error("Missing jump target at line %d\n", line);
|
||
|
break;
|
||
|
case JUMP_BATTERY:
|
||
|
/* two labels*/
|
||
|
if (token[wnum].word)
|
||
|
reloc_label[addr++] = strdup(token[wnum].word);
|
||
|
else {
|
||
|
Error("Missing first jump target "
|
||
|
"at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
wnum++;
|
||
|
if (token[wnum].word)
|
||
|
reloc_label[addr++] = strdup(token[wnum].word);
|
||
|
else
|
||
|
Error("Missing second jump target "
|
||
|
"at line %d\n", line);
|
||
|
break;
|
||
|
|
||
|
case SET_BRIGHTNESS:
|
||
|
/* one 8-bit arg */
|
||
|
if (token[wnum].is_num)
|
||
|
prog->p.data[addr++] = token[wnum].val;
|
||
|
else
|
||
|
Error("Missing/invalid arg at line %d\n", line);
|
||
|
break;
|
||
|
|
||
|
case SET_WAIT_DELAY:
|
||
|
case SET_RAMP_DELAY:
|
||
|
/* one 32-bit arg */
|
||
|
if (token[wnum].is_num) {
|
||
|
prog->p.data[addr++] =
|
||
|
(token[wnum].val >> 24) & 0xff;
|
||
|
prog->p.data[addr++] =
|
||
|
(token[wnum].val >> 16) & 0xff;
|
||
|
prog->p.data[addr++] =
|
||
|
(token[wnum].val >> 8) & 0xff;
|
||
|
prog->p.data[addr++] =
|
||
|
token[wnum].val & 0xff;
|
||
|
} else {
|
||
|
Error("Missing/invalid arg at line %d\n", line);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SET_COLOR_SINGLE:
|
||
|
/* one magic word, then one more 8-bit arg */
|
||
|
i = is_color_arg(token[wnum].word, 3, &token[wnum].val);
|
||
|
if (!i) {
|
||
|
Error("Missing/invalid arg at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
/* save the magic number */
|
||
|
prog->p.data[addr++] = token[wnum++].val;
|
||
|
/* and the color immediate */
|
||
|
if (token[wnum].is_num) {
|
||
|
prog->p.data[addr++] =
|
||
|
token[wnum++].val;
|
||
|
} else {
|
||
|
Error("Missing/Invalid arg "
|
||
|
"at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case SET_COLOR_RGB:
|
||
|
/* one magic word, then three more 8-bit args */
|
||
|
i = is_color_arg(token[wnum].word, 2, &token[wnum].val);
|
||
|
if (!i) {
|
||
|
Error("Missing/invalid arg at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
/* save the magic number */
|
||
|
prog->p.data[addr++] = token[wnum++].val;
|
||
|
/* and the color immediates */
|
||
|
for (i = 0; i < 3; i++) {
|
||
|
if (token[wnum].is_num) {
|
||
|
prog->p.data[addr++] =
|
||
|
token[wnum++].val;
|
||
|
} else {
|
||
|
Error("Missing/Invalid arg "
|
||
|
"at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/* No args needed */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Did we run past the end? */
|
||
|
if (addr > EC_LB_PROG_LEN) {
|
||
|
Error("out of program space at line %d\n", line);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (ferror(fp))
|
||
|
Error("problem while reading input: %s\n", strerror(errno));
|
||
|
|
||
|
if (!hit_errors)
|
||
|
fixup_symbols(prog);
|
||
|
|
||
|
if (!hit_errors)
|
||
|
prog->p.size = addr;
|
||
|
|
||
|
if (!prog->p.size)
|
||
|
Error("input file produced no output bytes\n");
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
struct safe_lightbar_program safe_prog;
|
||
|
int opt_decode = 0;
|
||
|
int c;
|
||
|
int errorcnt = 0;
|
||
|
char *infile, *outfile;
|
||
|
FILE *ifp, *ofp;
|
||
|
|
||
|
char *progname = strrchr(argv[0], '/');
|
||
|
if (progname)
|
||
|
progname++;
|
||
|
else
|
||
|
progname = argv[0];
|
||
|
|
||
|
opterr = 0; /* quiet, you */
|
||
|
while ((c = getopt(argc, argv, ":dv")) != -1) {
|
||
|
switch (c) {
|
||
|
case 'd':
|
||
|
opt_decode = 1;
|
||
|
break;
|
||
|
case 'v':
|
||
|
opt_verbose = 1;
|
||
|
break;
|
||
|
|
||
|
case '?':
|
||
|
fprintf(stderr, "%s: unrecognized switch: -%c\n",
|
||
|
progname, optopt);
|
||
|
errorcnt++;
|
||
|
break;
|
||
|
case ':':
|
||
|
fprintf(stderr, "%s: missing argument to -%c\n",
|
||
|
progname, optopt);
|
||
|
errorcnt++;
|
||
|
break;
|
||
|
default:
|
||
|
errorcnt++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (errorcnt) {
|
||
|
fprintf(stderr, usage, progname);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (argc - optind > 0) {
|
||
|
infile = argv[optind];
|
||
|
ifp = fopen(infile, "rb");
|
||
|
if (!ifp) {
|
||
|
fprintf(stderr,
|
||
|
"%s: Unable to open %s for reading: %s\n",
|
||
|
progname, infile, strerror(errno));
|
||
|
exit(1);
|
||
|
}
|
||
|
} else {
|
||
|
infile = "stdin";
|
||
|
ifp = stdin;
|
||
|
}
|
||
|
|
||
|
if (argc - optind > 1) {
|
||
|
outfile = argv[optind + 1];
|
||
|
ofp = fopen(outfile, "wb");
|
||
|
if (!ofp) {
|
||
|
fprintf(stderr,
|
||
|
"%s: Unable to open %s for writing: %s\n",
|
||
|
progname, outfile, strerror(errno));
|
||
|
exit(1);
|
||
|
}
|
||
|
} else {
|
||
|
outfile = "stdout";
|
||
|
ofp = stdout;
|
||
|
}
|
||
|
|
||
|
if (opt_decode) {
|
||
|
read_binary(ifp, &safe_prog);
|
||
|
fclose(ifp);
|
||
|
if (hit_errors)
|
||
|
return 1;
|
||
|
fprintf(ofp, "# %s\n", infile);
|
||
|
disassemble_prog(ofp, &safe_prog);
|
||
|
fclose(ofp);
|
||
|
} else {
|
||
|
memset(&safe_prog, 0, sizeof(safe_prog));
|
||
|
compile(ifp, &safe_prog);
|
||
|
fclose(ifp);
|
||
|
if (!hit_errors) {
|
||
|
if (1 != fwrite(safe_prog.p.data,
|
||
|
safe_prog.p.size, 1, ofp))
|
||
|
Error("%s: Unable to write to %s: %s\n",
|
||
|
progname, outfile, strerror(errno));
|
||
|
else
|
||
|
fprintf(stderr, "0x%02x bytes written to %s\n",
|
||
|
safe_prog.p.size, outfile);
|
||
|
}
|
||
|
fclose(ofp);
|
||
|
}
|
||
|
|
||
|
return hit_errors;
|
||
|
}
|