mirror of
				https://github.com/netwide-assembler/nasm.git
				synced 2025-10-10 00:25:06 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1993 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1993 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* assemble.c   code generation for the Netwide Assembler
 | |
|  *
 | |
|  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
 | |
|  * Julian Hall. All rights reserved. The software is
 | |
|  * redistributable under the licence given in the file "Licence"
 | |
|  * distributed in the NASM archive.
 | |
|  *
 | |
|  * the actual codes (C syntax, i.e. octal):
 | |
|  * \0            - terminates the code. (Unless it's a literal of course.)
 | |
|  * \1, \2, \3    - that many literal bytes follow in the code stream
 | |
|  * \4, \6        - the POP/PUSH (respectively) codes for CS, DS, ES, SS
 | |
|  *                 (POP is never used for CS) depending on operand 0
 | |
|  * \5, \7        - the second byte of POP/PUSH codes for FS, GS, depending
 | |
|  *                 on operand 0
 | |
|  * \10, \11, \12 - a literal byte follows in the code stream, to be added
 | |
|  *                 to the register value of operand 0, 1 or 2
 | |
|  * \17           - encodes the literal byte 0. (Some compilers don't take
 | |
|  *                 kindly to a zero byte in the _middle_ of a compile time
 | |
|  *                 string constant, so I had to put this hack in.)
 | |
|  * \14, \15, \16 - a signed byte immediate operand, from operand 0, 1 or 2
 | |
|  * \20, \21, \22 - a byte immediate operand, from operand 0, 1 or 2
 | |
|  * \24, \25, \26 - an unsigned byte immediate operand, from operand 0, 1 or 2
 | |
|  * \30, \31, \32 - a word immediate operand, from operand 0, 1 or 2
 | |
|  * \34, \35, \36 - select between \3[012] and \4[012] depending on 16/32 bit
 | |
|  *                 assembly mode or the operand-size override on the operand
 | |
|  * \37           - a word constant, from the _segment_ part of operand 0
 | |
|  * \40, \41, \42 - a long immediate operand, from operand 0, 1 or 2
 | |
|  * \44, \45, \46 - select between \3[012], \4[012] and \5[456]
 | |
|  *		   depending on assembly mode or the address-size override
 | |
|  *		   on the operand.
 | |
|  * \50, \51, \52 - a byte relative operand, from operand 0, 1 or 2
 | |
|  * \54, \55, \56 - a qword immediate operand, from operand 0, 1 or 2
 | |
|  * \60, \61, \62 - a word relative operand, from operand 0, 1 or 2
 | |
|  * \64, \65, \66 - select between \6[012] and \7[012] depending on 16/32 bit
 | |
|  *                 assembly mode or the operand-size override on the operand
 | |
|  * \70, \71, \72 - a long relative operand, from operand 0, 1 or 2
 | |
|  * \1ab          - a ModRM, calculated on EA in operand a, with the spare
 | |
|  *                 field the register value of operand b.
 | |
|  * \130,\131,\132 - an immediate word or signed byte for operand 0, 1, or 2
 | |
|  * \133,\134,\135 - or 2 (s-field) into next opcode byte if operand 0, 1, or 2
 | |
|  *		    is a signed byte rather than a word.
 | |
|  * \140,\141,\142 - an immediate dword or signed byte for operand 0, 1, or 2
 | |
|  * \143,\144,\145 - or 2 (s-field) into next opcode byte if operand 0, 1, or 2
 | |
|  *		    is a signed byte rather than a dword.
 | |
|  * \150,\151,\152 - an immediate qword or signed byte for operand 0, 1, or 2
 | |
|  * \153,\154,\155 - or 2 (s-field) into next opcode byte if operand 0, 1, or 2
 | |
|  *		    is a signed byte rather than a qword.
 | |
|  * \2ab          - a ModRM, calculated on EA in operand a, with the spare
 | |
|  *                 field equal to digit b.
 | |
|  * \30x          - might be an 0x67 byte, depending on the address size of
 | |
|  *                 the memory reference in operand x.
 | |
|  * \310          - indicates fixed 16-bit address size, i.e. optional 0x67.
 | |
|  * \311          - indicates fixed 32-bit address size, i.e. optional 0x67.
 | |
|  * \312          - (disassembler only) marker on LOOP, LOOPxx instructions.
 | |
|  * \313          - indicates fixed 64-bit address size, 0x67 invalid.
 | |
|  * \320          - indicates fixed 16-bit operand size, i.e. optional 0x66.
 | |
|  * \321          - indicates fixed 32-bit operand size, i.e. optional 0x66.
 | |
|  * \322          - indicates that this instruction is only valid when the
 | |
|  *                 operand size is the default (instruction to disassembler,
 | |
|  *                 generates no code in the assembler)
 | |
|  * \323          - indicates fixed 64-bit operand size, REX on extensions only.
 | |
|  * \324          - indicates 64-bit operand size requiring REX prefix.
 | |
|  * \330          - a literal byte follows in the code stream, to be added
 | |
|  *                 to the condition code value of the instruction.
 | |
|  * \331          - instruction not valid with REP prefix.  Hint for
 | |
|  *                 disassembler only; for SSE instructions.
 | |
|  * \332          - disassemble a rep (0xF3 byte) prefix as repe not rep.
 | |
|  * \333          - REP prefix (0xF3 byte); for SSE instructions.  Not encoded
 | |
|  *                 as a literal byte in order to aid the disassembler.
 | |
|  * \334          - LOCK prefix used instead of REX.R
 | |
|  * \340          - reserve <operand 0> bytes of uninitialized storage.
 | |
|  *                 Operand 0 had better be a segmentless constant.
 | |
|  * \364          - operand-size prefix (0x66) not permitted
 | |
|  * \365          - address-size prefix (0x67) not permitted
 | |
|  * \366          - operand-size prefix (0x66) used as opcode extension
 | |
|  * \367          - address-size prefix (0x67) used as opcode extension
 | |
|  * \370,\371,\372 - match only if operand 0 meets byte jump criteria.
 | |
|  *		   370 is used for Jcc, 371 is used for JMP.
 | |
|  * \373		 - assemble 0x03 if bits==16, 0x05 if bits==32;
 | |
|  *		   used for conditional jump over longer jump
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <inttypes.h>
 | |
| 
 | |
| #include "nasm.h"
 | |
| #include "nasmlib.h"
 | |
| #include "assemble.h"
 | |
| #include "insns.h"
 | |
| #include "preproc.h"
 | |
| #include "regflags.c"
 | |
| #include "regvals.c"
 | |
| 
 | |
| typedef struct {
 | |
|     int sib_present;                 /* is a SIB byte necessary? */
 | |
|     int bytes;                       /* # of bytes of offset needed */
 | |
|     int size;                        /* lazy - this is sib+bytes+1 */
 | |
|     uint8_t modrm, sib, rex, rip;    /* the bytes themselves */
 | |
| } ea;
 | |
| 
 | |
| static uint32_t cpu;            /* cpu level received from nasm.c */
 | |
| static efunc errfunc;
 | |
| static struct ofmt *outfmt;
 | |
| static ListGen *list;
 | |
| 
 | |
| static int32_t calcsize(int32_t, int32_t, int, insn *, const char *);
 | |
| static void gencode(int32_t, int32_t, int, insn *, const char *, int32_t);
 | |
| static int matches(const struct itemplate *, insn *, int bits);
 | |
| static int32_t regflag(const operand *);
 | |
| static int32_t regval(const operand *);
 | |
| static int rexflags(int, int32_t, int);
 | |
| static int op_rexflags(const operand *, int);
 | |
| static ea *process_ea(operand *, ea *, int, int, int32_t, int);
 | |
| static int chsize(operand *, int);
 | |
| 
 | |
| static void assert_no_prefix(insn * ins, int prefix)
 | |
| {
 | |
|     int j;
 | |
| 
 | |
|     for (j = 0; j < ins->nprefix; j++) {
 | |
| 	if (ins->prefixes[j] == prefix) {
 | |
| 	    errfunc(ERR_NONFATAL, "invalid %s prefix", prefix_name(prefix));
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This routine wrappers the real output format's output routine,
 | |
|  * in order to pass a copy of the data off to the listing file
 | |
|  * generator at the same time.
 | |
|  */
 | |
| static void out(int32_t offset, int32_t segto, const void *data,
 | |
|                 uint32_t type, int32_t segment, int32_t wrt)
 | |
| {
 | |
|     static int32_t lineno = 0;     /* static!!! */
 | |
|     static char *lnfname = NULL;
 | |
| 
 | |
|     if ((type & OUT_TYPMASK) == OUT_ADDRESS) {
 | |
|         if (segment != NO_SEG || wrt != NO_SEG) {
 | |
|             /*
 | |
|              * This address is relocated. We must write it as
 | |
|              * OUT_ADDRESS, so there's no work to be done here.
 | |
|              */
 | |
|             list->output(offset, data, type);
 | |
|         } else {
 | |
|             uint8_t p[8], *q = p;
 | |
|             /*
 | |
|              * This is a non-relocated address, and we're going to
 | |
|              * convert it into RAWDATA format.
 | |
|              */
 | |
|             if ((type & OUT_SIZMASK) == 4) {
 | |
|                 WRITELONG(q, *(int32_t *)data);
 | |
|                 list->output(offset, p, OUT_RAWDATA + 4);
 | |
|             } else if ((type & OUT_SIZMASK) == 8) {
 | |
|                 WRITEDLONG(q, *(int64_t *)data);
 | |
|                 list->output(offset, p, OUT_RAWDATA + 8);
 | |
|             } else {
 | |
|                 WRITESHORT(q, *(int32_t *)data);
 | |
|                 list->output(offset, p, OUT_RAWDATA + 2);
 | |
|             }
 | |
|         }
 | |
|     } else if ((type & OUT_TYPMASK) == OUT_RAWDATA) {
 | |
|         list->output(offset, data, type);
 | |
|     } else if ((type & OUT_TYPMASK) == OUT_RESERVE) {
 | |
|         list->output(offset, NULL, type);
 | |
|     } else if ((type & OUT_TYPMASK) == OUT_REL2ADR ||
 | |
|                (type & OUT_TYPMASK) == OUT_REL4ADR) {
 | |
|         list->output(offset, data, type);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * this call to src_get determines when we call the
 | |
|      * debug-format-specific "linenum" function
 | |
|      * it updates lineno and lnfname to the current values
 | |
|      * returning 0 if "same as last time", -2 if lnfname
 | |
|      * changed, and the amount by which lineno changed,
 | |
|      * if it did. thus, these variables must be static
 | |
|      */
 | |
| 
 | |
|     if (src_get(&lineno, &lnfname)) {
 | |
|         outfmt->current_dfmt->linenum(lnfname, lineno, segto);
 | |
|     }
 | |
| 
 | |
|     outfmt->output(segto, data, type, segment, wrt);
 | |
| }
 | |
| 
 | |
| static int jmp_match(int32_t segment, int32_t offset, int bits,
 | |
|                      insn * ins, const char *code)
 | |
| {
 | |
|     int32_t isize;
 | |
|     uint8_t c = code[0];
 | |
| 
 | |
|     if (c != 0370 && c != 0371)
 | |
|         return 0;
 | |
|     if (ins->oprs[0].opflags & OPFLAG_FORWARD) {
 | |
|         if ((optimizing < 0 || (ins->oprs[0].type & STRICT))
 | |
|             && c == 0370)
 | |
|             return 1;
 | |
|         else
 | |
|             return (pass0 == 0);        /* match a forward reference */
 | |
|     }
 | |
|     isize = calcsize(segment, offset, bits, ins, code);
 | |
|     if (ins->oprs[0].segment != segment)
 | |
|         return 0;
 | |
|     isize = ins->oprs[0].offset - offset - isize;       /* isize is now the delta */
 | |
|     if (isize >= -128L && isize <= 127L)
 | |
|         return 1;               /* it is byte size */
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int32_t assemble(int32_t segment, int32_t offset, int bits, uint32_t cp,
 | |
|               insn * instruction, struct ofmt *output, efunc error,
 | |
|               ListGen * listgen)
 | |
| {
 | |
|     const struct itemplate *temp;
 | |
|     int j;
 | |
|     int size_prob;
 | |
|     int32_t insn_end;
 | |
|     int32_t itimes;
 | |
|     int32_t start = offset;
 | |
|     int32_t wsize = 0;             /* size for DB etc. */
 | |
| 
 | |
|     errfunc = error;            /* to pass to other functions */
 | |
|     cpu = cp;
 | |
|     outfmt = output;            /* likewise */
 | |
|     list = listgen;             /* and again */
 | |
| 
 | |
|     switch (instruction->opcode) {
 | |
|     case -1:
 | |
|         return 0;
 | |
|     case I_DB:
 | |
|         wsize = 1;
 | |
|         break;
 | |
|     case I_DW:
 | |
|         wsize = 2;
 | |
|         break;
 | |
|     case I_DD:
 | |
|         wsize = 4;
 | |
|         break;
 | |
|     case I_DQ:
 | |
|         wsize = 8;
 | |
|         break;
 | |
|     case I_DT:
 | |
|         wsize = 10;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     if (wsize) {
 | |
|         extop *e;
 | |
|         int32_t t = instruction->times;
 | |
|         if (t < 0)
 | |
|             errfunc(ERR_PANIC,
 | |
|                     "instruction->times < 0 (%ld) in assemble()", t);
 | |
| 
 | |
|         while (t--) {           /* repeat TIMES times */
 | |
|             for (e = instruction->eops; e; e = e->next) {
 | |
|                 if (e->type == EOT_DB_NUMBER) {
 | |
|                     if (wsize == 1) {
 | |
|                         if (e->segment != NO_SEG)
 | |
|                             errfunc(ERR_NONFATAL,
 | |
|                                     "one-byte relocation attempted");
 | |
|                         else {
 | |
|                             uint8_t out_byte = e->offset;
 | |
|                             out(offset, segment, &out_byte,
 | |
|                                 OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                         }
 | |
|                     } else if (wsize > 8) {
 | |
|                         errfunc(ERR_NONFATAL, "integer supplied to a DT"
 | |
|                                 " instruction");
 | |
|                     } else
 | |
|                         out(offset, segment, &e->offset,
 | |
|                             OUT_ADDRESS + wsize, e->segment, e->wrt);
 | |
|                     offset += wsize;
 | |
|                 } else if (e->type == EOT_DB_STRING) {
 | |
|                     int align;
 | |
| 
 | |
|                     out(offset, segment, e->stringval,
 | |
|                         OUT_RAWDATA + e->stringlen, NO_SEG, NO_SEG);
 | |
|                     align = e->stringlen % wsize;
 | |
| 
 | |
|                     if (align) {
 | |
|                         align = wsize - align;
 | |
|                         out(offset, segment, "\0\0\0\0\0\0\0\0",
 | |
|                             OUT_RAWDATA + align, NO_SEG, NO_SEG);
 | |
|                     }
 | |
|                     offset += e->stringlen + align;
 | |
|                 }
 | |
|             }
 | |
|             if (t > 0 && t == instruction->times - 1) {
 | |
|                 /*
 | |
|                  * Dummy call to list->output to give the offset to the
 | |
|                  * listing module.
 | |
|                  */
 | |
|                 list->output(offset, NULL, OUT_RAWDATA);
 | |
|                 list->uplevel(LIST_TIMES);
 | |
|             }
 | |
|         }
 | |
|         if (instruction->times > 1)
 | |
|             list->downlevel(LIST_TIMES);
 | |
|         return offset - start;
 | |
|     }
 | |
| 
 | |
|     if (instruction->opcode == I_INCBIN) {
 | |
|         static char fname[FILENAME_MAX];
 | |
|         FILE *fp;
 | |
|         int32_t len;
 | |
|         char *prefix = "", *combine;
 | |
|         char **pPrevPath = NULL;
 | |
| 
 | |
|         len = FILENAME_MAX - 1;
 | |
|         if (len > instruction->eops->stringlen)
 | |
|             len = instruction->eops->stringlen;
 | |
|         strncpy(fname, instruction->eops->stringval, len);
 | |
|         fname[len] = '\0';
 | |
| 
 | |
|         while (1) {         /* added by alexfru: 'incbin' uses include paths */
 | |
|             combine = nasm_malloc(strlen(prefix) + len + 1);
 | |
|             strcpy(combine, prefix);
 | |
|             strcat(combine, fname);
 | |
| 
 | |
|             if ((fp = fopen(combine, "rb")) != NULL) {
 | |
|                 nasm_free(combine);
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             nasm_free(combine);
 | |
|             pPrevPath = pp_get_include_path_ptr(pPrevPath);
 | |
|             if (pPrevPath == NULL)
 | |
|                 break;
 | |
|             prefix = *pPrevPath;
 | |
|         }
 | |
| 
 | |
|         if (fp == NULL)
 | |
|             error(ERR_NONFATAL, "`incbin': unable to open file `%s'",
 | |
|                   fname);
 | |
|         else if (fseek(fp, 0L, SEEK_END) < 0)
 | |
|             error(ERR_NONFATAL, "`incbin': unable to seek on file `%s'",
 | |
|                   fname);
 | |
|         else {
 | |
|             static char buf[2048];
 | |
|             int32_t t = instruction->times;
 | |
|             int32_t base = 0;
 | |
| 
 | |
|             len = ftell(fp);
 | |
|             if (instruction->eops->next) {
 | |
|                 base = instruction->eops->next->offset;
 | |
|                 len -= base;
 | |
|                 if (instruction->eops->next->next &&
 | |
|                     len > instruction->eops->next->next->offset)
 | |
|                     len = instruction->eops->next->next->offset;
 | |
|             }
 | |
|             /*
 | |
|              * Dummy call to list->output to give the offset to the
 | |
|              * listing module.
 | |
|              */
 | |
|             list->output(offset, NULL, OUT_RAWDATA);
 | |
|             list->uplevel(LIST_INCBIN);
 | |
|             while (t--) {
 | |
|                 int32_t l;
 | |
| 
 | |
|                 fseek(fp, base, SEEK_SET);
 | |
|                 l = len;
 | |
|                 while (l > 0) {
 | |
|                     int32_t m =
 | |
|                         fread(buf, 1, (l > sizeof(buf) ? sizeof(buf) : l),
 | |
|                               fp);
 | |
|                     if (!m) {
 | |
|                         /*
 | |
|                          * This shouldn't happen unless the file
 | |
|                          * actually changes while we are reading
 | |
|                          * it.
 | |
|                          */
 | |
|                         error(ERR_NONFATAL,
 | |
|                               "`incbin': unexpected EOF while"
 | |
|                               " reading file `%s'", fname);
 | |
|                         t = 0;  /* Try to exit cleanly */
 | |
|                         break;
 | |
|                     }
 | |
|                     out(offset, segment, buf, OUT_RAWDATA + m,
 | |
|                         NO_SEG, NO_SEG);
 | |
|                     l -= m;
 | |
|                 }
 | |
|             }
 | |
|             list->downlevel(LIST_INCBIN);
 | |
|             if (instruction->times > 1) {
 | |
|                 /*
 | |
|                  * Dummy call to list->output to give the offset to the
 | |
|                  * listing module.
 | |
|                  */
 | |
|                 list->output(offset, NULL, OUT_RAWDATA);
 | |
|                 list->uplevel(LIST_TIMES);
 | |
|                 list->downlevel(LIST_TIMES);
 | |
|             }
 | |
|             fclose(fp);
 | |
|             return instruction->times * len;
 | |
|         }
 | |
|         return 0;               /* if we're here, there's an error */
 | |
|     }
 | |
| 
 | |
|     size_prob = FALSE;
 | |
|     
 | |
|     for (temp = nasm_instructions[instruction->opcode]; temp->opcode != -1; temp++){ 
 | |
|         int m = matches(temp, instruction, bits);
 | |
|         
 | |
|         if (m == 99)
 | |
|             m += jmp_match(segment, offset, bits, instruction, temp->code);
 | |
| 
 | |
|         if (m == 100) {         /* matches! */
 | |
|             const char *codes = temp->code;
 | |
|             int32_t insn_size = calcsize(segment, offset, bits,
 | |
|                                       instruction, codes);
 | |
|             itimes = instruction->times;
 | |
|             if (insn_size < 0)  /* shouldn't be, on pass two */
 | |
|                 error(ERR_PANIC, "errors made it through from pass one");
 | |
|             else
 | |
|                 while (itimes--) {
 | |
|                     for (j = 0; j < instruction->nprefix; j++) {
 | |
|                         uint8_t c = 0;
 | |
|                         switch (instruction->prefixes[j]) {
 | |
|                         case P_LOCK:
 | |
|                             c = 0xF0;
 | |
|                             break;
 | |
|                         case P_REPNE:
 | |
|                         case P_REPNZ:
 | |
|                             c = 0xF2;
 | |
|                             break;
 | |
|                         case P_REPE:
 | |
|                         case P_REPZ:
 | |
|                         case P_REP:
 | |
|                             c = 0xF3;
 | |
|                             break;
 | |
|                         case R_CS:
 | |
|                             if (bits == 64) {
 | |
|                                 error(ERR_WARNING,
 | |
|                                       "cs segment base ignored in 64-bit mode");
 | |
|                             }
 | |
|                             c = 0x2E;
 | |
|                             break;
 | |
|                         case R_DS:
 | |
|                             if (bits == 64) {
 | |
|                                 error(ERR_WARNING,
 | |
|                                       "ds segment base ignored in 64-bit mode");
 | |
|                             }
 | |
|                             c = 0x3E;
 | |
|                             break;
 | |
|                         case R_ES:
 | |
|                            if (bits == 64) {
 | |
|                                 error(ERR_WARNING,
 | |
|                                       "es segment base ignored in 64-bit mode");
 | |
|                            }
 | |
|                             c = 0x26;
 | |
|                             break;
 | |
|                         case R_FS:
 | |
|                             c = 0x64;
 | |
|                             break;
 | |
|                         case R_GS:
 | |
|                             c = 0x65;
 | |
|                             break;
 | |
|                         case R_SS:
 | |
|                             if (bits == 64) {
 | |
|                                 error(ERR_WARNING,
 | |
|                                       "ss segment base ignored in 64-bit mode");
 | |
|                             }
 | |
|                             c = 0x36;
 | |
|                             break;
 | |
|                         case R_SEGR6:
 | |
|                         case R_SEGR7:
 | |
|                             error(ERR_NONFATAL,
 | |
|                                   "segr6 and segr7 cannot be used as prefixes");
 | |
|                             break;
 | |
|                         case P_A16:
 | |
|                             if (bits == 64) {
 | |
|                                 error(ERR_NONFATAL,
 | |
| 				      "16-bit addressing is not supported "
 | |
| 				      "in 64-bit mode");
 | |
|                                 break;
 | |
|                             }
 | |
|                             if (bits != 16)
 | |
|                                 c = 0x67;
 | |
|                             break;
 | |
|                         case P_A32:
 | |
|                             if (bits != 32)
 | |
|                                 c = 0x67;
 | |
|                             break;
 | |
|                         case P_O16:
 | |
|                             if (bits != 16)
 | |
|                                 c = 0x66;
 | |
|                             break;
 | |
|                         case P_O32:
 | |
|                             if (bits == 16)
 | |
|                                 c = 0x66;
 | |
|                             break;
 | |
|                         default:
 | |
|                             error(ERR_PANIC, "invalid instruction prefix");
 | |
|                         }
 | |
|                         if (c != 0) {
 | |
|                             out(offset, segment, &c, OUT_RAWDATA + 1,
 | |
|                                 NO_SEG, NO_SEG);
 | |
|                             offset++;
 | |
|                         }
 | |
|                     }                    
 | |
|                     insn_end = offset + insn_size;
 | |
|                     gencode(segment, offset, bits, instruction, codes,
 | |
|                             insn_end);
 | |
|                     offset += insn_size;
 | |
|                     if (itimes > 0 && itimes == instruction->times - 1) {
 | |
|                         /*
 | |
|                          * Dummy call to list->output to give the offset to the
 | |
|                          * listing module.
 | |
|                          */
 | |
|                         list->output(offset, NULL, OUT_RAWDATA);
 | |
|                         list->uplevel(LIST_TIMES);
 | |
|                     }
 | |
|                 }
 | |
|             if (instruction->times > 1)
 | |
|                 list->downlevel(LIST_TIMES);
 | |
|             return offset - start;
 | |
|         } else if (m > 0 && m > size_prob) {
 | |
|             size_prob = m;
 | |
|         }
 | |
| //        temp++;
 | |
|     }
 | |
| 
 | |
|     if (temp->opcode == -1) {   /* didn't match any instruction */
 | |
| 	switch (size_prob) {
 | |
| 	case 1:
 | |
| 	    error(ERR_NONFATAL, "operation size not specified");
 | |
| 	    break;
 | |
| 	case 2:
 | |
|             error(ERR_NONFATAL, "mismatch in operand sizes");
 | |
| 	    break;
 | |
| 	case 3:
 | |
|             error(ERR_NONFATAL, "no instruction for this cpu level");
 | |
| 	    break;
 | |
| 	case 4:
 | |
|             error(ERR_NONFATAL, "instruction not supported in 64-bit mode");
 | |
| 	    break;
 | |
| 	default:
 | |
|             error(ERR_NONFATAL,
 | |
|                   "invalid combination of opcode and operands");
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int32_t insn_size(int32_t segment, int32_t offset, int bits, uint32_t cp,
 | |
|                insn * instruction, efunc error)
 | |
| {
 | |
|     const struct itemplate *temp;
 | |
| 
 | |
|     errfunc = error;            /* to pass to other functions */
 | |
|     cpu = cp;
 | |
| 
 | |
|     if (instruction->opcode == -1)
 | |
|         return 0;
 | |
| 
 | |
|     if (instruction->opcode == I_DB ||
 | |
|         instruction->opcode == I_DW ||
 | |
|         instruction->opcode == I_DD ||
 | |
|         instruction->opcode == I_DQ || instruction->opcode == I_DT) {
 | |
|         extop *e;
 | |
|         int32_t isize, osize, wsize = 0;   /* placate gcc */
 | |
| 
 | |
|         isize = 0;
 | |
|         switch (instruction->opcode) {
 | |
|         case I_DB:
 | |
|             wsize = 1;
 | |
|             break;
 | |
|         case I_DW:
 | |
|             wsize = 2;
 | |
|             break;
 | |
|         case I_DD:
 | |
|             wsize = 4;
 | |
|             break;
 | |
|         case I_DQ:
 | |
|             wsize = 8;
 | |
|             break;
 | |
|         case I_DT:
 | |
|             wsize = 10;
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         for (e = instruction->eops; e; e = e->next) {
 | |
|             int32_t align;
 | |
| 
 | |
|             osize = 0;
 | |
|             if (e->type == EOT_DB_NUMBER)
 | |
|                 osize = 1;
 | |
|             else if (e->type == EOT_DB_STRING)
 | |
|                 osize = e->stringlen;
 | |
| 
 | |
|             align = (-osize) % wsize;
 | |
|             if (align < 0)
 | |
|                 align += wsize;
 | |
|             isize += osize + align;
 | |
|         }
 | |
|         return isize * instruction->times;
 | |
|     }
 | |
| 
 | |
|     if (instruction->opcode == I_INCBIN) {
 | |
|         char fname[FILENAME_MAX];
 | |
|         FILE *fp;
 | |
|         int32_t len;
 | |
|         char *prefix = "", *combine;
 | |
|         char **pPrevPath = NULL;
 | |
| 
 | |
|         len = FILENAME_MAX - 1;
 | |
|         if (len > instruction->eops->stringlen)
 | |
|             len = instruction->eops->stringlen;
 | |
|         strncpy(fname, instruction->eops->stringval, len);
 | |
|         fname[len] = '\0';
 | |
| 
 | |
|         while (1) {             /* added by alexfru: 'incbin' uses include paths */
 | |
|             combine = nasm_malloc(strlen(prefix) + len + 1);
 | |
|             strcpy(combine, prefix);
 | |
|             strcat(combine, fname);
 | |
| 
 | |
|             if ((fp = fopen(combine, "rb")) != NULL) {
 | |
|                 nasm_free(combine);
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             nasm_free(combine);
 | |
|             pPrevPath = pp_get_include_path_ptr(pPrevPath);
 | |
|             if (pPrevPath == NULL)
 | |
|                 break;
 | |
|             prefix = *pPrevPath;
 | |
|         }
 | |
| 
 | |
|         if (fp == NULL)
 | |
|             error(ERR_NONFATAL, "`incbin': unable to open file `%s'",
 | |
|                   fname);
 | |
|         else if (fseek(fp, 0L, SEEK_END) < 0)
 | |
|             error(ERR_NONFATAL, "`incbin': unable to seek on file `%s'",
 | |
|                   fname);
 | |
|         else {
 | |
|             len = ftell(fp);
 | |
|             fclose(fp);
 | |
|             if (instruction->eops->next) {
 | |
|                 len -= instruction->eops->next->offset;
 | |
|                 if (instruction->eops->next->next &&
 | |
|                     len > instruction->eops->next->next->offset) {
 | |
|                     len = instruction->eops->next->next->offset;
 | |
|                 }
 | |
|             }
 | |
|             return instruction->times * len;
 | |
|         }
 | |
|         return 0;               /* if we're here, there's an error */
 | |
|     }
 | |
| 
 | |
|     for (temp = nasm_instructions[instruction->opcode]; temp->opcode != -1; temp++) {
 | |
|         int m = matches(temp, instruction, bits);
 | |
|         if (m == 99)
 | |
|             m += jmp_match(segment, offset, bits, instruction, temp->code);
 | |
| 
 | |
|         if (m == 100) {
 | |
|             /* we've matched an instruction. */
 | |
|             int32_t isize;
 | |
|             const char *codes = temp->code;
 | |
|             int j;
 | |
| 
 | |
|             isize = calcsize(segment, offset, bits, instruction, codes);
 | |
|             if (isize < 0)
 | |
|                 return -1;
 | |
|             for (j = 0; j < instruction->nprefix; j++) {
 | |
|                 if ((instruction->prefixes[j] != P_A16 &&
 | |
|                      instruction->prefixes[j] != P_O16 && bits == 16) ||
 | |
|                     (instruction->prefixes[j] != P_A32 &&
 | |
|                      instruction->prefixes[j] != P_O32 && bits >= 32)) {
 | |
|                     isize++;
 | |
|                 }
 | |
|             }
 | |
|             return isize * instruction->times;
 | |
|         }
 | |
|     }
 | |
|     return -1;                  /* didn't match any instruction */
 | |
| }
 | |
| 
 | |
| /* check that  opn[op]  is a signed byte of size 16 or 32,
 | |
| 					and return the signed value*/
 | |
| static int is_sbyte(insn * ins, int op, int size)
 | |
| {
 | |
|     int32_t v;
 | |
|     int ret;
 | |
| 
 | |
|     ret = !(ins->forw_ref && ins->oprs[op].opflags) &&  /* dead in the water on forward reference or External */
 | |
|         optimizing >= 0 &&
 | |
|         !(ins->oprs[op].type & STRICT) &&
 | |
|         ins->oprs[op].wrt == NO_SEG && ins->oprs[op].segment == NO_SEG;
 | |
| 
 | |
|     v = ins->oprs[op].offset;
 | |
|     if (size == 16)
 | |
|         v = (int16_t)v;    /* sign extend if 16 bits */
 | |
| 
 | |
|     return ret && v >= -128L && v <= 127L;
 | |
| }
 | |
| 
 | |
| static int32_t calcsize(int32_t segment, int32_t offset, int bits,
 | |
|                      insn * ins, const char *codes)
 | |
| {
 | |
|     int32_t length = 0;
 | |
|     uint8_t c;
 | |
|     int rex_mask = ~0;
 | |
|     ins->rex = 0;               /* Ensure REX is reset */
 | |
| 
 | |
|     (void)segment;              /* Don't warn that this parameter is unused */
 | |
|     (void)offset;               /* Don't warn that this parameter is unused */
 | |
| 
 | |
|     while (*codes)
 | |
|         switch (c = *codes++) {
 | |
|         case 01:
 | |
|         case 02:
 | |
|         case 03:
 | |
|             codes += c, length += c;
 | |
|             break;
 | |
|         case 04:
 | |
|         case 05:
 | |
|         case 06:
 | |
|         case 07:
 | |
|             length++;
 | |
|             break;
 | |
|         case 010:
 | |
|         case 011:
 | |
|         case 012:
 | |
| 	    ins->rex |=
 | |
| 		op_rexflags(&ins->oprs[c - 010], REX_B|REX_H|REX_P|REX_W);
 | |
|             codes++, length++;
 | |
|             break;
 | |
|         case 017:
 | |
|             length++;
 | |
|             break;
 | |
|         case 014:
 | |
|         case 015:
 | |
|         case 016:
 | |
|             length++;
 | |
|             break;
 | |
|         case 020:
 | |
|         case 021:
 | |
|         case 022:
 | |
|             length++;
 | |
|             break;
 | |
|         case 024:
 | |
|         case 025:
 | |
|         case 026:
 | |
|             length++;
 | |
|             break;
 | |
|         case 030:
 | |
|         case 031:
 | |
|         case 032:
 | |
|             length += 2;
 | |
|             break;
 | |
|         case 034:
 | |
|         case 035:
 | |
|         case 036:
 | |
|             if (ins->oprs[c - 034].type & (BITS16 | BITS32 | BITS64))
 | |
|                 length += (ins->oprs[c - 034].type & BITS16) ? 2 : 4;
 | |
|             else
 | |
|                 length += (bits == 16) ? 2 : 4;
 | |
|             break;
 | |
|         case 037:
 | |
|             length += 2;
 | |
|             break;
 | |
|         case 040:
 | |
|         case 041:
 | |
|         case 042:
 | |
|             length += 4;
 | |
|             break;
 | |
|         case 044:
 | |
|         case 045:
 | |
|         case 046:
 | |
|             length += ((ins->oprs[c - 044].addr_size ?
 | |
|                         ins->oprs[c - 044].addr_size : bits) >> 3);
 | |
|             break;
 | |
|         case 050:
 | |
|         case 051:
 | |
|         case 052:
 | |
|             length++;
 | |
|             break;
 | |
|         case 054:
 | |
|         case 055:
 | |
|         case 056:
 | |
|             length += 8; /* MOV reg64/imm */
 | |
|             break;
 | |
|         case 060:
 | |
|         case 061:
 | |
|         case 062:
 | |
|             length += 2;
 | |
|             break;
 | |
|         case 064:
 | |
|         case 065:
 | |
|         case 066:
 | |
|             if (ins->oprs[c - 064].type & (BITS16 | BITS32 | BITS64))
 | |
|                 length += (ins->oprs[c - 064].type & BITS16) ? 2 : 4;
 | |
|             else
 | |
|                 length += (bits == 16) ? 2 : 4;
 | |
|             break;
 | |
|         case 070:
 | |
|         case 071:
 | |
|         case 072:
 | |
|             length += 4;
 | |
|             break;
 | |
|         case 0130:
 | |
|         case 0131:
 | |
|         case 0132:
 | |
|             length += is_sbyte(ins, c - 0130, 16) ? 1 : 2;
 | |
|             break;
 | |
|         case 0133:
 | |
|         case 0134:
 | |
|         case 0135:
 | |
|             codes += 2;
 | |
|             length++;
 | |
|             break;
 | |
|         case 0140:
 | |
|         case 0141:
 | |
|         case 0142:
 | |
|             length += is_sbyte(ins, c - 0140, 32) ? 1 : 4;
 | |
|             break;
 | |
|         case 0143:
 | |
|         case 0144:
 | |
|         case 0145:
 | |
|             codes += 2;
 | |
|             length++;
 | |
|             break;
 | |
|         case 0300:
 | |
|         case 0301:
 | |
|         case 0302:         
 | |
|             length += chsize(&ins->oprs[c - 0300], bits);
 | |
|             break;
 | |
|         case 0310:
 | |
|             length += (bits != 16);
 | |
|             break;
 | |
|         case 0311:
 | |
|             length += (bits != 32);
 | |
|             break;
 | |
|         case 0312:
 | |
|             break;     
 | |
|         case 0313:
 | |
|             break;
 | |
|         case 0320:
 | |
|             length += (bits != 16);
 | |
|             break;
 | |
|         case 0321:
 | |
|             length += (bits == 16);
 | |
|             break;
 | |
|         case 0322:
 | |
|             break;
 | |
|         case 0323:
 | |
|             rex_mask &= ~REX_W;
 | |
|             break;
 | |
|         case 0324:
 | |
| 	    ins->rex |= REX_W;
 | |
|             break;
 | |
|         case 0330:
 | |
|             codes++, length++;
 | |
|             break;
 | |
|         case 0331:
 | |
|         case 0332:
 | |
|             break;
 | |
|         case 0333:
 | |
|             length++;
 | |
|             break;
 | |
| 	case 0334:
 | |
| 	    assert_no_prefix(ins, P_LOCK);
 | |
| 	    ins->rex |= REX_L;
 | |
| 	    break;
 | |
|         case 0340:
 | |
|         case 0341:
 | |
|         case 0342:
 | |
|             if (ins->oprs[0].segment != NO_SEG)
 | |
|                 errfunc(ERR_NONFATAL, "attempt to reserve non-constant"
 | |
|                         " quantity of BSS space");
 | |
|             else
 | |
|                 length += ins->oprs[0].offset << (c - 0340);
 | |
|             break;
 | |
| 	case 0364:
 | |
| 	case 0365:
 | |
| 	    break;
 | |
|         case 0366:
 | |
|         case 0367:
 | |
| 	    length++;
 | |
| 	    break;
 | |
|         case 0370:
 | |
|         case 0371:
 | |
|         case 0372:
 | |
|             break;
 | |
|         case 0373:
 | |
|             length++;
 | |
|             break;
 | |
|         default:               /* can't do it by 'case' statements */
 | |
|             if (c >= 0100 && c <= 0277) {       /* it's an EA */
 | |
|                 ea ea_data;
 | |
|                 int rfield;
 | |
| 		int32_t rflags;
 | |
|                 ea_data.rex = 0;           /* Ensure ea.REX is initially 0 */
 | |
|                 
 | |
| 		if (c <= 0177) {
 | |
| 		    /* pick rfield from operand b */
 | |
| 		    rflags = regflag(&ins->oprs[c & 7]);
 | |
| 		    rfield = regvals[ins->oprs[c & 7].basereg];
 | |
| 		} else {
 | |
| 		    rflags = 0;
 | |
| 		    rfield = c & 7;
 | |
| 		}
 | |
| 
 | |
|                 if (!process_ea
 | |
|                     (&ins->oprs[(c >> 3) & 7], &ea_data, bits,
 | |
|                      rfield, rflags, ins->forw_ref)) {
 | |
|                     errfunc(ERR_NONFATAL, "invalid effective address");
 | |
|                     return -1;
 | |
|                 } else {
 | |
| 		    ins->rex |= ea_data.rex;
 | |
|                     length += ea_data.size;
 | |
|                 }
 | |
|             } else
 | |
|                 errfunc(ERR_PANIC, "internal instruction table corrupt"
 | |
|                         ": instruction code 0x%02X given", c);
 | |
|         }
 | |
| 
 | |
|     ins->rex &= rex_mask;
 | |
|     if (ins->rex & REX_REAL) {
 | |
| 	if (ins->rex & REX_H) {
 | |
| 	    errfunc(ERR_NONFATAL, "cannot use high register in rex instruction");
 | |
| 	    return -1;
 | |
| 	} else if (bits == 64 ||
 | |
| 		   ((ins->rex & REX_L) &&
 | |
| 		    !(ins->rex & (REX_P|REX_W|REX_X|REX_B)) &&
 | |
| 		    cpu >= IF_X86_64)) {
 | |
| 	    length++;
 | |
| 	} else {
 | |
| 	  errfunc(ERR_NONFATAL, "invalid operands in non-64-bit mode");
 | |
| 	  return -1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return length;
 | |
| }
 | |
| 
 | |
| #define EMIT_REX()							\
 | |
|     if((ins->rex & REX_REAL) && (bits == 64)) {				\
 | |
| 	ins->rex = (ins->rex & REX_REAL)|REX_P;				\
 | |
| 	out(offset, segment, &ins->rex, OUT_RAWDATA+1, NO_SEG, NO_SEG); \
 | |
| 	ins->rex = 0;							\
 | |
| 	offset += 1; \
 | |
|     }
 | |
| 
 | |
| static void gencode(int32_t segment, int32_t offset, int bits,
 | |
|                     insn * ins, const char *codes, int32_t insn_end)
 | |
| {
 | |
|     static char condval[] = {   /* conditional opcodes */
 | |
|         0x7, 0x3, 0x2, 0x6, 0x2, 0x4, 0xF, 0xD, 0xC, 0xE, 0x6, 0x2,
 | |
|         0x3, 0x7, 0x3, 0x5, 0xE, 0xC, 0xD, 0xF, 0x1, 0xB, 0x9, 0x5,
 | |
|         0x0, 0xA, 0xA, 0xB, 0x8, 0x4
 | |
|     };
 | |
|     uint8_t c;
 | |
|     uint8_t bytes[4];
 | |
|     int32_t size;
 | |
|     int64_t data;
 | |
|     
 | |
|     while (*codes)
 | |
|         switch (c = *codes++) {
 | |
|         case 01:
 | |
|         case 02:
 | |
|         case 03:
 | |
| 	    EMIT_REX();
 | |
|             out(offset, segment, codes, OUT_RAWDATA + c, NO_SEG, NO_SEG);
 | |
|             codes += c;
 | |
|             offset += c;
 | |
|             break;
 | |
| 
 | |
|         case 04:
 | |
|         case 06:
 | |
|             switch (ins->oprs[0].basereg) {
 | |
|             case R_CS:
 | |
|                 bytes[0] = 0x0E + (c == 0x04 ? 1 : 0);
 | |
|                 break;
 | |
|             case R_DS:
 | |
|                 bytes[0] = 0x1E + (c == 0x04 ? 1 : 0);
 | |
|                 break;
 | |
|             case R_ES:
 | |
|                 bytes[0] = 0x06 + (c == 0x04 ? 1 : 0);
 | |
|                 break;
 | |
|             case R_SS:
 | |
|                 bytes[0] = 0x16 + (c == 0x04 ? 1 : 0);
 | |
|                 break;
 | |
|             default:
 | |
|                 errfunc(ERR_PANIC,
 | |
|                         "bizarre 8086 segment register received");
 | |
|             }
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset++;
 | |
|             break;
 | |
| 
 | |
|         case 05:
 | |
|         case 07:
 | |
|             switch (ins->oprs[0].basereg) {
 | |
|             case R_FS:
 | |
|                 bytes[0] = 0xA0 + (c == 0x05 ? 1 : 0);
 | |
|                 break;
 | |
|             case R_GS:
 | |
|                 bytes[0] = 0xA8 + (c == 0x05 ? 1 : 0);
 | |
|                 break;
 | |
|             default:
 | |
|                 errfunc(ERR_PANIC,
 | |
|                         "bizarre 386 segment register received");
 | |
|             }
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset++;
 | |
|             break;
 | |
| 
 | |
|         case 010:
 | |
|         case 011:
 | |
|         case 012:
 | |
| 	    EMIT_REX();
 | |
|             bytes[0] = *codes++ + ((regval(&ins->oprs[c - 010])) & 7);
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 017:
 | |
|             bytes[0] = 0;
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 014:
 | |
|         case 015:
 | |
|         case 016:
 | |
|             if (ins->oprs[c - 014].offset < -128
 | |
|                 || ins->oprs[c - 014].offset > 127) {
 | |
|                 errfunc(ERR_WARNING, "signed byte value exceeds bounds");
 | |
|             }
 | |
| 
 | |
|             if (ins->oprs[c - 014].segment != NO_SEG) {
 | |
|                 data = ins->oprs[c - 014].offset;
 | |
|                 out(offset, segment, &data, OUT_ADDRESS + 1,
 | |
|                     ins->oprs[c - 014].segment, ins->oprs[c - 014].wrt);
 | |
|             } else {
 | |
|                 bytes[0] = ins->oprs[c - 014].offset;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG,
 | |
|                     NO_SEG);
 | |
|             }
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 020:
 | |
|         case 021:
 | |
|         case 022:
 | |
|             if (ins->oprs[c - 020].offset < -256
 | |
|                 || ins->oprs[c - 020].offset > 255) {
 | |
|                 errfunc(ERR_WARNING, "byte value exceeds bounds");
 | |
|             }
 | |
|             if (ins->oprs[c - 020].segment != NO_SEG) {
 | |
|                 data = ins->oprs[c - 020].offset;
 | |
|                 out(offset, segment, &data, OUT_ADDRESS + 1,
 | |
|                     ins->oprs[c - 020].segment, ins->oprs[c - 020].wrt);
 | |
|             } else {
 | |
|                 bytes[0] = ins->oprs[c - 020].offset;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG,
 | |
|                     NO_SEG);
 | |
|             }
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 024:
 | |
|         case 025:
 | |
|         case 026:
 | |
|             if (ins->oprs[c - 024].offset < 0
 | |
|                 || ins->oprs[c - 024].offset > 255)
 | |
|                 errfunc(ERR_WARNING, "unsigned byte value exceeds bounds");
 | |
|             if (ins->oprs[c - 024].segment != NO_SEG) {
 | |
|                 data = ins->oprs[c - 024].offset;
 | |
|                 out(offset, segment, &data, OUT_ADDRESS + 1,
 | |
|                     ins->oprs[c - 024].segment, ins->oprs[c - 024].wrt);
 | |
|             } else {
 | |
|                 bytes[0] = ins->oprs[c - 024].offset;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG,
 | |
|                     NO_SEG);
 | |
|             }
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 030:
 | |
|         case 031:
 | |
|         case 032:
 | |
|             if (ins->oprs[c - 030].segment == NO_SEG &&
 | |
|                 ins->oprs[c - 030].wrt == NO_SEG &&
 | |
|                 (ins->oprs[c - 030].offset < -65536L ||
 | |
|                  ins->oprs[c - 030].offset > 65535L)) {
 | |
|                 errfunc(ERR_WARNING, "word value exceeds bounds");
 | |
|             }
 | |
|             data = ins->oprs[c - 030].offset;
 | |
|             out(offset, segment, &data, OUT_ADDRESS + 2,
 | |
|                 ins->oprs[c - 030].segment, ins->oprs[c - 030].wrt);
 | |
|             offset += 2;
 | |
|             break;
 | |
| 
 | |
|         case 034:
 | |
|         case 035:
 | |
|         case 036:
 | |
|             if (ins->oprs[c - 034].type & (BITS16 | BITS32))
 | |
|                 size = (ins->oprs[c - 034].type & BITS16) ? 2 : 4;
 | |
|             else
 | |
|                 size = (bits == 16) ? 2 : 4;
 | |
|             data = ins->oprs[c - 034].offset;
 | |
|             if (size == 2 && (data < -65536L || data > 65535L))
 | |
|                 errfunc(ERR_WARNING, "word value exceeds bounds");
 | |
|             out(offset, segment, &data, OUT_ADDRESS + size,
 | |
|                 ins->oprs[c - 034].segment, ins->oprs[c - 034].wrt);
 | |
|             offset += size;
 | |
|             break;
 | |
| 
 | |
|         case 037:
 | |
|             if (ins->oprs[0].segment == NO_SEG)
 | |
|                 errfunc(ERR_NONFATAL, "value referenced by FAR is not"
 | |
|                         " relocatable");
 | |
|             data = 0L;
 | |
|             out(offset, segment, &data, OUT_ADDRESS + 2,
 | |
|                 outfmt->segbase(1 + ins->oprs[0].segment),
 | |
|                 ins->oprs[0].wrt);
 | |
|             offset += 2;
 | |
|             break;
 | |
| 
 | |
|         case 040:
 | |
|         case 041:
 | |
|         case 042:
 | |
|             data = ins->oprs[c - 040].offset;
 | |
|             out(offset, segment, &data, OUT_ADDRESS + 4,
 | |
|                 ins->oprs[c - 040].segment, ins->oprs[c - 040].wrt);
 | |
|             offset += 4;
 | |
|             break;
 | |
| 
 | |
|         case 044:
 | |
|         case 045:
 | |
|         case 046:
 | |
|             data = ins->oprs[c - 044].offset;
 | |
|             size = ((ins->oprs[c - 044].addr_size ?
 | |
|                      ins->oprs[c - 044].addr_size : bits) >> 3);
 | |
|             if (size == 2 && (data < -65536L || data > 65535L))
 | |
|                 errfunc(ERR_WARNING, "word value exceeds bounds");
 | |
|             out(offset, segment, &data, OUT_ADDRESS + size,
 | |
|                 ins->oprs[c - 044].segment, ins->oprs[c - 044].wrt);
 | |
|             offset += size;
 | |
|             break;
 | |
| 
 | |
|         case 050:
 | |
|         case 051:
 | |
|         case 052:
 | |
|             if (ins->oprs[c - 050].segment != segment)
 | |
|                 errfunc(ERR_NONFATAL,
 | |
|                         "short relative jump outside segment");
 | |
|             data = ins->oprs[c - 050].offset - insn_end;
 | |
|             if (data > 127 || data < -128)
 | |
|                 errfunc(ERR_NONFATAL, "short jump is out of range");
 | |
|             bytes[0] = data;
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
|             
 | |
|         case 054:
 | |
|         case 055:
 | |
|         case 056:
 | |
|             data = (int64_t)ins->oprs[c - 054].offset;
 | |
|             out(offset, segment, &data, OUT_ADDRESS + 8,
 | |
|                 ins->oprs[c - 054].segment, ins->oprs[c - 054].wrt);
 | |
|             offset += 8;
 | |
|             break;
 | |
| 
 | |
|         case 060:
 | |
|         case 061:
 | |
|         case 062:
 | |
|             if (ins->oprs[c - 060].segment != segment) {
 | |
|                 data = ins->oprs[c - 060].offset;
 | |
|                 out(offset, segment, &data,
 | |
|                     OUT_REL2ADR + insn_end - offset,
 | |
|                     ins->oprs[c - 060].segment, ins->oprs[c - 060].wrt);
 | |
|             } else {
 | |
|                 data = ins->oprs[c - 060].offset - insn_end;
 | |
|                 out(offset, segment, &data,
 | |
|                     OUT_ADDRESS + 2, NO_SEG, NO_SEG);
 | |
|             }
 | |
|             offset += 2;
 | |
|             break;
 | |
| 
 | |
|         case 064:
 | |
|         case 065:
 | |
|         case 066:
 | |
|             if (ins->oprs[c - 064].type & (BITS16 | BITS32 | BITS64))
 | |
|                 size = (ins->oprs[c - 064].type & BITS16) ? 2 : 4;
 | |
|             else
 | |
|                 size = (bits == 16) ? 2 : 4;
 | |
|             if (ins->oprs[c - 064].segment != segment) {
 | |
|                 int32_t reltype = (size == 2 ? OUT_REL2ADR : OUT_REL4ADR);
 | |
|                 data = ins->oprs[c - 064].offset;
 | |
|                 out(offset, segment, &data, reltype + insn_end - offset,
 | |
|                     ins->oprs[c - 064].segment, ins->oprs[c - 064].wrt);
 | |
|             } else {
 | |
|                 data = ins->oprs[c - 064].offset - insn_end;
 | |
|                 out(offset, segment, &data,
 | |
|                     OUT_ADDRESS + size, NO_SEG, NO_SEG);
 | |
|             }
 | |
|             offset += size;
 | |
|             break;
 | |
| 
 | |
|         case 070:
 | |
|         case 071:
 | |
|         case 072:
 | |
|             if (ins->oprs[c - 070].segment != segment) {
 | |
|                 data = ins->oprs[c - 070].offset;
 | |
|                 out(offset, segment, &data,
 | |
|                     OUT_REL4ADR + insn_end - offset,
 | |
|                     ins->oprs[c - 070].segment, ins->oprs[c - 070].wrt);
 | |
|             } else {
 | |
|                 data = ins->oprs[c - 070].offset - insn_end;
 | |
|                 out(offset, segment, &data,
 | |
|                     OUT_ADDRESS + 4, NO_SEG, NO_SEG);
 | |
|             }
 | |
|             offset += 4;
 | |
|             break;
 | |
| 
 | |
|         case 0130:
 | |
|         case 0131:
 | |
|         case 0132:
 | |
|             data = ins->oprs[c - 0130].offset;
 | |
|             if (is_sbyte(ins, c - 0130, 16)) {
 | |
|                 bytes[0] = data;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG,
 | |
|                     NO_SEG);
 | |
|                 offset++;
 | |
|             } else {
 | |
|                 if (ins->oprs[c - 0130].segment == NO_SEG &&
 | |
|                     ins->oprs[c - 0130].wrt == NO_SEG &&
 | |
|                     (data < -65536L || data > 65535L)) {
 | |
|                     errfunc(ERR_WARNING, "word value exceeds bounds");
 | |
|                 }
 | |
|                 out(offset, segment, &data, OUT_ADDRESS + 2,
 | |
|                     ins->oprs[c - 0130].segment, ins->oprs[c - 0130].wrt);
 | |
|                 offset += 2;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case 0133:
 | |
|         case 0134:
 | |
|         case 0135:
 | |
| 	    EMIT_REX();
 | |
|             codes++;
 | |
|             bytes[0] = *codes++;
 | |
|             if (is_sbyte(ins, c - 0133, 16))
 | |
|                 bytes[0] |= 2;  /* s-bit */
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset++;
 | |
|             break;
 | |
| 
 | |
|         case 0140:
 | |
|         case 0141:
 | |
|         case 0142:
 | |
|             data = ins->oprs[c - 0140].offset;
 | |
|             if (is_sbyte(ins, c - 0140, 32)) {
 | |
|                 bytes[0] = data;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG,
 | |
|                     NO_SEG);
 | |
|                 offset++;
 | |
|             } else {
 | |
|                 out(offset, segment, &data, OUT_ADDRESS + 4,
 | |
|                     ins->oprs[c - 0140].segment, ins->oprs[c - 0140].wrt);
 | |
|                 offset += 4;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case 0143:
 | |
|         case 0144:
 | |
|         case 0145:
 | |
| 	    EMIT_REX();
 | |
|             codes++;
 | |
|             bytes[0] = *codes++;
 | |
|             if (is_sbyte(ins, c - 0143, 32))
 | |
|                 bytes[0] |= 2;  /* s-bit */
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset++;
 | |
|             break;
 | |
| 
 | |
|         case 0300:
 | |
|         case 0301:
 | |
|         case 0302:
 | |
|             if (chsize(&ins->oprs[c - 0300], bits)) {
 | |
|                 *bytes = 0x67;
 | |
|                 out(offset, segment, bytes,
 | |
|                     OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                 offset += 1;
 | |
|             } else
 | |
|                 offset += 0;
 | |
|             break;
 | |
| 
 | |
|         case 0310:
 | |
|             if (bits != 16) {
 | |
|                 *bytes = 0x67;
 | |
|                 out(offset, segment, bytes,
 | |
|                     OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                 offset += 1;
 | |
|             } else
 | |
|                 offset += 0;
 | |
|             break;
 | |
| 
 | |
|         case 0311:
 | |
|             if (bits != 32) {
 | |
|                 *bytes = 0x67;
 | |
|                 out(offset, segment, bytes,
 | |
|                     OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                 offset += 1;
 | |
|             } else
 | |
|                 offset += 0;
 | |
|             break;
 | |
| 
 | |
|         case 0312:
 | |
|             break;
 | |
| 
 | |
|         case 0313:
 | |
|             ins->rex = 0;
 | |
|             break;
 | |
|             
 | |
|         case 0320:
 | |
|             if (bits != 16) {
 | |
|                 *bytes = 0x66;
 | |
|                 out(offset, segment, bytes,
 | |
|                     OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                 offset += 1;
 | |
|             } else
 | |
|                 offset += 0;
 | |
|             break;
 | |
| 
 | |
|         case 0321:
 | |
|             if (bits == 16) {
 | |
|                 *bytes = 0x66;
 | |
|                 out(offset, segment, bytes,
 | |
|                     OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                 offset += 1;
 | |
|             } else
 | |
|                 offset += 0;
 | |
|             break;
 | |
| 
 | |
|         case 0322:
 | |
|         case 0323:     
 | |
|             break;                
 | |
|             
 | |
|         case 0324:
 | |
|             ins->rex |= REX_W;
 | |
|             break;
 | |
|         
 | |
|         case 0330:
 | |
|             *bytes = *codes++ ^ condval[ins->condition];
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 0331:
 | |
|         case 0332:
 | |
|             break;
 | |
| 
 | |
|         case 0333:
 | |
|             *bytes = 0xF3;
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 0334:
 | |
|             if (ins->rex & REX_R) {
 | |
|                 *bytes = 0xF0;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|                 offset += 1;
 | |
|             }
 | |
|             ins->rex &= ~(REX_L|REX_R);
 | |
|             break;
 | |
| 
 | |
|         case 0340:
 | |
|         case 0341:
 | |
|         case 0342:
 | |
|             if (ins->oprs[0].segment != NO_SEG)
 | |
|                 errfunc(ERR_PANIC, "non-constant BSS size in pass two");
 | |
|             else {
 | |
|                 int32_t size = ins->oprs[0].offset << (c - 0340);
 | |
|                 if (size > 0)
 | |
|                     out(offset, segment, NULL,
 | |
|                         OUT_RESERVE + size, NO_SEG, NO_SEG);
 | |
|                 offset += size;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
| 	case 0364:
 | |
| 	case 0365:
 | |
| 	    break;
 | |
| 
 | |
|         case 0366:
 | |
| 	case 0367:
 | |
|             *bytes = c - 0366 + 0x66;
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         case 0370:
 | |
|         case 0371:
 | |
|         case 0372:
 | |
|             break;
 | |
| 
 | |
|         case 0373:
 | |
|             *bytes = bits == 16 ? 3 : 5;
 | |
|             out(offset, segment, bytes, OUT_RAWDATA + 1, NO_SEG, NO_SEG);
 | |
|             offset += 1;
 | |
|             break;
 | |
| 
 | |
|         default:               /* can't do it by 'case' statements */
 | |
|             if (c >= 0100 && c <= 0277) {       /* it's an EA */
 | |
|                 ea ea_data;
 | |
|                 int rfield;
 | |
| 		int32_t rflags;
 | |
|                 uint8_t *p;
 | |
|                 int32_t s;
 | |
|                 
 | |
|                 if (c <= 0177) {
 | |
| 		    /* pick rfield from operand b */
 | |
| 		    rflags = regflag(&ins->oprs[c & 7]);
 | |
|                     rfield = regvals[ins->oprs[c & 7].basereg];
 | |
| 		} else {
 | |
| 		    /* rfield is constant */
 | |
| 		    rflags = 0;
 | |
|                     rfield = c & 7;
 | |
| 		}
 | |
| 
 | |
|                 if (!process_ea
 | |
|                     (&ins->oprs[(c >> 3) & 7], &ea_data, bits,
 | |
| 		     rfield, rflags, ins->forw_ref)) {
 | |
|                     errfunc(ERR_NONFATAL, "invalid effective address");
 | |
|                 }
 | |
|                             
 | |
|                 p = bytes;
 | |
|                 *p++ = ea_data.modrm;
 | |
|                 if (ea_data.sib_present)
 | |
|                     *p++ = ea_data.sib;
 | |
| 
 | |
|                 s = p - bytes;
 | |
|                 out(offset, segment, bytes, OUT_RAWDATA + s,
 | |
|                     NO_SEG, NO_SEG);
 | |
| 
 | |
|                 switch (ea_data.bytes) {
 | |
|                 case 0:
 | |
|                     break;
 | |
|                 case 1:
 | |
|                     if (ins->oprs[(c >> 3) & 7].segment != NO_SEG) {
 | |
|                         data = ins->oprs[(c >> 3) & 7].offset;
 | |
|                         out(offset, segment, &data, OUT_ADDRESS + 1,
 | |
|                             ins->oprs[(c >> 3) & 7].segment,
 | |
|                             ins->oprs[(c >> 3) & 7].wrt);
 | |
|                     } else {
 | |
|                         *bytes = ins->oprs[(c >> 3) & 7].offset;
 | |
|                         out(offset, segment, bytes, OUT_RAWDATA + 1,
 | |
|                             NO_SEG, NO_SEG);
 | |
|                     }
 | |
|                     s++;
 | |
|                     break;
 | |
|                 case 8:               
 | |
|                 case 2:
 | |
|                 case 4:
 | |
|                     data = ins->oprs[(c >> 3) & 7].offset;
 | |
|                     if (ea_data.rip && (ins->oprs[(c >> 3) & 7].segment == 0xFFFFFFFF))
 | |
|                             ea_data.rip = 0;    /* Make distinction between Symbols and Immediates */
 | |
|                     out(offset, segment, &data,   /* RIP = Relative, not Absolute */
 | |
|                         (ea_data.rip ?  OUT_REL4ADR : OUT_ADDRESS) + ea_data.bytes,
 | |
|                         ins->oprs[(c >> 3) & 7].segment,
 | |
|                         ins->oprs[(c >> 3) & 7].wrt);
 | |
|                     s += ea_data.bytes;
 | |
|                     break;
 | |
|                 }
 | |
|                 offset += s;
 | |
|             } else
 | |
|                 errfunc(ERR_PANIC, "internal instruction table corrupt"
 | |
|                         ": instruction code 0x%02X given", c);
 | |
|         }
 | |
| }
 | |
| 
 | |
| static int32_t regflag(const operand * o)
 | |
| {
 | |
|     if (o->basereg < EXPR_REG_START || o->basereg >= REG_ENUM_LIMIT) {
 | |
|         errfunc(ERR_PANIC, "invalid operand passed to regflag()");
 | |
|     }
 | |
|     return reg_flags[o->basereg];
 | |
| }
 | |
| 
 | |
| static int32_t regval(const operand * o)
 | |
| {
 | |
|     if (o->basereg < EXPR_REG_START || o->basereg >= REG_ENUM_LIMIT) {
 | |
|         errfunc(ERR_PANIC, "invalid operand passed to regval()");
 | |
|     }
 | |
|     return regvals[o->basereg];
 | |
| }
 | |
| 
 | |
| static int op_rexflags(const operand * o, int mask)
 | |
| {
 | |
|     int32_t flags;
 | |
|     int val;
 | |
| 
 | |
|     if (o->basereg < EXPR_REG_START || o->basereg >= REG_ENUM_LIMIT) {
 | |
|         errfunc(ERR_PANIC, "invalid operand passed to op_rexflags()");
 | |
|     }
 | |
| 
 | |
|     flags = reg_flags[o->basereg];
 | |
|     val = regvals[o->basereg];
 | |
| 
 | |
|     return rexflags(val, flags, mask);
 | |
| }
 | |
| 
 | |
| static int rexflags(int val, int32_t flags, int mask)
 | |
| {
 | |
|     int rex = 0;
 | |
| 
 | |
|     if (val >= 8)
 | |
| 	rex |= REX_B|REX_X|REX_R;
 | |
|     if (flags & BITS64)
 | |
| 	rex |= REX_W;
 | |
|     if (!(REG_HIGH & ~flags))	/* AH, CH, DH, BH */
 | |
| 	rex |= REX_H;
 | |
|     else if (!(REG8 & ~flags) && val >= 4) /* SPL, BPL, SIL, DIL */
 | |
| 	rex |= REX_P;
 | |
| 
 | |
|     return rex & mask;
 | |
| }
 | |
| 
 | |
| static int matches(const struct itemplate *itemp, insn * instruction, int bits)
 | |
| {
 | |
|     int i, size[3], asize, oprs, ret;
 | |
| 
 | |
|     ret = 100;
 | |
| 
 | |
|     /*
 | |
|      * Check the opcode
 | |
|      */
 | |
|     if (itemp->opcode != instruction->opcode)
 | |
|         return 0;
 | |
| 
 | |
|     /*
 | |
|      * Count the operands
 | |
|      */
 | |
|     if (itemp->operands != instruction->operands)
 | |
|         return 0;
 | |
| 
 | |
|     /*
 | |
|      * Check that no spurious colons or TOs are present
 | |
|      */
 | |
|     for (i = 0; i < itemp->operands; i++)
 | |
|         if (instruction->oprs[i].type & ~itemp->opd[i] & (COLON | TO))
 | |
|             return 0;
 | |
|             
 | |
|     /*
 | |
|      * Check that the operand flags all match up
 | |
|      */
 | |
|     for (i = 0; i < itemp->operands; i++)
 | |
|         if (itemp->opd[i] & ~instruction->oprs[i].type ||
 | |
|             ((itemp->opd[i] & SIZE_MASK) &&
 | |
|              ((itemp->opd[i] ^ instruction->oprs[i].type) & SIZE_MASK))) {
 | |
|             if ((itemp->opd[i] & ~instruction->oprs[i].type & ~SIZE_MASK) ||
 | |
|                 (instruction->oprs[i].type & SIZE_MASK))
 | |
|                 return 0;
 | |
|             else
 | |
|                 return 1;
 | |
|         }
 | |
| 
 | |
|     /*
 | |
|      * Check operand sizes
 | |
|      */
 | |
|     if (itemp->flags & IF_ARMASK) {
 | |
|         size[0] = size[1] = size[2] = 0;
 | |
| 
 | |
|         switch (itemp->flags & IF_ARMASK) {
 | |
|         case IF_AR0:
 | |
|             i = 0;
 | |
|             break;
 | |
|         case IF_AR1:
 | |
|             i = 1;
 | |
|             break;
 | |
|         case IF_AR2:
 | |
|             i = 2;
 | |
|             break;
 | |
|         default:
 | |
|             break;              /* Shouldn't happen */
 | |
|         }
 | |
|         if (itemp->flags & IF_SB) {
 | |
|             size[i] = BITS8;
 | |
|         } else if (itemp->flags & IF_SW) {
 | |
|             size[i] = BITS16;
 | |
|         } else if (itemp->flags & IF_SD) {
 | |
|             size[i] = BITS32;
 | |
|         } else if (itemp->flags & IF_SQ) {
 | |
|             size[i] = BITS64;
 | |
|         }
 | |
|     } else {
 | |
|         asize = 0;
 | |
|         if (itemp->flags & IF_SB) {
 | |
|             asize = BITS8;
 | |
|             oprs = itemp->operands;
 | |
|         } else if (itemp->flags & IF_SW) {
 | |
|             asize = BITS16;
 | |
|             oprs = itemp->operands;
 | |
|         } else if (itemp->flags & IF_SD) {
 | |
|             asize = BITS32;
 | |
|             oprs = itemp->operands;
 | |
|         } else if (itemp->flags & IF_SQ) {
 | |
|             asize = BITS64;
 | |
|             oprs = itemp->operands;
 | |
|         }
 | |
|         size[0] = size[1] = size[2] = asize;
 | |
|     }
 | |
|     
 | |
|     if (itemp->flags & (IF_SM | IF_SM2)) {
 | |
|         oprs = (itemp->flags & IF_SM2 ? 2 : itemp->operands);
 | |
|         asize = 0;
 | |
|         for (i = 0; i < oprs; i++) {
 | |
|             if ((asize = itemp->opd[i] & SIZE_MASK) != 0) {
 | |
|                 int j;
 | |
|                 for (j = 0; j < oprs; j++)
 | |
|                     size[j] = asize;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     } else {
 | |
|         oprs = itemp->operands;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < itemp->operands; i++) {
 | |
|         if (!(itemp->opd[i] & SIZE_MASK) &&
 | |
|             (instruction->oprs[i].type & SIZE_MASK & ~size[i]))
 | |
|             return 2;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Check template is okay at the set cpu level
 | |
|      */
 | |
|     if (((itemp->flags & IF_PLEVEL) > cpu))
 | |
|         return 3;
 | |
|         
 | |
|     /*
 | |
|      * Check if instruction is available in long mode
 | |
|      */
 | |
|     if ((itemp->flags & IF_NOLONG) && (bits == 64))
 | |
|         return 4;
 | |
| 
 | |
|     /*
 | |
|      * Check if special handling needed for Jumps
 | |
|      */
 | |
|     if ((uint8_t)(itemp->code[0]) >= 0370)
 | |
|         return 99;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static ea *process_ea(operand * input, ea * output, int addrbits,
 | |
|                       int rfield, int32_t rflags, int forw_ref)
 | |
| {
 | |
|     output->rip = FALSE;
 | |
| 
 | |
|     /* REX flags for the rfield operand */
 | |
|     output->rex |= rexflags(rfield, rflags, REX_R|REX_P|REX_W|REX_H);
 | |
| 
 | |
|     if (!(REGISTER & ~input->type)) {   /* register direct */
 | |
|         int i;
 | |
| 	int32_t f;
 | |
| 
 | |
|         if (input->basereg < EXPR_REG_START /* Verify as Register */
 | |
|             || input->basereg >= REG_ENUM_LIMIT)
 | |
|             return NULL;
 | |
| 	f = regflag(input);
 | |
|         i = regvals[input->basereg];
 | |
| 
 | |
| 	if (REG_EA & ~f)
 | |
| 	    return NULL;	/* Invalid EA register */
 | |
| 	
 | |
| 	output->rex |= op_rexflags(input, REX_B|REX_P|REX_W|REX_H);
 | |
| 
 | |
|         output->sib_present = FALSE;             /* no SIB necessary */
 | |
|         output->bytes = 0;  /* no offset necessary either */
 | |
|         output->modrm = 0xC0 | ((rfield & 7) << 3) | (i & 7);
 | |
|     } else {                    /* it's a memory reference */
 | |
|         if (input->basereg == -1
 | |
|             && (input->indexreg == -1 || input->scale == 0)) {
 | |
|             /* it's a pure offset */
 | |
|             if (input->addr_size)
 | |
|                 addrbits = input->addr_size;
 | |
| 
 | |
|             if (globalbits == 64 && (~input->type & IP_REL)) {
 | |
|               int scale, index, base;
 | |
|               output->sib_present = TRUE;
 | |
|               scale = 0;
 | |
|               index = 4;
 | |
|               base = 5;
 | |
|               output->sib = (scale << 6) | (index << 3) | base;
 | |
|               output->bytes = 4;
 | |
|               output->modrm = 4 | ((rfield & 7) << 3);
 | |
| 	      output->rip = FALSE;
 | |
|             } else {
 | |
|               output->sib_present = FALSE;
 | |
|               output->bytes = (addrbits != 16 ? 4 : 2);
 | |
|               output->modrm = (addrbits != 16 ? 5 : 6) | ((rfield & 7) << 3);
 | |
| 	      output->rip = globalbits == 64;
 | |
|             }
 | |
|         } else {                /* it's an indirection */
 | |
|             int i = input->indexreg, b = input->basereg, s = input->scale;
 | |
|             int32_t o = input->offset, seg = input->segment;
 | |
|             int hb = input->hintbase, ht = input->hinttype;
 | |
|             int t;
 | |
|             int it, bt;
 | |
| 	    int32_t ix, bx;	/* register flags */
 | |
| 
 | |
|             if (s == 0)
 | |
|                 i = -1;         /* make this easy, at least */
 | |
|                 
 | |
|             if (i >= EXPR_REG_START && i < REG_ENUM_LIMIT) {
 | |
|                 it = regvals[i];
 | |
| 		ix = reg_flags[i];
 | |
| 	    } else {
 | |
|                 it = -1;
 | |
| 		ix = 0;
 | |
| 	    }
 | |
|                 
 | |
| 	    if (b != -1 && b >= EXPR_REG_START && b < REG_ENUM_LIMIT) {
 | |
|                 bt = regvals[b];
 | |
| 		bx = reg_flags[b];
 | |
| 	    } else {
 | |
|                 bt = -1;
 | |
| 		bx = 0;
 | |
| 	    }
 | |
|                 
 | |
| 	    /* check for a 32/64-bit memory reference... */
 | |
| 	    if ((ix|bx) & (BITS32|BITS64)) {
 | |
|                 /* it must be a 32/64-bit memory reference. Firstly we have
 | |
|                  * to check that all registers involved are type E/Rxx. */
 | |
| 		int32_t sok = BITS32|BITS64;
 | |
| 
 | |
|                 if (it != -1) {
 | |
| 		    if (!(REG64 & ~ix) || !(REG32 & ~ix))
 | |
| 			sok &= ix;
 | |
| 		    else
 | |
| 			return NULL;
 | |
| 		}
 | |
| 
 | |
| 		if (bt != -1) {
 | |
| 		    if (REG_GPR & ~bx)
 | |
| 			return NULL; /* Invalid register */
 | |
| 		    if (~sok & bx & SIZE_MASK)
 | |
| 			return NULL; /* Invalid size */
 | |
| 		    sok &= ~bx;
 | |
| 		}
 | |
|                 
 | |
|                 /* While we're here, ensure the user didn't specify WORD. */
 | |
|                 if (input->addr_size == 16 ||
 | |
| 		    (input->addr_size == 32 && !(sok & BITS32)) ||
 | |
| 		    (input->addr_size == 64 && !(sok & BITS64)))
 | |
|                     return NULL;
 | |
| 
 | |
|                 /* now reorganize base/index */
 | |
|                 if (s == 1 && bt != it && bt != -1 && it != -1 &&
 | |
|                     ((hb == b && ht == EAH_NOTBASE)
 | |
|                      || (hb == i && ht == EAH_MAKEBASE))) {
 | |
| 		    /* swap if hints say so */
 | |
|                     t = bt, bt = it, it = t;
 | |
| 		    t = bx, bx = ix, ix = t;
 | |
| 		}
 | |
|                 if (bt == it)     /* convert EAX+2*EAX to 3*EAX */
 | |
|                     bt = -1, bx = 0, s++;
 | |
|                 if (bt == -1 && s == 1 && !(hb == it && ht == EAH_NOTBASE)) {
 | |
| 		    /* make single reg base, unless hint */
 | |
|                     bt = it, bx = ix, it = -1, ix = 0;
 | |
| 		}
 | |
|                 if (((s == 2 && it != REG_NUM_ESP
 | |
|                       && !(input->eaflags & EAF_TIMESTWO)) || s == 3
 | |
|                      || s == 5 || s == 9) && bt == -1)
 | |
|                     bt = it, bx = ix, s--; /* convert 3*EAX to EAX+2*EAX */
 | |
|                 if (it == -1 && (bt & 7) != REG_NUM_ESP
 | |
|                     && (input->eaflags & EAF_TIMESTWO))
 | |
|                     it = bt, ix = bx, bt = -1, bx = 0, s = 1;
 | |
|                 /* convert [NOSPLIT EAX] to sib format with 0x0 displacement */
 | |
|                 if (s == 1 && it == REG_NUM_ESP) {
 | |
| 		    /* swap ESP into base if scale is 1 */
 | |
|                     t = it, it = bt, bt = t;
 | |
| 		    t = ix, ix = bx, bx = t;
 | |
| 		}
 | |
|                 if (it == REG_NUM_ESP
 | |
|                     || (s != 1 && s != 2 && s != 4 && s != 8 && it != -1))
 | |
|                     return NULL;        /* wrong, for various reasons */
 | |
| 
 | |
| 		output->rex |= rexflags(it, ix, REX_X);
 | |
| 		output->rex |= rexflags(bt, bx, REX_B);
 | |
| 
 | |
|                 if (it == -1 && (bt & 7) != REG_NUM_ESP) {
 | |
| 		    /* no SIB needed */
 | |
|                     int mod, rm;
 | |
|                     
 | |
|                     if (bt == -1) {
 | |
|                         rm = 5;
 | |
|                         mod = 0;
 | |
|                     } else {
 | |
|                         rm = (bt & 7);
 | |
|                         if (rm != REG_NUM_EBP && o == 0 &&
 | |
|                                 seg == NO_SEG && !forw_ref &&
 | |
|                                 !(input->eaflags &
 | |
|                                   (EAF_BYTEOFFS | EAF_WORDOFFS)))
 | |
|                             mod = 0;
 | |
|                         else if (input->eaflags & EAF_BYTEOFFS ||
 | |
|                                  (o >= -128 && o <= 127 && seg == NO_SEG
 | |
|                                   && !forw_ref
 | |
|                                   && !(input->eaflags & EAF_WORDOFFS)))
 | |
|                             mod = 1;
 | |
|                         else
 | |
|                             mod = 2;
 | |
|                     }
 | |
| 
 | |
|                     output->sib_present = FALSE;
 | |
|                     output->bytes = (bt == -1 || mod == 2 ? 4 : mod);
 | |
|                     output->modrm = (mod << 6) | ((rfield & 7) << 3) | rm;
 | |
|                 } else {
 | |
| 		    /* we need a SIB */
 | |
|                     int mod, scale, index, base;
 | |
|                     
 | |
|                     if (it == -1)
 | |
|                         index = 4, s = 1;
 | |
|                     else
 | |
|                         index = (it & 7);
 | |
|                     
 | |
|                     switch (s) {
 | |
|                     case 1:
 | |
|                         scale = 0;
 | |
|                         break;
 | |
|                     case 2:
 | |
|                         scale = 1;
 | |
|                         break;
 | |
|                     case 4:
 | |
|                         scale = 2;
 | |
|                         break;
 | |
|                     case 8:
 | |
|                         scale = 3;
 | |
|                         break;
 | |
|                     default:   /* then what the smeg is it? */
 | |
|                         return NULL;    /* panic */
 | |
|                     }
 | |
|                     
 | |
|                     if (bt == -1) {
 | |
|                         base = 5;
 | |
|                         mod = 0;
 | |
|                     } else {
 | |
|                         base = (bt & 7);
 | |
|                         if (base != REG_NUM_EBP && o == 0 &&
 | |
|                                     seg == NO_SEG && !forw_ref &&
 | |
|                                     !(input->eaflags &
 | |
|                                       (EAF_BYTEOFFS | EAF_WORDOFFS)))
 | |
|                             mod = 0;
 | |
|                         else if (input->eaflags & EAF_BYTEOFFS ||
 | |
|                                  (o >= -128 && o <= 127 && seg == NO_SEG
 | |
|                                   && !forw_ref
 | |
|                                   && !(input->eaflags & EAF_WORDOFFS)))
 | |
|                             mod = 1;
 | |
|                         else
 | |
|                             mod = 2;
 | |
|                     }
 | |
| 
 | |
|                     output->sib_present = TRUE;
 | |
|                     output->bytes =  (bt == -1 || mod == 2 ? 4 : mod);
 | |
|                     output->modrm = (mod << 6) | ((rfield & 7) << 3) | 4;
 | |
|                     output->sib = (scale << 6) | (index << 3) | base;
 | |
|                 }
 | |
|             } else {            /* it's 16-bit */
 | |
|                 int mod, rm;
 | |
|                 
 | |
|                 /* check for 64-bit long mode */
 | |
|                 if (addrbits == 64)
 | |
|                     return NULL;
 | |
| 
 | |
|                 /* check all registers are BX, BP, SI or DI */
 | |
|                 if ((b != -1 && b != R_BP && b != R_BX && b != R_SI
 | |
|                      && b != R_DI) || (i != -1 && i != R_BP && i != R_BX
 | |
|                                        && i != R_SI && i != R_DI))
 | |
|                     return NULL;
 | |
| 
 | |
|                 /* ensure the user didn't specify DWORD/QWORD */
 | |
|                 if (input->addr_size == 32 || input->addr_size == 64)
 | |
|                     return NULL;
 | |
| 
 | |
|                 if (s != 1 && i != -1)
 | |
|                     return NULL;        /* no can do, in 16-bit EA */
 | |
|                 if (b == -1 && i != -1) {
 | |
|                     int tmp = b;
 | |
|                     b = i;
 | |
|                     i = tmp;
 | |
|                 }               /* swap */
 | |
|                 if ((b == R_SI || b == R_DI) && i != -1) {
 | |
|                     int tmp = b;
 | |
|                     b = i;
 | |
|                     i = tmp;
 | |
|                 }
 | |
|                 /* have BX/BP as base, SI/DI index */
 | |
|                 if (b == i)
 | |
|                     return NULL;        /* shouldn't ever happen, in theory */
 | |
|                 if (i != -1 && b != -1 &&
 | |
|                     (i == R_BP || i == R_BX || b == R_SI || b == R_DI))
 | |
|                     return NULL;        /* invalid combinations */
 | |
|                 if (b == -1)    /* pure offset: handled above */
 | |
|                     return NULL;        /* so if it gets to here, panic! */
 | |
| 
 | |
|                 rm = -1;
 | |
|                 if (i != -1)
 | |
|                     switch (i * 256 + b) {
 | |
|                     case R_SI * 256 + R_BX:
 | |
|                         rm = 0;
 | |
|                         break;
 | |
|                     case R_DI * 256 + R_BX:
 | |
|                         rm = 1;
 | |
|                         break;
 | |
|                     case R_SI * 256 + R_BP:
 | |
|                         rm = 2;
 | |
|                         break;
 | |
|                     case R_DI * 256 + R_BP:
 | |
|                         rm = 3;
 | |
|                         break;
 | |
|                 } else
 | |
|                     switch (b) {
 | |
|                     case R_SI:
 | |
|                         rm = 4;
 | |
|                         break;
 | |
|                     case R_DI:
 | |
|                         rm = 5;
 | |
|                         break;
 | |
|                     case R_BP:
 | |
|                         rm = 6;
 | |
|                         break;
 | |
|                     case R_BX:
 | |
|                         rm = 7;
 | |
|                         break;
 | |
|                     }
 | |
|                 if (rm == -1)   /* can't happen, in theory */
 | |
|                     return NULL;        /* so panic if it does */
 | |
| 
 | |
|                 if (o == 0 && seg == NO_SEG && !forw_ref && rm != 6 &&
 | |
|                     !(input->eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
 | |
|                     mod = 0;
 | |
|                 else if (input->eaflags & EAF_BYTEOFFS ||
 | |
|                          (o >= -128 && o <= 127 && seg == NO_SEG
 | |
|                           && !forw_ref
 | |
|                           && !(input->eaflags & EAF_WORDOFFS)))
 | |
|                     mod = 1;
 | |
|                 else
 | |
|                     mod = 2;
 | |
| 
 | |
|                 output->sib_present = FALSE;    /* no SIB - it's 16-bit */
 | |
|                 output->bytes = mod;    /* bytes of offset needed */
 | |
|                 output->modrm = (mod << 6) | ((rfield & 7) << 3) | rm;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     output->size = 1 + output->sib_present + output->bytes;
 | |
|     return output;
 | |
| }
 | |
| 
 | |
| static int chsize(operand * input, int addrbits)
 | |
| {
 | |
|     if (!(MEMORY & ~input->type)) {
 | |
|         int32_t i, b;
 | |
|         
 | |
|         if (input->indexreg < EXPR_REG_START /* Verify as Register */
 | |
|             || input->indexreg >= REG_ENUM_LIMIT)
 | |
|             i = 0;
 | |
|         else
 | |
|             i = reg_flags[input->indexreg];
 | |
| 
 | |
|         if (input->basereg < EXPR_REG_START /* Verify as Register */
 | |
|             || input->basereg >= REG_ENUM_LIMIT)
 | |
|             b = 0;
 | |
|         else
 | |
|             b = reg_flags[input->basereg];
 | |
| 
 | |
|         if (input->scale == 0)
 | |
|             i = 0;
 | |
| 
 | |
|         if (!i && !b)		/* pure offset */
 | |
|             return (input->addr_size != 0 && input->addr_size != addrbits);
 | |
|             
 | |
|         if (!(REG32 & ~i) || !(REG32 & ~b))
 | |
|             return (addrbits != 32);
 | |
|         else
 | |
|             return (addrbits == 32);
 | |
|     } else {
 | |
|         return 0;
 | |
|     }
 | |
| }
 |