简体   繁体   English

根据偶数/奇数 popcount 向右/左移动

[英]Shift right/left based on an even/odd popcount

My code should check if a variable has an odd or even number of bits turned on, and then shift right the amount of bits turned on if the amount is even or if it is odd shift left that many times.我的代码应该检查一个变量是否有奇数或偶数位打开,然后右移打开的位数,如果数量是偶数,或者如果它是奇数,则向左移动那么多次。

This is my code:这是我的代码:

IDEAL
MODEL small
STACK 100h
DATASEG
TAV db 00001001b
t_copy db 00001001b

CODESEG
start:

    xor cx,cx
    mov cx,8
    xor al,al
    L1:
        shr [t_copy],1
        jnc nc
        inc al
        nc:
        
        dec cx
        jnz L1
    mov cl,al
    and al,00000001h
    
    cmp al,0h
    jz even_               
    jnz odd
    
    
    odd:
        shl [TAV],cl
        jmp exit
    even_:
        shr [TAV],cl
    
exit:
    mov ax, 4c00h
    int 21h
END start

When I run the code it doesn't shift and doesn't change the value of the variable.当我运行代码时,它不会移动,也不会更改变量的值。 I think it changes the value of the variable as index in the memory. Do you know how I can fix it?我认为它将变量的值更改为 memory 中的索引。您知道我该如何解决吗?

When you run the.EXE that TASM creates for you, execution begins at the start label in the code segment and the CS segment register points at it.当您运行 TASM 为您创建的 .EXE 时,执行从代码段中的 label开始,CS 段寄存器指向它。 For your program to function correctly, similarly the DS segment register should point to the data segment of your program.为了让你的程序正确到function,同样DS段寄存器应该指向你程序的数据段。 Sadly this is not so by default since DS is pointing at the Program Segment Prefix PSP.遗憾的是,默认情况下并非如此,因为 DS 指向程序段前缀 PSP。 You have to setup DS yourself with the following code:您必须使用以下代码自行设置 DS:

mov  ax, @data
mov  ds, ax

If you would have chosen for MODEL tiny (instead of MODEL small ) the problem would not have shown itself since then all 4 segment registers would have been equal to each other.如果您选择MODEL tiny (而不是MODEL small ),那么问题就不会出现,因为所有 4 个段寄存器都将彼此相等。


 xor cx,cx mov cx,8

Zeroing the register before loading the register is an unnecessary operation.在加载寄存器之前将寄存器清零是不必要的操作。

 jnc nc inc al nc:

Although this construct is correct, incrementing AL if the carry is set can be much simpler via adc al, 0 .尽管这个构造是正确的,但如果设置了进位,则递增 AL 可以通过adc al, 0简单得多。 If the carry flag is clear nothing is added, and if the carry flag is set then 1 is added.如果进位标志清零,则不添加任何内容,如果进位标志已设置,则添加 1。

 and al,00000001h cmp al,0h

Checking whether a value is even/odd is done by looking at the least significant bit which you are doing fine.检查一个值是否是偶数/奇数是通过查看你做得好的最低有效位来完成的。 Point is, you don't need that cmp afterwards since the and instruction already defines the zero flag that you want to use for branching.重点是,您之后不需要cmp ,因为and指令已经定义了要用于分支的零标志。
Better still, if you used test instead of and , you would receive the same zero flag and not modify the register at all.更好的是,如果您使用test而不是and ,您将收到相同的零标志并且根本不会修改寄存器。 Writing test cl, 1 would save you from using the additional register.编写test cl, 1将使您免于使用额外的寄存器。

 jz even_ jnz odd odd:

This jnz conditional jump serves no purpose.这个jnz条件跳转没有任何用处。 If the condition is met the execution jumps to the odd label, and if the condition is not met the execution falls through into the odd label.如果条件满足则执行跳转到奇数label,如果条件不满足则执行跳转到奇数label

 shr [t_copy],1

You can improve your solution by processing the data from a register instead of from the memory. You would not need the copy either.您可以通过处理来自寄存器而不是来自 memory 的数据来改进您的解决方案。您也不需要副本。

CODESEG
start:
    mov  ax, @data
    mov  ds, ax

    xor  cx, cx     ; CL is popcount, CH is a convenient 0
    mov  al, [TAV]
L1:
    shr  al, 1      ; The bit that comes out of AL
    adc  cl, ch     ;   is added to CL
    test al, al     ; If AL got empty, further adding would be useless
    jnz  L1
    test cl, 1      ; Non-destructive checking of the least significant bit
    jnz  ON
    shr  [TAV], cl  ; Shift right if popcount is EVEN
    jmp  exit               
ON:
    shl  [TAV], cl  ; Shift left if popcount is ODD
exit:

EDIT: This error described below doesn't actually exist, I misread the code.编辑:下面描述的这个错误实际上并不存在,我误读了代码。

You're close, but there's one small mistake here.你很接近,但这里有一个小错误。 When you do and al,1 , you've actually altered al and the original population count is now lost.当您执行and al,1时,您实际上已经更改了al并且原始人口计数现在丢失了。 Fortunately, there's an easy way to fix this, use test al,1 instead.幸运的是,有一种简单的方法可以解决这个问题,使用test al,1代替。 This affects the zero flag the same way that and al,1 does, except al remains the same it was before the test .这会以and al,1相同的方式影响零标志,除了al保持与test之前相同。 So try this out and see if it helps:所以试试这个,看看它是否有帮助:

    mov cl,al
    test al,1
    
    ;cmp al,0h ;this line is redundant.
    jz even_               
    ;jnz odd   ;this line is redundant.
    odd:
        shl [TAV],cl
        jmp exit
    even_:
        shr [TAV],cl

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

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