2 очень мелкие задачки

Тема в разделе "WASM.A&O", создана пользователем Black_mirror, 23 июн 2006.

  1. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Нечто похожее уже было, но "повторенье - мать ученья" 8)



    Есть структура состоящая из 4 байт:
    Код (Text):
    1. секунды db ? ;(0..59) младший байт
    2. минуты  db ? ;(0..59)
    3. часы    db ? ;(0..23)
    4. дни     db ? ;(-128..127) старший байт




    Необходимо написать оптимизированные по размеру функции time_add и time_sub, принимающие в регистрах r1,r2 (выбираете произвольно, но должны совпадать для обеих функций) две такие структуры, и вычисляющие r1=r1+r2 и r1=r1-r2 соответственно. Код должен быть без переходов (за исключением ret'а в конце функции), r2 портить нельзя, секунды и минуты в результате не должны выходить за диапазон 0..59, а часы за диапазон 0..23.



    Примеры:

    0F2828h(15:40:40) + 0F2828h(15:40:40) = 1071514h(1д 7:21:20)

    0F2828h(15:40:40) - 1071514h(1д 7:21:20) = 0FF081314h (-1д 8:19:20)
     
  2. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Первое что приходит в голову (без претензий на "оптимальность")
    Код (Text):
    1.   ;сложение: r1=eax, r2=edx
    2.   mov ebx,0E8C4C4h
    3.   add eax,ebx
    4.   add eax,edx
    5.   mov ecx,eax
    6.   and ecx,C0C0C0h
    7.   shr ecx,6
    8.   imul ecx,85
    9.   and ecx,ebx
    10.   sub eax,ecx
    11.  
    12.   ;вычитание
    13.   sub eax,edx
    14.   mov ecx,eax
    15.   and ecx,C0C0C0h
    16.   shr ecx,6
    17.   imul ecx,85
    18.   and ecx,0E8C4C4h
    19.   sub eax,ecx
     
  3. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    leo

    - так вот он какой настоящий дзен!



    all

    У вас пока есть уникальный шанс улучшить данный код как минимум на 2-3 байта. Главное - правильно выбрать регистры.
     
  4. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Раз народ регистры выбирать не хочет, придётся это сделать самому.



    r1=ecx,r2=edx

    Сложение, 25 байт:
    Код (Text):
    1.     mov ebx,$E8C4C4
    2.     add ecx,edx
    3.     add ecx,ebx
    4.     shld eax,ecx,26
    5.     and eax,$30303
    6.     imul eax,85
    7.     and eax,ebx
    8.     sub ecx,eax




    Вычитание, 21 байт:
    Код (Text):
    1.     sub ecx,edx
    2.     shld eax,ecx,26
    3.     and eax,$30303
    4.     imul eax,85
    5.     and eax,$E8C4C4
    6.     sub ecx,eax
     
  5. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Вот наконец собрался приписать примечание насчет использования imul :)



    Разумеется ничего оригинального и дзенного в этом нет, т.к. mul\imul - это стандартный прием размножения или перестановки бит. Все становится ясно и понятно, если представить imul r,i в двоичном виде как сумму из N исходных чисел r, где N - число единичных бит в множителе i, а сдвиги задаются позициями этих бит в i. Например, в рассматриваемом случае мы имеем в каждом байте либо 0, либо 11b, из которых нужно сделать 0FFh. Это ес-но делается путем суммирования 4-х значений со сдвигом 0,2,4,6 бит
    Код (Text):
    1.  
    2.   хххххх00000011  ;=r*00000001
    3. + хххх00000011..  ;=r*00000100
    4. + хх00000011....  ;=r*00010000
    5. + 00000011......  ;=r*01000000
    6. -----------------------------
    7. = хххххх11111111  ;=r*01010101b = imul r,85
    Этот прием также можно использовать для упаковки (группировки), распаковки и подсчета установленных бит в байтах или тетрадах. Единственое что требуется это аккуратно расписать сдвиги и обеспечить отсутствие наложений, которые могут исказить результат (предварительный AND). Например, подсчитать кол-во байт в которых установлен младший бит можно так:
    Код (Text):
    1.   ;and eax,1010101h
    2.   imul eax,1010101h
    3.   shr eax,24
    Чтобы биты не суммировались, а собрались в кучку (сгруппировались) нужно, чтобы сдвиги в каждом слагаемом imul были не кратны 8 и шли с нарастанием или убыванием. Например, чтобы сгруппировать биты в инверсном порядке нужно умножить на число с возрастающими сдвигами 8040201h (+ shr 24), а для прямого порядка - на число с убывающими сдвигами 204081h (+ shr 21). Ес-но при желании можно расставить биты и в произвольном порядке.

    Аналогичным образом делается и обратная операция (распаковка) - выбираем сдвиги так, чтобы каждый бит байта встал на свою позицию в определенном байте, затем после imul делаем AND для обнуления мусора