x86程序集:Irvine32 - 获取数组的最后一个元素

时间:2016-05-06 12:55:31

标签: arrays assembly x86 irvine32

我是大会的新手,我需要帮助完成汇编语言Irvine32的任务。我想知道我哪里出错了。我相信我的代码是80%正确,但有一些我没有看到或认识到的东西。这是节目详情。

“编写一个包含单词数组的汇编语言程序。程序将数组的最后一个元素加载到一个适当大小的寄存器中并打印出来。(不要硬编码最后一个元素的索引。)”

INCLUDE Irvine32.inc    
.data
  val1 word 1,2,3,4,5,6
  val2 = ($-val1)/2
.code
main PROC        
  mov ax, 0
  mov ax, val1[val2]

  Call WriteDec
  Call DumpRegs
 exit
main ENDP
END main

2 个答案:

答案 0 :(得分:2)

首先,您的代码有一个错误:val1[val2]索引,其中元素以字数计,而不是以字节为单位的长度(除非MASM语法比我预期的更神奇)。它从数组的末尾开始读取,因为第一个元素位于val1[0]

要查找结尾,您需要知道长度(显式长度,如传递给memcpy(3)的缓冲区),或者搜索一个sentinel元素(隐式长度,如传递的C字符串)到strcpy(3))。

拥有一个接受显式长度作为参数的函数对我来说似乎很好。它显然比对哨兵元素的循环扫描更有效,并且所示的阵列不包括一个。 (请参阅Jose的答案,了解使用'$'(即36)作为哨兵值的建议。-10可能是更明智的哨兵/终结者。 )

显然知道长度要好得多,因为不需要循环扫描整个阵列。

如果您编写val2 = 6或更差val2 dw 6,我只会将其称为硬编码,而不是在汇编时从阵列计算。如果你想编写一个可以使用非编译时常量数组的函数,你可以让它接受长度作为内存中的值,而不是嵌入到其加载指令中的立即数。

e.g。

长度作为内存中的参数

.data
  array word 1,2,3,4,5,6
  array_len word ($-array)/2    ; some assemblers have syntactic sugar to calc this for you, like a SIZE operator or something.

.code
main PROC       ; inputs: array and array_len in static storage
                ; output: ax = last element of array
                ; clobbers: si

  ; mov ax, 0   ; This is useless, the next mov overwrites it.

  mov si, [array_len] ; do we need to save/restore si with push/pop in this ABI?

  add si,si           ; multiply by 2: length in words -> length in bytes
  mov ax, [array + si - 2]   ; note that the -2 folds into array at assemble time, so it's just a disp16 + index addressing mode

  Call WriteDec
  Call DumpRegs
 exit
main ENDP
END main

您还可以编写一个函数来在堆栈或寄存器中获取指针和长度args,并让main传递这些参数。

您可以通过接受以字节为单位的长度,或者一个开始和一个结束指针来保存add(或shl)(就像采用{{1的C ++ STL范围函数一样) }和.begin()迭代器)。如果你有结束指针,你根本不需要开始指针,除非如果它们相等(大小= 0)则返回错误。

或者,如果您没有使用过时的16位代码,则可以在寻址模式下使用缩放索引,例如.end()。你包括[array + esi * 2] ...

答案 1 :(得分:1)

我认为你达到最后一个元素的解决方案效率最高(($-val1)/2),但是@ zx485是正确的,你的老师可能会认为你在作弊,所以,在其他解决方案中,你可以达到最后一个元素用循环和指针SI:

INCLUDE Irvine32.inc    
.data
  val1 word 1,2,3,4,5,6
  val2 = ($-val1)/2
.code
main PROC        
; mov ax, 0
; mov ax, val1[val2]

  mov cx, val2-1        ;COUNTER FOR LOOP (LENGTH-1).
  mov si, offset val1   ;SI POINTS TO FIRST WORD IN ARRAY.
repeat:
  add si, 2             ;POINT TO NEXT WORD IN ARRAY.  
  loop repeat           ;CX--, IF CX > 0 REPEAT.

  mov ax, [ si ]        ;LAST WORD!

  Call WriteDec
  Call DumpRegs
 exit
main ENDP
END main

一个较短的方法是摆脱循环并使用SI指针直接跳到最后一个元素(并稍微改变val2):

INCLUDE Irvine32.inc    
.data
  val1 dw 1,2,3,4,5,6
  val2 = ($-val1)-2      ;NOW WE GET LENGTH - 2 BYTES.
.code
main PROC           
; mov ax, 0
; mov ax, val1[val2]

  mov si, offset val1   ;SI POINTS TO FIRST WORD IN ARRAY.
  add si, val2          ;SI POINTS TO THE LAST WORD.
  mov ax, [ si ]        ;LAST WORD!

  Call WriteDec
  Call DumpRegs
 exit
main ENDP
END main

并且"是",您可以加入这两行:

  mov si, offset val1   ;SI POINTS TO FIRST WORD IN ARRAY.
  add si, val2          ;SI POINTS TO THE LAST WORD.

成为一体,我把他们分开来互相评论:

  mov si, offset val1 + val2

如果您无法使用val2 = ($-val1)/2 ,一个选项是为数组选择一些终止字符,例如'$',并循环直到它为止实测值:

INCLUDE Irvine32.inc    
.data
  val1 word 1,2,3,4,5,6,'$'                ;ARRAY WITH TERMINATING CHARACTER.
  ;val2 = ($-val1)/2
.code
main PROC        
  ;mov ax, 0
  ;mov ax, val1[val2]

  mov si, offset val1    ;SI POINTS TO VAL1.
  mov ax, '$'            ;TERMINATING CHARACTER.
repeat:
  cmp [ si ], ax
  je  dollar_found       ;IF [ SI ] == '$'
  add si, 2              ;NEXT WORD IN ARRAY.
  jmp repeat

dollar_found:  
  sub si, 2              ;PREVIOUS WORD.
  mov ax, [ si ]         ;FINAL WORD!

  Call WriteDec
  Call DumpRegs
 exit
main ENDP
END main