从最高有效位或高位开始提取寄存器的位

时间:2019-04-02 04:55:07

标签: assembly x86 bit-manipulation nasm

编辑:

我没想到这个问题会如此迅速地受到关注。根据我已经收到的答案,似乎我可能遗漏了重要的信息。该模式不是固定位数。一些字母可能具有更多或更少的位。即,B具有5位,但是C可能使用多达6位,但没有一个使用多于一个字节。我在问题中包括了一个“ A”位模式的示例,该模式每行使用7位。另请参阅问题底部的编辑。

我是新来的。我的位模式对应于字母的文本表示形式。每个1代表$(或任何符号),每个0代表空格。即:

    $$$$            11110
    $   $           10001
    $   $           10001
    $$$$            11110
    $   $           10001
    $   $           10001
    $$$$            11110

   $            0001000  
  $ $           0010100
 $$$$$          0111110  
$     $         1000001

我编写了一个汇编语言程序,该程序读取每个模式并根据其读取的是1还是0来打印正确的符号。要确定它是1还是0,我将寄存器与1相加,然后将位右移等于每行中位数的次数,然后比较结果:

请注意,每一行的位都存储在一个单独的2字节字的底部,我将其加载到8位寄存器中。

patternb:   dw 011110b,010001b,010001b,011110b,010001b,010001b,011110b

rowloop:
    mov bl,[patternb+si]    ;iterate through each element in binary array

    patternloop:    
        mov bh,bl   ;move bit pattern into register so that we can change it
        and bh,1    ;AND register to find set bits and store back in register
        shr bl,1    ;SHIFT original bit pettern right
        cmp bh,1    ;check if bit is set or not (1=set, else 0)
        je writesym ;if set, write symbol
        jne writeblank  ;if not set, write space

问题在于AND的工作方式。显然,它是从最低有效位开始的,并且随着这些位向右移而打印,但这会导致问题,即它以“反向”顺序打印字母。即:

 ####
#   #
#   #
 ####
#   #
#   #
 ####

我尝试了一些操作,但似乎没有任何效果。我也尝试过移动和旋转位模式以对应正确的打印,但这不适用于每一行,因为不需要以这种方式来操纵每一行。 (例如,第2行将正确打印,而无需首先进行操作)。我对A-E的每个字母都有相同的位模式技术。

理想情况下,我希望它从最高有效位开始比较,然后以正确的顺序打印出来,但是我不确定如何处理其中的位为了实现这一目标。

编辑: 在彼得·杜尼奥(Peter Duniho)回答之后,我想发表一些我尝试过的事情: 我尝试过用10000b对模式进行“与”运算,然后对结果进行ROL处理,以得到00001b的答案,然后将位向左移。然后比较结果以查看应打印哪个符号。这也不起作用,但是因为位模式并不总是固定的,所以无论如何都不是解决方案。

    mov bh,bl   ;move bit pattern into register so that we can change it
    and bh,10000b ;AND register to find set bits and store back in register
    rol bh,1    ;rol result to obtain 00001b
    shl bl,1    ;SHIFT original bit pettern right
    cmp bh,1    ;check if bit is set or not (1=set, else 0)
    je writesym ;if set, write symbol
    jne writeblank  ;if not set, write space

我现在解决这个问题最接近的方法(在Peter Duniho的回答作为指导的帮助下)是将我的位数组存储为完整的8位格式(即011110000b等,而不是{{ 1}},否则汇编程序将其隐式存储为011110b,如Martin Rosenau的答案所述(我们不希望这样做),并将其与完整的00011101存储在一起(因为我们最多使用8位,而这使我们可以检查MSB),而不是我以前尝试的1(10000000b),然后使用上面的ROL和Compare方法(或者将其与000000001b比较)。该循环总共运行7次(由于每个字母有7行/位的模式,除了A具有4行,所以A不能正确打印,但这是我可以在某些条件下解决的问题。程序可以工作并打印现在正确。这是我使用的代码:

10000000b

我已将Peter的解决方案标记为答案,因为它为我指出了解决该问题的正确方向。但是正如他提到的那样,有很多方法可以解决此问题(如发布的不同解决方案所示),但是对于我自己的代码,他的实现恰恰是我最容易实现的目标。

Martin Rosenau的答案也很有见地,尤其是优化方面。我将在有更多时间的时候尝试实现这些功能,然后更新上面的解决方案。

3 个答案:

答案 0 :(得分:3)

  

理想情况下,我希望它从最高有效位开始进行比较,然后以正确的顺序打印出来

对我来说似乎是个好主意。您是否按照这些方法尝试了什么?如果是这样,您具体尝试了什么?您遇到什么具体困难?

与此同时…

由什么决定要检查的位数(即循环计数)?是固定的吗?如果是这样,为什么不只将AND与位的高位而不是低位(例如10000b,又名16)并左移而不是右移?

例如

mov bh,bl     ;move bit pattern into register so that we can change it
and bh,10000b ;AND register to find set bits and store back in register
shl bl,1      ;SHIFT original bit pattern left
cmp bh,10000b ;check if bit is set or not (1=set, else 0)
je writesym   ;if set, write symbol
jne writeblank  ;if not set, write space

如果直到运行时才知道循环计数,则可以为每个迭代进行移位:

mov bh,bl     ;move bit pattern into register so that we can change it
shr bh,cl     ;the assumption being that cl has the width of your bit pattern
dec cl        ;next bit
and bh,1      ;AND register to find set bits and store back in register
cmp bh,1      ;check if bit is set or not (1=set, else 0)
je writesym   ;if set, write symbol
jne writeblank  ;if not set, write space

如果您已经在循环中使用CX,则显然需要对上述内容进行一些修改。但是希望您能掌握基本的想法。

上述内容的一种变化是通过存储al并向左移适当的计数(例如1),然后使用shl al,cl作为操作数,将AND位模式存储在另一个寄存器(例如al)中像上面第一个示例中的10000b一样。

这些不是您唯一的选择。您将需要大大缩小问题的局限性以获得更具体的答案。但是,假设这是学习ASM的练习,那么这对您来说是一个很好的机会,可以阅读和了解有关您可用的位操作的更多信息。 :)

答案 1 :(得分:3)

  

问题在于AND的工作方式。

您的第一个问题是右移的工作方式。右移将删除“图像”中最右边的“像素”,并将该像素的左侧像素移动到最右侧位置(这样该像素将是下一个要打印的像素):

"$$$ $ $ " ->
" $$$ $ $" ->
"  $$$ $ " ->
"   $$$ $" ->
...

如果要从左向右打印像素,则必须执行左移,而不是右移。

(请注意,可以使用shl bl,1或使用add bl,bl进行左移。)

由于“图像”的宽度只有5个“像素”,而一个字节只有8位,因此您必须确定是在图像的左侧还是右侧添加未使用的位。

例如:

"$$$ $" = 11101000 or 00011101 ?

假设您决定在左侧添加像素(00011101-如果将数字指定为011101b,汇编器将隐式执行)。

这时,您将必须执行AND操作,该操作的值必须代表最左边的像素集:

Old:              New:

and bh,1          and bh, 010000b
shr bl,1          shl bl, 1

顺便说一句:您的程序可能有两种优化:

1)使用最左侧的位不会丢失的事实:

patternloop:    
    shl bl,1
    test bl,0100000b
    je writesym

此优化使用了以下事实:字节中有3个空闲位,因此字节的左位在左移时不会“丢失”:

"0 0 0<1>1 1 0 1"
  -> Left shift ->
"0 0<1>1 1 0 1 0"

"< >" = Bit you are interested in

指令test bl,xxx影响ZF标志(影响je指令)的方式与两个指令and bl,xxx后跟{{1}的组合的方式相同},但不会修改cmp bl,0寄存器!

2)使用右移将位移至bl的事实:

CF

此优化假定“ $$$ $”存储为11101000而不是00011101。它使用以下事实:patternloop: shl bl,1 jc writesym 将在执行操作之前将最左边(最高)位复制到shl标志中移位(假设移位了一位):

CF

如果设置了BL="<1>1 1 0 1 0 0 0", CF=? -> Left shift (using SHL or ADD) -> BL="1 1 0 1 0 0 0 0", CF=<1> 标志,则jc指令将执行跳转。

答案 2 :(得分:0)

您可以使用操作码ROL,将操作码向MSB旋转一位,并且MSB将被旋转到LSB的位置,例如:

11110000-> ROL 1-> 11100001

以便您可以执行以下操作:

ROL1->测试LSB-> ROL1->测试LSB->...。

在您的情况下,bl是一个8位元的混响器,循环ROL,测试8次以绘制ascii艺术