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

Posted

技术标签:

【中文标题】ATmega128:加减 16 位数字(汇编)【英文标题】:ATmega128: Adding and subtracting 16-bit numbers (assembly) 【发布时间】:2012-02-09 20:58:01 【问题描述】:

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

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

【问题讨论】:

这是家庭作业吗?如果是这样,请将其标记为这样。是的,首先您需要加载一些寄存器,然后您需要将它们全部相加或相减。您总是可以退回到 add/adc 或 sub/sbc 来执行大量的位/字节加减。 如果我使用 add 或 adc,如何正确存储结果? 【参考方案1】:

带着铅笔和纸回到小学。如果我想添加 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 的指令集,上面发生的事情应该会跳出来。

编辑:

C 示例可能会有所帮助...(切换到十六进制)

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);

EDIT2:

我希望这不是家庭作业...

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

【讨论】:

感谢这篇文章。刚刚把我的头绕过去。【参考方案2】:

好吧,您实际上并没有发出任何添加指令。我无论如何都不是 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 中的低字节。

【讨论】:

嗯,没用.. 我只在三个寄存器中得到 0xFF。就像它被最大化或什么的? LDS 指令的规范说它只能在 16 到 31 的寄存器上正常工作。我相应地编辑了我的帖子。 不使用lds,而使用ldm呢?不确定 RAM ramstart 的分配位置,但考虑到您将数字和代码放在同一区域,它可能位于“程序内存”中。我的猜测是这就是为什么你得到 0xFF - SRAM 未初始化或不存在。【参考方案3】:

您的数据表有来自this 链接的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 位的。

【讨论】:

嗯.. 你在哪里加载 number2? ldm 我假设你的意思是 ldi? 将地址立即加载到 r0 中。将程序模式从 r0 加载到 r16,r17,r18,r19。所以我的意思是 ldm 指令。我很困惑如何通过寄存器从 RAM 中获取值,因为它们的长度只有 8 位。 啊,X/Y/Z 记录了 Z80 天。我的错。【参考方案4】:
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,高(编号 2) LDI R31,低(编号 2) 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 端口!

【讨论】:

以上是关于ATmega128:加减 16 位数字(汇编)的主要内容,如果未能解决你的问题,请参考以下文章

如何在汇编中将两个十六进制 128 位数字相乘

Atmel studio 汇编:8 位数字的平方根

如何访问 NEON 指令中的完整 128 位?

16位汇编第五讲各种指令详解第一讲

Atmel AVR 汇编器时间戳计数器

使用 ATmega16A 的 MAX5402 数字电位器的 SPI 配置