[英]ATmega128: Adding and subtracting 16-bit numbers (assembly)
我正在使用ATmega128微控制器,可能需要添加两个16位数字。 我正在使用AVR Studio,到目前为止,这是我得到的:
.include "m128def.inc";
.equ ramstart = 0x100
.def temp = r16
.dseg
.org ramstart
number1: .byte 2
number2: .byte 2
.cseg
.org 0
rjmp start
start:
; number1 := 0x7856
ldi temp, low(number1)
sts number1, temp
ldi temp, high(number1)
sts number1+1, temp
; number2 := 0x34B2
lds temp, number1
sts number2, temp
lds temp, number1+1
sts number2+1, temp
slutt:
rjmp slutt
这与我第一次使用任何类型的程序集相距不远,我知道我做错了什么,但似乎无法弄清楚是什么。 我是否缺少进位标志?
用铅笔和纸回到小学。 如果我想添加1234和5678
1234
+ 5678
======
4 + 8是2携带1
1
1234
+ 5678
======
2
等等
00110 <-- carry bits
1234 <-- first operand
+ 5678 <-- second operand
======
6912
“ ones”列上方的进位位有效,称为进位,而离开最左列的进位位被执行。
如果我的纸张宽度足够一次只能添加两列怎么办?
110
34
+ 78
======
12
我从两个低位数字开始,我需要零作为进位。得到带进位的结果12。
现在,我进行该进位,将其用作下两位数字的进位。 这个加法器我必须能够从先前的加法中进行进位,并将其用作此加法的进位。
001
12
+ 56
====
69
说完所有内容,我得到69和12,将它们加在一起得到6912,但不需要完整的4位加法器即可到达那里。 您可以永久重复此操作,或者直到内存,寄存器或时钟周期用完为止。
avr可能有其他方法可以解决该问题,但是大多数处理器至少具有加法的两种形式和减法的两种形式,以便您可以将加法器级联为所需的宽度。 检查avr的指令集,上面发生的事情应该会跳出来。
编辑:
AC示例可能会有所帮助...(切换到十六进制)
unsigned int a,b,c,d,cin,cout,x,y;
a=0x12; b=0x34;
c=0x56; d=0x78;
x=b+d; //dont want a carry in or assume it is zero
cout=x&0x100;
if(cout) cin=1; else cin=0;
y=a+c+cin; //need the carry out on the prior add as the carry in here
x&=0xFF;
y&=0xFF;
printf("0x%02X%02X\n",y,x);
编辑2:
我希望这不是一项家庭作业...
ldi r20,0x12
ldi r21,0x34
ldi r22,0x56
ldi r23,0x78
add r21,r23
adc r20,r22
结果在r20高字节和r21低字节中
如果您需要从ram读取数据,则有很多方法,这是假定16位数字是小端的
lds r0,0x100
lds r1,0x101
lds r2,0x102
lds r3,0x103
add r0,r2
adc r1,r3
r0结果的下半部分,r1结果的下半部分。
或使用x,y或z指针寄存器之一
;put 0x0100 in Z
ldi r30,0x00
ldi r31,0x01
ld r0,z+
ld r1,z+
ld r2,z+
ld r3,z+
add r0,r2
adc r1,r3
好吧,您实际上并没有发出任何附加说明。 我无论如何都不是AVR程序员,但是快速浏览一下ATmega128的指令集后,类似这样的事情似乎更加正确。 我假设您的汇编器使用Intel语法,并且数字存储为Little Endian。
lds r16, number1 ; low byte of number1
lds r17, number2 ; low byte of number2
add r16, r17 ; number1 += number2
lds r17, number1+1 ; high byte of number1
lds r18, number2+1 ; high byte of number2
adc r17, r18 ; add the high bytes including the carry flag generated by the "add" instruction above
因此,结果存储在r17:r16
,例如r17
的高字节, r16
的低字节。
您的数据表中有此链接的add
和adc
。 正如我在上面猜测的那样,也许您需要使用程序内存负载ldm来获取数量。
基本上:
ldi r0, number1 ; get low address of number 1 in a register. ldm r16, r0+ ; low-byte of number 1 - inc pointer after each read with r0+ ldm r17, r0+ ; high-byte of number 1 ldm r18, r0+ ; low-byte of number 2 ldm r19, r0+ ; high-byte of number 2 add r16, r18 ; add low bytes adc r17, r19 ; add hi-bytes with carry ; r16/r17 now holds the sum of the number1 and number2 as a 16-bit number. ; Store to RAM or whatever you want with them. ; Note, you may have to push/pop registers depending on your system requirements...
上面的代码不起作用,并且有一个有效的示例...
如果我使用post-increment ldm命令正确理解了上下文,并且所有寄存器都是8位的。
ldi temp, low(number1)
sts number1, temp
ldi temp, high(number1)
符号“ number1”具有地址值,而不是该地址的内容。 由于您已在.ORG RAMSTART处放置了“ number1”(可能为0100十六进制),因此low(number1)等于00,high(number1)等于01
如果要获取地址“ number1”的内容,则必须首先将该地址放入一个16位地址寄存器,例如Z寄存器=(R30,R31),如下所示
LDI R30,HIGH(数字1)LDI R31,LOW(数字1)
现在,可以通过以下方式使用Z寄存器来寻址地址“ number1”中的VALUE:
LD R16,Z + LD R17.Z
现在您在R16,R17中具有16位值现在您必须对“ number2”执行相同的操作
LDI R30,HIGH(数字2)LDI R31,LOW(数字2)LD R18,Z + LD R19.Z
现在您有了R18,R19中的第二个16位数字
现在,您将它们与从LSB到MSB的进位加在一起
ADD R19,R17;添加LSB的第一个ADC R18,R16;然后将MSB加进位
答案现在在R18,R19中
结论:AVR具有真正的无聊,低效的指令集。 但是,它比8051具有一个优势:堆栈可以在RAM中的任何位置,这使您可以运行多个进程,每个进程都有自己的堆栈。 但总的来说,与冯·诺依曼(von Neumann)架构相比,所有哈佛架构处理器都SUCK。 如果只有的话,请上帝保佑我,有人用8085或Z80制作了一个微控制器,带有片上RAM和FLASH,所有引脚都可以自由用作I / O端口!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.