diff options
Diffstat (limited to 'gas/config/tc-z8k.c')
-rw-r--r-- | gas/config/tc-z8k.c | 1589 |
1 files changed, 1589 insertions, 0 deletions
diff --git a/gas/config/tc-z8k.c b/gas/config/tc-z8k.c new file mode 100644 index 0000000..1611341 --- /dev/null +++ b/gas/config/tc-z8k.c @@ -0,0 +1,1589 @@ +/* tc-z8k.c -- Assemble code for the Zilog Z800n + Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation. + + This file is part of GAS, the GNU Assembler. + + GAS 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 2, or (at your option) + any later version. + + GAS 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 GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* + Written By Steve Chamberlain + sac@cygnus.com + */ +#define DEFINE_TABLE +#include <stdio.h> + +#include "opcodes/z8k-opc.h" + +#include "as.h" +#include "bfd.h" +#include <ctype.h> + +const char comment_chars[] = +{'!', 0}; +const char line_separator_chars[] = +{';', 0}; +const char line_comment_chars[] = +{'#', 0}; + +extern int machine; +extern int coff_flags; +int segmented_mode; +const int md_reloc_size; + +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + pseudo-op name without dot + function to call to execute this pseudo-op + Integer arg to pass to the function + */ + +void cons (); + +void +s_segm () +{ + segmented_mode = 1; + machine = bfd_mach_z8001; + coff_flags = F_Z8001; +} + +void +s_unseg () +{ + segmented_mode = 0; + machine = bfd_mach_z8002; + coff_flags = F_Z8002; +} + +static +void +even () +{ + frag_align (1, 0, 0); + record_alignment (now_seg, 1); +} + +void obj_coff_section (); + +int +tohex (c) + int c; +{ + if (isdigit (c)) + return c - '0'; + if (islower (c)) + return c - 'a' + 10; + return c - 'A' + 10; +} + +void +sval () +{ + + SKIP_WHITESPACE (); + if (*input_line_pointer == '\'') + { + int c; + input_line_pointer++; + c = *input_line_pointer++; + while (c != '\'') + { + if (c == '%') + { + c = (tohex (input_line_pointer[0]) << 4) + | tohex (input_line_pointer[1]); + input_line_pointer += 2; + } + FRAG_APPEND_1_CHAR (c); + c = *input_line_pointer++; + } + demand_empty_rest_of_line (); + } + +} +const pseudo_typeS md_pseudo_table[] = +{ + {"int", cons, 2}, + {"data.b", cons, 1}, + {"data.w", cons, 2}, + {"data.l", cons, 4}, + {"form", listing_psize, 0}, + {"heading", listing_title, 0}, + {"import", s_ignore, 0}, + {"page", listing_eject, 0}, + {"program", s_ignore, 0}, + {"z8001", s_segm, 0}, + {"z8002", s_unseg, 0}, + + + {"segm", s_segm, 0}, + {"unsegm", s_unseg, 0}, + {"unseg", s_unseg, 0}, + {"name", s_app_file, 0}, + {"global", s_globl, 0}, + {"wval", cons, 2}, + {"lval", cons, 4}, + {"bval", cons, 1}, + {"sval", sval, 0}, + {"rsect", obj_coff_section, 0}, + {"sect", obj_coff_section, 0}, + {"block", s_space, 0}, + {"even", even, 0}, + {0, 0, 0} +}; + +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +static struct hash_control *opcode_hash_control; /* Opcode mnemonics */ + +void +md_begin () +{ + opcode_entry_type *opcode; + char *prev_name = ""; + int idx = 0; + + opcode_hash_control = hash_new (); + + for (opcode = z8k_table; opcode->name; opcode++) + { + /* Only enter unique codes into the table */ + char *src = opcode->name; + + if (strcmp (opcode->name, prev_name)) + { + hash_insert (opcode_hash_control, opcode->name, (char *) opcode); + idx++; + } + opcode->idx = idx; + prev_name = opcode->name; + } + + /* default to z8002 */ + s_unseg (); + + /* insert the pseudo ops too */ + for (idx = 0; md_pseudo_table[idx].poc_name; idx++) + { + opcode_entry_type *fake_opcode; + fake_opcode = (opcode_entry_type *) malloc (sizeof (opcode_entry_type)); + fake_opcode->name = md_pseudo_table[idx].poc_name, + fake_opcode->func = (void *) (md_pseudo_table + idx); + fake_opcode->opcode = 250; + hash_insert (opcode_hash_control, fake_opcode->name, fake_opcode); + } + + linkrelax = 1; +} + +struct z8k_exp +{ + char *e_beg; + char *e_end; + expressionS e_exp; +}; +typedef struct z8k_op +{ + char regsize; /* 'b','w','r','q' */ + unsigned int reg; /* 0..15 */ + + int mode; + + unsigned int x_reg; /* any other register associated with the mode */ + expressionS exp; /* any expression */ +} + +op_type; + +static expressionS *da_operand; +static expressionS *imm_operand; + +int reg[16]; +int the_cc; +int the_ctrl; +int the_flags; +int the_interrupt; + +char * +DEFUN (whatreg, (reg, src), + int *reg AND + char *src) +{ + if (isdigit (src[1])) + { + *reg = (src[0] - '0') * 10 + src[1] - '0'; + return src + 2; + } + else + { + *reg = (src[0] - '0'); + return src + 1; + } +} + +/* + parse operands + + rh0-rh7, rl0-rl7 + r0-r15 + rr0-rr14 + rq0--rq12 + WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp + r0l,r0h,..r7l,r7h + @WREG + @WREG+ + @-WREG + #const + + */ + +/* try and parse a reg name, returns number of chars consumed */ +char * +DEFUN (parse_reg, (src, mode, reg), + char *src AND + int *mode AND + unsigned int *reg) +{ + char *res = 0; + char regno; + + if (src[0] == 's' && src[1] == 'p') + { + if (segmented_mode) + { + *mode = CLASS_REG_LONG; + *reg = 14; + } + else + { + *mode = CLASS_REG_WORD; + *reg = 15; + } + return src + 2; + } + if (src[0] == 'r') + { + if (src[1] == 'r') + { + *mode = CLASS_REG_LONG; + res = whatreg (reg, src + 2); + regno = *reg; + if (regno > 14) + as_warn (_("register rr%d, out of range."),regno); + } + else if (src[1] == 'h') + { + *mode = CLASS_REG_BYTE; + res = whatreg (reg, src + 2); + regno = *reg; + if (regno > 7) + as_warn (_("register rh%d, out of range."),regno); + } + else if (src[1] == 'l') + { + *mode = CLASS_REG_BYTE; + res = whatreg (reg, src + 2); + regno = *reg; + if (regno > 7) + as_warn (_("register rl%d, out of range."),regno); + *reg += 8; + } + else if (src[1] == 'q') + { + *mode = CLASS_REG_QUAD; + res = whatreg (reg, src + 2); + regno = *reg; + if (regno > 12) + as_warn (_("register rq%d, out of range."),regno); + } + else + { + *mode = CLASS_REG_WORD; + res = whatreg (reg, src + 1); + regno = *reg; + if (regno > 15) + as_warn (_("register r%d, out of range."),regno); + } + } + return res; + +} + +char * +DEFUN (parse_exp, (s, op), + char *s AND + expressionS * op) +{ + char *save = input_line_pointer; + char *new; + + input_line_pointer = s; + expression (op); + if (op->X_op == O_absent) + as_bad (_("missing operand")); + new = input_line_pointer; + input_line_pointer = save; + return new; +} + +/* The many forms of operand: + + <rb> + <r> + <rr> + <rq> + @r + #exp + exp + exp(r) + r(#exp) + r(r) + + + + */ + +static +char * +DEFUN (checkfor, (ptr, what), + char *ptr AND + char what) +{ + if (*ptr == what) + ptr++; + else + { + as_bad (_("expected %c"), what); + } + return ptr; +} + +/* Make sure the mode supplied is the size of a word */ +static void +DEFUN (regword, (mode, string), + int mode AND + char *string) +{ + int ok; + + ok = CLASS_REG_WORD; + if (ok != mode) + { + as_bad (_("register is wrong size for a word %s"), string); + } +} + +/* Make sure the mode supplied is the size of an address */ +static void +DEFUN (regaddr, (mode, string), + int mode AND + char *string) +{ + int ok; + + ok = segmented_mode ? CLASS_REG_LONG : CLASS_REG_WORD; + if (ok != mode) + { + as_bad (_("register is wrong size for address %s"), string); + } +} + +struct ctrl_names +{ + int value; + char *name; +}; + +struct ctrl_names ctrl_table[] = +{ + 0x2, "fcw", + 0X3, "refresh", + 0x4, "psapseg", + 0x5, "psapoff", + 0x5, "psap", + 0x6, "nspseg", + 0x7, "nspoff", + 0x7, "nsp", + 0, 0 +}; + +static void +DEFUN (get_ctrl_operand, (ptr, mode, dst), + char **ptr AND + struct z8k_op *mode AND + unsigned int dst) +{ + char *src = *ptr; + int r; + int i; + + while (*src == ' ') + src++; + + mode->mode = CLASS_CTRL; + for (i = 0; ctrl_table[i].name; i++) + { + int j; + + for (j = 0; ctrl_table[i].name[j]; j++) + { + if (ctrl_table[i].name[j] != src[j]) + goto fail; + } + the_ctrl = ctrl_table[i].value; + *ptr = src + j; + return; + fail:; + } + the_ctrl = 0; + return; +} + +struct flag_names +{ + int value; + char *name; + +}; + +struct flag_names flag_table[] = +{ + 0x1, "p", + 0x1, "v", + 0x2, "s", + 0x4, "z", + 0x8, "c", + 0x0, "+", + 0, 0 +}; + +static void +DEFUN (get_flags_operand, (ptr, mode, dst), + char **ptr AND + struct z8k_op *mode AND + unsigned int dst) +{ + char *src = *ptr; + int r; + int i; + int j; + + while (*src == ' ') + src++; + + mode->mode = CLASS_FLAGS; + the_flags = 0; + for (j = 0; j <= 9; j++) + { + if (!src[j]) + goto done; + for (i = 0; flag_table[i].name; i++) + { + if (flag_table[i].name[0] == src[j]) + { + the_flags = the_flags | flag_table[i].value; + goto match; + } + } + goto done; + match: + ; + } + done: + *ptr = src + j; + return; +} + + +struct interrupt_names +{ + int value; + char *name; + +}; + +struct interrupt_names intr_table[] = +{ + 0x1, "nvi", + 0x2, "vi", + 0x3, "both", + 0x3, "all", + 0, 0 +}; + +static void +DEFUN (get_interrupt_operand, (ptr, mode, dst), + char **ptr AND + struct z8k_op *mode AND + unsigned int dst) +{ + char *src = *ptr; + int r; + int i; + + while (*src == ' ') + src++; + + mode->mode = CLASS_IMM; + for (i = 0; intr_table[i].name; i++) + { + int j; + + for (j = 0; intr_table[i].name[j]; j++) + { + if (intr_table[i].name[j] != src[j]) + goto fail; + } + the_interrupt = intr_table[i].value; + *ptr = src + j; + return; + fail:; + } + the_interrupt = 0x0; + return; +} + +struct cc_names +{ + int value; + char *name; + +}; + +struct cc_names table[] = +{ + 0x0, "f", + 0x1, "lt", + 0x2, "le", + 0x3, "ule", + 0x4, "ov", + 0x4, "pe", + 0x5, "mi", + 0x6, "eq", + 0x6, "z", + 0x7, "c", + 0x7, "ult", + 0x8, "t", + 0x9, "ge", + 0xa, "gt", + 0xb, "ugt", + 0xc, "nov", + 0xc, "po", + 0xd, "pl", + 0xe, "ne", + 0xe, "nz", + 0xf, "nc", + 0xf, "uge", + 0, 0 +}; + +static void +DEFUN (get_cc_operand, (ptr, mode, dst), + char **ptr AND + struct z8k_op *mode AND + unsigned int dst) +{ + char *src = *ptr; + int r; + int i; + + while (*src == ' ') + src++; + + mode->mode = CLASS_CC; + for (i = 0; table[i].name; i++) + { + int j; + + for (j = 0; table[i].name[j]; j++) + { + if (table[i].name[j] != src[j]) + goto fail; + } + the_cc = table[i].value; + *ptr = src + j; + return; + fail:; + } + the_cc = 0x8; +} + +static void +get_operand (ptr, mode, dst) + char **ptr; + struct z8k_op *mode; + unsigned int dst; +{ + char *src = *ptr; + char *end; + unsigned int num; + unsigned int len; + unsigned int size; + + mode->mode = 0; + + while (*src == ' ') + src++; + if (*src == '#') + { + mode->mode = CLASS_IMM; + imm_operand = &(mode->exp); + src = parse_exp (src + 1, &(mode->exp)); + } + else if (*src == '@') + { + int d; + + mode->mode = CLASS_IR; + src = parse_reg (src + 1, &d, &mode->reg); + } + else + { + int regn; + + end = parse_reg (src, &mode->mode, ®n); + + if (end) + { + int nw, nr; + + src = end; + if (*src == '(') + { + src++; + end = parse_reg (src, &nw, &nr); + if (end) + { + /* Got Ra(Rb) */ + src = end; + + if (*src != ')') + { + as_bad (_("Missing ) in ra(rb)")); + } + else + { + src++; + } + + regaddr (mode->mode, "ra(rb) ra"); +/* regword (mode->mode, "ra(rb) rb");*/ + mode->mode = CLASS_BX; + mode->reg = regn; + mode->x_reg = nr; + reg[ARG_RX] = nr; + } + else + { + /* Got Ra(disp) */ + if (*src == '#') + src++; + src = parse_exp (src, &(mode->exp)); + src = checkfor (src, ')'); + mode->mode = CLASS_BA; + mode->reg = regn; + mode->x_reg = 0; + imm_operand = &(mode->exp); + } + } + else + { + mode->reg = regn; + mode->x_reg = 0; + } + } + else + { + /* No initial reg */ + src = parse_exp (src, &(mode->exp)); + if (*src == '(') + { + src++; + end = parse_reg (src, &(mode->mode), ®n); + regword (mode->mode, "addr(Ra) ra"); + mode->mode = CLASS_X; + mode->reg = regn; + mode->x_reg = 0; + da_operand = &(mode->exp); + src = checkfor (end, ')'); + } + else + { + /* Just an address */ + mode->mode = CLASS_DA; + mode->reg = 0; + mode->x_reg = 0; + da_operand = &(mode->exp); + } + } + } + *ptr = src; +} + +static +char * +get_operands (opcode, op_end, operand) + opcode_entry_type *opcode; + char *op_end; + op_type *operand; +{ + char *ptr = op_end; +char *savptr; + switch (opcode->noperands) + { + case 0: + operand[0].mode = 0; + operand[1].mode = 0; + break; + + case 1: + ptr++; + if (opcode->arg_info[0] == CLASS_CC) + { + get_cc_operand (&ptr, operand + 0, 0); + } + else if (opcode->arg_info[0] == CLASS_FLAGS) + { + get_flags_operand (&ptr, operand + 0, 0); + } + else if (opcode->arg_info[0] == (CLASS_IMM +(ARG_IMM2))) + { + get_interrupt_operand (&ptr, operand + 0, 0); + } + else + { + get_operand (&ptr, operand + 0, 0); + } + operand[1].mode = 0; + break; + + case 2: + ptr++; + savptr = ptr; + if (opcode->arg_info[0] == CLASS_CC) + { + get_cc_operand (&ptr, operand + 0, 0); + } + else if (opcode->arg_info[0] == CLASS_CTRL) + { + get_ctrl_operand (&ptr, operand + 0, 0); + if (the_ctrl == 0) + { + ptr = savptr; + get_operand (&ptr, operand + 0, 0); + if (ptr == 0) + return; + if (*ptr == ',') + ptr++; + get_ctrl_operand (&ptr, operand + 1, 1); + return ptr; + } + } + else + { + get_operand (&ptr, operand + 0, 0); + } + if (ptr == 0) + return; + if (*ptr == ',') + ptr++; + get_operand (&ptr, operand + 1, 1); + break; + + case 3: + ptr++; + get_operand (&ptr, operand + 0, 0); + if (*ptr == ',') + ptr++; + get_operand (&ptr, operand + 1, 1); + if (*ptr == ',') + ptr++; + get_operand (&ptr, operand + 2, 2); + break; + + case 4: + ptr++; + get_operand (&ptr, operand + 0, 0); + if (*ptr == ',') + ptr++; + get_operand (&ptr, operand + 1, 1); + if (*ptr == ',') + ptr++; + get_operand (&ptr, operand + 2, 2); + if (*ptr == ',') + ptr++; + get_cc_operand (&ptr, operand + 3, 3); + break; + default: + abort (); + } + + return ptr; +} + +/* Passed a pointer to a list of opcodes which use different + addressing modes, return the opcode which matches the opcodes + provided + */ + +static +opcode_entry_type * +DEFUN (get_specific, (opcode, operands), + opcode_entry_type * opcode AND + op_type * operands) + +{ + opcode_entry_type *this_try = opcode; + int found = 0; + unsigned int noperands = opcode->noperands; + + unsigned int dispreg; + unsigned int this_index = opcode->idx; + + while (this_index == opcode->idx && !found) + { + unsigned int i; + + this_try = opcode++; + for (i = 0; i < noperands; i++) + { + int mode = operands[i].mode; + + if ((mode & CLASS_MASK) != (this_try->arg_info[i] & CLASS_MASK)) + { + /* it could be an pc rel operand, if this is a da mode and + we like disps, then insert it */ + + if (mode == CLASS_DA && this_try->arg_info[i] == CLASS_DISP) + { + /* This is the case */ + operands[i].mode = CLASS_DISP; + } + else if (mode == CLASS_BA && this_try->arg_info[i]) + { + /* Can't think of a way to turn what we've been given into + something that's ok */ + goto fail; + } + else if (this_try->arg_info[i] & CLASS_PR) + { + if (mode == CLASS_REG_LONG && segmented_mode) + { + /* ok */ + } + else if (mode == CLASS_REG_WORD && !segmented_mode) + { + /* ok */ + } + else + goto fail; + } + else + goto fail; + } + switch (mode & CLASS_MASK) + { + default: + break; + case CLASS_X: + case CLASS_IR: + case CLASS_BA: + case CLASS_BX: + case CLASS_DISP: + case CLASS_REG: + case CLASS_REG_WORD: + case CLASS_REG_BYTE: + case CLASS_REG_QUAD: + case CLASS_REG_LONG: + case CLASS_REGN0: + reg[this_try->arg_info[i] & ARG_MASK] = operands[i].reg; + break; + } + } + + found = 1; + fail:; + } + if (found) + return this_try; + else + return 0; +} + +static void +DEFUN (check_operand, (operand, width, string), + struct z8k_op *operand AND + unsigned int width AND + char *string) +{ + if (operand->exp.X_add_symbol == 0 + && operand->exp.X_op_symbol == 0) + { + + /* No symbol involved, let's look at offset, it's dangerous if any of + the high bits are not 0 or ff's, find out by oring or anding with + the width and seeing if the answer is 0 or all fs*/ + if ((operand->exp.X_add_number & ~width) != 0 && + (operand->exp.X_add_number | width) != (~0)) + { + as_warn (_("operand %s0x%x out of range."), string, operand->exp.X_add_number); + } + } + +} + +static char buffer[20]; + +static void +DEFUN (newfix, (ptr, type, operand), + int ptr AND + int type AND + expressionS * operand) +{ + if (operand->X_add_symbol + || operand->X_op_symbol + || operand->X_add_number) + { + fix_new_exp (frag_now, + ptr, + 1, + operand, + 0, + type); + } +} + +static char * +DEFUN (apply_fix, (ptr, type, operand, size), + char *ptr AND + int type AND + expressionS * operand AND + int size) +{ + int n = operand->X_add_number; + + operand->X_add_number = n; + newfix ((ptr - buffer) / 2, type, operand); +#if 1 + switch (size) + { + case 8: /* 8 nibbles == 32 bits */ + *ptr++ = n >> 28; + *ptr++ = n >> 24; + *ptr++ = n >> 20; + *ptr++ = n >> 16; + case 4: /* 4 niblles == 16 bits */ + *ptr++ = n >> 12; + *ptr++ = n >> 8; + case 2: + *ptr++ = n >> 4; + case 1: + *ptr++ = n >> 0; + break; + } +#endif + return ptr; + +} + +/* Now we know what sort of opcodes it is, lets build the bytes - + */ +#define INSERT(x,y) *x++ = y>>24; *x++ = y>> 16; *x++=y>>8; *x++ =y; +static void +build_bytes (this_try, operand) + opcode_entry_type * this_try; + struct z8k_op *operand; +{ + unsigned int i; + + int length; + char *output; + char *output_ptr = buffer; + char part; + int c; + char high; + int nib; + int nibble; + unsigned int *class_ptr; + + frag_wane (frag_now); + frag_new (0); + + memset (buffer, 20, 0); + class_ptr = this_try->byte_info; +top:; + + for (nibble = 0; c = *class_ptr++; nibble++) + { + + switch (c & CLASS_MASK) + { + default: + + abort (); + case CLASS_ADDRESS: + /* Direct address, we don't cope with the SS mode right now */ + if (segmented_mode) + { + da_operand->X_add_number |= 0x80000000; + output_ptr = apply_fix (output_ptr, R_IMM32, da_operand, 8); + } + else + { + output_ptr = apply_fix (output_ptr, R_IMM16, da_operand, 4); + } + da_operand = 0; + break; + case CLASS_DISP8: + /* pc rel 8 bit */ + output_ptr = apply_fix (output_ptr, R_JR, da_operand, 2); + da_operand = 0; + break; + + case CLASS_0DISP7: + /* pc rel 7 bit */ + *output_ptr = 0; + output_ptr = apply_fix (output_ptr, R_DISP7, da_operand, 2); + da_operand = 0; + break; + + case CLASS_1DISP7: + /* pc rel 7 bit */ + *output_ptr = 0x80; + output_ptr = apply_fix (output_ptr, R_DISP7, da_operand, 2); + output_ptr[-2] = 0x8; + da_operand = 0; + break; + + case CLASS_BIT_1OR2: + *output_ptr = c & 0xf; + if (imm_operand) + { + if (imm_operand->X_add_number == 2) + { + *output_ptr |= 2; + } + else if (imm_operand->X_add_number != 1) + { + as_bad (_("immediate must be 1 or 2")); + } + } + else + { + as_bad (_("immediate 1 or 2 expected")); + } + output_ptr++; + break; + case CLASS_CC: + *output_ptr++ = the_cc; + break; + case CLASS_0CCC: + *output_ptr++ = the_ctrl; + break; + case CLASS_1CCC: + *output_ptr++ = the_ctrl | 0x8; + break; + case CLASS_00II: + *output_ptr++ = (~the_interrupt & 0x3); + break; + case CLASS_01II: + *output_ptr++ = (~the_interrupt & 0x3) | 0x4; + break; + case CLASS_FLAGS: + *output_ptr++ = the_flags; + break; + case CLASS_BIT: + *output_ptr++ = c & 0xf; + break; + case CLASS_REGN0: + if (reg[c & 0xf] == 0) + { + as_bad (_("can't use R0 here")); + } + case CLASS_REG: + case CLASS_REG_BYTE: + case CLASS_REG_WORD: + case CLASS_REG_LONG: + case CLASS_REG_QUAD: + /* Insert bit mattern of + right reg */ + *output_ptr++ = reg[c & 0xf]; + break; + case CLASS_DISP: + output_ptr = apply_fix (output_ptr, R_IMM16, da_operand, 4); + da_operand = 0; + break; + + case CLASS_IMM: + { + nib = 0; + switch (c & ARG_MASK) + { + case ARG_IMM4: + output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1); + break; + case ARG_IMM4M1: + imm_operand->X_add_number--; + output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1); + break; + case ARG_IMMNMINUS1: + imm_operand->X_add_number--; + output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1); + break; + case ARG_NIM8: + imm_operand->X_add_number = -imm_operand->X_add_number; + case ARG_IMM8: + output_ptr = apply_fix (output_ptr, R_IMM8, imm_operand, 2); + break; + case ARG_IMM16: + output_ptr = apply_fix (output_ptr, R_IMM16, imm_operand, 4); + break; + + case ARG_IMM32: + output_ptr = apply_fix (output_ptr, R_IMM32, imm_operand, 8); + break; + + default: + abort (); + } + } + } + } + + /* Copy from the nibble buffer into the frag */ + + { + int length = (output_ptr - buffer) / 2; + char *src = buffer; + char *fragp = frag_more (length); + + while (src < output_ptr) + { + *fragp = (src[0] << 4) | src[1]; + src += 2; + fragp++; + } + + } + +} + +/* This is the guts of the machine-dependent assembler. STR points to a + machine dependent instruction. This funciton is supposed to emit + the frags/bytes it assembles to. + */ + +void +DEFUN (md_assemble, (str), + char *str) +{ + char *op_start; + char *op_end; + unsigned int i; + struct z8k_op operand[3]; + opcode_entry_type *opcode; + opcode_entry_type *prev_opcode; + + char *dot = 0; + char c; + + /* Drop leading whitespace */ + while (*str == ' ') + str++; + + /* find the op code end */ + for (op_start = op_end = str; + *op_end != 0 && *op_end != ' '; + op_end++) + { + } + + ; + + if (op_end == op_start) + { + as_bad (_("can't find opcode ")); + } + c = *op_end; + + *op_end = 0; + + opcode = (opcode_entry_type *) hash_find (opcode_hash_control, + op_start); + + + if (opcode == NULL) + { + as_bad (_("unknown opcode")); + return; + } + + if (opcode->opcode == 250) + { + /* was really a pseudo op */ + + pseudo_typeS *p; + char oc; + + char *old = input_line_pointer; + *op_end = c; + + + input_line_pointer = op_end; + + oc = *old; + *old = '\n'; + while (*input_line_pointer == ' ') + input_line_pointer++; + p = (pseudo_typeS *) (opcode->func); + + (p->poc_handler) (p->poc_val); + input_line_pointer = old; + *old = oc; + } + else + { + input_line_pointer = get_operands (opcode, op_end, + operand); + prev_opcode = opcode; + + opcode = get_specific (opcode, operand); + + if (opcode == 0) + { + /* Couldn't find an opcode which matched the operands */ + char *where = frag_more (2); + + where[0] = 0x0; + where[1] = 0x0; + + as_bad (_("Can't find opcode to match operands")); + return; + } + + build_bytes (opcode, operand); + } +} + +void +DEFUN (tc_crawl_symbol_chain, (headers), + object_headers * headers) +{ + printf (_("call to tc_crawl_symbol_chain \n")); +} + +symbolS * +DEFUN (md_undefined_symbol, (name), + char *name) +{ + return 0; +} + +void +DEFUN (tc_headers_hook, (headers), + object_headers * headers) +{ + printf (_("call to tc_headers_hook \n")); +} + +/* Various routines to kill one day */ +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +/* Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof (type, litP, sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee (); + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP = 0; + return _("Bad call to MD_ATOF()"); + } + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * sizeof (LITTLENUM_TYPE); + for (wordP = words; prec--;) + { + md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + return 0; +} + +CONST char *md_shortopts = "z:"; +struct option md_longopts[] = { + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof(md_longopts); + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + switch (c) + { + case 'z': + if (!strcmp (arg, "8001")) + s_segm (); + else if (!strcmp (arg, "8002")) + s_unseg (); + else + { + as_bad (_("invalid architecture -z%s"), arg); + return 0; + } + break; + + default: + return 0; + } + + return 1; +} + +void +md_show_usage (stream) + FILE *stream; +{ + fprintf(stream, _("\ +Z8K options:\n\ +-z8001 generate segmented code\n\ +-z8002 generate unsegmented code\n")); +} + +void +tc_aout_fix_to_chars () +{ + printf (_("call to tc_aout_fix_to_chars \n")); + abort (); +} + +void +md_convert_frag (headers, seg, fragP) + object_headers *headers; + segT seg; + fragS *fragP; +{ + printf (_("call to md_convert_frag \n")); + abort (); +} + +valueT +DEFUN (md_section_align, (seg, size), + segT seg AND + valueT size) +{ + return ((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg])); + +} + +void +md_apply_fix (fixP, val) + fixS *fixP; + long val; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + + switch (fixP->fx_r_type) + { + case R_IMM4L: + buf[0] = (buf[0] & 0xf0) | ((buf[0] + val) & 0xf); + break; + + case R_JR: + + *buf++ = val; + /* if (val != 0) abort();*/ + break; + + case R_DISP7: + + *buf++ += val; + /* if (val != 0) abort();*/ + break; + + case R_IMM8: + buf[0] += val; + break; + case R_IMM16: + *buf++ = (val >> 8); + *buf++ = val; + break; + case R_IMM32: + *buf++ = (val >> 24); + *buf++ = (val >> 16); + *buf++ = (val >> 8); + *buf++ = val; + break; +#if 0 + case R_DA | R_SEG: + *buf++ = (val >> 16); + *buf++ = 0x00; + *buf++ = (val >> 8); + *buf++ = val; + break; +#endif + + case 0: + md_number_to_chars (buf, val, fixP->fx_size); + break; + + default: + abort (); + + } +} + +int +md_estimate_size_before_relax (fragP, segment_type) + register fragS *fragP; + register segT segment_type; +{ + printf (_("call tomd_estimate_size_before_relax \n")); + abort (); +} + +/* Put number into target byte order */ + +void +DEFUN (md_number_to_chars, (ptr, use, nbytes), + char *ptr AND + valueT use AND + int nbytes) +{ + number_to_chars_bigendian (ptr, use, nbytes); +} +long +md_pcrel_from (fixP) + fixS *fixP; +{ + abort (); +} + +void +tc_coff_symbol_emit_hook (s) + struct symbol *s; +{ +} + +void +tc_reloc_mangle (fix_ptr, intr, base) + fixS *fix_ptr; + struct internal_reloc *intr; + bfd_vma base; + +{ + symbolS *symbol_ptr; + + if (fix_ptr->fx_addsy && + fix_ptr->fx_subsy) + { + symbolS *add = fix_ptr->fx_addsy; + symbolS *sub = fix_ptr->fx_subsy; + if (S_GET_SEGMENT(add) != S_GET_SEGMENT(sub)) + { + as_bad(_("Can't subtract symbols in different sections %s %s"), + S_GET_NAME(add), S_GET_NAME(sub)); + } + else { + int diff = S_GET_VALUE(add) - S_GET_VALUE(sub); + fix_ptr->fx_addsy = 0; + fix_ptr->fx_subsy = 0; + fix_ptr->fx_offset += diff; + } + } + symbol_ptr = fix_ptr->fx_addsy; + + /* If this relocation is attached to a symbol then it's ok + to output it */ + if (fix_ptr->fx_r_type == 0) + { + /* cons likes to create reloc32's whatever the size of the reloc.. */ + switch (fix_ptr->fx_size) + { + case 2: + intr->r_type = R_IMM16; + break; + case 1: + intr->r_type = R_IMM8; + break; + case 4: + intr->r_type = R_IMM32; + break; + default: + abort (); + } + + } + else + { + intr->r_type = fix_ptr->fx_r_type; + } + + intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base; + intr->r_offset = fix_ptr->fx_offset; + + if (symbol_ptr) + intr->r_symndx = symbol_ptr->sy_number; + else + intr->r_symndx = -1; +} + |