将值插入数组并显示,nasm [英] Insert values into array and display, 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屋!