签到

05月08日
尚未签到

共有回帖数 0

    花好月圆

    等级:
    在机器中模拟乘、除法的运算操作(这里没有关于除法的部分)

    因为最近醉心于研究电子电路,写这个是突然间的兴趣所至。
    同时也可以让想了解机器运行的本质的人做参考。
    做为计算机,它和数学思维密切相关。

    这里有几个算式:
    [b代表2进制]
    10     =   1010b  
    10 * 2 =  10100b
    10 * 3 =  11110b
    10 * 4 = 101000b

    首先分析:
    (分析过程中全以无符号数做分析对象.2进制数做操作对象)

    1、每左移一位等价于乘2,右移一位等价于除2;

    2、可以将移位(等价于乘2)的动作应用到更广的范围(乘与2以上的数);
             
         分析和验证:
             (13 * 27)
             13 =  1101
             27 = 11011
         先通过查看2进制位的标志来分析27
             [27 = 11011]          
         因为数中:
             [11011]
                  ^bit 0 = 1
             [11011]
                 ^ bit 1 = 1  
             [11011]
                ^  bit 2 = 0
             [11011]
               ^   bit 3 = 1
             [11011]
              ^    bit 4 = 1
             27这个数里,最高位为bit4,其中bit0.bit1.bit3.bit4为1(逻辑真),bit2为0(逻辑假)。
             对13*27里根据对应位的值来对乘数(13)进行移位操作。  
             即若bit 1 为真就[左移1次] 若bit 3 为真就[左移3次],
             每次移位后和前一次移位的结果相加,直到位被检查完为止。

             另外bit 0 为真时移动0次的结果等于原数,由此可以区别于通常的乘法方式,
             这里只进行移位操作,可以把每左移一次理解为乘2,这样就可以抽象出算法来。

             抽象和简化:

             通过分析可以得到这样一个简单的方法:
           a*b[n] = (a*2^0 *b[0]) + (a*2^1 *b[1]) + (a*2^2 *b[2]) + (a*2^3 *b[3]) +... + (a*2^n * b[n])
             其中b[n]里n是位数,每个b[n]代表1位2进制数。

             
             这样就可以得到一个具有普遍意义的方法,再此方法上还可以进一步简化和提高效率
             (如先比较13 27 里那个大,再把大的数当作移位对象,小的数当作移位条件).

             虽然很简单,但用程序语言来描述也许能让人看得明白些.下面是一段具有和机器特性相似的代码。
             用c语言描述(只是用于验证、描述,没有实用意义)就是:
    //-----------
    #include stdio.h
    #define ulong unsigned long

       int main()
       {   ulong a,b;
           ulong c,n;
           ulong t;
           int i;
           t = 1;
           c = 0;
           n = sizeof(ulong) 3;  //n = sizeof(ulong) * 8        
       
           printf("%s %dn","bit length",n);   //可用位长度(能存储结果的长度)
           printf("%sn","input:");
           scanf("%d %d",&a,&b);
           printf("%d %s %d %s",a," * ",b," = ");

           i = 0;
           for(i;in;i++)        //扫描前面给的位长度,这里是32
           {        
               if(b&(t  i))    // b and t   (判断该位是否为真(1),有乘法的特性)        
               {   c += a  i;  //a向左移位i次,结果放入c进行累积
               }
           }
           printf("%d",c);
       }

    //-----------
    在debug里实现的小片段
    ------------
    C:GCCTESTdebug testm1.com                                          
    -u100 13a                                                              
    0CAE:0100 33C0          XOR     AX,AX                                  
    0CAE:0102 A33A01        MOV     [013A],AX                              
    0CAE:0105 B81E00        MOV     AX,001E            ;//[十进制]    30                                
    0CAE:0108 BBC801        MOV     BX,01C8            ;//[十进制]   456     // 30 * 456                
    0CAE:010B 891E3801      MOV     [0138],BX                                                          
    0CAE:010F 32C9          XOR     CL,CL                                                              
    0CAE:0111 8BD0          MOV     DX,AX              ;//dx 置入  30                                  
    0CAE:0113 52            PUSH    DX                                                                  
    0CAE:0114 83E201        AND     DX,+01             ;//从这里开始,反复执行   //比较dx最低位是否为0                                                
    0CAE:0117 740A          JZ      0123               ;//为0就跳过处理部分进行下次比较的初始化  
    0CAE:0119 D3E3          SHL     BX,CL              ;//对bx中的数左移cl 位                          
    0CAE:011B A13A01        MOV     AX,[013A]          ;//上次的结果取给AX                              
    0CAE:011E 03C3          ADD     AX,BX              ;//上次的结果和当前结果相加                      
    0CAE:0120 A33A01        MOV     [013A],AX          ;//保存这次的结果                                
    0CAE:0123 5A            POP     DX                 ;//取出剩余                                      
    0CAE:0124 D1EA          SHR     DX,1               ;//右移1位        
    0CAE:0126 FEC1          INC     CL                 ;//当前对应位                    
    0CAE:0128 83FA00        CMP     DX,+00             ;//比较dx是否为0                    
    0CAE:012B 7607          JBE     0134               ;//小于等于0就跳到结尾                    
    0CAE:012D 8B1E3801      MOV     BX,[0138]                              
    0CAE:0131 52            PUSH    DX                                    
    0CAE:0132 EBE0          JMP     0114                                  
    0CAE:0134 A13A01        MOV     AX,[013A]          ;//结束前把结果取给AX[这句可以省略]                    
    0CAE:0137 C3            RET                                            
    0CAE:0138 0000          ADD     [BX+SI],AL                            
    0CAE:013A 0000          ADD     [BX+SI],AL                            
    -g=100 137                                         ;//运行,并设置断点为137 [ret]命令处                    
                                                                         
    AX=3570  BX=1C80  CX=0005  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
    DS=0CAE  ES=0CAE  SS=0CAE  CS=0CAE  IP=0137   NV UP EI PL ZR NA PE NC  
    0CAE:0137 C3            RET
    ------------
    AX里就是结果了。
    AX = 3570 (十六进制)  转换为十进制是 13680  用windows里的计算器可以进行转换,马上验证一下
    13680 / 30 = 456
    456 * 30 = 13680
                                             
    ,我也有一个类似的程序,也是用汇编语言写的,是一个带用户截面的运算器,可以计算加法,减法,乘法,除法,但是只能进行两位数的计算,你如果有空,帮我改进一下,让它的功能更加完善
    ;*****************************************************************
    ;带用户界面的计算器----可以计算加法,减法,乘法和除法,但是不能计算
    ;带括号的四则运算,运行时,先输入两个两位数,再输入运算符号,就能输
    ;出计算结果,例如:计算65+79,先输入"65""79",再输入“+?,但是该程序
    ;有一个毛病,如果要计算一位数,必须输入两个数字,如果要输入9,必须输
    ;入09才行
    ;******************************************************************
    CURS MACRO A,B
    MOV AH,2 ;置光标位置
    MOV BH,0 ;页号为0
    MOV DH,A ;设置光标的坐标为(A.B)
    MOV DL,B
    INT 10H ;BIOS调用
     
    ENDM
    ;-------------------------------
    DISPMSG MACRO MESSAGE
    LEA DX,MESSAGE ;传递MESSAGE的偏移地址
    MOV AH,9 ;显示字符串
    INT 21H ;DOS调用

    ENDM
    ;----------------------------------

    CLEAR MACRO COLOR ;清屏
    MOV AH,6 ;屏幕初始化
    MOV AL,0 ;页号
    MOV CH,0 ;设置左上角的坐标为(0,0)
    MOV CL,0
    MOV DL,100 ;设置右上角的坐标为(100,100)
    MOV DH,100
    MOV BH,COLOR ;卷入行属性
    INT 10H ;BIOS调用
    ENDM

    .MODEL SMALL
    .STACK
    .DATA ;数据段定义
    MSG0 DB '* * * * * * * * * * * * * * * * * * * *',0DH,0AH ;,0dh,0ah,0dh,0ah
    DB ' * *',0DH,0AH
    ;db '* *',0dh,0ah
    ;db '* *',0dh,0ah
    DB ' * --------COUNTER-------- *',0DH,0AH ;,0dh,0ah,0dh,0ah
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * DATA1:'
    DB '_______________ *',0DH,0AH ;,0dh,0ah,0dh,0ah,0dh
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * *',0DH
    DB ' * DATA2:'
    DB '_______________ *',0DH,0AH ;,0dh,0ah,0dh,0ah,0dh
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * *',0DH
    DB ' * RESULT:'
    DB '_______________ *',0DH,0AH,0DH ;,0ah,0dh,0ah,0dh,0ah,0dh,0ah,0dh,0ah,0dh
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * *',0DH,0AH
    DB ' * * * * * * * * * * * * * * * * * * * *','$'
    MSG1 DB 'choose +,-,*,/','$'
    MSG2 DB 'you choose wrong','$',0AH,0DH
    MSG3 DB 'Press ESC to Exit!',0DH,0AH,'$'
    MSG4 DB 'Press any key to continue!',0DH,0AH,'$'
    MSG5 DB '-','$'
    MSG6 DB '.','$'
    MSG7 DB 'error input!','$'
    MSG8 DB 'data2 is wrong!','$'
    DATA1 DW ?
    DATA2 DW ?
    .CODE ;代码段从这里开始
    ;--------------------------------------------
    DEC_DIV PROC NEAR
    CWD ;将字转换为双字,AX内容符号扩展到DX
    DIV CX
    PUSH DX
    MOV DL,AL
    ADD DL,30H ;将不是数字的字符转换为数字
    MOV AH,2 ;显示输出
    INT 21H ;DOS调用
    POP DX
    MOV AX,DX
    RET
     
    DEC_DIV ENDP
    ;------------------------------------------
    CHANGE PROC NEAR ;该子程序的功能:将AX中存储的十六进制数转换为十进制数
    CMP AX,1000
    JAE C1 ;AX≥1000则转移
    CMP AX,100
    JAE C2 ;AX≥100则转移
    CMP AX,10
    JAE C3 ;AX≥10则转移
    JMP C4 ;AX<10

    C1: MOV CX,1000D
    CALL DEC_DIV

    C2: MOV CX,100D
    CALL DEC_DIV

    C3: MOV CX,10D
    CALL DEC_DIV

    C4: MOV CX,1D
    CALL DEC_DIV
    RET
    CHANGE ENDP
    ;-------------------------------
    BEGIN PROC NEAR
     
    BACK: CLEAR 1EH
    CURS 2,20 ;设置光标为(2.20)
    DISPMSG MSG0 ;显示用户界面
    CURS 8,41 ;设置光标为(8,41) ,第一行
    MOV BX,0
    MOV CX,2
    NEWCHAR1:
    MOV AH,1 ;键盘输入并回显
    INT 21H ;DOS调用
    CMP AL,30H ;检查输入的字符是不是数字
    JAE J1 ;如果不是,则转移至J1
    JMP D2 ;是,转移到D2执行

    J1: CMP AL,39H ; 与9的ASCII侣氡
    JBE J2 ;如果输入的字符是0-9,则转移至J2
    JMP D2 ;如果不是数字,则转移至D2
    J2: SUB AL,30H
    CBW ;AL的内容符号扩展到AH
    XCHG AX,BX
    MOV DI,10
    MUL DI
    XCHG AX,BX
    ADD BX,AX
    LOOP NEWCHAR1
    MOV DATA1,BX
    CURS 11,41 ;设置光标位为(11,40),第二行
    MOV BX,0
    MOV CX,2
    NEWCHAR2:
    MOV AH,1 ;键盘输入并回显
    INT 21H ;DOS调用
    CMP AL,30H ;与0的ASCII码比较
    JAE J3 ;如果输入数字大于等于0则跳转至J3
    JMP D2
    J3: CMP AL,39H ;与9比较
    JBE J4 ;如果小于或等于9则跳转
    JMP D2 ;如果输入不是数字则跳转
    J4: SUB AL,30H ;如果输入不是0-9,则转化成相应的字符
    CBW ;将AL符号扩展到AH
    XCHG AX,BX
    MOV DI,10
    MUL DI
    XCHG AX,BX
    ADD BX,AX
    LOOP NEWCHAR2
    MOV DATA2,BX
    MOV AX,DATA1
    MOV BX,DATA2
    PUSH AX
    PUSH BX
    CURS 15,33 ;设置光标的位置为(15.33),第三行
    DISPMSG MSG1 ;显示提示信息
    MOV AH,1 ;接受键盘输入并回显
    INT 21H ;DOS调用
    CMP AL,'+'
    JE ADDI ;如果运算符为+,则计算两数之和
    CMP AL,'-'
    JE SUBT ;如果运算符为-,则计算两数之差
    CMP AL,'*'
    JE MULT ;如果运算符为*,则计算两数之积
    CMP AL,'/'
    JE DIVI ;如果运算符为/,则计算两数之商
    CURS 16,25 ;设置光标为(16,25)
    DISPMSG MSG2 ;在屏幕上输出提示信息
    JMP D1

    ;----------------------------------------
    ;ADD THE TWO DATAS  
    ADDI: POP BX
    POP AX
    ADD AX,BX ;计算两数字之和
    PUSH AX
    CURS 14,41 ;设置光标位置为(14,41)
    POP AX
    CALL CHANGE ;将结果转换为十进制数
    JMP D1
    ;---------------------------------------
    ;SUBTRACT THE TWO DATAS
    SUBT: POP BX
    POP AX
    CMP AX,BX ;比较两数大小
    JAE D0 ;如果AX=BX则跳转
    XCHG AX,BX
    PUSH AX
    PUSH BX
    CURS 14,40 ;设置光标位置为(14,40)
    DISPMSG MSG5 ;在屏幕上显示负号
    POP BX
    POP AX
    D0: SUB AX,BX ;计算两数之差
    PUSH AX
    CURS 14,41 ;设置光标位置为(14,41)
    POP AX
    CALL CHANGE ;将结果转换为十进制数
    JMP D1
    ;---------------------------------------
    ;MULTIPLY THE TWO DATAS  
    MULT: POP BX
    POP AX
    MUL BX ;计算两数之积
    PUSH AX
    CURS 14,41 ;设置光标位置为(14,41)
    POP AX
    CALL CHANGE ;将结果转换为十进制数
    JMP D1
    ;---------------------------------------
    ;DIVIDE THE TWO DATAS
    DIVI: POP BX
    POP AX
    CMP BX,0 ;判断除数是否为0
    JE ERROR ;如果是0则跳转
    CWD
    DIV BX ;计算两数之商
    PUSH DX
    PUSH AX
    CURS 14,41 ;设置光标位置为(14,41)
    POP AX

    CALL CHANGE ;将十六进制数转换为十进制数
    POP DX
    CMP DX,0
    JNE LOP ;如果DX不等于0则跳转
    JMP D1

    LOP: MOV CX,6 ;设置精确度为小数点后6位
    PUSH DX
    DISPMSG MSG6 ;输出小数点
    POP DX

    CON: PUSH BX
    MOV AX,DX
    MOV BX,10
    MUL BX
    POP BX
    CWD ;将AX的内容符号扩展到双字
    DIV BX ;计算小数点后每一位的数字
    PUSH DX
    PUSH CX
    CALL CHANGE ;将十六进制数转化为十进制数
    POP CX
    POP DX
    CMP DX,0 ;判断是否已经计算到小数点后第六位
    JE D1 ;如果是,则跳转
    LOOP CON ;如果不是,就继续计算
    ERROR: CURS 15,33 ;设置光标位置为(15,33)
    DISPMSG MSG8 ;提示出错

    D1: RET
    D2: PUSH AX
    PUSH BX
    CURS 15,33 ;设置光标位置为(15,33)
    DISPMSG MSG7 ;提示输入出错
    POP BX
    POP AX
    RET

    BEGIN ENDP
    ;----------------------------------------
    MAIN PROC NEAR
     
    MOV AX,@DATA
    MOV DS,AX
    MOV ES,AX
    PUSH DS
    XOR AX,AX
    PUSH AX
    MOV DATA2,0 ;将缓冲区清空
    MOV DATA1,0
    AGAIN:
    CALL BEGIN
    CURS 16,23 ;设置光标位置为(16.23)
    DISPMSG MSG3 ;在屏幕上输出提示信息,提示退出的方法
    CURS 17,23 ;设置光标位置为(17.23)
    DISPMSG MSG4 ;在屏幕上输出提示信息,提示继续的方法
    MOV AH,7 ;键盘输入,无回显
    INT 21H ;DOS调用
    CMP AL,27 ;判断输入的是否为ESC
    JNE AGAIN ;如果不是就继续执行以上运算
    EXIT: MOV AX,4C00H ;如果是就退出系统
    INT 21H ;DOS调用
    MAIN ENDP
    END MAIN

    楼主 2016-02-04 17:55 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知