共有回帖数 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 回复
Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号
意见反馈 |
关于直线 |
版权声明 |
会员须知