#include <unistd.h>
#include <stdio.h>
#include <string.h>
+#include <strings.h>
#include <errno.h>
#include <sysexits.h>
#include <assert.h>
* 2012 04 07 - implementation started
* 2012 04 10 - functional
* 2012 04 16 - support dat statements
+ * 2012 05 05 - v1.7 revision started
+ * 2012 05 08 - v1.7 revision implemented
*
* TODO
* needs ability to specify location for code or data
* needs ability to specify label as relative to another label
* short labels not correctly computed
+ * in label struct, store index of instruction rather than ptr, ptrs for iteration in addr calculation are ugly
*/
static const char * const src_id_ = "$Id$";
}
}
+/* LSB-0 aaaaaabbbbbooooo */
+#define OPCODE_BITS 5
+#define OPERAND_B_BITS 5
+#define OPERAND_A_BITS 6
+#define N_BIT_MASK(__x__) ((1 << (__x__)) - 1)
+
/* instructions have operands */
struct operand_ {
l = (struct label_ *)DYNARRAY_ITEM(*labels, i);
+ DEBUG_PRINTFQ("%s: calculating address of label '%s'\n", __func__, l->label);
+
+#if 0
+force full resolution while debugging
/* if it's already calculated, great. */
if (l->ready)
continue;
+#endif
/*
* starting at the instruction for this label,
* until we get to the start or a known prior label address.
* update our label with the freshly calculated addr
*/
- for (instr = ((struct label_ *)DYNARRAY_ITEM(*labels, i))->instr;
- instr >= (struct instruction_ **)DYNARRAY_ITEM(*instructionps, 0);
- instr--) {
- if ((*instr)->ready)
- DEBUG_PRINTF("%s: instr not ready\n", __func__);
+ /* first fetch the instruction associated with the label we want to know about.. */
+ /* the addr of this instruction will be whatever follows all the preceding instructions */
+ /* so back up one before counting instruction lengths... */
+ instr = ((struct label_ *)DYNARRAY_ITEM(*labels, i))->instr;
+ /* is it the first one? */
+ if (instr == (struct instruction_ **)DYNARRAY_ITEM(*instructionps, 0))
+ break;
+
+ instr--;
+
+ while (instr >= (struct instruction_ **)DYNARRAY_ITEM(*instructionps, 0)) {
+ if ((*instr)->ready == 0)
+ DEBUG_PRINTF("%s: instr '%s' not ready\n", __func__, (*instr)->opcode);
word_count += (*instr)->length;
+ DEBUG_PRINTF("%s: instr '%s' takes '%u' bytes\n", __func__, (*instr)->opcode, (*instr)->length);
+
/* have we come across an instruction which a label points to?
it should already be calculated, so just add that on and be done */
if ((*instr)->label
word_count += addr;
break;
}
+ instr--;
}
l->addr = word_count;
l->ready = 1;
char value;
} opcodes_lower_nibble[] = {
{ "JSR", 0x00 },
- /* { "future nbi instruction", 0x00 }, */
+ { "INT", 0x00 },
+ { "IAG", 0x00 },
+ { "IAS", 0x00 },
+ { "RFI", 0x00 },
+ { "IAQ", 0x00 },
+ { "HWN", 0x00 },
+ { "HWQ", 0x00 },
+ { "HWI", 0x00 },
{ "SET", 0x01 },
{ "ADD", 0x02 },
{ "SUB", 0x03 },
{ "MUL", 0x04 },
- { "DIV", 0x05 },
- { "MOD", 0x06 },
- { "SHL", 0x07 },
- { "SHR", 0x08 },
- { "AND", 0x09 },
- { "BOR", 0x0a },
- { "XOR", 0x0b },
- { "IFE", 0x0c },
- { "IFN", 0x0d },
- { "IFG", 0x0e },
- { "IFB", 0x0f },
+ { "MLI", 0x05 },
+ { "DIV", 0x06 },
+ { "DVI", 0x07 },
+ { "MOD", 0x08 },
+ { "MDI", 0x09 },
+ { "AND", 0x0a },
+ { "BOR", 0x0b },
+ { "XOR", 0x0c },
+ { "SHR", 0x0d },
+ { "ASR", 0x0e },
+ { "SHL", 0x0f },
+ { "IFB", 0x10 },
+ { "IFC", 0x11 },
+ { "IFE", 0x12 },
+ { "IFN", 0x13 },
+ { "IFG", 0x14 },
+ { "IFA", 0x15 },
+ { "IFL", 0x16 },
+ { "IFU", 0x17 },
+ { "ADX", 0x1a },
+ { "SBX", 0x1b },
+ { "STI", 0x1e },
+ { "SDI", 0x1f },
{ "", 0x00 }
}, *o;
} nbi_opcodes_bits[] = {
{ " ", 0x00 }, /* reserved for future */
{ "JSR", 0x01 },
+ { "INT", 0x08 },
+ { "IAG", 0x09 },
+ { "IAS", 0x0a },
+ { "RFI", 0x0b },
+ { "IAQ", 0x0c },
+ { "HWN", 0x10 },
+ { "HWQ", 0x11 },
+ { "HWI", 0x12 },
{ "", 0x00 }
}, *o;
* returns -1 if it could not parse the operand
* returns -2 if it could not parse the operand due to an unresolved label
* notes: nextword may be overwritten even if it's not used in final instruction
+ *
*/
static
int value_bits_(struct dynamic_array *labels, const char *operand_orig, DCPU16_WORD *nextword, unsigned int *nextwordused, unsigned int allow_short_labels) {
}
/* easy matches */
- if (strcasecmp(operand, "POP") == 0) {
+
+ /* push and pop now share the same operand value */
+ if (strcasecmp(operand, "POP") == 0
+ || strcasecmp(operand, "[SP++]") == 0) {
DEBUG_PRINTFQ("is POP\n");
return 0x18;
}
- if (strcasecmp(operand, "PUSH") == 0) {
+ if (strcasecmp(operand, "PUSH") == 0
+ || strcasecmp(operand, "[--SP]") == 0) {
DEBUG_PRINTFQ("is PUSH\n");
- return 0x19;
+ return 0x18;
}
- if (strcasecmp(operand, "PEEK") == 0) {
+
+ if (strcasecmp(operand, "PEEK") == 0
+ || strcasecmp(operand, "[SP]") == 0) {
DEBUG_PRINTFQ("is PEEK\n");
+ return 0x19;
+ }
+
+ /* this could be better, if we had a real token tree */
+ if (strncasecmp(operand, "PICK", 4) == 0) {
+ DEBUG_PRINTFQ("is PICK ");
+
+ errno = 0;
+ l = strtoul(operand + 4, &ep, 0);
+ if (errno == 0
+ && (*(operand + 4) && (*ep == '\0')) ) {
+ if (l > 0xffff) {
+ DEBUG_PRINTFQ("(out of range)\n");
+ fprintf(stderr, "constant invalid in operand '%s'\n", operand_orig);
+ return -1;
+ }
+ } else if (errno == ERANGE) {
+ DEBUG_PRINTFQ("(out of range)\n");
+ fprintf(stderr, "constant invalid in operand '%s'\n", operand_orig);
+ return -1;
+ }
+ *nextword = l & 0xffff;
+ *nextwordused += 1;
+ DEBUG_PRINTFQ("0x%04x\n", *nextword);
return 0x1a;
}
+
if (strcasecmp(operand, "SP") == 0) {
DEBUG_PRINTFQ("is register SP\n");
return 0x1b;
DEBUG_PRINTFQ("is register PC\n");
return 0x1c;
}
- if (strcasecmp(operand, "O") == 0) {
- DEBUG_PRINTFQ("is register O\n");
+ if (strcasecmp(operand, "EX") == 0) {
+ DEBUG_PRINTFQ("is register EX\n");
return 0x1d;
}
char *reg;
char *constant;
+ DEBUG_PRINTFQ("is multipart.. ");
+
/* eat the plus */
*ep = '\0';
ep++;
/* figure out which one is which */
- if (strlen(ep) == 1
- && strchr("AaBbCcXxYyZzIiJj", *ep)) {
+ if ((strlen(ep) == 1 && strchr("AaBbCcXxYyZzIiJj", *ep))
+ || (strlen(ep) == 2 && strcasecmp(ep, "SP")) ) {
reg = ep;
constant = operand;
- } else if (strlen(operand) == 1
- && strchr("AaBbCcXxYyZzIiJj", *operand) ) {
+ } else if ((strlen(operand) == 1 && strchr("AaBbCcXxYyZzIiJj", *operand))
+ || (strlen(operand) == 2 && strcasecmp(operand, "SP")) ) {
reg = operand;
constant = ep;
} else {
/* seems fine */
*nextword = l & 0xffff;
*nextwordused += 1;
+
+ /* special case [SP+n]/PICK n */
+ if (strlen(reg) == 2) {
+ DEBUG_PRINTFQ("is PICK 0x%04x\n", *nextword);
+ return 0x1a;
+ }
+
DEBUG_PRINTFQ("is a dereferenced register (%c) + constant (%hu)\n", *reg, *nextword);
return 0x10 | register_enumerate_(*reg);
- } else if (errno) {
- DEBUG_PRINTFQ("is out of range\n");
- fprintf(stderr, "trouble with operand '%s': %s\n", operand_orig, strerror(errno));
- return -1;
+ } else if (errno == ERANGE) {
+ fprintf(stderr, "%s('%s'):%s\n", "strtoul", constant, strerror(errno));
}
/* what? still here? assume it's a label, I guess */
*nextwordused += 1;
return 0x1e;
} else if (errno) {
- DEBUG_PRINTFQ("is out of range\n");
- fprintf(stderr, "trouble with operand '%s': %s\n", operand_orig, strerror(errno));
+ /* if number wasn't parsable, just fall through and assume it's a label */
}
/* not a number? try a label */
}
DEBUG_PRINTFQ("is literal value (%lu)\n", l);
- if (l < 0x20) {
- return l + 0x20;
+ if (l < 0x1f) {
+ return l + 0x21;
+ }
+ if (l == 0xffff) {
+ return 0x20;
}
*nextword = l & 0xffff;
}
DEBUG_PRINTFQ("is label '%s' (0x%02hx)\n", operand, *nextword);
- if (*nextword < 0x20 && allow_short_labels) {
+ if (allow_short_labels
+ && (*nextword < 0x1f) ) {
DEBUG_PRINTF("small value label win\n");
- return (0x20 + *nextword) & 0x3f;
+ return (0x21 + *nextword) & N_BIT_MASK(OPERAND_A_BITS);
+ }
+ if (allow_short_labels
+ && (*nextword == 0xffff) ) {
+ DEBUG_PRINTF("small value label win\n");
+ return 0x20;
}
*nextwordused += 1;
int r;
if (with_label)
- r = printf("%-16s %3s", i->label ? i->label : "", i->opcode);
- else
- r = printf("%3s", i->opcode);
+ r = printf("%-16s ", i->label ? i->label : "");
+
+ r = printf("%3s", i->opcode ? i->opcode : "");
for (o = i->operands; o; o = o->next)
r += printf(" %s%s", o->operand, o->next ? "," : "");
+ if (i->ready) {
+ DCPU16_WORD l;
+ printf(" [");
+ l = dcpu16_mnemonify_buf(i->instr_words);
+ printf("]");
+
+ if (i->length != l)
+ DEBUG_PRINTF("!!internal inconsistency!! i->length:%u l:%hu should match\n", i->length, l);
+ }
return r;
}
* Parses a zero-terminated line of input into a newly-allocated struct instruction_.
* [label] instruction [operand[,operand[,...]]]
* Does no validation of contents of any of these tokens, as of yet.
+ * does not clean up after itself if a malloc fails
*/
static
int tokenize_line_(char *line, struct instruction_ **next_instr) {
/* if we have an opcode, we'll need at least one word to compile instruction */
instr_words_needed++;
+ /* build a list of operands to hang off this instruction */
while ( (x = strqtok_r(NULL, ",", '\\', quotes, &qt, &st)) ) {
struct operand_ *new_operand;
char *y;
struct operand_ *o = i->operands;
if (opt_.verbose > 2) {
- printf("%s: assembling %p ", __func__, i);
+ printf("%s: assembling %p ", __func__, (void *)i);
instruction_print_(i, 1);
- printf("(line :%zu)\n", i->src_line);
+ printf("(line %zu)\n", i->src_line);
}
- if (i->ready) {
- /* already assembled, nothing to do */
+ if (i->opcode == NULL) {
+ assert(i->label);
+ assert(i->operands == NULL);
+ /* just a label, move along */
+ i->length = 0;
+ i->ready = 1;
return 0;
}
char *x;
unsigned long l;
- DEBUG_PRINTF("DAT operand:'%s' next:%p\n", o->operand, o->next);
+ DEBUG_PRINTF("DAT operand:'%s' next:%p\n", o->operand, (void *)o->next);
/* is this a string? */
+ /* does it start with a quote, and end with the same quote? */
if ( (x = strchr("\"'`", o->operand[0])) ) {
dat_len = strlen(o->operand) - 1;
if (o->operand[dat_len] == *x) {
/* otherwise assume it's a label, even if we don't know what it is */
if (label_addr_(labels, o->operand, &i->instr_words[i->length])) {
- DEBUG_PRINTF("(deferred label resolution)\n");
+ DEBUG_PRINTF("(deferred label '%s' resolution)\n", o->operand);
incomplete = 1;
}
i->length++;
fprintf(stderr, "'\n");
return -1;
}
- i->instr_words[0] |= 0x0f & bits;
+ i->instr_words[0] |= bits & N_BIT_MASK(OPCODE_BITS);
- /* in rendered bytecode, all instructions have two operands; nbi instructions take 'first operand' bits. */
- if ((bits & 0x0f) == 0) {
+ /* in rendered bytecode, all instructions have a and b operands; nbi instructions occupy 'b operand' bits. */
+ if ((bits & N_BIT_MASK(OPCODE_BITS)) == 0) {
bits = nbi_opcode_bits_(i->opcode);
if (bits < 0) {
fprintf(stderr, "INTERNAL ERROR: missing instruction in nbi opcode table\n");
}
o = o->next;
}
- i->instr_words[0] |= (bits & 0x3f) << 4;
+ if (bits > N_BIT_MASK(OPERAND_B_BITS)) {
+ fprintf(stderr, "%s: internal error: operand '%s' generated out of range\n", __func__, "b");
+ return -1;
+ }
+ i->instr_words[0] |= (bits & N_BIT_MASK(OPERAND_B_BITS)) << OPCODE_BITS;
if (o == NULL) {
fprintf(stderr, "'%s' requires more operands\n", i->opcode);
bits = 0;
}
o = o->next;
- i->instr_words[0] |= (bits & 0x3f) << 10;
+ if (bits > N_BIT_MASK(OPERAND_A_BITS)) {
+ fprintf(stderr, "%s: internal error: operand '%s' generated out of range\n", __func__, "a");
+ }
+ i->instr_words[0] |= (bits & N_BIT_MASK(OPERAND_A_BITS)) << (OPCODE_BITS + OPERAND_B_BITS);
if (o != NULL) {
fprintf(stderr, "too many operands\n");
return retval;
}
if (! (*instrp)->ready) {
- fprintf(stderr, "instruction not resolvable\n");
+ fprintf(stderr, "instruction not resolvable at line %lu\n", (*instrp)->src_line);
return -1;
}
}