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

发布于 2025-01-03 23:17:06 字数 585 浏览 3 评论 0原文

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

这离我第一次使用任何类型的程序集已经不远了,我知道我做错了什么,但似乎不知道是什么。我错过了进位标志吗?

I'm working with a ATmega128 microcontroller and supposedly need to add two 16-bit numbers. I'm using AVR Studio and this is what I got so far:

.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

This is not far from the first time I'm using any type of assembly, I know I'm doing something wrong, but can't seem to figure out what. Am I missing the carry flag?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不喜欢何必死缠烂打 2025-01-10 23:17:06

带着铅笔和纸回到小学。如果我想加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

Back to gradeschool with pencil and paper. If I want to add 1234 and 5678

  1234
+ 5678
======

4+8 is 2 carry the 1

    1
  1234
+ 5678
======
     2    

and so on

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

the carry bit above the ones column is significant, it is called the carry in, and the carry
bit that leaves the leftmost column is carry out.

What if I only had paper wide enough to add two columns at a time?

 110 
  34 
+ 78 
======
  12

I start with the two lower sets of digits, and I require a zero as a carry in. I get a result 12 with a carry out.

Now I take that carry out, use it as a carry in for the next two digits. This adder I must be able to take a carry out from a prior add and use it as the carry in for this add.

 001
  12
+ 56
====
  69

When all is said and done I get 69 and 12, put those together I get 6912 but didnt need a full 4 digit adder to get there. You can repeat this forever or until you run out of memory, registers or clock cycles.

The avr may have other ways to solve the problem, but most processors at least have two forms of add and two forms of subtract so that you can cascade the adder to be as wide as you need. Examine the instruction set for the avr and what is going on above should jump out at you.

EDIT:

A C example might help...(switching to hex)

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:

I hope this is not a homework assignment...

ldi r20,0x12
ldi r21,0x34
ldi r22,0x56
ldi r23,0x78
add r21,r23
adc r20,r22

result is in r20 high byte, and r21 low byte

if you need to read from ram there are many ways, this assumes the 16 bit numbers are little endian

lds r0,0x100
lds r1,0x101
lds r2,0x102
lds r3,0x103
add r0,r2
adc r1,r3

r0 low half of result, r1 upper half.

or use one of the x,y,or z pointer registers

;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
对你而言 2025-01-10 23:17:06

好吧,你并没有真正发出任何附加指令。我无论如何都不是 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中。

Well, you're not really issuing any addition instruction. I'm not an AVR programmer in any way, but after a quick glance at the instruction set of the ATmega128, something like this seems much more correct. I'm assuming your assembler uses the Intel syntax and that the numbers are stored as 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

The result is therefore stored in r17:r16, e.g. the high byte in r17, and the low byte in r16.

枉心 2025-01-10 23:17:06

您的数据表具有来自此<的addadc /a> 链接。正如我上面猜测的,也许您需要使用程序内存加载、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 位。

Your datasheet has add and adc from this link. As I guessed above, perhaps you need to use program memory loads, ldm to get at your numbers.

Basically:



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...

Above code won't work and there is a working example...

If I'm understanding the context correctly with the post-increment ldm command and all registers are 8-bit.

北音执念 2025-01-10 23:17:06
ldi temp, low(number1)
sts number1, temp
ldi temp, high(number1)

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

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

LDI R30, HIGH(number1)
LDI R31, LOW(number1)

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

LD R16,Z+
LD R17.Z

现在 R16,R17 中有 16 位值
现在您必须对“number2”

LDI R30,HIGH(number2) 执行相同的操作
LDI R31,低(编号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 中的任何位置,这允许您运行多个进程,每个进程都有自己的堆栈。
但总的来说,与冯·诺依曼架构相比,所有哈佛架构处理器都很糟糕。
如果上帝保佑,我希望有人能制作一个基于 8085 或 Z80 的微控制器,带有片上 RAM 和闪存,让所有引脚都可以用作 I/O 端口!

ldi temp, low(number1)
sts number1, temp
ldi temp, high(number1)

The symbol "number1" has the value of an address, not the contents of that address.
Since you have placed "number1" at .ORG RAMSTART, which is probably 0100 Hex, then
low(number1) is equal to 00 and high(number1) is equal to 01

If you want the CONTENTS of the address "number1", you have to first get the address into a
16-bit address register, for example the Z register = (R30,R31) by the following

LDI R30, HIGH(number1)
LDI R31, LOW(number1)

Now the Z register can be used to address the VALUE in the address "number1" by:

LD R16,Z+
LD R17.Z

Now you have the 16 bit value in R16,R17
Now you have to do the same for "number2"

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

Now you have the second 16-bit number in R18, R19

Now you add them together with carry from the LSB to the MSB

ADD R19,R17 ;add the LSB's first
ADC R18,R16 ;then add the MSBs with the carry bit

The answer is now in R18,R19

Conclusion: The AVR has a really crass, inefficient instruction set. It has one advantage over the 8051, however: That is, the stack can be anywhere in RAM, which allows you to have multiple processes running, each with their own stacks.
But in general, all Harvard architecture processors SUCK compared to von Neumann architectures.
If only, please God I wish, somebody made a microcontroller based on the 8085 or Z80, with on-chip RAM and FLASH, leaving all the pins free to be I/O ports!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文