2017-11-10 231 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