1use super::{Instruction, QVM, VM_MAGIC};
4use opcodes::Opcode;
5use super::errors::*;
6use nom;
7use nom::{le_u32, le_u8};
8
9type Input = u8;
10type InputSlice<'a> = &'a [Input];
11
12macro_rules! instruction {
14 ($name:ident, $opcode:path, $instruction:path) => {
15 named!($name<InputSlice,Instruction>,
16 value!($instruction, tag!([$opcode as Input]))
17 );
18 }
19}
20
21macro_rules! instruction_with_operand {
23 ($name:ident, $opcode:path, $operand_fn:ident, $instruction:path) => {
24 named!($name<InputSlice,Instruction>,
25 do_parse!(
26 tag!([$opcode as Input]) >>
27 operand: $operand_fn >>
28 ($instruction(operand))
29 )
30 );
31 }
32}
33
34macro_rules! instruction_u32 {
36 ($name:ident, $opcode:path, $instruction:path) => {
37 instruction_with_operand!($name, $opcode, le_u32, $instruction);
38 }
39}
40
41macro_rules! instruction_u8 {
43 ($name:ident, $opcode:path, $instruction:path) => {
44 instruction_with_operand!($name, $opcode, le_u8, $instruction);
45 }
46}
47
48
49instruction!(instruction_undef, Opcode::UNDEF, Instruction::UNDEF);
50
51instruction!(instruction_ignore, Opcode::IGNORE, Instruction::IGNORE);
52
53instruction!(instruction_break, Opcode::BREAK, Instruction::BREAK);
54
55instruction_u32!(instruction_enter, Opcode::ENTER, Instruction::ENTER);
56instruction_u32!(instruction_leave, Opcode::LEAVE, Instruction::LEAVE);
57instruction!(instruction_call, Opcode::CALL, Instruction::CALL);
58instruction!(instruction_push, Opcode::PUSH, Instruction::PUSH);
59instruction!(instruction_pop, Opcode::POP, Instruction::POP);
60
61instruction_u32!(instruction_const, Opcode::CONST, Instruction::CONST);
62instruction_u32!(instruction_local, Opcode::LOCAL, Instruction::LOCAL);
63
64instruction!(instruction_jump, Opcode::JUMP, Instruction::JUMP);
65
66instruction_u32!(instruction_eq, Opcode::EQ, Instruction::EQ);
67instruction_u32!(instruction_ne, Opcode::NE, Instruction::NE);
68
69instruction_u32!(instruction_lti, Opcode::LTI, Instruction::LTI);
70instruction_u32!(instruction_lei, Opcode::LEI, Instruction::LEI);
71instruction_u32!(instruction_gti, Opcode::GTI, Instruction::GTI);
72instruction_u32!(instruction_gei, Opcode::GEI, Instruction::GEI);
73
74instruction_u32!(instruction_ltu, Opcode::LTU, Instruction::LTU);
75instruction_u32!(instruction_leu, Opcode::LEU, Instruction::LEU);
76instruction_u32!(instruction_gtu, Opcode::GTU, Instruction::GTU);
77instruction_u32!(instruction_geu, Opcode::GEU, Instruction::GEU);
78
79instruction_u32!(instruction_eqf, Opcode::EQF, Instruction::EQF);
80instruction_u32!(instruction_nef, Opcode::NEF, Instruction::NEF);
81
82instruction_u32!(instruction_ltf, Opcode::LTF, Instruction::LTF);
83instruction_u32!(instruction_lef, Opcode::LEF, Instruction::LEF);
84instruction_u32!(instruction_gtf, Opcode::GTF, Instruction::GTF);
85instruction_u32!(instruction_gef, Opcode::GEF, Instruction::GEF);
86
87instruction!(instruction_load1, Opcode::LOAD1, Instruction::LOAD1);
88instruction!(instruction_load2, Opcode::LOAD2, Instruction::LOAD2);
89instruction!(instruction_load4, Opcode::LOAD4, Instruction::LOAD4);
90instruction!(instruction_store1, Opcode::STORE1, Instruction::STORE1);
91instruction!(instruction_store2, Opcode::STORE2, Instruction::STORE2);
92instruction!(instruction_store4, Opcode::STORE4, Instruction::STORE4);
93instruction_u8!(instruction_arg, Opcode::ARG, Instruction::ARG);
94
95instruction_u32!(instruction_block_copy,
96 Opcode::BLOCK_COPY,
97 Instruction::BLOCK_COPY);
98
99instruction!(instruction_sex8, Opcode::SEX8, Instruction::SEX8);
100instruction!(instruction_sex16, Opcode::SEX16, Instruction::SEX16);
101
102instruction!(instruction_negi, Opcode::NEGI, Instruction::NEGI);
103instruction!(instruction_add, Opcode::ADD, Instruction::ADD);
104instruction!(instruction_sub, Opcode::SUB, Instruction::SUB);
105instruction!(instruction_divi, Opcode::DIVI, Instruction::DIVI);
106instruction!(instruction_divu, Opcode::DIVU, Instruction::DIVU);
107instruction!(instruction_modi, Opcode::MODI, Instruction::MODI);
108instruction!(instruction_modu, Opcode::MODU, Instruction::MODU);
109instruction!(instruction_muli, Opcode::MULI, Instruction::MULI);
110instruction!(instruction_mulu, Opcode::MULU, Instruction::MULU);
111
112instruction!(instruction_band, Opcode::BAND, Instruction::BAND);
113instruction!(instruction_bor, Opcode::BOR, Instruction::BOR);
114instruction!(instruction_bxor, Opcode::BXOR, Instruction::BXOR);
115instruction!(instruction_bcom, Opcode::BCOM, Instruction::BCOM);
116
117instruction!(instruction_lsh, Opcode::LSH, Instruction::LSH);
118instruction!(instruction_rshi, Opcode::RSHI, Instruction::RSHI);
119instruction!(instruction_rshu, Opcode::RSHU, Instruction::RSHU);
120
121instruction!(instruction_negf, Opcode::NEGF, Instruction::NEGF);
122instruction!(instruction_addf, Opcode::ADDF, Instruction::ADDF);
123instruction!(instruction_subf, Opcode::SUBF, Instruction::SUBF);
124instruction!(instruction_divf, Opcode::DIVF, Instruction::DIVF);
125instruction!(instruction_mulf, Opcode::MULF, Instruction::MULF);
126
127instruction!(instruction_cvif, Opcode::CVIF, Instruction::CVIF);
128instruction!(instruction_cvfi, Opcode::CVFI, Instruction::CVFI);
129
130
131named!(ins<InputSlice,Instruction>,
132 alt!(instruction_undef
133 | instruction_ignore
134 | instruction_break
135 | instruction_enter | instruction_leave | instruction_call | instruction_push | instruction_pop
136 | instruction_const | instruction_local
137 | instruction_jump
138 | instruction_eq | instruction_ne
139 | instruction_lti | instruction_lei | instruction_gti | instruction_gei
140 | instruction_ltu | instruction_leu | instruction_gtu | instruction_geu
141 | instruction_eqf | instruction_nef
142 | instruction_ltf | instruction_lef | instruction_gtf | instruction_gef
143 | instruction_load1 | instruction_load2 | instruction_load4 | instruction_store1 | instruction_store2 | instruction_store4 | instruction_arg
144 | instruction_block_copy
145 | instruction_sex8 | instruction_sex16
146 | instruction_negi | instruction_add | instruction_sub | instruction_divi | instruction_divu | instruction_modi | instruction_modu | instruction_muli | instruction_mulu
147 | instruction_band | instruction_bor | instruction_bxor | instruction_bcom
148 | instruction_lsh | instruction_rshi | instruction_rshu
149 | instruction_negf | instruction_addf | instruction_subf | instruction_divf | instruction_mulf
150 | instruction_cvif | instruction_cvfi
151 )
152);
153
154macro_rules! length_size(
155 ($i:expr, $s:expr, $submac:ident!( $($args:tt)* )) => (
156 {
157 match take!($i, $s as usize) {
158 nom::IResult::Error(e) => nom::IResult::Error(e),
159 nom::IResult::Incomplete(nom::Needed::Unknown) => nom::IResult::Incomplete(nom::Needed::Unknown),
160 nom::IResult::Incomplete(nom::Needed::Size(n)) => {
161 nom::IResult::Incomplete(nom::Needed::Size(
162 n + nom::InputLength::input_len(&($i)) - ($s)
163 ))
164 },
165 nom::IResult::Done(i2, o2) => {
166 match complete!(o2, $submac!($($args)*)) {
167 nom::IResult::Error(e) => nom::IResult::Error(e),
168 nom::IResult::Incomplete(i) => nom::IResult::Incomplete(i),
169 nom::IResult::Done(_, o3) => nom::IResult::Done(i2, o3)
170 }
171 }
172 }
173 }
174 );
175
176 ($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
177 length_value!($i, $submac!($($args)*), call!($g));
178 );
179
180 ($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
181 length_value!($i, call!($f), $submac!($($args)*));
182 );
183
184 ($i:expr, $f:expr, $g:expr) => (
185 length_value!($i, call!($f), call!($g));
186 );
187);
188
189const HEADER_LENGTH_V1: u32 = 32;
190
191named!(qvm<InputSlice, QVM>,
192 do_parse!(
193 tag!(VM_MAGIC) >>
194instruction_count: le_u32 >>
196 code_offset: le_u32 >>
197 code_length: le_u32 >>
198 data_offset: le_u32 >>
199 data_length: le_u32 >>
200 lit_length: le_u32 >>
201 bss_length: le_u32 >>
202 take!(code_offset - HEADER_LENGTH_V1) >>
204 code: length_size!(
205 code_length as usize,
206 count!(ins, instruction_count as usize)
207 ) >>
208 take!(data_offset - code_offset - code_length) >>
210 data: length_size!(
211 data_length as usize,
212 count!(le_u32, data_length as usize / 4)
213 ) >>
214 lit: length_size!(
216 lit_length as usize,
217 count!(le_u8, lit_length as usize)
218 ) >>
219 eof!() >>
220 (
221 QVM {
222 code: code,
223 data: data,
224 lit: lit,
225 bss_length: bss_length,
226 }
227 )
228 )
229);
230
231
232pub fn parse_qvm(data: InputSlice) -> Result<QVM> {
234 match qvm(data).to_full_result() {
235 Ok(v) => Ok(v),
236 Err(e) => Err(ErrorKind::Parser(e).into()),
237 }
238}
239
240
241#[cfg(test)]
242mod tests {
243 use super::{instruction_break, instruction_enter, instruction_arg, ins, qvm, parse_qvm,
244 InputSlice};
245 use bytecode::Instruction;
246 use nom::IResult;
247 use nom;
248 use QVM;
249
250 const Q3ASM_STACK_SIZE: usize = 0x10000;
252
253 #[test]
254 fn test_instruction_break_exact_match() {
255 let data = [0x2];
256 let result = instruction_break(&data);
257 assert_eq!(result, IResult::Done(&b""[..], Instruction::BREAK));
258 }
259
260 #[test]
261 fn test_instruction_break_tag_mismatch() {
262 let data = [0x0];
263 let result = instruction_break(&data);
264 assert_eq!(result, IResult::Error(nom::ErrorKind::Tag));
265 }
266
267 #[test]
268 fn test_instruction_enter_exact_match() {
269 let data = [0x3, 0x42, 0x0, 0x0, 0x0];
270 let result = instruction_enter(&data);
271 assert_eq!(result, IResult::Done(&b""[..], Instruction::ENTER(0x42)));
272 }
273
274 #[test]
275 fn test_instruction_arg_exact_match() {
276 let data = [0x21, 0x42];
277 let result = instruction_arg(&data);
278 assert_eq!(result, IResult::Done(&b""[..], Instruction::ARG(0x42)));
279 }
280
281 #[test]
282 fn test_ins_enter_exact_match() {
283 let data = [0x3, 0x42, 0x0, 0x0, 0x0];
284 let result = ins(&data);
285 assert_eq!(result, IResult::Done(&b""[..], Instruction::ENTER(0x42)));
286 }
287
288 #[test]
289 fn test_qvm_file_minimal() {
290 let data = include_bytes!("../assets/mod-minimal.qvm");
291 let result = qvm(data);
292 let expected = QVM {
293 code: vec![Instruction::ENTER(8),
294 Instruction::CONST(4294967295), Instruction::LEAVE(8),
296 Instruction::PUSH,
297 Instruction::LEAVE(8)],
298 data: vec![0],
299 lit: vec![],
300 bss_length: Q3ASM_STACK_SIZE as u32,
301 };
302 assert_eq!(result, IResult::Done(&b""[..], expected));
303 }
304
305 #[test]
306 fn test_qvm_file_bss() {
307 let data = include_bytes!("../assets/mod-bss.qvm");
308 let result = qvm(data);
309 let expected = QVM {
310 code: vec![Instruction::ENTER(8),
311 Instruction::CONST(4294967295), Instruction::LEAVE(8),
313 Instruction::PUSH,
314 Instruction::LEAVE(8)],
315 data: vec![0],
316 lit: vec![],
317 bss_length: Q3ASM_STACK_SIZE as u32 + 4,
318 };
319 assert_eq!(result, IResult::Done(&b""[..], expected));
320 }
321
322 #[test]
323 fn test_qvm_file_data() {
324 let data = include_bytes!("../assets/mod-data.qvm");
325 let result = qvm(data);
326 let expected = QVM {
327 code: vec![Instruction::ENTER(8),
328 Instruction::CONST(4294967295), Instruction::LEAVE(8),
330 Instruction::PUSH,
331 Instruction::LEAVE(8)],
332 data: vec![
333 0, 0xDEADBEEF,
335 ],
336 lit: vec![],
337 bss_length: Q3ASM_STACK_SIZE as u32,
338 };
339 assert_eq!(result, IResult::Done(&b""[..], expected));
340 }
341
342 #[test]
343 fn test_qvm_file_lit() {
344 let data = include_bytes!("../assets/mod-lit.qvm");
345 let result = qvm(data);
346 let expected = QVM {
347 code: vec![Instruction::ENTER(8),
348 Instruction::CONST(4294967295), Instruction::LEAVE(8),
350 Instruction::PUSH,
351 Instruction::LEAVE(8)],
352 data: vec![0],
353 lit: vec![
354 '!' as u8,
355 0, 0,
357 0,
358 ],
359 bss_length: Q3ASM_STACK_SIZE as u32,
360 };
361 assert_eq!(result, IResult::Done(&b""[..], expected));
362 }
363
364
365 #[test]
366 fn test_ins_file() {
367 let data = include_bytes!("../assets/mod-minimal.qvm");
368 named!(ins5<InputSlice,Vec<Instruction>>, count!(ins, 5));
369 let result = ins5(&data[32..53]);
370 let expected = vec![Instruction::ENTER(8),
371 Instruction::CONST(4294967295), Instruction::LEAVE(8),
373 Instruction::PUSH,
374 Instruction::LEAVE(8)];
375 assert_eq!(result, IResult::Done(&b""[..], expected));
376 }
377
378 #[test]
380 #[ignore]
382 fn test_parse_qvm_ioq3_qagame() {
383 let data = include_bytes!("../assets/ioq3/baseq3/vm/qagame.qvm");
384 let result = parse_qvm(data).unwrap();
385 }
387
388}