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