【问题标题】:evaluating segments of a byte in C在 C 中评估一个字节的段
【发布时间】:2017-12-22 04:24:55
【问题描述】:

我正在尝试在不使用反汇编表的情况下对 Intel 8080 进行反汇编。我读入内存字节并将它们分成位段:(7, 6), (5, 4, 3), (2, 1, 0)。我想知道这些部分的数值是多少,这里是一个例子:

给定这个字节:

0b00110000

第 7 位和第 6 位计算结果为:

0b00 -> 0

位 5 到 3:

0b110 -> 6

位 2 到 0:

0b000 -> 0

现在,我对 C 语言非常缺乏经验,我很难想出一个优雅而简单的解决方案来解决这个问题。到目前为止,我的想法如下:

  1. 创建 3 个字节副本(每个部分 1 个)

  2. 清除除我感兴趣的位之外的所有位(按位与掩码)

  3. 将字节移动适当的位数

  4. 读取值

这会做这项工作还是有更好的方法?

【问题讨论】:

  • 试试看是否有效。

标签: c bit-manipulation


【解决方案1】:

如果您考虑需要做什么,您只需要关注一元>> 移位运算符和& 运算符。它还有助于提出一个位掩码,以便仅将那些需要的位从可能需要的任何中间计算中隔离出来。 (位掩码只是一个变量,其中包含一个数字,其1 位和0 位可以在按位运算中使用,以提供您想要的结果)。

在这里,在您所有的位分离中,您将只关心移位后的 2 或 3 个低位以获得最终结果。在您查看(7,6) 的情况下,没有其他位需要掩码,只有您正在查看需要掩码的低 3 位的情况。

位掩码(或什么是幻数)的角度来看,您需要一个掩码来保留最后 3 位并丢弃所有剩余的高位。因此,对于 8 位值,您需要 00000111,或者只是数字 7

首先,如果您想检查一个 8 位数字中的最高 2 位,那么您只需将您的数字右移6,例如给定一个字节 b 在您的情况下保存总值 0x00110000,您需要。

b >> 6;

这里不需要屏蔽任何东西,因为没有更高的位。

对于您示例中的位(5,4,3),您需要向右移动3,然后您需要将& 与您的mask7b00000111)的中间结果去掉剩下的两位(6,7),例如

uint8_t mask = 7;
...
(b >> 3) & mask;

在最后一部分,您的(2,1,0) 位,不需要移位,您只需清除位2 上的所有位,例如用你的面具摆脱(7,6,5,4,3)

b & mask

总而言之,使用精确宽度类型,您可以执行类似于以下的操作。该程序将您的字节值作为其第一个参数(使用48,例如,如果没有给出参数,则默认使用0x0011000)并输出移位和掩码的结果:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

int main (int argc, char *argv[]) {

    uint64_t tmp = argc > 1 ? strtoul (argv[1], NULL, 0) : 48;
    uint8_t b = 0,
        mask = 7;

    if (tmp > UINT8_MAX) {
        fprintf (stderr, "input exceeds UINT8_MAX.\n");
        return 1;
    }

    b = (uint8_t)tmp;

    printf ("(7,6)   0x%02" PRIx8 "\n", b >> 6);
    printf ("(5,4,3) 0x%02" PRIx8 "\n", (b >> 3) & mask);
    printf ("(2,1,0) 0x%02" PRIx8 "\n", b & mask);

    return 0;
}

使用/输出示例

$ /bin/bytes_233
(7,6)   0x00
(5,4,3) 0x06
(2,1,0) 0x00

检查一下,如果您还有其他问题,请告诉我。

【讨论】:

  • 谢谢你,这绝对让我明白了!
  • 当然,很高兴能帮上忙,我们都可以不时用手让灯泡亮一下。
【解决方案2】:

为了解析出二进制值的特定位,我们使用位移位和位掩码。使用以下概念可以通过这种方式获得任何位段:

(data >> n) & mask

其中data是原始数据,n是该数据开始的位位置,mask是“全1”的位掩码,对应于段的长度。

“all one”的mask 可以通过知道段大小获得:(1u &lt;&lt; size) - 1。例如,如果段的大小为 3 位,则 1

因此,如果我们知道位大小和位位置(偏移量),我们就可以获取数据。示例:

#include <stdio.h>
#include <inttypes.h>

#define SEG1_BITS   2
#define SEG1_OFFSET 6
#define SEG2_BITS   3
#define SEG2_OFFSET 3
#define SEG3_BITS   2
#define SEG3_OFFSET 0

#define SEG1(d) ( ((uint32_t)d >> SEG1_OFFSET) & ((1u<<SEG1_BITS)-1) )
#define SEG2(d) ( ((uint32_t)d >> SEG2_OFFSET) & ((1u<<SEG2_BITS)-1) )
#define SEG3(d) ( ((uint32_t)d >> SEG3_OFFSET) & ((1u<<SEG3_BITS)-1) )

int main (void)
{
  uint8_t data = 0x30;

  printf("%"PRIu32"\n", SEG1(data));
  printf("%"PRIu32"\n", SEG2(data));
  printf("%"PRIu32"\n", SEG3(data));
  return 0;
}

带有“X宏”以避免代码重复的高级版本:

#include <stdio.h>
#include <inttypes.h>

#define SEG_LIST     \
  /*bits, offset */  \
  X(2,    6)         \
  X(3,    3)         \
  X(2,    0)

#define SEG(data, bits, n) ( ((uint32_t)data >> n) & ((1u<<bits)-1) )

int main (void)
{
  uint8_t data = 0x30;

  #define X(bits, n) printf("%"PRIu32"\n", SEG(data, bits, n));
    SEG_LIST
  #undef X

  return 0;
}

【讨论】:

  • 我会使用无符号类型进行左移(1u &lt;&lt; size) - 1,以避免在将较大类型的代码调整为 8 位时意外转移到符号位。
  • @user694733 确实!感谢您的发现。
【解决方案3】:

由于问题具有扩展性,因此我不得不对其进行概括并编写此方法:

uint8_t bitsAsValue(uint8_t* byte, int start, int end) {
    uint8_t mask = 0b00000000;
    uint8_t mask_setter = 0b00000001;
    for (int i = start; i <= end; i++) {
        mask |= (mask_setter << i);
    }
    uint8_t value = *byte & mask;
    value = value >> start;
    return value;
}

它将从一个字节的连续段中提取值。

【讨论】:

    猜你喜欢
    • 2017-11-04
    • 1970-01-01
    • 2017-02-15
    • 2020-09-06
    • 1970-01-01
    • 2011-12-22
    • 2011-02-05
    • 2014-01-28
    • 2016-07-26
    相关资源
    最近更新 更多