将值插入数组并显示,nasm [英] Insert values into array and display, nasm

查看:86
本文介绍了将值插入数组并显示,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

解决方案

数组",向量"等...所有这些都是更高层次的概念.机器具有可通过单字节寻址的存储器,以及由代码实现哪种逻辑取决于您.但是您应该能够在两个级别上都考虑到它,因为内存中的单个字节都有各自的地址,并且完全了解您的代码逻辑,以及它将如何安排这些字节的使用以形成某物的数组".

使用.bss扇区的定义,您定义了一个符号/标签array,它等于.bss段开始的内存地址.然后,您保留200个字节的空间,因此要添加的其他内容(如另一个标签)将从地址.bss+200开始.

例如,假设将二进制文件加载到内存中并跳转到入口点后,.bss的地址为0x1000.

然后

mov dword [array],0x12345678

将在地址0x1000 .. 0x1003的内存中存储4个字节,特定字节的值为78 56 34 12(该dword值的小尾数分解).

如果要执行mov dword [array+199],0x12345678,则将值0x78写入该resb 200的最后一个正式保留字节,其余3个字节将覆盖地址.bss + 200,.bss + 201和.bss + 202(如果您要在其中放置一些东西,可能会破坏某些其他数据,或者,如果它超出了内存页边界,并且可能使应用程序崩溃,并且您处于进程可用内存的末尾).

由于要将N个 byte 值存储到数组中,最简单的逻辑是将第一个值存储在地址array+0中,将第二个值存储在array+1中,依此类推(对于 dword 认为最合乎逻辑的方式是array+0, array+4, array+8, ....).

mov [array+0],al可用于存储第一个值.但这不是很实际,如果您正在某种循环中读取输入.假设您要从用户读取最多200个值,否则值99会更快结束,那么您可以按寄存器使用索引,例如:

    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
    ...

对于word/dword/qword元素数组,x86在内存操作数中具有缩放因子,因此您可以使用加1的索引值和类似以下的地址值:

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

对于其他大小的元素,使用指针代替索引通常更有效,并且像add rdi,96一样通过执行add <pointer_reg>, <size_of_element>移至下一个元素,以避免每次访问的索引值相乘.

等...读回值的工作方式相同,但操作数取反.

顺便说一句,这些示例并没有将值"覆盖"到数组中,而是将其插入"到数组中.计算机内存已经存在并且具有一些值(.bss被libc或OS IIRC归零?否则可能存在一些垃圾),因此它只是用用户的值覆盖旧的垃圾值. resb仍然有200字节的保留"内存,并且您的代码必须跟踪实际大小(输入值的数量)才能知道,用户输入的结束位置以及垃圾数据的起始位置(否则您最终可能会写将99值也放入数组,并将其用作终止符"值,则只需要数组的地址即可扫描其内容,并在找到值99时停止.)

以防万一,您仍然想知道为什么我有时使用方括号,有时不使用方括号,此Q + A看起来足够详细,并且YASM语法与NASM在方括号用法中相同: 解决方案

"arrays", "vectors", etc... all that is higher level concept. The machine has memory, which is addressable by single byte, and what kind of logic you implement with your code, that's up to you. But you should be able to think about it on both levels, as single bytes in memory, each having it's own address, and fully understand your code logic, how it will arrange usage of those bytes to form "array of something".

With your definition of .bss sector you define one symbol/label array, which is equal to the address into memory where the .bss segment starts. Then you reserve 200 bytes of space, so anything else you will add after (like another label) will start at address .bss+200.

Let's say (for example) after loading your binary into memory and jumping to entry point, the .bss is at address 0x1000.

Then

mov dword [array],0x12345678

will store 4 bytes into memory at addresses 0x1000 .. 0x1003, with particular bytes having values 78 56 34 12 (little-endian break down of that dword value).

If you will do mov dword [array+199],0x12345678, you will write value 0x78 into the last officially reserved byte by that resb 200, and remaining 3 bytes will overwrite the memory at addresses .bss+200, .bss+201 and .bss+202 (probably damaging some other data, if you will put something there, or crashing your application, if it will cross the memory page boundary, and you are at the end of available memory for your process).

As you want to store N byte values into array, the simplest logic is to store first value at address array+0, second at array+1, etc... (for dword values the most logical way is array+0, array+4, array+8, ....).

i.e. mov [array+0],al can be used to store first value. But that's not very practical, if you are reading the input in some kind of loop. Let's say you want to read at most 200 values from user, or value 99 will end sooner, then you can use indexing by register, like:

    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)

I.e. for user input 32, 72, 13, 0, 16, 99 the memory at address 0x1000 will have 5 bytes modified, containing (in hexa) now: 20 48 0D 00 10 ?? ?? ?? ....

If you are somewhat skilled asm programmer, you will not only index by register, but also avoid the hardcoded array label, so you would probably do an subroutine which takes as argument target address (of array), and maximum count:

; 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

Then you can call that subroutine from main code like this:

    ...
    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
    ...

For word/dword/qword element arrays the x86 has scaling factor in memory operand, so you can use index value going by +1, and address value like:

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

For other sized elements it's usually more efficient to have pointer instead of index, and move to next element by doing add <pointer_reg>, <size_of_element> like add rdi,96, to avoid multiplication of index value for each access.

etc... reading values back is working in the same way, but reversed operands.

btw, these example don't as much "insert" values into array, as "overwrite" it. The computer memory already exists there and has some values (.bss gets zeroed by libc or OS IIRC? Otherwise some garbage may be there), so it's just overwriting old junk values with the values from user. There's still 200 bytes of memory "reserved" by resb, and your code must keep track of real size (count of inputted values) to know, where the user input ends, and where garbage data starts (or you may eventually write the 99 value into array too, and use that as "terminator" value, then you need only address of array to scan it content, and stop when value 99 is found).

EDIT:

And just in case you are still wondering why I am sometimes using square brackets and sometimes not, this Q+A looks detailed enough and YASM syntax is same as NASM in brackets usage: Basic use of immediates (square brackets) in x86 Assembly and yasm

这篇关于将值插入数组并显示,nasm的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆