Как правильно очистить массив байтов?

Applications development, KoOS API questions
  • floppy121 wrote:Что я делаю не так?
    Всё не так :)
    Если вопрос - почему не работает, то причина в команде

    Code: Select all

        mov byte[user_command + counter], 0 ; очищение байта в user_command
    
    Здесь user_command и counter это адреса переменных. Ты их складываешь (это делается на этапе ассемблирования), получая некий адрес, и записываешь туда байт 00. Всегда. По одному и тому-же адресу.

    Что нужно сделать, чтобы заработал конкретно этот код. Ну, во-первых нужно использовать регистр eax, т.к. к адресу нужно прибавлять 32-битное значение. А во-вторых, вышеупомянутую команду нужно записать немного иначе. К тому-же никакой переменной counter не нужно, её роль будет играть регистр eax.

    Code: Select all

    clear_user_command:
    
        mov eax, 0
    
        .cycle_clear:
    
        mov byte[user_command][eax], 0 ; очищение байта в user_command
        add eax, 1
        cmp eax, 255
        jne .cycle_clear
    
        ; печать получившейся строки
        mov esi, user_command
        call print_asciiz
    
        ret
    Запись в память можно делать:
    - по фиксированному адресу mov byte[user_command], 0
    - по адресу в регистре mov byte[eax], 0
    - или по сумме фиксированного значения и регистра mov byte[user_command][eax], 0

    Команду mov eax, 0 обычно заменяют на более короткую (в коде) xor eax,eax
    Команду add eax, 1 лучше заменить на более короткую inc eax

    Но вообще, для заполнения области памяти имеется команда rep stosb (или rep stosd, которая быстрее, и записывает по 4 байта за цикл).
  • tsdima wrote:- или по сумме фиксированного значения и регистра mov byte[user_command][eax], 0
    К сожалению именно этот, самый нужный пример, не собирается:

    Code: Select all

    flat assembler  version 1.71.54
    userparser.inc [501]:
        mov byte[user_command][eax], 0
    error: invalid operand
    При этом остальные два примера собираются нормально. Не могу понять в чём дело...
  • floppy121 wrote:К сожалению именно этот, самый нужный пример, не собирается
    Забыл, что fasm не любит такую запись. Попробуй mov byte[user_command+eax], 0
  • tsdima wrote:
    floppy121 wrote:К сожалению именно этот, самый нужный пример, не собирается
    Забыл, что fasm не любит такую запись. Попробуй mov byte[user_command+eax], 0
    Благодарю, сработало! :P вот что в итоге получилось:
    ircc.asm

    Code: Select all

    MAX_COMMAND_LEN         = 512
    MAX_COMMAND_LEN_BYTES   = MAX_COMMAND_LEN*4
    userparser.inc

    Code: Select all

    clear_user_command:
    
        xor eax, eax
    
      .cycle_clear:
    
        mov byte[user_command + eax], 0
        inc eax
        cmp eax, MAX_COMMAND_LEN_BYTES
        jne .cycle_clear
    
        ret
    Last edited by floppy121 on Thu Jun 07, 2018 7:14 pm, edited 1 time in total.
  • Можно ещё добавить варианты, используемые в других программах.

    Для заполнения области памяти байтами с определённым значением(в том числе нулевым) обычно используется memset.
    Вот эта функция в Tiny C Compiler http://websvn.kolibrios.org/filedetails ... memset.asm

    Code: Select all

    format ELF
    section '.text' executable
    public memset
    memset:
      push  edi
      mov   edi,[esp+8]
      mov   eax,[esp+12]
      mov   ecx,[esp+16]
      jecxz  .no_set
      cld
      rep   stosb
    .no_set:
      mov eax, [esp+8]
      pop   edi
      ret
    А вот она же в программе graph http://websvn.kolibrios.org/filedetails ... memset.asm

    Code: Select all

    format MS COFF
    section '.text' code readable executable
    public _memset
    _memset:
            push    edi
            mov     edi, [esp+8]
            mov     al, [esp+12]
            mov     ecx, [esp+16]
            rep     stosb
            pop     edi
            ret
    Я же в своей libc использую такой вариант

    Code: Select all

    GLOBAL _memset
    
    SECTION .text
    
    ;**********************************************************************************
    _memset: ;//////////////////////////////////////////////////////////////////////////
    ;**********************************************************************************
    %define ptr    [esp +  4 +1*4] ; Pointer to block of memory to fill
    %define value  [esp +  8 +1*4] ; Value to be set
    %define num    [esp + 12 +1*4] ; Number of bytes to set to value
    DEBUG.PRINT.LINE "memset start"
            push   edi
            mov    eax, value
            mov    edx, num   
            mov    edi, ptr   
            mov    ecx, eax
            mov    ah, al
            mov    ch, cl
            shl    ecx, 16
            or     eax, ecx
            mov    ecx, edx
            shr    ecx, 2
            and    edx, 3
            rep stosd
            mov    ecx, edx
            rep stosb        
            mov    eax, ptr        
            pop    edi
    DEBUG.PRINT.LINE "memset finish"
            ret
    %undef ptr
    %undef value
    %undef num
            
    Вот функция FillChar из RTL Delphi7

    Code: Select all

    {     ->EAX     Pointer to destination  }
    {       EDX     count   }
    {       CL      value   }
            PUSH    EDI
            MOV     EDI,EAX { Point EDI to destination              }
            MOV     CH,CL   { Fill EAX with value repeated 4 times  }
            MOV     EAX,ECX
            SHL     EAX,16
            MOV     AX,CX
            MOV     ECX,EDX
            SAR     ECX,2
            JS      @@exit
            REP     STOSD   { Fill count DIV 4 dwords       }
            MOV     ECX,EDX
            AND     ECX,3
            REP     STOSB   { Fill count MOD 4 bytes        }
    @@exit:
            POP     EDI
    Вот memset от микрософта(CRT Microsoft Visual C++ 6.0)

    Code: Select all

    public  memset
    memset proc
            mov     edx,[esp + 0ch] ; edx = "count"
            mov     ecx,[esp + 4]   ; ecx points to "dst"
            test    edx,edx         ; 0?
            jz      short toend     ; if so, nothing to do
            xor     eax,eax
            mov     al,[esp + 8]    ; the byte "value" to be stored
    ; Align address on dword boundary
            push    edi             ; preserve edi
            mov     edi,ecx         ; edi = dest pointer
            cmp     edx,4           ; if it's less then 4 bytes
            jb      tail            ; tail needs edi and edx to be initialized
            neg     ecx
            and     ecx,3           ; ecx = # bytes before dword boundary
            jz      short dwords    ; jump if address already aligned
            sub     edx,ecx         ; edx = adjusted count (for later)
    adjust_loop:
            mov     [edi],al
            inc     edi
            dec     ecx
            jnz     adjust_loop
    dwords:
    ; set all 4 bytes of eax to [value]
            mov     ecx,eax         ; ecx=0/0/0/value
            shl     eax,8           ; eax=0/0/value/0
            add     eax,ecx         ; eax=0/0val/val
            mov     ecx,eax         ; ecx=0/0/val/val
            shl     eax,10h         ; eax=val/val/0/0
            add     eax,ecx         ; eax = all 4 bytes = [value]
    ; Set dword-sized blocks
            mov     ecx,edx         ; move original count to ecx
            and     edx,3           ; prepare in edx byte count (for tail loop)
            shr     ecx,2           ; adjust ecx to be dword count
            jz      tail            ; jump if it was less then 4 bytes
            rep     stosd
    main_loop_tail:
            test    edx,edx         ; if there is no tail bytes,
            jz      finish          ; we finish, and it's time to leave
    ; Set remaining bytes
    tail:
            mov     [edi],al        ; set remaining bytes
            inc     edi
            dec     edx             ; if there is some more bytes
            jnz     tail            ; continue to fill them
    ; Done
    finish:
            mov     eax,[esp + 8]   ; return dest pointer
            pop     edi             ; restore edi
            ret
    toend:
            mov     eax,[esp + 4]   ; return dest pointer
            ret
    memset  endp
    Если кто-то интересовался проектом Ziron http://codeziron.com/index.php то там в memutils используется memFill — слегка изменённый вариант функции для моей libc(см. выше)

    Code: Select all

    //******************************************************//
    //                                                      //
    //     Ziron Run Time Library                           //
    //     Ziron Memory Utils (memutils.zir)                //
    //                                                      //
    //     Copyright (c) 2013-####, OverHertz OOD           //
    //                                                      //
    //******************************************************//
    
    #ifdef BITS == 32:
    
    #set frame off;
    
    /*
    /    Fill block of memory with specified value
    /    $buf -- Pointer to block of memory to fill
    /    $len -- Number of bytes to set to value
    /    $ch_ -- Value to be set
    */
    inline function m_memFill($buf, $len, $_ch) {
    	// Copyright (c) 0CodErr, KolibriOS team
    	using(edi) {
    		eax = 0;
    		al  = $_ch;
    		edx = $len;
    		edi = $buf;
    		push edi
    		ecx = eax;
    		ah  = al;
    		ch  = cl;
    		ecx << 16;
    		eax |= ecx;
    		ecx = edx;
    		ecx >> 2;
    		edx &= 3;
    		rep stosd;
    		ecx = edx;
    		rep stosb;
    		pop eax
    	}
    	
    	$return eax;
    }
    
    function memFill(char* buf; dword len; char ch_) {
    	eax = m_memFill(buf, len, ch_);
    }
  • В простейшем случае, если надо очистить первые 256 байтов( или другое кратное 4 число):
    xor eax,eax ; так обнулил eax. Нужно всё зачистить не нулями, а другим число(напр. 0х13) - пишем mov eax,0x13131313
    mov edi,my_mem ;та память, которую очистим
    mov ecx, 256/4 ; - тут количество байтов, делённо на 4 - для скорости очищаем сразу по 4 байта
    rep stosd
    Если уж очень надо очистить именно 255, то или меняем stosd на stosb( ну и в ecx уже пишем 255, без всякого деления), или усложняем код: зачищаем первые 252 байта с помощью stosd, потом дочищаем stosb. На практике так делать стоит, если у нас десяток килобайт чистится.
    Чем больше сыра, тем больше в нём дыр. Чем больше дыр, тем меньше в нём собственно сыра. Значит, чем больше сыра, тем меньше сыра!
  • Who is online

    Users browsing this forum: No registered users and 3 guests