2017-11-10 197 views
0

首先,這是一項家庭作業。將值插入數組並顯示,nasm

我有一個循環獲得兩個數字的值單獨,並加入他們的第一個數字乘以10,並加上第二個數字來獲得一個整數。

我正在做這一切,並保存在我的AL寄存器中,現在我想將該整數插入到一個數組中,然後掃描該數組並顯示這些數字。

如何插入矢量並從矢量讀取?

我的數組:

section .bss 
    array resb 200 

我的數字轉換:

sub byte[digit_une], 30h 
sub byte[digit_two], 30h 

mov al, byte[digit_one]   
mov dl, 10     ;dl = 10 
mul dl      ;al = ax = 10 * digit_one 
add al, byte[digit_two]  ;al = al + digit_two = digit_one * 10 + digit_two 
+0

與'digito_um'一樣是'digit_one'嗎?你是否在同一個地方有兩個標籤,或者只有一半標籤翻譯成英文?如果它們不在同一地點,那麼'sub'指令與乘法無關。 –

+0

當你說「插入」時,你的意思是你想複製一個以後的所有元素(比如[C++'std :: vector :: insert'](http://en.cppreference.com/w/cpp /容器/載體/插入)),或者你想覆蓋一個元素,如正常的數組元素分配?或者你的意思是一個SIMD向量(像'xmm0',使用'pinsrb xmm0,eax,5'來替換'xmm0'的字節5'al')? –

+0

什麼是目標平臺... Ubuntu的? 32或64位二進制? – Ped7g

回答

2

「陣列」, 「載體」,等等......所有這些都是更高層次的概念。該機器具有可通過單字節尋址的存儲器,以及您使用代碼實現的邏輯類型,這取決於您。但是你應該能夠在兩個層面上思考它,就像內存中的單個字節一樣,每個字節都有自己的地址,並且完全理解你的代碼邏輯,它將如何安排那些字節的使用以形成「一些東西」。

隨着您對.bss扇區的定義,您可以定義一個符號/標籤array,它等於.bss段開始的內存地址。然後你保留200字節的空間,所以你將添加的任何其他內容(如另一個標籤)將從地址.bss+200開始。

假設(例如)將二進制文件加載到內存並跳轉到入口點後,.bss位於地址0x1000

然後

mov dword [array],0x12345678 

將存儲4個字節到內存地址0x1000 .. 0x1003,與具有值78 56 34 12(即DWORD值的小端擊穿)特定字節。

如果你會做mov dword [array+199],0x12345678,你會寫值0x78到最後正式保留字節由resb 200,和其餘3個字節將覆蓋在地址的.bss + 200內存和.bss + 201和.bss + 202(可能會損壞一些其他數據,如果您將放置某些內容,或者崩潰應用程序,如果它將跨越內存頁面邊界,並且您處於可用內存的末尾)。

由於要到N 字節值存儲到陣列中,最簡單的邏輯是存儲第一值的地址array+0,第二在array+1等...(爲DWORD值最合乎邏輯的方法是array+0, array+4, array+8, ....) 。

mov [array+0],al可以用來存儲第一個值。但是這不是很實用,如果你正在閱讀某種循環的輸入。比方說,你想閱讀的用戶,或價值99最多200個值會結束得越早,那麼你可以通過註冊使用索引,如:

xor esi,esi ; rsi = index = 0 
    mov ecx,200 ; rcx = 200 (max inputs) 
input_loop: 

    ; do input into AL = 0..99 integer (preserve RSI and RCX!) 
    ... 

    cmp al,99 
    je input_loop_terminate 
    mov [array+rsi], al ; store the new value into array 
    inc rsi  ; ++index 
    dec rcx  ; --counter 
    jnz input_loop ; loop until counter is zero 
input_loop_terminate: 
    ; here RSI contains number of inputted values 
    ; and memory from address array contains byte values (w/o the 99) 

即對於用戶輸入32,72,13,0,16,99,地址0x1000處的存儲器將具有5個字節的修改,其中包含(現在是六進制):20 48 0D 00 10 ?? ?? ?? ...

如果你有些熟練的asm程序員,你不但可以通過寄存器索引,也可以避免使用硬編碼的array標籤,所以你可能會做一個子程序作爲參數目標地址(數組),最大數量:

; function to read user input, rsi = array address, rcx = max count 
; does modify many other registers 
; returns amount of inputted values in rax 
take_some_byte_values_from_user: 
    jrcxz .error_zero_max_count ; validate count argument 
    lea rdi,[rsi+rcx] ; rdi = address of first byte beyond buffer 
    neg rcx   ; rcx = -count (!) 
     ;^small trick to make counter work also as index 
     ; the index values will be: -200, -199, -198, ... 
     ; and that's perfect for that "address of byte beyond buffer" 
.input_loop: 

    ; do input into AL = 0..99 integer (preserve RSI, RDI and RCX!) 
    ... 

    cmp al,99 
    je .input_loop_terminate 
    mov [rdi+rcx], al ; store the new value into array 
    inc rcx   ; ++counter (and index) 
    jnz .input_loop ; loop until counter is zero 
.input_loop_terminate: 
    ; calculate inputted size into RAX 
    lea rax,[rdi+rcx] ; address beyond last written value 
    sub rax,rsi  ; rax = count of inputted values 
    ret 

.error_zero_max_count: 
    xor eax,eax  ; rax = 0, zero values were read 
    ret 

然後就可以調用從主代碼子程序是這樣的:

... 
    mov rsi,array ; rsi = address of reserved memory for data 
    mov ecx,200  ; rcx = max values count 
    call take_some_byte_values_from_user 
    ; keep RAX (array.length = "0..200" value) somewhere 
    test al,al  ; as 200 was max, testing only 8 bits is OK 
    jz no_input_from_user ; zero values were entered 
    ... 

對於字/雙字/四字元件陣列的x86內存操作數比例係數,這樣你就可以使用索引值會通過+1和地址ESS值等:

mov [array+4*rsi],eax ; store dword value into "array[rsi]" 

對於其他尺寸元素它通常是更有效的具有指針,而不是索引,並且通過這樣做add <pointer_reg>, <size_of_element>add rdi,96移動到下一個元素,以避免索引值的乘法對每個訪問。

etc ...讀取值返回的方式是相同的,但是顛倒了操作數。

順便說一句,這些例子並沒有像「覆蓋」它那麼多的「插入」值到數組中。計算機的內存已經存在,並且有一些值(.bss被libc或OS IIRC歸零?否則就會出現一些垃圾),所以它只是用來自用戶的值覆蓋舊的垃圾值。在resb中,仍有200字節的內存「保留」,並且您的代碼必須記錄實際大小(輸入值的計數),以瞭解用戶輸入結束的位置以及垃圾數據的起始位置(或者您最終可以編寫值99並將其用作「終止符」值,那麼只需要數組的地址來掃描它的內容,並在找到值99時停止)。

編輯:

以防萬一你仍然不知道爲什麼我有時會用方括號,有時沒有,這Q +一個看起來很詳細,YASM語法相同NASM括號用法:Basic use of immediates (square brackets) in x86 Assembly and yasm

+0

編寫'mov dword [array],0x12345678'更好。 (將'dword'說明符應用於內存操作數,而不是直接)。它在NASM語法中實際上並不明確,但是該慣例使得讀'add qword [rdi],嚴格的dword 0x12'('strict dword'完全獨立於'dword',意味着你想要imm32編碼,而不是imm8,這對於小的立即數是可能的。64位操作數大小仍然使用32位立即數。)無論如何,如果你在YASM qword中寫入'add qword [rdi],dword 0x12',那麼它是一個'add r/m64,imm8'。在NASM中,錯誤。 –

+0

Hrm,NASM扼流圈上'添加qword [rdi],嚴格雙字0x12'。看來你可以寫'添加qword [rdi],嚴格qword 0x12'來獲得'48 81 07 12 00 00 00',這是有點假,因爲它仍然是imm32。 YASM在那裏拒絕「嚴格的qword」,只接受「嚴格的dword」。無論如何,將大小放在內存操作數上,而不是立即數,以便與'strict'保持一致,以強制直接編碼大小。 –

+0

@PeterCordes @PeterCordes有趣的是,從來沒有這樣想過,操作碼有時候實際上包含不同的數據大小,並且cpu不會/符號將它們擴展爲正確的方式,但當然,你是絕對正確的。 – Ped7g