3

我正在使用 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

这与我第一次使用任何类型的程序集相距不远,我知道我做错了什么,但似乎无法弄清楚是什么。我错过了进位标志吗?

4

4 回答 4

9

用铅笔和纸回到小学。如果我想添加 1234 和 5678

  1234
+ 5678
======

4+8 是 2 携带 1

    1
  1234
+ 5678
======
     2    

等等

 00110 <-- carry bits
  1234 <-- first operand
+ 5678 <-- second operand
======
  6912

个位列上方的进位位是有效的,称为进位,离开最左侧列的进位位是进位。

如果我只有足够宽的纸张一次添加两列怎么办?

 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
于 2012-02-09T21:26:56.917 回答
1

您的数据表具有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...

上面的代码不起作用,并且有一个工作示例...

如果我使用后增量 ldm 命令正确理解上下文并且所有寄存器都是 8 位的。

于 2012-02-09T21:39:05.700 回答
1

好吧,你并没有真正发出任何附加指令。无论如何,我都不是 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例如高字节 inr17和低字节 in 中r16

于 2012-02-09T21:06:54.197 回答
0
ldi temp, low(number1)
sts number1, temp
ldi temp, high(number1)

符号“number1”具有地址的值,而不是该地址的内容。由于您在 .ORG RAMSTART 中放置了“number1”,这可能是 0100 Hex,因此 low(number1) 等于 00,high(number1) 等于 01

如果您想要地址“number1”的内容,您必须首先将地址放入一个 16 位地址寄存器,例如 Z 寄存器 = (R30,R31) 通过以下方式

LDI R30,高(编号 1) LDI R31,低(编号 1)

现在 Z 寄存器可用于通过以下方式寻址地址“number1”中的 VALUE:

LD R16,Z+ LD R17.Z

现在你在 R16,R17 中有 16 位值现在你必须对“number2”做同样的事情

LDI R30, HIGH(number2) LDI R31, LOW(number2) LD R18,Z+ LD R19.Z

现在你有了 R18 中的第二个 16 位数字,R19

现在将它们与从 LSB 到 MSB 的进位相加

ADD R19,R17 ;添加 LSB 的第一个 ADC R18,R16 ;然后添加带进位的 MSB

答案现在在 R18,R19

结论:AVR 有一个非常粗鲁、低效的指令集。但是,它与 8051 相比有一个优势:即堆栈可以位于 RAM 中的任何位置,这允许您运行多个进程,每个进程都有自己的堆栈。但总的来说,与冯诺依曼架构相比,所有哈佛架构处理器都很糟糕。如果只是,上帝,我希望有人制造了一个基于 8085 或 Z80 的微控制器,带有片上 RAM 和 FLASH,让所有引脚都空闲为 I/O 端口!

于 2012-12-07T06:53:43.880 回答