@@ -434,15 +434,12 @@ impl AmlValue {
434434 }
435435 }
436436
437- /// Stores an IndexField's index (as specified by its offset) and returns the bit offset within
438- /// the Data part of the field
439- fn write_index (
440- & mut self ,
437+ fn index_field_access < F : FnMut ( u64 , usize , usize , usize ) -> Result < ( ) , AmlError > > (
441438 index_flags : & FieldFlags ,
442439 offset : u64 ,
443440 length : u64 ,
444- context : & mut AmlContext ,
445- ) -> Result < usize , AmlError > {
441+ mut access : F ,
442+ ) -> Result < ( ) , AmlError > {
446443 let index_align = match index_flags. access_type ( ) ? {
447444 FieldAccessType :: Any => 8 ,
448445 FieldAccessType :: Byte => 8 ,
@@ -452,31 +449,37 @@ impl AmlValue {
452449 FieldAccessType :: Buffer => 8 ,
453450 } ;
454451
455- // Value to write to the Index part of the field
456- let length = length as usize ;
457- let index_value = ( offset / 8 ) & !( ( index_align >> 3 ) - 1 ) ;
458- let bit_offset = ( offset - index_value * 8 ) as usize ;
459-
460- // TODO handle cases when access_type/offset/length combinations lead to crossing of index
461- // boundary
462- if ( bit_offset + length - 1 ) / index_align as usize != 0 {
463- todo ! (
464- "IndexField access crosses the index boundary (range: {:#x?}, access type: {} bits)" ,
465- bit_offset..( bit_offset + length) ,
466- index_align
467- ) ;
468- }
452+ let mut length = length as usize ;
453+ let mut index = ( offset / 8 ) & !( ( index_align >> 3 ) - 1 ) ;
454+
455+ // Bit offset in the target Data field
456+ let mut bit_offset = ( offset - index * 8 ) as usize ;
457+ // Bit offset in the source value
458+ let mut pos = 0 ;
459+
460+ while length != 0 {
461+ // Bit offset within a single value
462+ let value_pos = bit_offset % index_align as usize ;
463+ // Number of bits until the end of the value
464+ let value_limit = index_align as usize - value_pos;
465+ // Number of bits to access
466+ let len = cmp:: min ( length, value_limit) ;
467+
468+ access ( index, pos, value_pos, len) ?;
469469
470- // Write the desired index
471- // NOTE not sure if the spec says the index field can only be an integer one, but I can't
472- // think of any reason for it to be anything else
473- self . write_field ( AmlValue :: Integer ( index_value ) , context ) ? ;
470+ // Advance the bit position
471+ bit_offset += len ;
472+ pos += len ;
473+ length -= len ;
474474
475- Ok ( bit_offset)
475+ // Move to the next index
476+ index += index_align >> 3 ;
477+ }
478+
479+ Ok ( ( ) )
476480 }
477481
478- /// Reads from an IndexField, returning either an `Integer` or a `Buffer` depending on the
479- /// field size
482+ /// Reads from an IndexField, returning either an `Integer`
480483 pub fn read_index_field ( & self , context : & mut AmlContext ) -> Result < AmlValue , AmlError > {
481484 let AmlValue :: IndexField { index, data, flags, offset, length } = self else {
482485 return Err ( AmlError :: IncompatibleValueConversion {
@@ -488,14 +491,22 @@ impl AmlValue {
488491 let mut index_field = context. namespace . get_mut ( * index) ?. clone ( ) ;
489492 let data_field = context. namespace . get_mut ( * data) ?. clone ( ) ;
490493
491- // Write the Index part of the field
492- let bit_offset = index_field. write_index ( flags, * offset, * length, context) ?;
493-
494494 // TODO buffer field accesses
495+ let mut value = 0u64 ;
496+
497+ Self :: index_field_access ( flags, * offset, * length, |index, value_offset, field_offset, length| {
498+ // Store the bit range index to the Index field
499+ index_field. write_field ( AmlValue :: Integer ( index) , context) ?;
500+
501+ // Read the bit range from the Data field
502+ let data = data_field. read_field ( context) ?. as_integer ( context) ?;
503+ let bits = data. get_bits ( field_offset..field_offset + length) ;
495504
496- // Read the value of the Data field
497- let field_value = data_field. read_field ( context) ?. as_integer ( context) ?;
498- let value = field_value. get_bits ( bit_offset..( bit_offset + * length as usize ) ) ;
505+ // Copy the bit range to the value
506+ value. set_bits ( value_offset..value_offset + length, bits) ;
507+
508+ Ok ( ( ) )
509+ } ) ?;
499510
500511 Ok ( AmlValue :: Integer ( value) )
501512 }
@@ -511,22 +522,24 @@ impl AmlValue {
511522 let mut index_field = context. namespace . get_mut ( * index) ?. clone ( ) ;
512523 let mut data_field = context. namespace . get_mut ( * data) ?. clone ( ) ;
513524
514- // Write the Index part of the field
515- let bit_offset = index_field. write_index ( flags, * offset, * length, context) ?;
525+ let value = value. as_integer ( context) ?;
516526
517- // TODO handle field update rule properly
518- // TODO buffer field accesses
527+ Self :: index_field_access ( flags , * offset , * length , |index , value_offset , field_offset , length| {
528+ // TODO handle the UpdateRule flag
519529
520- // Read the old value of the Data field ( to preserve bits we're not interested in)
521- let mut field_value = data_field . read_field ( context ) ? . as_integer ( context) ?;
530+ // Store the bit range index to the Index field
531+ index_field . write_field ( AmlValue :: Integer ( index ) , context) ?;
522532
523- // Modify the bits
524- field_value . set_bits ( bit_offset.. ( bit_offset + * length as usize ) , value. as_integer ( context ) ? ) ;
533+ // Extract the bits going to this specific part of the field
534+ let bits = value. get_bits ( value_offset..value_offset + length ) ;
525535
526- // Write the Data field back
527- data_field. write_field ( AmlValue :: Integer ( field_value) , context) ?;
536+ // Read/modify/store the data field
537+ let mut data = data_field. read_field ( context) ?. as_integer ( context) ?;
538+ data. set_bits ( field_offset..field_offset + length, bits) ;
539+ data_field. write_field ( AmlValue :: Integer ( data) , context) ?;
528540
529- Ok ( ( ) )
541+ Ok ( ( ) )
542+ } )
530543 }
531544
532545 /// Reads from a field of an opregion, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`,
0 commit comments