繁体   English   中英

ATmega128:加减16位数字(汇编)

[英]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的低字节。

您的数据表中有链接的addadc 正如我在上面猜测的那样,也许您需要使用程序内存负载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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM