【问题标题】:how are multiple chunks of 8-bit words converted to a single number?如何将多个 8 位字块转换为单个数字?
【发布时间】:2016-12-28 18:32:29
【问题描述】:

首先,我知道如果我有一台 8 位计算机,它只能处理 8 位数字,不高于 8,但我知道它仍然可以表示 16 位数字甚至 32 ,64,128 位数字,通过在 ram 中分配更多内存。 但为了简单起见,我们只以 16 位数字为例。

假设我们在 ram 中有一个 16 位的数字,如下所示:

12 34 <-- That's Hexadecimal btw

让我们也把它写成二进制,以防你们更喜欢二进制形式:

00010010 00110100 <-- Binary
              &
      4660 in decimal

现在,我们知道计算机无法将这个大数字(4660)理解为一个数字,因为计算机只能理解 8 位数字,最多只能到 255。所以右边的字节会保持原样是:

00110100 <-- 52 in decimal

但左字节:

00010010 <-- would be 18 if it was the byte on the right, 
             but since it is on the left, it means that its
             4608 

所以我的问题是,如果计算机只能理解小于 255 的数字,它如何将第二个字节读取为 4608,然后如何将这两个字节解释为单个数字(4660)?

谢谢,如果您感到困惑,请随时在 cmets 中问我。我说得尽可能清楚。

【问题讨论】:

  • 最好不要认为计算机“理解”除指令集之外的任何东西。 8 位计算机具有读取字节、以各种方式混合它们以及写入字节的指令。这些字节的值只会变成你头脑中的数字。您要问的是“如果我只有单字节指令,我该如何加/减/乘/除”> 256 的数字?在学校里,你被教导如何计算大数,即使你只记得个位数的加法和乘法表。
  • 原来是一样的..?
  • 你能举一个使用二进制和实际代码的例子吗。我可以用伪代码。

标签: algorithm cpu-architecture


【解决方案1】:

嗯,这是一个比 HW 架构更多的编程问题,因为 CPU 在您的测试用例中仅执行 8 位操作,并且不了解 16 位。您的示例是:8 位 ALU 上的 16 位算术,通常通过拆分为数字的高半和低半(并加入后者)来完成。这可以通过更多方式完成,例如这里很少(使用 C++):

  1. 转移

    const int _h=0; // MSB location
    const int _l=1; // LSB location
    BYTE h,l; // 8 bit halves
    WORD hl;  // 16 bit value
    h=((BYTE*)(&hl))[_h];
    l=((BYTE*)(&hl))[_l];
    // here do your 8bit stuff on h,l
    ((BYTE*)(&hl))[_h]=h;
    ((BYTE*)(&hl))[_l]=l;
    

    您需要从/复制到 8 位/16 位“注册”副本,这很慢,但有时可以缓解问题。

  2. 指针

    const int _h=0; // MSB location
    const int _l=1; // LSB location
    WORD hl;  // 16 bit value
    BYTE *h=((BYTE*)(&hl))+_h;
    BYTE *l=((BYTE*)(&hl))+_l;
    // here do your 8bit stuff on *h,*l or h[0],l[0]
    

    您无需复制任何内容,只需使用指针访问 *h,*l 而不是 h,l。指针初始化只完成一次。

  3. 联合

    const int _h=0; // MSB location
    const int _l=1; // LSB location
    union reg16 
     {
     WORD dw;  // 16 bit value
     BYTE db[2]; // 8 bit values
     } a;
    // here do your 8bit stuff on a.db[_h],a.db[_l]
    

    这与 #2 相同,但形式更易于管理

  4. CPU 8/16 位寄存器

    即使是 8 位 CPU,通常也有 16 位寄存器,可以通过其一半甚至完整的寄存器访问。例如,在 Z80 上,你得到了AF,BC,DE,HL,PC,SP,其中大部分也可以通过它的半寄存器直接访问。因此,有使用hl 的说明,也有使用h,l 的说明。 在 x86 上也是一样的,例如:

    mov AX,1234h
    

    与(除了时间和可能的代码长度之外)相同:

    mov AH,12h
    mov AL,34h
    

简而言之,这就是 8/16 位之间的转换,但我假设您正在询问更多关于如何完成操作的问题。这是通过使用 Carry flag 完成的(遗憾的是,大多数高级语言和汇编程序都缺少该标志)。例如 8 位 ALU(x86 架构)上的 16 位加法是这样完成的:

// ax=ax+bx
add al,bl
adc ah,bh

所以首先你添加最低的BYTE,然后是最高的+进位。欲了解更多信息,请参阅:

有关如何实现其他操作的更多信息,请参阅 bignum 算术的任何实现。

[编辑1]

这里是 C++ 小示例,说明如何仅使用 8 位算术打印 16 位数字。您可以使用 8 位 ALU 作为构建块来进行 N*8 位操作,就像我做 16 位操作一样...

//---------------------------------------------------------------------------
// unsigned 8 bit ALU in C++
//---------------------------------------------------------------------------
BYTE cy;                    // carry flag cy = { 0,1 }
void inc(BYTE &a);          // a++
void dec(BYTE &a);          // a--
BYTE add(BYTE a,BYTE b);    // = a+b
BYTE adc(BYTE a,BYTE b);    // = a+b+cy
BYTE sub(BYTE a,BYTE b);    // = a-b
BYTE sbc(BYTE a,BYTE b);    // = a-b-cy
void mul(BYTE &h,BYTE &l,BYTE a,BYTE b);    // (h,l) = a/b
void div(BYTE &h,BYTE &l,BYTE &r,BYTE ah,BYTE al,BYTE b);   // (h,l) = (ah,al)/b ; r = (ah,al)%b
//---------------------------------------------------------------------------
void inc(BYTE &a) { if (a==0xFF) cy=1; else cy=0; a++; }
void dec(BYTE &a) { if (a==0x00) cy=1; else cy=0; a--; }
BYTE add(BYTE a,BYTE b)
    {
    BYTE c=a+b;
    cy=DWORD(((a &1)+(b &1)   )>>1);
    cy=DWORD(((a>>1)+(b>>1)+cy)>>7);
    return c;
    }
BYTE adc(BYTE a,BYTE b)
    {
    BYTE c=a+b+cy;
    cy=DWORD(((a &1)+(b &1)+cy)>>1);
    cy=DWORD(((a>>1)+(b>>1)+cy)>>7);
    return c;
    }
BYTE sub(BYTE a,BYTE b)
    {
    BYTE c=a-b;
    if (a<b) cy=1; else cy=0;
    return c;
    }
BYTE sbc(BYTE a,BYTE b)
    {
    BYTE c=a-b-cy;
    if (cy) { if (a<=b) cy=1; else cy=0; }
    else    { if (a< b) cy=1; else cy=0; }
    return c;
    }
void mul(BYTE &h,BYTE &l,BYTE a,BYTE b)
    {
    BYTE ah,al;
    h=0; l=0; ah=0; al=a;
    if ((a==0)||(b==0)) return;
    // long binary multiplication
    for (;b;b>>=1)
        {
        if (BYTE(b&1))
            {
            l=add(l,al);    // (h,l)+=(ah,al)
            h=adc(h,ah);
            }
        al=add(al,al);      // (ah,al)<<=1
        ah=adc(ah,ah);
        }
    }
void div(BYTE &ch,BYTE &cl,BYTE &r,BYTE ah,BYTE al,BYTE b)
    {
    BYTE bh,bl,sh,dh,dl,h,l;
    // init
    bh=0; bl=b; sh=0;   // (bh,bl) = b<<sh so it is >= (ah,al) without overflow
    ch=0; cl=0; r=0;    // results = 0
    dh=0; dl=1;         // (dh,dl) = 1<<sh
    if (!b) return;     // division by zero error
    if ((!ah)&&(!al)) return;   // division of zero
    for (;bh<128;)
        {
        if (( ah)&&(bh>=ah)) break;
        if ((!ah)&&(bl>=al)) break;
        bl=add(bl,bl);
        bh=adc(bh,bh);
        dl=add(dl,dl);
        dh=adc(dh,dh);
        sh++;
        }
    // long binary division
    for (;;)
        {
        l=sub(al,bl);   // (h,l) = (ah,al)-(bh,bl)
        h=sbc(ah,bh);
        if (cy==0)      // no overflow
            {
            al=l; ah=h;
            cl=add(cl,dl);  // increment result by (dh,dl)
            ch=adc(ch,dh);
            }
        else{           // overflow -> shoft right
            if (sh==0) break;
            sh--;
            bl>>=1;     // (bh,bl) >>= 1
            if (BYTE(bh&1)) bl|=128;
            bh>>=1;
            dl>>=1;     // (dh,dl) >>= 1
            if (BYTE(dh&1)) dl|=128;
            dh>>=1;
            }
        }
    r=al;       // remainder (low 8bit)
    }
//---------------------------------------------------------------------------
// print 16bit dec with 8bit arithmetics
//---------------------------------------------------------------------------
AnsiString prn16(BYTE h,BYTE l)
    {
    AnsiString s="";
    BYTE r; int i,j; char c;
    // divide by 10 and print the remainders
    for (;;)
        {
        if ((!h)&&(!l)) break;
        div(h,l,r,h,l,10);  // (h,l)=(h,l)/10; r=(h,l)%10;
        s+=char('0'+r);         // add digit to text
        }
    if (s=="") s="0";
    // reverse order
    i=1; j=s.Length();
    for (;i<j;i++,j--) { c=s[i]; s[i]=s[j]; s[j]=c; }
    return s;
    }
//---------------------------------------------------------------------------

我使用 VCL AnsiString 进行文本存储,您可以将其更改为任何字符串甚至 char[]。 您需要将整个数字分开,而不仅仅是 BYTE。看看div 函数是如何工作的。这里是 264 打印的最低有效位示例264%10...

a = 264 = 00000001 00001000 bin
b =  10 = 00000000 00001010 bin
d =   1 = 00000000 00000001 bin
// apply shift sh so b>=a 
a = 00000001 00001000 bin
b = 00000001 01000000 bin
d = 00000000 00100000 bin
sh = 5
// a-=b c+=d while a>=b
// a<b already so no change
a = 00000001 00001000 bin b = 00000001 01000000 bin c = 00000000 00000000 bin d = 00000000 00100000 bin
// shift right
b = 00000000 10100000 bin d = 00000000 00010000 bin sh = 4
// a-=b c+=d while a>=b
a = 00000000 01101000 bin c = 00000000 00010000 bin
// shift right
b = 00000000 01010000 bin d = 00000000 00001000 bin sh = 3
// a-=b c+=d while a>=b
a = 00000000 00011000 bin c = 00000000 00011000 bin
// shift right
b = 00000000 00101000 bin d = 00000000 00000100 bin sh = 2
b = 00000000 00010100 bin d = 00000000 00000010 bin sh = 1
// a-=b c+=d while a>=b
a = 00000000 00000100 bin c = 00000000 00011010 bin
// shift right
b = 00000000 00001010 bin d = 00000000 00000001 bin sh = 0
// a<b so stop a is remainder -> digit = 4
//now a=c and divide again from the start to get next digit ...

【讨论】:

  • 谢谢兄弟,这个答案真的很有帮助+1
  • 但是如果它是 8 位 alu 就可以了。它如何在屏幕上打印这个 16 位数字。我知道将它打印为十六进制数字非常容易,因为它只需要在内存中打印或存储 ah 和 al 的值,然后再将它们打印出来。但是它将如何将这个计算机无法理解的大 16 位数字转换为 1 个大数字,因为它只读取每个字节的信息字节,所以如果 16 位数字是 01 A0,即十进制的 416,最高数字是 8比特计算机能理解的是255,那么如果计算机超出了它的知识能力,如何打印出这个数字
  • @CosnotraLF 很容易,您只需在 16 位算术中连续除以 10,该算法由 8 位运算组成……以相反的顺序打印余数。 divmod 操作可以转换为易于实现的 +,- 操作集。对于更快的方法,请使用二进制除法(对于 div 和 shift 使用移位和减法,对于 mul 和/或基数方法使用添加...)有关更多信息,请参阅bigint hex/dec conversion in C++
  • @CosnotraLF 我一生中读过的关于这个主题的最好的书是这本Assembler a ZX Spectrum,但它是捷克语的(不确定是否存在任何英文翻译)它涵盖了用于 16 位运算的 8 位算术,打印数字,在低级别处理 IO 和 gfx 等等。这里是 PDF 版本:1st part2nd part
  • @CosnotraLF 添加了 [edit1] 与 264 示例和简单的 C++ 代码来执行此操作...您不需要首先做很多事情,您需要确保 8 位的东西正常工作然后才担心编程更高的东西。只需确保您的 ALU 具有您需要的所有功能(+、-、inc、dec、AND、OR、XOR、SHL、SHR、ROL、ROR)和标志Z,S,CY,AC 其余可以通过控制单元中的微码完成...
【解决方案2】:

将它们解释为 base-256。

>>> 18*256 + 52
4660

【讨论】:

    猜你喜欢
    • 2020-10-17
    • 1970-01-01
    • 2015-02-28
    • 2017-09-07
    • 1970-01-01
    • 2021-06-12
    • 2013-09-05
    • 1970-01-01
    • 2012-06-06
    相关资源
    最近更新 更多