Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix binary generation, validate more things

  • Loading branch information...
commit 665121c00be0cf9fecbcd73a79a0790d2dfa367d 1 parent 1a7649f
@jonpovey authored
View
2  Makefile
@@ -14,6 +14,8 @@ DEPS := $(SRCS) $(HDRS) Makefile
CFLAGS := -g -Wall
+#CFLAGS += -DDEBUG
+
# TODO: partial compliation, auto dependencies
.SUFFIXES:
View
7 README
@@ -11,7 +11,7 @@ This project includes GPLv2 parts from the Linux kernel, so I suppose I'll
release it GPLv2.
Features:
-- Spec 1.5 compliant (hopefuly)
+- Spec 1.7 compliant
- Case-insensitive regarding opcodes and register names
- Compile-time constant expressions supporting most C arithmetic and bitwise
operators, and parentheses e.g.
@@ -19,14 +19,17 @@ Features:
DAT label + 2
- Strings support C escape sequences e.g. "\x0f\t1f CHARACTERS\n\0"
- Optimised small literals in the presence of symbols
-- Forced long literals to resolve borderline "SET A, n - future_label" cases
+- Forced long literals to resolve borderline cases involving labels
- Pretty printed annotated assembler output with PC and machine code
TODO list:
- Command-line options
- Macros
- Local symbols
+- Rounding, warning on out-of-range constants
+- Interpret 0xffff as -1 for short literal
- Work on supporting llvm-dcpu16
- Revisit label redefinition semantics (latest wins, maybe unusual)
- Nicer warnings and errors
+- Error on undefined symbol (instead of pretending it's zero)
- Hint register EX if symbol 'O' is found but undefined
View
11 das.y
@@ -65,8 +65,15 @@ statement:
;
instr:
- OP2 operand ',' operand { gen_instruction($1, $2, $4); }
- | OP1 operand { gen_instruction($1, NULL, $2); }
+ OP2 operand ',' operand {
+ operand_set_position($2, OP_POS_B);
+ operand_set_position($4, OP_POS_A);
+ gen_instruction($1, $2, $4);
+ }
+ | OP1 operand {
+ operand_set_position($2, OP_POS_A);
+ gen_instruction($1, NULL, $2);
+ }
| error { parse_error("bad instruction "); }
;
View
33 dasdefs.c
@@ -8,14 +8,14 @@
#include <stdlib.h>
#include <string.h>
-#include "dasdefs.h"
+#include "common.h"
/*
* macro magic. use GCC designated initialisers to build a sparse array
* of strings indexed by opcode. special opcodes are offset by 0x20
*/
#define OP(val, op, count) [val] = #op
-#define SOP(val, op, count) [val | 0x20] = #op
+#define SOP(val, op, count) [val | SPECIAL_OPCODE] = #op
static char *opcodes[64] = { OPCODES SPECIAL_OPCODES };
#undef OP
#undef SOP
@@ -48,6 +48,11 @@ inline int valid_reg(int reg)
return reg > 0 && reg < ARRAY_SIZE(registers);
}
+inline int valid_opcode(int op)
+{
+ return op >= 0 && op < ARRAY_SIZE(opcodes) && opcodes[op];
+}
+
/* get op value for an opcode string */
int str2opcode(char *str)
{
@@ -56,10 +61,26 @@ int str2opcode(char *str)
char* opcode2str(int op)
{
- if (op >= 0 && op < ARRAY_SIZE(opcodes) && opcodes[op])
- return opcodes[op];
- else
- return "INVALID";
+ assert(valid_opcode(op));
+ return opcodes[op];
+}
+
+u16 opcode2bits(int opcode)
+{
+ assert(valid_opcode(opcode));
+ if (is_special(opcode)) {
+ /* will need << 5 to make into valid machine code */
+ return opcode & ~SPECIAL_OPCODE;
+ } else {
+ return opcode;
+ }
+}
+
+int is_special(int opcode)
+{
+ if (BUG_ON(!valid_opcode(opcode)))
+ return -1;
+ return opcode & SPECIAL_OPCODE;
}
int str2reg(char *str)
View
2  dasdefs.h
@@ -15,6 +15,8 @@ struct reg {
int str2opcode(char *str);
char* opcode2str(int op);
+u16 opcode2bits(int opcode);
+int is_special(int opcode);
int str2reg(char *str);
char* reg2str(int reg);
View
125 instruction.c
@@ -8,6 +8,7 @@
struct operand {
enum opstyle style;
+ enum op_pos position;
int indirect;
int reg;
struct expr *expr;
@@ -26,6 +27,9 @@ struct instr {
#define MAX_SHORT_LITERAL 0x1e
#define MIN_SHORT_LITERAL -1
+#define BINGEN_DBG(fmt, args...) (void)0
+#define BINGEN_DBG_FUNC(fmt, args...) (void)0
+
/*
* Parse phase support
*/
@@ -34,6 +38,9 @@ static struct statement_ops instruction_statement_ops;
/* do some parse-time validation checks on an operand */
void operand_validate(struct operand *o)
{
+ DBG_FUNC("pos:%s style:%d indirect:%d reg:%d(%s) expr:%p\n",
+ o->position == OP_POS_A ? "a" : "b",
+ o->style, o->indirect, o->reg, reg2str(o->reg), o->expr);
/*
* general-purpose and special regs are combined now by the parser,
* check for shennanigans like [PC + 3] or bare X + 1
@@ -64,6 +71,27 @@ void operand_validate(struct operand *o)
BUG();
}
}
+
+ /* validate a/b specifics */
+ if (o->position == OP_POS_A) {
+ if (o->reg == REG_PUSH) {
+ error("PUSH not usable as source ('a') operand");
+ }
+ } else if (o->position == OP_POS_B) {
+ if (o->reg == REG_POP) {
+ error("POP not usable as destination ('b') operand");
+ }
+ if (o->expr && !o->reg && !o->indirect) {
+ /*
+ * literal destination will be ignored, warn. Maybe has valid use
+ * for arithmetic, EX things?
+ */
+ warn("Literal value used as destination");
+ }
+ } else {
+ BUG();
+ }
+
}
/* generate an operand from reg (maybe -1), expr (maybe null), style (type) */
@@ -80,10 +108,24 @@ struct operand* gen_operand(int reg, struct expr *expr, enum opstyle style)
struct operand* operand_set_indirect(struct operand *o)
{
+ assert(o);
+ BUG_ON(o->indirect);
o->indirect = 1;
return o;
}
+/*
+ * need to know if operand is in a or b position for PUSH/POP validation,
+ * b literal warnings, and b short literal disable.
+ */
+struct operand* operand_set_position(struct operand *o, enum op_pos pos)
+{
+ assert(pos == OP_POS_A || pos == OP_POS_B);
+ BUG_ON(o->position != 0);
+ o->position = pos;
+ return o;
+}
+
/* generate an instruction from an opcode and one or two values */
void gen_instruction(int opcode, struct operand *b, struct operand *a)
{
@@ -94,22 +136,9 @@ void gen_instruction(int opcode, struct operand *b, struct operand *a)
/* do validation at some later stage? */
if (a) {
operand_validate(a);
- if (a->reg == REG_PUSH) {
- error("PUSH not allowed in source ('a') operand");
- }
}
if (b) {
operand_validate(b);
- if (b->reg == REG_POP) {
- error("POP not allowed in destination ('b') operand");
- }
- if (b->expr && !b->reg && !b->indirect) {
- /*
- * literal destination will be ignored, warn. Maybe has valid use
- * for arithmetic, EX things?
- */
- warn("Literal value used as destination");
- }
}
//DBG("add instruction %p to list\n", i);
add_statement(i, &instruction_statement_ops);
@@ -130,8 +159,8 @@ int operand_word_count(struct operand *o)
return o->known_word_count;
if (o->expr) {
- if (o->indirect) {
- /* all indirect forms use next-word */
+ if (o->indirect || o->reg == REG_PICK || o->position == OP_POS_B) {
+ /* all indirect forms use next-word, and b can't be short literal */
words = 2;
} else {
/* literal, is it short? */
@@ -179,19 +208,23 @@ void operand_genbits(struct operand *o)
o->known_word_count = operand_word_count(o);
}
+ // FIXME warn and truncate oversize values somewhere
if (o->expr)
exprval = expr_value(o->expr);
- /* FIXME binary generation is BROKEN since reg rework */
if (o->reg)
o->firstbits = reg2bits(o->reg);
else
o->firstbits = 0;
- if (o->firstbits & 0x10) { // magic numbers ahoy!
- /* x-reg, bits set already */
- BUG_ON(o->expr);
- } else if (o->reg < 0) {
+ if (o->reg && !is_gpreg(o->reg)) {
+ /* x-reg, firstbits are good */
+ if (o->reg == REG_PICK) {
+ /* PICK needs nextword */
+ assert(o->expr);
+ words = 2;
+ }
+ } else if (!o->reg) {
/* no reg. have a expr. */
if (o->indirect) {
/* [next word] form */
@@ -199,6 +232,7 @@ void operand_genbits(struct operand *o)
words = 2;
} else if (o->known_word_count == 1) {
/* small literal */
+ BUG_ON(o->position == OP_POS_B);
BUG_ON(exprval > MAX_SHORT_LITERAL || exprval < MIN_SHORT_LITERAL);
o->firstbits = 0x20 | (u16)(exprval - MIN_SHORT_LITERAL);
} else {
@@ -296,30 +330,29 @@ int instruction_get_binary(u16 *dest, void *private)
assert(i->a);
operand_genbits(i->a);
- DBG("opcode:%x", i->opcode);
- DBG(" a:%x", operand_firstbits(i->a));
+ BINGEN_DBG_FUNC("opcode:%x", i->opcode);
+ BINGEN_DBG(" a:%x", operand_firstbits(i->a));
if (i->b) {
operand_genbits(i->b);
- DBG(" b:%x", operand_firstbits(i->b));
+ BINGEN_DBG(" b:%x", operand_firstbits(i->b));
}
- if (i->opcode & SPECIAL_OPCODE) {
- /* special */
- DBG(" special");
- word |= (i->opcode & 0x1f) << 5;
- DBG(" w/op:%x ", word);
+ if (is_special(i->opcode)) {
+ BINGEN_DBG(" special");
+ word |= opcode2bits(i->opcode) << 5;
+ BINGEN_DBG(" w/op:%x ", word);
word |= operand_firstbits(i->a) << 10;
} else {
assert(i->b);
- DBG(" normal");
- word |= i->opcode;
- DBG(" w/op:%04x", word);
+ BINGEN_DBG(" normal");
+ word |= opcode2bits(i->opcode);
+ BINGEN_DBG(" w/op:%04x", word);
word |= operand_firstbits(i->a) << 10;
- DBG(" w/a:%x", word);
+ BINGEN_DBG(" w/a:%x", word);
/* b may not be short literal form */
word |= operand_firstbits(i->b) << 5;
}
- DBG("\n");
+ BINGEN_DBG("\n");
dest[nwords++] = word;
if (operand_needs_nextword(i->a)) {
dest[nwords++] = operand_nextbits(i->a);
@@ -335,16 +368,24 @@ int operand_print_asm(char *buf, struct operand *o)
int count = 0;
assert(o);
- if (o->indirect)
- count += sprintf(buf + count, "[");
- if (o->expr)
+ if (o->style == OPSTYLE_PICK) {
+ assert(o->reg == REG_PICK);
+ assert(o->expr);
+ /* can't be indirect */
+ count += sprintf(buf + count, "%s ", reg2str(o->reg));
count += expr_print_asm(buf + count, o->expr);
- if (o->expr && o->reg != -1)
- count += sprintf(buf + count, " + ");
- if (o->reg != -1)
- count += sprintf(buf + count, "%s", reg2str(o->reg));
- if (o->indirect)
- count += sprintf(buf + count, "]");
+ } else {
+ if (o->indirect)
+ count += sprintf(buf + count, "[");
+ if (o->expr)
+ count += expr_print_asm(buf + count, o->expr);
+ if (o->expr && o->reg)
+ count += sprintf(buf + count, " + ");
+ if (o->reg)
+ count += sprintf(buf + count, "%s", reg2str(o->reg));
+ if (o->indirect)
+ count += sprintf(buf + count, "]");
+ }
return count;
}
View
8 instruction.h
@@ -10,15 +10,21 @@ struct operand;
struct instr;
enum opstyle {
- OPSTYLE_SOLO,
+ OPSTYLE_SOLO = 1,
OPSTYLE_PICK,
OPSTYLE_PLUS,
};
+enum op_pos {
+ OP_POS_A = 1,
+ OP_POS_B,
+};
+
/* Parse */
struct operand* gen_operand(int reg, struct expr*, enum opstyle style);
void gen_instruction(int opcode, struct operand *b, struct operand *a);
struct operand* operand_set_indirect(struct operand *);
+struct operand* operand_set_position(struct operand *o, enum op_pos pos);
/* Analysis */
View
9 output.h
@@ -1,8 +1,6 @@
#ifndef OUTPUT_H
#define OUTPUT_H
-#define DEBUG
-
/* wrap fprintf as print() so extra things may be added if wanted */
#define print(to, fmt, args...) fprintf(to, fmt, ##args)
@@ -24,10 +22,15 @@
#define DBG(fmt, args...) (void)0
#endif
+#define DBG_FUNC(fmt, args...) DBG("%s: " fmt, __func__, ##args)
+
#define DBG_MEM(fmt, args...) (void)0
#define TRACE0(fmt, args...) (void)0
-#define TRACE1(fmt, args...) (void)0
+
+//#define TRACE1(fmt, args...) (void)0
+#define TRACE1(fmt, args...) DBG(fmt, ##args)
+
#define TRACE2(fmt, args...) (void)0
#endif
View
4 statement.c
@@ -186,8 +186,8 @@ void fprint_bin_chunk(FILE *f, u16 *binbuf, int binwords, int start_col,
*/
int statements_fprint_asm(FILE *f)
{
- char linebuf[1024]; /* should be big enough... */
- u16 binbuf[512]; /* ditto */
+ char linebuf[102400]; /* should be big enough... */
+ u16 binbuf[65536]; /* ditto */
int lines = 0, col = 0;
int pc = 0;
int asm_main_col = options.asm_main_col;
View
3  tests/opcodes.s
@@ -5,9 +5,6 @@
SET PUSH, A
;SET [--SP], A ; later
- SET POP, A ; error (POP only goes in a)
- SET B, PUSH ; error (POP only goes in b)
-
:foo
SET B, PEEK
SET PEEK, A
Please sign in to comment.
Something went wrong with that request. Please try again.