简体   繁体   English

如何在用于 Linux 的 nasm x86 程序集中复制数组,移植 16 位 DOS 代码?

[英]How can i copy an array in nasm x86 assembly for Linux, porting 16-bit DOS code?

I have to write a program which copy an array in other array, using x86 assembler我必须编写一个程序来复制其他数组中的数组,使用 x86 汇编程序

The original code is written in MSDOS' TASM for 8086 processor, but I want port this to Linux NASM using i386 processor原始代码是用 MSDOS 的 TASM 编写的 8086 处理器,但我想使用 i386 处理器将其移植到 Linux NASM

The code in TASM is this: TASM 中的代码是这样的:

.MODEL SMALL

.DATA

    TABLE_A DB 10, 5, 1
    TABLE_B DB 0, 0, 0

.CODE

    MOV AX, SEG TABLE_B
    MOV DS, AX

    MOV SI, 0

    LOOP:
        MOV AL, TABLE_A[SI]
        MOV TABLE_B[SI], AL

        INC SI
        CMP SI, 2
    JBE LOOP


    MOV AH, 4Ch
    INT 21h

END

I'm trying to rewrite this in nasm, but I don't get to sit in the correct array position, similar to TABLE_A[SI] instruction我试图在 nasm 中重写它,但我没有坐在正确的数组位置,类似于 TABLE_A[SI] 指令

How can I do it?我该怎么做?

The final code in nasm is this nasm 中的最终代码是这样的

section .text
global _start
cpu 386

_start: _开始:

MOV ESI, TABLE_A
MOV EDI, TABLE_B
MOV CX, 3

COPY_LOOP:      
    MOV AL, [ESI]
    MOV [EDI], AL

    INC SI
    INC DI
LOOP COPY_LOOP

MOV AX,1
INT 80h

section .data
TABLE_A DB 10, 5, 1
TABLE_B DB 0, 0, 0

How could I do it?我怎么能做到?

(question from comments on self-answer) (来自对自我回答的评论的问题)

Well, first you read Instruction reference guide to understand what the instruction does, and then you can use it, if it fits your purpose.好吧,首先您阅读指令参考指南以了解指令的作用,然后您可以使用它,如果它符合您的目的。 This is the important step, keep re-reading instruction details every so often, to verify it does modify registers and flags in a way you expect it.这是重要的一步,经常重新阅读指令详细信息,以验证它确实以您期望的方式修改了寄存器和标志。 Especially if in debugger you see the CPU state of change you didn't expect.特别是如果在调试器中您看到 CPU 状态的变化是您没有预料到的。

As you are in linux, the ds / es segment registers are very likely already set to reasonably values (covering .data section), so after setting eSi to S ource address, eDi to D estination address, and eCx to C ount, you write instead of COPY_LOOP: just rep movsb ... and then exit trough int 80h (eax=1) .如你在Linux中, ds / es段寄存器很可能已经被设置为合理值(覆盖.data部分),所以设置后eSi乌尔斯河地址S, eDid estination地址, eCxC'mount,则写而不是COPY_LOOP:只是rep movsb ... 然后退出低谷int 80h (eax=1) (notice the emphasized letters in register names, Intel picked those intentionally to make it easy to recall) (注意寄存器名称中的强调字母,英特尔特意选择了这些字母,以便于回忆)

BTW, just now I noticed, you wrote in your code sort of bugs:顺便说一句,刚才我注意到,你在你的代码中写了一些错误:

  1. inc si/di should be inc esi/edi , because you use esi/edi to address. inc si/di应该是inc esi/edi ,因为您使用 esi/edi 来寻址。 If you would be copying array over 64k memory boundary, inc si would wrap around on it.如果您要复制超过 64k 内存边界的数组,则inc si将环绕它。

  2. set ecx to 3, in 32b mode the loop instruction does use whole 32b ecx , not 16b part cx only.ecx设置为 3,在 32b 模式下, loop指令确实使用整个 32b ecx ,而不仅仅是 16b 部分cx If the code ahead of copy would use some large number in ecx setting some of upper 16 bits, your loop would copy many more bytes than only 3.如果复制之前的代码将在ecx使用一些大数字来设置一些高 16 位,那么您的循环将复制比仅 3 个更多的字节。

  3. ahead of calling int 80h again you must set whole eax with the function number, otherwise you risk to have some garbage in upper 16 bits of eax from previous code, requesting invalid function.在再次调用int 80h之前,您必须使用函数编号设置整个eax ,否则您可能会在之前代码的eax高 16 位中出现一些垃圾,从而请求无效的函数。

So after applying these your code may look like this:因此,在应用这些之后,您的代码可能如下所示:

section .text
global _start
cpu 386

_start:
    MOV ESI, TABLE_A
    MOV EDI, TABLE_B
    MOV ECX, 3
    REP MOVSB  ; copy ECX bytes from DS:ESI to ES:EDI

    MOV EAX,1  ; call sys_exit, again FIXED to EAX!
    INT 80h

section .data

TABLE_A DB 10, 5, 1
TABLE_B DB 0, 0, 0

If you did read the docs about registers, you should already understand what is difference between eax and ax .如果您确实阅读了有关寄存器的文档,您应该已经了解eaxax之间的区别。 In Linux you are in 32b mode (when you link the binary as 32b elf, nowadays the 64b may be default on 64b system, which differs a bit from 32b mode), so by default use the 32b register variants.在 Linux 中,您处于 32b 模式(当您将二进制文件链接为 32b elf 时,现在 64b 可能是 64b 系统上的默认值,这与 32b 模式略有不同),因此默认情况下使用 32b 寄存器变体。 Unless you really want the 16b/8b variant for particular reason, and you make sure the code doesn't work later with 32b register while you set only less of it (like loop , rep movsb and int 80h do).除非您出于特殊原因确实想要 16b/8b 变体,并且您确保代码在稍后使用 32b 寄存器时不起作用,而您只设置较少的寄存器(例如looprep movsbint 80h )。

Also it makes the code usually faster, as using 16b ax in 32b mode requires additional opcode byte ahead of instruction, for example mov eax,ebx is 2 bytes opcode 89 D8 , mov ax,bx is 3 bytes opcode 66 89 D8 .它还使代码通常更快,因为在 32b 模式下使用 16b ax需要在指令前额外的操作码字节,例如mov eax,ebx是 2 字节操作码89 D8mov ax,bx是 3 字节操作码66 89 D8

In response to marc回应马克

I tried this form, without successful result:我尝试了这种形式,但没有成功:

MOV SI, 0
MOV AX, 0

LOOP:       
    MOV AX, [TABLE_A + SI]
    MOV [TABLE_B + SI], AX

    INC SI
    CMP SI, 2
JBE LOOP

Use pointers ( SI , DI ) to the arrays and CX as counter :使用指向数组和CX指针( SIDI )作为计数器:

MOV SI, Table_A     ;POINTER TO TABLE_A.
MOV DI, Table_B     ;POINTER TO TABLE_B.
MOV CX, 3           ;ARRAY LENGTH.
REPEAT:       
    MOV AL, [SI]
    MOV [DI], AL
    INC SI
    INC DI
    LOOP REPEAT     ;CX-1. IF CX>0 JUMP TO REPEAT.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM