C语言中,程序员所定义的每一个变量,不管是基本类型(char,unsigned char,int unsigned int,short,unsigned short,long,unsigned long,double)的变量还是构造类型(数组,struct,enum,union,链表等)的变量,在编译之后都安排在了内存中的特定存储区域里面了, 每一个变量都对应特定的内存空间。
汇编语言中,根本就没有变量的说法,程序员所面对的就是赤裸裸的存储空间,可能是RAM,可能是EEPROM,还可能是FLASH,程序员需要自己给这些存储空间命名(相对于C语言,汇编语言完全需要自己分配内存),然后直接进行访问(读操作或者写操作)。例如在RAM空间中给地址为$100的存储单元命名,然后清空它的值,接着把I2C数据寄存器中的值读取到该存储单元中,接着再对该值自加1,最后将它的值保存到EEPROM的某个存储单元和FLASH的某个存储单元中。代码如下:
stm8/
#include "mapping.inc"
#include "stm8s103f.inc"
;ram的存储空间为$0000~$03FF
;在ram存储空间中地址为$100的存储单元命名为data_ram
data_ram EQU $100 ;用data_ram代表地址为$100的存储单元
;eeprom的存储空间为$4000~$427F
;在eeprom存储空间中地址为$4001的存储单元命名为data_eeprom
data_eeprom EQU $4001
;flash的存储空间为$8080~9FFF
;在flash存储空间中地址为$9000的存储单元命名为data_flash
data_flash EQU $9000
; I2C Bus Interface (I2C) at 0x5210
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.I2C_CR1 DS.B 1 ; I2C control register 1
.I2C_CR2 DS.B 1 ; I2C control register 2
.I2C_FREQR DS.B 1 ; I2C frequency register
.I2C_OARL DS.B 1 ; I2C Own address register low
.I2C_OARH DS.B 1 ; I2C Own address register high
reserved13 DS.B 1 ; unused
.I2C_DR DS.B 1 ; I2C data register
; Flash at 0x505a
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.FLASH_CR1 DS.B 1 ; Flash control register 1
.FLASH_CR2 DS.B 1 ; Flash control register 2
.FLASH_NCR2 DS.B 1 ; Flash complementary control register 2
.FLASH_FPR DS.B 1 ; Flash protection register
.FLASH_NFPR DS.B 1 ; Flash complementary protection register
.FLASH_IAPSR DS.B 1 ; Flash in-application programming status register
reserved2 DS.B 2 ; unused
.FLASH_PUKR DS.B 1 ; Flash Program memory unprotection register
reserved3 DS.B 1 ; unused
.FLASH_DUKR DS.B 1 ; Data EEPROM unprotection register
reserved4 DS.B 59 ; unused
; Independent Watchdog (IWDG) at 0x50e0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.IWDG_KR DS.B 1 ; IWDG Key Register
.IWDG_PR DS.B 1 ; IWDG Prescaler Register
.IWDG_RLR DS.B 1 ; IWDG Reload Register
reserved10 DS.B 13 ; unused
;----------------------------------------
;----------------------------------------
segment 'rom'
main.l
CLR data_ram ;72 5F 01 00
LD A,I2C_DR ;C6 52 16
LD data_ram,A ;C7 01 00
INC data_ram ;72 5C 01 00
CALL UnkeyprocessEep
CALL unkeyprocessFla
JRA main ;20 XX/20 A8
TAB1.L
DC.B $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$0B,$0C,$0D,$0E,$0F
UnkeyprocessEep.L
MOV FLASH_DUKR,#$AE ;35 AE 50 64
NOP ;9D
MOV FLASH_DUKR,#$56 ;35 56 50 64
MOV IWDG_KR,#$AA ;35 AA 50 E0
BTJF FLASH_IAPSR,#3,UnkeyprocessEep ;72 0n MS LS XX /72 07 50 5F EE
UnkeyprocessendEep.L
LD A,data_ram ;C6 01 00
LD data_eeprom,A ;C7 40 01
MOV IWDG_KR,#$AA ;35 AA 50 E0
BTJF FLASH_IAPSR,#2,UnkeyprocessendEep ;72 05 50 5F F1
RET
TAB2.L
DC.B $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$AA,$BB,$CC,$DD,$EE,$FF
unkeyprocessFla.L
MOV FLASH_DUKR,#$AE ;35 AE 50 64
NOP ;9D
MOV FLASH_DUKR,#$56 ;35 56 50 64
MOV IWDG_KR,#$AA ;35 AA 50 E0
BTJF FLASH_IAPSR,#3,unkeyprocessFla ;72 07 50 5F EE
UnkeyprocessendFla.L
LD A,data_ram ;C6 01 00
LD data_flash,A ;C7 90 00
MOV IWDG_KR,#$AA ;35 AA 50 E0
BTJF FLASH_IAPSR,#2,UnkeyprocessendFla ;72 07 50 5F F1
LD data_flash,A ;C7 90 00
RET
interrupt NonHandledInterrupt
NonHandledInterrupt.l
iret ;80
segment 'vectit'
dc.l {$82000000+main} ; reset
dc.l {$82000000+NonHandledInterrupt} ; trap
dc.l {$82000000+NonHandledInterrupt} ; irq0
dc.l {$82000000+NonHandledInterrupt} ; irq1
dc.l {$82000000+NonHandledInterrupt} ; irq2
dc.l {$82000000+NonHandledInterrupt} ; irq3
dc.l {$82000000+NonHandledInterrupt} ; irq4
dc.l {$82000000+NonHandledInterrupt} ; irq5
dc.l {$82000000+NonHandledInterrupt} ; irq6
dc.l {$82000000+NonHandledInterrupt} ; irq7
dc.l {$82000000+NonHandledInterrupt} ; irq8
dc.l {$82000000+NonHandledInterrupt} ; irq9
dc.l {$82000000+NonHandledInterrupt} ; irq10
dc.l {$82000000+NonHandledInterrupt} ; irq11
dc.l {$82000000+NonHandledInterrupt} ; irq12
dc.l {$82000000+NonHandledInterrupt} ; irq13
dc.l {$82000000+NonHandledInterrupt} ; irq14
dc.l {$82000000+NonHandledInterrupt} ; irq15
dc.l {$82000000+NonHandledInterrupt} ; irq16
dc.l {$82000000+NonHandledInterrupt} ; irq17
dc.l {$82000000+NonHandledInterrupt} ; irq18
dc.l {$82000000+NonHandledInterrupt} ; irq19
dc.l {$82000000+NonHandledInterrupt} ; irq20
dc.l {$82000000+NonHandledInterrupt} ; irq21
dc.l {$82000000+NonHandledInterrupt} ; irq22
dc.l {$82000000+NonHandledInterrupt} ; irq23
dc.l {$82000000+NonHandledInterrupt} ; irq24
dc.l {$82000000+NonHandledInterrupt} ; irq25
dc.l {$82000000+NonHandledInterrupt} ; irq26
dc.l {$82000000+NonHandledInterrupt} ; irq27
dc.l {$82000000+NonHandledInterrupt} ; irq28
dc.l {$82000000+NonHandledInterrupt} ; irq29
end
上面的汇编代码被汇编器编译为如下的二进制机器码:
最上面地址为8000~807F的区域为中断向量表;下边最左边绿框为对应单片机FLASH的地址;中间对应真正的机器码;右边框对应机器码的ASCLL码值。
我们拿data_ram为例进行说明,站在C语言的角度来讲,data_ram其实就是一个变量,CLR data_ram其实就是将该变量清零;但是站在汇编语言角度来讲,它就代表了一个地址,这个地址就是0x0100,于是CLR data_ram最终生成的机器码就是72 5F 01 00,单片机CPU取出这个指令后就会把地址0x0100处的存储单元清零。另外为了说明常数是怎么存储的,我拿下面两个常熟表进行说明:
TAB1.L
DC.B $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$0B,$0C,$0D,$0E,$0F
TAB2.L
DC.B $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$AA,$BB,$CC,$DD,$EE,$FF
这两个表的数值直接跟代码一起混合放在了代码存储区,由于CPU执行代码完全是根据PC寄存器指定的地址进行的,而PC寄存器是不会指向上面的这两个表的,也就是说上面这两个表是不会当做代码执行的,比如CALL UnkeyprocessEep这条指令中标号UnkeyprocessEep就会跳过TAB1,而直接找到子程序UnkeyprocessEep处。
汇编器生成机器码之后会组织成一个文件,这个文件可以是.bin格式的,可以是.hex格式的,可以是.s19格式的,可以是sx格式的,还可以是eep格式的,它们并不是直接把赤裸裸的机器码放在上面,而是基于特定的规范增加了前缀和后缀,最终下载到单片机内部FLASH的时候就会对其进行解码,生产赤裸裸的机器码,存放到单片机FLASH中。以S19格式为例,下图就是S19文件转为机器码再下载到FLASH中的过程:
如果上面代码用C语言描述就是:
//宏定义,下面就可以直接对一个地址读写操作
#define I2C_DR (*(uint8_t *)0x5216)
#define FLASH_DUKR (*(uint8_t *)0x5064)
#define IWDG_KR (*(uint8_t *)0x50E0)
#define FLASH_IAPSR (*(uint8_t *)0x505F)
#define data_eeprom (*(uint8_t *)0x4001)
#define data_flash (*(uint8_t *)0x9001)//要保证0x9001并没有被代码占用
//定义一个usigned char类型的变量data_ram
uint8_t data_ram;
//把I2C数据寄存器中的值搬运到data_ram存储单元中
data_ram=I2C_DR;
//eeprom解锁
do
{
FLASH_DUKR=0xae;
Delayus(1);
FLASH_DUKR=0x56;
IWDG_KR=0xaa;
}while(0==FLASH_IAPSR&(1<<3);
//把数据保存到eeprom中
data_eeprom=data1+1;
//flash解锁
do
{
FLASH_DUKR=0xae;
Delayus(1);
FLASH_DUKR=0x56;
IWDG_KR=0xaa;
}while(0==FLASH_IAPSR&(1<<3);
//把数据保存到flash中
data_flash=data_ram+1;
从上面的汇编代码和C语言代码可以很明显的看出,C语言代码中有些东西是由编译器处理的,比如全局变量data_ram内存空间分配,编译器遇到uint8_t data_ram;之后就会在RAM中给它安排一个存储单元,这个存储单元的地址是固定不变的,而这其实就是分配好了变量data_ram的存储空间。