Explorar o código

Implement DefConcatRes

Another of those opcodes that doesn't look too bad when you start, and then
turns into a bit of a monstrosity. I think this will work fine.
Isaac Woods %!s(int64=3) %!d(string=hai) anos
pai
achega
a883868dd5
Modificáronse 2 ficheiros con 58 adicións e 0 borrados
  1. 1 0
      aml/src/opcode.rs
  2. 57 0
      aml/src/type2.rs

+ 1 - 0
aml/src/opcode.rs

@@ -53,6 +53,7 @@ pub const DEF_CONCAT_OP: u8 = 0x73;
 pub const DEF_SHIFT_LEFT: u8 = 0x79;
 pub const DEF_SHIFT_RIGHT: u8 = 0x7a;
 pub const DEF_AND_OP: u8 = 0x7b;
+pub const DEF_CONCAT_RES_OP: u8 = 0x84;
 pub const DEF_L_OR_OP: u8 = 0x91;
 pub const DEF_L_NOT_OP: u8 = 0x92;
 pub const DEF_L_EQUAL_OP: u8 = 0x93;

+ 57 - 0
aml/src/type2.rs

@@ -45,6 +45,7 @@ where
             def_and(),
             def_buffer(),
             def_concat(),
+            def_concat_res(),
             def_l_equal(),
             def_l_greater(),
             def_l_greater_equal(),
@@ -194,6 +195,62 @@ where
         .map(|((), result)| Ok(result))
 }
 
+pub fn def_concat_res<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
+where
+    'c: 'a,
+{
+    /*
+     * DefConcatRes := 0x84 BufData BufData Target
+     * BufData := TermArg => Buffer
+     */
+    opcode(opcode::DEF_CONCAT_RES_OP)
+        .then(comment_scope(
+            DebugVerbosity::AllScopes,
+            "DefConcatRes",
+            term_arg().then(term_arg()).then(target()).map_with_context(|((left, right), target), context| {
+                let left = try_with_context!(context, left.as_buffer(context));
+                let right = try_with_context!(context, right.as_buffer(context));
+
+                if left.len() == 1 || right.len() == 1 {
+                    return (Err(Propagate::Err(AmlError::ResourceDescriptorTooShort)), context);
+                }
+
+                /*
+                 * `left` and `right` are buffers of resource descriptors, which we're trying to concatenate into a
+                 * new, single buffer containing all of the descriptors from the source buffers. We need to strip
+                 * off the end tags (2 bytes from each buffer), and then add our own end tag.
+                 *
+                 * XXX: either buffer may be empty (contains no tags), and so our arithmetic has to be careful.
+                 */
+                let result = {
+                    let mut result =
+                        Vec::with_capacity(left.len().saturating_sub(2) + right.len().saturating_sub(2) + 2);
+                    result.extend_from_slice(if left.len() == 0 { &[] } else { &left[..(left.len() - 2)] });
+                    result.extend_from_slice(if right.len() == 0 { &[] } else { &right[..(right.len() - 2)] });
+
+                    /*
+                     * Construct a new end tag, including a new checksum:
+                     *    | Bits        | Field             | Value                     |
+                     *    |-------------|-------------------|---------------------------|
+                     *    | 0-2         | Length - n bytes  | 1 (for checksum)          |
+                     *    | 3-6         | Small item type   | 0x0f = end tag descriptor |
+                     *    | 7           | 0 = small item    | 0                         |
+                     */
+                    result.push(0b01111001);
+                    result.push(
+                        result.iter().fold(0u8, |checksum, byte| checksum.wrapping_add(*byte)).wrapping_neg(),
+                    );
+
+                    AmlValue::Buffer(result)
+                };
+
+                try_with_context!(context, context.store(target, result.clone()));
+                (Ok(result), context)
+            }),
+        ))
+        .map(|((), result)| Ok(result))
+}
+
 fn def_l_or<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
 where
     'c: 'a,