Commit | Line | Data |
---|---|---|
276dd6ef | 1 | /* tc-dvp.c -- Assembler for the DVP |
f7306261 | 2 | Copyright (C) 1997, 1998 Free Software Foundation. |
209fb346 DE |
3 | |
4 | This file is part of GAS, the GNU Assembler. | |
5 | ||
6 | GAS is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GAS is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GAS; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 59 Temple Place - Suite 330, | |
19 | Boston, MA 02111-1307, USA. */ | |
20 | ||
21 | #include <stdio.h> | |
22 | #include <ctype.h> | |
1ece1d56 | 23 | |
e033023f | 24 | #include "sysdep.h" |
209fb346 | 25 | #include "as.h" |
f6428b86 | 26 | #include "subsegs.h" |
276dd6ef | 27 | /* Needed by opcode/dvp.h. */ |
f6428b86 | 28 | #include "dis-asm.h" |
276dd6ef | 29 | #include "opcode/dvp.h" |
3a6b8910 | 30 | #include "elf/mips.h" |
95bfad6d | 31 | |
1ece1d56 DE |
32 | #ifdef USE_STDARG |
33 | #include <stdarg.h> | |
34 | #else | |
35 | #include <varargs.h> | |
36 | #endif | |
37 | ||
8b901ef8 DE |
38 | /* Value of VIF `nop' instruction. */ |
39 | #define VIFNOP 0 | |
40 | ||
ba4be194 DE |
41 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
42 | ||
1ece1d56 DE |
43 | /* Compute DMA operand index number of OP. */ |
44 | #define DMA_OPERAND_INDEX(op) ((op) - dma_operands) | |
19f12fb4 | 45 | |
91572941 DE |
46 | /* Our local label prefix. */ |
47 | #define LOCAL_LABEL_PREFIX ".L" | |
48 | /* Label prefix for end markers used in autocounts. */ | |
49 | #define END_LABEL_PREFIX ".L.end." | |
9152beba DE |
50 | /* Label to use for unique labels. */ |
51 | #define UNIQUE_LABEL_PREFIX ".L.dvptmp." | |
91572941 | 52 | |
1ece1d56 | 53 | static long parse_float PARAMS ((char **, const char **)); |
57d0c830 | 54 | static symbolS * create_label PARAMS ((const char *, const char *)); |
b6d331b9 | 55 | static symbolS * create_colon_label PARAMS ((int, const char *, const char *)); |
57d0c830 | 56 | static char * unique_name PARAMS ((const char *)); |
fa3671a3 DE |
57 | static int compute_nloop PARAMS ((gif_type, int, int)); |
58 | static void check_nloop PARAMS ((gif_type, int, int, int, | |
59 | char *, unsigned int)); | |
1ece1d56 | 60 | static long eval_expr PARAMS ((int, int, const char *, ...)); |
1ece1d56 | 61 | static long parse_dma_addr_autocount (); |
8151801a | 62 | static void inline_dma_data PARAMS ((int, DVP_INSN *)); |
e9cb12e4 | 63 | static void setup_dma_autocount PARAMS ((const char *, DVP_INSN *, int)); |
8dddf63f | 64 | |
6856244d DE |
65 | static void insert_operand |
66 | PARAMS ((dvp_cpu, const dvp_opcode *, const dvp_operand *, int, | |
67 | DVP_INSN *, offsetT, const char **)); | |
68 | static void insert_operand_final | |
69 | PARAMS ((dvp_cpu, const dvp_operand *, int, | |
70 | DVP_INSN *, offsetT, char *, unsigned int)); | |
020ba60b | 71 | |
8b901ef8 DE |
72 | static void insert_mpg_marker PARAMS ((unsigned long)); |
73 | static void insert_unpack_marker PARAMS ((unsigned long)); | |
74 | static int insert_file PARAMS ((const char *, | |
75 | void (*) PARAMS ((unsigned long)), | |
76 | unsigned long, int)); | |
e033023f | 77 | |
8b901ef8 | 78 | static int vif_length_value PARAMS ((int, int, int, int)); |
b4cbabb8 | 79 | static void install_vif_length PARAMS ((char *, int)); |
e033023f | 80 | |
209fb346 DE |
81 | const char comment_chars[] = ";"; |
82 | const char line_comment_chars[] = "#"; | |
83 | const char line_separator_chars[] = "!"; | |
84 | const char EXP_CHARS[] = "eE"; | |
85 | const char FLT_CHARS[] = "dD"; | |
8b901ef8 | 86 | \f |
e033023f DE |
87 | /* Current assembler state. |
88 | Instructions like mpg and direct are followed by a restricted set of | |
fe9efeb6 DE |
89 | instructions. In the case of a '*' length argument an end marker must |
90 | be provided. (e.g. mpg is followed by vu insns until a .EndMpg is | |
498fcb9c DE |
91 | seen). |
92 | ||
93 | Allowed state transitions: | |
94 | ASM_INIT -> ASM_MPG | |
95 | ASM_DIRECT -> ASM_GIF | |
96 | ASM_UNPACK | |
97 | ASM_VU | |
98 | */ | |
99 | ||
e1b747c4 | 100 | typedef enum { |
498fcb9c | 101 | ASM_INIT, ASM_DIRECT, ASM_MPG, ASM_UNPACK, ASM_VU, ASM_GIF |
e1b747c4 | 102 | } asm_state; |
498fcb9c DE |
103 | |
104 | /* We need to maintain a stack of the current and previous status to handle | |
105 | such things as "direct ...; gifpacked ... ; .endgif ; .enddirect". */ | |
106 | #define MAX_STATE_DEPTH 2 | |
107 | static asm_state asm_state_stack[MAX_STATE_DEPTH]; | |
108 | /* Current state's index in the stack. */ | |
109 | static int cur_state_index; | |
110 | /* Macro to fetch the current state. */ | |
111 | #define CUR_ASM_STATE (asm_state_stack[cur_state_index]) | |
112 | ||
113 | /* Functions to push/pop the state stack. */ | |
114 | static void push_asm_state PARAMS ((asm_state)); | |
115 | static void pop_asm_state PARAMS ((int)); | |
116 | static void set_asm_state PARAMS ((asm_state)); | |
8b901ef8 | 117 | \f |
b6d331b9 | 118 | /* Current cpu (machine variant) type state. |
57d0c830 DE |
119 | We copy the mips16 way of recording what the current machine type is in |
120 | the code. A label is created whenever necessary and has an "other" value | |
121 | the denotes the machine type. */ | |
b6d331b9 | 122 | static dvp_cpu cur_cpu; |
57d0c830 | 123 | /* Record the current mach type. */ |
b6d331b9 | 124 | static void record_mach PARAMS ((dvp_cpu, int)); |
3b4389e2 DE |
125 | /* Force emission of mach type label at next insn. |
126 | This isn't static as TC_START_LABEL uses it. */ | |
127 | int force_mach_label PARAMS ((void)); | |
b6d331b9 DE |
128 | /* Given a dvp_cpu value, return the STO_DVP value to use. */ |
129 | static int cpu_sto PARAMS ((dvp_cpu, const char **)); | |
57d0c830 | 130 | |
91572941 | 131 | /* Nonzero if inside .DmaData. */ |
8151801a | 132 | static int dma_data_state = 0; |
91572941 | 133 | /* Label of .DmaData (internally generated for inline data). */ |
8151801a DE |
134 | static const char *dma_data_name; |
135 | ||
8b901ef8 DE |
136 | /* Variable length VIF insn support. */ |
137 | /* Label at start of insn's data. */ | |
138 | static symbolS *vif_data_start; | |
139 | /* Label at end of insn's data. */ | |
140 | static symbolS *vif_data_end; | |
141 | ||
142 | /* Special symbol $.mpgloc. */ | |
143 | static symbolS *mpgloc_sym; | |
144 | /* Special symbol $.unpackloc. */ | |
145 | static symbolS *unpackloc_sym; | |
146 | ||
147 | /* GIF insn support. */ | |
148 | /* Type of insn. */ | |
149 | static int gif_insn_type; | |
150 | /* Name of label of insn's data. */ | |
8151801a | 151 | static const char *gif_data_name; |
8b901ef8 | 152 | /* Pointer to frag of insn. */ |
fa3671a3 DE |
153 | static fragS *gif_insn_frag; |
154 | /* Pointer to current gif insn in gif_insn_frag. */ | |
155 | static char *gif_insn_frag_loc; | |
fe9efeb6 | 156 | /* The length value specified in the insn, or -1 if '*'. */ |
8b901ef8 | 157 | static int gif_user_value; |
b5d20cf6 | 158 | |
83920d29 DE |
159 | /* Count of vu insns seen since the last mpg. |
160 | Set to -1 to disable automatic mpg insertion. */ | |
161 | static int vu_count; | |
162 | ||
b4cbabb8 DE |
163 | /* Non-zero if packing vif instructions in dma tags. */ |
164 | static int dma_pack_vif_p; | |
fe9efeb6 DE |
165 | |
166 | /* Non-zero if dma insns are to be included in the output. | |
167 | This is the default, but writing "if (! no_dma)" is klunky. */ | |
08847198 | 168 | static int output_dma = 1; |
b4cbabb8 DE |
169 | /* Non-zero if vif insns are to be included in the output. */ |
170 | static int output_vif = 1; | |
8b901ef8 DE |
171 | |
172 | /* Current opcode/operand for use by md_operand. */ | |
173 | static const dvp_opcode *cur_opcode; | |
174 | static const dvp_operand *cur_operand; | |
175 | ||
176 | /* Options for the `caller' argument to s_endmpg. */ | |
177 | typedef enum { ENDMPG_USER, ENDMPG_INTERNAL, ENDMPG_MIDDLE } endmpg_caller; | |
209fb346 DE |
178 | \f |
179 | const char *md_shortopts = ""; | |
180 | ||
181 | struct option md_longopts[] = | |
182 | { | |
fe9efeb6 DE |
183 | #define OPTION_NO_DMA (OPTION_MD_BASE + 1) |
184 | { "no-dma", no_argument, NULL, OPTION_NO_DMA }, | |
b4cbabb8 DE |
185 | #define OPTION_NO_DMA_VIF (OPTION_NO_DMA + 1) |
186 | { "no-dma-vif", no_argument, NULL, OPTION_NO_DMA_VIF }, | |
209fb346 DE |
187 | |
188 | {NULL, no_argument, NULL, 0} | |
189 | }; | |
fe9efeb6 | 190 | size_t md_longopts_size = sizeof(md_longopts); |
209fb346 DE |
191 | |
192 | int | |
193 | md_parse_option (c, arg) | |
194 | int c; | |
195 | char *arg; | |
196 | { | |
fe9efeb6 DE |
197 | switch (c) |
198 | { | |
199 | case OPTION_NO_DMA : | |
200 | output_dma = 0; | |
201 | break; | |
b4cbabb8 | 202 | case OPTION_NO_DMA_VIF : |
fe9efeb6 | 203 | output_dma = 0; |
b4cbabb8 | 204 | output_vif = 0; |
fe9efeb6 DE |
205 | break; |
206 | default : | |
207 | return 0; | |
208 | } | |
209 | return 1; | |
209fb346 DE |
210 | } |
211 | ||
212 | void | |
213 | md_show_usage (stream) | |
214 | FILE *stream; | |
215 | { | |
fe9efeb6 DE |
216 | fprintf (stream, "\ |
217 | DVP options:\n\ | |
218 | -no-dma do not include DMA instructions in the output\n\ | |
b4cbabb8 | 219 | -no-dma-vif do not include DMA or VIF instructions in the output\n\ |
fe9efeb6 | 220 | "); |
209fb346 DE |
221 | } |
222 | ||
276dd6ef | 223 | /* Set by md_assemble for use by dvp_fill_insn. */ |
209fb346 DE |
224 | static subsegT prev_subseg; |
225 | static segT prev_seg; | |
226 | ||
b5d20cf6 | 227 | static void s_dmadata PARAMS ((int)); |
3b2215c2 | 228 | static void s_enddmadata PARAMS ((int)); |
b4cbabb8 | 229 | static void s_dmapackvif PARAMS ((int)); |
b5d20cf6 | 230 | static void s_enddirect PARAMS ((int)); |
b5d20cf6 DE |
231 | static void s_endmpg PARAMS ((int)); |
232 | static void s_endunpack PARAMS ((int)); | |
498fcb9c | 233 | static void s_endgif PARAMS ((int)); |
e033023f | 234 | static void s_state PARAMS ((int)); |
b5d20cf6 | 235 | |
209fb346 DE |
236 | /* The target specific pseudo-ops which we support. */ |
237 | const pseudo_typeS md_pseudo_table[] = | |
238 | { | |
1ece1d56 | 239 | { "word", cons, 4 }, |
fa3671a3 | 240 | { "quad", cons, 16 }, |
1ece1d56 DE |
241 | { "dmadata", s_dmadata, 0 }, |
242 | { "dmapackvif", s_dmapackvif, 0 }, | |
243 | { "enddirect", s_enddirect, 0 }, | |
244 | { "enddmadata", s_enddmadata, 0 }, | |
8b901ef8 | 245 | { "endmpg", s_endmpg, ENDMPG_USER }, |
1ece1d56 | 246 | { "endunpack", s_endunpack, 0 }, |
498fcb9c | 247 | { "endgif", s_endgif, 0 }, |
1ece1d56 DE |
248 | { "vu", s_state, ASM_VU }, |
249 | { NULL, NULL, 0 } | |
209fb346 DE |
250 | }; |
251 | \f | |
252 | void | |
253 | md_begin () | |
254 | { | |
f6428b86 DE |
255 | /* Initialize the opcode tables. |
256 | This involves computing the hash chains. */ | |
276dd6ef | 257 | dvp_opcode_init_tables (0); |
b5d20cf6 | 258 | |
3b4389e2 DE |
259 | /* Force a mach type label for the first insn. */ |
260 | force_mach_label (); | |
57d0c830 DE |
261 | |
262 | /* Initialize the parsing state. */ | |
498fcb9c DE |
263 | cur_state_index = 0; |
264 | set_asm_state (ASM_INIT); | |
57d0c830 | 265 | |
7f28a81d DE |
266 | /* Pack vif insns in dma tags by default. */ |
267 | dma_pack_vif_p = 1; | |
83920d29 DE |
268 | |
269 | /* Disable automatic mpg insertion. */ | |
270 | vu_count = -1; | |
8b901ef8 DE |
271 | |
272 | /* Create special symbols. */ | |
273 | mpgloc_sym = expr_build_uconstant (0); | |
274 | unpackloc_sym = expr_build_uconstant (0); | |
209fb346 | 275 | } |
f7306261 | 276 | \f |
f6428b86 DE |
277 | /* We need to keep a list of fixups. We can't simply generate them as |
278 | we go, because that would require us to first create the frag, and | |
279 | that would screw up references to ``.''. */ | |
280 | ||
276dd6ef | 281 | struct dvp_fixup |
f6428b86 | 282 | { |
276dd6ef | 283 | /* index into `dvp_operands' */ |
f6428b86 | 284 | int opindex; |
e033023f DE |
285 | /* byte offset from beginning of instruction */ |
286 | int offset; | |
8b901ef8 DE |
287 | /* user specified value [when there is one] */ |
288 | int user_value; | |
289 | /* wl,cl values, only used with unpack insn */ | |
290 | short wl,cl; | |
291 | /* the expression */ | |
f6428b86 DE |
292 | expressionS exp; |
293 | }; | |
294 | ||
295 | #define MAX_FIXUPS 5 | |
296 | ||
f7306261 | 297 | static int fixup_count; |
276dd6ef | 298 | static struct dvp_fixup fixups[MAX_FIXUPS]; |
f7306261 | 299 | |
95bfad6d DE |
300 | /* Given a cpu type and operand number, return a temporary reloc type |
301 | for use in generating the fixup that encodes the cpu type and operand. */ | |
3a6b8910 | 302 | static int encode_fixup_reloc_type PARAMS ((dvp_cpu, int)); |
95bfad6d | 303 | /* Given an encoded fixup reloc type, decode it into cpu and operand. */ |
3a6b8910 | 304 | static void decode_fixup_reloc_type PARAMS ((int, dvp_cpu *, |
276dd6ef | 305 | const dvp_operand **)); |
95bfad6d | 306 | |
b5d20cf6 | 307 | static void assemble_dma PARAMS ((char *)); |
b4cbabb8 DE |
308 | static void assemble_gif PARAMS ((char *)); |
309 | static void assemble_vif PARAMS ((char *)); | |
b5d20cf6 | 310 | static void assemble_vu PARAMS ((char *)); |
3a6b8910 DE |
311 | static const dvp_opcode * assemble_vu_insn PARAMS ((dvp_cpu, |
312 | const dvp_opcode *, | |
313 | const dvp_operand *, | |
314 | char **, char *)); | |
315 | static const dvp_opcode * assemble_one_insn PARAMS ((dvp_cpu, | |
316 | const dvp_opcode *, | |
317 | const dvp_operand *, | |
318 | char **, DVP_INSN *)); | |
319 | ||
320 | /* Main entry point for assembling an instruction. */ | |
f6428b86 | 321 | |
209fb346 DE |
322 | void |
323 | md_assemble (str) | |
324 | char *str; | |
b5d20cf6 | 325 | { |
f7306261 DE |
326 | /* Skip leading white space. */ |
327 | while (isspace (*str)) | |
328 | str++; | |
329 | ||
8151801a | 330 | /* After a gif tag, no insns can appear until a .endgif is seen. */ |
498fcb9c | 331 | if (CUR_ASM_STATE == ASM_GIF) |
8151801a DE |
332 | { |
333 | as_bad ("missing .endgif"); | |
498fcb9c DE |
334 | pop_asm_state (1); |
335 | } | |
336 | /* Ditto for unpack. */ | |
337 | if (CUR_ASM_STATE == ASM_UNPACK) | |
338 | { | |
339 | as_bad ("missing .endunpack"); | |
340 | pop_asm_state (1); | |
341 | } | |
342 | #if 0 /* this doesn't work of course as gif insns may follow */ | |
343 | /* Ditto for direct. */ | |
344 | if (CUR_ASM_STATE == ASM_DIRECT) | |
345 | { | |
346 | as_bad ("missing .enddirect"); | |
347 | pop_asm_state (1); | |
8151801a | 348 | } |
498fcb9c | 349 | #endif |
8151801a | 350 | |
498fcb9c | 351 | if (CUR_ASM_STATE == ASM_INIT) |
b5d20cf6 DE |
352 | { |
353 | if (strncasecmp (str, "dma", 3) == 0) | |
354 | assemble_dma (str); | |
1ece1d56 | 355 | else if (strncasecmp (str, "gif", 3) == 0) |
19f12fb4 | 356 | assemble_gif (str); |
b5d20cf6 | 357 | else |
b4cbabb8 | 358 | assemble_vif (str); |
b5d20cf6 | 359 | } |
498fcb9c | 360 | else if (CUR_ASM_STATE == ASM_DIRECT) |
d3c6610c | 361 | assemble_gif (str); |
498fcb9c DE |
362 | else if (CUR_ASM_STATE == ASM_VU |
363 | || CUR_ASM_STATE == ASM_MPG) | |
b5d20cf6 | 364 | assemble_vu (str); |
e033023f | 365 | else |
8b901ef8 | 366 | as_fatal ("internal error: unknown parse state"); |
b5d20cf6 DE |
367 | } |
368 | ||
f7306261 | 369 | /* Subroutine of md_assemble to assemble DMA instructions. */ |
b5d20cf6 DE |
370 | |
371 | static void | |
372 | assemble_dma (str) | |
373 | char *str; | |
374 | { | |
7f28a81d | 375 | DVP_INSN insn_buf[2]; |
1ece1d56 DE |
376 | /* Insn's length, in 32 bit words. */ |
377 | int len; | |
378 | /* Pointer to allocated frag. */ | |
379 | char *f; | |
380 | int i; | |
381 | const dvp_opcode *opcode; | |
382 | ||
e9cb12e4 DE |
383 | if (output_dma) |
384 | { | |
385 | /* Do an implicit alignment to a 16 byte boundary. | |
386 | Do it now so that inline dma data labels are at the right place. */ | |
8b901ef8 DE |
387 | /* ??? One can certainly argue all this implicit alignment is |
388 | questionable. The thing is assembler programming is all that will | |
389 | mostly likely ever be done and not doing so forces an extra [and | |
390 | arguably unnecessary] burden on the programmer. */ | |
e9cb12e4 DE |
391 | frag_align (4, 0, 0); |
392 | record_alignment (now_seg, 4); | |
393 | } | |
394 | ||
395 | /* This is the DMA tag. */ | |
1ece1d56 DE |
396 | insn_buf[0] = 0; |
397 | insn_buf[1] = 0; | |
1ece1d56 DE |
398 | |
399 | opcode = assemble_one_insn (DVP_DMA, | |
400 | dma_opcode_lookup_asm (str), dma_operands, | |
401 | &str, insn_buf); | |
402 | if (opcode == NULL) | |
403 | return; | |
404 | if (!output_dma) | |
405 | return; | |
406 | ||
b6d331b9 | 407 | record_mach (DVP_DMA, 0); |
57d0c830 | 408 | |
7f28a81d | 409 | f = frag_more (8); |
1ece1d56 DE |
410 | |
411 | /* Write out the DMA instruction. */ | |
7f28a81d | 412 | for (i = 0; i < 2; ++i) |
1ece1d56 | 413 | md_number_to_chars (f + i * 4, insn_buf[i], 4); |
f7306261 | 414 | |
1ece1d56 DE |
415 | /* Create any fixups. */ |
416 | /* FIXME: It might eventually be possible to combine all the various | |
417 | copies of this bit of code. */ | |
418 | for (i = 0; i < fixup_count; ++i) | |
3b2215c2 | 419 | { |
1ece1d56 DE |
420 | int op_type, reloc_type, offset; |
421 | const dvp_operand *operand; | |
3b2215c2 | 422 | |
1ece1d56 DE |
423 | /* Create a fixup for this operand. |
424 | At this point we do not use a bfd_reloc_code_real_type for | |
425 | operands residing in the insn, but instead just use the | |
426 | operand index. This lets us easily handle fixups for any | |
427 | operand type, although that is admittedly not a very exciting | |
428 | feature. We pick a BFD reloc type in md_apply_fix. */ | |
429 | ||
430 | op_type = fixups[i].opindex; | |
431 | offset = fixups[i].offset; | |
432 | reloc_type = encode_fixup_reloc_type (DVP_DMA, op_type); | |
433 | operand = &dma_operands[op_type]; | |
434 | fix_new_exp (frag_now, f + offset - frag_now->fr_literal, 4, | |
435 | &fixups[i].exp, | |
436 | (operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0, | |
437 | (bfd_reloc_code_real_type) reloc_type); | |
3b2215c2 | 438 | } |
7f28a81d DE |
439 | |
440 | /* The upper two words are vif insns. */ | |
441 | record_mach (DVP_VIF, 0); | |
442 | ||
443 | /* If not doing dma/vif packing, fill out the insn with vif nops. | |
444 | ??? We take advantage of the fact that the default fill value of zero | |
445 | is the vifnop insn. This occurs for example when handling mpg | |
446 | alignment. It also occurs when one dma tag immediately follows the | |
447 | previous one. */ | |
448 | if (! dma_pack_vif_p) | |
449 | { | |
450 | f = frag_more (8); | |
7f28a81d DE |
451 | md_number_to_chars (f + 0, VIFNOP, 4); |
452 | md_number_to_chars (f + 4, VIFNOP, 4); | |
453 | } | |
b5d20cf6 DE |
454 | } |
455 | ||
b4cbabb8 | 456 | /* Subroutine of md_assemble to assemble VIF instructions. */ |
b5d20cf6 DE |
457 | |
458 | static void | |
b4cbabb8 | 459 | assemble_vif (str) |
b5d20cf6 DE |
460 | char *str; |
461 | { | |
95bfad6d DE |
462 | /* Space for the instruction. |
463 | The variable length insns can require much more space than this. | |
464 | It is allocated later, when we know we have such an insn. */ | |
276dd6ef | 465 | DVP_INSN insn_buf[5]; |
95bfad6d DE |
466 | /* Insn's length, in 32 bit words. */ |
467 | int len; | |
95bfad6d DE |
468 | /* Pointer to allocated frag. */ |
469 | char *f; | |
8b901ef8 | 470 | int i,wl,cl; |
3a6b8910 | 471 | const dvp_opcode *opcode; |
8b901ef8 DE |
472 | fragS * insn_frag; |
473 | /* Name of file to read data from. */ | |
474 | const char *file; | |
475 | /* Length in 32 bit words. */ | |
476 | int data_len; | |
95bfad6d | 477 | |
b4cbabb8 DE |
478 | opcode = assemble_one_insn (DVP_VIF, |
479 | vif_opcode_lookup_asm (str), vif_operands, | |
3a6b8910 DE |
480 | &str, insn_buf); |
481 | if (opcode == NULL) | |
95bfad6d DE |
482 | return; |
483 | ||
b4cbabb8 | 484 | if (opcode->flags & VIF_OPCODE_LENVAR) |
e033023f | 485 | len = 1; /* actual data follows later */ |
b4cbabb8 | 486 | else if (opcode->flags & VIF_OPCODE_LEN2) |
3a6b8910 | 487 | len = 2; |
b4cbabb8 | 488 | else if (opcode->flags & VIF_OPCODE_LEN5) |
3a6b8910 | 489 | len = 5; |
95bfad6d | 490 | else |
3a6b8910 DE |
491 | len = 1; |
492 | ||
fe9efeb6 | 493 | /* We still have to switch modes (if mpg for example) so we can't exit |
b4cbabb8 | 494 | early if -no-vif. */ |
8b901ef8 | 495 | |
b4cbabb8 | 496 | if (output_vif) |
95bfad6d | 497 | { |
83920d29 | 498 | /* Record the mach before doing the alignment so that we properly |
8b901ef8 DE |
499 | disassemble any inserted vifnop's. For mpg and direct insns |
500 | force the recording of the mach type for the next insn. The data | |
501 | will switch the mach type and we want to ensure it's switched | |
502 | back. */ | |
83920d29 | 503 | |
8b901ef8 | 504 | if (opcode->flags & (VIF_OPCODE_MPG | VIF_OPCODE_DIRECT)) |
b6d331b9 DE |
505 | record_mach (DVP_VIF, 1); |
506 | else | |
507 | record_mach (DVP_VIF, 0); | |
57d0c830 | 508 | |
8b901ef8 DE |
509 | /* For variable length instructions record a fixup that is the symbol |
510 | marking the end of the data. eval_expr will queue the fixup | |
511 | which will then be emitted later. */ | |
512 | if (opcode->flags & VIF_OPCODE_LENVAR) | |
513 | { | |
514 | char *name; | |
515 | ||
516 | asprintf (&name, "%s%s", LOCAL_LABEL_PREFIX, | |
517 | unique_name ("varlen")); | |
518 | vif_data_end = symbol_new (name, now_seg, 0, 0); | |
519 | symbol_table_insert (vif_data_end); | |
520 | fixups[fixup_count].exp.X_op = O_symbol; | |
521 | fixups[fixup_count].exp.X_add_symbol = vif_data_end; | |
522 | fixups[fixup_count].exp.X_add_number = 0; | |
523 | fixups[fixup_count].opindex = vif_operand_datalen_special; | |
524 | fixups[fixup_count].offset = 0; | |
525 | ||
526 | /* See what the user specified. */ | |
527 | vif_get_var_data (&file, &data_len); | |
528 | if (file) | |
529 | data_len = -1; | |
530 | fixups[fixup_count].user_value = data_len; | |
531 | /* Get the wl,cl values. Only useful for the unpack insn but | |
532 | it doesn't hurt to always record them. */ | |
533 | vif_get_wl_cl (&wl, &cl); | |
534 | fixups[fixup_count].wl = wl; | |
535 | fixups[fixup_count].cl = cl; | |
536 | ++fixup_count; | |
537 | } | |
538 | ||
539 | /* Obtain space in which to store the instruction. */ | |
540 | ||
83920d29 DE |
541 | if (opcode->flags & VIF_OPCODE_MPG) |
542 | { | |
8b901ef8 DE |
543 | /* The data must be aligned on a 64 bit boundary (so the mpg insn |
544 | comes just before that 64 bit boundary). | |
545 | Do this by putting the mpg insn in a relaxable fragment | |
546 | with a symbol that marks the beginning of the aligned data. */ | |
547 | ||
050ac694 DE |
548 | /* Ensure relaxable fragments are in their own fragment. |
549 | Otherwise there can be two fixups in one fragment | |
550 | (e.g. a dma tag followed by a `direct' insn) and md_apply_fix3 | |
551 | has trouble determining where to apply the fixup (because | |
552 | we set fr_opcode for the `direct' insn because it can move | |
553 | in the fragment). */ | |
554 | frag_wane (frag_now); | |
555 | frag_new (0); | |
556 | ||
8b901ef8 DE |
557 | /* This dance with frag_grow is to ensure the variable part and |
558 | fixed part are in the same fragment. */ | |
559 | frag_grow (8); | |
560 | /* Allocate space for the fixed part. */ | |
561 | f = frag_more (4); | |
562 | insn_frag = frag_now; | |
563 | ||
564 | frag_var (rs_machine_dependent, | |
565 | 4, /* max chars */ | |
566 | 0, /* variable part already allocated */ | |
567 | 1, /* subtype */ | |
568 | NULL, /* no symbol */ | |
569 | 0, /* offset */ | |
570 | f); /* opcode */ | |
571 | ||
83920d29 DE |
572 | frag_align (3, 0, 0); |
573 | record_alignment (now_seg, 3); | |
8b901ef8 DE |
574 | |
575 | /* Put a symbol at the start of data. The relaxation code uses | |
576 | this to figure out how many bytes to insert. $.mpgloc | |
577 | calculations also use it. */ | |
578 | vif_data_start = create_colon_label (STO_DVP_VU, LOCAL_LABEL_PREFIX, | |
579 | unique_name ("mpg")); | |
580 | insn_frag->fr_symbol = vif_data_start; | |
581 | ||
582 | /* Get the value of mpgloc. If it wasn't '*' update $.mpgloc. */ | |
583 | { | |
584 | int mpgloc = vif_get_mpgloc (); | |
585 | if (mpgloc != -1) | |
586 | { | |
587 | mpgloc_sym->sy_value.X_op = O_constant; | |
588 | mpgloc_sym->sy_value.X_add_number = mpgloc; | |
589 | mpgloc_sym->sy_value.X_unsigned = 1; | |
590 | } | |
591 | } | |
83920d29 DE |
592 | } |
593 | else if (opcode->flags & VIF_OPCODE_DIRECT) | |
594 | { | |
8b901ef8 DE |
595 | /* The data must be aligned on a 128 bit boundary (so the direct insn |
596 | comes just before that 128 bit boundary). | |
597 | Do this by putting the direct insn in a relaxable fragment. | |
598 | with a symbol that marks the beginning of the aligned data. */ | |
599 | ||
050ac694 DE |
600 | /* Ensure relaxable fragments are in their own fragment. |
601 | Otherwise there can be two fixups in one fragment | |
602 | (e.g. a dma tag followed by a `direct' insn) and md_apply_fix3 | |
603 | has trouble determining where to apply the fixup (because | |
604 | we set fr_opcode for the `direct' insn because it can move | |
605 | in the fragment). */ | |
606 | frag_wane (frag_now); | |
607 | frag_new (0); | |
608 | ||
8b901ef8 DE |
609 | /* This dance with frag_grow is to ensure the variable part and |
610 | fixed part are in the same fragment. */ | |
611 | frag_grow (16); | |
612 | /* Allocate space for the fixed part. */ | |
613 | f = frag_more (4); | |
614 | insn_frag = frag_now; | |
615 | ||
616 | frag_var (rs_machine_dependent, | |
617 | 12, /* max chars */ | |
618 | 0, /* variable part already allocated */ | |
619 | 2, /* subtype */ | |
620 | NULL, /* no symbol */ | |
621 | 0, /* offset */ | |
622 | f); /* opcode */ | |
623 | ||
83920d29 DE |
624 | frag_align (4, 0, 0); |
625 | record_alignment (now_seg, 4); | |
83920d29 | 626 | |
8b901ef8 DE |
627 | /* Put a symbol at the start of data. The relaxation code uses |
628 | this to figure out how many bytes to insert. */ | |
629 | vif_data_start = create_colon_label (0, LOCAL_LABEL_PREFIX, | |
630 | unique_name ("direct")); | |
631 | insn_frag->fr_symbol = vif_data_start; | |
632 | } | |
633 | else if (opcode->flags & VIF_OPCODE_UNPACK) | |
634 | { | |
635 | f = frag_more (len * 4); | |
636 | insn_frag = frag_now; | |
637 | /* Put a symbol at the start of data. $.unpackloc calculations | |
638 | use it. */ | |
639 | vif_data_start = create_colon_label (STO_DVP_VIF, LOCAL_LABEL_PREFIX, | |
640 | unique_name ("unpack")); | |
641 | ||
642 | /* Get the value of unpackloc. If it wasn't '*' update | |
643 | $.unpackloc. */ | |
644 | { | |
645 | int unpackloc = vif_get_unpackloc (); | |
646 | if (unpackloc != -1) | |
647 | { | |
648 | unpackloc_sym->sy_value.X_op = O_constant; | |
649 | unpackloc_sym->sy_value.X_add_number = unpackloc; | |
650 | unpackloc_sym->sy_value.X_unsigned = 1; | |
651 | } | |
652 | } | |
653 | } | |
654 | else | |
655 | { | |
656 | /* Reminder: it is important to fetch enough space in one call to | |
657 | `frag_more'. We use (f - frag_now->fr_literal) to compute where | |
658 | we are and we don't want frag_now to change between calls. */ | |
659 | f = frag_more (len * 4); | |
660 | insn_frag = frag_now; | |
661 | } | |
fe9efeb6 DE |
662 | |
663 | /* Write out the instruction. */ | |
664 | for (i = 0; i < len; ++i) | |
665 | md_number_to_chars (f + i * 4, insn_buf[i], 4); | |
666 | ||
667 | /* Create any fixups. */ | |
668 | /* FIXME: It might eventually be possible to combine all the various | |
669 | copies of this bit of code. */ | |
670 | for (i = 0; i < fixup_count; ++i) | |
671 | { | |
672 | int op_type, reloc_type, offset; | |
673 | const dvp_operand *operand; | |
8b901ef8 | 674 | fixS *fixP; |
f7306261 | 675 | |
fe9efeb6 DE |
676 | /* Create a fixup for this operand. |
677 | At this point we do not use a bfd_reloc_code_real_type for | |
678 | operands residing in the insn, but instead just use the | |
679 | operand index. This lets us easily handle fixups for any | |
680 | operand type, although that is admittedly not a very exciting | |
681 | feature. We pick a BFD reloc type in md_apply_fix. */ | |
682 | ||
683 | op_type = fixups[i].opindex; | |
684 | offset = fixups[i].offset; | |
b4cbabb8 DE |
685 | reloc_type = encode_fixup_reloc_type (DVP_VIF, op_type); |
686 | operand = &vif_operands[op_type]; | |
8b901ef8 DE |
687 | fixP = fix_new_exp (insn_frag, f + offset - insn_frag->fr_literal, 4, |
688 | &fixups[i].exp, | |
689 | (operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0, | |
690 | (bfd_reloc_code_real_type) reloc_type); | |
691 | fixP->tc_fix_data.user_value = fixups[i].user_value; | |
692 | fixP->tc_fix_data.wl = fixups[i].wl; | |
693 | fixP->tc_fix_data.cl = fixups[i].cl; | |
694 | ||
695 | /* Set fx_tcbit so other parts of the code know this fixup is for | |
696 | a vif insn. */ | |
697 | fixP->fx_tcbit = 1; | |
fe9efeb6 | 698 | } |
95bfad6d | 699 | } |
e033023f | 700 | |
e1b747c4 DE |
701 | /* Handle variable length insns. */ |
702 | ||
b4cbabb8 | 703 | if (opcode->flags & VIF_OPCODE_LENVAR) |
e033023f | 704 | { |
8b901ef8 | 705 | /* See what the user specified. */ |
b4cbabb8 | 706 | vif_get_var_data (&file, &data_len); |
ba4be194 | 707 | |
e033023f DE |
708 | if (file) |
709 | { | |
1fb06680 DE |
710 | int byte_len; |
711 | ||
ba4be194 DE |
712 | /* The handling for each of mpg,direct,unpack is basically the same: |
713 | - emit a label to set the mach type for the data we're inserting | |
714 | - switch to the new assembler state | |
715 | - insert the file | |
716 | - call the `end' handler */ | |
717 | ||
1fb06680 | 718 | if (opcode->flags & VIF_OPCODE_MPG) |
ba4be194 DE |
719 | { |
720 | record_mach (DVP_VUUP, 1); | |
721 | set_asm_state (ASM_MPG); | |
8b901ef8 DE |
722 | byte_len = insert_file (file, insert_mpg_marker, 0, 256 * 8); |
723 | s_endmpg (ENDMPG_INTERNAL); | |
ba4be194 | 724 | } |
1fb06680 | 725 | else if (opcode->flags & VIF_OPCODE_DIRECT) |
ba4be194 DE |
726 | { |
727 | record_mach (DVP_GIF, 1); | |
728 | set_asm_state (ASM_DIRECT); | |
8b901ef8 | 729 | byte_len = insert_file (file, NULL, 0, 0); |
ba4be194 DE |
730 | s_enddirect (1); |
731 | } | |
1fb06680 | 732 | else if (opcode->flags & VIF_OPCODE_UNPACK) |
1fb06680 | 733 | { |
8b901ef8 | 734 | int max_len = 0; /*unpack_max_byte_len (insn_buf[0]);*/ |
ba4be194 | 735 | set_asm_state (ASM_UNPACK); |
8b901ef8 DE |
736 | byte_len = insert_file (file, NULL /*insert_unpack_marker*/, |
737 | insn_buf[0], max_len); | |
ba4be194 | 738 | s_endunpack (1); |
1fb06680 | 739 | } |
ba4be194 | 740 | else |
8b901ef8 | 741 | as_fatal ("internal error: unknown cpu type for variable length vif insn"); |
e033023f | 742 | } |
8b901ef8 | 743 | else /* file == NULL */ |
e033023f DE |
744 | { |
745 | /* data_len == -1 means the value must be computed from | |
746 | the data. */ | |
8b901ef8 | 747 | if (data_len == 0 || data_len <= -2) |
e033023f | 748 | as_bad ("invalid data length"); |
ba4be194 | 749 | |
8b901ef8 DE |
750 | if (output_vif && data_len != -1) |
751 | install_vif_length (f, data_len); | |
752 | ||
b4cbabb8 | 753 | if (opcode->flags & VIF_OPCODE_MPG) |
83920d29 DE |
754 | { |
755 | set_asm_state (ASM_MPG); | |
756 | /* Enable automatic mpg insertion every 256 insns. */ | |
757 | vu_count = 0; | |
758 | } | |
b4cbabb8 | 759 | else if (opcode->flags & VIF_OPCODE_DIRECT) |
498fcb9c | 760 | set_asm_state (ASM_DIRECT); |
b4cbabb8 | 761 | else if (opcode->flags & VIF_OPCODE_UNPACK) |
498fcb9c | 762 | set_asm_state (ASM_UNPACK); |
e033023f DE |
763 | } |
764 | } | |
b5d20cf6 DE |
765 | } |
766 | ||
b4cbabb8 | 767 | /* Subroutine of md_assemble to assemble GIF instructions. */ |
b5d20cf6 DE |
768 | |
769 | static void | |
b4cbabb8 | 770 | assemble_gif (str) |
b5d20cf6 DE |
771 | char *str; |
772 | { | |
276dd6ef | 773 | DVP_INSN insn_buf[4]; |
3a6b8910 | 774 | const dvp_opcode *opcode; |
8151801a DE |
775 | char *f; |
776 | int i; | |
777 | ||
778 | insn_buf[0] = insn_buf[1] = insn_buf[2] = insn_buf[3] = 0; | |
f7306261 | 779 | |
b4cbabb8 DE |
780 | opcode = assemble_one_insn (DVP_GIF, |
781 | gif_opcode_lookup_asm (str), gif_operands, | |
3a6b8910 DE |
782 | &str, insn_buf); |
783 | if (opcode == NULL) | |
95bfad6d | 784 | return; |
8151801a DE |
785 | |
786 | /* Do an implicit alignment to a 16 byte boundary. */ | |
787 | frag_align (4, 0, 0); | |
788 | record_alignment (now_seg, 4); | |
789 | ||
9152beba DE |
790 | /* Insert a label so we can compute the number of quadwords when the |
791 | .endgif is seen. This is put before the mach type label because gif | |
792 | insns are followed by data and we don't want the disassembler to try | |
793 | to disassemble them as mips insns (since it uses the st_other field) | |
794 | of the closest label to choose the mach type and since we don't have | |
795 | a special st_other value for "data". */ | |
8b901ef8 DE |
796 | gif_data_name = S_GET_NAME (create_colon_label (0, LOCAL_LABEL_PREFIX, |
797 | unique_name ("gifdata"))); | |
9152beba | 798 | |
b6d331b9 | 799 | record_mach (DVP_GIF, 1); |
57d0c830 | 800 | |
fa3671a3 DE |
801 | gif_insn_frag_loc = f = frag_more (16); |
802 | gif_insn_frag = frag_now; | |
8151801a DE |
803 | for (i = 0; i < 4; ++i) |
804 | md_number_to_chars (f + i * 4, insn_buf[i], 4); | |
805 | ||
8151801a DE |
806 | /* Record the type of the gif tag so we know how to compute nloop |
807 | in s_endgif. */ | |
808 | if (strcmp (opcode->mnemonic, "gifpacked") == 0) | |
809 | gif_insn_type = GIF_PACKED; | |
810 | else if (strcmp (opcode->mnemonic, "gifreglist") == 0) | |
811 | gif_insn_type = GIF_REGLIST; | |
812 | else if (strcmp (opcode->mnemonic, "gifimage") == 0) | |
813 | gif_insn_type = GIF_IMAGE; | |
814 | else | |
815 | abort (); | |
498fcb9c | 816 | push_asm_state (ASM_GIF); |
b5d20cf6 DE |
817 | } |
818 | ||
819 | /* Subroutine of md_assemble to assemble VU instructions. */ | |
820 | ||
821 | static void | |
822 | assemble_vu (str) | |
823 | char *str; | |
209fb346 | 824 | { |
57d0c830 DE |
825 | char *f; |
826 | const dvp_opcode *opcode; | |
827 | ||
83920d29 DE |
828 | /* Handle automatic mpg insertion if enabled. */ |
829 | if (CUR_ASM_STATE == ASM_MPG | |
830 | && vu_count == 256) | |
8b901ef8 | 831 | insert_mpg_marker (0); |
83920d29 DE |
832 | |
833 | /* Do an implicit alignment to a 8 byte boundary. */ | |
834 | frag_align (3, 0, 0); | |
835 | record_alignment (now_seg, 3); | |
836 | ||
b6d331b9 | 837 | record_mach (DVP_VUUP, 0); |
57d0c830 | 838 | |
020ba60b DE |
839 | /* The lower instruction has the lower address. |
840 | Handle this by grabbing 8 bytes now, and then filling each word | |
841 | as appropriate. */ | |
57d0c830 | 842 | f = frag_more (8); |
020ba60b | 843 | |
61e09fac | 844 | #ifdef VERTICAL_BAR_SEPARATOR |
f6428b86 DE |
845 | char *p = strchr (str, '|'); |
846 | ||
847 | if (p == NULL) | |
848 | { | |
849 | as_bad ("lower slot missing in `%s'", str); | |
850 | return; | |
851 | } | |
852 | ||
853 | *p = 0; | |
3a6b8910 DE |
854 | opcode = assemble_vu_insn (DVP_VUUP, |
855 | vu_upper_opcode_lookup_asm (str), vu_operands, | |
856 | &str, f + 4); | |
f6428b86 | 857 | *p = '|'; |
3a6b8910 | 858 | str = p + 1; |
61e09fac | 859 | #else |
3a6b8910 DE |
860 | opcode = assemble_vu_insn (DVP_VUUP, |
861 | vu_upper_opcode_lookup_asm (str), vu_operands, | |
862 | &str, f + 4); | |
61e09fac | 863 | #endif |
19f12fb4 DE |
864 | |
865 | /* Don't assemble next one if we couldn't assemble the first. */ | |
866 | if (opcode == NULL) | |
867 | return; | |
868 | opcode = assemble_vu_insn (DVP_VULO, | |
869 | vu_lower_opcode_lookup_asm (str), vu_operands, | |
870 | &str, f); | |
871 | /* If this was the "loi" pseudo-insn, we need to set the `i' bit. */ | |
18daef64 DE |
872 | if (opcode != NULL) |
873 | { | |
874 | if (strcmp (opcode->mnemonic, "loi") == 0) | |
875 | f[7] |= 0x80; | |
876 | } | |
83920d29 DE |
877 | |
878 | /* Increment the vu insn counter. | |
879 | If get reach 256 we need to insert an `mpg'. */ | |
880 | ++vu_count; | |
f6428b86 DE |
881 | } |
882 | ||
3a6b8910 DE |
883 | static const dvp_opcode * |
884 | assemble_vu_insn (cpu, opcode, operand_table, pstr, buf) | |
885 | dvp_cpu cpu; | |
276dd6ef DE |
886 | const dvp_opcode *opcode; |
887 | const dvp_operand *operand_table; | |
3a6b8910 | 888 | char **pstr; |
020ba60b | 889 | char *buf; |
f6428b86 | 890 | { |
f7306261 | 891 | int i; |
276dd6ef | 892 | DVP_INSN insn; |
f6428b86 | 893 | |
3a6b8910 DE |
894 | opcode = assemble_one_insn (cpu, opcode, operand_table, pstr, &insn); |
895 | if (opcode == NULL) | |
f7306261 | 896 | return NULL; |
f6428b86 | 897 | |
f7306261 DE |
898 | /* Write out the instruction. |
899 | Reminder: it is important to fetch enough space in one call to | |
900 | `frag_more'. We use (f - frag_now->fr_literal) to compute where | |
901 | we are and we don't want frag_now to change between calls. */ | |
902 | md_number_to_chars (buf, insn, 4); | |
f6428b86 | 903 | |
f7306261 DE |
904 | /* Create any fixups. */ |
905 | for (i = 0; i < fixup_count; ++i) | |
906 | { | |
907 | int op_type, reloc_type; | |
276dd6ef | 908 | const dvp_operand *operand; |
f7306261 DE |
909 | |
910 | /* Create a fixup for this operand. | |
911 | At this point we do not use a bfd_reloc_code_real_type for | |
912 | operands residing in the insn, but instead just use the | |
913 | operand index. This lets us easily handle fixups for any | |
914 | operand type, although that is admittedly not a very exciting | |
915 | feature. We pick a BFD reloc type in md_apply_fix. */ | |
916 | ||
917 | op_type = fixups[i].opindex; | |
95bfad6d | 918 | reloc_type = encode_fixup_reloc_type (cpu, op_type); |
276dd6ef | 919 | operand = &vu_operands[op_type]; |
f7306261 DE |
920 | fix_new_exp (frag_now, buf - frag_now->fr_literal, 4, |
921 | &fixups[i].exp, | |
276dd6ef | 922 | (operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0, |
f7306261 DE |
923 | (bfd_reloc_code_real_type) reloc_type); |
924 | } | |
925 | ||
926 | /* All done. */ | |
3a6b8910 | 927 | return opcode; |
f7306261 DE |
928 | } |
929 | ||
3a6b8910 | 930 | /* Assemble one instruction at *PSTR. |
f7306261 DE |
931 | CPU indicates what component we're assembling for. |
932 | The assembled instruction is stored in INSN_BUF. | |
3a6b8910 | 933 | OPCODE is a pointer to the head of the hash chain. |
f7306261 | 934 | |
3a6b8910 | 935 | *PSTR is updated to point passed the parsed instruction. |
f7306261 | 936 | |
3a6b8910 DE |
937 | If the insn is successfully parsed the result is a pointer to the opcode |
938 | entry that successfully matched and *PSTR is updated to point passed | |
939 | the parsed insn. If an error occurs the result is NULL and *PSTR is left | |
940 | at some random point in the string (??? may wish to leave it pointing where | |
941 | the error occured). */ | |
942 | ||
943 | static const dvp_opcode * | |
944 | assemble_one_insn (cpu, opcode, operand_table, pstr, insn_buf) | |
945 | dvp_cpu cpu; | |
276dd6ef DE |
946 | const dvp_opcode *opcode; |
947 | const dvp_operand *operand_table; | |
3a6b8910 | 948 | char **pstr; |
276dd6ef | 949 | DVP_INSN *insn_buf; |
f7306261 | 950 | { |
3a6b8910 | 951 | char *start, *str; |
f6428b86 DE |
952 | |
953 | /* Keep looking until we find a match. */ | |
954 | ||
3a6b8910 | 955 | start = str = *pstr; |
276dd6ef | 956 | for ( ; opcode != NULL; opcode = DVP_OPCODE_NEXT_ASM (opcode)) |
f6428b86 | 957 | { |
91572941 | 958 | int past_opcode_p, num_suffixes; |
f6428b86 | 959 | const unsigned char *syn; |
f6428b86 DE |
960 | |
961 | /* Ensure the mnemonic part matches. */ | |
962 | for (str = start, syn = opcode->mnemonic; *syn != '\0'; ++str, ++syn) | |
963 | if (tolower (*str) != tolower (*syn)) | |
964 | break; | |
965 | if (*syn != '\0') | |
966 | continue; | |
f6428b86 DE |
967 | |
968 | /* Scan the syntax string. If it doesn't match, try the next one. */ | |
969 | ||
276dd6ef | 970 | dvp_opcode_init_parse (); |
91572941 | 971 | insn_buf[opcode->opcode_word] = opcode->value; |
f7306261 | 972 | fixup_count = 0; |
f6428b86 DE |
973 | past_opcode_p = 0; |
974 | num_suffixes = 0; | |
f6428b86 DE |
975 | |
976 | /* We don't check for (*str != '\0') here because we want to parse | |
977 | any trailing fake arguments in the syntax string. */ | |
978 | for (/*str = start, */ syn = opcode->syntax; *syn != '\0'; ) | |
979 | { | |
980 | int mods,index; | |
276dd6ef | 981 | const dvp_operand *operand; |
f6428b86 | 982 | const char *errmsg; |
8b901ef8 | 983 | long value; |
f6428b86 | 984 | |
fbe2ad46 DE |
985 | /* Non operand chars must match exactly. |
986 | Operand chars that are letters are not part of symbols | |
987 | and are case insensitive. */ | |
f6428b86 DE |
988 | if (*syn < 128) |
989 | { | |
fbe2ad46 | 990 | if (tolower (*str) == tolower (*syn)) |
f6428b86 DE |
991 | { |
992 | if (*syn == ' ') | |
993 | past_opcode_p = 1; | |
994 | ++syn; | |
995 | ++str; | |
996 | } | |
997 | else | |
998 | break; | |
999 | continue; | |
1000 | } | |
1001 | ||
1002 | /* We have a suffix or an operand. Pick out any modifiers. */ | |
1003 | mods = 0; | |
276dd6ef DE |
1004 | index = DVP_OPERAND_INDEX (*syn); |
1005 | while (DVP_MOD_P (operand_table[index].flags)) | |
f6428b86 | 1006 | { |
276dd6ef | 1007 | mods |= operand_table[index].flags & DVP_MOD_BITS; |
f6428b86 | 1008 | ++syn; |
276dd6ef | 1009 | index = DVP_OPERAND_INDEX (*syn); |
f6428b86 | 1010 | } |
f7306261 | 1011 | operand = operand_table + index; |
f6428b86 | 1012 | |
276dd6ef | 1013 | if (operand->flags & DVP_OPERAND_FAKE) |
f6428b86 | 1014 | { |
a48a6f23 | 1015 | long value = 0; |
91572941 DE |
1016 | |
1017 | if (operand->flags & DVP_OPERAND_DMA_INLINE) | |
1018 | { | |
8151801a | 1019 | inline_dma_data ((mods & DVP_OPERAND_AUTOCOUNT) != 0, |
91572941 DE |
1020 | insn_buf); |
1021 | ++syn; | |
1022 | continue; | |
1023 | } | |
1024 | ||
a48a6f23 DE |
1025 | if (operand->parse) |
1026 | { | |
1027 | errmsg = NULL; | |
1028 | value = (*operand->parse) (opcode, operand, mods, | |
1029 | &str, &errmsg); | |
1030 | if (errmsg) | |
1031 | break; | |
1032 | } | |
f6428b86 DE |
1033 | if (operand->insert) |
1034 | { | |
f62a42d0 | 1035 | errmsg = NULL; |
a48a6f23 DE |
1036 | (*operand->insert) (opcode, operand, mods, insn_buf, |
1037 | (offsetT) value, &errmsg); | |
f6428b86 DE |
1038 | /* If we get an error, go on to try the next insn. */ |
1039 | if (errmsg) | |
1040 | break; | |
1041 | } | |
1042 | ++syn; | |
8b901ef8 | 1043 | continue; |
f6428b86 | 1044 | } |
8b901ef8 | 1045 | |
f6428b86 | 1046 | /* Are we finished with suffixes? */ |
8b901ef8 | 1047 | if (!past_opcode_p) |
f6428b86 | 1048 | { |
f6428b86 DE |
1049 | long suf_value; |
1050 | ||
276dd6ef | 1051 | if (!(operand->flags & DVP_OPERAND_SUFFIX)) |
8b901ef8 | 1052 | as_fatal ("internal error: bad opcode table, missing suffix flag"); |
f6428b86 DE |
1053 | |
1054 | /* If we're at a space in the input string, we want to skip the | |
1055 | remaining suffixes. There may be some fake ones though, so | |
1056 | just go on to try the next one. */ | |
1057 | if (*str == ' ') | |
1058 | { | |
1059 | ++syn; | |
1060 | continue; | |
1061 | } | |
1062 | ||
19f12fb4 | 1063 | /* Parse the suffix. */ |
f7306261 | 1064 | errmsg = NULL; |
19f12fb4 | 1065 | suf_value = (*operand->parse) (opcode, operand, mods, &str, |
95bfad6d | 1066 | &errmsg); |
f6428b86 DE |
1067 | if (errmsg) |
1068 | { | |
fbe2ad46 DE |
1069 | /* This can happen, for example, in ARC's in "blle foo" and |
1070 | we're currently using the template "b%q%.n %j". The "bl" | |
1071 | insn occurs later in the table so "lle" isn't an illegal | |
1072 | suffix. */ | |
f6428b86 DE |
1073 | break; |
1074 | } | |
6856244d | 1075 | |
f6428b86 | 1076 | /* Insert the suffix's value into the insn. */ |
6856244d DE |
1077 | insert_operand (cpu, opcode, operand, mods, insn_buf, |
1078 | (offsetT) suf_value, &errmsg); | |
f6428b86 | 1079 | |
f6428b86 | 1080 | ++syn; |
8b901ef8 | 1081 | continue; |
f6428b86 | 1082 | } |
f6428b86 | 1083 | |
8b901ef8 DE |
1084 | /* This is an operand, either a register or an expression of |
1085 | some kind. */ | |
1086 | ||
1087 | value = 0; | |
1088 | ||
1089 | if (operand->flags & DVP_OPERAND_SUFFIX) | |
1090 | as_fatal ("internal error: bad opcode table, suffix wrong"); | |
f6428b86 | 1091 | |
8b901ef8 DE |
1092 | /* Is there anything left to parse? |
1093 | We don't check for this at the top because we want to parse | |
1094 | any trailing fake arguments in the syntax string. */ | |
1095 | /* ??? This doesn't allow operands with a legal value of "". */ | |
1096 | if (*str == '\0') | |
1097 | break; | |
1098 | ||
1099 | /* Parse the operand. */ | |
1100 | if (operand->flags & DVP_OPERAND_FLOAT) | |
1101 | { | |
1102 | errmsg = 0; | |
1103 | value = parse_float (&str, &errmsg); | |
1104 | if (errmsg) | |
f6428b86 | 1105 | break; |
8b901ef8 DE |
1106 | } |
1107 | else if ((operand->flags & DVP_OPERAND_DMA_ADDR) | |
1108 | && (mods & DVP_OPERAND_AUTOCOUNT)) | |
1109 | { | |
1110 | errmsg = 0; | |
1111 | value = parse_dma_addr_autocount (opcode, operand, mods, | |
1112 | insn_buf, &str, &errmsg); | |
1113 | if (errmsg) | |
1114 | break; | |
1115 | } | |
1116 | else | |
1117 | { | |
1118 | char *origstr,*hold; | |
1119 | expressionS exp; | |
f6428b86 | 1120 | |
8b901ef8 DE |
1121 | /* First see if there is a special parser. */ |
1122 | origstr = str; | |
1123 | if (operand->parse) | |
f6428b86 | 1124 | { |
f7306261 | 1125 | errmsg = NULL; |
95bfad6d DE |
1126 | value = (*operand->parse) (opcode, operand, mods, |
1127 | &str, &errmsg); | |
71af45ec DE |
1128 | if (errmsg) |
1129 | break; | |
f6428b86 | 1130 | } |
8b901ef8 DE |
1131 | |
1132 | /* If there wasn't a special parser, or there was and it | |
1133 | left the input stream unchanged, use the general | |
1134 | expression parser. */ | |
1135 | if (str == origstr) | |
f6428b86 DE |
1136 | { |
1137 | hold = input_line_pointer; | |
1138 | input_line_pointer = str; | |
8b901ef8 DE |
1139 | /* Set cur_{opcode,operand} for md_operand. */ |
1140 | cur_opcode = opcode; | |
1141 | cur_operand = operand; | |
f6428b86 | 1142 | expression (&exp); |
8b901ef8 | 1143 | cur_opcode = NULL; |
f6428b86 DE |
1144 | str = input_line_pointer; |
1145 | input_line_pointer = hold; | |
1146 | ||
71af45ec DE |
1147 | if (exp.X_op == O_illegal |
1148 | || exp.X_op == O_absent) | |
1149 | break; | |
f6428b86 | 1150 | else if (exp.X_op == O_constant) |
71af45ec | 1151 | value = exp.X_add_number; |
f6428b86 | 1152 | else if (exp.X_op == O_register) |
8b901ef8 | 1153 | as_fatal ("internal error: got O_register"); |
f6428b86 DE |
1154 | else |
1155 | { | |
1156 | /* We need to generate a fixup for this expression. */ | |
f7306261 | 1157 | if (fixup_count >= MAX_FIXUPS) |
8b901ef8 | 1158 | as_fatal ("internal error: too many fixups"); |
f7306261 DE |
1159 | fixups[fixup_count].exp = exp; |
1160 | fixups[fixup_count].opindex = index; | |
1ece1d56 DE |
1161 | /* FIXME: Revisit. Do we really need operand->word? |
1162 | The endianness of a 128 bit DMAtag is rather | |
1163 | twisted. How about defining word 0 as the word with | |
1164 | the lowest address and basing operand-shift off that. | |
1165 | operand->word could then be deleted. */ | |
1166 | if (operand->word != 0) | |
1167 | fixups[fixup_count].offset = operand->word * 4; | |
1168 | else | |
1169 | fixups[fixup_count].offset = (operand->shift / 32) * 4; | |
f7306261 | 1170 | ++fixup_count; |
f6428b86 DE |
1171 | value = 0; |
1172 | } | |
1173 | } | |
8b901ef8 | 1174 | } |
f6428b86 | 1175 | |
8b901ef8 DE |
1176 | /* Insert the register or expression into the instruction. */ |
1177 | errmsg = NULL; | |
1178 | insert_operand (cpu, opcode, operand, mods, insn_buf, | |
1179 | (offsetT) value, &errmsg); | |
1180 | if (errmsg != (const char *) NULL) | |
1181 | break; | |
f6428b86 | 1182 | |
8b901ef8 | 1183 | ++syn; |
f6428b86 DE |
1184 | } |
1185 | ||
1186 | /* If we're at the end of the syntax string, we're done. */ | |
f6428b86 DE |
1187 | if (*syn == '\0') |
1188 | { | |
1189 | int i; | |
f6428b86 DE |
1190 | |
1191 | /* For the moment we assume a valid `str' can only contain blanks | |
1192 | now. IE: We needn't try again with a longer version of the | |
1193 | insn and it is assumed that longer versions of insns appear | |
1194 | before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */ | |
1195 | ||
1196 | while (isspace (*str)) | |
1197 | ++str; | |
1198 | ||
61e09fac DE |
1199 | if (*str != '\0' |
1200 | #ifndef VERTICAL_BAR_SEPARATOR | |
3a6b8910 | 1201 | && cpu != DVP_VUUP |
61e09fac DE |
1202 | #endif |
1203 | ) | |
f6428b86 DE |
1204 | as_bad ("junk at end of line: `%s'", str); |
1205 | ||
f7306261 DE |
1206 | /* It's now up to the caller to emit the instruction and any |
1207 | relocations. */ | |
3a6b8910 DE |
1208 | *pstr = str; |
1209 | return opcode; | |
f6428b86 DE |
1210 | } |
1211 | ||
1212 | /* Try the next entry. */ | |
1213 | } | |
1214 | ||
1215 | as_bad ("bad instruction `%s'", start); | |
61e09fac | 1216 | return 0; |
209fb346 | 1217 | } |
498fcb9c | 1218 | \f |
b6d331b9 | 1219 | /* Given a dvp cpu type, return it's STO_DVP value. |
7f28a81d | 1220 | The label prefix to use is stored in *PNAME. */ |
b6d331b9 DE |
1221 | |
1222 | static int | |
1223 | cpu_sto (cpu, pname) | |
1224 | dvp_cpu cpu; | |
1225 | const char **pname; | |
1226 | { | |
1227 | switch (cpu) | |
1228 | { | |
1229 | case DVP_DMA : *pname = ".dma."; return STO_DVP_DMA; | |
1230 | case DVP_VIF : *pname = ".vif."; return STO_DVP_VIF; | |
1231 | case DVP_GIF : *pname = ".gif."; return STO_DVP_GIF; | |
1232 | case DVP_VUUP : *pname = ".vu."; return STO_DVP_VU; | |
1233 | } | |
1234 | abort (); | |
1235 | } | |
1236 | ||
1237 | /* Record the current mach type in the object file. | |
1238 | If FORCE_NEXT_P is non-zero, force a label to be emitted the next time | |
1239 | we're called. This is useful for variable length instructions that can | |
1240 | have labels embedded within them. */ | |
57d0c830 DE |
1241 | |
1242 | static void | |
b6d331b9 DE |
1243 | record_mach (cpu, force_next_p) |
1244 | dvp_cpu cpu; | |
1245 | int force_next_p; | |
57d0c830 DE |
1246 | { |
1247 | symbolS *label; | |
b6d331b9 DE |
1248 | const char *name; |
1249 | int sto; | |
57d0c830 | 1250 | |
b6d331b9 | 1251 | if (cpu == cur_cpu) |
57d0c830 DE |
1252 | return; |
1253 | ||
b6d331b9 DE |
1254 | sto = cpu_sto (cpu, &name); |
1255 | ||
1256 | label = create_colon_label (sto, "", unique_name (name)); | |
57d0c830 | 1257 | |
b6d331b9 DE |
1258 | if (force_next_p) |
1259 | cur_cpu = DVP_UNKNOWN; | |
1260 | else | |
1261 | cur_cpu = cpu; | |
57d0c830 DE |
1262 | } |
1263 | ||
3b4389e2 DE |
1264 | /* Force emission of mach type label at next insn. |
1265 | This isn't static as TC_START_LABEL uses it. | |
1266 | The result is the value of TC_START_LABEL. */ | |
1267 | ||
1268 | int | |
1269 | force_mach_label () | |
1270 | { | |
1271 | cur_cpu = DVP_UNKNOWN; | |
1272 | return 1; | |
1273 | } | |
1274 | ||
498fcb9c DE |
1275 | /* Push/pop the current parsing state. */ |
1276 | ||
1277 | static void | |
1278 | push_asm_state (new_state) | |
1279 | asm_state new_state; | |
1280 | { | |
1281 | ++cur_state_index; | |
1282 | if (cur_state_index == MAX_STATE_DEPTH) | |
8b901ef8 | 1283 | as_fatal ("internal error: unexpected state push"); |
498fcb9c DE |
1284 | asm_state_stack[cur_state_index] = new_state; |
1285 | } | |
1286 | ||
1287 | /* TOP_OK_P is non-zero if it's ok that we're at the top of the stack. | |
1288 | This happens if there are errors in the assembler code. | |
1289 | We just reset the stack to its "init" state. */ | |
209fb346 | 1290 | |
498fcb9c DE |
1291 | static void |
1292 | pop_asm_state (top_ok_p) | |
1293 | int top_ok_p; | |
1294 | { | |
1295 | if (cur_state_index == 0) | |
1296 | { | |
1297 | if (top_ok_p) | |
1298 | asm_state_stack[cur_state_index] = ASM_INIT; | |
1299 | else | |
8b901ef8 | 1300 | as_fatal ("internal error: unexpected state pop"); |
498fcb9c DE |
1301 | } |
1302 | else | |
1303 | --cur_state_index; | |
1304 | } | |
1305 | ||
1306 | static void | |
1307 | set_asm_state (state) | |
1308 | asm_state state; | |
1309 | { | |
1310 | CUR_ASM_STATE = state; | |
1311 | } | |
1312 | \f | |
209fb346 DE |
1313 | void |
1314 | md_operand (expressionP) | |
1315 | expressionS *expressionP; | |
1316 | { | |
8b901ef8 DE |
1317 | /* Check if this is a '*' for mpgloc. */ |
1318 | if (cur_opcode | |
1319 | && (cur_opcode->flags & VIF_OPCODE_MPG) != 0 | |
1320 | && (cur_operand->flags & DVP_OPERAND_VU_ADDRESS) != 0 | |
1321 | && *input_line_pointer == '*') | |
1322 | { | |
1323 | expressionP->X_op = O_symbol; | |
1324 | expressionP->X_add_symbol = mpgloc_sym; | |
1325 | expressionP->X_add_number = 0; | |
1326 | ||
1327 | /* Advance over the '*'. */ | |
1328 | ++input_line_pointer; | |
1329 | } | |
1330 | /* Check if this is a '*' for mpgloc. */ | |
1331 | else if (cur_opcode | |
1332 | && (cur_opcode->flags & VIF_OPCODE_UNPACK) != 0 | |
1333 | && (cur_operand->flags & DVP_OPERAND_UNPACK_ADDRESS) != 0 | |
1334 | && *input_line_pointer == '*') | |
1335 | { | |
1336 | expressionP->X_op = O_symbol; | |
1337 | expressionP->X_add_symbol = unpackloc_sym; | |
1338 | expressionP->X_add_number = 0; | |
1339 | ||
1340 | /* Advance over the '*'. */ | |
1341 | ++input_line_pointer; | |
1342 | } | |
209fb346 DE |
1343 | } |
1344 | ||
1345 | valueT | |
1346 | md_section_align (segment, size) | |
1347 | segT segment; | |
1348 | valueT size; | |
1349 | { | |
1350 | int align = bfd_get_section_alignment (stdoutput, segment); | |
1351 | return ((size + (1 << align) - 1) & (-1 << align)); | |
1352 | } | |
1353 | ||
1354 | symbolS * | |
1355 | md_undefined_symbol (name) | |
1356 | char *name; | |
1357 | { | |
1358 | return 0; | |
1359 | } | |
e1b747c4 DE |
1360 | |
1361 | /* Called after parsing the file via md_after_pass_hook. */ | |
1362 | ||
1363 | void | |
69312dac | 1364 | dvp_after_pass_hook () |
e1b747c4 | 1365 | { |
7f28a81d DE |
1366 | /* If doing dma packing, ensure the last dma tag is filled out. */ |
1367 | if (dma_pack_vif_p) | |
1368 | { | |
1369 | /* Nothing to do as vifnops are zero and frag_align at beginning | |
1370 | of dmatag is all we need. */ | |
1371 | } | |
1372 | ||
d3c6610c DE |
1373 | #if 0 /* ??? Doesn't work unless we keep track of the nested include file |
1374 | level. */ | |
e1b747c4 | 1375 | /* Check for missing .EndMpg, and supply one if necessary. */ |
498fcb9c | 1376 | if (CUR_ASM_STATE == ASM_MPG) |
8b901ef8 | 1377 | s_endmpg (ENDMPG_INTERNAL); |
498fcb9c | 1378 | else if (CUR_ASM_STATE == ASM_DIRECT) |
e1b747c4 | 1379 | s_enddirect (0); |
498fcb9c | 1380 | else if (CUR_ASM_STATE == ASM_UNPACK) |
e1b747c4 | 1381 | s_endunpack (0); |
d3c6610c | 1382 | #endif |
e1b747c4 | 1383 | } |
69312dac DE |
1384 | |
1385 | /* Called when a label is defined via tc_frob_label. */ | |
1386 | ||
1387 | void | |
1388 | dvp_frob_label (sym) | |
1389 | symbolS *sym; | |
1390 | { | |
b6d331b9 DE |
1391 | /* All labels in vu code must be specially marked for the disassembler. |
1392 | The disassembler ignores all previous information at each new label | |
1393 | (that has a higher address than the last one). */ | |
69312dac DE |
1394 | if (CUR_ASM_STATE == ASM_MPG |
1395 | || CUR_ASM_STATE == ASM_VU) | |
1396 | S_SET_OTHER (sym, STO_DVP_VU); | |
1397 | } | |
209fb346 | 1398 | \f |
8b901ef8 DE |
1399 | /* mpg/direct alignment is handled via relaxation */ |
1400 | ||
1401 | /* Return an initial guess of the length by which a fragment must grow to | |
1402 | hold a branch to reach its destination. | |
1403 | Also updates fr_type/fr_subtype as necessary. | |
1404 | ||
1405 | Called just before doing relaxation. | |
1406 | Any symbol that is now undefined will not become defined. | |
1407 | The guess for fr_var is ACTUALLY the growth beyond fr_fix. | |
1408 | Whatever we do to grow fr_fix or fr_var contributes to our returned value. | |
1409 | Although it may not be explicit in the frag, pretend fr_var starts with a | |
1410 | 0 value. */ | |
1411 | ||
1412 | int | |
1413 | md_estimate_size_before_relax (fragP, segment) | |
1414 | fragS * fragP; | |
1415 | segT segment; | |
1416 | { | |
1417 | /* Our initial estimate is always 0. */ | |
1418 | return 0; | |
1419 | } | |
1420 | ||
1421 | /* Perform the relaxation. | |
1422 | All we have to do is figure out how many bytes we need to insert to | |
1423 | get to the recorded symbol (which is at the required alignment). */ | |
1424 | ||
1425 | long | |
1426 | dvp_relax_frag (fragP, stretch) | |
1427 | fragS * fragP; | |
1428 | long stretch; | |
1429 | { | |
1430 | /* Address of variable part. */ | |
1431 | long address = fragP->fr_address + fragP->fr_fix; | |
1432 | /* Symbol marking start of data. */ | |
1433 | symbolS * symbolP = fragP->fr_symbol; | |
1434 | /* Address of the symbol. */ | |
1435 | long target = S_GET_VALUE (symbolP) + symbolP->sy_frag->fr_address; | |
1436 | long growth; | |
1437 | ||
1438 | /* subtype >= 10 means "done" */ | |
1439 | if (fragP->fr_subtype >= 10) | |
1440 | return 0; | |
1441 | ||
1442 | /* subtype 1 = mpg */ | |
1443 | if (fragP->fr_subtype == 1) | |
1444 | { | |
1445 | growth = target - address; | |
1446 | if (growth < 0) | |
1447 | as_fatal ("internal error: bad mpg alignment handling"); | |
1448 | fragP->fr_subtype = 10 + growth; | |
1449 | return growth; | |
1450 | } | |
1451 | ||
1452 | /* subtype 2 = direct */ | |
1453 | if (fragP->fr_subtype == 2) | |
1454 | { | |
1455 | growth = target - address; | |
1456 | if (growth < 0) | |
1457 | as_fatal ("internal error: bad direct alignment handling"); | |
1458 | fragP->fr_subtype = 10 + growth; | |
1459 | return growth; | |
1460 | } | |
1461 | ||
1462 | as_fatal ("internal error: unknown fr_subtype"); | |
1463 | } | |
1464 | ||
1465 | /* *fragP has been relaxed to its final size, and now needs to have | |
1466 | the bytes inside it modified to conform to the new size. | |
1467 | ||
1468 | Called after relaxation is finished. | |
1469 | fragP->fr_type == rs_machine_dependent. | |
1470 | fragP->fr_subtype is the subtype of what the address relaxed to. */ | |
1471 | ||
1472 | void | |
1473 | md_convert_frag (abfd, sec, fragP) | |
1474 | bfd * abfd; | |
1475 | segT sec; | |
1476 | fragS * fragP; | |
1477 | { | |
1478 | int growth = fragP->fr_subtype - 10; | |
1479 | ||
1480 | fragP->fr_fix += growth; | |
1481 | ||
1482 | if (growth != 0) | |
1483 | { | |
1484 | /* We had to grow this fragment. Shift the mpg/direct insn to the end | |
1485 | (so it abuts the following data). */ | |
1486 | DVP_INSN insn = bfd_getl32 (fragP->fr_opcode); | |
1487 | md_number_to_chars (fragP->fr_opcode, VIFNOP, 4); | |
1488 | if (growth > 8) | |
1489 | md_number_to_chars (fragP->fr_opcode, VIFNOP, 8); | |
1490 | md_number_to_chars (fragP->fr_literal + fragP->fr_fix - 4, insn, 4); | |
1491 | ||
1492 | /* Adjust fr_opcode so md_apply_fix3 works with the right bytes. */ | |
1493 | fragP->fr_opcode += growth; | |
1494 | } | |
1495 | } | |
1496 | \f | |
209fb346 DE |
1497 | /* Functions concerning relocs. */ |
1498 | ||
95bfad6d | 1499 | /* Spacing between each cpu type's operand numbers. |
7f28a81d | 1500 | Should be at least as big as any operand table. */ |
95bfad6d DE |
1501 | #define RELOC_SPACING 256 |
1502 | ||
1503 | /* Given a cpu type and operand number, return a temporary reloc type | |
1504 | for use in generating the fixup that encodes the cpu type and operand | |
1505 | number. */ | |
1506 | ||
1507 | static int | |
1508 | encode_fixup_reloc_type (cpu, opnum) | |
3a6b8910 | 1509 | dvp_cpu cpu; |
95bfad6d DE |
1510 | int opnum; |
1511 | { | |
1512 | return (int) BFD_RELOC_UNUSED + ((int) cpu * RELOC_SPACING) + opnum; | |
1513 | } | |
1514 | ||
1515 | /* Given a fixup reloc type, decode it into cpu type and operand. */ | |
1516 | ||
1517 | static void | |
1518 | decode_fixup_reloc_type (fixup_reloc, cpuP, operandP) | |
1519 | int fixup_reloc; | |
3a6b8910 | 1520 | dvp_cpu *cpuP; |
276dd6ef | 1521 | const dvp_operand **operandP; |
95bfad6d | 1522 | { |
3a6b8910 | 1523 | dvp_cpu cpu = (fixup_reloc - (int) BFD_RELOC_UNUSED) / RELOC_SPACING; |
95bfad6d DE |
1524 | int opnum = (fixup_reloc - (int) BFD_RELOC_UNUSED) % RELOC_SPACING; |
1525 | ||
1526 | *cpuP = cpu; | |
1527 | switch (cpu) | |
1528 | { | |
3a6b8910 DE |
1529 | case DVP_VUUP : *operandP = &vu_operands[opnum]; break; |
1530 | case DVP_VULO : *operandP = &vu_operands[opnum]; break; | |
1531 | case DVP_DMA : *operandP = &dma_operands[opnum]; break; | |
b4cbabb8 DE |
1532 | case DVP_VIF : *operandP = &vif_operands[opnum]; break; |
1533 | case DVP_GIF : *operandP = &gif_operands[opnum]; break; | |
8b901ef8 | 1534 | default : as_fatal ("internal error: bad fixup encoding"); |
95bfad6d DE |
1535 | } |
1536 | } | |
1537 | ||
1538 | /* Given a fixup reloc type, return a pointer to the operand | |
1539 | ||
209fb346 DE |
1540 | /* The location from which a PC relative jump should be calculated, |
1541 | given a PC relative reloc. */ | |
1542 | ||
1543 | long | |
1544 | md_pcrel_from_section (fixP, sec) | |
1545 | fixS *fixP; | |
1546 | segT sec; | |
1547 | { | |
1548 | if (fixP->fx_addsy != (symbolS *) NULL | |
1549 | && (! S_IS_DEFINED (fixP->fx_addsy) | |
1550 | || S_GET_SEGMENT (fixP->fx_addsy) != sec)) | |
1551 | { | |
8b901ef8 DE |
1552 | /* If fx_tcbit is set this is for a vif insn and thus should never |
1553 | happen in correct code. */ | |
1554 | /* ??? The error message could be a bit more descriptive. */ | |
1555 | if (fixP->fx_tcbit) | |
1556 | as_bad ("unable to compute length of vif insn"); | |
209fb346 | 1557 | /* The symbol is undefined (or is defined but not in this section). |
b6675c1a DE |
1558 | Let the linker figure it out. +8: branch offsets are relative to the |
1559 | delay slot. */ | |
1560 | return 8; | |
209fb346 DE |
1561 | } |
1562 | ||
8b901ef8 DE |
1563 | /* If fx_tcbit is set, this is a vif end-of-variable-length-insn marker. |
1564 | In this case the offset is relative to the start of data. | |
1565 | Otherwise we assume this is a vu branch. In this case | |
1566 | offsets are calculated based on the address of the next insn. */ | |
1567 | if (fixP->fx_tcbit) | |
1568 | { | |
1569 | /* As a further refinement, if fr_opcode is NULL this is `unpack' | |
1570 | which doesn't involve any relaxing. */ | |
1571 | if (fixP->fx_frag->fr_opcode == NULL) | |
1572 | return fixP->fx_frag->fr_address + fixP->fx_where + 4; | |
1573 | else | |
1574 | return fixP->fx_frag->fr_address + fixP->fx_frag->fr_fix; | |
1575 | } | |
1576 | else | |
1577 | return ((fixP->fx_frag->fr_address + fixP->fx_where) & -8L) + 8; | |
209fb346 DE |
1578 | } |
1579 | ||
1580 | /* Apply a fixup to the object code. This is called for all the | |
1581 | fixups we generated by calls to fix_new_exp. At this point all symbol | |
1582 | values should be fully resolved, and we attempt to completely resolve the | |
1583 | reloc. If we can not do that, we determine the correct reloc code and put | |
1584 | it back in the fixup. */ | |
1585 | ||
1586 | int | |
1587 | md_apply_fix3 (fixP, valueP, seg) | |
1588 | fixS *fixP; | |
1589 | valueT *valueP; | |
1590 | segT seg; | |
1591 | { | |
1592 | char *where = fixP->fx_frag->fr_literal + fixP->fx_where; | |
1593 | valueT value; | |
1594 | ||
020ba60b DE |
1595 | /* FIXME FIXME FIXME: The value we are passed in *valueP includes |
1596 | the symbol values. Since we are using BFD_ASSEMBLER, if we are | |
1597 | doing this relocation the code in write.c is going to call | |
1598 | bfd_perform_relocation, which is also going to use the symbol | |
1599 | value. That means that if the reloc is fully resolved we want to | |
1600 | use *valueP since bfd_perform_relocation is not being used. | |
1601 | However, if the reloc is not fully resolved we do not want to use | |
1602 | *valueP, and must use fx_offset instead. However, if the reloc | |
1603 | is PC relative, we do want to use *valueP since it includes the | |
1604 | result of md_pcrel_from. This is confusing. */ | |
1605 | ||
1606 | if (fixP->fx_addsy == (symbolS *) NULL) | |
1607 | { | |
1608 | value = *valueP; | |
1609 | fixP->fx_done = 1; | |
1610 | } | |
1611 | else if (fixP->fx_pcrel) | |
1612 | { | |
1613 | value = *valueP; | |
1614 | } | |
1615 | else | |
1616 | { | |
1617 | value = fixP->fx_offset; | |
1618 | if (fixP->fx_subsy != (symbolS *) NULL) | |
1619 | { | |
1620 | if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section) | |
1621 | value -= S_GET_VALUE (fixP->fx_subsy); | |
1622 | else | |
1623 | { | |
1624 | /* We can't actually support subtracting a symbol. */ | |
1625 | as_bad_where (fixP->fx_file, fixP->fx_line, | |
1626 | "expression too complex"); | |
1627 | } | |
1628 | } | |
1629 | } | |
1630 | ||
1ece1d56 | 1631 | /* Check for dvp operands. These are indicated with a reloc value |
020ba60b DE |
1632 | >= BFD_RELOC_UNUSED. */ |
1633 | ||
1634 | if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED) | |
1635 | { | |
3a6b8910 | 1636 | dvp_cpu cpu; |
276dd6ef DE |
1637 | const dvp_operand *operand; |
1638 | DVP_INSN insn; | |
8b901ef8 DE |
1639 | fragS *fragP = fixP->fx_frag; |
1640 | ||
1641 | /* If this was a relaxable insn, the opcode may have moved. Find it. */ | |
1642 | if (fragP->fr_opcode != NULL) | |
1643 | where = fragP->fr_opcode; | |
020ba60b | 1644 | |
95bfad6d DE |
1645 | decode_fixup_reloc_type ((int) fixP->fx_r_type, |
1646 | & cpu, & operand); | |
020ba60b | 1647 | |
8b901ef8 DE |
1648 | /* For variable length vif insn data lengths, validate the user specified |
1649 | value or install the computed value in the instruction. */ | |
1650 | if (cpu == DVP_VIF | |
1651 | && (operand - vif_operands) == vif_operand_datalen_special) | |
1652 | { | |
1653 | value = vif_length_value (where[3], | |
1654 | fixP->tc_fix_data.wl, fixP->tc_fix_data.cl, | |
1655 | value); | |
1656 | if (fixP->tc_fix_data.user_value != -1) | |
1657 | { | |
1658 | if (fixP->tc_fix_data.user_value != value) | |
1659 | as_warn_where (fixP->fx_file, fixP->fx_line, | |
1660 | "specified length value doesn't match computed value"); | |
1661 | /* Don't override the user specified value. */ | |
1662 | } | |
1663 | else | |
1664 | { | |
1665 | if (output_vif) | |
1666 | { | |
1667 | install_vif_length (where, value); | |
1668 | } | |
1669 | } | |
1670 | fixP->fx_done = 1; | |
1671 | return 1; | |
1672 | } | |
1673 | ||
fa3671a3 DE |
1674 | /* For the gif nloop operand, if it was specified by the user ensure |
1675 | it matches the value we computed. */ | |
1676 | if (cpu == DVP_GIF | |
1677 | && (operand - gif_operands) == gif_operand_nloop) | |
1678 | { | |
1679 | value = compute_nloop (fixP->tc_fix_data.type, | |
1680 | fixP->tc_fix_data.nregs, | |
1681 | value); | |
8b901ef8 | 1682 | if (fixP->tc_fix_data.user_value != -1) |
fa3671a3 DE |
1683 | { |
1684 | check_nloop (fixP->tc_fix_data.type, | |
1685 | fixP->tc_fix_data.nregs, | |
8b901ef8 | 1686 | fixP->tc_fix_data.user_value, |
fa3671a3 DE |
1687 | value, |
1688 | fixP->fx_file, fixP->fx_line); | |
1689 | /* Don't override the user specified value. */ | |
1690 | fixP->fx_done = 1; | |
1691 | return 1; | |
1692 | } | |
1693 | } | |
1694 | ||
020ba60b | 1695 | /* Fetch the instruction, insert the fully resolved operand |
1ece1d56 DE |
1696 | value, and stuff the instruction back again. The fixup is recorded |
1697 | at the appropriate word so pass DVP_MOD_THIS_WORD so any offset | |
1698 | specified in the tables is ignored. */ | |
020ba60b | 1699 | insn = bfd_getl32 ((unsigned char *) where); |
6856244d DE |
1700 | insert_operand_final (cpu, operand, DVP_MOD_THIS_WORD, &insn, |
1701 | (offsetT) value, fixP->fx_file, fixP->fx_line); | |
020ba60b DE |
1702 | bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); |
1703 | ||
8b901ef8 DE |
1704 | /* If this is mpgloc/unpackloc, we're done. */ |
1705 | if (operand->flags & (DVP_OPERAND_VU_ADDRESS | DVP_OPERAND_UNPACK_ADDRESS)) | |
1706 | fixP->fx_done = 1; | |
1707 | ||
020ba60b DE |
1708 | if (fixP->fx_done) |
1709 | { | |
1710 | /* Nothing else to do here. */ | |
1711 | return 1; | |
1712 | } | |
1713 | ||
1714 | /* Determine a BFD reloc value based on the operand information. | |
1715 | We are only prepared to turn a few of the operands into relocs. */ | |
1716 | /* FIXME: This test is a hack. */ | |
276dd6ef | 1717 | if ((operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0) |
020ba60b | 1718 | { |
1ece1d56 | 1719 | assert (operand->bits == 11 |
020ba60b | 1720 | && operand->shift == 0); |
3a6b8910 | 1721 | fixP->fx_r_type = BFD_RELOC_MIPS_DVP_11_PCREL; |
020ba60b | 1722 | } |
91572941 DE |
1723 | else if ((operand->flags & DVP_OPERAND_DMA_ADDR) != 0 |
1724 | || (operand->flags & DVP_OPERAND_DMA_NEXT) != 0) | |
1ece1d56 DE |
1725 | { |
1726 | assert (operand->bits == 27 | |
1727 | && operand->shift == 4); | |
1728 | fixP->fx_r_type = BFD_RELOC_MIPS_DVP_27_S4; | |
1729 | } | |
020ba60b DE |
1730 | else |
1731 | { | |
1732 | as_bad_where (fixP->fx_file, fixP->fx_line, | |
1733 | "unresolved expression that must be resolved"); | |
1734 | fixP->fx_done = 1; | |
1735 | return 1; | |
1736 | } | |
1737 | } | |
1738 | else | |
1739 | { | |
1740 | switch (fixP->fx_r_type) | |
1741 | { | |
1742 | case BFD_RELOC_8: | |
1743 | md_number_to_chars (where, value, 1); | |
1744 | break; | |
1745 | case BFD_RELOC_16: | |
1746 | md_number_to_chars (where, value, 2); | |
1747 | break; | |
1748 | case BFD_RELOC_32: | |
1749 | md_number_to_chars (where, value, 4); | |
1750 | break; | |
1751 | default: | |
8b901ef8 | 1752 | as_fatal ("internal error: unexpected fixup"); |
020ba60b DE |
1753 | } |
1754 | } | |
1755 | ||
1756 | fixP->fx_addnumber = value; | |
1757 | ||
1758 | return 1; | |
209fb346 DE |
1759 | } |
1760 | ||
1761 | /* Translate internal representation of relocation info to BFD target | |
1762 | format. */ | |
1763 | ||
1764 | arelent * | |
020ba60b | 1765 | tc_gen_reloc (section, fixP) |
209fb346 | 1766 | asection *section; |
020ba60b | 1767 | fixS *fixP; |
209fb346 | 1768 | { |
020ba60b DE |
1769 | arelent *reloc; |
1770 | ||
1771 | reloc = (arelent *) xmalloc (sizeof (arelent)); | |
1772 | ||
1773 | reloc->sym_ptr_ptr = &fixP->fx_addsy->bsym; | |
1774 | reloc->address = fixP->fx_frag->fr_address + fixP->fx_where; | |
1775 | reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); | |
1776 | if (reloc->howto == (reloc_howto_type *) NULL) | |
1777 | { | |
1778 | as_bad_where (fixP->fx_file, fixP->fx_line, | |
1779 | "internal error: can't export reloc type %d (`%s')", | |
1780 | fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type)); | |
1781 | return NULL; | |
1782 | } | |
1783 | ||
1784 | assert (!fixP->fx_pcrel == !reloc->howto->pc_relative); | |
1785 | ||
1786 | reloc->addend = fixP->fx_addnumber; | |
1787 | ||
1788 | return reloc; | |
209fb346 DE |
1789 | } |
1790 | \f | |
1791 | /* Write a value out to the object file, using the appropriate endianness. */ | |
1792 | ||
1793 | void | |
1794 | md_number_to_chars (buf, val, n) | |
1795 | char *buf; | |
1796 | valueT val; | |
1797 | int n; | |
1798 | { | |
1799 | if (target_big_endian) | |
1800 | number_to_chars_bigendian (buf, val, n); | |
1801 | else | |
1802 | number_to_chars_littleendian (buf, val, n); | |
1803 | } | |
1804 | ||
1805 | /* Turn a string in input_line_pointer into a floating point constant of type | |
1806 | type, and store the appropriate bytes in *litP. The number of LITTLENUMS | |
1807 | emitted is stored in *sizeP . An error message is returned, or NULL on OK. | |
1808 | */ | |
1809 | ||
1810 | /* Equal to MAX_PRECISION in atof-ieee.c */ | |
1811 | #define MAX_LITTLENUMS 6 | |
1812 | ||
1813 | char * | |
1814 | md_atof (type, litP, sizeP) | |
1815 | char type; | |
1816 | char *litP; | |
1817 | int *sizeP; | |
1818 | { | |
1819 | int i,prec; | |
1820 | LITTLENUM_TYPE words[MAX_LITTLENUMS]; | |
1821 | LITTLENUM_TYPE *wordP; | |
1822 | char *t; | |
1823 | char *atof_ieee (); | |
1824 | ||
1825 | switch (type) | |
1826 | { | |
1827 | case 'f': | |
1828 | case 'F': | |
1829 | case 's': | |
1830 | case 'S': | |
1831 | prec = 2; | |
1832 | break; | |
1833 | ||
1834 | case 'd': | |
1835 | case 'D': | |
1836 | case 'r': | |
1837 | case 'R': | |
1838 | prec = 4; | |
1839 | break; | |
1840 | ||
1841 | /* FIXME: Some targets allow other format chars for bigger sizes here. */ | |
1842 | ||
1843 | default: | |
1844 | *sizeP = 0; | |
1845 | return "Bad call to md_atof()"; | |
1846 | } | |
1847 | ||
1848 | t = atof_ieee (input_line_pointer, type, words); | |
1849 | if (t) | |
1850 | input_line_pointer = t; | |
1851 | *sizeP = prec * sizeof (LITTLENUM_TYPE); | |
1852 | ||
1853 | if (target_big_endian) | |
1854 | { | |
1855 | for (i = 0; i < prec; i++) | |
1856 | { | |
1857 | md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); | |
1858 | litP += sizeof (LITTLENUM_TYPE); | |
1859 | } | |
1860 | } | |
1861 | else | |
1862 | { | |
1863 | for (i = prec - 1; i >= 0; i--) | |
1864 | { | |
1865 | md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); | |
1866 | litP += sizeof (LITTLENUM_TYPE); | |
1867 | } | |
1868 | } | |
1869 | ||
1870 | return 0; | |
1871 | } | |
020ba60b | 1872 | \f |
91572941 DE |
1873 | /* Miscellaneous utilities. */ |
1874 | ||
19f12fb4 DE |
1875 | /* Parse a 32 bit floating point number. |
1876 | The result is those 32 bits as an integer. */ | |
1877 | ||
1878 | static long | |
1879 | parse_float (pstr, errmsg) | |
1880 | char **pstr; | |
1881 | const char **errmsg; | |
1882 | { | |
1883 | LITTLENUM_TYPE words[MAX_LITTLENUMS]; | |
1884 | char *p; | |
1885 | ||
1886 | p = atof_ieee (*pstr, 'f', words); | |
1887 | *pstr = p; | |
1888 | return (words[0] << 16) | words[1]; | |
1889 | } | |
8dddf63f | 1890 | |
91572941 | 1891 | /* Scan a symbol and return a pointer to one past the end. */ |
8dddf63f | 1892 | |
8dddf63f DE |
1893 | #define issymchar(ch) (isalnum(ch) || ch == '_') |
1894 | static char * | |
1ece1d56 | 1895 | scan_symbol (sym) |
8dddf63f DE |
1896 | char *sym; |
1897 | { | |
1ece1d56 DE |
1898 | while (*sym && issymchar (*sym)) |
1899 | ++sym; | |
1900 | return sym; | |
8dddf63f DE |
1901 | } |
1902 | ||
8b901ef8 | 1903 | /* Evaluate an expression for an operand. |
1ece1d56 DE |
1904 | The result is the value of the expression if it can be evaluated, |
1905 | or 0 if it cannot (say because some symbols haven't been defined yet) | |
8151801a DE |
1906 | in which case a fixup is queued. |
1907 | ||
1908 | If OPINDEX is 0, don't queue any fixups, just return 0. */ | |
1ece1d56 DE |
1909 | |
1910 | static long | |
1911 | #ifdef USE_STDARG | |
1912 | eval_expr (int opindex, int offset, const char *fmt, ...) | |
1913 | #else | |
1914 | eval_expr (opindex, offset, fmt, va_alist) | |
1915 | int opindex,offset; | |
1916 | const char *fmt; | |
1917 | va_dcl | |
1918 | #endif | |
1919 | { | |
1920 | long value; | |
1921 | va_list ap; | |
1922 | char *str,*save_input; | |
1923 | expressionS exp; | |
1924 | ||
1925 | #ifdef USE_STDARG | |
1926 | va_start (ap, fmt); | |
1927 | #else | |
1928 | va_start (ap); | |
1929 | #endif | |
1930 | vasprintf (&str, fmt, ap); | |
1931 | va_end (ap); | |
1932 | ||
1933 | save_input = input_line_pointer; | |
1934 | input_line_pointer = str; | |
1935 | expression (&exp); | |
1936 | input_line_pointer = save_input; | |
1937 | free (str); | |
1938 | if (exp.X_op == O_constant) | |
1939 | value = exp.X_add_number; | |
1940 | else | |
8dddf63f | 1941 | { |
8151801a DE |
1942 | if (opindex != 0) |
1943 | { | |
1944 | fixups[fixup_count].exp = exp; | |
1945 | fixups[fixup_count].opindex = opindex; | |
1946 | fixups[fixup_count].offset = offset; | |
8b901ef8 DE |
1947 | fixups[fixup_count].user_value = -1; |
1948 | fixups[fixup_count].wl = -1; | |
1949 | fixups[fixup_count].cl = -1; | |
8151801a DE |
1950 | ++fixup_count; |
1951 | } | |
1ece1d56 | 1952 | value = 0; |
8dddf63f | 1953 | } |
1ece1d56 DE |
1954 | return value; |
1955 | } | |
8dddf63f | 1956 | |
1ece1d56 | 1957 | /* Create a label named by concatenating PREFIX to NAME. */ |
8dddf63f | 1958 | |
57d0c830 | 1959 | static symbolS * |
1ece1d56 DE |
1960 | create_label (prefix, name) |
1961 | const char *prefix, *name; | |
1962 | { | |
1963 | int namelen = strlen (name); | |
1964 | int prefixlen = strlen (prefix); | |
1965 | char *fullname; | |
57d0c830 | 1966 | symbolS *result; |
1ece1d56 DE |
1967 | |
1968 | fullname = xmalloc (prefixlen + namelen + 1); | |
1969 | strcpy (fullname, prefix); | |
1970 | strcat (fullname, name); | |
1971 | result = symbol_find_or_make (fullname); | |
1972 | free (fullname); | |
1973 | return result; | |
1974 | } | |
8dddf63f | 1975 | |
1ece1d56 | 1976 | /* Create a label named by concatenating PREFIX to NAME, |
b6d331b9 DE |
1977 | and define it as `.'. |
1978 | STO, if non-zero, is the st_other value to assign to this label. | |
3b4389e2 | 1979 | If STO is zero `cur_cpu', call force_mach_label to force record_mach to |
b6d331b9 | 1980 | emit a cpu label. Otherwise the disassembler gets confused. */ |
8dddf63f | 1981 | |
57d0c830 | 1982 | static symbolS * |
b6d331b9 DE |
1983 | create_colon_label (sto, prefix, name) |
1984 | int sto; | |
1ece1d56 DE |
1985 | const char *prefix, *name; |
1986 | { | |
1987 | int namelen = strlen (name); | |
1988 | int prefixlen = strlen (prefix); | |
1989 | char *fullname; | |
57d0c830 | 1990 | symbolS *result; |
1ece1d56 DE |
1991 | |
1992 | fullname = xmalloc (prefixlen + namelen + 1); | |
1993 | strcpy (fullname, prefix); | |
1994 | strcat (fullname, name); | |
1995 | result = colon (fullname); | |
b6d331b9 DE |
1996 | if (sto) |
1997 | S_SET_OTHER (result, sto); | |
1998 | else | |
3b4389e2 | 1999 | force_mach_label (); |
1ece1d56 DE |
2000 | free (fullname); |
2001 | return result; | |
8dddf63f | 2002 | } |
91572941 | 2003 | |
57d0c830 DE |
2004 | /* Return a malloc'd string useful in creating unique labels. |
2005 | PREFIX is the prefix to use or NULL if we're to pick one. */ | |
91572941 DE |
2006 | |
2007 | static char * | |
57d0c830 DE |
2008 | unique_name (prefix) |
2009 | const char *prefix; | |
91572941 DE |
2010 | { |
2011 | static int counter; | |
2012 | char *result; | |
2013 | ||
57d0c830 | 2014 | if (prefix == NULL) |
9152beba | 2015 | prefix = UNIQUE_LABEL_PREFIX; |
57d0c830 | 2016 | asprintf (&result, "%s%d", prefix, counter); |
91572941 DE |
2017 | ++counter; |
2018 | return result; | |
2019 | } | |
8b901ef8 | 2020 | \f |
fa3671a3 DE |
2021 | /* Compute a value for nloop. */ |
2022 | ||
2023 | static int | |
2024 | compute_nloop (type, nregs, bytes) | |
2025 | gif_type type; | |
2026 | int nregs, bytes; | |
2027 | { | |
2028 | int computed_nloop; | |
2029 | ||
2030 | switch (type) | |
2031 | { | |
2032 | case GIF_PACKED : | |
2033 | /* We can't compute a value if no regs were specified and there is a | |
2034 | non-zero amount of data. Just set to something useful, a warning | |
2035 | will be issued later. */ | |
2036 | if (nregs == 0) | |
2037 | nregs = 1; | |
2038 | computed_nloop = (bytes >> 4) / nregs; | |
2039 | break; | |
2040 | case GIF_REGLIST : | |
2041 | if (nregs == 0) | |
2042 | nregs = 1; | |
2043 | computed_nloop = (bytes >> 3) / nregs; | |
2044 | break; | |
2045 | case GIF_IMAGE : | |
2046 | computed_nloop = bytes >> 4; | |
2047 | break; | |
2048 | } | |
2049 | ||
2050 | return computed_nloop; | |
2051 | } | |
2052 | ||
2053 | /* Issue a warning if the user specified nloop value doesn't match the | |
2054 | computed value. */ | |
2055 | ||
2056 | static void | |
2057 | check_nloop (type, nregs, user_nloop, computed_nloop, file, line) | |
2058 | gif_type type; | |
2059 | int nregs,user_nloop,computed_nloop; | |
2060 | char *file; | |
2061 | unsigned int line; | |
2062 | { | |
2063 | if (user_nloop != computed_nloop) | |
2064 | as_warn_where (file, line, "nloop value does not match amount of data"); | |
2065 | } | |
91572941 | 2066 | \f |
e9cb12e4 DE |
2067 | /* Compute the auto-count value for a DMA tag. |
2068 | INLINE_P is non-zero if the dma data is inline. */ | |
91572941 DE |
2069 | |
2070 | static void | |
e9cb12e4 | 2071 | setup_dma_autocount (name, insn_buf, inline_p) |
91572941 DE |
2072 | const char *name; |
2073 | DVP_INSN *insn_buf; | |
e9cb12e4 | 2074 | int inline_p; |
91572941 DE |
2075 | { |
2076 | long count; | |
2077 | ||
e9cb12e4 DE |
2078 | if (inline_p) |
2079 | { | |
2080 | /* -1: The count is the number of following quadwords, so skip the one | |
2081 | containing the dma tag. */ | |
2082 | count = eval_expr (dma_operand_count, 0, | |
2083 | "((%s%s - %s) >> 4) - 1", END_LABEL_PREFIX, name, name); | |
2084 | } | |
2085 | else | |
2086 | { | |
2087 | /* We don't want to subtract 1 here as the begin and end labels | |
2088 | properly surround the data we want to compute the length of. */ | |
2089 | count = eval_expr (dma_operand_count, 0, | |
2090 | "(%s%s - %s) >> 4", END_LABEL_PREFIX, name, name); | |
2091 | } | |
91572941 DE |
2092 | |
2093 | /* Store the count field. */ | |
e9cb12e4 DE |
2094 | insn_buf[0] &= 0xffff0000; |
2095 | insn_buf[0] |= count & 0x0000ffff; | |
91572941 DE |
2096 | } |
2097 | ||
2098 | /* Record that inline data follows. */ | |
2099 | ||
2100 | static void | |
8151801a | 2101 | inline_dma_data (autocount_p, insn_buf) |
91572941 DE |
2102 | int autocount_p; |
2103 | DVP_INSN *insn_buf; | |
2104 | { | |
8151801a | 2105 | if (dma_data_state != 0 ) |
91572941 DE |
2106 | { |
2107 | as_bad ("DmaData blocks cannot be nested."); | |
2108 | return; | |
2109 | } | |
2110 | ||
8151801a | 2111 | dma_data_state = 1; |
91572941 DE |
2112 | |
2113 | if (autocount_p) | |
2114 | { | |
b6d331b9 | 2115 | dma_data_name = S_GET_NAME (create_colon_label (0, "", unique_name (NULL))); |
e9cb12e4 | 2116 | setup_dma_autocount (dma_data_name, insn_buf, 1); |
91572941 DE |
2117 | } |
2118 | else | |
8151801a | 2119 | dma_data_name = 0; |
91572941 DE |
2120 | } |
2121 | ||
2122 | /* Compute the auto-count value for a DMA tag with out-of-line data. */ | |
2123 | ||
2124 | static long | |
2125 | parse_dma_addr_autocount (opcode, operand, mods, insn_buf, pstr, errmsg) | |
2126 | const dvp_opcode *opcode; | |
2127 | const dvp_operand *operand; | |
2128 | int mods; | |
2129 | DVP_INSN *insn_buf; | |
2130 | char **pstr; | |
2131 | const char **errmsg; | |
2132 | { | |
2133 | char *start = *pstr; | |
2134 | char *end = start; | |
2135 | long retval; | |
2136 | /* Data reference must be a .DmaData label. */ | |
57d0c830 | 2137 | symbolS *label, *label2, *endlabel; |
91572941 DE |
2138 | const char *name; |
2139 | char c; | |
2140 | ||
2141 | label = label2 = 0; | |
2142 | if (! is_name_beginner (*start)) | |
2143 | { | |
2144 | *errmsg = "invalid .DmaData label"; | |
2145 | return 0; | |
2146 | } | |
2147 | ||
2148 | name = start; | |
2149 | end = scan_symbol (name); | |
2150 | c = *end; | |
2151 | *end = 0; | |
2152 | label = symbol_find_or_make (name); | |
2153 | *end = c; | |
2154 | ||
2155 | label2 = create_label ("_$", name); | |
2156 | endlabel = create_label (END_LABEL_PREFIX, name); | |
2157 | ||
e9cb12e4 | 2158 | retval = eval_expr (dma_operand_addr, 4, name); |
91572941 | 2159 | |
e9cb12e4 | 2160 | setup_dma_autocount (name, insn_buf, 0); |
91572941 DE |
2161 | |
2162 | *pstr = end; | |
2163 | return retval; | |
2164 | } | |
8dddf63f | 2165 | \f |
8b901ef8 DE |
2166 | /* Return the length value to insert in a VIF instruction whose upper |
2167 | byte is CMD and whose data length is BYTES. | |
2168 | WL,CL are used for unpack insns and are the stcycl values in effect. | |
2169 | This does not do the max -> 0 conversion. */ | |
e1b747c4 DE |
2170 | |
2171 | static int | |
8b901ef8 DE |
2172 | vif_length_value (cmd, wl, cl, bytes) |
2173 | int cmd; | |
2174 | int wl,cl; | |
2175 | int bytes; | |
e1b747c4 | 2176 | { |
8b901ef8 | 2177 | switch (cmd & 0x70) |
e1b747c4 | 2178 | { |
8b901ef8 DE |
2179 | case 0x50 : /* direct */ |
2180 | /* ??? Worry about data /= 16 cuts off? */ | |
2181 | return bytes / 16; | |
2182 | case 0x40 : /* mpg */ | |
2183 | /* ??? Worry about data /= 8 cuts off? */ | |
2184 | return bytes / 8; | |
2185 | case 0x60 : /* unpack */ | |
2186 | case 0x70 : | |
2187 | return vif_unpack_len_value (cmd & 15, wl, cl, bytes); | |
2188 | default : | |
2189 | as_fatal ("internal error: bad call to vif_length_value"); | |
e1b747c4 | 2190 | } |
e1b747c4 DE |
2191 | } |
2192 | ||
8b901ef8 DE |
2193 | /* Install length LEN in the vif insn at BUF. |
2194 | LEN is the actual value to store, except that the max->0 conversion | |
2195 | hasn't been done (we do it). | |
e033023f DE |
2196 | The bytes in BUF are in target order. */ |
2197 | ||
2198 | static void | |
b4cbabb8 | 2199 | install_vif_length (buf, len) |
e033023f DE |
2200 | char *buf; |
2201 | int len; | |
2202 | { | |
8b901ef8 | 2203 | unsigned char cmd = buf[3]; |
e033023f DE |
2204 | |
2205 | if ((cmd & 0x70) == 0x40) | |
2206 | { | |
2207 | /* mpg */ | |
e033023f | 2208 | if (len > 256) |
d476d46a DE |
2209 | as_bad ("`mpg' data length must be between 1 and 256"); |
2210 | buf[2] = len == 256 ? 0 : len; | |
e033023f DE |
2211 | } |
2212 | else if ((cmd & 0x70) == 0x50) | |
2213 | { | |
2214 | /* direct/directhl */ | |
d476d46a DE |
2215 | if (len > 65536) |
2216 | as_bad ("`direct' data length must be between 1 and 65536"); | |
2217 | len = len == 65536 ? 0 : len; | |
2218 | buf[0] = len; | |
2219 | buf[1] = len >> 8; | |
e033023f DE |
2220 | } |
2221 | else if ((cmd & 0x60) == 0x60) | |
2222 | { | |
2223 | /* unpack */ | |
8b901ef8 | 2224 | /* len == -1 means wl,cl are unknown and thus we can't compute |
65309b98 DE |
2225 | a useful value */ |
2226 | if (len == -1) | |
2227 | { | |
2228 | as_bad ("missing `stcycle', can't compute length of `unpack' insn"); | |
2229 | len = 1; | |
2230 | } | |
8b901ef8 | 2231 | if (len < 1 || len > 256) |
65309b98 | 2232 | as_bad ("`unpack' data length must be between 1 and 256"); |
57d0c830 DE |
2233 | len = len == 256 ? 0 : len; |
2234 | buf[2] = len; | |
e033023f DE |
2235 | } |
2236 | else | |
8b901ef8 | 2237 | as_fatal ("internal error: bad call to install_vif_length"); |
e033023f DE |
2238 | } |
2239 | ||
8b901ef8 DE |
2240 | /* Finish off the current set of mpg insns, and start a new set. |
2241 | The IGNORE arg exists because insert_unpack_marker uses it and both | |
2242 | of these functions are passed to insert_file. */ | |
ba4be194 DE |
2243 | |
2244 | static void | |
8b901ef8 DE |
2245 | insert_mpg_marker (ignore) |
2246 | unsigned long ignore; | |
ba4be194 | 2247 | { |
8b901ef8 DE |
2248 | s_endmpg (ENDMPG_MIDDLE); |
2249 | /* mpgloc is updated by s_endmpg. */ | |
ba4be194 DE |
2250 | md_assemble ("mpg *,*"); |
2251 | /* Record the cpu type in case we're in the middle of reading binary | |
2252 | data. */ | |
2253 | record_mach (DVP_VUUP, 0); | |
2254 | } | |
2255 | ||
8b901ef8 DE |
2256 | /* Finish off the current unpack insn and start a new one. |
2257 | INSN0 is the first word of the insn and is used to figure out what | |
2258 | kind of unpack insn it is. */ | |
2259 | ||
2260 | static void | |
2261 | insert_unpack_marker (insn0) | |
2262 | unsigned long insn0; | |
2263 | { | |
2264 | } | |
2265 | ||
e033023f | 2266 | /* Insert a file into the output. |
ba4be194 | 2267 | The -I arg passed to GAS is used to specify where to find the file. |
8b901ef8 DE |
2268 | INSERT_MARKER if non-NULL is called every SIZE bytes with an argument of |
2269 | INSERT_MARKER_ARG. This is used by the mpg insn to insert mpg's every 256 | |
2270 | insns and by the unpack insn. | |
e033023f DE |
2271 | The result is the number of bytes inserted. |
2272 | If an error occurs an error message is printed and zero is returned. */ | |
2273 | ||
2274 | static int | |
8b901ef8 | 2275 | insert_file (file, insert_marker, insert_marker_arg, size) |
e033023f | 2276 | const char *file; |
8b901ef8 DE |
2277 | void (*insert_marker) PARAMS ((unsigned long)); |
2278 | unsigned long insert_marker_arg; | |
ba4be194 | 2279 | int size; |
e033023f DE |
2280 | { |
2281 | FILE *f; | |
2282 | char buf[256]; | |
ba4be194 | 2283 | int i, n, total, left_before_marker; |
fe9efeb6 | 2284 | char *path; |
e033023f | 2285 | |
fe9efeb6 DE |
2286 | path = xmalloc (strlen (file) + include_dir_maxlen + 5 /*slop*/); |
2287 | f = NULL; | |
2288 | for (i = 0; i < include_dir_count; i++) | |
2289 | { | |
2290 | strcpy (path, include_dirs[i]); | |
2291 | strcat (path, "/"); | |
2292 | strcat (path, file); | |
2293 | if ((f = fopen (path, FOPEN_RB)) != NULL) | |
2294 | break; | |
2295 | } | |
2296 | free (path); | |
2297 | if (f == NULL) | |
2298 | f = fopen (file, FOPEN_RB); | |
e033023f DE |
2299 | if (f == NULL) |
2300 | { | |
2301 | as_bad ("unable to read file `%s'", file); | |
2302 | return 0; | |
2303 | } | |
2304 | ||
2305 | total = 0; | |
ba4be194 | 2306 | left_before_marker = 0; |
e033023f | 2307 | do { |
ba4be194 DE |
2308 | int bytes; |
2309 | if (insert_marker) | |
2310 | bytes = MIN (size - left_before_marker, sizeof (buf)); | |
2311 | else | |
2312 | bytes = sizeof (buf); | |
2313 | n = fread (buf, 1, bytes, f); | |
e033023f DE |
2314 | if (n > 0) |
2315 | { | |
2316 | char *fr = frag_more (n); | |
2317 | memcpy (fr, buf, n); | |
2318 | total += n; | |
ba4be194 DE |
2319 | if (insert_marker) |
2320 | { | |
2321 | left_before_marker += n; | |
2322 | if (left_before_marker > size) | |
8b901ef8 | 2323 | as_fatal ("internal error: file insertion sanity checky failed"); |
ba4be194 DE |
2324 | if (left_before_marker == size) |
2325 | { | |
8b901ef8 | 2326 | (*insert_marker) (insert_marker_arg); |
ba4be194 DE |
2327 | left_before_marker = 0; |
2328 | } | |
2329 | } | |
e033023f DE |
2330 | } |
2331 | } while (n > 0); | |
2332 | ||
2333 | fclose (f); | |
fe9efeb6 | 2334 | /* We assume the file is smaller than 2^31 bytes. |
ba4be194 | 2335 | Ok, we shouldn't make any assumptions. */ |
e033023f DE |
2336 | return total; |
2337 | } | |
2338 | ||
020ba60b DE |
2339 | /* Insert an operand value into an instruction. */ |
2340 | ||
6856244d DE |
2341 | static void |
2342 | insert_operand (cpu, opcode, operand, mods, insn_buf, val, errmsg) | |
3a6b8910 | 2343 | dvp_cpu cpu; |
6856244d | 2344 | const dvp_opcode *opcode; |
276dd6ef | 2345 | const dvp_operand *operand; |
020ba60b | 2346 | int mods; |
6856244d DE |
2347 | DVP_INSN *insn_buf; |
2348 | offsetT val; | |
2349 | const char **errmsg; | |
2350 | { | |
2351 | if (operand->insert) | |
2352 | { | |
2353 | (*operand->insert) (opcode, operand, mods, insn_buf, (long) val, errmsg); | |
2354 | } | |
2355 | else | |
2356 | { | |
b4cbabb8 DE |
2357 | /* We currently assume a field does not cross a word boundary. */ |
2358 | int shift = ((mods & DVP_MOD_THIS_WORD) | |
2359 | ? (operand->shift & 31) | |
2360 | : operand->shift); | |
8151801a DE |
2361 | /* FIXME: revisit */ |
2362 | if (operand->word == 0) | |
b4cbabb8 | 2363 | { |
8151801a DE |
2364 | int word = (mods & DVP_MOD_THIS_WORD) ? 0 : (shift / 32); |
2365 | if (operand->bits == 32) | |
2366 | insn_buf[word] = val; | |
2367 | else | |
2368 | { | |
2369 | shift = shift % 32; | |
2370 | insn_buf[word] |= ((long) val & ((1 << operand->bits) - 1)) << shift; | |
2371 | } | |
b4cbabb8 | 2372 | } |
6856244d DE |
2373 | else |
2374 | { | |
8151801a DE |
2375 | int word = (mods & DVP_MOD_THIS_WORD) ? 0 : operand->word; |
2376 | if (operand->bits == 32) | |
2377 | insn_buf[word] = val; | |
2378 | else | |
2379 | { | |
2380 | long temp = (long) val & ((1 << operand->bits) - 1); | |
2381 | insn_buf[word] |= temp << operand->shift; | |
2382 | } | |
6856244d DE |
2383 | } |
2384 | } | |
2385 | } | |
2386 | ||
2387 | /* Insert an operand's final value into an instruction. | |
2388 | Here we can give warning messages about operand values if we want to. */ | |
2389 | ||
2390 | static void | |
2391 | insert_operand_final (cpu, operand, mods, insn_buf, val, file, line) | |
2392 | dvp_cpu cpu; | |
2393 | const dvp_operand *operand; | |
2394 | int mods; | |
2395 | DVP_INSN *insn_buf; | |
020ba60b DE |
2396 | offsetT val; |
2397 | char *file; | |
2398 | unsigned int line; | |
2399 | { | |
2400 | if (operand->bits != 32) | |
2401 | { | |
b6675c1a | 2402 | offsetT min, max, test; |
020ba60b | 2403 | |
276dd6ef | 2404 | if ((operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0) |
020ba60b DE |
2405 | { |
2406 | if ((val & 7) != 0) | |
2407 | { | |
2408 | if (file == (char *) NULL) | |
2409 | as_warn ("branch to misaligned address"); | |
2410 | else | |
2411 | as_warn_where (file, line, "branch to misaligned address"); | |
2412 | } | |
2413 | val >>= 3; | |
2414 | } | |
2415 | ||
276dd6ef | 2416 | if ((operand->flags & DVP_OPERAND_SIGNED) != 0) |
020ba60b | 2417 | { |
276dd6ef | 2418 | if ((operand->flags & DVP_OPERAND_SIGNOPT) != 0) |
020ba60b DE |
2419 | max = (1 << operand->bits) - 1; |
2420 | else | |
2421 | max = (1 << (operand->bits - 1)) - 1; | |
2422 | min = - (1 << (operand->bits - 1)); | |
2423 | } | |
2424 | else | |
2425 | { | |
2426 | max = (1 << operand->bits) - 1; | |
2427 | min = 0; | |
2428 | } | |
2429 | ||
276dd6ef | 2430 | if ((operand->flags & DVP_OPERAND_NEGATIVE) != 0) |
020ba60b DE |
2431 | test = - val; |
2432 | else | |
2433 | test = val; | |
2434 | ||
2435 | if (test < (offsetT) min || test > (offsetT) max) | |
2436 | { | |
2437 | const char *err = | |
2438 | "operand out of range (%s not between %ld and %ld)"; | |
2439 | char buf[100]; | |
2440 | ||
2441 | sprint_value (buf, test); | |
2442 | if (file == (char *) NULL) | |
2443 | as_warn (err, buf, min, max); | |
2444 | else | |
2445 | as_warn_where (file, line, err, buf, min, max); | |
2446 | } | |
2447 | } | |
2448 | ||
6856244d DE |
2449 | { |
2450 | const char *errmsg = NULL; | |
2451 | insert_operand (cpu, NULL, operand, mods, insn_buf, val, &errmsg); | |
2452 | if (errmsg != NULL) | |
2453 | as_warn_where (file, line, errmsg); | |
2454 | } | |
020ba60b | 2455 | } |
b5d20cf6 | 2456 | \f |
91572941 | 2457 | /* DVP pseudo ops. */ |
3b2215c2 JL |
2458 | |
2459 | static void | |
1ece1d56 | 2460 | s_dmadata (ignore) |
3b2215c2 JL |
2461 | int ignore; |
2462 | { | |
1ece1d56 | 2463 | char *name, c; |
63589bf4 | 2464 | |
8151801a | 2465 | dma_data_name = 0; |
3b2215c2 | 2466 | |
8151801a | 2467 | if (dma_data_state != 0) |
4411d964 | 2468 | { |
1ece1d56 DE |
2469 | as_bad ("DmaData blocks cannot be nested."); |
2470 | ignore_rest_of_line (); | |
2471 | return; | |
3b2215c2 | 2472 | } |
8151801a | 2473 | dma_data_state = 1; |
4411d964 | 2474 | |
1ece1d56 DE |
2475 | SKIP_WHITESPACE (); /* Leading whitespace is part of operand. */ |
2476 | name = input_line_pointer; | |
4411d964 | 2477 | |
1ece1d56 | 2478 | if (!is_name_beginner (*name)) |
3b2215c2 | 2479 | { |
1ece1d56 DE |
2480 | as_bad ("invalid identifier for \".DmaData\""); |
2481 | ignore_rest_of_line (); | |
2482 | return; | |
3b2215c2 | 2483 | } |
b5d20cf6 | 2484 | |
e7201c9e DE |
2485 | /* Do an implicit alignment to a 16 byte boundary. */ |
2486 | frag_align (4, 0, 0); | |
2487 | record_alignment (now_seg, 4); | |
2488 | ||
1ece1d56 DE |
2489 | c = get_symbol_end (); |
2490 | line_label = colon (name); /* user-defined label */ | |
8151801a | 2491 | dma_data_name = S_GET_NAME (line_label); |
1ece1d56 | 2492 | *input_line_pointer = c; |
63589bf4 | 2493 | |
3b4389e2 DE |
2494 | /* Force emission of a machine type label for the next insn. */ |
2495 | force_mach_label (); | |
2496 | ||
1ece1d56 | 2497 | demand_empty_rest_of_line (); |
3b2215c2 | 2498 | } |
b5d20cf6 | 2499 | |
3b2215c2 | 2500 | static void |
1ece1d56 | 2501 | s_enddmadata (ignore) |
3b2215c2 JL |
2502 | int ignore; |
2503 | { | |
8151801a | 2504 | if (dma_data_state != 1) |
3b2215c2 | 2505 | { |
1ece1d56 DE |
2506 | as_warn (".EndDmaData encountered outside a DmaData block -- ignored."); |
2507 | ignore_rest_of_line (); | |
8151801a | 2508 | dma_data_name = 0; |
3b2215c2 | 2509 | } |
8151801a | 2510 | dma_data_state = 0; |
1ece1d56 DE |
2511 | demand_empty_rest_of_line (); |
2512 | ||
e9cb12e4 DE |
2513 | /* If count provided, verify it is correct. */ |
2514 | /* ... */ | |
2515 | ||
89036011 DE |
2516 | /* Fill the data out to a multiple of 16 bytes. */ |
2517 | /* FIXME: Are the fill contents right? */ | |
2518 | frag_align (4, 0, 0); | |
2519 | ||
1ece1d56 DE |
2520 | /* "label" points to beginning of block. |
2521 | Create a name for the final label like _$<name>. */ | |
8151801a | 2522 | if (dma_data_name) |
89036011 | 2523 | create_colon_label (0, END_LABEL_PREFIX, dma_data_name); |
b5d20cf6 | 2524 | } |
91572941 | 2525 | |
b5d20cf6 | 2526 | static void |
1ece1d56 | 2527 | s_dmapackvif (ignore) |
4411d964 | 2528 | int ignore; |
b5d20cf6 | 2529 | { |
1ece1d56 | 2530 | /* Syntax: .dmapackvif 0|1 */ |
63589bf4 | 2531 | |
1ece1d56 DE |
2532 | /* Leading whitespace is part of operand. */ |
2533 | SKIP_WHITESPACE (); | |
2534 | switch (*input_line_pointer++) | |
63589bf4 | 2535 | { |
3b2215c2 | 2536 | case '0': |
1ece1d56 DE |
2537 | dma_pack_vif_p = 0; |
2538 | break; | |
3b2215c2 | 2539 | case '1': |
1ece1d56 DE |
2540 | dma_pack_vif_p = 1; |
2541 | break; | |
63589bf4 | 2542 | default: |
7f28a81d | 2543 | as_bad ("illegal argument to `.dmapackvif'"); |
63589bf4 | 2544 | } |
1ece1d56 | 2545 | demand_empty_rest_of_line (); |
b5d20cf6 DE |
2546 | } |
2547 | ||
ba4be194 DE |
2548 | /* INTERNAL_P is non-zero if invoked internally by this file rather than |
2549 | by the user. In this case we don't touch the input stream. */ | |
2550 | ||
b5d20cf6 | 2551 | static void |
ba4be194 DE |
2552 | s_enddirect (internal_p) |
2553 | int internal_p; | |
b5d20cf6 | 2554 | { |
498fcb9c | 2555 | if (CUR_ASM_STATE != ASM_DIRECT) |
e1b747c4 DE |
2556 | { |
2557 | as_bad ("`.enddirect' has no matching `direct' instruction"); | |
2558 | return; | |
2559 | } | |
2560 | ||
8b901ef8 DE |
2561 | /* Record in the end data symbol the current location. */ |
2562 | if (now_seg != S_GET_SEGMENT (vif_data_end)) | |
2563 | as_bad (".enddirect in different section"); | |
2564 | vif_data_end->sy_frag = frag_now; | |
2565 | S_SET_VALUE (vif_data_end, (valueT) frag_now_fix ()); | |
e1b747c4 | 2566 | |
498fcb9c DE |
2567 | set_asm_state (ASM_INIT); |
2568 | ||
8b901ef8 DE |
2569 | /* Needn't be reset, but to catch bugs it is. */ |
2570 | vif_data_end = NULL; | |
498fcb9c | 2571 | |
ba4be194 DE |
2572 | if (! internal_p) |
2573 | demand_empty_rest_of_line (); | |
498fcb9c DE |
2574 | } |
2575 | ||
8b901ef8 DE |
2576 | /* CALLER denotes who's calling us. |
2577 | If ENDMPG_USER then .endmpg was found in the input stream. | |
2578 | If ENDMPG_INTERNAL then we've been invoked to finish off file insertion. | |
2579 | If ENDMPG_MIDDLE then we've been invoked in the middle of a long stretch | |
2580 | of vu code. */ | |
83920d29 | 2581 | |
498fcb9c | 2582 | static void |
8b901ef8 DE |
2583 | s_endmpg (caller) |
2584 | int caller; | |
498fcb9c | 2585 | { |
498fcb9c DE |
2586 | if (CUR_ASM_STATE != ASM_MPG) |
2587 | { | |
2588 | as_bad ("`.endmpg' has no matching `mpg' instruction"); | |
2589 | return; | |
2590 | } | |
2591 | ||
8b901ef8 DE |
2592 | /* Record in the end data symbol the current location. */ |
2593 | if (now_seg != S_GET_SEGMENT (vif_data_end)) | |
2594 | as_bad (".endmpg in different section"); | |
2595 | vif_data_end->sy_frag = frag_now; | |
2596 | S_SET_VALUE (vif_data_end, (valueT) frag_now_fix ()); | |
2597 | ||
2598 | /* Update $.mpgloc. | |
2599 | We have to leave the old value alone as it may be used in fixups | |
2600 | already recorded. The new value is the old value plus the number of | |
2601 | double words in this chunk. */ | |
2602 | { | |
2603 | symbolS *s; | |
2604 | s = expr_build_binary (O_subtract, vif_data_end, vif_data_start); | |
2605 | s = expr_build_binary (O_divide, s, expr_build_uconstant (8)); | |
2606 | mpgloc_sym = expr_build_binary (O_add, mpgloc_sym, s); | |
2607 | } | |
498fcb9c DE |
2608 | |
2609 | set_asm_state (ASM_INIT); | |
2610 | ||
8b901ef8 DE |
2611 | /* Needn't be reset, but to catch bugs it is. */ |
2612 | vif_data_end = NULL; | |
498fcb9c | 2613 | |
83920d29 | 2614 | /* Reset the vu insn counter. */ |
8b901ef8 DE |
2615 | if (caller != ENDMPG_MIDDLE) |
2616 | vu_count = -1; | |
498fcb9c | 2617 | |
8b901ef8 | 2618 | if (caller == ENDMPG_USER) |
83920d29 | 2619 | demand_empty_rest_of_line (); |
498fcb9c DE |
2620 | } |
2621 | ||
ba4be194 DE |
2622 | /* INTERNAL_P is non-zero if invoked internally by this file rather than |
2623 | by the user. In this case we don't touch the input stream. */ | |
2624 | ||
498fcb9c | 2625 | static void |
ba4be194 DE |
2626 | s_endunpack (internal_p) |
2627 | int internal_p; | |
498fcb9c | 2628 | { |
498fcb9c DE |
2629 | if (CUR_ASM_STATE != ASM_UNPACK) |
2630 | { | |
2631 | as_bad ("`.endunpack' has no matching `unpack' instruction"); | |
2632 | return; | |
2633 | } | |
2634 | ||
8b901ef8 DE |
2635 | /* Record in the end data symbol the current location. */ |
2636 | if (now_seg != S_GET_SEGMENT (vif_data_end)) | |
2637 | as_bad (".endunpack in different section"); | |
2638 | vif_data_end->sy_frag = frag_now; | |
2639 | S_SET_VALUE (vif_data_end, (valueT) frag_now_fix ()); | |
65309b98 | 2640 | |
8b901ef8 DE |
2641 | /* Update $.UnpackLoc. */ |
2642 | { | |
2643 | symbolS *s; | |
2644 | s = expr_build_binary (O_subtract, vif_data_end, vif_data_start); | |
050ac694 DE |
2645 | /* Round up to next quadword boundary. */ |
2646 | /* FIXME: This isn't correct, the size of the input data is not the | |
2647 | size of the output data. Someone else can fix this. */ | |
2648 | s = expr_build_binary (O_add, s, expr_build_uconstant (15)); | |
8b901ef8 DE |
2649 | s = expr_build_binary (O_divide, s, expr_build_uconstant (16)); |
2650 | unpackloc_sym = expr_build_binary (O_add, unpackloc_sym, s); | |
2651 | } | |
498fcb9c | 2652 | |
050ac694 DE |
2653 | /* Round up to next word boundary. */ |
2654 | frag_align (2, 0, 0); | |
2655 | ||
498fcb9c | 2656 | set_asm_state (ASM_INIT); |
fe9efeb6 | 2657 | |
8b901ef8 DE |
2658 | /* Needn't be reset, but to catch bugs it is. */ |
2659 | vif_data_end = NULL; | |
498fcb9c | 2660 | |
ba4be194 DE |
2661 | if (! internal_p) |
2662 | demand_empty_rest_of_line (); | |
b5d20cf6 DE |
2663 | } |
2664 | ||
8151801a DE |
2665 | static void |
2666 | s_endgif (ignore) | |
2667 | int ignore; | |
2668 | { | |
fa3671a3 | 2669 | int bytes; |
d030671b DE |
2670 | int specified_nloop = gif_nloop (); |
2671 | int computed_nloop; | |
2672 | int nregs = gif_nregs (); | |
fa3671a3 DE |
2673 | char *file; |
2674 | unsigned int line; | |
2675 | ||
2676 | as_where (&file, &line); | |
8151801a | 2677 | |
498fcb9c | 2678 | if (CUR_ASM_STATE != ASM_GIF) |
8151801a DE |
2679 | { |
2680 | as_bad (".endgif doesn't follow a gif tag"); | |
2681 | return; | |
2682 | } | |
498fcb9c | 2683 | pop_asm_state (0); |
8151801a | 2684 | |
fa3671a3 DE |
2685 | /* Fill out to proper boundary. |
2686 | ??? This may cause eval_expr to always queue a fixup. So be it. */ | |
2687 | switch (gif_insn_type) | |
8151801a | 2688 | { |
fa3671a3 DE |
2689 | case GIF_PACKED : frag_align (4, 0, 0); break; |
2690 | case GIF_REGLIST : frag_align (3, 0, 0); break; | |
2691 | case GIF_IMAGE : frag_align (4, 0, 0); break; | |
8151801a DE |
2692 | } |
2693 | ||
8b901ef8 DE |
2694 | /* The -16 is because the `gif_data_name' label is emitted at the |
2695 | start of the gif tag. If we're in a different frag from the one we | |
2696 | started with, this can't be computed until much later. To cope we queue | |
2697 | a fixup and deal with it then. | |
fa3671a3 DE |
2698 | ??? The other way to handle this is by having expr() compute "syma - symb" |
2699 | when they're in different fragments but the difference is constant. | |
2700 | Not sure how much of a slowdown that will introduce though. */ | |
2701 | fixup_count = 0; | |
2702 | bytes = eval_expr (gif_operand_nloop, 0, ". - %s - 16", gif_data_name); | |
2703 | ||
2704 | /* Compute a value for nloop if we can. */ | |
2705 | ||
2706 | if (fixup_count == 0) | |
8151801a | 2707 | { |
fa3671a3 | 2708 | computed_nloop = compute_nloop (gif_insn_type, nregs, bytes); |
d030671b | 2709 | |
fa3671a3 DE |
2710 | /* If the user specified nloop, verify it. */ |
2711 | if (specified_nloop != -1) | |
8b901ef8 DE |
2712 | check_nloop (gif_insn_type, nregs, |
2713 | specified_nloop, computed_nloop, | |
fa3671a3 DE |
2714 | file, line); |
2715 | } | |
d030671b | 2716 | |
fa3671a3 DE |
2717 | /* If computation of nloop can't be done yet, queue a fixup and do it later. |
2718 | Otherwise validate nloop if specified or write the computed value into | |
d030671b | 2719 | the insn. */ |
fa3671a3 DE |
2720 | |
2721 | if (fixup_count != 0) | |
d030671b | 2722 | { |
fa3671a3 DE |
2723 | /* FIXME: It might eventually be possible to combine all the various |
2724 | copies of this bit of code. */ | |
2725 | int op_type, reloc_type, offset; | |
2726 | const dvp_operand *operand; | |
2727 | fixS *fix; | |
2728 | ||
2729 | op_type = fixups[0].opindex; | |
2730 | offset = fixups[0].offset; | |
2731 | reloc_type = encode_fixup_reloc_type (DVP_GIF, op_type); | |
2732 | operand = &gif_operands[op_type]; | |
2733 | fix = fix_new_exp (gif_insn_frag, | |
8b901ef8 DE |
2734 | (gif_insn_frag_loc + offset |
2735 | - gif_insn_frag->fr_literal), | |
fa3671a3 DE |
2736 | 4, &fixups[0].exp, 0, |
2737 | (bfd_reloc_code_real_type) reloc_type); | |
2738 | /* Record user specified value so we can test it when we compute the | |
2739 | actual value. */ | |
2740 | fix->tc_fix_data.type = gif_insn_type; | |
2741 | fix->tc_fix_data.nregs = nregs; | |
8b901ef8 | 2742 | fix->tc_fix_data.user_value = specified_nloop; |
8151801a | 2743 | } |
fa3671a3 DE |
2744 | else if (specified_nloop != -1) |
2745 | ; /* nothing to do */ | |
8151801a DE |
2746 | else |
2747 | { | |
fa3671a3 | 2748 | DVP_INSN insn = bfd_getl32 (gif_insn_frag_loc); |
8151801a DE |
2749 | insert_operand_final (DVP_GIF, &gif_operands[gif_operand_nloop], |
2750 | DVP_MOD_THIS_WORD, &insn, | |
d030671b | 2751 | (offsetT) computed_nloop, file, line); |
fa3671a3 | 2752 | bfd_putl32 ((bfd_vma) insn, gif_insn_frag_loc); |
8151801a DE |
2753 | } |
2754 | ||
8b901ef8 | 2755 | /* These needn't be reset, but to catch bugs they are. */ |
8151801a | 2756 | gif_data_name = NULL; |
8b901ef8 DE |
2757 | gif_insn_frag = NULL; |
2758 | gif_insn_frag_loc = NULL; | |
2759 | ||
8151801a DE |
2760 | demand_empty_rest_of_line (); |
2761 | } | |
2762 | ||
b5d20cf6 | 2763 | static void |
e033023f DE |
2764 | s_state (state) |
2765 | int state; | |
b5d20cf6 | 2766 | { |
d3c6610c DE |
2767 | /* If in MPG state and the user requests to change to VU state, |
2768 | leave the state as MPG. This happens when we see an mpg followed | |
2769 | by a .include that has .vu. */ | |
498fcb9c | 2770 | if (CUR_ASM_STATE == ASM_MPG && state == ASM_VU) |
d3c6610c DE |
2771 | return; |
2772 | ||
498fcb9c | 2773 | set_asm_state (state); |
4411d964 | 2774 | |
d3c6610c | 2775 | demand_empty_rest_of_line (); |
e1b747c4 | 2776 | } |