asmpeach/assembler/generator/
generate.rs

1use crate::assembler::resource::{Opcode, RelaSymbol, Symbol};
2use indexmap::map::IndexMap;
3
4#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
5struct RelativeJumpSpec {
6    operand_offset: isize,
7    address: isize,
8    is_label: bool,
9}
10
11impl RelativeJumpSpec {
12    fn new_label(addr: isize) -> Self {
13        Self {
14            operand_offset: 0,
15            address: addr,
16            is_label: true,
17        }
18    }
19    fn new_jump(offset: isize) -> Self {
20        Self {
21            operand_offset: offset,
22            address: offset,
23            is_label: false,
24        }
25    }
26}
27
28pub fn generate_main(symbols: &mut IndexMap<String, Symbol>) -> IndexMap<String, Vec<RelaSymbol>> {
29    let mut reloc_syms = IndexMap::new();
30
31    for (sym_name, sym) in symbols.iter_mut() {
32        let (mut sym_codes, relocs_in_sym) = gen_symbol_code(sym);
33        reloc_syms.insert(sym_name.to_string(), relocs_in_sym);
34
35        // アラインメント調整
36        let mut extra_bytes: Vec<u8> = Vec::new();
37
38        let rest_bytes = sym_codes.len() % 4;
39        for _ in 0..(4 - rest_bytes) {
40            extra_bytes.push(0x00);
41        }
42        sym_codes.append(&mut extra_bytes);
43
44        sym.codes = sym_codes;
45    }
46
47    reloc_syms
48}
49
50fn gen_symbol_code(sym: &Symbol) -> (Vec<u8>, Vec<RelaSymbol>) {
51    let mut relative_jump_offset: IndexMap<String, Vec<RelativeJumpSpec>> = IndexMap::new();
52    let mut code_offset = 0;
53
54    let mut symbol_codes = Vec::new();
55    let mut relocations = Vec::new();
56
57    // ラベルごとに機械語に変換
58    for group in sym.groups.iter() {
59        // jump系命令がラベルの前に存在した場合
60        if let Some(specs) = relative_jump_offset.get(&group.label) {
61            for spec in specs {
62                // 相対オフセットの計算
63                let relative_offset = code_offset - spec.address;
64
65                for (idx, addr) in (relative_offset as u32).to_le_bytes().iter().enumerate() {
66                    symbol_codes[idx + (spec.operand_offset - 4) as usize] = *addr;
67                }
68            }
69        } else {
70            // ラベルがjump系命令の前に存在した場合
71            if !group.label.ends_with("_entry") {
72                // ラベルの位置を保存しておく
73                relative_jump_offset.insert(
74                    group.label.to_string(),
75                    vec![RelativeJumpSpec::new_label(code_offset)],
76                );
77            }
78        }
79
80        for inst in group.insts.iter() {
81            // いくつかの命令は再配置シンボルの生成など,
82            // 機械語への変換以外にも操作が必要.
83
84            match &inst.opcode {
85                Opcode::CALLFUNC(func) => {
86                    // 適当なアドレスを生成しておく
87                    let mut inst_bytes = vec![0xe8, 0x00, 0x00, 0x00, 0x00];
88
89                    let rela64 = new_rela64(func.copy_label(), code_offset);
90                    relocations.push(rela64);
91
92                    code_offset += inst_bytes.len() as isize;
93                    symbol_codes.append(&mut inst_bytes);
94                }
95
96                // jump
97                Opcode::JELABEL { label } => {
98                    let mut inst_bytes = inst.to_bytes();
99                    inst_bytes.append(&mut vec![0x00, 0x00, 0x00, 0x00]);
100                    code_offset += inst_bytes.len() as isize;
101                    symbol_codes.append(&mut inst_bytes);
102
103                    resolve_jump(
104                        label,
105                        code_offset,
106                        &mut relative_jump_offset,
107                        &mut symbol_codes,
108                    );
109                }
110                Opcode::JLELABEL { label } => {
111                    let mut inst_bytes = inst.to_bytes();
112                    inst_bytes.append(&mut vec![0x00, 0x00, 0x00, 0x00]);
113                    code_offset += inst_bytes.len() as isize;
114                    symbol_codes.append(&mut inst_bytes);
115
116                    resolve_jump(
117                        label,
118                        code_offset,
119                        &mut relative_jump_offset,
120                        &mut symbol_codes,
121                    );
122                }
123                Opcode::JMPLABEL { label } => {
124                    let mut inst_bytes = inst.to_bytes();
125                    inst_bytes.append(&mut vec![0x00, 0x00, 0x00, 0x00]);
126                    code_offset += inst_bytes.len() as isize;
127                    symbol_codes.append(&mut inst_bytes);
128
129                    resolve_jump(
130                        label,
131                        code_offset,
132                        &mut relative_jump_offset,
133                        &mut symbol_codes,
134                    );
135                }
136                _ => {
137                    let mut inst_bytes = inst.to_bytes();
138                    code_offset += inst_bytes.len() as isize;
139                    symbol_codes.append(&mut inst_bytes);
140                }
141            }
142        }
143    }
144
145    (symbol_codes, relocations)
146}
147
148fn resolve_jump(
149    label: &str,
150    length: isize,
151    relative_jump: &mut IndexMap<String, Vec<RelativeJumpSpec>>,
152    sym_codes: &mut Vec<u8>,
153) {
154    if let Some(specs) = relative_jump.get_mut(label) {
155        for spec in specs.iter() {
156            // jump -> jump みたいなものは無視
157            if !spec.is_label {
158                continue;
159            }
160
161            // 相対オフセットの計算
162            let relative_offset = spec.address - length;
163            for (idx, addr) in (relative_offset as i32).to_le_bytes().iter().enumerate() {
164                sym_codes[idx + (length - 4) as usize] = *addr;
165            }
166        }
167
168        specs.push(RelativeJumpSpec::new_jump(length));
169    } else {
170        // jump系命令がラベルの前に存在した場合
171        relative_jump.insert(label.to_string(), vec![RelativeJumpSpec::new_jump(length)]);
172    }
173}
174
175fn new_rela64(name: String, offset: isize) -> RelaSymbol {
176    let mut rela64: RelaSymbol = Default::default();
177    rela64.rela64.set_addend(-4);
178    rela64.name = name;
179
180    // opcode 分スキップ
181    rela64.rela64.set_offset(offset as u64 + 1);
182
183    rela64
184}