URL
https://opencores.org/ocsvn/tinyvliw8/tinyvliw8/trunk
Subversion Repositories tinyvliw8
[/] [tinyvliw8/] [trunk/] [tools/] [asm/] [src/] [main.c] - Rev 6
Compare with Previous | Blame | View Log
/** * \file main.c * \author Oliver Stecklina <stecklina@ihp-microelectronics.com> * \date 20.10.2013 * * \brief IHPvliw8 assembler main program file. * * <p> * Copyright (C) 2015 IHP GmbH, Frankfurt (Oder), Germany * * This code is free software. It is licensed under the EUPL, Version 1.1 * or - as soon they will be approved by the European Commission - subsequent * versions of the EUPL (the "License"). * You may redistribute this code and/or modify it under the terms of this * License. * You may not use this work except in compliance with the License. * You may obtain a copy of the License at: * * http://joinup.ec.europa.eu/software/page/eupl/licence-eupl * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * </p> */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> #include "config.h" #define _VARNAME_LEN 8 #define _LABEL_LEN 8 typedef struct _label_s { char name[_LABEL_LEN]; unsigned short addr; struct _label_s *next; } _label_t; typedef struct _var_s { char name[_VARNAME_LEN]; unsigned char value; unsigned char addr; struct _var_s *next; } _var_t; typedef enum _section_e { _section_data = 0, _section_code } _section_t; static char *_infile = NULL; static char *_outfile = "out"; static _var_t *_var = NULL; static _label_t *_label = NULL; static unsigned short _code_addr = 0x0000; static int _parse_args(int argc, char *argv[]) { int err, i; err = 0; for (i = 1; 0 == err && i < argc; i++) { if ('-' != *argv[i]) { if ((i + 1) == argc) { _infile = argv[i]; break; } fprintf(stderr, "\tERROR! bad option <%s>\n", argv[i]); err = -EINVAL; break; } switch (*(argv[i] + 1)) { case 'o': if ((i + 1) < argc) { i++; _outfile = argv[i]; break; } fprintf(stderr, "\tERROR! option <%s> requires an argument\n", argv[i]); err = -EINVAL; break; case 'h': printf("%s [option] <input>\n", argv[0]); printf("\t-o <file> output filename (default: out.bin)\n"); printf("\t-h useful help\n"); exit(0); } } if (0 == err && NULL == _infile) { fprintf(stderr, "\tERROR! no input file given\n"); err = -EINVAL; } return err; } static int _shrink_line(char *line, unsigned int size) { unsigned int s, i; int len; len = 0; s = 0; for (i = 0; i < size; i++) { int out; out = 0; switch (line[i]) { case '\0': out = 1; break; case '/': /* comment */ out = 1; if (0 == len) break; line[len] = '\n'; len++; line[len] = '\0'; break; case ';': /* line finished */ line[len] = '\n'; len++; line[len] = '\0'; out = 1; break; case '|': /* instruction delimiter - jmp #0x08 | ld R0, #0x01 */ case ',': /* operand delimiter - add r1, r3 */ switch (s) { case 1: /* leading blank, kick it */ line[len - 1] = line[i]; break; case 0: line[len] = line[i]; len++; break; } s = 2; break; case ' ': case '\t': if (0 < i) { if (0 == s) { line[len] = ' '; len++; } } if (0 == s) s = 1; break; case '\r': case '\n': break; default: if (len != (int) i) line[len] = line[i]; len++; s = 0; } if (0 != out) break; } return len; } typedef struct _token_s { struct { unsigned char num; char **data; } inst[2]; } _token_t; static _token_t *_tokenize(char *line) { _token_t *t; t = (_token_t *) malloc(sizeof(_token_t)); if (t) { unsigned char inst; memset((char *) t, 0x00, sizeof(_token_t)); inst = 0; t->inst[inst].num = 1; t->inst[inst].data = (char **) malloc(sizeof(char*) * (t->inst[inst].num + 1)); if (NULL != t->inst[inst].data) { unsigned char state; int i; t->inst[inst].data[0] = line; t->inst[inst].data[1] = NULL; i = 0; state = 0; while ('\0' != line[i]) { unsigned char add; add = 0; switch (line[i]) { case ' ': if (0 == state) { add = 1; state = 1; } break; case ',': add = 1; break; case '|': inst++; add = 1; state = 0; break; } if (0 != add && '\0' != line[i + 1]) { line[i] = '\0'; t->inst[inst].data = (char **) realloc(t->inst[inst].data, sizeof(char*) * (t->inst[inst].num + 2)); if (NULL != t->inst[inst].data) { i++; t->inst[inst].data[t->inst[inst].num] = &line[i]; t->inst[inst].data[t->inst[inst].num + 1] = NULL; t->inst[inst].num++; } else { /* error, no more memory, cleaning up */ if (0 < inst) free(t->inst[0].data); free(t); t = NULL; break; } } i++; } } } return t; } static int _add_data_item(char *line) { _token_t *tok; int err; tok = _tokenize(line); if (NULL != tok) { if (NULL != tok->inst[0].data[0] && NULL != tok->inst[0].data[1]) { int namelen; unsigned char addr; long value; namelen = strlen(tok->inst[0].data[0]); if (_VARNAME_LEN < namelen) { err = -ENAMETOOLONG; goto out; } addr = 0x00; if (NULL != _var) { if (CONFIG_DATA_ADDR_MAX == _var->addr) { err = -ENOSPC; goto out; } addr = _var->addr + 1; } value = strtol(tok->inst[0].data[1], NULL, 0); if (0 <= value && 256 > value) { _var_t *nv; nv = (_var_t *) malloc(sizeof(_var_t)); if (NULL != nv) { memset((char *) nv, 0, sizeof(_var_t)); memcpy(&nv->name[0], tok->inst[0].data[0], namelen); nv->addr = addr; nv->value = 0xff & value; nv->next = _var; _var = nv; err = 0; } } else err = -ERANGE; } else err = -EINVAL; out: if (NULL != tok->inst[0].data) free(tok->inst[0].data); if (NULL != tok->inst[1].data) free(tok->inst[1].data); free(tok); } else err = -ENOMEM; return err; } typedef enum _opcode_e { _opcode_ld = 0, _opcode_ldi, _opcode_st, _opcode_sti, _opcode_jmp, _opcode_jc, _opcode_jz, _opcode_jnz, _opcode_add, _opcode_addi, _opcode_shl, _opcode_shli, _opcode_shr, _opcode_shri, _opcode_or, _opcode_nor, _opcode_and, _opcode_nand, _opcode_xor, _opcode_xnor, _opcode_unknown } _opcode_t; static _opcode_t _opcode_get(char *token) { unsigned int i; for (i = 0; '\0' != token[i]; i++) token[i] = tolower(token[i]); if (0 == strcmp("ldi", token)) return _opcode_ldi; if (0 == strcmp("ld", token)) return _opcode_ld; if (0 == strcmp("sti", token)) return _opcode_sti; if (0 == strcmp("st", token)) return _opcode_st; if (0 == strcmp("jmp", token)) return _opcode_jmp; if (0 == strcmp("jc", token)) return _opcode_jc; if (0 == strcmp("jz", token)) return _opcode_jz; if (0 == strcmp("jnz", token)) return _opcode_jnz; if (0 == strcmp("addi", token)) return _opcode_addi; if (0 == strcmp("add", token)) return _opcode_add; if (0 == strcmp("shli", token)) return _opcode_shli; if (0 == strcmp("shl", token)) return _opcode_shl; if (0 == strcmp("shri", token)) return _opcode_shri; if (0 == strcmp("shr", token)) return _opcode_shr; if (0 == strcmp("nor", token)) return _opcode_nor; if (0 == strcmp("or", token)) return _opcode_or; if (0 == strcmp("nand", token)) return _opcode_nand; if (0 == strcmp("and", token)) return _opcode_and; if (0 == strcmp("xnor", token)) return _opcode_xnor; if (0 == strcmp("xor", token)) return _opcode_xor; return _opcode_unknown; } static unsigned char _get_reg(char *token) { if ('r' == tolower(*token)) return (*(token + 1) - '0'); return 0xff; } typedef enum _ldst_vtype_e { _ldst_vtype_addr = 0, _ldst_vtype_reg } _ldst_vtype_t; typedef struct _ldst_op_s { _opcode_t opcode; unsigned char reg; _ldst_vtype_t vtype; union { unsigned char reg; unsigned char addr; unsigned char raw; } vt; } _ldst_op_t; typedef struct _alu_op_s { _opcode_t opcode; unsigned char reg; struct { unsigned char prefix; unsigned char type; union { unsigned char reg; unsigned char value; } data; } op[2]; } _alu_op_t; typedef struct _jmp_op_s { _opcode_t opcode; unsigned short addr; _label_t *label; } _jmp_op_t; typedef enum _op_type_e { _op_type_ldst = 0, _op_type_alu, _op_type_jmp } _op_type_t; typedef struct _op_s { unsigned short addr; _op_type_t opt[2]; union { _ldst_op_t *ldst; _alu_op_t *alu; _jmp_op_t *jmp; void *raw; } op[2]; struct _op_s *next; } _op_t; static _op_t *_op = NULL; static _label_t *_label_get_by_name(const char *name) { _label_t *v; for (v = _label; v != NULL; v = v->next) { if (0 == strcmp(v->name, name)) break; } return v; } static int _add_label(char *label, unsigned short addr) { int err; err = strlen(label); if (_LABEL_LEN > err) { _label_t *l; l = _label_get_by_name(label); if (NULL != l) { if (0xffff != l->addr) { fprintf(stderr, "label %s defined twice\n", label); return -EBUSY; } l->addr = addr; return 0; } l = (_label_t *) malloc(sizeof(_label_t)); if (NULL != l) { memset((char *) l, 0x00, sizeof(_label_t)); memcpy(&l->name[0], label, err); l->addr = addr; l->next = _label; _label = l; err = 0; } else err = -ENOMEM; } else err = -ENAMETOOLONG; return err; } static _var_t *_get_var_by_name(const char *name) { _var_t *v; for (v = _var; v != NULL; v = v->next) { if (0 == strcmp(v->name, name)) break; } return v; } static _jmp_op_t *_jmp_gen_item(_opcode_t opcode, char *value) { _jmp_op_t *op; int err; if (NULL == value) return NULL; op = (_jmp_op_t *) malloc(sizeof(_jmp_op_t)); if (NULL == op) return NULL; memset((char *) op, 0x00, sizeof(_jmp_op_t)); op->opcode = opcode; err = 0; switch (*value) { _label_t *label; long v; case '$': label = _label_get_by_name(&value[1]); if (NULL != label) { op->addr = label->addr; op->label = label; } else { op->addr = 0xffff; err = _add_label(&value[1], 0xffff); if (0 == err) { op->label = _label_get_by_name(&value[1]); if (NULL == op->label) { fprintf(stderr, "error while adding virtual label <%s>\n", &value[1]); } } else fprintf(stderr, "add virtual label <%s> failed, error %d\n", &value[1], err); } break; case '-': v = strtol(value + 1, NULL, 0); if (0 > v || CONFIG_CODE_SIZE <= v) { err = -ERANGE; break; } if (v > _code_addr) { err = -EINVAL; break; } op->addr = _code_addr - v; break; case '+': v = strtol(value + 1, NULL, 0); if (0 > v || CONFIG_CODE_SIZE <= v) { err = -ERANGE; break; } if (CONFIG_CODE_SIZE <= (v + _code_addr)) { err = -EINVAL; break; } op->addr = _code_addr + v; break; case '#': v = strtol(value + 1, NULL, 0); if (0 <= v && CONFIG_CODE_SIZE > v) { op->addr = v; } else err = -ERANGE; break; default: fprintf(stderr, "syntax error at label\n"); err = -EINVAL; } if (0 != err) { free(op); op = NULL; } return op; } static _ldst_op_t *_ldst_gen_item(_opcode_t opcode, char *reg_str, char *value) { unsigned char reg; _ldst_op_t *op; int err; if (NULL == reg_str || NULL == value) return NULL; reg = _get_reg(reg_str); if (CONFIG_MAX_REG_NUM < reg) return NULL; op = (_ldst_op_t *) malloc(sizeof(_ldst_op_t)); if (NULL == op) return NULL; memset((char *) op, 0x00, sizeof(_ldst_op_t)); op->opcode = opcode; op->reg = reg; err = 0; switch (*value) { _var_t *var; long v; case '@': op->vtype = _ldst_vtype_reg; reg = _get_reg(&value[1]); if (CONFIG_MAX_REG_NUM >= reg) { op->vt.reg = reg; } else err = -EINVAL; break; case '%': op->vtype = _ldst_vtype_addr; var = _get_var_by_name(&value[1]); if (var) { op->vt.addr = var->addr; } else { fprintf(stderr, "undefined variable %s\n", &value[1]); err = -EINVAL; } break; case '#': op->vtype = _ldst_vtype_addr; v = strtol(value + 1, NULL, 0); if (0 <= v && 0xff >= v) { op->vt.addr = 0xff & v; } else err = -ERANGE; break; default: err = -EINVAL; } if (0 != err) { free(op); op = NULL; } return op; } static _alu_op_t *_alu_gen_item(_opcode_t opcode, char *reg_str, char *op1, char *op2) { _alu_op_t *op; unsigned char i, reg; char *operand[] = {op1, op2}; int err; if (NULL == reg_str || NULL == op1) { fprintf(stderr, "parse error \n"); return NULL; } reg = _get_reg(reg_str); if (CONFIG_MAX_REG_NUM < reg) { fprintf(stderr, "invalid destination register %s\n", reg_str); return NULL; } op = (_alu_op_t *) malloc(sizeof(_alu_op_t)); if (NULL == op) return NULL; memset((char *) op, 0x00, sizeof(_alu_op_t)); op->opcode = opcode; op->reg = reg; for (i = 0; i< 2; i++) { if (NULL == operand[i]) { if (0 == i) { err = -EINVAL; break; } /* copy operand 0 to operand 1 */ memcpy((char *) &op->op[1], (char *) &op->op[0], sizeof(op->op[0])); /* set operand 0 to destination register */ op->op[0].prefix = 0; op->op[0].type = 0; op->op[0].data.reg = op->reg; break; } switch (*operand[i]) { long v; case '-': /* two's complement */ case '~': /* invert */ op->op[i].prefix = 1; op->op[i].type = 0; reg = _get_reg(operand[i] + 1); if (CONFIG_MAX_REG_NUM >= reg) { op->op[i].data.reg = reg; } else err = -EINVAL; break; case '#': /* constant value */ op->op[i].prefix = 0; op->op[i].type = 1; v = strtol(operand[i] + 1, NULL, 0); if (0 <= v && 0xff >= v) { op->op[i].data.value = 0xff & v; } else err = -ERANGE; break; default: /* register */ op->op[i].prefix = 0; op->op[i].type = 0; reg = _get_reg(operand[i]); if (CONFIG_MAX_REG_NUM >= reg) { op->op[i].data.reg = reg; } else err = -EINVAL; break; } } if (0 != err) { fprintf(stderr, "error %d at 0x%02x %s %s %s failed\n", err, opcode, reg_str, op1, op2); free(op); op = NULL; } return op; } static int _add_code_item(char *line) { _token_t *tok; int err; for (err = 0; err < (int) strlen(line); err++) if ('\n' == line[err] || '\r' == line[err]) { line[err] = '\0'; break; } if (0 < err && ':' == line[err - 1]) { line[err - 1] = '\0'; return _add_label(line, (NULL == _op) ? 0x00 : _op->addr + 1); } tok = _tokenize(line); if (NULL != tok) { unsigned char inst; _opcode_t opcode; unsigned short addr; _op_t *op; if (NULL == tok->inst[0].data[0]) { err = -EINVAL; goto out; } if (CONFIG_CODE_ADDR_MAX < _code_addr) { err = -ENOSPC; goto out; } addr = _code_addr; _code_addr += 1; op = malloc(sizeof(_op_t)); if (NULL == op) { err = -ENOMEM; goto out; } err = 0; for (inst = 0; 0 == err && inst < 2; inst++) { if (0 == tok->inst[inst].num) continue; opcode = _opcode_get(tok->inst[inst].data[0]); switch (opcode) { case _opcode_jmp: case _opcode_jz: case _opcode_jc: case _opcode_jnz: op->op[inst].jmp = _jmp_gen_item(opcode, tok->inst[inst].data[1]); op->opt[inst] = _op_type_jmp; break; case _opcode_ld: case _opcode_ldi: case _opcode_st: case _opcode_sti: op->op[inst].ldst = _ldst_gen_item(opcode, tok->inst[inst].data[1], tok->inst[inst].data[2]); op->opt[inst] = _op_type_ldst; break; case _opcode_add: case _opcode_addi: case _opcode_shl: case _opcode_shli: case _opcode_shr: case _opcode_shri: case _opcode_or: case _opcode_nor: case _opcode_and: case _opcode_nand: case _opcode_xor: case _opcode_xnor: if (4 == tok->inst[inst].num) op->op[inst].alu = _alu_gen_item(opcode, tok->inst[inst].data[1], tok->inst[inst].data[2], tok->inst[inst].data[3]); else op->op[inst].alu = _alu_gen_item(opcode, tok->inst[inst].data[1], tok->inst[inst].data[2], NULL); op->opt[inst] = _op_type_alu; break; default: op->op[inst].raw = NULL; err = -EINVAL; } if (NULL == op->op[inst].raw) { err = -EINVAL; break; } op->addr = addr; } if (NULL == op->op[1].raw) { op->op[1].raw = op->op[0].raw; op->opt[1] = op->opt[0]; } if (0 == err) { op->next = _op; _op = op; } else free(op); out: if (NULL != tok->inst[0].data) free(tok->inst[0].data); if (NULL != tok->inst[1].data) free(tok->inst[1].data); free(tok); } else err = -ENOMEM; return err; } static int _parse_line(char *line, _section_t *sec_ptr) { int err; if ('.' == *line) { /* section definition */ if ('d' == *(line + 1)) { *sec_ptr = _section_data; return 0; } if ('c' == *(line + 1)) { *sec_ptr = _section_code; return 0; } if ('i' == *(line + 1)) { unsigned char inum; inum = *(line + 4) - '0'; if (CONFIG_IRQ_NUM >= inum) { _code_addr = CONFIG_IRQ_START + inum; return 0; } else return -EINVAL; } return -EINVAL; } err = 0; switch (*sec_ptr) { case _section_data: err = _add_data_item(line); break; case _section_code: err = _add_code_item(line); break; } return err; } static int _jmp_dump(unsigned char *dst, _jmp_op_t *op) { int err; err = 0; switch (op->opcode) { case _opcode_jmp: dst[0] = 0xe0; break; case _opcode_jz: dst[0] = 0xe8; break; case _opcode_jc: dst[0] = 0xf0; break; case _opcode_jnz: dst[0] = 0xf8; break; default: err = -EINVAL; } if (0xffff == op->addr) { if (NULL == op->label) { fprintf(stderr, "error, unresolved label at %p\n", op); return -ENOENT; } op->addr = op->label->addr; } dst[0] |= (op->addr >> 8) & 0x07; dst[1] = op->addr & 0x00ff; return err; } static int _ldst_dump(unsigned char *dst, _ldst_op_t *op) { int err; err = 0; switch (op->opcode) { case _opcode_ld: dst[0] = 0x00; break; case _opcode_ldi: dst[0] = 0x10; break; case _opcode_st: dst[0] = 0x20; break; case _opcode_sti: dst[0] = 0x30; break; default: err = -EINVAL; } if (_ldst_vtype_reg == op->vtype) dst[0] |= 0x08; dst[0] |= op->reg; dst[1] = op->vt.raw; return err; } static void _alu_dump_operand(unsigned char *dst, _alu_op_t *op) { if (1 == op->op[1].type) { dst[1] = op->op[1].data.value; dst[0] |= 0x08; return; } dst[1] = ((0 != op->op[0].prefix) ? 0x08 : 0x00) | (op->op[0].data.reg & 0x07); dst[1] <<= 4; dst[1] |= ((0 != op->op[1].prefix) ? 0x08 : 0x00) | (op->op[1].data.reg & 0x07); } static int _alu_dump(unsigned char *dst, _alu_op_t *op) { int err; dst[0] = op->reg & 0x07; dst[1] = 0x00; err = 0; switch (op->opcode) { case _opcode_addi: dst[0] |= 0x10; /* no break */ case _opcode_add: dst[0] |= 0x02 << 5; _alu_dump_operand(dst, op); break; case _opcode_shli: case _opcode_shri: dst[0] |= 0x08; /* no break */ case _opcode_shr: dst[0] |= 0x10; /* no break */ case _opcode_shl: dst[0] |= 0x03 << 5; dst[1] = op->op[0].data.reg & 0x07; break; case _opcode_nor: dst[0] |= 0x10; /* no break */ case _opcode_or: dst[0] |= 0x05 << 5; _alu_dump_operand(dst, op); break; case _opcode_nand: dst[0] |= 0x10; /* no break */ case _opcode_and: dst[0] |= 0x04 << 5; _alu_dump_operand(dst, op); break; case _opcode_xnor: dst[0] |= 0x10; /* no break */ case _opcode_xor: dst[0] |= 0x06 << 5; _alu_dump_operand(dst, op); break; default: err = -EINVAL; } return err; } static int _write_code(char *name, _op_t *op) { char filename[64]; int err; err = snprintf(filename, 64, "%s.bin", name); if (64 <= err) { return -ENAMETOOLONG; } err = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (0 <= err) { unsigned int i, max_addr; _op_t *t; int fd; fd = err; err = 0; max_addr = 0; for (t = op; NULL != t; t = t->next) { if (t->addr > max_addr) max_addr = t->addr; } for (i = 0; 0 == err && i <= max_addr; i++) { unsigned short value[2]; int k; for (t = op; 0 == err && NULL != t; t = t->next) { if (i == t->addr) { break; } } value[0] = value[1] = 0; if (NULL != t) { for (k = 0; 0 == err && k < 2; k++) { switch (t->opt[k]) { case _op_type_jmp: err = _jmp_dump((unsigned char *) &value[k], t->op[k].jmp); break; case _op_type_ldst: err = _ldst_dump((unsigned char *) &value[k], t->op[k].ldst); break; case _op_type_alu: err = _alu_dump((unsigned char *) &value[k], t->op[k].alu); break; } if (0 != err) break; if (t->opt[0] == t->opt[1]) { value[1] = value[0]; break; } } } while (0 == err) { err = write(fd, &value[0], sizeof(value)); if (sizeof(value) == err) { err = 0; break; } err = -errno; if (-EINTR == err) err = 0; } } close(fd); } else err = -errno; return err; } static int _parse_file(const char *filename) { FILE *f; int err; f = fopen(filename, "r"); if (NULL != f) { _section_t sec; int line; char l[80]; sec = _section_code; err = 0; line = 0; while (0 == err && NULL != fgets(&l[0], 80, f)) { int len; line++; len = _shrink_line(&l[0], 80); if (0 > len) { err = len; fprintf(stderr, "parse error (%d) near line %d\n", err, line); break; } if (0 == len) continue; err = _parse_line(&l[0], &sec); if (0 != err) { fprintf(stderr, "parse error (%d) near line %d\n", err, line); } } fclose(f); if (0 == err) { err = _write_code(_outfile, _op); if (0 != err) { fprintf(stderr, "write data failed, error %d\n", err); } } while (NULL != _op) { _op_t *op; op = _op->next; if (_op->op[0].raw != _op->op[1].raw) free(_op->op[1].raw); free(_op->op[0].raw); free(_op); _op = op; } while (NULL != _var) { _var_t *t; t = _var->next; free(_var); _var = t; } } else { fprintf(stderr, "failed to open file %s\n", filename); err = -errno; } return err; } int main(int argc, char *argv[]) { int err; err = _parse_args(argc, argv); if (0 == err) { err = _parse_file(_infile); } return err; }