как нельзя писать на fasm'e(оформление кода)

Тема в разделе "WASM.HEAP", создана пользователем common_up, 1 сен 2010.

  1. common_up

    common_up New Member

    Публикаций:
    0
    Регистрация:
    4 июл 2010
    Сообщения:
    85
    Причиной создания этого топика есть:
    1) мне в руки попал кусок кода написанный на си. И это был не просто кусок сорса. Это был эллегантный до мозга костей код. Этот код можно было читать даже тогда, когда знаешь си на уровне школьника. Я просто влюбился в стиль офомления:)
    2) постоянное желание менять что-то к лучшему.

    Незнаю как Вы отреагируете на этот пост, но возможно он кому-то поможет не наступать на грабли, связанные с написанием программ на flat assembler. Можно было бы не полениться и написать небольшую статью на эту тему, но наблюдается глобальная нехватка времени. Итак начнем:

    Я сравнительно недавно программирую на фасме и ввиду того, что код написанный на ассемблере достаточно емкий(обьем скомпилированного бинарника маленький, а сорс - ёмкий) всегда пытался вывести некоторую формулу написания кода, который бы отличался нормальной читабельностью, но в то же время время, потраченное на оформление стремилось к минимуму.
    пробовал и так и эдак лепить комментарии, смотрел как пишут другие. Скажу честно: большая часть сорсов, которые написаны на фасме по своему оформлению просто ужас в чистом виде. Возьмем к примеру сорс, морфан 1,0 (вроде 1,0):

    Код (Text):
    1. ;----------------------
    2. ;                           SUB REG32,1
    3. ;                           INC REG32,1
    4. INS3:  ; 4 BYTE
    5. mov byte [esi],83h
    6. inc esi
    7.  
    8. mov eax,8
    9. call brandom32
    10. push eax
    11. add eax,0E8h
    12.  
    13. mov byte [esi],al
    14. inc esi
    15. mov byte [esi],01h
    16. inc esi
    17. pop eax
    18. add eax,40h
    19.  
    20. mov byte [esi],al
    21. inc esi
    22. add edi,4
    23. jmp nextp
    24.  
    25. ;---------------------------------
    26. ;    PUSHAD
    27. ;    3nuyf
    28. ;    POPAD
    29. INS4:  ; 2 BYTE, To ALL
    30. mov byte [esi],60h
    31. inc esi
    32.  
    33. ; THIS GARBAGE REG32
    34.  
    35. mov byte [esi],61h
    36. inc esi
    37. add edi,2
    38. jmp nextp
    39.  
    40. ;----------------------------------
    недостатки:

    1)так нельзя. Во-первых нет отступа, во вторых всякие ;--------- или ;xxxxxxxxx или ;=============== только вначале кажутся милыми и очаровательными, но когда начинаешь смотреть на код, которого к примеру на 500 строк, то восприятие его несколько ухудшается.

    Единственное что, там в принципе нормально разбито на процедуры, хотя они там вперемешку с кодом(т.к нет отступа) и поэтому без спецIDE которые умеют сворачивать процедуры читать данный код(к примеру в fasmw) - это просто пипец.

    2) Все разбито на небольшие блоки. Гуд, но только тогда, когда пишешь сам проект. Любой, кто будет читать такой код никогда не разберется в нем. Нужно ставить много мелких бряков и собирать с бряками. Далее - в отладчик и смотреть что там происходит. Иначе - никак. Разбор такого кодеса не очень сложен, но достаточно трудоёмен. При этом можно бродить по коду эдак дня три. Да и сам автор, если он откроет свой сорец через три-четыре месяца будет плеваться на самого себя, что не прокоментил участки кода и так вот писал(привет-привет автору кода;)).

    недостатков в коде крайне много, кто читал много сорсов - тот в принципе поймет.

    Дальше от себя:
    1)Никогда не используйте много локальных и глобальных меток, как средство отделения блоков кода. Используйте метки только тогда, когда вы будет в них прыгать в каком-то цикле.
    Вот что получается из всего этого:
    [​IMG]
    Причиной этого кстати является топик созданный мной немного ранее: https://www.wasm.ru/forum/viewtopic.php?id=38490
    такой стиль - головная боль и причина многих суидидов(наверное).
    2) Программируйте процедурами. Все сабфункции не ленитесь и выносите в отдельные инклюды - так будет понятней, что делает основной код
    3) используйте локальные переменные, пытайтесь избегать глобальных переменных, по крайней мере там, где это возможно.
    4) не комментируйте блоки кода перед самим кодом типа:

    Код (Text):
    1. ;dfgvsdfgsdvsdvsd
    2. ;sdfvsvsdvsdvsdvsdvsdvsdv
    3. ;vsvsdvsdvsdvsdvsdvsdvvvvvvvvv
    4. xor eax,eax
    5. xor eax,eax
    6. xor eax,eax
    это нереально читать вообще. Код сбивается в кучу и вообще ничего не понятно в нем.

    5)в процедурах пересохраняйте параметры самой процедуры в локальные переменные
    6) никогда не пишите смещениями типа:
    Код (Text):
    1. mov eax,[eax+0x3c]
    лучше написать инклюд и сделать так:
    Код (Text):
    1. mov eax,[eax+IMAGE_DOS_HEADER.e_lfanew]
    согласитесь, по-приятней будет
    7)хватит короче
    Вот к какой концепции оформления я пришел:
    1)весь код - тройной таб
    2)разделение блоков кода следующим образом:
    Код (Text):
    1. ;;
    2. ;;краткое описание блока
    3. ;;
    4. отступ кода три таба
    5. содержимое блока
    3)комментарии -> двойная точка с запятой
    4)ненавижу циклы код типа такого:
    Код (Text):
    1. .if
    2.  .if
    3.    .if
    4.     .if
    5.      .if
    6.       .if
    7. ..............
    8. ................
    9.  
    10.                     .endif
    11. ..............
    12. ............
    13. ..............
    14. ...........
    15.                    .endif
    16. ............
    17. ..............
    18. ...........
    19.  
    20.               .endif
    21.                  .endif
    22.                 .endif
    23.                .endif
    24.               .endif
    25.              .endif
    делайте типа(будет скриншот) такого:

    в общем дальше описывать не буду - просто приложу скрин
    [​IMG]

    В общем-то не ставлю из себя крутого перца\гуру и тд. Просто может то, что я написал - поможет тем, кто знает чуть меньше, ну и вызовет улыбку у тех - кто знает много больше, чем я:)
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
  3. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    оформлять код следует не хуже чем это делает IDA
    если вы можете нормально читать листинг IDA, вы прочитаете и исходник.
     
  4. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.561
    Адрес:
    Russia
    common_up
    Мда... А скрины очень информативные. Пересоздайте скрины, иначе обсуждать практически нечего.
    Не согласен. Если это функция, или начало определенного алгоритма, имеет смысл как раз описать его подробно, а не расписывать по каждой строчке. Это более грамотно. Хорошие специалисты так делают. Это нормальная практика. И это читаемо.
    А дальше вы пишете:
    Противоречите собственному 4ому пункту. Да и вообще - подробнее. Кратное оно хорошо, но чем подробнее тем лучше.

    Хех, вы это ненавидите. А что вы любите? cmp\je\jne... etc :)

    А вообще гляньте код клерка. Вполне читабельно.

    Вы к тому же ничего не сказали про именование переменных\процедур, конвенций вызовов.

    Пока хватит.
     
  5. Guru_of_Zen

    Guru_of_Zen Member

    Публикаций:
    0
    Регистрация:
    21 янв 2010
    Сообщения:
    288
    common_up
    ну чай дядь Томаш задает ритм и стиль...
    видели сорцы фасма-то ?
    код идет сплошной стеной, ток в переходах вроде есть отступы.
    ни комментариев, ни дробления так сказать логического нету -- ничего.
    оно внутри-то конечно может и ни разу не быдлокод, но оформление уродское.

    Clerk
    имхо махонький недостаток -- неэкономное использование вертикального пространства, комментирование вида:

    Код (Text):
    1. PREFIX_REPEAT_COUNT     EQU     11      ; Prefix table length
    2.  
    3. ;
    4. ; defines all the possible IO privileged IO instructions
    5. ;
    6.  
    7. IOInstructionTable      label byte
    8. ;       db      0fah                    ; cli
    9. ;       db      0fdh                    ; sti
    10.         db      0e4h, 0e5h, 0ech, 0edh  ; IN
    11.         db      6ch, 6dh                ; INS
    12.         db      0e6h, 0e7h, 0eeh, 0efh  ; OUT
    13.         db      6eh, 6fh                ; OUTS
    14.  
    15. IO_INSTRUCTION_TABLE_LENGTH     EQU     12
    16.  
    17. ;
    18. ; definition for  floating status word error mask
    19. ;
    20.  
    21. FSW_INVALID_OPERATION   EQU     1
    22. FSW_DENORMAL            EQU     2
    можно бы и не ставить ; сверху и снизу комментария. пустых двух строк хватило бы.
    а то выходит 1 строка смысловая -- а занимает целых 5 строк вертикального пространства.
    с другой стороны -- в глаза бросается лучше, читабельнее выходит.

    P.S. тут могу провести параллель с С : 1TBS допустим вертикальное прос-во экономит, но менее заметен, а ГНУтые и гей-стили наоброт.
     
  6. Paguo_86PK

    Paguo_86PK Руслан

    Публикаций:
    0
    Регистрация:
    8 окт 2007
    Сообщения:
    911
    Адрес:
    Ташкент
    Пользуюсь в основном этой утилитой. В результате, пишу на ассемблере, но почти C-выражениями. Написал утилиту на этом языке в 1000 строк, на выходе получил около 10000 asm-строк.
    Удобно. Но на сайте демо-версия с уймой ограничений с примером для fasm.
    Консольная версия - полностью функциональная была... Но под DOS :dntknw:
     
  7. dyn

    dyn New Member

    Публикаций:
    0
    Регистрация:
    30 окт 2009
    Сообщения:
    566
    ИМХО, код клерка читабелен, но затраты времени на описание каждого шага превышают затраты на программирование.
     
  8. Guru_of_Zen

    Guru_of_Zen Member

    Публикаций:
    0
    Регистрация:
    21 янв 2010
    Сообщения:
    288
    ... , но зато снижают затраты времени (и сил) при дальнейшем сопровождении...
     
  9. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Guru_of_Zen
    Разделение коментов и кода делает восприятие их более удобным. Не все пользуются такими тулзами как ТС, в том числе и наверно архитекторы собиравшие этот код. Да и я пользуюсь блокнотом.
    dyn
    Там довольно сложный код, который читает вся корпорация. Поэтому нормальные коменты необходимы. Как пример возьмите какойнибудь мой код, допустим этот http://files.virustech.org/indy/Code/AVrf2/Ldr.asm - коменты минимальны, так как особо комментировать нечего, оформление приличное(имена понятные и пр.).
     
  10. dyn

    dyn New Member

    Публикаций:
    0
    Регистрация:
    30 окт 2009
    Сообщения:
    566
    Clerk
    Да, довольно красиво :)
     
  11. iZzz32

    iZzz32 Sergey Sfeli

    Публикаций:
    0
    Регистрация:
    3 сен 2006
    Сообщения:
    355
    dyn, но не слишком читабельно:
    Код (Text):
    1.         Call SEH_Prolog
    2.         .if ImageBase == NULL
    3.         mov eax,fs:[TEB.Peb]
    4.         mov ecx,PEB.LoaderLock[eax]
    5.         mov eax,PEB.Ldr[eax]
    6.         mov eax,PEB_LDR_DATA.InLoadOrderModuleList.Flink[eax]
    7.         mov eax,LDR_DATA_TABLE_ENTRY.InLoadOrderModuleList.Flink[eax]
    8.         mov eax,LDR_DATA_TABLE_ENTRY.DllBase[eax]       ; ntdll.dll
    9.         mov ImageBase,eax
    10.         .endif
    11.         invoke LdrImageNtHeader, ImageBase, addr ExitFlag
    Если тут .if выдвинуть или код вдвинуть, будет проще находить .endif-ы и .else. Что по этому поводу думает Clerk? Почему так?
    А мне кажется, что неиспользуемые локальные метки, сдвинутые вправо на четыре пробела (код на восемь, глобальные не сдвинуты) читаются быстрее комментариев, не засоряют при этом листинг, да ещё и нахаляву разделяют смысловые куски кода. Если не переусердствовать, конечно.
     
  12. JCronuz

    JCronuz New Member

    Публикаций:
    0
    Регистрация:
    26 сен 2007
    Сообщения:
    1.240
    Адрес:
    Russia
    Майкрософт свои исходники очень красиво оформляет, чего нельзя сказать о линукс исходниках, где отсутствует венгерская нотация, ужаснее исходников мир незнает
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    iZzz32
    Я использую условные конструкции по минимому. Незачем вводить табуляцию для не вложенных условных конструкций. Можно было написать локальную метку, но просто печатать мне удобнее так. Код набирается очень быстро.
     
  14. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    JCronuz
    Но только микрософт отказался от венгерки.
     
  15. S_Alex

    S_Alex Alex

    Публикаций:
    0
    Регистрация:
    27 авг 2004
    Сообщения:
    561
    Адрес:
    Ukraine
    В 2006 году писал прогу...
    Выглядит так.
    Код (Text):
    1. ;--------------------------------------------------------------------------------
    2. MatrixNCopy proc  NMatrixS:DWORD,NMatrixD:DWORD
    3. LOCAL MatrixName[2]:DWORD
    4.   invoke  MatrixNFind,NMatrixS
    5.   .if eax
    6.     mov   esi,    eax
    7.       ;<Проверим матрицу на правильность
    8.       mov   eax,  [esi.N_MATRIX.lpMatrix]
    9.       invoke  MatrixCheck,eax
    10.       .if !eax
    11.         invoke  PrintErrors,05h,$TA0('MatrixNCopy')
    12.         ret
    13.       .endif  
    14.       ;>Проверим матрицу на правильность
    15.     invoke  MatrixNFind,NULL
    16.     .if eax
    17.       mov   edi,    eax
    18.  
    19.       ;<Выделим память под копию матрицы
    20.       mov   eax,  [esi+N_MATRIX.lpMatrix]
    21.      
    22.       ;<Поправка на размер заголовка матрицы
    23.       mov   eax,  [eax+MATRIX.mSize]
    24.       add   eax,  sizeof  MATRIX
    25.       ;>Поправка на размер заголовка матрицы
    26.      
    27.       invoke  GlobalAlloc,LMEM_FIXED or LMEM_ZEROINIT,eax
    28.       ;>Выделим память под копию матрицы
    29.      
    30.       mov   [edi+N_MATRIX.lpMatrix],  eax   ;Запишем указатель на новую матрицу
    31.       mov   eax,      NMatrixD
    32.       mov   [edi+N_MATRIX.Name_M],  eax   ;Запишем имя новой матрицы
    33.       mov   eax,  edi
    34.      
    35.       ;<Поправка на размер заголовка матрицы
    36.       mov   eax,  [esi+N_MATRIX.lpMatrix]
    37.       mov   eax,  [eax+MATRIX.mSize]
    38.       add   eax,  sizeof  MATRIX
    39.       ;>Поправка на размер заголовка матрицы
    40.      
    41.       invoke  CopyMemory,[edi.N_MATRIX.lpMatrix],[esi.N_MATRIX.lpMatrix],eax
    42.     .else
    43.       invoke  PrintErrors,00Eh,$TA0('MatrixNCopy')  
    44.     .endif
    45.   .else
    46.     mov   eax,    NMatrixS
    47.     mov   MatrixName, eax
    48.     xor   eax,    eax
    49.     mov   MatrixName[4],  eax
    50.     invoke  PrintErrors,011h,ADDR MatrixName
    51.   .endif
    52.   ret
    53. MatrixNCopy endp
    Стараюсь давать имена меткам и процедурам так, что бы потом было меньше вопросов.
    Да и если код ложить ровно и использовать .equ для задания констант с нормальными и понятными именами. То и коментов потом меньше нужно писать. И так понятно, что и куда.

    Сравните две строчки. (под AVR)
    Код (Text):
    1. ldi r16,  SetDDRAM|Line2|Pos13
    2. ...
    3. ldi r16,  $CD
    4. ...
    Ко второй строке ну явно будет куча вопросов.
    А есть ли вопросы к первой? Кто и что не понял?
     
  16. Medstrax

    Medstrax Забанен

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    673
    Соглашусь. Но к чему следовать мелкомягкому стилю типа юзанья RPL_MASK!?
    Когда то мыщ недоумевал, поддерживаю. Зачем нагромождать, если можно заюзать литерал?
    В обозримом будущем RPL_MASK явно не изменится ))
     
  17. JCronuz

    JCronuz New Member

    Публикаций:
    0
    Регистрация:
    26 сен 2007
    Сообщения:
    1.240
    Адрес:
    Russia
    Booster Жаль, очень жаль ;)
     
  18. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    medstrax1
    Например or dword ptr [ebx],3 - что в Ebх ссылка на значение сегментного регистра не понятно. Если определить как or dword ptr [ebx],RPL_MASK - сразу понятно, ну если известен смысл константы.