diff options
| author | Mike Pall <mike> | 2009-12-08 19:46:35 +0100 |
|---|---|---|
| committer | Mike Pall <mike> | 2009-12-08 19:46:35 +0100 |
| commit | 55b16959717084884fd4a0cbae6d19e3786c20c7 (patch) | |
| tree | c8a07a43c13679751ed25a9d06796e9e7b2134a6 /dynasm | |
| download | luajit-2.0.0-beta1.tar.gz luajit-2.0.0-beta1.tar.bz2 luajit-2.0.0-beta1.zip | |
RELEASE LuaJIT-2.0.0-beta1v2.0.0-beta1
Diffstat (limited to 'dynasm')
| -rw-r--r-- | dynasm/dasm_proto.h | 69 | ||||
| -rw-r--r-- | dynasm/dasm_x86.h | 467 | ||||
| -rw-r--r-- | dynasm/dasm_x86.lua | 1799 | ||||
| -rw-r--r-- | dynasm/dynasm.lua | 1070 |
4 files changed, 3405 insertions, 0 deletions
diff --git a/dynasm/dasm_proto.h b/dynasm/dasm_proto.h new file mode 100644 index 00000000..94d9a9e2 --- /dev/null +++ b/dynasm/dasm_proto.h | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* | ||
| 2 | ** DynASM encoding engine prototypes. | ||
| 3 | ** Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 4 | ** Released under the MIT/X license. See dynasm.lua for full copyright notice. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _DASM_PROTO_H | ||
| 8 | #define _DASM_PROTO_H | ||
| 9 | |||
| 10 | #include <stddef.h> | ||
| 11 | #include <stdarg.h> | ||
| 12 | |||
| 13 | #define DASM_IDENT "DynASM 1.2.1" | ||
| 14 | #define DASM_VERSION 10201 /* 1.2.1 */ | ||
| 15 | |||
| 16 | #ifndef Dst_DECL | ||
| 17 | #define Dst_DECL dasm_State *Dst | ||
| 18 | #endif | ||
| 19 | |||
| 20 | #ifndef Dst_GET | ||
| 21 | #define Dst_GET (Dst) | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #ifndef DASM_FDEF | ||
| 25 | #define DASM_FDEF extern | ||
| 26 | #endif | ||
| 27 | |||
| 28 | |||
| 29 | /* Internal DynASM encoder state. */ | ||
| 30 | typedef struct dasm_State dasm_State; | ||
| 31 | |||
| 32 | /* Action list type. */ | ||
| 33 | typedef const unsigned char *dasm_ActList; | ||
| 34 | |||
| 35 | |||
| 36 | /* Initialize and free DynASM state. */ | ||
| 37 | DASM_FDEF void dasm_init(Dst_DECL, int maxsection); | ||
| 38 | DASM_FDEF void dasm_free(Dst_DECL); | ||
| 39 | |||
| 40 | /* Setup global array. Must be called before dasm_setup(). */ | ||
| 41 | DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl); | ||
| 42 | |||
| 43 | /* Grow PC label array. Can be called after dasm_setup(), too. */ | ||
| 44 | DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc); | ||
| 45 | |||
| 46 | /* Setup encoder. */ | ||
| 47 | DASM_FDEF void dasm_setup(Dst_DECL, dasm_ActList actionlist); | ||
| 48 | |||
| 49 | /* Feed encoder with actions. Calls are generated by pre-processor. */ | ||
| 50 | DASM_FDEF void dasm_put(Dst_DECL, int start, ...); | ||
| 51 | |||
| 52 | /* Link sections and return the resulting size. */ | ||
| 53 | DASM_FDEF int dasm_link(Dst_DECL, size_t *szp); | ||
| 54 | |||
| 55 | /* Encode sections into buffer. */ | ||
| 56 | DASM_FDEF int dasm_encode(Dst_DECL, void *buffer); | ||
| 57 | |||
| 58 | /* Get PC label offset. */ | ||
| 59 | DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc); | ||
| 60 | |||
| 61 | #ifdef DASM_CHECKS | ||
| 62 | /* Optional sanity checker to call between isolated encoding steps. */ | ||
| 63 | DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch); | ||
| 64 | #else | ||
| 65 | #define dasm_checkstep(a, b) 0 | ||
| 66 | #endif | ||
| 67 | |||
| 68 | |||
| 69 | #endif /* _DASM_PROTO_H */ | ||
diff --git a/dynasm/dasm_x86.h b/dynasm/dasm_x86.h new file mode 100644 index 00000000..dab33e5a --- /dev/null +++ b/dynasm/dasm_x86.h | |||
| @@ -0,0 +1,467 @@ | |||
| 1 | /* | ||
| 2 | ** DynASM x86 encoding engine. | ||
| 3 | ** Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 4 | ** Released under the MIT/X license. See dynasm.lua for full copyright notice. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <stddef.h> | ||
| 8 | #include <stdarg.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | |||
| 12 | #define DASM_ARCH "x86" | ||
| 13 | |||
| 14 | #ifndef DASM_EXTERN | ||
| 15 | #define DASM_EXTERN(a,b,c,d) 0 | ||
| 16 | #endif | ||
| 17 | |||
| 18 | /* Action definitions. DASM_STOP must be 255. */ | ||
| 19 | enum { | ||
| 20 | DASM_DISP = 233, | ||
| 21 | DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB, | ||
| 22 | DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC, | ||
| 23 | DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN, | ||
| 24 | DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP | ||
| 25 | }; | ||
| 26 | |||
| 27 | /* Maximum number of section buffer positions for a single dasm_put() call. */ | ||
| 28 | #define DASM_MAXSECPOS 25 | ||
| 29 | |||
| 30 | /* DynASM encoder status codes. Action list offset or number are or'ed in. */ | ||
| 31 | #define DASM_S_OK 0x00000000 | ||
| 32 | #define DASM_S_NOMEM 0x01000000 | ||
| 33 | #define DASM_S_PHASE 0x02000000 | ||
| 34 | #define DASM_S_MATCH_SEC 0x03000000 | ||
| 35 | #define DASM_S_RANGE_I 0x11000000 | ||
| 36 | #define DASM_S_RANGE_SEC 0x12000000 | ||
| 37 | #define DASM_S_RANGE_LG 0x13000000 | ||
| 38 | #define DASM_S_RANGE_PC 0x14000000 | ||
| 39 | #define DASM_S_RANGE_VREG 0x15000000 | ||
| 40 | #define DASM_S_UNDEF_L 0x21000000 | ||
| 41 | #define DASM_S_UNDEF_PC 0x22000000 | ||
| 42 | |||
| 43 | /* Macros to convert positions (8 bit section + 24 bit index). */ | ||
| 44 | #define DASM_POS2IDX(pos) ((pos)&0x00ffffff) | ||
| 45 | #define DASM_POS2BIAS(pos) ((pos)&0xff000000) | ||
| 46 | #define DASM_SEC2POS(sec) ((sec)<<24) | ||
| 47 | #define DASM_POS2SEC(pos) ((pos)>>24) | ||
| 48 | #define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) | ||
| 49 | |||
| 50 | /* Per-section structure. */ | ||
| 51 | typedef struct dasm_Section { | ||
| 52 | int *rbuf; /* Biased buffer pointer (negative section bias). */ | ||
| 53 | int *buf; /* True buffer pointer. */ | ||
| 54 | size_t bsize; /* Buffer size in bytes. */ | ||
| 55 | int pos; /* Biased buffer position. */ | ||
| 56 | int epos; /* End of biased buffer position - max single put. */ | ||
| 57 | int ofs; /* Byte offset into section. */ | ||
| 58 | } dasm_Section; | ||
| 59 | |||
| 60 | /* Core structure holding the DynASM encoding state. */ | ||
| 61 | struct dasm_State { | ||
| 62 | size_t psize; /* Allocated size of this structure. */ | ||
| 63 | dasm_ActList actionlist; /* Current actionlist pointer. */ | ||
| 64 | int *lglabels; /* Local/global chain/pos ptrs. */ | ||
| 65 | size_t lgsize; | ||
| 66 | int *pclabels; /* PC label chains/pos ptrs. */ | ||
| 67 | size_t pcsize; | ||
| 68 | void **globals; /* Array of globals (bias -10). */ | ||
| 69 | dasm_Section *section; /* Pointer to active section. */ | ||
| 70 | size_t codesize; /* Total size of all code sections. */ | ||
| 71 | int maxsection; /* 0 <= sectionidx < maxsection. */ | ||
| 72 | int status; /* Status code. */ | ||
| 73 | dasm_Section sections[1]; /* All sections. Alloc-extended. */ | ||
| 74 | }; | ||
| 75 | |||
| 76 | /* The size of the core structure depends on the max. number of sections. */ | ||
| 77 | #define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) | ||
| 78 | |||
| 79 | |||
| 80 | /* Initialize DynASM state. */ | ||
| 81 | void dasm_init(Dst_DECL, int maxsection) | ||
| 82 | { | ||
| 83 | dasm_State *D; | ||
| 84 | size_t psz = 0; | ||
| 85 | int i; | ||
| 86 | Dst_REF = NULL; | ||
| 87 | DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); | ||
| 88 | D = Dst_REF; | ||
| 89 | D->psize = psz; | ||
| 90 | D->lglabels = NULL; | ||
| 91 | D->lgsize = 0; | ||
| 92 | D->pclabels = NULL; | ||
| 93 | D->pcsize = 0; | ||
| 94 | D->globals = NULL; | ||
| 95 | D->maxsection = maxsection; | ||
| 96 | for (i = 0; i < maxsection; i++) { | ||
| 97 | D->sections[i].buf = NULL; /* Need this for pass3. */ | ||
| 98 | D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); | ||
| 99 | D->sections[i].bsize = 0; | ||
| 100 | D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | /* Free DynASM state. */ | ||
| 105 | void dasm_free(Dst_DECL) | ||
| 106 | { | ||
| 107 | dasm_State *D = Dst_REF; | ||
| 108 | int i; | ||
| 109 | for (i = 0; i < D->maxsection; i++) | ||
| 110 | if (D->sections[i].buf) | ||
| 111 | DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); | ||
| 112 | if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize); | ||
| 113 | if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize); | ||
| 114 | DASM_M_FREE(Dst, D, D->psize); | ||
| 115 | } | ||
| 116 | |||
| 117 | /* Setup global label array. Must be called before dasm_setup(). */ | ||
| 118 | void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) | ||
| 119 | { | ||
| 120 | dasm_State *D = Dst_REF; | ||
| 121 | D->globals = gl - 10; /* Negative bias to compensate for locals. */ | ||
| 122 | DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int)); | ||
| 123 | } | ||
| 124 | |||
| 125 | /* Grow PC label array. Can be called after dasm_setup(), too. */ | ||
| 126 | void dasm_growpc(Dst_DECL, unsigned int maxpc) | ||
| 127 | { | ||
| 128 | dasm_State *D = Dst_REF; | ||
| 129 | size_t osz = D->pcsize; | ||
| 130 | DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int)); | ||
| 131 | memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz); | ||
| 132 | } | ||
| 133 | |||
| 134 | /* Setup encoder. */ | ||
| 135 | void dasm_setup(Dst_DECL, dasm_ActList actionlist) | ||
| 136 | { | ||
| 137 | dasm_State *D = Dst_REF; | ||
| 138 | int i; | ||
| 139 | D->actionlist = actionlist; | ||
| 140 | D->status = DASM_S_OK; | ||
| 141 | D->section = &D->sections[0]; | ||
| 142 | memset((void *)D->lglabels, 0, D->lgsize); | ||
| 143 | if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize); | ||
| 144 | for (i = 0; i < D->maxsection; i++) { | ||
| 145 | D->sections[i].pos = DASM_SEC2POS(i); | ||
| 146 | D->sections[i].ofs = 0; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | |||
| 151 | #ifdef DASM_CHECKS | ||
| 152 | #define CK(x, st) \ | ||
| 153 | do { if (!(x)) { \ | ||
| 154 | D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0) | ||
| 155 | #define CKPL(kind, st) \ | ||
| 156 | do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ | ||
| 157 | D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0) | ||
| 158 | #else | ||
| 159 | #define CK(x, st) ((void)0) | ||
| 160 | #define CKPL(kind, st) ((void)0) | ||
| 161 | #endif | ||
| 162 | |||
| 163 | /* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ | ||
| 164 | void dasm_put(Dst_DECL, int start, ...) | ||
| 165 | { | ||
| 166 | va_list ap; | ||
| 167 | dasm_State *D = Dst_REF; | ||
| 168 | dasm_ActList p = D->actionlist + start; | ||
| 169 | dasm_Section *sec = D->section; | ||
| 170 | int pos = sec->pos, ofs = sec->ofs, mrm = 4; | ||
| 171 | int *b; | ||
| 172 | |||
| 173 | if (pos >= sec->epos) { | ||
| 174 | DASM_M_GROW(Dst, int, sec->buf, sec->bsize, | ||
| 175 | sec->bsize + 2*DASM_MAXSECPOS*sizeof(int)); | ||
| 176 | sec->rbuf = sec->buf - DASM_POS2BIAS(pos); | ||
| 177 | sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos); | ||
| 178 | } | ||
| 179 | |||
| 180 | b = sec->rbuf; | ||
| 181 | b[pos++] = start; | ||
| 182 | |||
| 183 | va_start(ap, start); | ||
| 184 | while (1) { | ||
| 185 | int action = *p++; | ||
| 186 | if (action < DASM_DISP) { | ||
| 187 | ofs++; | ||
| 188 | } else if (action <= DASM_REL_A) { | ||
| 189 | int n = va_arg(ap, int); | ||
| 190 | b[pos++] = n; | ||
| 191 | switch (action) { | ||
| 192 | case DASM_DISP: | ||
| 193 | if (n == 0) { if ((mrm&7) == 4) mrm = p[-2]; if ((mrm&7) != 5) break; } | ||
| 194 | case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob; | ||
| 195 | case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */ | ||
| 196 | case DASM_IMM_D: ofs += 4; break; | ||
| 197 | case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob; | ||
| 198 | case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break; | ||
| 199 | case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob; | ||
| 200 | case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break; | ||
| 201 | case DASM_SPACE: p++; ofs += n; break; | ||
| 202 | case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */ | ||
| 203 | case DASM_VREG: CK((n&-8) == 0 && (n != 4 || (*p&1) == 0), RANGE_VREG); | ||
| 204 | if (*p++ == 1 && *p == DASM_DISP) mrm = n; continue; | ||
| 205 | } | ||
| 206 | mrm = 4; | ||
| 207 | } else { | ||
| 208 | int *pl, n; | ||
| 209 | switch (action) { | ||
| 210 | case DASM_REL_LG: | ||
| 211 | case DASM_IMM_LG: | ||
| 212 | n = *p++; pl = D->lglabels + n; | ||
| 213 | if (n <= 246) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */ | ||
| 214 | pl -= 246; n = *pl; | ||
| 215 | if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */ | ||
| 216 | goto linkrel; | ||
| 217 | case DASM_REL_PC: | ||
| 218 | case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC); | ||
| 219 | putrel: | ||
| 220 | n = *pl; | ||
| 221 | if (n < 0) { /* Label exists. Get label pos and store it. */ | ||
| 222 | b[pos] = -n; | ||
| 223 | } else { | ||
| 224 | linkrel: | ||
| 225 | b[pos] = n; /* Else link to rel chain, anchored at label. */ | ||
| 226 | *pl = pos; | ||
| 227 | } | ||
| 228 | pos++; | ||
| 229 | ofs += 4; /* Maximum offset needed. */ | ||
| 230 | if (action == DASM_REL_LG || action == DASM_REL_PC) | ||
| 231 | b[pos++] = ofs; /* Store pass1 offset estimate. */ | ||
| 232 | break; | ||
| 233 | case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel; | ||
| 234 | case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC); | ||
| 235 | putlabel: | ||
| 236 | n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ | ||
| 237 | while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; } | ||
| 238 | *pl = -pos; /* Label exists now. */ | ||
| 239 | b[pos++] = ofs; /* Store pass1 offset estimate. */ | ||
| 240 | break; | ||
| 241 | case DASM_ALIGN: | ||
| 242 | ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */ | ||
| 243 | b[pos++] = ofs; /* Store pass1 offset estimate. */ | ||
| 244 | break; | ||
| 245 | case DASM_EXTERN: p += 2; ofs += 4; break; | ||
| 246 | case DASM_ESC: p++; ofs++; break; | ||
| 247 | case DASM_MARK: mrm = p[-2]; break; | ||
| 248 | case DASM_SECTION: | ||
| 249 | n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n]; | ||
| 250 | case DASM_STOP: goto stop; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | stop: | ||
| 255 | va_end(ap); | ||
| 256 | sec->pos = pos; | ||
| 257 | sec->ofs = ofs; | ||
| 258 | } | ||
| 259 | #undef CK | ||
| 260 | |||
| 261 | /* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */ | ||
| 262 | int dasm_link(Dst_DECL, size_t *szp) | ||
| 263 | { | ||
| 264 | dasm_State *D = Dst_REF; | ||
| 265 | int secnum; | ||
| 266 | int ofs = 0; | ||
| 267 | |||
| 268 | #ifdef DASM_CHECKS | ||
| 269 | *szp = 0; | ||
| 270 | if (D->status != DASM_S_OK) return D->status; | ||
| 271 | { | ||
| 272 | int pc; | ||
| 273 | for (pc = 0; pc*sizeof(int) < D->pcsize; pc++) | ||
| 274 | if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc; | ||
| 275 | } | ||
| 276 | #endif | ||
| 277 | |||
| 278 | { /* Handle globals not defined in this translation unit. */ | ||
| 279 | int idx; | ||
| 280 | for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) { | ||
| 281 | int n = D->lglabels[idx]; | ||
| 282 | /* Undefined label: Collapse rel chain and replace with marker (< 0). */ | ||
| 283 | while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; } | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | /* Combine all code sections. No support for data sections (yet). */ | ||
| 288 | for (secnum = 0; secnum < D->maxsection; secnum++) { | ||
| 289 | dasm_Section *sec = D->sections + secnum; | ||
| 290 | int *b = sec->rbuf; | ||
| 291 | int pos = DASM_SEC2POS(secnum); | ||
| 292 | int lastpos = sec->pos; | ||
| 293 | |||
| 294 | while (pos != lastpos) { | ||
| 295 | dasm_ActList p = D->actionlist + b[pos++]; | ||
| 296 | while (1) { | ||
| 297 | int op, action = *p++; | ||
| 298 | switch (action) { | ||
| 299 | case DASM_REL_LG: p++; op = p[-3]; goto rel_pc; | ||
| 300 | case DASM_REL_PC: op = p[-2]; rel_pc: { | ||
| 301 | int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0); | ||
| 302 | if (shrink) { /* Shrinkable branch opcode? */ | ||
| 303 | int lofs, lpos = b[pos]; | ||
| 304 | if (lpos < 0) goto noshrink; /* Ext global? */ | ||
| 305 | lofs = *DASM_POS2PTR(D, lpos); | ||
| 306 | if (lpos > pos) { /* Fwd label: add cumulative section offsets. */ | ||
| 307 | int i; | ||
| 308 | for (i = secnum; i < DASM_POS2SEC(lpos); i++) | ||
| 309 | lofs += D->sections[i].ofs; | ||
| 310 | } else { | ||
| 311 | lofs -= ofs; /* Bkwd label: unfix offset. */ | ||
| 312 | } | ||
| 313 | lofs -= b[pos+1]; /* Short branch ok? */ | ||
| 314 | if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */ | ||
| 315 | else { noshrink: shrink = 0; } /* No, cannot shrink op. */ | ||
| 316 | } | ||
| 317 | b[pos+1] = shrink; | ||
| 318 | pos += 2; | ||
| 319 | break; | ||
| 320 | } | ||
| 321 | case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++; | ||
| 322 | case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W: | ||
| 323 | case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB: | ||
| 324 | case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break; | ||
| 325 | case DASM_LABEL_LG: p++; | ||
| 326 | case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */ | ||
| 327 | case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */ | ||
| 328 | case DASM_EXTERN: p += 2; break; | ||
| 329 | case DASM_ESC: p++; break; | ||
| 330 | case DASM_MARK: break; | ||
| 331 | case DASM_SECTION: case DASM_STOP: goto stop; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | stop: (void)0; | ||
| 335 | } | ||
| 336 | ofs += sec->ofs; /* Next section starts right after current section. */ | ||
| 337 | } | ||
| 338 | |||
| 339 | D->codesize = ofs; /* Total size of all code sections */ | ||
| 340 | *szp = ofs; | ||
| 341 | return DASM_S_OK; | ||
| 342 | } | ||
| 343 | |||
| 344 | #define dasmb(x) *cp++ = (unsigned char)(x) | ||
| 345 | #ifndef DASM_ALIGNED_WRITES | ||
| 346 | #define dasmw(x) \ | ||
| 347 | do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0) | ||
| 348 | #define dasmd(x) \ | ||
| 349 | do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0) | ||
| 350 | #else | ||
| 351 | #define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0) | ||
| 352 | #define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0) | ||
| 353 | #endif | ||
| 354 | |||
| 355 | /* Pass 3: Encode sections. */ | ||
| 356 | int dasm_encode(Dst_DECL, void *buffer) | ||
| 357 | { | ||
| 358 | dasm_State *D = Dst_REF; | ||
| 359 | unsigned char *base = (unsigned char *)buffer; | ||
| 360 | unsigned char *cp = base; | ||
| 361 | int secnum; | ||
| 362 | |||
| 363 | /* Encode all code sections. No support for data sections (yet). */ | ||
| 364 | for (secnum = 0; secnum < D->maxsection; secnum++) { | ||
| 365 | dasm_Section *sec = D->sections + secnum; | ||
| 366 | int *b = sec->buf; | ||
| 367 | int *endb = sec->rbuf + sec->pos; | ||
| 368 | |||
| 369 | while (b != endb) { | ||
| 370 | dasm_ActList p = D->actionlist + *b++; | ||
| 371 | unsigned char *mark = NULL; | ||
| 372 | while (1) { | ||
| 373 | int action = *p++; | ||
| 374 | int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0; | ||
| 375 | switch (action) { | ||
| 376 | case DASM_DISP: if (!mark) mark = cp; { | ||
| 377 | unsigned char *mm = mark; | ||
| 378 | if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL; | ||
| 379 | if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7; | ||
| 380 | if (mrm != 5) { mm[-1] -= 0x80; break; } } | ||
| 381 | if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40; | ||
| 382 | } | ||
| 383 | case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break; | ||
| 384 | case DASM_IMM_DB: if (((n+128)&-256) == 0) { | ||
| 385 | db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb; | ||
| 386 | } else mark = NULL; | ||
| 387 | case DASM_IMM_D: wd: dasmd(n); break; | ||
| 388 | case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL; | ||
| 389 | case DASM_IMM_W: dasmw(n); break; | ||
| 390 | case DASM_VREG: { int t = *p++; if (t >= 2) n<<=3; cp[-1] |= n; break; } | ||
| 391 | case DASM_REL_LG: p++; if (n >= 0) goto rel_pc; | ||
| 392 | b++; n = (int)(ptrdiff_t)D->globals[-n]; | ||
| 393 | case DASM_REL_A: rel_a: n -= (int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */ | ||
| 394 | case DASM_REL_PC: rel_pc: { | ||
| 395 | int shrink = *b++; | ||
| 396 | int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; } | ||
| 397 | n = *pb - ((int)(cp-base) + 4-shrink); | ||
| 398 | if (shrink == 0) goto wd; | ||
| 399 | if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb; | ||
| 400 | goto wb; | ||
| 401 | } | ||
| 402 | case DASM_IMM_LG: | ||
| 403 | p++; if (n < 0) { n = (int)(ptrdiff_t)D->globals[-n]; goto wd; } | ||
| 404 | case DASM_IMM_PC: { | ||
| 405 | int *pb = DASM_POS2PTR(D, n); | ||
| 406 | n = *pb < 0 ? pb[1] : (*pb + (int)(ptrdiff_t)base); | ||
| 407 | goto wd; | ||
| 408 | } | ||
| 409 | case DASM_LABEL_LG: { | ||
| 410 | int idx = *p++; | ||
| 411 | if (idx >= 10) | ||
| 412 | D->globals[idx] = (void *)(base + (*p == DASM_SETLABEL ? *b : n)); | ||
| 413 | break; | ||
| 414 | } | ||
| 415 | case DASM_LABEL_PC: case DASM_SETLABEL: break; | ||
| 416 | case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; } | ||
| 417 | case DASM_ALIGN: | ||
| 418 | n = *p++; | ||
| 419 | while (((cp-base) & n)) *cp++ = 0x90; /* nop */ | ||
| 420 | break; | ||
| 421 | case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd; | ||
| 422 | case DASM_MARK: mark = cp; break; | ||
| 423 | case DASM_ESC: action = *p++; | ||
| 424 | default: *cp++ = action; break; | ||
| 425 | case DASM_SECTION: case DASM_STOP: goto stop; | ||
| 426 | } | ||
| 427 | } | ||
| 428 | stop: (void)0; | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | if (base + D->codesize != cp) /* Check for phase errors. */ | ||
| 433 | return DASM_S_PHASE; | ||
| 434 | return DASM_S_OK; | ||
| 435 | } | ||
| 436 | |||
| 437 | /* Get PC label offset. */ | ||
| 438 | int dasm_getpclabel(Dst_DECL, unsigned int pc) | ||
| 439 | { | ||
| 440 | dasm_State *D = Dst_REF; | ||
| 441 | if (pc*sizeof(int) < D->pcsize) { | ||
| 442 | int pos = D->pclabels[pc]; | ||
| 443 | if (pos < 0) return *DASM_POS2PTR(D, -pos); | ||
| 444 | if (pos > 0) return -1; /* Undefined. */ | ||
| 445 | } | ||
| 446 | return -2; /* Unused or out of range. */ | ||
| 447 | } | ||
| 448 | |||
| 449 | #ifdef DASM_CHECKS | ||
| 450 | /* Optional sanity checker to call between isolated encoding steps. */ | ||
| 451 | int dasm_checkstep(Dst_DECL, int secmatch) | ||
| 452 | { | ||
| 453 | dasm_State *D = Dst_REF; | ||
| 454 | if (D->status == DASM_S_OK) { | ||
| 455 | int i; | ||
| 456 | for (i = 1; i <= 9; i++) { | ||
| 457 | if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; } | ||
| 458 | D->lglabels[i] = 0; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | if (D->status == DASM_S_OK && secmatch >= 0 && | ||
| 462 | D->section != &D->sections[secmatch]) | ||
| 463 | D->status = DASM_S_MATCH_SEC|(D->section-D->sections); | ||
| 464 | return D->status; | ||
| 465 | } | ||
| 466 | #endif | ||
| 467 | |||
diff --git a/dynasm/dasm_x86.lua b/dynasm/dasm_x86.lua new file mode 100644 index 00000000..82210806 --- /dev/null +++ b/dynasm/dasm_x86.lua | |||
| @@ -0,0 +1,1799 @@ | |||
| 1 | ------------------------------------------------------------------------------ | ||
| 2 | -- DynASM x86 module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 5 | -- See dynasm.lua for full copyright notice. | ||
| 6 | ------------------------------------------------------------------------------ | ||
| 7 | |||
| 8 | -- Module information: | ||
| 9 | local _info = { | ||
| 10 | arch = "x86", | ||
| 11 | description = "DynASM x86 (i386) module", | ||
| 12 | version = "1.2.1", | ||
| 13 | vernum = 10201, | ||
| 14 | release = "2009-04-16", | ||
| 15 | author = "Mike Pall", | ||
| 16 | license = "MIT", | ||
| 17 | } | ||
| 18 | |||
| 19 | -- Exported glue functions for the arch-specific module. | ||
| 20 | local _M = { _info = _info } | ||
| 21 | |||
| 22 | -- Cache library functions. | ||
| 23 | local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs | ||
| 24 | local assert, unpack = assert, unpack | ||
| 25 | local _s = string | ||
| 26 | local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char | ||
| 27 | local find, match, gmatch, gsub = _s.find, _s.match, _s.gmatch, _s.gsub | ||
| 28 | local concat, sort = table.concat, table.sort | ||
| 29 | local char, unpack = string.char, unpack | ||
| 30 | |||
| 31 | -- Inherited tables and callbacks. | ||
| 32 | local g_opt, g_arch | ||
| 33 | local wline, werror, wfatal, wwarn | ||
| 34 | |||
| 35 | -- Action name list. | ||
| 36 | -- CHECK: Keep this in sync with the C code! | ||
| 37 | local action_names = { | ||
| 38 | -- int arg, 1 buffer pos: | ||
| 39 | "DISP", "IMM_S", "IMM_B", "IMM_W", "IMM_D", "IMM_WB", "IMM_DB", | ||
| 40 | -- action arg (1 byte), int arg, 1 buffer pos (reg/num): | ||
| 41 | "VREG", "SPACE", | ||
| 42 | -- ptrdiff_t arg, 1 buffer pos (address): !x64 | ||
| 43 | "SETLABEL", "REL_A", | ||
| 44 | -- action arg (1 byte) or int arg, 2 buffer pos (link, offset): | ||
| 45 | "REL_LG", "REL_PC", | ||
| 46 | -- action arg (1 byte) or int arg, 1 buffer pos (link): | ||
| 47 | "IMM_LG", "IMM_PC", | ||
| 48 | -- action arg (1 byte) or int arg, 1 buffer pos (offset): | ||
| 49 | "LABEL_LG", "LABEL_PC", | ||
| 50 | -- action arg (1 byte), 1 buffer pos (offset): | ||
| 51 | "ALIGN", | ||
| 52 | -- action args (2 bytes), no buffer pos. | ||
| 53 | "EXTERN", | ||
| 54 | -- action arg (1 byte), no buffer pos. | ||
| 55 | "ESC", | ||
| 56 | -- no action arg, no buffer pos. | ||
| 57 | "MARK", | ||
| 58 | -- action arg (1 byte), no buffer pos, terminal action: | ||
| 59 | "SECTION", | ||
| 60 | -- no args, no buffer pos, terminal action: | ||
| 61 | "STOP" | ||
| 62 | } | ||
| 63 | |||
| 64 | -- Maximum number of section buffer positions for dasm_put(). | ||
| 65 | -- CHECK: Keep this in sync with the C code! | ||
| 66 | local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines. | ||
| 67 | |||
| 68 | -- Action name -> action number (dynamically generated below). | ||
| 69 | local map_action = {} | ||
| 70 | -- First action number. Everything below does not need to be escaped. | ||
| 71 | local actfirst = 256-#action_names | ||
| 72 | |||
| 73 | -- Action list buffer and string (only used to remove dupes). | ||
| 74 | local actlist = {} | ||
| 75 | local actstr = "" | ||
| 76 | |||
| 77 | -- Argument list for next dasm_put(). Start with offset 0 into action list. | ||
| 78 | local actargs = { 0 } | ||
| 79 | |||
| 80 | -- Current number of section buffer positions for dasm_put(). | ||
| 81 | local secpos = 1 | ||
| 82 | |||
| 83 | ------------------------------------------------------------------------------ | ||
| 84 | |||
| 85 | -- Compute action numbers for action names. | ||
| 86 | for n,name in ipairs(action_names) do | ||
| 87 | local num = actfirst + n - 1 | ||
| 88 | map_action[name] = num | ||
| 89 | end | ||
| 90 | |||
| 91 | -- Dump action names and numbers. | ||
| 92 | local function dumpactions(out) | ||
| 93 | out:write("DynASM encoding engine action codes:\n") | ||
| 94 | for n,name in ipairs(action_names) do | ||
| 95 | local num = map_action[name] | ||
| 96 | out:write(format(" %-10s %02X %d\n", name, num, num)) | ||
| 97 | end | ||
| 98 | out:write("\n") | ||
| 99 | end | ||
| 100 | |||
| 101 | -- Write action list buffer as a huge static C array. | ||
| 102 | local function writeactions(out, name) | ||
| 103 | local nn = #actlist | ||
| 104 | local last = actlist[nn] or 255 | ||
| 105 | actlist[nn] = nil -- Remove last byte. | ||
| 106 | if nn == 0 then nn = 1 end | ||
| 107 | out:write("static const unsigned char ", name, "[", nn, "] = {\n") | ||
| 108 | local s = " " | ||
| 109 | for n,b in ipairs(actlist) do | ||
| 110 | s = s..b.."," | ||
| 111 | if #s >= 75 then | ||
| 112 | assert(out:write(s, "\n")) | ||
| 113 | s = " " | ||
| 114 | end | ||
| 115 | end | ||
| 116 | out:write(s, last, "\n};\n\n") -- Add last byte back. | ||
| 117 | end | ||
| 118 | |||
| 119 | ------------------------------------------------------------------------------ | ||
| 120 | |||
| 121 | -- Add byte to action list. | ||
| 122 | local function wputxb(n) | ||
| 123 | assert(n >= 0 and n <= 255 and n % 1 == 0, "byte out of range") | ||
| 124 | actlist[#actlist+1] = n | ||
| 125 | end | ||
| 126 | |||
| 127 | -- Add action to list with optional arg. Advance buffer pos, too. | ||
| 128 | local function waction(action, a, num) | ||
| 129 | wputxb(assert(map_action[action], "bad action name `"..action.."'")) | ||
| 130 | if a then actargs[#actargs+1] = a end | ||
| 131 | if a or num then secpos = secpos + (num or 1) end | ||
| 132 | end | ||
| 133 | |||
| 134 | -- Add call to embedded DynASM C code. | ||
| 135 | local function wcall(func, args) | ||
| 136 | wline(format("dasm_%s(Dst, %s);", func, concat(args, ", ")), true) | ||
| 137 | end | ||
| 138 | |||
| 139 | -- Delete duplicate action list chunks. A tad slow, but so what. | ||
| 140 | local function dedupechunk(offset) | ||
| 141 | local al, as = actlist, actstr | ||
| 142 | local chunk = char(unpack(al, offset+1, #al)) | ||
| 143 | local orig = find(as, chunk, 1, true) | ||
| 144 | if orig then | ||
| 145 | actargs[1] = orig-1 -- Replace with original offset. | ||
| 146 | for i=offset+1,#al do al[i] = nil end -- Kill dupe. | ||
| 147 | else | ||
| 148 | actstr = as..chunk | ||
| 149 | end | ||
| 150 | end | ||
| 151 | |||
| 152 | -- Flush action list (intervening C code or buffer pos overflow). | ||
| 153 | local function wflush(term) | ||
| 154 | local offset = actargs[1] | ||
| 155 | if #actlist == offset then return end -- Nothing to flush. | ||
| 156 | if not term then waction("STOP") end -- Terminate action list. | ||
| 157 | dedupechunk(offset) | ||
| 158 | wcall("put", actargs) -- Add call to dasm_put(). | ||
| 159 | actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put(). | ||
| 160 | secpos = 1 -- The actionlist offset occupies a buffer position, too. | ||
| 161 | end | ||
| 162 | |||
| 163 | -- Put escaped byte. | ||
| 164 | local function wputb(n) | ||
| 165 | if n >= actfirst then waction("ESC") end -- Need to escape byte. | ||
| 166 | wputxb(n) | ||
| 167 | end | ||
| 168 | |||
| 169 | ------------------------------------------------------------------------------ | ||
| 170 | |||
| 171 | -- Global label name -> global label number. With auto assignment on 1st use. | ||
| 172 | local next_global = 10 | ||
| 173 | local map_global = setmetatable({}, { __index = function(t, name) | ||
| 174 | if not match(name, "^[%a_][%w_]*$") then werror("bad global label") end | ||
| 175 | local n = next_global | ||
| 176 | if n > 246 then werror("too many global labels") end | ||
| 177 | next_global = n + 1 | ||
| 178 | t[name] = n | ||
| 179 | return n | ||
| 180 | end}) | ||
| 181 | |||
| 182 | -- Dump global labels. | ||
| 183 | local function dumpglobals(out, lvl) | ||
| 184 | local t = {} | ||
| 185 | for name, n in pairs(map_global) do t[n] = name end | ||
| 186 | out:write("Global labels:\n") | ||
| 187 | for i=10,next_global-1 do | ||
| 188 | out:write(format(" %s\n", t[i])) | ||
| 189 | end | ||
| 190 | out:write("\n") | ||
| 191 | end | ||
| 192 | |||
| 193 | -- Write global label enum. | ||
| 194 | local function writeglobals(out, prefix) | ||
| 195 | local t = {} | ||
| 196 | for name, n in pairs(map_global) do t[n] = name end | ||
| 197 | out:write("enum {\n") | ||
| 198 | for i=10,next_global-1 do | ||
| 199 | out:write(" ", prefix, t[i], ",\n") | ||
| 200 | end | ||
| 201 | out:write(" ", prefix, "_MAX\n};\n") | ||
| 202 | end | ||
| 203 | |||
| 204 | -- Write global label names. | ||
| 205 | local function writeglobalnames(out, name) | ||
| 206 | local t = {} | ||
| 207 | for name, n in pairs(map_global) do t[n] = name end | ||
| 208 | out:write("static const char *const ", name, "[] = {\n") | ||
| 209 | for i=10,next_global-1 do | ||
| 210 | out:write(" \"", t[i], "\",\n") | ||
| 211 | end | ||
| 212 | out:write(" (const char *)0\n};\n") | ||
| 213 | end | ||
| 214 | |||
| 215 | ------------------------------------------------------------------------------ | ||
| 216 | |||
| 217 | -- Extern label name -> extern label number. With auto assignment on 1st use. | ||
| 218 | local next_extern = -1 | ||
| 219 | local map_extern = setmetatable({}, { __index = function(t, name) | ||
| 220 | -- No restrictions on the name for now. | ||
| 221 | local n = next_extern | ||
| 222 | if n < -256 then werror("too many extern labels") end | ||
| 223 | next_extern = n - 1 | ||
| 224 | t[name] = n | ||
| 225 | return n | ||
| 226 | end}) | ||
| 227 | |||
| 228 | -- Dump extern labels. | ||
| 229 | local function dumpexterns(out, lvl) | ||
| 230 | local t = {} | ||
| 231 | for name, n in pairs(map_extern) do t[-n] = name end | ||
| 232 | out:write("Extern labels:\n") | ||
| 233 | for i=1,-next_extern-1 do | ||
| 234 | out:write(format(" %s\n", t[i])) | ||
| 235 | end | ||
| 236 | out:write("\n") | ||
| 237 | end | ||
| 238 | |||
| 239 | -- Write extern label names. | ||
| 240 | local function writeexternnames(out, name) | ||
| 241 | local t = {} | ||
| 242 | for name, n in pairs(map_extern) do t[-n] = name end | ||
| 243 | out:write("static const char *const ", name, "[] = {\n") | ||
| 244 | for i=1,-next_extern-1 do | ||
| 245 | out:write(" \"", t[i], "\",\n") | ||
| 246 | end | ||
| 247 | out:write(" (const char *)0\n};\n") | ||
| 248 | end | ||
| 249 | |||
| 250 | ------------------------------------------------------------------------------ | ||
| 251 | |||
| 252 | -- Arch-specific maps. | ||
| 253 | local map_archdef = {} -- Ext. register name -> int. name. | ||
| 254 | local map_reg_rev = {} -- Int. register name -> ext. name. | ||
| 255 | local map_reg_num = {} -- Int. register name -> register number. | ||
| 256 | local map_reg_opsize = {} -- Int. register name -> operand size. | ||
| 257 | local map_reg_valid_base = {} -- Int. register name -> valid base register? | ||
| 258 | local map_reg_valid_index = {} -- Int. register name -> valid index register? | ||
| 259 | local reg_list = {} -- Canonical list of int. register names. | ||
| 260 | |||
| 261 | local map_type = {} -- Type name -> { ctype, reg } | ||
| 262 | local ctypenum = 0 -- Type number (for _PTx macros). | ||
| 263 | |||
| 264 | local addrsize = "d" -- Size for address operands. !x64 | ||
| 265 | |||
| 266 | -- Helper function to fill register maps. | ||
| 267 | local function mkrmap(sz, cl, names) | ||
| 268 | local cname = format("@%s", sz) | ||
| 269 | reg_list[#reg_list+1] = cname | ||
| 270 | map_archdef[cl] = cname | ||
| 271 | map_reg_rev[cname] = cl | ||
| 272 | map_reg_num[cname] = -1 | ||
| 273 | map_reg_opsize[cname] = sz | ||
| 274 | if sz == addrsize then | ||
| 275 | map_reg_valid_base[cname] = true | ||
| 276 | map_reg_valid_index[cname] = true | ||
| 277 | end | ||
| 278 | for n,name in ipairs(names) do | ||
| 279 | local iname = format("@%s%x", sz, n-1) | ||
| 280 | reg_list[#reg_list+1] = iname | ||
| 281 | map_archdef[name] = iname | ||
| 282 | map_reg_rev[iname] = name | ||
| 283 | map_reg_num[iname] = n-1 | ||
| 284 | map_reg_opsize[iname] = sz | ||
| 285 | if sz == addrsize then | ||
| 286 | map_reg_valid_base[iname] = true | ||
| 287 | map_reg_valid_index[iname] = true | ||
| 288 | end | ||
| 289 | end | ||
| 290 | reg_list[#reg_list+1] = "" | ||
| 291 | end | ||
| 292 | |||
| 293 | -- Integer registers (dword, word and byte sized). | ||
| 294 | mkrmap("d", "Rd", {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"}) | ||
| 295 | map_reg_valid_index[map_archdef.esp] = false | ||
| 296 | mkrmap("w", "Rw", {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"}) | ||
| 297 | mkrmap("b", "Rb", {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"}) | ||
| 298 | map_archdef["Ra"] = "@"..addrsize | ||
| 299 | |||
| 300 | -- FP registers (internally tword sized, but use "f" as operand size). | ||
| 301 | mkrmap("f", "Rf", {"st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7"}) | ||
| 302 | |||
| 303 | -- SSE registers (oword sized, but qword and dword accessible). | ||
| 304 | mkrmap("o", "xmm", {"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"}) | ||
| 305 | |||
| 306 | -- Operand size prefixes to codes. | ||
| 307 | local map_opsize = { | ||
| 308 | byte = "b", word = "w", dword = "d", qword = "q", oword = "o", tword = "t", | ||
| 309 | aword = addrsize, | ||
| 310 | } | ||
| 311 | |||
| 312 | -- Operand size code to number. | ||
| 313 | local map_opsizenum = { | ||
| 314 | b = 1, w = 2, d = 4, q = 8, o = 16, t = 10, | ||
| 315 | } | ||
| 316 | |||
| 317 | -- Operand size code to name. | ||
| 318 | local map_opsizename = { | ||
| 319 | b = "byte", w = "word", d = "dword", q = "qword", o = "oword", t = "tword", | ||
| 320 | f = "fpword", | ||
| 321 | } | ||
| 322 | |||
| 323 | -- Valid index register scale factors. | ||
| 324 | local map_xsc = { | ||
| 325 | ["1"] = 0, ["2"] = 1, ["4"] = 2, ["8"] = 3, | ||
| 326 | } | ||
| 327 | |||
| 328 | -- Condition codes. | ||
| 329 | local map_cc = { | ||
| 330 | o = 0, no = 1, b = 2, nb = 3, e = 4, ne = 5, be = 6, nbe = 7, | ||
| 331 | s = 8, ns = 9, p = 10, np = 11, l = 12, nl = 13, le = 14, nle = 15, | ||
| 332 | c = 2, nae = 2, nc = 3, ae = 3, z = 4, nz = 5, na = 6, a = 7, | ||
| 333 | pe = 10, po = 11, nge = 12, ge = 13, ng = 14, g = 15, | ||
| 334 | } | ||
| 335 | |||
| 336 | |||
| 337 | -- Reverse defines for registers. | ||
| 338 | function _M.revdef(s) | ||
| 339 | return gsub(s, "@%w+", map_reg_rev) | ||
| 340 | end | ||
| 341 | |||
| 342 | -- Dump register names and numbers | ||
| 343 | local function dumpregs(out) | ||
| 344 | out:write("Register names, sizes and internal numbers:\n") | ||
| 345 | for _,reg in ipairs(reg_list) do | ||
| 346 | if reg == "" then | ||
| 347 | out:write("\n") | ||
| 348 | else | ||
| 349 | local name = map_reg_rev[reg] | ||
| 350 | local num = map_reg_num[reg] | ||
| 351 | local opsize = map_opsizename[map_reg_opsize[reg]] | ||
| 352 | out:write(format(" %-5s %-8s %s\n", name, opsize, | ||
| 353 | num < 0 and "(variable)" or num)) | ||
| 354 | end | ||
| 355 | end | ||
| 356 | end | ||
| 357 | |||
| 358 | ------------------------------------------------------------------------------ | ||
| 359 | |||
| 360 | -- Put action for label arg (IMM_LG, IMM_PC, REL_LG, REL_PC). | ||
| 361 | local function wputlabel(aprefix, imm, num) | ||
| 362 | if type(imm) == "number" then | ||
| 363 | if imm < 0 then | ||
| 364 | waction("EXTERN") | ||
| 365 | wputxb(aprefix == "IMM_" and 0 or 1) | ||
| 366 | imm = -imm-1 | ||
| 367 | else | ||
| 368 | waction(aprefix.."LG", nil, num); | ||
| 369 | end | ||
| 370 | wputxb(imm) | ||
| 371 | else | ||
| 372 | waction(aprefix.."PC", imm, num) | ||
| 373 | end | ||
| 374 | end | ||
| 375 | |||
| 376 | -- Put signed byte or arg. | ||
| 377 | local function wputsbarg(n) | ||
| 378 | if type(n) == "number" then | ||
| 379 | if n < -128 or n > 127 then | ||
| 380 | werror("signed immediate byte out of range") | ||
| 381 | end | ||
| 382 | if n < 0 then n = n + 256 end | ||
| 383 | wputb(n) | ||
| 384 | else waction("IMM_S", n) end | ||
| 385 | end | ||
| 386 | |||
| 387 | -- Put unsigned byte or arg. | ||
| 388 | local function wputbarg(n) | ||
| 389 | if type(n) == "number" then | ||
| 390 | if n < 0 or n > 255 then | ||
| 391 | werror("unsigned immediate byte out of range") | ||
| 392 | end | ||
| 393 | wputb(n) | ||
| 394 | else waction("IMM_B", n) end | ||
| 395 | end | ||
| 396 | |||
| 397 | -- Put unsigned word or arg. | ||
| 398 | local function wputwarg(n) | ||
| 399 | if type(n) == "number" then | ||
| 400 | if n < 0 or n > 65535 then | ||
| 401 | werror("unsigned immediate word out of range") | ||
| 402 | end | ||
| 403 | local r = n%256; n = (n-r)/256; wputb(r); wputb(n); | ||
| 404 | else waction("IMM_W", n) end | ||
| 405 | end | ||
| 406 | |||
| 407 | -- Put signed or unsigned dword or arg. | ||
| 408 | local function wputdarg(n) | ||
| 409 | local tn = type(n) | ||
| 410 | if tn == "number" then | ||
| 411 | if n < 0 then n = n + 4294967296 end | ||
| 412 | local r = n%256; n = (n-r)/256; wputb(r); | ||
| 413 | r = n%256; n = (n-r)/256; wputb(r); | ||
| 414 | r = n%256; n = (n-r)/256; wputb(r); wputb(n); | ||
| 415 | elseif tn == "table" then | ||
| 416 | wputlabel("IMM_", n[1], 1) | ||
| 417 | else | ||
| 418 | waction("IMM_D", n) | ||
| 419 | end | ||
| 420 | end | ||
| 421 | |||
| 422 | -- Put operand-size dependent number or arg (defaults to dword). | ||
| 423 | local function wputszarg(sz, n) | ||
| 424 | if not sz or sz == "d" then wputdarg(n) | ||
| 425 | elseif sz == "w" then wputwarg(n) | ||
| 426 | elseif sz == "b" then wputbarg(n) | ||
| 427 | elseif sz == "s" then wputsbarg(n) | ||
| 428 | else werror("bad operand size") end | ||
| 429 | end | ||
| 430 | |||
| 431 | -- Put multi-byte opcode with operand-size dependent modifications. | ||
| 432 | local function wputop(sz, op) | ||
| 433 | local r | ||
| 434 | if sz == "w" then wputb(102) end | ||
| 435 | -- Needs >32 bit numbers, but only for crc32 eax, word [ebx] | ||
| 436 | if op >= 4294967296 then r = op%4294967296 wputb((op-r)/4294967296) op = r end | ||
| 437 | if op >= 16777216 then r = op % 16777216 wputb((op-r) / 16777216) op = r end | ||
| 438 | if op >= 65536 then r = op % 65536 wputb((op-r) / 65536) op = r end | ||
| 439 | if op >= 256 then r = op % 256 wputb((op-r) / 256) op = r end | ||
| 440 | if sz == "b" then op = op - 1 end | ||
| 441 | wputb(op) | ||
| 442 | end | ||
| 443 | |||
| 444 | -- Put ModRM or SIB formatted byte. | ||
| 445 | local function wputmodrm(m, s, rm, vs, vrm) | ||
| 446 | assert(m < 4 and s < 8 and rm < 8, "bad modrm operands") | ||
| 447 | wputb(64*m + 8*s + rm) | ||
| 448 | end | ||
| 449 | |||
| 450 | -- Put ModRM/SIB plus optional displacement. | ||
| 451 | local function wputmrmsib(t, imark, s, vsreg) | ||
| 452 | local vreg, vxreg | ||
| 453 | local reg, xreg = t.reg, t.xreg | ||
| 454 | if reg and reg < 0 then reg = 0; vreg = t.vreg end | ||
| 455 | if xreg and xreg < 0 then xreg = 0; vxreg = t.vxreg end | ||
| 456 | if s < 0 then s = 0 end | ||
| 457 | |||
| 458 | -- Register mode. | ||
| 459 | if sub(t.mode, 1, 1) == "r" then | ||
| 460 | wputmodrm(3, s, reg) | ||
| 461 | if vsreg then waction("VREG", vsreg); wputxb(2) end | ||
| 462 | if vreg then waction("VREG", vreg); wputxb(0) end | ||
| 463 | return | ||
| 464 | end | ||
| 465 | |||
| 466 | local disp = t.disp | ||
| 467 | local tdisp = type(disp) | ||
| 468 | -- No base register? | ||
| 469 | if not reg then | ||
| 470 | if xreg then | ||
| 471 | -- Indexed mode with index register only. | ||
| 472 | -- [xreg*xsc+disp] -> (0, s, esp) (xsc, xreg, ebp) | ||
| 473 | wputmodrm(0, s, 4) | ||
| 474 | if imark then waction("MARK") end | ||
| 475 | if vsreg then waction("VREG", vsreg); wputxb(2) end | ||
| 476 | wputmodrm(t.xsc, xreg, 5) | ||
| 477 | if vxreg then waction("VREG", vxreg); wputxb(3) end | ||
| 478 | else | ||
| 479 | -- Pure displacement. | ||
| 480 | wputmodrm(0, s, 5) -- [disp] -> (0, s, ebp) | ||
| 481 | if imark then waction("MARK") end | ||
| 482 | if vsreg then waction("VREG", vsreg); wputxb(2) end | ||
| 483 | end | ||
| 484 | wputdarg(disp) | ||
| 485 | return | ||
| 486 | end | ||
| 487 | |||
| 488 | local m | ||
| 489 | if tdisp == "number" then -- Check displacement size at assembly time. | ||
| 490 | if disp == 0 and reg ~= 5 then -- [ebp] -> [ebp+0] (in SIB, too) | ||
| 491 | if not vreg then m = 0 end -- Force DISP to allow [Rd(5)] -> [ebp+0] | ||
| 492 | elseif disp >= -128 and disp <= 127 then m = 1 | ||
| 493 | else m = 2 end | ||
| 494 | elseif tdisp == "table" then | ||
| 495 | m = 2 | ||
| 496 | end | ||
| 497 | |||
| 498 | -- Index register present or esp as base register: need SIB encoding. | ||
| 499 | if xreg or reg == 4 then | ||
| 500 | wputmodrm(m or 2, s, 4) -- ModRM. | ||
| 501 | if m == nil or imark then waction("MARK") end | ||
| 502 | if vsreg then waction("VREG", vsreg); wputxb(2) end | ||
| 503 | wputmodrm(t.xsc or 0, xreg or 4, reg) -- SIB. | ||
| 504 | if vxreg then waction("VREG", vxreg); wputxb(3) end | ||
| 505 | if vreg then waction("VREG", vreg); wputxb(1) end | ||
| 506 | else | ||
| 507 | wputmodrm(m or 2, s, reg) -- ModRM. | ||
| 508 | if (imark and (m == 1 or m == 2)) or | ||
| 509 | (m == nil and (vsreg or vreg)) then waction("MARK") end | ||
| 510 | if vsreg then waction("VREG", vsreg); wputxb(2) end | ||
| 511 | if vreg then waction("VREG", vreg); wputxb(1) end | ||
| 512 | end | ||
| 513 | |||
| 514 | -- Put displacement. | ||
| 515 | if m == 1 then wputsbarg(disp) | ||
| 516 | elseif m == 2 then wputdarg(disp) | ||
| 517 | elseif m == nil then waction("DISP", disp) end | ||
| 518 | end | ||
| 519 | |||
| 520 | ------------------------------------------------------------------------------ | ||
| 521 | |||
| 522 | -- Return human-readable operand mode string. | ||
| 523 | local function opmodestr(op, args) | ||
| 524 | local m = {} | ||
| 525 | for i=1,#args do | ||
| 526 | local a = args[i] | ||
| 527 | m[#m+1] = sub(a.mode, 1, 1)..(a.opsize or "?") | ||
| 528 | end | ||
| 529 | return op.." "..concat(m, ",") | ||
| 530 | end | ||
| 531 | |||
| 532 | -- Convert number to valid integer or nil. | ||
| 533 | local function toint(expr) | ||
| 534 | local n = tonumber(expr) | ||
| 535 | if n then | ||
| 536 | if n % 1 ~= 0 or n < -2147483648 or n > 4294967295 then | ||
| 537 | werror("bad integer number `"..expr.."'") | ||
| 538 | end | ||
| 539 | return n | ||
| 540 | end | ||
| 541 | end | ||
| 542 | |||
| 543 | -- Parse immediate expression. | ||
| 544 | local function immexpr(expr) | ||
| 545 | -- &expr (pointer) | ||
| 546 | if sub(expr, 1, 1) == "&" then | ||
| 547 | return "iPJ", format("(ptrdiff_t)(%s)", sub(expr,2)) | ||
| 548 | end | ||
| 549 | |||
| 550 | local prefix = sub(expr, 1, 2) | ||
| 551 | -- =>expr (pc label reference) | ||
| 552 | if prefix == "=>" then | ||
| 553 | return "iJ", sub(expr, 3) | ||
| 554 | end | ||
| 555 | -- ->name (global label reference) | ||
| 556 | if prefix == "->" then | ||
| 557 | return "iJ", map_global[sub(expr, 3)] | ||
| 558 | end | ||
| 559 | |||
| 560 | -- [<>][1-9] (local label reference) | ||
| 561 | local dir, lnum = match(expr, "^([<>])([1-9])$") | ||
| 562 | if dir then -- Fwd: 247-255, Bkwd: 1-9. | ||
| 563 | return "iJ", lnum + (dir == ">" and 246 or 0) | ||
| 564 | end | ||
| 565 | |||
| 566 | local extname = match(expr, "^extern%s+(%S+)$") | ||
| 567 | if extname then | ||
| 568 | return "iJ", map_extern[extname] | ||
| 569 | end | ||
| 570 | |||
| 571 | -- expr (interpreted as immediate) | ||
| 572 | return "iI", expr | ||
| 573 | end | ||
| 574 | |||
| 575 | -- Parse displacement expression: +-num, +-expr, +-opsize*num | ||
| 576 | local function dispexpr(expr) | ||
| 577 | local disp = expr == "" and 0 or toint(expr) | ||
| 578 | if disp then return disp end | ||
| 579 | local c, dispt = match(expr, "^([+-])%s*(.+)$") | ||
| 580 | if c == "+" then | ||
| 581 | expr = dispt | ||
| 582 | elseif not c then | ||
| 583 | werror("bad displacement expression `"..expr.."'") | ||
| 584 | end | ||
| 585 | local opsize, tailops = match(dispt, "^(%w+)%s*%*%s*(.+)$") | ||
| 586 | local ops, imm = map_opsize[opsize], toint(tailops) | ||
| 587 | if ops and imm then | ||
| 588 | if c == "-" then imm = -imm end | ||
| 589 | return imm*map_opsizenum[ops] | ||
| 590 | end | ||
| 591 | local mode, iexpr = immexpr(dispt) | ||
| 592 | if mode == "iJ" then | ||
| 593 | if c == "-" then werror("cannot invert label reference") end | ||
| 594 | return { iexpr } | ||
| 595 | end | ||
| 596 | return expr -- Need to return original signed expression. | ||
| 597 | end | ||
| 598 | |||
| 599 | -- Parse register or type expression. | ||
| 600 | local function rtexpr(expr) | ||
| 601 | if not expr then return end | ||
| 602 | local tname, ovreg = match(expr, "^([%w_]+):(@[%w_]+)$") | ||
| 603 | local tp = map_type[tname or expr] | ||
| 604 | if tp then | ||
| 605 | local reg = ovreg or tp.reg | ||
| 606 | local rnum = map_reg_num[reg] | ||
| 607 | if not rnum then | ||
| 608 | werror("type `"..(tname or expr).."' needs a register override") | ||
| 609 | end | ||
| 610 | if not map_reg_valid_base[reg] then | ||
| 611 | werror("bad base register override `"..(map_reg_rev[reg] or reg).."'") | ||
| 612 | end | ||
| 613 | return reg, rnum, tp | ||
| 614 | end | ||
| 615 | return expr, map_reg_num[expr] | ||
| 616 | end | ||
| 617 | |||
| 618 | -- Parse operand and return { mode, opsize, reg, xreg, xsc, disp, imm }. | ||
| 619 | local function parseoperand(param) | ||
| 620 | local t = {} | ||
| 621 | |||
| 622 | local expr = param | ||
| 623 | local opsize, tailops = match(param, "^(%w+)%s*(.+)$") | ||
| 624 | if opsize then | ||
| 625 | t.opsize = map_opsize[opsize] | ||
| 626 | if t.opsize then expr = tailops end | ||
| 627 | end | ||
| 628 | |||
| 629 | local br = match(expr, "^%[%s*(.-)%s*%]$") | ||
| 630 | repeat | ||
| 631 | if br then | ||
| 632 | t.mode = "xm" | ||
| 633 | |||
| 634 | -- [disp] | ||
| 635 | t.disp = toint(br) | ||
| 636 | if t.disp then | ||
| 637 | t.mode = "xmO" | ||
| 638 | break | ||
| 639 | end | ||
| 640 | |||
| 641 | -- [reg...] | ||
| 642 | local tp | ||
| 643 | local reg, tailr = match(br, "^([@%w_:]+)%s*(.*)$") | ||
| 644 | reg, t.reg, tp = rtexpr(reg) | ||
| 645 | if not t.reg then | ||
| 646 | -- [expr] | ||
| 647 | t.mode = "xmO" | ||
| 648 | t.disp = dispexpr("+"..br) | ||
| 649 | break | ||
| 650 | end | ||
| 651 | |||
| 652 | if t.reg == -1 then | ||
| 653 | t.vreg, tailr = match(tailr, "^(%b())(.*)$") | ||
| 654 | if not t.vreg then werror("bad variable register expression") end | ||
| 655 | end | ||
| 656 | |||
| 657 | -- [xreg*xsc] or [xreg*xsc+-disp] or [xreg*xsc+-expr] | ||
| 658 | local xsc, tailsc = match(tailr, "^%*%s*([1248])%s*(.*)$") | ||
| 659 | if xsc then | ||
| 660 | if not map_reg_valid_index[reg] then | ||
| 661 | werror("bad index register `"..map_reg_rev[reg].."'") | ||
| 662 | end | ||
| 663 | t.xsc = map_xsc[xsc] | ||
| 664 | t.xreg = t.reg | ||
| 665 | t.vxreg = t.vreg | ||
| 666 | t.reg = nil | ||
| 667 | t.vreg = nil | ||
| 668 | t.disp = dispexpr(tailsc) | ||
| 669 | break | ||
| 670 | end | ||
| 671 | if not map_reg_valid_base[reg] then | ||
| 672 | werror("bad base register `"..map_reg_rev[reg].."'") | ||
| 673 | end | ||
| 674 | |||
| 675 | -- [reg] or [reg+-disp] | ||
| 676 | t.disp = toint(tailr) or (tailr == "" and 0) | ||
| 677 | if t.disp then break end | ||
| 678 | |||
| 679 | -- [reg+xreg...] | ||
| 680 | local xreg, tailx = match(tailr, "^+%s*([@%w_:]+)%s*(.*)$") | ||
| 681 | xreg, t.xreg, tp = rtexpr(xreg) | ||
| 682 | if not t.xreg then | ||
| 683 | -- [reg+-expr] | ||
| 684 | t.disp = dispexpr(tailr) | ||
| 685 | break | ||
| 686 | end | ||
| 687 | if not map_reg_valid_index[xreg] then | ||
| 688 | werror("bad index register `"..map_reg_rev[xreg].."'") | ||
| 689 | end | ||
| 690 | |||
| 691 | if t.xreg == -1 then | ||
| 692 | t.vxreg, tailx = match(tailx, "^(%b())(.*)$") | ||
| 693 | if not t.vxreg then werror("bad variable register expression") end | ||
| 694 | end | ||
| 695 | |||
| 696 | -- [reg+xreg*xsc...] | ||
| 697 | local xsc, tailsc = match(tailx, "^%*%s*([1248])%s*(.*)$") | ||
| 698 | if xsc then | ||
| 699 | t.xsc = map_xsc[xsc] | ||
| 700 | tailx = tailsc | ||
| 701 | end | ||
| 702 | |||
| 703 | -- [...] or [...+-disp] or [...+-expr] | ||
| 704 | t.disp = dispexpr(tailx) | ||
| 705 | else | ||
| 706 | -- imm or opsize*imm | ||
| 707 | local imm = toint(expr) | ||
| 708 | if not imm and sub(expr, 1, 1) == "*" and t.opsize then | ||
| 709 | imm = toint(sub(expr, 2)) | ||
| 710 | if imm then | ||
| 711 | imm = imm * map_opsizenum[t.opsize] | ||
| 712 | t.opsize = nil | ||
| 713 | end | ||
| 714 | end | ||
| 715 | if imm then | ||
| 716 | if t.opsize then werror("bad operand size override") end | ||
| 717 | local m = "i" | ||
| 718 | if imm == 1 then m = m.."1" end | ||
| 719 | if imm >= 4294967168 and imm <= 4294967295 then imm = imm-4294967296 end | ||
| 720 | if imm >= -128 and imm <= 127 then m = m.."S" end | ||
| 721 | t.imm = imm | ||
| 722 | t.mode = m | ||
| 723 | break | ||
| 724 | end | ||
| 725 | |||
| 726 | local tp | ||
| 727 | local reg, tailr = match(expr, "^([@%w_:]+)%s*(.*)$") | ||
| 728 | reg, t.reg, tp = rtexpr(reg) | ||
| 729 | if t.reg then | ||
| 730 | if t.reg == -1 then | ||
| 731 | t.vreg, tailr = match(tailr, "^(%b())(.*)$") | ||
| 732 | if not t.vreg then werror("bad variable register expression") end | ||
| 733 | end | ||
| 734 | -- reg | ||
| 735 | if tailr == "" then | ||
| 736 | if t.opsize then werror("bad operand size override") end | ||
| 737 | t.opsize = map_reg_opsize[reg] | ||
| 738 | if t.opsize == "f" then | ||
| 739 | t.mode = t.reg == 0 and "fF" or "f" | ||
| 740 | else | ||
| 741 | if reg == "@w4" then wwarn("bad idea, try again with `esp'") end | ||
| 742 | t.mode = t.reg == 0 and "rmR" or (reg == "@b1" and "rmC" or "rm") | ||
| 743 | end | ||
| 744 | break | ||
| 745 | end | ||
| 746 | |||
| 747 | -- type[idx], type[idx].field, type->field -> [reg+offset_expr] | ||
| 748 | if not tp then werror("bad operand `"..param.."'") end | ||
| 749 | t.mode = "xm" | ||
| 750 | t.disp = format(tp.ctypefmt, tailr) | ||
| 751 | else | ||
| 752 | t.mode, t.imm = immexpr(expr) | ||
| 753 | if sub(t.mode, -1) == "J" then | ||
| 754 | if t.opsize and t.opsize ~= addrsize then | ||
| 755 | werror("bad operand size override") | ||
| 756 | end | ||
| 757 | t.opsize = addrsize | ||
| 758 | end | ||
| 759 | end | ||
| 760 | end | ||
| 761 | until true | ||
| 762 | return t | ||
| 763 | end | ||
| 764 | |||
| 765 | ------------------------------------------------------------------------------ | ||
| 766 | -- x86 Template String Description | ||
| 767 | -- =============================== | ||
| 768 | -- | ||
| 769 | -- Each template string is a list of [match:]pattern pairs, | ||
| 770 | -- separated by "|". The first match wins. No match means a | ||
| 771 | -- bad or unsupported combination of operand modes or sizes. | ||
| 772 | -- | ||
| 773 | -- The match part and the ":" is omitted if the operation has | ||
| 774 | -- no operands. Otherwise the first N characters are matched | ||
| 775 | -- against the mode strings of each of the N operands. | ||
| 776 | -- | ||
| 777 | -- The mode string for each operand type is (see parseoperand()): | ||
| 778 | -- Integer register: "rm", +"R" for eax, ax, al, +"C" for cl | ||
| 779 | -- FP register: "f", +"F" for st0 | ||
| 780 | -- Index operand: "xm", +"O" for [disp] (pure offset) | ||
| 781 | -- Immediate: "i", +"S" for signed 8 bit, +"1" for 1, | ||
| 782 | -- +"I" for arg, +"P" for pointer | ||
| 783 | -- Any: +"J" for valid jump targets | ||
| 784 | -- | ||
| 785 | -- So a match character "m" (mixed) matches both an integer register | ||
| 786 | -- and an index operand (to be encoded with the ModRM/SIB scheme). | ||
| 787 | -- But "r" matches only a register and "x" only an index operand | ||
| 788 | -- (e.g. for FP memory access operations). | ||
| 789 | -- | ||
| 790 | -- The operand size match string starts right after the mode match | ||
| 791 | -- characters and ends before the ":". "dwb" is assumed, if empty. | ||
| 792 | -- The effective data size of the operation is matched against this list. | ||
| 793 | -- | ||
| 794 | -- If only the regular "b", "w", "d", "q", "t" operand sizes are | ||
| 795 | -- present, then all operands must be the same size. Unspecified sizes | ||
| 796 | -- are ignored, but at least one operand must have a size or the pattern | ||
| 797 | -- won't match (use the "byte", "word", "dword", "qword", "tword" | ||
| 798 | -- operand size overrides. E.g.: mov dword [eax], 1). | ||
| 799 | -- | ||
| 800 | -- If the list has a "1" or "2" prefix, the operand size is taken | ||
| 801 | -- from the respective operand and any other operand sizes are ignored. | ||
| 802 | -- If the list contains only ".", all operand sizes are ignored. | ||
| 803 | -- If the list has a "/" prefix, the concatenated (mixed) operand sizes | ||
| 804 | -- are compared to the match. | ||
| 805 | -- | ||
| 806 | -- E.g. "rrdw" matches for either two dword registers or two word | ||
| 807 | -- registers. "Fx2dq" matches an st0 operand plus an index operand | ||
| 808 | -- pointing to a dword (float) or qword (double). | ||
| 809 | -- | ||
| 810 | -- Every character after the ":" is part of the pattern string: | ||
| 811 | -- Hex chars are accumulated to form the opcode (left to right). | ||
| 812 | -- "n" disables the standard opcode mods | ||
| 813 | -- (otherwise: -1 for "b", o16 prefix for "w") | ||
| 814 | -- "r"/"R" adds the reg. number from the 1st/2nd operand to the opcode. | ||
| 815 | -- "m"/"M" generates ModRM/SIB from the 1st/2nd operand. | ||
| 816 | -- The spare 3 bits are either filled with the last hex digit or | ||
| 817 | -- the result from a previous "r"/"R". The opcode is restored. | ||
| 818 | -- | ||
| 819 | -- All of the following characters force a flush of the opcode: | ||
| 820 | -- "o"/"O" stores a pure 32 bit disp (offset) from the 1st/2nd operand. | ||
| 821 | -- "S" stores a signed 8 bit immediate from the last operand. | ||
| 822 | -- "U" stores an unsigned 8 bit immediate from the last operand. | ||
| 823 | -- "W" stores an unsigned 16 bit immediate from the last operand. | ||
| 824 | -- "i" stores an operand sized immediate from the last operand. | ||
| 825 | -- "I" dito, but generates an action code to optionally modify | ||
| 826 | -- the opcode (+2) for a signed 8 bit immediate. | ||
| 827 | -- "J" generates one of the REL action codes from the last operand. | ||
| 828 | -- | ||
| 829 | ------------------------------------------------------------------------------ | ||
| 830 | |||
| 831 | -- Template strings for x86 instructions. Ordered by first opcode byte. | ||
| 832 | -- Unimplemented opcodes (deliberate omissions) are marked with *. | ||
| 833 | local map_op = { | ||
| 834 | -- 00-05: add... | ||
| 835 | -- 06: *push es | ||
| 836 | -- 07: *pop es | ||
| 837 | -- 08-0D: or... | ||
| 838 | -- 0E: *push cs | ||
| 839 | -- 0F: two byte opcode prefix | ||
| 840 | -- 10-15: adc... | ||
| 841 | -- 16: *push ss | ||
| 842 | -- 17: *pop ss | ||
| 843 | -- 18-1D: sbb... | ||
| 844 | -- 1E: *push ds | ||
| 845 | -- 1F: *pop ds | ||
| 846 | -- 20-25: and... | ||
| 847 | es_0 = "26", | ||
| 848 | -- 27: *daa | ||
| 849 | -- 28-2D: sub... | ||
| 850 | cs_0 = "2E", | ||
| 851 | -- 2F: *das | ||
| 852 | -- 30-35: xor... | ||
| 853 | ss_0 = "36", | ||
| 854 | -- 37: *aaa | ||
| 855 | -- 38-3D: cmp... | ||
| 856 | ds_0 = "3E", | ||
| 857 | -- 3F: *aas | ||
| 858 | inc_1 = "rdw:40r|m:FF0m", | ||
| 859 | dec_1 = "rdw:48r|m:FF1m", | ||
| 860 | push_1 = "rdw:50r|mdw:FF6m|S.:6AS|ib:n6Ai|i.:68i", | ||
| 861 | pop_1 = "rdw:58r|mdw:8F0m", | ||
| 862 | -- 60: *pusha, *pushad, *pushaw | ||
| 863 | -- 61: *popa, *popad, *popaw | ||
| 864 | -- 62: *bound rdw,x | ||
| 865 | -- 63: *arpl mw,rw | ||
| 866 | fs_0 = "64", | ||
| 867 | gs_0 = "65", | ||
| 868 | o16_0 = "66", | ||
| 869 | a16_0 = "67", | ||
| 870 | -- 68: push idw | ||
| 871 | -- 69: imul rdw,mdw,idw | ||
| 872 | -- 6A: push ib | ||
| 873 | -- 6B: imul rdw,mdw,S | ||
| 874 | -- 6C: *insb | ||
| 875 | -- 6D: *insd, *insw | ||
| 876 | -- 6E: *outsb | ||
| 877 | -- 6F: *outsd, *outsw | ||
| 878 | -- 70-7F: jcc lb | ||
| 879 | -- 80: add... mb,i | ||
| 880 | -- 81: add... mdw,i | ||
| 881 | -- 82: *undefined | ||
| 882 | -- 83: add... mdw,S | ||
| 883 | test_2 = "mr:85Rm|rm:85rM|Ri:A9ri|mi:F70mi", | ||
| 884 | -- 86: xchg rb,mb | ||
| 885 | -- 87: xchg rdw,mdw | ||
| 886 | -- 88: mov mb,r | ||
| 887 | -- 89: mov mdw,r | ||
| 888 | -- 8A: mov r,mb | ||
| 889 | -- 8B: mov r,mdw | ||
| 890 | -- 8C: *mov mdw,seg | ||
| 891 | lea_2 = "rxd:8DrM", | ||
| 892 | -- 8E: *mov seg,mdw | ||
| 893 | -- 8F: pop mdw | ||
| 894 | nop_0 = "90", | ||
| 895 | xchg_2 = "Rrdw:90R|rRdw:90r|rm:87rM|mr:87Rm", | ||
| 896 | cbw_0 = "6698", | ||
| 897 | cwde_0 = "98", | ||
| 898 | cwd_0 = "6699", | ||
| 899 | cdq_0 = "99", | ||
| 900 | -- 9A: *call iw:idw | ||
| 901 | wait_0 = "9B", | ||
| 902 | fwait_0 = "9B", | ||
| 903 | pushf_0 = "9C", | ||
| 904 | pushfw_0 = "669C", | ||
| 905 | pushfd_0 = "9C", | ||
| 906 | popf_0 = "9D", | ||
| 907 | popfw_0 = "669D", | ||
| 908 | popfd_0 = "9D", | ||
| 909 | sahf_0 = "9E", | ||
| 910 | lahf_0 = "9F", | ||
| 911 | mov_2 = "OR:A3o|RO:A1O|mr:89Rm|rm:8BrM|rib:nB0ri|ridw:B8ri|mi:C70mi", | ||
| 912 | movsb_0 = "A4", | ||
| 913 | movsw_0 = "66A5", | ||
| 914 | movsd_0 = "A5", | ||
| 915 | cmpsb_0 = "A6", | ||
| 916 | cmpsw_0 = "66A7", | ||
| 917 | cmpsd_0 = "A7", | ||
| 918 | -- A8: test Rb,i | ||
| 919 | -- A9: test Rdw,i | ||
| 920 | stosb_0 = "AA", | ||
| 921 | stosw_0 = "66AB", | ||
| 922 | stosd_0 = "AB", | ||
| 923 | lodsb_0 = "AC", | ||
| 924 | lodsw_0 = "66AD", | ||
| 925 | lodsd_0 = "AD", | ||
| 926 | scasb_0 = "AE", | ||
| 927 | scasw_0 = "66AF", | ||
| 928 | scasd_0 = "AF", | ||
| 929 | -- B0-B7: mov rb,i | ||
| 930 | -- B8-BF: mov rdw,i | ||
| 931 | -- C0: rol... mb,i | ||
| 932 | -- C1: rol... mdw,i | ||
| 933 | ret_1 = "i.:nC2W", | ||
| 934 | ret_0 = "C3", | ||
| 935 | -- C4: *les rdw,mq | ||
| 936 | -- C5: *lds rdw,mq | ||
| 937 | -- C6: mov mb,i | ||
| 938 | -- C7: mov mdw,i | ||
| 939 | -- C8: *enter iw,ib | ||
| 940 | leave_0 = "C9", | ||
| 941 | -- CA: *retf iw | ||
| 942 | -- CB: *retf | ||
| 943 | int3_0 = "CC", | ||
| 944 | int_1 = "i.:nCDU", | ||
| 945 | into_0 = "CE", | ||
| 946 | -- CF: *iret | ||
| 947 | -- D0: rol... mb,1 | ||
| 948 | -- D1: rol... mdw,1 | ||
| 949 | -- D2: rol... mb,cl | ||
| 950 | -- D3: rol... mb,cl | ||
| 951 | -- D4: *aam ib | ||
| 952 | -- D5: *aad ib | ||
| 953 | -- D6: *salc | ||
| 954 | -- D7: *xlat | ||
| 955 | -- D8-DF: floating point ops | ||
| 956 | -- E0: *loopne | ||
| 957 | -- E1: *loope | ||
| 958 | -- E2: *loop | ||
| 959 | -- E3: *jcxz, *jecxz | ||
| 960 | -- E4: *in Rb,ib | ||
| 961 | -- E5: *in Rdw,ib | ||
| 962 | -- E6: *out ib,Rb | ||
| 963 | -- E7: *out ib,Rdw | ||
| 964 | call_1 = "md:FF2m|J.:E8J", | ||
| 965 | jmp_1 = "md:FF4m|J.:E9J", -- short: EB | ||
| 966 | -- EA: *jmp iw:idw | ||
| 967 | -- EB: jmp ib | ||
| 968 | -- EC: *in Rb,dx | ||
| 969 | -- ED: *in Rdw,dx | ||
| 970 | -- EE: *out dx,Rb | ||
| 971 | -- EF: *out dx,Rdw | ||
| 972 | -- F0: *lock | ||
| 973 | int1_0 = "F1", | ||
| 974 | repne_0 = "F2", | ||
| 975 | repnz_0 = "F2", | ||
| 976 | rep_0 = "F3", | ||
| 977 | repe_0 = "F3", | ||
| 978 | repz_0 = "F3", | ||
| 979 | -- F4: *hlt | ||
| 980 | cmc_0 = "F5", | ||
| 981 | -- F6: test... mb,i; div... mb | ||
| 982 | -- F7: test... mdw,i; div... mdw | ||
| 983 | clc_0 = "F8", | ||
| 984 | stc_0 = "F9", | ||
| 985 | -- FA: *cli | ||
| 986 | cld_0 = "FC", | ||
| 987 | std_0 = "FD", | ||
| 988 | -- FE: inc... mb | ||
| 989 | -- FF: inc... mdw | ||
| 990 | |||
| 991 | -- misc ops | ||
| 992 | not_1 = "m:F72m", | ||
| 993 | neg_1 = "m:F73m", | ||
| 994 | mul_1 = "m:F74m", | ||
| 995 | imul_1 = "m:F75m", | ||
| 996 | div_1 = "m:F76m", | ||
| 997 | idiv_1 = "m:F77m", | ||
| 998 | |||
| 999 | imul_2 = "rmdw:0FAFrM|rIdw:69rmI|rSdw:6BrmS|ridw:69rmi", | ||
| 1000 | imul_3 = "rmIdw:69rMI|rmSdw:6BrMS|rmidw:69rMi", | ||
| 1001 | |||
| 1002 | movzx_2 = "rm/db:0FB6rM|rm/wb:0FB6rM|rm/dw:0FB7rM", | ||
| 1003 | movsx_2 = "rm/db:0FBErM|rm/wb:0FBErM|rm/dw:0FBFrM", | ||
| 1004 | |||
| 1005 | bswap_1 = "rd:0FC8r", | ||
| 1006 | bsf_2 = "rmdw:0FBCrM", | ||
| 1007 | bsr_2 = "rmdw:0FBDrM", | ||
| 1008 | bt_2 = "mrdw:0FA3Rm|midw:0FBA4mU", | ||
| 1009 | btc_2 = "mrdw:0FBBRm|midw:0FBA7mU", | ||
| 1010 | btr_2 = "mrdw:0FB3Rm|midw:0FBA6mU", | ||
| 1011 | bts_2 = "mrdw:0FABRm|midw:0FBA5mU", | ||
| 1012 | |||
| 1013 | rdtsc_0 = "0F31", -- P1+ | ||
| 1014 | cpuid_0 = "0FA2", -- P1+ | ||
| 1015 | |||
| 1016 | -- floating point ops | ||
| 1017 | fst_1 = "ff:DDD0r|xd:D92m|xq:DD2m", | ||
| 1018 | fstp_1 = "ff:DDD8r|xd:D93m|xq:DD3m|xt:DB7m", | ||
| 1019 | fld_1 = "ff:D9C0r|xd:D90m|xq:DD0m|xt:DB5m", | ||
| 1020 | |||
| 1021 | fpop_0 = "DDD8", -- Alias for fstp st0. | ||
| 1022 | |||
| 1023 | fist_1 = "xw:nDF2m|xd:DB2m", | ||
| 1024 | fistp_1 = "xw:nDF3m|xd:DB3m|xq:DF7m", | ||
| 1025 | fild_1 = "xw:nDF0m|xd:DB0m|xq:DF5m", | ||
| 1026 | |||
| 1027 | fxch_0 = "D9C9", | ||
| 1028 | fxch_1 = "ff:D9C8r", | ||
| 1029 | fxch_2 = "fFf:D9C8r|Fff:D9C8R", | ||
| 1030 | |||
| 1031 | fucom_1 = "ff:DDE0r", | ||
| 1032 | fucom_2 = "Fff:DDE0R", | ||
| 1033 | fucomp_1 = "ff:DDE8r", | ||
| 1034 | fucomp_2 = "Fff:DDE8R", | ||
| 1035 | fucomi_1 = "ff:DBE8r", -- P6+ | ||
| 1036 | fucomi_2 = "Fff:DBE8R", -- P6+ | ||
| 1037 | fucomip_1 = "ff:DFE8r", -- P6+ | ||
| 1038 | fucomip_2 = "Fff:DFE8R", -- P6+ | ||
| 1039 | fcomi_1 = "ff:DBF0r", -- P6+ | ||
| 1040 | fcomi_2 = "Fff:DBF0R", -- P6+ | ||
| 1041 | fcomip_1 = "ff:DFF0r", -- P6+ | ||
| 1042 | fcomip_2 = "Fff:DFF0R", -- P6+ | ||
| 1043 | fucompp_0 = "DAE9", | ||
| 1044 | fcompp_0 = "DED9", | ||
| 1045 | |||
| 1046 | fldcw_1 = "xw:nD95m", | ||
| 1047 | fstcw_1 = "xw:n9BD97m", | ||
| 1048 | fnstcw_1 = "xw:nD97m", | ||
| 1049 | fstsw_1 = "Rw:n9BDFE0|xw:n9BDD7m", | ||
| 1050 | fnstsw_1 = "Rw:nDFE0|xw:nDD7m", | ||
| 1051 | fclex_0 = "9BDBE2", | ||
| 1052 | fnclex_0 = "DBE2", | ||
| 1053 | |||
| 1054 | fnop_0 = "D9D0", | ||
| 1055 | -- D9D1-D9DF: unassigned | ||
| 1056 | |||
| 1057 | fchs_0 = "D9E0", | ||
| 1058 | fabs_0 = "D9E1", | ||
| 1059 | -- D9E2: unassigned | ||
| 1060 | -- D9E3: unassigned | ||
| 1061 | ftst_0 = "D9E4", | ||
| 1062 | fxam_0 = "D9E5", | ||
| 1063 | -- D9E6: unassigned | ||
| 1064 | -- D9E7: unassigned | ||
| 1065 | fld1_0 = "D9E8", | ||
| 1066 | fldl2t_0 = "D9E9", | ||
| 1067 | fldl2e_0 = "D9EA", | ||
| 1068 | fldpi_0 = "D9EB", | ||
| 1069 | fldlg2_0 = "D9EC", | ||
| 1070 | fldln2_0 = "D9ED", | ||
| 1071 | fldz_0 = "D9EE", | ||
| 1072 | -- D9EF: unassigned | ||
| 1073 | |||
| 1074 | f2xm1_0 = "D9F0", | ||
| 1075 | fyl2x_0 = "D9F1", | ||
| 1076 | fptan_0 = "D9F2", | ||
| 1077 | fpatan_0 = "D9F3", | ||
| 1078 | fxtract_0 = "D9F4", | ||
| 1079 | fprem1_0 = "D9F5", | ||
| 1080 | fdecstp_0 = "D9F6", | ||
| 1081 | fincstp_0 = "D9F7", | ||
| 1082 | fprem_0 = "D9F8", | ||
| 1083 | fyl2xp1_0 = "D9F9", | ||
| 1084 | fsqrt_0 = "D9FA", | ||
| 1085 | fsincos_0 = "D9FB", | ||
| 1086 | frndint_0 = "D9FC", | ||
| 1087 | fscale_0 = "D9FD", | ||
| 1088 | fsin_0 = "D9FE", | ||
| 1089 | fcos_0 = "D9FF", | ||
| 1090 | |||
| 1091 | -- SSE, SSE2 | ||
| 1092 | andnpd_2 = "rmo:660F55rM", | ||
| 1093 | andnps_2 = "rmo:0F55rM", | ||
| 1094 | andpd_2 = "rmo:660F54rM", | ||
| 1095 | andps_2 = "rmo:0F54rM", | ||
| 1096 | clflush_1 = "x.:0FAE7m", | ||
| 1097 | cmppd_3 = "rmio:660FC2rMU", | ||
| 1098 | cmpps_3 = "rmio:0FC2rMU", | ||
| 1099 | cmpsd_3 = "rmio:F20FC2rMU", | ||
| 1100 | cmpss_3 = "rmio:F30FC2rMU", | ||
| 1101 | comisd_2 = "rmo:660F2FrM", | ||
| 1102 | comiss_2 = "rmo:0F2FrM", | ||
| 1103 | cvtdq2pd_2 = "rro:F30FE6rM|rx/oq:", | ||
| 1104 | cvtdq2ps_2 = "rmo:0F5BrM", | ||
| 1105 | cvtpd2dq_2 = "rmo:F20FE6rM", | ||
| 1106 | cvtpd2ps_2 = "rmo:660F5ArM", | ||
| 1107 | cvtpi2pd_2 = "rx/oq:660F2ArM", | ||
| 1108 | cvtpi2ps_2 = "rx/oq:0F2ArM", | ||
| 1109 | cvtps2dq_2 = "rmo:660F5BrM", | ||
| 1110 | cvtps2pd_2 = "rro:0F5ArM|rx/oq:", | ||
| 1111 | cvtsd2si_2 = "rr/do:F20F2DrM|rx/dq:", | ||
| 1112 | cvtsd2ss_2 = "rro:F20F5ArM|rx/oq:", | ||
| 1113 | cvtsi2sd_2 = "rm/od:F20F2ArM", | ||
| 1114 | cvtsi2ss_2 = "rm/od:F30F2ArM", | ||
| 1115 | cvtss2sd_2 = "rro:F30F5ArM|rx/od:", | ||
| 1116 | cvtss2si_2 = "rr/do:F20F2CrM|rx/dd:", | ||
| 1117 | cvttpd2dq_2 = "rmo:660FE6rM", | ||
| 1118 | cvttps2dq_2 = "rmo:F30F5BrM", | ||
| 1119 | cvttsd2si_2 = "rr/do:F20F2CrM|rx/dq:", | ||
| 1120 | cvttss2si_2 = "rr/do:F30F2CrM|rx/dd:", | ||
| 1121 | ldmxcsr_1 = "xd:0FAE2m", | ||
| 1122 | lfence_0 = "0FAEE8", | ||
| 1123 | maskmovdqu_2 = "rro:660FF7rM", | ||
| 1124 | mfence_0 = "0FAEF0", | ||
| 1125 | movapd_2 = "rmo:660F28rM|mro:660F29Rm", | ||
| 1126 | movaps_2 = "rmo:0F28rM|mro:0F29Rm", | ||
| 1127 | movd_2 = "rm/od:660F6ErM|mr/do:660F7ERm", | ||
| 1128 | movdqa_2 = "rmo:660F6FrM|mro:660F7FRm", | ||
| 1129 | movdqu_2 = "rmo:F30F6FrM|mro:F30F7FRm", | ||
| 1130 | movhlps_2 = "rro:0F12rM", | ||
| 1131 | movhpd_2 = "rx/oq:660F16rM|xr/qo:660F17Rm", | ||
| 1132 | movhps_2 = "rx/oq:0F16rM|xr/qo:0F17Rm", | ||
| 1133 | movlhps_2 = "rro:0F16rM", | ||
| 1134 | movlpd_2 = "rx/oq:660F12rM|xr/qo:660F13Rm", | ||
| 1135 | movlps_2 = "rx/oq:0F12rM|xr/qo:0F13Rm", | ||
| 1136 | movmskpd_2 = "rr/do:660F50rM", | ||
| 1137 | movmskps_2 = "rr/do:0F50rM", | ||
| 1138 | movntdq_2 = "xro:660FE7Rm", | ||
| 1139 | movnti_2 = "xrd:0FC3Rm", | ||
| 1140 | movntpd_2 = "xro:660F2BRm", | ||
| 1141 | movntps_2 = "xro:0F2BRm", | ||
| 1142 | movq_2 = "rro:F30F7ErM|rx/oq:|xr/qo:660FD6Rm", | ||
| 1143 | movsd_2 = "rro:F20F10rM|rx/oq:|xr/qo:F20F11Rm", | ||
| 1144 | movss_2 = "rro:F30F10rM|rx/od:|xr/do:F30F11Rm", | ||
| 1145 | movupd_2 = "rmo:660F10rM|mro:660F11Rm", | ||
| 1146 | movups_2 = "rmo:0F10rM|mro:0F11Rm", | ||
| 1147 | orpd_2 = "rmo:660F56rM", | ||
| 1148 | orps_2 = "rmo:0F56rM", | ||
| 1149 | packssdw_2 = "rmo:660F6BrM", | ||
| 1150 | packsswb_2 = "rmo:660F63rM", | ||
| 1151 | packuswb_2 = "rmo:660F67rM", | ||
| 1152 | paddb_2 = "rmo:660FFCrM", | ||
| 1153 | paddd_2 = "rmo:660FFErM", | ||
| 1154 | paddq_2 = "rmo:660FD4rM", | ||
| 1155 | paddsb_2 = "rmo:660FECrM", | ||
| 1156 | paddsw_2 = "rmo:660FEDrM", | ||
| 1157 | paddusb_2 = "rmo:660FDCrM", | ||
| 1158 | paddusw_2 = "rmo:660FDDrM", | ||
| 1159 | paddw_2 = "rmo:660FFDrM", | ||
| 1160 | pand_2 = "rmo:660FDBrM", | ||
| 1161 | pandn_2 = "rmo:660FDFrM", | ||
| 1162 | pause_0 = "F390", | ||
| 1163 | pavgb_2 = "rmo:660FE0rM", | ||
| 1164 | pavgw_2 = "rmo:660FE3rM", | ||
| 1165 | pcmpeqb_2 = "rmo:660F74rM", | ||
| 1166 | pcmpeqd_2 = "rmo:660F76rM", | ||
| 1167 | pcmpeqw_2 = "rmo:660F75rM", | ||
| 1168 | pcmpgtb_2 = "rmo:660F64rM", | ||
| 1169 | pcmpgtd_2 = "rmo:660F66rM", | ||
| 1170 | pcmpgtw_2 = "rmo:660F65rM", | ||
| 1171 | pextrw_3 = "rri/do:660FC5rMU|xri/wo:660F3A15nrMU", -- Mem op: SSE4.1 only. | ||
| 1172 | pinsrw_3 = "rri/od:660FC4rMU|rxi/ow:", | ||
| 1173 | pmaddwd_2 = "rmo:660FF5rM", | ||
| 1174 | pmaxsw_2 = "rmo:660FEErM", | ||
| 1175 | pmaxub_2 = "rmo:660FDErM", | ||
| 1176 | pminsw_2 = "rmo:660FEArM", | ||
| 1177 | pminub_2 = "rmo:660FDArM", | ||
| 1178 | pmovmskb_2 = "rr/do:660FD7rM", | ||
| 1179 | pmulhuw_2 = "rmo:660FE4rM", | ||
| 1180 | pmulhw_2 = "rmo:660FE5rM", | ||
| 1181 | pmullw_2 = "rmo:660FD5rM", | ||
| 1182 | pmuludq_2 = "rmo:660FF4rM", | ||
| 1183 | por_2 = "rmo:660FEBrM", | ||
| 1184 | prefetchnta_1 = "xb:n0F180m", | ||
| 1185 | prefetcht0_1 = "xb:n0F181m", | ||
| 1186 | prefetcht1_1 = "xb:n0F182m", | ||
| 1187 | prefetcht2_1 = "xb:n0F183m", | ||
| 1188 | psadbw_2 = "rmo:660FF6rM", | ||
| 1189 | pshufd_3 = "rmio:660F70rMU", | ||
| 1190 | pshufhw_3 = "rmio:F30F70rMU", | ||
| 1191 | pshuflw_3 = "rmio:F20F70rMU", | ||
| 1192 | pslld_2 = "rmo:660FF2rM|rio:660F726mU", | ||
| 1193 | pslldq_2 = "rio:660F737mU", | ||
| 1194 | psllq_2 = "rmo:660FF3rM|rio:660F736mU", | ||
| 1195 | psllw_2 = "rmo:660FF1rM|rio:660F716mU", | ||
| 1196 | psrad_2 = "rmo:660FE2rM|rio:660F724mU", | ||
| 1197 | psraw_2 = "rmo:660FE1rM|rio:660F714mU", | ||
| 1198 | psrld_2 = "rmo:660FD2rM|rio:660F722mU", | ||
| 1199 | psrldq_2 = "rio:660F733mU", | ||
| 1200 | psrlq_2 = "rmo:660FD3rM|rio:660F732mU", | ||
| 1201 | psrlw_2 = "rmo:660FD1rM|rio:660F712mU", | ||
| 1202 | psubb_2 = "rmo:660FF8rM", | ||
| 1203 | psubd_2 = "rmo:660FFArM", | ||
| 1204 | psubq_2 = "rmo:660FFBrM", | ||
| 1205 | psubsb_2 = "rmo:660FE8rM", | ||
| 1206 | psubsw_2 = "rmo:660FE9rM", | ||
| 1207 | psubusb_2 = "rmo:660FD8rM", | ||
| 1208 | psubusw_2 = "rmo:660FD9rM", | ||
| 1209 | psubw_2 = "rmo:660FF9rM", | ||
| 1210 | punpckhbw_2 = "rmo:660F68rM", | ||
| 1211 | punpckhdq_2 = "rmo:660F6ArM", | ||
| 1212 | punpckhqdq_2 = "rmo:660F6DrM", | ||
| 1213 | punpckhwd_2 = "rmo:660F69rM", | ||
| 1214 | punpcklbw_2 = "rmo:660F60rM", | ||
| 1215 | punpckldq_2 = "rmo:660F62rM", | ||
| 1216 | punpcklqdq_2 = "rmo:660F6CrM", | ||
| 1217 | punpcklwd_2 = "rmo:660F61rM", | ||
| 1218 | pxor_2 = "rmo:660FEFrM", | ||
| 1219 | rcpps_2 = "rmo:0F53rM", | ||
| 1220 | rcpss_2 = "rmo:F30F53rM", | ||
| 1221 | rsqrtps_2 = "rmo:0F52rM", | ||
| 1222 | rsqrtss_2 = "rmo:F30F52rM", | ||
| 1223 | sfence_0 = "0FAEF8", | ||
| 1224 | shufpd_3 = "rmio:660FC6rMU", | ||
| 1225 | shufps_3 = "rmio:0FC6rMU", | ||
| 1226 | stmxcsr_1 = "xd:0FAE3m", | ||
| 1227 | ucomisd_2 = "rmo:660F2ErM", | ||
| 1228 | ucomiss_2 = "rmo:0F2ErM", | ||
| 1229 | unpckhpd_2 = "rmo:660F15rM", | ||
| 1230 | unpckhps_2 = "rmo:0F15rM", | ||
| 1231 | unpcklpd_2 = "rmo:660F14rM", | ||
| 1232 | unpcklps_2 = "rmo:0F14rM", | ||
| 1233 | xorpd_2 = "rmo:660F57rM", | ||
| 1234 | xorps_2 = "rmo:0F57rM", | ||
| 1235 | |||
| 1236 | -- SSE3 ops | ||
| 1237 | fisttp_1 = "xw:nDF1m|xd:DB1m|xq:DD1m", | ||
| 1238 | addsubpd_2 = "rmo:660FD0rM", | ||
| 1239 | addsubps_2 = "rmo:F20FD0rM", | ||
| 1240 | haddpd_2 = "rmo:660F7CrM", | ||
| 1241 | haddps_2 = "rmo:F20F7CrM", | ||
| 1242 | hsubpd_2 = "rmo:660F7DrM", | ||
| 1243 | hsubps_2 = "rmo:F20F7DrM", | ||
| 1244 | lddqu_2 = "rxo:F20FF0rM", | ||
| 1245 | movddup_2 = "rmo:F20F12rM", | ||
| 1246 | movshdup_2 = "rmo:F30F16rM", | ||
| 1247 | movsldup_2 = "rmo:F30F12rM", | ||
| 1248 | |||
| 1249 | -- SSSE3 ops | ||
| 1250 | pabsb_2 = "rmo:660F381CrM", | ||
| 1251 | pabsd_2 = "rmo:660F381ErM", | ||
| 1252 | pabsw_2 = "rmo:660F381DrM", | ||
| 1253 | palignr_3 = "rmio:660F3A0FrMU", | ||
| 1254 | phaddd_2 = "rmo:660F3802rM", | ||
| 1255 | phaddsw_2 = "rmo:660F3803rM", | ||
| 1256 | phaddw_2 = "rmo:660F3801rM", | ||
| 1257 | phsubd_2 = "rmo:660F3806rM", | ||
| 1258 | phsubsw_2 = "rmo:660F3807rM", | ||
| 1259 | phsubw_2 = "rmo:660F3805rM", | ||
| 1260 | pmaddubsw_2 = "rmo:660F3804rM", | ||
| 1261 | pmulhrsw_2 = "rmo:660F380BrM", | ||
| 1262 | pshufb_2 = "rmo:660F3800rM", | ||
| 1263 | psignb_2 = "rmo:660F3808rM", | ||
| 1264 | psignd_2 = "rmo:660F380ArM", | ||
| 1265 | psignw_2 = "rmo:660F3809rM", | ||
| 1266 | |||
| 1267 | -- SSE4.1 ops | ||
| 1268 | blendpd_3 = "rmio:660F3A0DrMU", | ||
| 1269 | blendps_3 = "rmio:660F3A0CrMU", | ||
| 1270 | blendvpd_3 = "rmRo:660F3815rM", | ||
| 1271 | blendvps_3 = "rmRo:660F3814rM", | ||
| 1272 | dppd_3 = "rmio:660F3A41rMU", | ||
| 1273 | dpps_3 = "rmio:660F3A40rMU", | ||
| 1274 | extractps_3 = "mri/do:660F3A17RmU", | ||
| 1275 | insertps_3 = "rrio:660F3A41rMU|rxi/od:", | ||
| 1276 | movntdqa_2 = "rmo:660F382ArM", | ||
| 1277 | mpsadbw_3 = "rmio:660F3A42rMU", | ||
| 1278 | packusdw_2 = "rmo:660F382BrM", | ||
| 1279 | pblendvb_3 = "rmRo:660F3810rM", | ||
| 1280 | pblendw_3 = "rmio:660F3A0ErMU", | ||
| 1281 | pcmpeqq_2 = "rmo:660F3829rM", | ||
| 1282 | pextrb_3 = "rri/do:660F3A14nRmU|xri/bo:", | ||
| 1283 | pextrd_3 = "mri/do:660F3A16RmU", | ||
| 1284 | -- x64: pextrq | ||
| 1285 | -- pextrw is SSE2, mem operand is SSE4.1 only | ||
| 1286 | phminposuw_2 = "rmo:660F3841rM", | ||
| 1287 | pinsrb_3 = "rri/od:660F3A20nrMU|rxi/ob:", | ||
| 1288 | pinsrd_3 = "rmi/od:660F3A22rMU", | ||
| 1289 | -- x64: pinsrq | ||
| 1290 | pmaxsb_2 = "rmo:660F383CrM", | ||
| 1291 | pmaxsd_2 = "rmo:660F383DrM", | ||
| 1292 | pmaxud_2 = "rmo:660F383FrM", | ||
| 1293 | pmaxuw_2 = "rmo:660F383ErM", | ||
| 1294 | pminsb_2 = "rmo:660F3838rM", | ||
| 1295 | pminsd_2 = "rmo:660F3839rM", | ||
| 1296 | pminud_2 = "rmo:660F383BrM", | ||
| 1297 | pminuw_2 = "rmo:660F383ArM", | ||
| 1298 | pmovsxbd_2 = "rro:660F3821rM|rx/od:", | ||
| 1299 | pmovsxbq_2 = "rro:660F3822rM|rx/ow:", | ||
| 1300 | pmovsxbw_2 = "rro:660F3820rM|rx/oq:", | ||
| 1301 | pmovsxdq_2 = "rro:660F3825rM|rx/oq:", | ||
| 1302 | pmovsxwd_2 = "rro:660F3823rM|rx/oq:", | ||
| 1303 | pmovsxwq_2 = "rro:660F3824rM|rx/od:", | ||
| 1304 | pmovzxbd_2 = "rro:660F3831rM|rx/od:", | ||
| 1305 | pmovzxbq_2 = "rro:660F3832rM|rx/ow:", | ||
| 1306 | pmovzxbw_2 = "rro:660F3830rM|rx/oq:", | ||
| 1307 | pmovzxdq_2 = "rro:660F3835rM|rx/oq:", | ||
| 1308 | pmovzxwd_2 = "rro:660F3833rM|rx/oq:", | ||
| 1309 | pmovzxwq_2 = "rro:660F3834rM|rx/od:", | ||
| 1310 | pmuldq_2 = "rmo:660F3828rM", | ||
| 1311 | pmulld_2 = "rmo:660F3840rM", | ||
| 1312 | ptest_2 = "rmo:660F3817rM", | ||
| 1313 | roundpd_3 = "rmio:660F3A09rMU", | ||
| 1314 | roundps_3 = "rmio:660F3A08rMU", | ||
| 1315 | roundsd_3 = "rrio:660F3A0BrMU|rxi/oq:", | ||
| 1316 | roundss_3 = "rrio:660F3A0ArMU|rxi/od:", | ||
| 1317 | |||
| 1318 | -- SSE4.2 ops | ||
| 1319 | crc32_2 = "rmd:F20F38F1rM|rm/dw:66F20F38F1rM|rm/db:F20F38F0nrM", | ||
| 1320 | pcmpestri_3 = "rmio:660F3A61rMU", | ||
| 1321 | pcmpestrm_3 = "rmio:660F3A60rMU", | ||
| 1322 | pcmpgtq_2 = "rmo:660F3837rM", | ||
| 1323 | pcmpistri_3 = "rmio:660F3A63rMU", | ||
| 1324 | pcmpistrm_3 = "rmio:660F3A62rMU", | ||
| 1325 | popcnt_2 = "rmdw:F30FB8rM", | ||
| 1326 | |||
| 1327 | -- SSE4a | ||
| 1328 | extrq_2 = "rro:660F79rM", | ||
| 1329 | extrq_3 = "riio:660F780mUU", | ||
| 1330 | insertq_2 = "rro:F20F79rM", | ||
| 1331 | insertq_4 = "rriio:F20F78rMUU", | ||
| 1332 | lzcnt_2 = "rmdw:F30FBDrM", | ||
| 1333 | movntsd_2 = "xr/qo:F20F2BRm", | ||
| 1334 | movntss_2 = "xr/do:F30F2BRm", | ||
| 1335 | -- popcnt is also in SSE4.2 | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | ------------------------------------------------------------------------------ | ||
| 1339 | |||
| 1340 | -- Arithmetic ops. | ||
| 1341 | for name,n in pairs{ add = 0, ["or"] = 1, adc = 2, sbb = 3, | ||
| 1342 | ["and"] = 4, sub = 5, xor = 6, cmp = 7 } do | ||
| 1343 | local n8 = n * 8 | ||
| 1344 | map_op[name.."_2"] = format( | ||
| 1345 | "mr:%02XRm|rm:%02XrM|mI1dw:81%XmI|mS1dw:83%XmS|Ri1dwb:%02Xri|mi1dwb:81%Xmi", | ||
| 1346 | 1+n8, 3+n8, n, n, 5+n8, n) | ||
| 1347 | end | ||
| 1348 | |||
| 1349 | -- Shift ops. | ||
| 1350 | for name,n in pairs{ rol = 0, ror = 1, rcl = 2, rcr = 3, | ||
| 1351 | shl = 4, shr = 5, sar = 7, sal = 4 } do | ||
| 1352 | map_op[name.."_2"] = format("m1:D1%Xm|mC1dwb:D3%Xm|mi:C1%XmU", n, n, n) | ||
| 1353 | end | ||
| 1354 | |||
| 1355 | -- Conditional ops. | ||
| 1356 | for cc,n in pairs(map_cc) do | ||
| 1357 | map_op["j"..cc.."_1"] = format("J.:0F8%XJ", n) -- short: 7%X | ||
| 1358 | map_op["set"..cc.."_1"] = format("mb:n0F9%X2m", n) | ||
| 1359 | map_op["cmov"..cc.."_2"] = format("rmdw:0F4%XrM", n) -- P6+ | ||
| 1360 | end | ||
| 1361 | |||
| 1362 | -- FP arithmetic ops. | ||
| 1363 | for name,n in pairs{ add = 0, mul = 1, com = 2, comp = 3, | ||
| 1364 | sub = 4, subr = 5, div = 6, divr = 7 } do | ||
| 1365 | local nc = 192 + n * 8 | ||
| 1366 | local nr = nc + (n < 4 and 0 or (n % 2 == 0 and 8 or -8)) | ||
| 1367 | local fn = "f"..name | ||
| 1368 | map_op[fn.."_1"] = format("ff:D8%02Xr|xd:D8%Xm|xq:DC%Xm", nc, n, n) | ||
| 1369 | if n == 2 or n == 3 then | ||
| 1370 | map_op[fn.."_2"] = format("Fff:D8%02XR|Fx2d:D8%XM|Fx2q:DC%XM", nc, n, n) | ||
| 1371 | else | ||
| 1372 | map_op[fn.."_2"] = format("Fff:D8%02XR|fFf:DC%02Xr|Fx2d:D8%XM|Fx2q:DC%XM", nc, nr, n, n) | ||
| 1373 | map_op[fn.."p_1"] = format("ff:DE%02Xr", nr) | ||
| 1374 | map_op[fn.."p_2"] = format("fFf:DE%02Xr", nr) | ||
| 1375 | end | ||
| 1376 | map_op["fi"..name.."_1"] = format("xd:DA%Xm|xw:nDE%Xm", n, n) | ||
| 1377 | end | ||
| 1378 | |||
| 1379 | -- FP conditional moves. | ||
| 1380 | for cc,n in pairs{ b=0, e=1, be=2, u=3, nb=4, ne=5, nbe=6, nu=7 } do | ||
| 1381 | local n4 = n % 4 | ||
| 1382 | local nc = 56000 + n4 * 8 + (n-n4) * 64 | ||
| 1383 | map_op["fcmov"..cc.."_1"] = format("ff:%04Xr", nc) -- P6+ | ||
| 1384 | map_op["fcmov"..cc.."_2"] = format("Fff:%04XR", nc) -- P6+ | ||
| 1385 | end | ||
| 1386 | |||
| 1387 | -- SSE FP arithmetic ops. | ||
| 1388 | for name,n in pairs{ sqrt = 1, add = 8, mul = 9, | ||
| 1389 | sub = 12, min = 13, div = 14, max = 15 } do | ||
| 1390 | map_op[name.."ps_2"] = format("rmo:0F5%XrM", n) | ||
| 1391 | map_op[name.."ss_2"] = format("rro:F30F5%XrM|rx/od:", n) | ||
| 1392 | map_op[name.."pd_2"] = format("rmo:660F5%XrM", n) | ||
| 1393 | map_op[name.."sd_2"] = format("rro:F20F5%XrM|rx/oq:", n) | ||
| 1394 | end | ||
| 1395 | |||
| 1396 | ------------------------------------------------------------------------------ | ||
| 1397 | |||
| 1398 | -- Process pattern string. | ||
| 1399 | local function dopattern(pat, args, sz, op) | ||
| 1400 | local digit, addin | ||
| 1401 | local opcode = 0 | ||
| 1402 | local szov = sz | ||
| 1403 | local narg = 1 | ||
| 1404 | |||
| 1405 | -- Limit number of section buffer positions used by a single dasm_put(). | ||
| 1406 | -- A single opcode needs a maximum of 2 positions. !x64 | ||
| 1407 | if secpos+2 > maxsecpos then wflush() end | ||
| 1408 | |||
| 1409 | -- Process each character. | ||
| 1410 | for c in gmatch(pat.."|", ".") do | ||
| 1411 | if match(c, "%x") then -- Hex digit. | ||
| 1412 | digit = byte(c) - 48 | ||
| 1413 | if digit > 48 then digit = digit - 39 | ||
| 1414 | elseif digit > 16 then digit = digit - 7 end | ||
| 1415 | opcode = opcode*16 + digit | ||
| 1416 | addin = nil | ||
| 1417 | elseif c == "n" then -- Disable operand size mods for opcode. | ||
| 1418 | szov = nil | ||
| 1419 | elseif c == "r" then -- Merge 1st operand regno. into opcode. | ||
| 1420 | addin = args[1]; opcode = opcode + addin.reg | ||
| 1421 | if narg < 2 then narg = 2 end | ||
| 1422 | elseif c == "R" then -- Merge 2nd operand regno. into opcode. | ||
| 1423 | addin = args[2]; opcode = opcode + addin.reg | ||
| 1424 | narg = 3 | ||
| 1425 | elseif c == "m" or c == "M" then -- Encode ModRM/SIB. | ||
| 1426 | local s | ||
| 1427 | if addin then | ||
| 1428 | s = addin.reg | ||
| 1429 | opcode = opcode - s -- Undo regno opcode merge. | ||
| 1430 | else | ||
| 1431 | s = opcode % 16 -- Undo last digit. | ||
| 1432 | opcode = (opcode - s) / 16 | ||
| 1433 | end | ||
| 1434 | wputop(szov, opcode); opcode = nil | ||
| 1435 | local imark = (sub(pat, -1) == "I") -- Force a mark (ugly). | ||
| 1436 | -- Put ModRM/SIB with regno/last digit as spare. | ||
| 1437 | local nn = c == "m" and 1 or 2 | ||
| 1438 | wputmrmsib(args[nn], imark, s, addin and addin.vreg) | ||
| 1439 | if narg <= nn then narg = nn + 1 end | ||
| 1440 | addin = nil | ||
| 1441 | else | ||
| 1442 | if opcode then -- Flush opcode. | ||
| 1443 | if addin and addin.reg == -1 then | ||
| 1444 | wputop(szov, opcode + 1) | ||
| 1445 | waction("VREG", addin.vreg); wputxb(0) | ||
| 1446 | else | ||
| 1447 | wputop(szov, opcode) | ||
| 1448 | end | ||
| 1449 | opcode = nil | ||
| 1450 | end | ||
| 1451 | if c == "|" then break end | ||
| 1452 | if c == "o" then -- Offset (pure 32 bit displacement). | ||
| 1453 | wputdarg(args[1].disp); if narg < 2 then narg = 2 end | ||
| 1454 | elseif c == "O" then | ||
| 1455 | wputdarg(args[2].disp); narg = 3 | ||
| 1456 | else | ||
| 1457 | -- Anything else is an immediate operand. | ||
| 1458 | local a = args[narg] | ||
| 1459 | narg = narg + 1 | ||
| 1460 | local mode, imm = a.mode, a.imm | ||
| 1461 | if mode == "iJ" and not match("iIJ", c) then | ||
| 1462 | werror("bad operand size for label") | ||
| 1463 | end | ||
| 1464 | if c == "S" then | ||
| 1465 | wputsbarg(imm) | ||
| 1466 | elseif c == "U" then | ||
| 1467 | wputbarg(imm) | ||
| 1468 | elseif c == "W" then | ||
| 1469 | wputwarg(imm) | ||
| 1470 | elseif c == "i" or c == "I" then | ||
| 1471 | if mode == "iJ" then | ||
| 1472 | wputlabel("IMM_", imm, 1) | ||
| 1473 | elseif mode == "iI" and c == "I" then | ||
| 1474 | waction(sz == "w" and "IMM_WB" or "IMM_DB", imm) | ||
| 1475 | else | ||
| 1476 | wputszarg(sz, imm) | ||
| 1477 | end | ||
| 1478 | elseif c == "J" then | ||
| 1479 | if mode == "iPJ" then | ||
| 1480 | waction("REL_A", imm) -- !x64 (secpos) | ||
| 1481 | else | ||
| 1482 | wputlabel("REL_", imm, 2) | ||
| 1483 | end | ||
| 1484 | else | ||
| 1485 | werror("bad char `"..c.."' in pattern `"..pat.."' for `"..op.."'") | ||
| 1486 | end | ||
| 1487 | end | ||
| 1488 | end | ||
| 1489 | end | ||
| 1490 | end | ||
| 1491 | |||
| 1492 | ------------------------------------------------------------------------------ | ||
| 1493 | |||
| 1494 | -- Mapping of operand modes to short names. Suppress output with '#'. | ||
| 1495 | local map_modename = { | ||
| 1496 | r = "reg", R = "eax", C = "cl", x = "mem", m = "mrm", i = "imm", | ||
| 1497 | f = "stx", F = "st0", J = "lbl", ["1"] = "1", | ||
| 1498 | I = "#", S = "#", O = "#", | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | -- Return a table/string showing all possible operand modes. | ||
| 1502 | local function templatehelp(template, nparams) | ||
| 1503 | if nparams == 0 then return "" end | ||
| 1504 | local t = {} | ||
| 1505 | for tm in gmatch(template, "[^%|]+") do | ||
| 1506 | local s = map_modename[sub(tm, 1, 1)] | ||
| 1507 | s = s..gsub(sub(tm, 2, nparams), ".", function(c) | ||
| 1508 | return ", "..map_modename[c] | ||
| 1509 | end) | ||
| 1510 | if not match(s, "#") then t[#t+1] = s end | ||
| 1511 | end | ||
| 1512 | return t | ||
| 1513 | end | ||
| 1514 | |||
| 1515 | -- Match operand modes against mode match part of template. | ||
| 1516 | local function matchtm(tm, args) | ||
| 1517 | for i=1,#args do | ||
| 1518 | if not match(args[i].mode, sub(tm, i, i)) then return end | ||
| 1519 | end | ||
| 1520 | return true | ||
| 1521 | end | ||
| 1522 | |||
| 1523 | -- Handle opcodes defined with template strings. | ||
| 1524 | map_op[".template__"] = function(params, template, nparams) | ||
| 1525 | if not params then return templatehelp(template, nparams) end | ||
| 1526 | local args = {} | ||
| 1527 | |||
| 1528 | -- Zero-operand opcodes have no match part. | ||
| 1529 | if #params == 0 then | ||
| 1530 | dopattern(template, args, "d", params.op) | ||
| 1531 | return | ||
| 1532 | end | ||
| 1533 | |||
| 1534 | -- Determine common operand size (coerce undefined size) or flag as mixed. | ||
| 1535 | local sz, szmix | ||
| 1536 | for i,p in ipairs(params) do | ||
| 1537 | args[i] = parseoperand(p) | ||
| 1538 | local nsz = args[i].opsize | ||
| 1539 | if nsz then | ||
| 1540 | if sz and sz ~= nsz then szmix = true else sz = nsz end | ||
| 1541 | end | ||
| 1542 | end | ||
| 1543 | |||
| 1544 | -- Try all match:pattern pairs (separated by '|'). | ||
| 1545 | local gotmatch, lastpat | ||
| 1546 | for tm in gmatch(template, "[^%|]+") do | ||
| 1547 | -- Split off size match (starts after mode match) and pattern string. | ||
| 1548 | local szm, pat = match(tm, "^(.-):(.*)$", #args+1) | ||
| 1549 | if pat == "" then pat = lastpat else lastpat = pat end | ||
| 1550 | if matchtm(tm, args) then | ||
| 1551 | local prefix = sub(szm, 1, 1) | ||
| 1552 | if prefix == "/" then -- Match both operand sizes. | ||
| 1553 | if args[1].opsize == sub(szm, 2, 2) and | ||
| 1554 | args[2].opsize == sub(szm, 3, 3) then | ||
| 1555 | dopattern(pat, args, sz, params.op) -- Process pattern string. | ||
| 1556 | return | ||
| 1557 | end | ||
| 1558 | else -- Match common operand size. | ||
| 1559 | local szp = sz | ||
| 1560 | if szm == "" then szm = "dwb" end -- Default size match. | ||
| 1561 | if prefix == "1" then szp = args[1].opsize; szmix = nil | ||
| 1562 | elseif prefix == "2" then szp = args[2].opsize; szmix = nil end | ||
| 1563 | if not szmix and (prefix == "." or match(szm, szp or "#")) then | ||
| 1564 | dopattern(pat, args, szp, params.op) -- Process pattern string. | ||
| 1565 | return | ||
| 1566 | end | ||
| 1567 | end | ||
| 1568 | gotmatch = true | ||
| 1569 | end | ||
| 1570 | end | ||
| 1571 | |||
| 1572 | local msg = "bad operand mode" | ||
| 1573 | if gotmatch then | ||
| 1574 | if szmix then | ||
| 1575 | msg = "mixed operand size" | ||
| 1576 | else | ||
| 1577 | msg = sz and "bad operand size" or "missing operand size" | ||
| 1578 | end | ||
| 1579 | end | ||
| 1580 | |||
| 1581 | werror(msg.." in `"..opmodestr(params.op, args).."'") | ||
| 1582 | end | ||
| 1583 | |||
| 1584 | ------------------------------------------------------------------------------ | ||
| 1585 | |||
| 1586 | -- Pseudo-opcodes for data storage. | ||
| 1587 | local function op_data(params) | ||
| 1588 | if not params then return "imm..." end | ||
| 1589 | local sz = sub(params.op, 2, 2) | ||
| 1590 | if sz == "a" then sz = addrsize end | ||
| 1591 | for _,p in ipairs(params) do | ||
| 1592 | local a = parseoperand(p) | ||
| 1593 | if sub(a.mode, 1, 1) ~= "i" or (a.opsize and a.opsize ~= sz) then | ||
| 1594 | werror("bad mode or size in `"..p.."'") | ||
| 1595 | end | ||
| 1596 | if a.mode == "iJ" then | ||
| 1597 | wputlabel("IMM_", a.imm, 1) | ||
| 1598 | else | ||
| 1599 | wputszarg(sz, a.imm) | ||
| 1600 | end | ||
| 1601 | end | ||
| 1602 | end | ||
| 1603 | |||
| 1604 | map_op[".byte_*"] = op_data | ||
| 1605 | map_op[".sbyte_*"] = op_data | ||
| 1606 | map_op[".word_*"] = op_data | ||
| 1607 | map_op[".dword_*"] = op_data | ||
| 1608 | map_op[".aword_*"] = op_data | ||
| 1609 | |||
| 1610 | ------------------------------------------------------------------------------ | ||
| 1611 | |||
| 1612 | -- Pseudo-opcode to mark the position where the action list is to be emitted. | ||
| 1613 | map_op[".actionlist_1"] = function(params) | ||
| 1614 | if not params then return "cvar" end | ||
| 1615 | local name = params[1] -- No syntax check. You get to keep the pieces. | ||
| 1616 | wline(function(out) writeactions(out, name) end) | ||
| 1617 | end | ||
| 1618 | |||
| 1619 | -- Pseudo-opcode to mark the position where the global enum is to be emitted. | ||
| 1620 | map_op[".globals_1"] = function(params) | ||
| 1621 | if not params then return "prefix" end | ||
| 1622 | local prefix = params[1] -- No syntax check. You get to keep the pieces. | ||
| 1623 | wline(function(out) writeglobals(out, prefix) end) | ||
| 1624 | end | ||
| 1625 | |||
| 1626 | -- Pseudo-opcode to mark the position where the global names are to be emitted. | ||
| 1627 | map_op[".globalnames_1"] = function(params) | ||
| 1628 | if not params then return "cvar" end | ||
| 1629 | local name = params[1] -- No syntax check. You get to keep the pieces. | ||
| 1630 | wline(function(out) writeglobalnames(out, name) end) | ||
| 1631 | end | ||
| 1632 | |||
| 1633 | -- Pseudo-opcode to mark the position where the extern names are to be emitted. | ||
| 1634 | map_op[".externnames_1"] = function(params) | ||
| 1635 | if not params then return "cvar" end | ||
| 1636 | local name = params[1] -- No syntax check. You get to keep the pieces. | ||
| 1637 | wline(function(out) writeexternnames(out, name) end) | ||
| 1638 | end | ||
| 1639 | |||
| 1640 | ------------------------------------------------------------------------------ | ||
| 1641 | |||
| 1642 | -- Label pseudo-opcode (converted from trailing colon form). | ||
| 1643 | map_op[".label_2"] = function(params) | ||
| 1644 | if not params then return "[1-9] | ->global | =>pcexpr [, addr]" end | ||
| 1645 | local a = parseoperand(params[1]) | ||
| 1646 | local mode, imm = a.mode, a.imm | ||
| 1647 | if type(imm) == "number" and (mode == "iJ" or (imm >= 1 and imm <= 9)) then | ||
| 1648 | -- Local label (1: ... 9:) or global label (->global:). | ||
| 1649 | waction("LABEL_LG", nil, 1) | ||
| 1650 | wputxb(imm) | ||
| 1651 | elseif mode == "iJ" then | ||
| 1652 | -- PC label (=>pcexpr:). | ||
| 1653 | waction("LABEL_PC", imm) | ||
| 1654 | else | ||
| 1655 | werror("bad label definition") | ||
| 1656 | end | ||
| 1657 | -- SETLABEL must immediately follow LABEL_LG/LABEL_PC. | ||
| 1658 | local addr = params[2] | ||
| 1659 | if addr then | ||
| 1660 | local a = parseoperand(params[2]) | ||
| 1661 | if a.mode == "iPJ" then | ||
| 1662 | waction("SETLABEL", a.imm) -- !x64 (secpos) | ||
| 1663 | else | ||
| 1664 | werror("bad label assignment") | ||
| 1665 | end | ||
| 1666 | end | ||
| 1667 | end | ||
| 1668 | map_op[".label_1"] = map_op[".label_2"] | ||
| 1669 | |||
| 1670 | ------------------------------------------------------------------------------ | ||
| 1671 | |||
| 1672 | -- Alignment pseudo-opcode. | ||
| 1673 | map_op[".align_1"] = function(params) | ||
| 1674 | if not params then return "numpow2" end | ||
| 1675 | local align = tonumber(params[1]) or map_opsizenum[map_opsize[params[1]]] | ||
| 1676 | if align then | ||
| 1677 | local x = align | ||
| 1678 | -- Must be a power of 2 in the range (2 ... 256). | ||
| 1679 | for i=1,8 do | ||
| 1680 | x = x / 2 | ||
| 1681 | if x == 1 then | ||
| 1682 | waction("ALIGN", nil, 1) | ||
| 1683 | wputxb(align-1) -- Action byte is 2**n-1. | ||
| 1684 | return | ||
| 1685 | end | ||
| 1686 | end | ||
| 1687 | end | ||
| 1688 | werror("bad alignment") | ||
| 1689 | end | ||
| 1690 | |||
| 1691 | -- Spacing pseudo-opcode. | ||
| 1692 | map_op[".space_2"] = function(params) | ||
| 1693 | if not params then return "num [, filler]" end | ||
| 1694 | waction("SPACE", params[1]) | ||
| 1695 | local fill = params[2] | ||
| 1696 | if fill then | ||
| 1697 | fill = tonumber(fill) | ||
| 1698 | if not fill or fill < 0 or fill > 255 then werror("bad filler") end | ||
| 1699 | end | ||
| 1700 | wputxb(fill or 0) | ||
| 1701 | end | ||
| 1702 | map_op[".space_1"] = map_op[".space_2"] | ||
| 1703 | |||
| 1704 | ------------------------------------------------------------------------------ | ||
| 1705 | |||
| 1706 | -- Pseudo-opcode for (primitive) type definitions (map to C types). | ||
| 1707 | map_op[".type_3"] = function(params, nparams) | ||
| 1708 | if not params then | ||
| 1709 | return nparams == 2 and "name, ctype" or "name, ctype, reg" | ||
| 1710 | end | ||
| 1711 | local name, ctype, reg = params[1], params[2], params[3] | ||
| 1712 | if not match(name, "^[%a_][%w_]*$") then | ||
| 1713 | werror("bad type name `"..name.."'") | ||
| 1714 | end | ||
| 1715 | local tp = map_type[name] | ||
| 1716 | if tp then | ||
| 1717 | werror("duplicate type `"..name.."'") | ||
| 1718 | end | ||
| 1719 | if reg and not map_reg_valid_base[reg] then | ||
| 1720 | werror("bad base register `"..(map_reg_rev[reg] or reg).."'") | ||
| 1721 | end | ||
| 1722 | -- Add #type to defines. A bit unclean to put it in map_archdef. | ||
| 1723 | map_archdef["#"..name] = "sizeof("..ctype..")" | ||
| 1724 | -- Add new type and emit shortcut define. | ||
| 1725 | local num = ctypenum + 1 | ||
| 1726 | map_type[name] = { | ||
| 1727 | ctype = ctype, | ||
| 1728 | ctypefmt = format("Dt%X(%%s)", num), | ||
| 1729 | reg = reg, | ||
| 1730 | } | ||
| 1731 | wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype)) | ||
| 1732 | ctypenum = num | ||
| 1733 | end | ||
| 1734 | map_op[".type_2"] = map_op[".type_3"] | ||
| 1735 | |||
| 1736 | -- Dump type definitions. | ||
| 1737 | local function dumptypes(out, lvl) | ||
| 1738 | local t = {} | ||
| 1739 | for name in pairs(map_type) do t[#t+1] = name end | ||
| 1740 | sort(t) | ||
| 1741 | out:write("Type definitions:\n") | ||
| 1742 | for _,name in ipairs(t) do | ||
| 1743 | local tp = map_type[name] | ||
| 1744 | local reg = tp.reg and map_reg_rev[tp.reg] or "" | ||
| 1745 | out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg)) | ||
| 1746 | end | ||
| 1747 | out:write("\n") | ||
| 1748 | end | ||
| 1749 | |||
| 1750 | ------------------------------------------------------------------------------ | ||
| 1751 | |||
| 1752 | -- Set the current section. | ||
| 1753 | function _M.section(num) | ||
| 1754 | waction("SECTION") | ||
| 1755 | wputxb(num) | ||
| 1756 | wflush(true) -- SECTION is a terminal action. | ||
| 1757 | end | ||
| 1758 | |||
| 1759 | ------------------------------------------------------------------------------ | ||
| 1760 | |||
| 1761 | -- Dump architecture description. | ||
| 1762 | function _M.dumparch(out) | ||
| 1763 | out:write(format("DynASM %s version %s, released %s\n\n", | ||
| 1764 | _info.arch, _info.version, _info.release)) | ||
| 1765 | dumpregs(out) | ||
| 1766 | dumpactions(out) | ||
| 1767 | end | ||
| 1768 | |||
| 1769 | -- Dump all user defined elements. | ||
| 1770 | function _M.dumpdef(out, lvl) | ||
| 1771 | dumptypes(out, lvl) | ||
| 1772 | dumpglobals(out, lvl) | ||
| 1773 | dumpexterns(out, lvl) | ||
| 1774 | end | ||
| 1775 | |||
| 1776 | ------------------------------------------------------------------------------ | ||
| 1777 | |||
| 1778 | -- Pass callbacks from/to the DynASM core. | ||
| 1779 | function _M.passcb(wl, we, wf, ww) | ||
| 1780 | wline, werror, wfatal, wwarn = wl, we, wf, ww | ||
| 1781 | return wflush | ||
| 1782 | end | ||
| 1783 | |||
| 1784 | -- Setup the arch-specific module. | ||
| 1785 | function _M.setup(arch, opt) | ||
| 1786 | g_arch, g_opt = arch, opt | ||
| 1787 | end | ||
| 1788 | |||
| 1789 | -- Merge the core maps and the arch-specific maps. | ||
| 1790 | function _M.mergemaps(map_coreop, map_def) | ||
| 1791 | setmetatable(map_op, { __index = map_coreop }) | ||
| 1792 | setmetatable(map_def, { __index = map_archdef }) | ||
| 1793 | return map_op, map_def | ||
| 1794 | end | ||
| 1795 | |||
| 1796 | return _M | ||
| 1797 | |||
| 1798 | ------------------------------------------------------------------------------ | ||
| 1799 | |||
diff --git a/dynasm/dynasm.lua b/dynasm/dynasm.lua new file mode 100644 index 00000000..20ff9cf5 --- /dev/null +++ b/dynasm/dynasm.lua | |||
| @@ -0,0 +1,1070 @@ | |||
| 1 | ------------------------------------------------------------------------------ | ||
| 2 | -- DynASM. A dynamic assembler for code generation engines. | ||
| 3 | -- Originally designed and implemented for LuaJIT. | ||
| 4 | -- | ||
| 5 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 6 | -- See below for full copyright notice. | ||
| 7 | ------------------------------------------------------------------------------ | ||
| 8 | |||
| 9 | -- Application information. | ||
| 10 | local _info = { | ||
| 11 | name = "DynASM", | ||
| 12 | description = "A dynamic assembler for code generation engines", | ||
| 13 | version = "1.2.1", | ||
| 14 | vernum = 10201, | ||
| 15 | release = "2009-04-16", | ||
| 16 | author = "Mike Pall", | ||
| 17 | url = "http://luajit.org/dynasm.html", | ||
| 18 | license = "MIT", | ||
| 19 | copyright = [[ | ||
| 20 | Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 21 | |||
| 22 | Permission is hereby granted, free of charge, to any person obtaining | ||
| 23 | a copy of this software and associated documentation files (the | ||
| 24 | "Software"), to deal in the Software without restriction, including | ||
| 25 | without limitation the rights to use, copy, modify, merge, publish, | ||
| 26 | distribute, sublicense, and/or sell copies of the Software, and to | ||
| 27 | permit persons to whom the Software is furnished to do so, subject to | ||
| 28 | the following conditions: | ||
| 29 | |||
| 30 | The above copyright notice and this permission notice shall be | ||
| 31 | included in all copies or substantial portions of the Software. | ||
| 32 | |||
| 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 34 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 35 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| 36 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| 37 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| 38 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| 39 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 40 | |||
| 41 | [ MIT license: http://www.opensource.org/licenses/mit-license.php ] | ||
| 42 | ]], | ||
| 43 | } | ||
| 44 | |||
| 45 | -- Cache library functions. | ||
| 46 | local type, pairs, ipairs = type, pairs, ipairs | ||
| 47 | local pcall, error, assert = pcall, error, assert | ||
| 48 | local _s = string | ||
| 49 | local sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsub | ||
| 50 | local format, rep, upper = _s.format, _s.rep, _s.upper | ||
| 51 | local _t = table | ||
| 52 | local insert, remove, concat, sort = _t.insert, _t.remove, _t.concat, _t.sort | ||
| 53 | local exit = os.exit | ||
| 54 | local io = io | ||
| 55 | local stdin, stdout, stderr = io.stdin, io.stdout, io.stderr | ||
| 56 | |||
| 57 | ------------------------------------------------------------------------------ | ||
| 58 | |||
| 59 | -- Program options. | ||
| 60 | local g_opt = {} | ||
| 61 | |||
| 62 | -- Global state for current file. | ||
| 63 | local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_arch | ||
| 64 | local g_errcount = 0 | ||
| 65 | |||
| 66 | -- Write buffer for output file. | ||
| 67 | local g_wbuffer, g_capbuffer | ||
| 68 | |||
| 69 | ------------------------------------------------------------------------------ | ||
| 70 | |||
| 71 | -- Write an output line (or callback function) to the buffer. | ||
| 72 | local function wline(line, needindent) | ||
| 73 | local buf = g_capbuffer or g_wbuffer | ||
| 74 | buf[#buf+1] = needindent and g_indent..line or line | ||
| 75 | g_synclineno = g_synclineno + 1 | ||
| 76 | end | ||
| 77 | |||
| 78 | -- Write assembler line as a comment, if requestd. | ||
| 79 | local function wcomment(aline) | ||
| 80 | if g_opt.comment then | ||
| 81 | wline(g_opt.comment..aline..g_opt.endcomment, true) | ||
| 82 | end | ||
| 83 | end | ||
| 84 | |||
| 85 | -- Resync CPP line numbers. | ||
| 86 | local function wsync() | ||
| 87 | if g_synclineno ~= g_lineno and g_opt.cpp then | ||
| 88 | wline("# "..g_lineno..' "'..g_fname..'"') | ||
| 89 | g_synclineno = g_lineno | ||
| 90 | end | ||
| 91 | end | ||
| 92 | |||
| 93 | -- Dummy action flush function. Replaced with arch-specific function later. | ||
| 94 | local function wflush(term) | ||
| 95 | end | ||
| 96 | |||
| 97 | -- Dump all buffered output lines. | ||
| 98 | local function wdumplines(out, buf) | ||
| 99 | for _,line in ipairs(buf) do | ||
| 100 | if type(line) == "string" then | ||
| 101 | assert(out:write(line, "\n")) | ||
| 102 | else | ||
| 103 | -- Special callback to dynamically insert lines after end of processing. | ||
| 104 | line(out) | ||
| 105 | end | ||
| 106 | end | ||
| 107 | end | ||
| 108 | |||
| 109 | ------------------------------------------------------------------------------ | ||
| 110 | |||
| 111 | -- Emit an error. Processing continues with next statement. | ||
| 112 | local function werror(msg) | ||
| 113 | error(format("%s:%s: error: %s:\n%s", g_fname, g_lineno, msg, g_curline), 0) | ||
| 114 | end | ||
| 115 | |||
| 116 | -- Emit a fatal error. Processing stops. | ||
| 117 | local function wfatal(msg) | ||
| 118 | g_errcount = "fatal" | ||
| 119 | werror(msg) | ||
| 120 | end | ||
| 121 | |||
| 122 | -- Print a warning. Processing continues. | ||
| 123 | local function wwarn(msg) | ||
| 124 | stderr:write(format("%s:%s: warning: %s:\n%s\n", | ||
| 125 | g_fname, g_lineno, msg, g_curline)) | ||
| 126 | end | ||
| 127 | |||
| 128 | -- Print caught error message. But suppress excessive errors. | ||
| 129 | local function wprinterr(...) | ||
| 130 | if type(g_errcount) == "number" then | ||
| 131 | -- Regular error. | ||
| 132 | g_errcount = g_errcount + 1 | ||
| 133 | if g_errcount < 21 then -- Seems to be a reasonable limit. | ||
| 134 | stderr:write(...) | ||
| 135 | elseif g_errcount == 21 then | ||
| 136 | stderr:write(g_fname, | ||
| 137 | ":*: warning: too many errors (suppressed further messages).\n") | ||
| 138 | end | ||
| 139 | else | ||
| 140 | -- Fatal error. | ||
| 141 | stderr:write(...) | ||
| 142 | return true -- Stop processing. | ||
| 143 | end | ||
| 144 | end | ||
| 145 | |||
| 146 | ------------------------------------------------------------------------------ | ||
| 147 | |||
| 148 | -- Map holding all option handlers. | ||
| 149 | local opt_map = {} | ||
| 150 | local opt_current | ||
| 151 | |||
| 152 | -- Print error and exit with error status. | ||
| 153 | local function opterror(...) | ||
| 154 | stderr:write("dynasm.lua: ERROR: ", ...) | ||
| 155 | stderr:write("\n") | ||
| 156 | exit(1) | ||
| 157 | end | ||
| 158 | |||
| 159 | -- Get option parameter. | ||
| 160 | local function optparam(args) | ||
| 161 | local argn = args.argn | ||
| 162 | local p = args[argn] | ||
| 163 | if not p then | ||
| 164 | opterror("missing parameter for option `", opt_current, "'.") | ||
| 165 | end | ||
| 166 | args.argn = argn + 1 | ||
| 167 | return p | ||
| 168 | end | ||
| 169 | |||
| 170 | ------------------------------------------------------------------------------ | ||
| 171 | |||
| 172 | -- Core pseudo-opcodes. | ||
| 173 | local map_coreop = {} | ||
| 174 | -- Dummy opcode map. Replaced by arch-specific map. | ||
| 175 | local map_op = {} | ||
| 176 | |||
| 177 | -- Forward declarations. | ||
| 178 | local dostmt | ||
| 179 | local readfile | ||
| 180 | |||
| 181 | ------------------------------------------------------------------------------ | ||
| 182 | |||
| 183 | -- Map for defines (initially empty, chains to arch-specific map). | ||
| 184 | local map_def = {} | ||
| 185 | |||
| 186 | -- Pseudo-opcode to define a substitution. | ||
| 187 | map_coreop[".define_2"] = function(params, nparams) | ||
| 188 | if not params then return nparams == 1 and "name" or "name, subst" end | ||
| 189 | local name, def = params[1], params[2] or "1" | ||
| 190 | if not match(name, "^[%a_][%w_]*$") then werror("bad or duplicate define") end | ||
| 191 | map_def[name] = def | ||
| 192 | end | ||
| 193 | map_coreop[".define_1"] = map_coreop[".define_2"] | ||
| 194 | |||
| 195 | -- Define a substitution on the command line. | ||
| 196 | function opt_map.D(args) | ||
| 197 | local namesubst = optparam(args) | ||
| 198 | local name, subst = match(namesubst, "^([%a_][%w_]*)=(.*)$") | ||
| 199 | if name then | ||
| 200 | map_def[name] = subst | ||
| 201 | elseif match(namesubst, "^[%a_][%w_]*$") then | ||
| 202 | map_def[namesubst] = "1" | ||
| 203 | else | ||
| 204 | opterror("bad define") | ||
| 205 | end | ||
| 206 | end | ||
| 207 | |||
| 208 | -- Undefine a substitution on the command line. | ||
| 209 | function opt_map.U(args) | ||
| 210 | local name = optparam(args) | ||
| 211 | if match(name, "^[%a_][%w_]*$") then | ||
| 212 | map_def[name] = nil | ||
| 213 | else | ||
| 214 | opterror("bad define") | ||
| 215 | end | ||
| 216 | end | ||
| 217 | |||
| 218 | -- Helper for definesubst. | ||
| 219 | local gotsubst | ||
| 220 | |||
| 221 | local function definesubst_one(word) | ||
| 222 | local subst = map_def[word] | ||
| 223 | if subst then gotsubst = word; return subst else return word end | ||
| 224 | end | ||
| 225 | |||
| 226 | -- Iteratively substitute defines. | ||
| 227 | local function definesubst(stmt) | ||
| 228 | -- Limit number of iterations. | ||
| 229 | for i=1,100 do | ||
| 230 | gotsubst = false | ||
| 231 | stmt = gsub(stmt, "#?[%w_]+", definesubst_one) | ||
| 232 | if not gotsubst then break end | ||
| 233 | end | ||
| 234 | if gotsubst then wfatal("recursive define involving `"..gotsubst.."'") end | ||
| 235 | return stmt | ||
| 236 | end | ||
| 237 | |||
| 238 | -- Dump all defines. | ||
| 239 | local function dumpdefines(out, lvl) | ||
| 240 | local t = {} | ||
| 241 | for name in pairs(map_def) do | ||
| 242 | t[#t+1] = name | ||
| 243 | end | ||
| 244 | sort(t) | ||
| 245 | out:write("Defines:\n") | ||
| 246 | for _,name in ipairs(t) do | ||
| 247 | local subst = map_def[name] | ||
| 248 | if g_arch then subst = g_arch.revdef(subst) end | ||
| 249 | out:write(format(" %-20s %s\n", name, subst)) | ||
| 250 | end | ||
| 251 | out:write("\n") | ||
| 252 | end | ||
| 253 | |||
| 254 | ------------------------------------------------------------------------------ | ||
| 255 | |||
| 256 | -- Support variables for conditional assembly. | ||
| 257 | local condlevel = 0 | ||
| 258 | local condstack = {} | ||
| 259 | |||
| 260 | -- Evaluate condition with a Lua expression. Substitutions already performed. | ||
| 261 | local function cond_eval(cond) | ||
| 262 | local func, err = loadstring("return "..cond) | ||
| 263 | if func then | ||
| 264 | setfenv(func, {}) -- No globals. All unknown identifiers evaluate to nil. | ||
| 265 | local ok, res = pcall(func) | ||
| 266 | if ok then | ||
| 267 | if res == 0 then return false end -- Oh well. | ||
| 268 | return not not res | ||
| 269 | end | ||
| 270 | err = res | ||
| 271 | end | ||
| 272 | wfatal("bad condition: "..err) | ||
| 273 | end | ||
| 274 | |||
| 275 | -- Skip statements until next conditional pseudo-opcode at the same level. | ||
| 276 | local function stmtskip() | ||
| 277 | local dostmt_save = dostmt | ||
| 278 | local lvl = 0 | ||
| 279 | dostmt = function(stmt) | ||
| 280 | local op = match(stmt, "^%s*(%S+)") | ||
| 281 | if op == ".if" then | ||
| 282 | lvl = lvl + 1 | ||
| 283 | elseif lvl ~= 0 then | ||
| 284 | if op == ".endif" then lvl = lvl - 1 end | ||
| 285 | elseif op == ".elif" or op == ".else" or op == ".endif" then | ||
| 286 | dostmt = dostmt_save | ||
| 287 | dostmt(stmt) | ||
| 288 | end | ||
| 289 | end | ||
| 290 | end | ||
| 291 | |||
| 292 | -- Pseudo-opcodes for conditional assembly. | ||
| 293 | map_coreop[".if_1"] = function(params) | ||
| 294 | if not params then return "condition" end | ||
| 295 | local lvl = condlevel + 1 | ||
| 296 | local res = cond_eval(params[1]) | ||
| 297 | condlevel = lvl | ||
| 298 | condstack[lvl] = res | ||
| 299 | if not res then stmtskip() end | ||
| 300 | end | ||
| 301 | |||
| 302 | map_coreop[".elif_1"] = function(params) | ||
| 303 | if not params then return "condition" end | ||
| 304 | if condlevel == 0 then wfatal(".elif without .if") end | ||
| 305 | local lvl = condlevel | ||
| 306 | local res = condstack[lvl] | ||
| 307 | if res then | ||
| 308 | if res == "else" then wfatal(".elif after .else") end | ||
| 309 | else | ||
| 310 | res = cond_eval(params[1]) | ||
| 311 | if res then | ||
| 312 | condstack[lvl] = res | ||
| 313 | return | ||
| 314 | end | ||
| 315 | end | ||
| 316 | stmtskip() | ||
| 317 | end | ||
| 318 | |||
| 319 | map_coreop[".else_0"] = function(params) | ||
| 320 | if condlevel == 0 then wfatal(".else without .if") end | ||
| 321 | local lvl = condlevel | ||
| 322 | local res = condstack[lvl] | ||
| 323 | condstack[lvl] = "else" | ||
| 324 | if res then | ||
| 325 | if res == "else" then wfatal(".else after .else") end | ||
| 326 | stmtskip() | ||
| 327 | end | ||
| 328 | end | ||
| 329 | |||
| 330 | map_coreop[".endif_0"] = function(params) | ||
| 331 | local lvl = condlevel | ||
| 332 | if lvl == 0 then wfatal(".endif without .if") end | ||
| 333 | condlevel = lvl - 1 | ||
| 334 | end | ||
| 335 | |||
| 336 | -- Check for unfinished conditionals. | ||
| 337 | local function checkconds() | ||
| 338 | if g_errcount ~= "fatal" and condlevel ~= 0 then | ||
| 339 | wprinterr(g_fname, ":*: error: unbalanced conditional\n") | ||
| 340 | end | ||
| 341 | end | ||
| 342 | |||
| 343 | ------------------------------------------------------------------------------ | ||
| 344 | |||
| 345 | -- Search for a file in the given path and open it for reading. | ||
| 346 | local function pathopen(path, name) | ||
| 347 | local dirsep = match(package.path, "\\") and "\\" or "/" | ||
| 348 | for _,p in ipairs(path) do | ||
| 349 | local fullname = p == "" and name or p..dirsep..name | ||
| 350 | local fin = io.open(fullname, "r") | ||
| 351 | if fin then | ||
| 352 | g_fname = fullname | ||
| 353 | return fin | ||
| 354 | end | ||
| 355 | end | ||
| 356 | end | ||
| 357 | |||
| 358 | -- Include a file. | ||
| 359 | map_coreop[".include_1"] = function(params) | ||
| 360 | if not params then return "filename" end | ||
| 361 | local name = params[1] | ||
| 362 | -- Save state. Ugly, I know. but upvalues are fast. | ||
| 363 | local gf, gl, gcl, gi = g_fname, g_lineno, g_curline, g_indent | ||
| 364 | -- Read the included file. | ||
| 365 | local fatal = readfile(pathopen(g_opt.include, name) or | ||
| 366 | wfatal("include file `"..name.."' not found")) | ||
| 367 | -- Restore state. | ||
| 368 | g_synclineno = -1 | ||
| 369 | g_fname, g_lineno, g_curline, g_indent = gf, gl, gcl, gi | ||
| 370 | if fatal then wfatal("in include file") end | ||
| 371 | end | ||
| 372 | |||
| 373 | -- Make .include initially available, too. | ||
| 374 | map_op[".include_1"] = map_coreop[".include_1"] | ||
| 375 | |||
| 376 | ------------------------------------------------------------------------------ | ||
| 377 | |||
| 378 | -- Support variables for macros. | ||
| 379 | local mac_capture, mac_lineno, mac_name | ||
| 380 | local mac_active = {} | ||
| 381 | local mac_list = {} | ||
| 382 | |||
| 383 | -- Pseudo-opcode to define a macro. | ||
| 384 | map_coreop[".macro_*"] = function(mparams) | ||
| 385 | if not mparams then return "name [, params...]" end | ||
| 386 | -- Split off and validate macro name. | ||
| 387 | local name = remove(mparams, 1) | ||
| 388 | if not name then werror("missing macro name") end | ||
| 389 | if not (match(name, "^[%a_][%w_%.]*$") or match(name, "^%.[%w_%.]+$")) then | ||
| 390 | wfatal("bad macro name `"..name.."'") | ||
| 391 | end | ||
| 392 | -- Validate macro parameter names. | ||
| 393 | local mdup = {} | ||
| 394 | for _,mp in ipairs(mparams) do | ||
| 395 | if not match(mp, "^[%a_][%w_]*$") then | ||
| 396 | wfatal("bad macro parameter name `"..mp.."'") | ||
| 397 | end | ||
| 398 | if mdup[mp] then wfatal("duplicate macro parameter name `"..mp.."'") end | ||
| 399 | mdup[mp] = true | ||
| 400 | end | ||
| 401 | -- Check for duplicate or recursive macro definitions. | ||
| 402 | local opname = name.."_"..#mparams | ||
| 403 | if map_op[opname] or map_op[name.."_*"] then | ||
| 404 | wfatal("duplicate macro `"..name.."' ("..#mparams.." parameters)") | ||
| 405 | end | ||
| 406 | if mac_capture then wfatal("recursive macro definition") end | ||
| 407 | |||
| 408 | -- Enable statement capture. | ||
| 409 | local lines = {} | ||
| 410 | mac_lineno = g_lineno | ||
| 411 | mac_name = name | ||
| 412 | mac_capture = function(stmt) -- Statement capture function. | ||
| 413 | -- Stop macro definition with .endmacro pseudo-opcode. | ||
| 414 | if not match(stmt, "^%s*.endmacro%s*$") then | ||
| 415 | lines[#lines+1] = stmt | ||
| 416 | return | ||
| 417 | end | ||
| 418 | mac_capture = nil | ||
| 419 | mac_lineno = nil | ||
| 420 | mac_name = nil | ||
| 421 | mac_list[#mac_list+1] = opname | ||
| 422 | -- Add macro-op definition. | ||
| 423 | map_op[opname] = function(params) | ||
| 424 | if not params then return mparams, lines end | ||
| 425 | -- Protect against recursive macro invocation. | ||
| 426 | if mac_active[opname] then wfatal("recursive macro invocation") end | ||
| 427 | mac_active[opname] = true | ||
| 428 | -- Setup substitution map. | ||
| 429 | local subst = {} | ||
| 430 | for i,mp in ipairs(mparams) do subst[mp] = params[i] end | ||
| 431 | local mcom | ||
| 432 | if g_opt.maccomment and g_opt.comment then | ||
| 433 | mcom = " MACRO "..name.." ("..#mparams..")" | ||
| 434 | wcomment("{"..mcom) | ||
| 435 | end | ||
| 436 | -- Loop through all captured statements | ||
| 437 | for _,stmt in ipairs(lines) do | ||
| 438 | -- Substitute macro parameters. | ||
| 439 | local st = gsub(stmt, "[%w_]+", subst) | ||
| 440 | st = definesubst(st) | ||
| 441 | st = gsub(st, "%s*%.%.%s*", "") -- Token paste a..b. | ||
| 442 | if mcom and sub(st, 1, 1) ~= "|" then wcomment(st) end | ||
| 443 | -- Emit statement. Use a protected call for better diagnostics. | ||
| 444 | local ok, err = pcall(dostmt, st) | ||
| 445 | if not ok then | ||
| 446 | -- Add the captured statement to the error. | ||
| 447 | wprinterr(err, "\n", g_indent, "| ", stmt, | ||
| 448 | "\t[MACRO ", name, " (", #mparams, ")]\n") | ||
| 449 | end | ||
| 450 | end | ||
| 451 | if mcom then wcomment("}"..mcom) end | ||
| 452 | mac_active[opname] = nil | ||
| 453 | end | ||
| 454 | end | ||
| 455 | end | ||
| 456 | |||
| 457 | -- An .endmacro pseudo-opcode outside of a macro definition is an error. | ||
| 458 | map_coreop[".endmacro_0"] = function(params) | ||
| 459 | wfatal(".endmacro without .macro") | ||
| 460 | end | ||
| 461 | |||
| 462 | -- Dump all macros and their contents (with -PP only). | ||
| 463 | local function dumpmacros(out, lvl) | ||
| 464 | sort(mac_list) | ||
| 465 | out:write("Macros:\n") | ||
| 466 | for _,opname in ipairs(mac_list) do | ||
| 467 | local name = sub(opname, 1, -3) | ||
| 468 | local params, lines = map_op[opname]() | ||
| 469 | out:write(format(" %-20s %s\n", name, concat(params, ", "))) | ||
| 470 | if lvl > 1 then | ||
| 471 | for _,line in ipairs(lines) do | ||
| 472 | out:write(" |", line, "\n") | ||
| 473 | end | ||
| 474 | out:write("\n") | ||
| 475 | end | ||
| 476 | end | ||
| 477 | out:write("\n") | ||
| 478 | end | ||
| 479 | |||
| 480 | -- Check for unfinished macro definitions. | ||
| 481 | local function checkmacros() | ||
| 482 | if mac_capture then | ||
| 483 | wprinterr(g_fname, ":", mac_lineno, | ||
| 484 | ": error: unfinished .macro `", mac_name ,"'\n") | ||
| 485 | end | ||
| 486 | end | ||
| 487 | |||
| 488 | ------------------------------------------------------------------------------ | ||
| 489 | |||
| 490 | -- Support variables for captures. | ||
| 491 | local cap_lineno, cap_name | ||
| 492 | local cap_buffers = {} | ||
| 493 | local cap_used = {} | ||
| 494 | |||
| 495 | -- Start a capture. | ||
| 496 | map_coreop[".capture_1"] = function(params) | ||
| 497 | if not params then return "name" end | ||
| 498 | wflush() | ||
| 499 | local name = params[1] | ||
| 500 | if not match(name, "^[%a_][%w_]*$") then | ||
| 501 | wfatal("bad capture name `"..name.."'") | ||
| 502 | end | ||
| 503 | if cap_name then | ||
| 504 | wfatal("already capturing to `"..cap_name.."' since line "..cap_lineno) | ||
| 505 | end | ||
| 506 | cap_name = name | ||
| 507 | cap_lineno = g_lineno | ||
| 508 | -- Create or continue a capture buffer and start the output line capture. | ||
| 509 | local buf = cap_buffers[name] | ||
| 510 | if not buf then buf = {}; cap_buffers[name] = buf end | ||
| 511 | g_capbuffer = buf | ||
| 512 | g_synclineno = 0 | ||
| 513 | end | ||
| 514 | |||
| 515 | -- Stop a capture. | ||
| 516 | map_coreop[".endcapture_0"] = function(params) | ||
| 517 | wflush() | ||
| 518 | if not cap_name then wfatal(".endcapture without a valid .capture") end | ||
| 519 | cap_name = nil | ||
| 520 | cap_lineno = nil | ||
| 521 | g_capbuffer = nil | ||
| 522 | g_synclineno = 0 | ||
| 523 | end | ||
| 524 | |||
| 525 | -- Dump a capture buffer. | ||
| 526 | map_coreop[".dumpcapture_1"] = function(params) | ||
| 527 | if not params then return "name" end | ||
| 528 | wflush() | ||
| 529 | local name = params[1] | ||
| 530 | if not match(name, "^[%a_][%w_]*$") then | ||
| 531 | wfatal("bad capture name `"..name.."'") | ||
| 532 | end | ||
| 533 | cap_used[name] = true | ||
| 534 | wline(function(out) | ||
| 535 | local buf = cap_buffers[name] | ||
| 536 | if buf then wdumplines(out, buf) end | ||
| 537 | end) | ||
| 538 | g_synclineno = 0 | ||
| 539 | end | ||
| 540 | |||
| 541 | -- Dump all captures and their buffers (with -PP only). | ||
| 542 | local function dumpcaptures(out, lvl) | ||
| 543 | out:write("Captures:\n") | ||
| 544 | for name,buf in pairs(cap_buffers) do | ||
| 545 | out:write(format(" %-20s %4s)\n", name, "("..#buf)) | ||
| 546 | if lvl > 1 then | ||
| 547 | local bar = rep("=", 76) | ||
| 548 | out:write(" ", bar, "\n") | ||
| 549 | for _,line in ipairs(buf) do | ||
| 550 | out:write(" ", line, "\n") | ||
| 551 | end | ||
| 552 | out:write(" ", bar, "\n\n") | ||
| 553 | end | ||
| 554 | end | ||
| 555 | out:write("\n") | ||
| 556 | end | ||
| 557 | |||
| 558 | -- Check for unfinished or unused captures. | ||
| 559 | local function checkcaptures() | ||
| 560 | if cap_name then | ||
| 561 | wprinterr(g_fname, ":", cap_lineno, | ||
| 562 | ": error: unfinished .capture `", cap_name,"'\n") | ||
| 563 | return | ||
| 564 | end | ||
| 565 | for name in pairs(cap_buffers) do | ||
| 566 | if not cap_used[name] then | ||
| 567 | wprinterr(g_fname, ":*: error: missing .dumpcapture ", name ,"\n") | ||
| 568 | end | ||
| 569 | end | ||
| 570 | end | ||
| 571 | |||
| 572 | ------------------------------------------------------------------------------ | ||
| 573 | |||
| 574 | -- Sections names. | ||
| 575 | local map_sections = {} | ||
| 576 | |||
| 577 | -- Pseudo-opcode to define code sections. | ||
| 578 | -- TODO: Data sections, BSS sections. Needs extra C code and API. | ||
| 579 | map_coreop[".section_*"] = function(params) | ||
| 580 | if not params then return "name..." end | ||
| 581 | if #map_sections > 0 then werror("duplicate section definition") end | ||
| 582 | wflush() | ||
| 583 | for sn,name in ipairs(params) do | ||
| 584 | local opname = "."..name.."_0" | ||
| 585 | if not match(name, "^[%a][%w_]*$") or | ||
| 586 | map_op[opname] or map_op["."..name.."_*"] then | ||
| 587 | werror("bad section name `"..name.."'") | ||
| 588 | end | ||
| 589 | map_sections[#map_sections+1] = name | ||
| 590 | wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1)) | ||
| 591 | map_op[opname] = function(params) g_arch.section(sn-1) end | ||
| 592 | end | ||
| 593 | wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections)) | ||
| 594 | end | ||
| 595 | |||
| 596 | -- Dump all sections. | ||
| 597 | local function dumpsections(out, lvl) | ||
| 598 | out:write("Sections:\n") | ||
| 599 | for _,name in ipairs(map_sections) do | ||
| 600 | out:write(format(" %s\n", name)) | ||
| 601 | end | ||
| 602 | out:write("\n") | ||
| 603 | end | ||
| 604 | |||
| 605 | ------------------------------------------------------------------------------ | ||
| 606 | |||
| 607 | -- Load architecture-specific module. | ||
| 608 | local function loadarch(arch) | ||
| 609 | if not match(arch, "^[%w_]+$") then return "bad arch name" end | ||
| 610 | local ok, m_arch = pcall(require, "dasm_"..arch) | ||
| 611 | if not ok then return "cannot load module: "..m_arch end | ||
| 612 | g_arch = m_arch | ||
| 613 | wflush = m_arch.passcb(wline, werror, wfatal, wwarn) | ||
| 614 | m_arch.setup(arch, g_opt) | ||
| 615 | map_op, map_def = m_arch.mergemaps(map_coreop, map_def) | ||
| 616 | end | ||
| 617 | |||
| 618 | -- Dump architecture description. | ||
| 619 | function opt_map.dumparch(args) | ||
| 620 | local name = optparam(args) | ||
| 621 | if not g_arch then | ||
| 622 | local err = loadarch(name) | ||
| 623 | if err then opterror(err) end | ||
| 624 | end | ||
| 625 | |||
| 626 | local t = {} | ||
| 627 | for name in pairs(map_coreop) do t[#t+1] = name end | ||
| 628 | for name in pairs(map_op) do t[#t+1] = name end | ||
| 629 | sort(t) | ||
| 630 | |||
| 631 | local out = stdout | ||
| 632 | local _arch = g_arch._info | ||
| 633 | out:write(format("%s version %s, released %s, %s\n", | ||
| 634 | _info.name, _info.version, _info.release, _info.url)) | ||
| 635 | g_arch.dumparch(out) | ||
| 636 | |||
| 637 | local pseudo = true | ||
| 638 | out:write("Pseudo-Opcodes:\n") | ||
| 639 | for _,sname in ipairs(t) do | ||
| 640 | local name, nparam = match(sname, "^(.+)_([0-9%*])$") | ||
| 641 | if name then | ||
| 642 | if pseudo and sub(name, 1, 1) ~= "." then | ||
| 643 | out:write("\nOpcodes:\n") | ||
| 644 | pseudo = false | ||
| 645 | end | ||
| 646 | local f = map_op[sname] | ||
| 647 | local s | ||
| 648 | if nparam ~= "*" then nparam = nparam + 0 end | ||
| 649 | if nparam == 0 then | ||
| 650 | s = "" | ||
| 651 | elseif type(f) == "string" then | ||
| 652 | s = map_op[".template__"](nil, f, nparam) | ||
| 653 | else | ||
| 654 | s = f(nil, nparam) | ||
| 655 | end | ||
| 656 | if type(s) == "table" then | ||
| 657 | for _,s2 in ipairs(s) do | ||
| 658 | out:write(format(" %-12s %s\n", name, s2)) | ||
| 659 | end | ||
| 660 | else | ||
| 661 | out:write(format(" %-12s %s\n", name, s)) | ||
| 662 | end | ||
| 663 | end | ||
| 664 | end | ||
| 665 | out:write("\n") | ||
| 666 | exit(0) | ||
| 667 | end | ||
| 668 | |||
| 669 | -- Pseudo-opcode to set the architecture. | ||
| 670 | -- Only initially available (map_op is replaced when called). | ||
| 671 | map_op[".arch_1"] = function(params) | ||
| 672 | if not params then return "name" end | ||
| 673 | local err = loadarch(params[1]) | ||
| 674 | if err then wfatal(err) end | ||
| 675 | end | ||
| 676 | |||
| 677 | -- Dummy .arch pseudo-opcode to improve the error report. | ||
| 678 | map_coreop[".arch_1"] = function(params) | ||
| 679 | if not params then return "name" end | ||
| 680 | wfatal("duplicate .arch statement") | ||
| 681 | end | ||
| 682 | |||
| 683 | ------------------------------------------------------------------------------ | ||
| 684 | |||
| 685 | -- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'. | ||
| 686 | map_coreop[".nop_*"] = function(params) | ||
| 687 | if not params then return "[ignored...]" end | ||
| 688 | end | ||
| 689 | |||
| 690 | -- Pseudo-opcodes to raise errors. | ||
| 691 | map_coreop[".error_1"] = function(params) | ||
| 692 | if not params then return "message" end | ||
| 693 | werror(params[1]) | ||
| 694 | end | ||
| 695 | |||
| 696 | map_coreop[".fatal_1"] = function(params) | ||
| 697 | if not params then return "message" end | ||
| 698 | wfatal(params[1]) | ||
| 699 | end | ||
| 700 | |||
| 701 | -- Dump all user defined elements. | ||
| 702 | local function dumpdef(out) | ||
| 703 | local lvl = g_opt.dumpdef | ||
| 704 | if lvl == 0 then return end | ||
| 705 | dumpsections(out, lvl) | ||
| 706 | dumpdefines(out, lvl) | ||
| 707 | if g_arch then g_arch.dumpdef(out, lvl) end | ||
| 708 | dumpmacros(out, lvl) | ||
| 709 | dumpcaptures(out, lvl) | ||
| 710 | end | ||
| 711 | |||
| 712 | ------------------------------------------------------------------------------ | ||
| 713 | |||
| 714 | -- Helper for splitstmt. | ||
| 715 | local splitlvl | ||
| 716 | |||
| 717 | local function splitstmt_one(c) | ||
| 718 | if c == "(" then | ||
| 719 | splitlvl = ")"..splitlvl | ||
| 720 | elseif c == "[" then | ||
| 721 | splitlvl = "]"..splitlvl | ||
| 722 | elseif c == ")" or c == "]" then | ||
| 723 | if sub(splitlvl, 1, 1) ~= c then werror("unbalanced () or []") end | ||
| 724 | splitlvl = sub(splitlvl, 2) | ||
| 725 | elseif splitlvl == "" then | ||
| 726 | return " \0 " | ||
| 727 | end | ||
| 728 | return c | ||
| 729 | end | ||
| 730 | |||
| 731 | -- Split statement into (pseudo-)opcode and params. | ||
| 732 | local function splitstmt(stmt) | ||
| 733 | -- Convert label with trailing-colon into .label statement. | ||
| 734 | local label = match(stmt, "^%s*(.+):%s*$") | ||
| 735 | if label then return ".label", {label} end | ||
| 736 | |||
| 737 | -- Split at commas and equal signs, but obey parentheses and brackets. | ||
| 738 | splitlvl = "" | ||
| 739 | stmt = gsub(stmt, "[,%(%)%[%]]", splitstmt_one) | ||
| 740 | if splitlvl ~= "" then werror("unbalanced () or []") end | ||
| 741 | |||
| 742 | -- Split off opcode. | ||
| 743 | local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$") | ||
| 744 | if not op then werror("bad statement syntax") end | ||
| 745 | |||
| 746 | -- Split parameters. | ||
| 747 | local params = {} | ||
| 748 | for p in gmatch(other, "%s*(%Z+)%z?") do | ||
| 749 | params[#params+1] = gsub(p, "%s+$", "") | ||
| 750 | end | ||
| 751 | if #params > 16 then werror("too many parameters") end | ||
| 752 | |||
| 753 | params.op = op | ||
| 754 | return op, params | ||
| 755 | end | ||
| 756 | |||
| 757 | -- Process a single statement. | ||
| 758 | dostmt = function(stmt) | ||
| 759 | -- Ignore empty statements. | ||
| 760 | if match(stmt, "^%s*$") then return end | ||
| 761 | |||
| 762 | -- Capture macro defs before substitution. | ||
| 763 | if mac_capture then return mac_capture(stmt) end | ||
| 764 | stmt = definesubst(stmt) | ||
| 765 | |||
| 766 | -- Emit C code without parsing the line. | ||
| 767 | if sub(stmt, 1, 1) == "|" then | ||
| 768 | local tail = sub(stmt, 2) | ||
| 769 | wflush() | ||
| 770 | if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end | ||
| 771 | return | ||
| 772 | end | ||
| 773 | |||
| 774 | -- Split into (pseudo-)opcode and params. | ||
| 775 | local op, params = splitstmt(stmt) | ||
| 776 | |||
| 777 | -- Get opcode handler (matching # of parameters or generic handler). | ||
| 778 | local f = map_op[op.."_"..#params] or map_op[op.."_*"] | ||
| 779 | if not f then | ||
| 780 | if not g_arch then wfatal("first statement must be .arch") end | ||
| 781 | -- Improve error report. | ||
| 782 | for i=0,16 do | ||
| 783 | if map_op[op.."_"..i] then | ||
| 784 | werror("wrong number of parameters for `"..op.."'") | ||
| 785 | end | ||
| 786 | end | ||
| 787 | werror("unknown statement `"..op.."'") | ||
| 788 | end | ||
| 789 | |||
| 790 | -- Call opcode handler or special handler for template strings. | ||
| 791 | if type(f) == "string" then | ||
| 792 | map_op[".template__"](params, f) | ||
| 793 | else | ||
| 794 | f(params) | ||
| 795 | end | ||
| 796 | end | ||
| 797 | |||
| 798 | -- Process a single line. | ||
| 799 | local function doline(line) | ||
| 800 | if g_opt.flushline then wflush() end | ||
| 801 | |||
| 802 | -- Assembler line? | ||
| 803 | local indent, aline = match(line, "^(%s*)%|(.*)$") | ||
| 804 | if not aline then | ||
| 805 | -- No, plain C code line, need to flush first. | ||
| 806 | wflush() | ||
| 807 | wsync() | ||
| 808 | wline(line, false) | ||
| 809 | return | ||
| 810 | end | ||
| 811 | |||
| 812 | g_indent = indent -- Remember current line indentation. | ||
| 813 | |||
| 814 | -- Emit C code (even from macros). Avoids echo and line parsing. | ||
| 815 | if sub(aline, 1, 1) == "|" then | ||
| 816 | if not mac_capture then | ||
| 817 | wsync() | ||
| 818 | elseif g_opt.comment then | ||
| 819 | wsync() | ||
| 820 | wcomment(aline) | ||
| 821 | end | ||
| 822 | dostmt(aline) | ||
| 823 | return | ||
| 824 | end | ||
| 825 | |||
| 826 | -- Echo assembler line as a comment. | ||
| 827 | if g_opt.comment then | ||
| 828 | wsync() | ||
| 829 | wcomment(aline) | ||
| 830 | end | ||
| 831 | |||
| 832 | -- Strip assembler comments. | ||
| 833 | aline = gsub(aline, "//.*$", "") | ||
| 834 | |||
| 835 | -- Split line into statements at semicolons. | ||
| 836 | if match(aline, ";") then | ||
| 837 | for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end | ||
| 838 | else | ||
| 839 | dostmt(aline) | ||
| 840 | end | ||
| 841 | end | ||
| 842 | |||
| 843 | ------------------------------------------------------------------------------ | ||
| 844 | |||
| 845 | -- Write DynASM header. | ||
| 846 | local function dasmhead(out) | ||
| 847 | out:write(format([[ | ||
| 848 | /* | ||
| 849 | ** This file has been pre-processed with DynASM. | ||
| 850 | ** %s | ||
| 851 | ** DynASM version %s, DynASM %s version %s | ||
| 852 | ** DO NOT EDIT! The original file is in "%s". | ||
| 853 | */ | ||
| 854 | |||
| 855 | #if DASM_VERSION != %d | ||
| 856 | #error "Version mismatch between DynASM and included encoding engine" | ||
| 857 | #endif | ||
| 858 | |||
| 859 | ]], _info.url, | ||
| 860 | _info.version, g_arch._info.arch, g_arch._info.version, | ||
| 861 | g_fname, _info.vernum)) | ||
| 862 | end | ||
| 863 | |||
| 864 | -- Read input file. | ||
| 865 | readfile = function(fin) | ||
| 866 | g_indent = "" | ||
| 867 | g_lineno = 0 | ||
| 868 | g_synclineno = -1 | ||
| 869 | |||
| 870 | -- Process all lines. | ||
| 871 | for line in fin:lines() do | ||
| 872 | g_lineno = g_lineno + 1 | ||
| 873 | g_curline = line | ||
| 874 | local ok, err = pcall(doline, line) | ||
| 875 | if not ok and wprinterr(err, "\n") then return true end | ||
| 876 | end | ||
| 877 | wflush() | ||
| 878 | |||
| 879 | -- Close input file. | ||
| 880 | assert(fin == stdin or fin:close()) | ||
| 881 | end | ||
| 882 | |||
| 883 | -- Write output file. | ||
| 884 | local function writefile(outfile) | ||
| 885 | local fout | ||
| 886 | |||
| 887 | -- Open output file. | ||
| 888 | if outfile == nil or outfile == "-" then | ||
| 889 | fout = stdout | ||
| 890 | else | ||
| 891 | fout = assert(io.open(outfile, "w")) | ||
| 892 | end | ||
| 893 | |||
| 894 | -- Write all buffered lines | ||
| 895 | wdumplines(fout, g_wbuffer) | ||
| 896 | |||
| 897 | -- Close output file. | ||
| 898 | assert(fout == stdout or fout:close()) | ||
| 899 | |||
| 900 | -- Optionally dump definitions. | ||
| 901 | dumpdef(fout == stdout and stderr or stdout) | ||
| 902 | end | ||
| 903 | |||
| 904 | -- Translate an input file to an output file. | ||
| 905 | local function translate(infile, outfile) | ||
| 906 | g_wbuffer = {} | ||
| 907 | g_indent = "" | ||
| 908 | g_lineno = 0 | ||
| 909 | g_synclineno = -1 | ||
| 910 | |||
| 911 | -- Put header. | ||
| 912 | wline(dasmhead) | ||
| 913 | |||
| 914 | -- Read input file. | ||
| 915 | local fin | ||
| 916 | if infile == "-" then | ||
| 917 | g_fname = "(stdin)" | ||
| 918 | fin = stdin | ||
| 919 | else | ||
| 920 | g_fname = infile | ||
| 921 | fin = assert(io.open(infile, "r")) | ||
| 922 | end | ||
| 923 | readfile(fin) | ||
| 924 | |||
| 925 | -- Check for errors. | ||
| 926 | if not g_arch then | ||
| 927 | wprinterr(g_fname, ":*: error: missing .arch directive\n") | ||
| 928 | end | ||
| 929 | checkconds() | ||
| 930 | checkmacros() | ||
| 931 | checkcaptures() | ||
| 932 | |||
| 933 | if g_errcount ~= 0 then | ||
| 934 | stderr:write(g_fname, ":*: info: ", g_errcount, " error", | ||
| 935 | (type(g_errcount) == "number" and g_errcount > 1) and "s" or "", | ||
| 936 | " in input file -- no output file generated.\n") | ||
| 937 | dumpdef(stderr) | ||
| 938 | exit(1) | ||
| 939 | end | ||
| 940 | |||
| 941 | -- Write output file. | ||
| 942 | writefile(outfile) | ||
| 943 | end | ||
| 944 | |||
| 945 | ------------------------------------------------------------------------------ | ||
| 946 | |||
| 947 | -- Print help text. | ||
| 948 | function opt_map.help() | ||
| 949 | stdout:write("DynASM -- ", _info.description, ".\n") | ||
| 950 | stdout:write("DynASM ", _info.version, " ", _info.release, " ", _info.url, "\n") | ||
| 951 | stdout:write[[ | ||
| 952 | |||
| 953 | Usage: dynasm [OPTION]... INFILE.dasc|- | ||
| 954 | |||
| 955 | -h, --help Display this help text. | ||
| 956 | -V, --version Display version and copyright information. | ||
| 957 | |||
| 958 | -o, --outfile FILE Output file name (default is stdout). | ||
| 959 | -I, --include DIR Add directory to the include search path. | ||
| 960 | |||
| 961 | -c, --ccomment Use /* */ comments for assembler lines. | ||
| 962 | -C, --cppcomment Use // comments for assembler lines (default). | ||
| 963 | -N, --nocomment Suppress assembler lines in output. | ||
| 964 | -M, --maccomment Show macro expansions as comments (default off). | ||
| 965 | |||
| 966 | -L, --nolineno Suppress CPP line number information in output. | ||
| 967 | -F, --flushline Flush action list for every line. | ||
| 968 | |||
| 969 | -D NAME[=SUBST] Define a substitution. | ||
| 970 | -U NAME Undefine a substitution. | ||
| 971 | |||
| 972 | -P, --dumpdef Dump defines, macros, etc. Repeat for more output. | ||
| 973 | -A, --dumparch ARCH Load architecture ARCH and dump description. | ||
| 974 | ]] | ||
| 975 | exit(0) | ||
| 976 | end | ||
| 977 | |||
| 978 | -- Print version information. | ||
| 979 | function opt_map.version() | ||
| 980 | stdout:write(format("%s version %s, released %s\n%s\n\n%s", | ||
| 981 | _info.name, _info.version, _info.release, _info.url, _info.copyright)) | ||
| 982 | exit(0) | ||
| 983 | end | ||
| 984 | |||
| 985 | -- Misc. options. | ||
| 986 | function opt_map.outfile(args) g_opt.outfile = optparam(args) end | ||
| 987 | function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end | ||
| 988 | function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end | ||
| 989 | function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end | ||
| 990 | function opt_map.nocomment() g_opt.comment = false end | ||
| 991 | function opt_map.maccomment() g_opt.maccomment = true end | ||
| 992 | function opt_map.nolineno() g_opt.cpp = false end | ||
| 993 | function opt_map.flushline() g_opt.flushline = true end | ||
| 994 | function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end | ||
| 995 | |||
| 996 | ------------------------------------------------------------------------------ | ||
| 997 | |||
| 998 | -- Short aliases for long options. | ||
| 999 | local opt_alias = { | ||
| 1000 | h = "help", ["?"] = "help", V = "version", | ||
| 1001 | o = "outfile", I = "include", | ||
| 1002 | c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment", | ||
| 1003 | L = "nolineno", F = "flushline", | ||
| 1004 | P = "dumpdef", A = "dumparch", | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | -- Parse single option. | ||
| 1008 | local function parseopt(opt, args) | ||
| 1009 | opt_current = #opt == 1 and "-"..opt or "--"..opt | ||
| 1010 | local f = opt_map[opt] or opt_map[opt_alias[opt]] | ||
| 1011 | if not f then | ||
| 1012 | opterror("unrecognized option `", opt_current, "'. Try `--help'.\n") | ||
| 1013 | end | ||
| 1014 | f(args) | ||
| 1015 | end | ||
| 1016 | |||
| 1017 | -- Parse arguments. | ||
| 1018 | local function parseargs(args) | ||
| 1019 | -- Default options. | ||
| 1020 | g_opt.comment = "//|" | ||
| 1021 | g_opt.endcomment = "" | ||
| 1022 | g_opt.cpp = true | ||
| 1023 | g_opt.dumpdef = 0 | ||
| 1024 | g_opt.include = { "" } | ||
| 1025 | |||
| 1026 | -- Process all option arguments. | ||
| 1027 | args.argn = 1 | ||
| 1028 | repeat | ||
| 1029 | local a = args[args.argn] | ||
| 1030 | if not a then break end | ||
| 1031 | local lopt, opt = match(a, "^%-(%-?)(.+)") | ||
| 1032 | if not opt then break end | ||
| 1033 | args.argn = args.argn + 1 | ||
| 1034 | if lopt == "" then | ||
| 1035 | -- Loop through short options. | ||
| 1036 | for o in gmatch(opt, ".") do parseopt(o, args) end | ||
| 1037 | else | ||
| 1038 | -- Long option. | ||
| 1039 | parseopt(opt, args) | ||
| 1040 | end | ||
| 1041 | until false | ||
| 1042 | |||
| 1043 | -- Check for proper number of arguments. | ||
| 1044 | local nargs = #args - args.argn + 1 | ||
| 1045 | if nargs ~= 1 then | ||
| 1046 | if nargs == 0 then | ||
| 1047 | if g_opt.dumpdef > 0 then return dumpdef(stdout) end | ||
| 1048 | end | ||
| 1049 | opt_map.help() | ||
| 1050 | end | ||
| 1051 | |||
| 1052 | -- Translate a single input file to a single output file | ||
| 1053 | -- TODO: Handle multiple files? | ||
| 1054 | translate(args[args.argn], g_opt.outfile) | ||
| 1055 | end | ||
| 1056 | |||
| 1057 | ------------------------------------------------------------------------------ | ||
| 1058 | |||
| 1059 | -- Add the directory dynasm.lua resides in to the Lua module search path. | ||
| 1060 | local arg = arg | ||
| 1061 | if arg and arg[0] then | ||
| 1062 | local prefix = match(arg[0], "^(.*[/\\])") | ||
| 1063 | if prefix then package.path = prefix.."?.lua;"..package.path end | ||
| 1064 | end | ||
| 1065 | |||
| 1066 | -- Start DynASM. | ||
| 1067 | parseargs{...} | ||
| 1068 | |||
| 1069 | ------------------------------------------------------------------------------ | ||
| 1070 | |||
