简体   繁体   English

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

[英]ATmega128: Adding and subtracting 16-bit numbers (assembly)

I'm working with a ATmega128 microcontroller and supposedly need to add two 16-bit numbers. 我正在使用ATmega128微控制器,可能需要添加两个16位数字。 I'm using AVR Studio and this is what I got so far: 我正在使用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

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? 我是否缺少进位标志?

Back to gradeschool with pencil and paper. 用铅笔和纸回到小学。 If I want to add 1234 and 5678 如果我想添加1234和5678

  1234
+ 5678
======

4+8 is 2 carry the 1 4 + 8是2携带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. “ ones”列上方的进位位有效,称为进位,而离开最左列的进位位被执行。

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. 我从两个低位数字开始,我需要零作为进位。得到带进位的结果12。

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. 说完所有内容,我得到69和12,将它们加在一起得到6912,但不需要完整的4位加法器即可到达那里。 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. avr可能有其他方法可以解决该问题,但是大多数处理器至少具有加法的两种形式和减法的两种形式,以便您可以将加法器级联为所需的宽度。 Examine the instruction set for the avr and what is going on above should jump out at you. 检查avr的指令集,上面发生的事情应该会跳出来。

EDIT: 编辑:

AC example might help...(switching to hex) 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);

EDIT2: 编辑2:

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 结果在r20高字节和r21低字节中

if you need to read from ram there are many ways, this assumes the 16 bit numbers are little endian 如果您需要从ram读取数据,则有很多方法,这是假定16位数字是小端的

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. r0结果的下半部分,r1结果的下半部分。

or use one of the x,y,or z pointer registers 或使用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

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. 我无论如何都不是AVR程序员,但是快速浏览一下ATmega128的指令集后,类似这样的事情似乎更加正确。 I'm assuming your assembler uses the Intel syntax and that the numbers are stored as Little Endian. 我假设您的汇编器使用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

The result is therefore stored in r17:r16 , eg the high byte in r17 , and the low byte in r16 . 因此,结果存储在r17:r16 ,例如r17的高字节, r16的低字节。

Your datasheet has add and adc from this link. 您的数据表中有链接的addadc As I guessed above, perhaps you need to use program memory loads, ldm to get at your numbers. 正如我在上面猜测的那样,也许您需要使用程序内存负载ldm来获取数量。

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. 如果我使用post-increment ldm命令正确理解了上下文,并且所有寄存器都是8位的。

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. 符号“ number1”具有地址值,而不是该地址的内容。 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 由于您已在.ORG RAMSTART处放置了“ number1”(可能为0100十六进制),因此low(number1)等于00,high(number1)等于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 如果要获取地址“ number1”的内容,则必须首先将该地址放入一个16位地址寄存器,例如Z寄存器=(R30,R31),如下所示

LDI R30, HIGH(number1) LDI R31, LOW(number1) LDI R30,HIGH(数字1)LDI R31,LOW(数字1)

Now the Z register can be used to address the VALUE in the address "number1" by: 现在,可以通过以下方式使用Z寄存器来寻址地址“ number1”中的VALUE:

LD R16,Z+ LD R17.Z 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" 现在您在R16,R17中具有16位值现在您必须对“ number2”执行相同的操作

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

Now you have the second 16-bit number in R18, R19 现在您有了R18,R19中的第二个16位数字

Now you add them together with carry from the LSB to the MSB 现在,您将它们与从LSB到MSB的进位加在一起

ADD R19,R17 ;add the LSB's first ADC R18,R16 ;then add the MSBs with the carry bit ADD R19,R17;添加LSB的第一个ADC R18,R16;然后将MSB加进位

The answer is now in R18,R19 答案现在在R18,R19中

Conclusion: The AVR has a really crass, inefficient instruction set. 结论:AVR具有真正的无聊,低效的指令集。 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. 但是,它比8051具有一个优势:堆栈可以在RAM中的任何位置,这使您可以运行多个进程,每个进程都有自己的堆栈。 But in general, all Harvard architecture processors SUCK compared to von Neumann architectures. 但总的来说,与冯·诺依曼(von Neumann)架构相比,所有哈佛架构处理器都SUCK。 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! 如果只有的话,请上帝保佑我,有人用8085或Z80制作了一个微控制器,带有片上RAM和FLASH,所有引脚都可以自由用作I / O端口!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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