找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
楼主: newlined
打印 上一主题 下一主题
收起左侧

请教各位大神一个汇编语言堆栈大小计算的问题

[复制链接]
41#
ID:227818 发表于 2022-7-15 11:15 | 只看该作者
188610329 发表于 2022-7-14 21:07
R0~R7 属于 通用寄存器, 是不能PUSH的。
如果是完全自己写的代码,配合USING 可以用 PUSH AR0~AR7 方式 ...

您好,四组寄存器都已经用到了,其中有1组还是有2个同级别的低级中断共用的.考虑到这2个低级别中断不会同时被单片机响应,也没有用它们传递参数,只是用R0和R1访问间接地址,应该是不会冲突吧。
回复

使用道具 举报

42#
ID:401564 发表于 2022-7-15 14:06 | 只看该作者
我感觉你还是把代码上传上来吧
既然你说是多年前的程序,那就说明以前的代码是对的,问题估计就是出现你自己的代码上
把你自己的代码上传就行,不用把整个程序都上传的
你的代码在程序中是哪里调用的,如果是中断,就把中断中调用的位置和代码上传就行
MOV R0,#20H  之类的,它总不会连这也算商业机密吧
现在都是一群人在乱猜,压根就不知道你程序一个大概的流程
很有可能这只是一个简单的问题而已,却搞得那么复杂
回复

使用道具 举报

43#
ID:624769 发表于 2022-7-15 15:34 | 只看该作者
newlined 发表于 2022-7-15 11:15
您好,四组寄存器都已经用到了,其中有1组还是有2个同级别的低级中断共用的.考虑到这2个低级别中断不会同 ...

看你另外一个帖子, 你再找单片机原理的书? 所以,问题出在,你并不理解你的单片机?
我之前34楼的问题,你也没有回复,所以,我也不知道该从哪里可以帮到你,但是,个人觉得堆栈的问题,概率不大。考虑到你可能存在的“重入”问题,简单跟你说几个关键原理。
CSEG AT 0000H
LJMP  MAIN
CSEG AT 0003H
LJMP  EX0_INT

MAIN:
; 此处省略外部中断初始化程序
MAIN_Loop:
LCALL   Delay
SJMP   MAIN_Loop

EX0_INT:
PUSH   PSW
MOV    PSW,#08H
USING   1
LCALL  Delay
POP    PSW
RETI

Delay:
MOV   R7,#100H
DJNZ  R7,$
RET

这个例子,完全是为了 举例子而举例子。

我们可以看到  Delay 这个函数, 重入了, 但是,有影响么? 没有!!!, 只要确保  主程序所在 寄存器组, 不等于 中断使用的寄存器组, R0~R7, 是不会受 重入影响的。

我们再看这个例子:
CSEG AT 0000H
LJMP  MAIN
CSEG AT 0003H
LJMP  EX0_INT

MAIN:
; 此处省略外部中断初始化程序
MAIN_Loop:
LCALL   Delay
SJMP   MAIN_Loop

EX0_INT:           ;这次,中断里我们不切换寄存器
;PUSH   PSW
;MOV    PSW,#08H
;USING   1
LCALL  Delay
;POP    PSW
RETI

Delay:
USING    0            
PUSH   AR7             ; 函数内部,保护现场, 这也是常规意义的, “可重入函数”
MOV   R7,#100H
DJNZ  R7,$
POP    AR7
RET


这个例子,我们依然 重入了,   但是,有影响么??, 没有!!!!
所以,重入问题,是完全可以  靠人解决的, 编译器,绝对不会比人“更高级”,你既然现在在折腾汇编代码,就不要考虑 C 可能的问题,毕竟C 你是依赖编译器的,而,汇编,你只能依靠你自己(因为你的代码要“保密”,所以你也靠不到我们)。


基本上,目前从你这里得到的信息,只能帮到你这里了, 当然,如果你回复了 我在34楼的问题,也许可以给多你一点支持。

回复

使用道具 举报

44#
ID:883242 发表于 2022-7-15 20:13 | 只看该作者
现在越来越混乱了。楼主在20楼说重入警告,这是c51独有的,汇编的重入问题完全由用户控制,根本不可能出现警告,然后楼主又说用汇编。楼主连自己用的是个啥都不知道,大家还是散了吧。
回复

使用道具 举报

45#
ID:227818 发表于 2022-7-16 08:06 | 只看该作者
Y_G_G 发表于 2022-7-15 14:06
我感觉你还是把代码上传上来吧
既然你说是多年前的程序,那就说明以前的代码是对的,问题估计就是出现你自己 ...

是这样,是一家小公司,硬件都在那里,大家都可以看到,软件是这家公司的支柱,老板有交代,不好贴,请大家原谅。在在大家的指导下,把DPTR保护后,跑了1天多,软件不死机了,谢谢大家。
回复

使用道具 举报

46#
ID:227818 发表于 2022-7-16 08:12 | 只看该作者
本帖最后由 newlined 于 2022-7-16 08:42 编辑
188610329 发表于 2022-7-15 15:34
看你另外一个帖子, 你再找单片机原理的书? 所以,问题出在,你并不理解你的单片机?
我之前34楼的问题,你也 ...

这一段程序我看懂了,函数重入后,对这个函数用到的寄存器,函数被调用之前用到,函数调用完成后,还要用到的要保护后,就不会出问题。有了问题后,总是漫天找问题。
回复

使用道具 举报

47#
ID:227818 发表于 2022-7-16 08:24 | 只看该作者
Hephaestus 发表于 2022-7-15 20:13
现在越来越混乱了。楼主在20楼说重入警告,这是c51独有的,汇编的重入问题完全由用户控制,根本不可能出现 ...

不好意思,最近记忆力严重衰退,连老板都笑话我,也许我记串了。有一次,一个问题我们两个人,商讨后解决了,过了几天,我说那个问题怎么办,老板说我们两个商量后不是已经解决了吗,如何如何解决的,我听后好久才回忆起来。
对51的汇编,我是边学边用,实际上,我一般是先用KEIL C写出来,运行通过后再按照这个思路,改成汇编。这个汇编程序中,有一个函数,主程序和中断都调用,也许是出问题,我就复制了一份,另起一个名字,函数内部用到的直接寻址的地址,用另外的地址,运行通过了。
回复

使用道具 举报

48#
ID:149642 发表于 2022-7-16 08:38 | 只看该作者
这么复杂的程序,空间够用的情况下直接使用全局变量不好么,随便你咋中断,随便你咋调用,我都不用压堆的.
回复

使用道具 举报

49#
ID:227818 发表于 2022-7-16 08:42 | 只看该作者
188610329 发表于 2022-7-14 20:06
如果,你进入中断,用的是不同的寄存器组, 那么,你对 R0 的操作,是不会影响 主程序的 R0的,因为,此  ...

程序中,四个工作区都用到了,其中两个低级中断共用一个区,因为不会同时被单片机响应,不会出问题。以前的堆栈,设在50H到64H之间,64H到80H有的地址用了,有的还空着,我就把用的改在一起,空出来的跟64H连在一起做堆栈.
回复

使用道具 举报

50#
ID:883242 发表于 2022-7-16 08:51 | 只看该作者
newlined 发表于 2022-7-16 08:42
程序中,四个工作区都用到了,其中两个低级中断共用一个区,因为不会同时被单片机响应,不会出问题。以前的堆 ...

越来越混乱了,我在32楼问你用到idata了吗?你在39楼回答用到了。只有用到了80h以上空间才会用到idata,而C51编译器会自动把堆栈分配到idata上面。如果你用汇编的话,也应该利用高128字节来做堆栈,然而你又说堆栈在50h和64h之间???到底是怎么写的?这种空对空的说根本解决任何问题,按42楼建议,如果原程序是商业机密你不可以贴,但是你自己写的那部分,你拿捏不准的地方不贴出来,没有讨论的价值,也不太可能有人能提出关键性建议。
回复

使用道具 举报

51#
ID:887371 发表于 2022-7-16 10:13 | 只看该作者
本帖最后由 datouyuan 于 2022-7-16 10:22 编辑
newlined 发表于 2022-7-16 08:42
程序中,四个工作区都用到了,其中两个低级中断共用一个区,因为不会同时被单片机响应,不会出问题。以前的堆 ...

看来你这代码问题很大。
51的堆栈要安排在ram的最后。
例如你需要32字节堆栈,那么堆栈空间为0xe0~0xff。初始化SP要小于等于0xe0。
例如你需要48字节堆栈,那么堆栈空间为0xd0~0xff。初始化SP要小于等于0xd0。
小于0x80区域(data区),可以直接寻址,变量尽量安排在此区域。
>=0x80区域(idata区),只能间接寻址,变量在data区安排不下时(重要!!!),将数组变量、使用不频繁的变量安排在此区域。

编写汇编代码时,要确定所有变量使用了多少字节,例如使用了150个字节,那么SP应等于150,那么堆栈空间为150~0xff(共106字节)。
这帖子中,我提到的仅仅是些基本原则,估计楼主独立解决应该比较困难,建议楼主将代码交给有经验的帮忙。
回复

使用道具 举报

52#
ID:887371 发表于 2022-7-16 11:58 | 只看该作者
newlined 发表于 2022-7-16 08:24
不好意思,最近记忆力严重衰退,连老板都笑话我,也许我记串了。有一次,一个问题我们两个人,商讨后解决 ...

这样只是解决了编译器报警问题。
前提你要保证你的业务逻辑是正确的,假如业务逻辑有bug,即使你添加了再入属性,或者复制一份,都不能解决问题。
回复

使用道具 举报

53#
ID:624769 发表于 2022-7-16 18:00 | 只看该作者
newlined 发表于 2022-7-16 08:42
程序中,四个工作区都用到了,其中两个低级中断共用一个区,因为不会同时被单片机响应,不会出问题。以前的堆 ...

堆栈不能这么搞, 堆栈是向上增长的,所以应该分配在内存的最末尾,而且,我不知道你用的什么单片机,最初的编写环境是怎么样的,照理你这代码 如果用的标准 A51 规范来写,你所有的变量声明不用  SEGMENT 来定的么? 不然,维护起来有多复杂?正常不应该是这么一个套路么?My_DATA SEGMENT DATA        ;预约DATA 内存
RSEG My_DATA
   ABC:           DS     1
   EFG:           DS     1
   TEMP1:       DS     1
   TEMP2:       DS     1
My_IDATA SEGMNET IDATA   ;预约IDATA 内存
RSEG My_IDATA
   REV_BUF:      DS     32
   Disp_BUF:      DS     8
   ?STACK:        DS     1

CSEG   AT   0000H
LJMP      MAIN

Main_PROG  SEGMENT CODE
RSEG    Main_PROG
MAIN:
       MOV     SP,#?STACK-1                ;定位堆栈起点

       SJMP     $

END

你那个代码,到底是怎么折腾的,能够把 堆栈地址夹在那么奇怪的一个地方的?


评分

参与人数 1黑币 +15 收起 理由
datouyuan + 15 很给力!

查看全部评分

回复

使用道具 举报

54#
ID:227818 发表于 2022-7-18 16:39 | 只看该作者
datouyuan 发表于 2022-7-16 10:13
看来你这代码问题很大。
51的堆栈要安排在ram的最后。
例如你需要32字节堆栈,那么堆栈空间为0xe0~0xff ...

堆栈以前就是那样设定的。再就是提到堆栈可以安排在80H到FFH之间,我以前不知道,我以为这些地址只可以利用R0,R1间接寻址来访问,使用。
回复

使用道具 举报

55#
ID:227818 发表于 2022-7-18 16:42 | 只看该作者
datouyuan 发表于 2022-7-16 11:58
这样只是解决了编译器报警问题。
前提你要保证你的业务逻辑是正确的,假如业务逻辑有bug,即使你添加了 ...

业务逻辑保证是正确的,里边用到的地址都改了,不会冲突。
回复

使用道具 举报

56#
ID:227818 发表于 2022-7-18 16:53 | 只看该作者
188610329 发表于 2022-7-16 18:00
堆栈不能这么搞, 堆栈是向上增长的,所以应该分配在内存的最末尾,而且,我不知道你用的什么单片机,最 ...

我知道堆栈是向上生长的,程序中以前就是那样设定的,我也不知道为什么,我设定的话起码会设在80H之前,我原以为堆栈的最大地址是7FH,80H到FFH是间接地址,不可以做堆栈。程序对DPTR保护后,运行了2天。没有再出以前的问题。在这里请教下,您前边的帖子里提到的指令 PUSH AR0  ,PUSH  AR1 是在哪里能学到,一般的书里都没有。再就是这个帖子里的伪指令,在哪里可以学到?一般的书里也没有,谢谢。
回复

使用道具 举报

57#
ID:227818 发表于 2022-7-18 17:01 | 只看该作者
买了这2本书,也没有起多大作用,讲到中断,也只是说,保护现场,恢复现场,也没有具体的说要恢复那些东西,还不如在本帖中学到的东西多

IMG_20220718_165504[1].jpg (3.82 MB, 下载次数: 717)

IMG_20220718_165504[1].jpg
回复

使用道具 举报

58#
ID:227818 发表于 2022-7-18 17:03 | 只看该作者
还有这一本

IMG_20220718_165516[1].jpg (3.14 MB, 下载次数: 705)

IMG_20220718_165516[1].jpg
回复

使用道具 举报

59#
ID:227818 发表于 2022-7-18 17:07 | 只看该作者
@ 188610329,单片机是STC 8H
回复

使用道具 举报

60#
ID:227818 发表于 2022-7-18 17:08 | 只看该作者
也许看这些书需要一定的功力,我还远远不够。
回复

使用道具 举报

61#
ID:624769 发表于 2022-7-18 19:36 | 只看该作者
newlined 发表于 2022-7-18 16:53
我知道堆栈是向上生长的,程序中以前就是那样设定的,我也不知道为什么,我设定的话起码会设在80H之前, ...

AR0~AR7 是伪地址, 属于KEIL A51 专用,在KEIL的帮助里面可以找到,一般讲汇编的书,不会写,要讲KEIL的书才会写, 主要是为了弥补 A51 指令的空白, 你如果已经打了一点汇编基础,你会知道, 你如果要把R7 的东西复制给 R6, 是不能直接复制的,这个时候,就可以用到 “伪地址”: MOV  R6,AR7  来达到目的, 此时的 AR7, 其实是一个 指向 R7 所在的直接地址。相当于: MOV R6,07H  这条指令,唯一的好处是,AR7 可以指代 4组通用寄存器的 任何一个组,编译的时候,会帮你自动 改直接地址。所以,同样道理,PUSH 只能PUSH 直接地址,我们可以用PUSH AR7来填补无法 PUSH R7的空白。 其实主要内容就这么点。你要还有兴趣 可以去KEIL的 帮助里面搜一下。

再跟你讲一下SP, SP其实就是指针,你仔细看51方面的书,会提到 3个8位地址指针,2个16位指针,分别对应的就是: R0,R1,SP,DPTR,PC 这5个指针。 其中: SP是半自动,PC 是全自动, R0,R1,DPTR是全手动。所以,基于SP是8位指针的特性,他的工作原理和 R0,R1 是一样的,就是访问的 IDATA 间接寻址内存。而PUSH 和 POP 指令你可以理解成:PUSH ACC
PUSH PSW
........
POP  PSW
POP  ACC
RETI

//以下代码不存在,是解释上面代码的动作。
MOV   @SP,ACC
INC   SP
MOV   @SP,PSW
INC   SP
..............
DEC  SP
MOV  PSW,@SP
DEC  SP
MOV  ACC,@SP
RETI

通过这个代码,我们可以看到。其实,你用 R0,R1 完全可以 软件模拟出 PUSH POP 指令。 反过来,指针不够用的时候, SP也可以临时拿来当 R0,R1 的替补用。

最后,说一下,代码是死的,就看你怎么用。说到底,还是要去体会和理解 单片机的工作原理。



回复

使用道具 举报

62#
ID:401564 发表于 2022-7-19 01:18 | 只看该作者

你要看的是这一本书,网上应该可以搜索到不要钱的PDF版本,也可以买二手的,马云家30块左右,超过这个价钱就不要买了,我买的时候才二十多块
汇编没有那么复杂的,不要纠结堆栈了,8051是软件堆栈,我从来都不去管它的,等到你用到了硬件堆栈,你才会体验到汇编的毛病而且8051是复杂指令,功能多得很,你要是用到精简指令,你就会更加的体会到汇编的无聊
现在的编译器效率已经非常不错了,优先选择C语言,实在不行了再用汇编
回复

使用道具 举报

63#
ID:227818 发表于 2022-8-1 08:16 | 只看该作者
前一段时间,我母亲生病住院,我去陪床,我母亲出院那一天,我父亲很高兴,去做饭,又烫伤了脚,我没有时间仔细看这个帖子,很抱歉。
回复

使用道具 举报

64#
ID:227818 发表于 2022-8-1 08:21 | 只看该作者
188610329 发表于 2022-7-18 19:36
AR0~AR7 是伪地址, 属于KEIL A51 专用,在KEIL的帮助里面可以找到,一般讲汇编的书,不会写,要讲KEIL的 ...

看了这个帖子,我明白了,AR7是不是已经宏定义成了07H?我以前对地址了解不透彻,看来我要对这方面仔细揣摩。
回复

使用道具 举报

65#
ID:227818 发表于 2022-8-1 08:25 | 只看该作者
Y_G_G 发表于 2022-7-19 01:18
你要看的是这一本书,网上应该可以搜索到不要钱的PDF版本,也可以买二手的,马云家30块左右,超过这个价钱就 ...

在网上我搜到了这本书的下载网站,但好像需要注册,需要电话号码什么的。在淘宝上有一家卖30几元,但需要45天发货,不知道怎么回事。
回复

使用道具 举报

66#
ID:401564 发表于 2022-8-1 13:17 | 只看该作者
这么些天了,还没有搞明白这个?
00H-1FH  这就是R0-R7的几个工作组
20H-2FH 这是可以位寻址的,位地址是从00H开始的,字节地址也有00H,通过指令来区分是位操作还是字节操作
SETE 00H 这是位操作
MOV 00H,#255 这是字节操作

一般是编写代码的时候,00H-1FH留给工作组使用,20H-2FH留给位寻址用
所以,你一般看到的用到内存的,都是多30H开始的

回复

使用道具 举报

67#
ID:624769 发表于 2022-8-1 17:21 | 只看该作者
newlined 发表于 2022-8-1 08:21
看了这个帖子,我明白了,AR7是不是已经宏定义成了07H?我以前对地址了解不透彻,看来我要对这方面仔细揣 ...

AR7  不一定是 07H,  也可能是 0FH, 17H 或者 1FH。 这牵涉到另一条 伪指令: USING  ,  当代码向上搜索 找到USING 0 则编译的时候会把  PUSH AR7  当作 PUSH 07H 来编译, 如果找到 USING 1 则会在编译的时候 把 PUSH AR7 当作 PUSH 0FH 来编译。 所以 AR7 不是绝对的,是可变的,好处是,如果更换换寄存器组,你可以不需要一行行代码去把 代表R7地址手动更改。缺点是,USING 必需要控制好。
回复

使用道具 举报

68#
ID:227818 发表于 2022-8-4 09:59 | 只看该作者
188610329 发表于 2022-8-1 17:21
AR7  不一定是 07H,  也可能是 0FH, 17H 或者 1FH。 这牵涉到另一条 伪指令: USING  ,  当代码向上搜索  ...

这一段我还不理解,但我会尽量避免出现这个问题,以后我会慢慢领会。现在我遇到一个新的问题,就是单片机是STC的8H系列,您肯定知道,它对扇区的读写命令是类似这样的:

        ISP_CMD                        EQU                0C5H                        ;ISP命令寄存器
        ISP_CONTR                EQU                0C7H                        ;ISP控制寄存器
        ISP_TPS                        EQU                0F5H                        ;ISP等待时间寄存器

        MOV                          ISP_CONTR,#ENABLE_ISP
        MOV                               ISP_CMD,#01H                  ; 读扇区
        MOV                          ISP_TPS,#0CH  ;
这一段程序在主程序中用到了,在一个中断中也用到了,在中断中,对 ISP_CMD,ISP_CONTR进行压栈保护?就是 PUSH ISP_CMD,PUSH ISP_CONTR ,退出中断时 再 POP ISP_CONTR,POP ISP_CMD,这样保护能达到目的吗?编译是通过了,但我没有把握,您看可以吗?
回复

使用道具 举报

69#
ID:227818 发表于 2022-8-4 11:12 | 只看该作者
Y_G_G 发表于 2022-8-1 13:17
这么些天了,还没有搞明白这个?
00H-1FH  这就是R0-R7的几个工作组
20H-2FH 这是可以位寻址的,位地址是从0 ...

这个地址我基本明白了,但对地址的运用上,还是不行,我可能需要一些实践才能领会。
回复

使用道具 举报

70#
ID:624769 发表于 2022-8-4 14:56 | 只看该作者
newlined 发表于 2022-8-4 09:59
这一段我还不理解,但我会尽量避免出现这个问题,以后我会慢慢领会。现在我遇到一个新的问题,就是单片机 ...

要说保护的话……
ISP_CMD, ISP_CONTR,之外,ISP_ADDRH, ISP_ADDRL 这些都要一起保护。
但是这些都没用。严格来讲,STC系列而言, ISP/IAP操作是唯一无法保护现场的操作。
因为: IAP_TRIG  这个启动指令, 需要连续发送两条 0x5A, 和 0xA5 的启动指令,这个是绝对不允许被打断的。所以,一般,会需要在程序中,先  CLR EA,禁止中断, 等所有 IAP操作完成 后 再次 SET EA 允许中断,这个才是关键。所以说,了解单片机的工作原理,还是重点中的重点。而如果在 主程序中已经禁止了中断,那么,相关操作就不需要 被PUSH和POP来现场保护了。

最后:IAP_TPS 是STC8系列用来设置 IAP操作速度的,理论上来说,只要你单片机不要不停的改变速度, MOV IAP_TPS,#0CH 之需要执行一次,里面的值是不会,也不需要再修改的。所以,通常来说,IAP_TPS 也是没有必要PUSH和POP的。
回复

使用道具 举报

71#
ID:227818 发表于 2022-8-4 15:56 | 只看该作者
188610329 发表于 2022-8-4 14:56
要说保护的话……
ISP_CMD, ISP_CONTR,之外,ISP_ADDRH, ISP_ADDRL 这些都要一起保护。
但是这些都没 ...

真是的,您提醒了我,ISP_ADDRH, ISP_ADDRL我没有保护,程序运行主程序*ISP_ADDRH, ISP_ADDRL
回复

使用道具 举报

72#
ID:227818 发表于 2022-8-4 16:00 | 只看该作者
笔记本的键盘不好用,可能是我前一段时间摔了一下.
回复

使用道具 举报

73#
ID:227818 发表于 2022-8-4 16:11 | 只看该作者
程序运行过程中,有是会出现问题,可能是这个原因,两条 0x5A, 和 0xA5 的启动指令之前,中断是关的,之后打开了,实际上这个是以前的程序,我后来补充了一些程序中,尤其是中断中用到了操作EEPROM的语句,没有对主程序中操作EEPROM的现场保护。我如果对DPTR,ISP_CMD, ISP_CONTR,ISP_ADDRH, ISP_ADDRL保护了,就没有问题了吧。
回复

使用道具 举报

74#
ID:624769 发表于 2022-8-4 18:10 来自触屏版 | 只看该作者
手机打字,我就长话短说了。 首先,trig指令发送前关中断,发送后开中断,是最低底线。 其次,我不太清楚你程序如何设计,stc8系列是支持movc读取eeprom的,所以,我的话,所有读指令都是movc完成的,写指令才会用iap指令,所以基本上是不需要保护iap相关寄存器,只需要保护dptr就可以的。 最后,牵涉到架构了,如果有可能,所有的写,擦eeprom的行为,集中在一个函数,或者一个中段中完成,对整个程序是最优的。
回复

使用道具 举报

75#
ID:227818 发表于 2022-8-6 14:32 | 只看该作者
读指令movc可以不需要保护iap相关寄存器吗?我仔细揣摩下。
回复

使用道具 举报

76#
ID:624769 发表于 2022-8-7 17:59 | 只看该作者
newlined 发表于 2022-8-6 14:32
读指令movc可以不需要保护iap相关寄存器吗?我仔细揣摩下。

STC的 Eeprom 是把 Flash 空间模拟成 Eeprom 来操作,尤其是STC8系列,他在物理上就是 ROM的一部分。所以,只需要把它当 ROM来读就可以了。那么读取ROM的方式事就是:

MOV DPTR,#16位绝对地址
CLR A
MOVC A,@A+DPTR

就能读取了。整个过程没有 IAP相关寄存器的任何事情,因此,自然不需要 对 IAP相关寄存器做任何 保护工作。至于具体的 MOVC 读取时地址这块的可以详见 STC8系列的手册。
回复

使用道具 举报

77#
ID:227818 发表于 2022-10-6 09:23 | 只看该作者
Y_G_G 发表于 2022-7-19 01:18
你要看的是这一本书,网上应该可以搜索到不要钱的PDF版本,也可以买二手的,马云家30块左右,超过这个价钱就 ...

我买回了这本书,正在学习中,谢谢。
回复

使用道具 举报

78#
ID:227818 发表于 2022-10-6 09:28 | 只看该作者
188610329 发表于 2022-8-7 17:59
STC的 Eeprom 是把 Flash 空间模拟成 Eeprom 来操作,尤其是STC8系列,他在物理上就是 ROM的一部分。所以 ...

好的,您说的,我慢慢理会。这一段时间,正看程序,原程序中bug不少,比如,一个子程序,有时用LCALL调用,当然,这是对的,竟然有时用LJMP跳转过去,我都不知道最后执行了RET之后去了哪里。
回复

使用道具 举报

79#
ID:624769 发表于 2022-10-6 15:00 | 只看该作者
newlined 发表于 2022-10-6 09:28
好的,您说的,我慢慢理会。这一段时间,正看程序,原程序中bug不少,比如,一个子程序,有时用LCALL调用 ...

唉…… 这是基础中的基础啊……
给你举个例子吧:
有个函数:
SEND_HALF_BCD:
ANL   A,#0FH
ORL   A,#30H

SEND_BYTE:
JNB     TI,$
CLR     TI
MOV   SBUF,A
RET

你要发送  一个BCD出去, 可以制作这么一个函数:
_Send_BCD:         ;通过R7传入
MOV   A,R7
SWAP A
LCALL  SEND_HALF_BCD
MOV   A,R7
LCALL  SEND_HALF_BCD

RET
通常, 你觉得应该这么写对吧? 但是,这么写其实是完全没有 用到汇编的优势 。
所以,既然用了汇编,通常用如下写法:
_Send_BCD:         ;通过R7传入
MOV   A,R7
SWAP A
LCALL  SEND_HALF_BCD
MOV   A,R7
LJMP  SEND_HALF_BCD
这样写, 就避免了 无意义的 RET。 程序会依然回 到  LCALL  _Send_BCD 的地方。而效率会变高

所以, 你要搞清楚  有时LCALL 有时LJMP, 到底是 BUG  还是你没理解  程序本身的 真正目的?



回复

使用道具 举报

80#
ID:227818 发表于 2022-10-25 13:50 | 只看该作者
188610329 发表于 2022-10-6 15:00
唉…… 这是基础中的基础啊……
给你举个例子吧:
有个函数:

你好,这个例子,我一直试图理解,但我没有想通。我是这样理解的,LCALL 一个函数时,堆栈会保存当前调用时的地址,RET 时,这个地址会恢复到PC程序计数器中,如果LJMP到一个函数,堆栈不会保存当前地址,执行到RET时,堆栈里的两个字节,恢复到PC程序计数器中,这个应该是错误的吧。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表