Assembler i tablice wielowymiarowe

0

Krótko i do rzeczy: Zastanawiam się czy dobrze rozumiem wielowymiarowe tablice w ASM? [NASM 32bit konkretnie]

Zrobiłem dla testu coś takiego:

 
global _main
extern _printf
extern _scanf

section .data
tab    dd    1, 11, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33
fmt    db    "%d ", 0
nl     db    0ah, 0dh

section .bss
i    resd    1
j    resd    1

offset_1    resd    1
offset_2    resd    1

section .text
_main:
    mov ebp, esp; for correct debugging
    
    mov dword[i], 0
    mov dword[j], 0
    mov dword[offset_1], 0
    mov dword[offset_2], 4
    
pierwsza:
    mov edi, dword[offset_1]
    push dword[tab+edi+esi]
    push fmt
    call _printf
    add esp, 8
    
    mov dword[j], 0
    druga:
        mov esi, dword[offset_2]
        push dword[tab+edi+esi]
        push fmt
        call _printf
        add esp, 8
        
        mov dword[offset_2], esi
        add dword[offset_2], 4
        
        inc dword[j]
        cmp dword[j], 3
        jne druga
        
    push nl
    call _printf
    add esp, 4        
    
    mov dword[offset_1], edi
    add dword[offset_1], 4
    
    inc dword[i]
    cmp dword[i], 3
    jne pierwsza

koniec:
    xor eax, eax
    ret

Tab to tak naprawdę ciągły obszar pamięci ale cyfry 1, 2 oraz 3 oznaczają wiersze w tablicy 2D. Dwucyfrowe wartości to wartości kolumn tablicy 2D.

I teraz tak: skoro to jest ciągły obszar pamięci to tak naprawdę tablice wielowymiarowe można sobie implementować wedle uznania operując jedynie offsetami przesuwającymi kolumny i wiersze. :) Właściwie jak wskaźniki tablicowe w C (stąd to się przecież wzięło).

Pytanie początkującego asmowca: czy dobrze to rozumiem? :)

2

Dobrze to rozumiesz. To ciągłe obszary pamięci. Swoją drogą oczy mnie bolą od twojego kodu, jeśli piszesz w assemblerze to pisz w assemblerze i korzystaj z rejestrów zamiast z globalnych zmiennych. Przykładowo rejestr EBX jest wolny i nie jest modyfikowany pomiędzy wywołaniami funkcji, masz w kodzie często powtarzającą się wartość 0 w różnych operacjach, wyzeruj EBX na początku XOR EBX, EBX i używaj jego wartości w operacjach z zerem, np. AND DWORD[ZMIENNA],EBX zamiast MOV DWORD[ZMIENNA],0. Możesz też wywalić jakąś zmienną globalną (która potem nie jest nigdzie wykorzystywana) i zastąpić ją np. rejestrem EBX. Naucz się korzystać poprawnie z rejestrów, a twoje życie z assemblerem stanie się proste i przyjemne :)

0

Wziąłem sobie do serca Twoje rady i rzeczywiście teraz kod jest bardziej czytelny. Jedyne zmienne to tak naprawdę sama tablica i wskaźniki na format danych oraz nową linię. Jeszcze wrzuciłem wskaźnik do funkcji do rejestru, bo gwarantuje to szybsze wywołanie.

global _main
extern _printf

section .data
fmt    db    "%d ", 0
nl     db    0ah, 0dh

section .bss
tablica    resd    6; Tablica dwuwymiarowa

section .text
_main:
    xor edi, edi; Licznik pierwszego wymiaru
    xor ebx, ebx; Licznik drugiego wymiaru
    mov esi, _printf; Przekazanie wskaźnika do funkcji do rejestru
    
pierwsza:
    druga:
        mov eax, ebx; Przechowanie w eax licznika petli
        add ebx, edi; Sumowanie obu licznikow
        
        mov ecx, ebx; Przerzucenie wyniku do ecx
        mov ebx, eax; Odtworzenie stanu licznika petli
    
        mov dword[tablica+edi+ebx], ecx; Wrzucenie wyniku sumy jako elementu tablicy, żeby coś tam było jako dane testowe.
        
        push dword[tablica+edi+ebx]
        push fmt
        call esi
        add esp, 8
        
        inc ebx
        cmp ebx, 3
        jne druga    

    push nl
    call esi
    add esp, 4

    xor ebx, ebx
    inc edi
    cmp edi, 2
    jne pierwsza

koniec:
    xor eax, eax
    ret

1 użytkowników online, w tym zalogowanych: 0, gości: 1