1 module gbaid.gba.assembly;
2 
3 import std.algorithm.searching : find;
4 import std.exception : assumeUnique;
5 
6 version (D_InlineAsm_X86_64) {
7     public enum string LINE_BACKGROUND_TEXT_ASM = "asm {"
8         ~ import("line_background_text_x64.s").convertToDASM() ~
9     "}";
10     public enum string LINE_BACKGROUND_AFFINE_ASM = "asm {"
11         ~ import("line_background_affine_x64.s").convertToDASM() ~
12     "}";
13     public enum string ADD_WITH_FLAGS_ASM = "asm {"
14         ~ import("add_with_flags_x64.s").convertToDASM() ~
15     "}";
16     public enum string SUB_WITH_FLAGS_ASM = "asm {"
17         ~ import("sub_with_flags_x64.s").convertToDASM() ~
18     "}";
19 } else version (D_InlineAsm_X86) {
20     public enum string LINE_BACKGROUND_TEXT_ASM = "asm {"
21         ~ import("line_background_text_x64.s").x64_to_x86().convertToDASM() ~
22     "}";
23     public enum string LINE_BACKGROUND_AFFINE_ASM = "asm {"
24         ~ import("line_background_affine_x64.s").x64_to_x86().convertToDASM() ~
25     "}";
26     public enum string ADD_WITH_FLAGS_ASM = "asm {"
27         ~ import("add_with_flags_x64.s").x64_to_x86().convertToDASM() ~
28     "}";
29     public enum string SUB_WITH_FLAGS_ASM = "asm {"
30         ~ import("sub_with_flags_x64.s").x64_to_x86().convertToDASM() ~
31     "}";
32 }
33 
34 private string convertToDASM(string rawAsm) {
35     return rawAsm.removeComments().addSemiColons();
36 }
37 
38 private string removeComments(inout char[] asmStr) {
39     char[] noComments;
40     auto ignore = false;
41     foreach (c; asmStr) {
42         if (c == ';') {
43             ignore = true;
44         } else if (c == '\r' || c == '\n') {
45             ignore = false;
46         }
47         if (!ignore) {
48             noComments ~= c;
49         }
50     }
51     return noComments.assumeUnique();
52 }
53 
54 private string addSemiColons(inout char[] asmStr) {
55     char[] withSemiColons = [asmStr[0]];
56     foreach (i, c; asmStr[1 .. $]) {
57         withSemiColons ~= c;
58         if (c == '\n' && asmStr[i - 1] != ':') {
59             withSemiColons ~= ';';
60         }
61     }
62     return withSemiColons.assumeUnique();
63 }
64 
65 // Very basic conversion for the purpose of this project only
66 // Only converts 64 registers to 32 bit and pushfq to pusfd
67 private string x64_to_x86(inout char[] x64) {
68     size_t length = x64.length;
69     char[] x86;
70     x86.length = length;
71     foreach (i; 0 .. length - 2) {
72         if (x64[i] == 'R' && x64[i + 2] == 'X') {
73             char c = x64[i + 1];
74             if (c == 'A' || c == 'B' || c == 'C' || c == 'D') {
75                 x86[i] = 'E';
76                 continue;
77             }
78         }
79         x86[i] = x64[i];
80     }
81     x86[length - 1] = x64[length - 1];
82     x86[length - 2] = x64[length - 2];
83 
84     auto pushOp = x86.find("pushfq");
85     if (pushOp.length > 0) {
86         pushOp[5] = 'd';
87     }
88 
89     return x86.assumeUnique;
90 }