不谙世事的雨滴 发表于 2023-5-11 07:39

如何在89C51/52下,将片外RAM中的一段内容,无损移动到片外RAM中的任意地方

最近学校里单片机课程做了个实验,如下:

实验指导书上给的代码:


不得不说这个loop看得头有点晕,不过好在大概明白是怎么跑的了,而且还发现一个潜在的bug,如果目的区域和源区域没有重合,当然没问题,但是一旦有重合区域,源内容就会失真,演示图如下:

我本来想如果重合了,就不处理了,后来想了想,如果从后往前传呢?于是更好的办法就有了:

这样不管是向前还是向后传送,都能无损移动了,关键是选对移动的方法(向前:从后往前移动;向后:从前往后移动)
改进代码如下:
;说明:
;        此程序将89C51/89C52单片机的片外RAM中的一段字符串,
;        整体移动至片外RAM的任意地方,源字符串所在位置将被清零。
;R2、R3存【源字符串的首地址】(16BIT,R2高R3低)
;R4、R5存【写入目标的首地址】(16BIT,R4高R5低)
;R6、R7存【字符串长度】,也就是字符串中字符的总个数(为16位无符号正整数)
;运行前,请在仿真软件中手动给上述寄存器赋值


ORG 0000H
LJMP MAIN

ORG 0033H
MAIN:
;安排额外的缓存地址
CACHE_0 EQU 30H
CACHE_1 EQU 31H
;记录寄存器地址,方便PUSH和POP(堆栈操作只能跟direct)
DR2                EQU 02H
DR3                EQU 03H
DR4                EQU 04H
DR5                EQU 05H
DR6                EQU 06H
DR7                EQU 07H
;A=E0H
DRA                EQU 0E0H

;运行前先保存参数
PUSH        DR6
PUSH        DR7
PUSH        DR2
PUSH        DR3
PUSH        DR4
PUSH        DR5
;SJMP ICP1



;=================================================================

ICP1:                ;INITIAL_CHECK_POINT_1,初始检查点1
;如果R6、R7均为0(传送个数为0),直接结束
;只要有一个不为0,就跳转至初始检查点2,否则结束程序
CJNE R7,#00H,ICP2
CJNE R6,#00H,ICP2
LJMP FINISH


;=================================================================

ICP2:                ;INITIAL_CHECK_POINT_2,初始检查点2
;比较【源】和【目的】两个首地址的大小,并安排传送的策略方法

P2H_1:        ;P2H_1:先比较高位
MOV A,R4
MOV CACHE_0,R2
CJNE A,CACHE_0,P2H_2
;高位相等,还得判断低位,跳“P2L_1”
SJMP P2L_1

P2H_2:        ;P2H_2:高位不等
;
;【目的】高位大于【源】,即【目的】>【源】
;选择方法2(从后往前传送),先跳转至准备工作“RST_H”
JNC                RST_H
;
;【目的】高位小于【源】,即【目的】<【源】
;选择方法1(从前往后传送),由于首地址已在寄存器中准备好,
;于是直接跳转至方法1(从前往后传送)
SJMP        SG_T_1

P2L_1:        ;P2L_1:高位相等,比较低位
MOV A,R5
MOV CACHE_0,R3
CJNE A,CACHE_0,P2L_2
;低位也相等⇩⇩⇩
;高低位均相等,只要传送个数大于零(上面“初始检查点1”已保证),
;也是合法的,效果是字符串保持不变;
;放行,下面直接选择方法1。
SJMP SG_T_1

P2L_2:        ;P2L_2:低位不等(高位相等)
;
;【目的】低位大于【源】(高位相等),即【目的】>【源】
;选择方法2(从后往前传送),先跳转至准备工作“RST_H”
JNC                RST_H
;
;【目的】低位小于【源】(高位相等),即【目的】<【源】
;选择方法1(从前往后传送),由于首地址已在寄存器中准备好,
;于是直接跳转至方法1(从前往后传送)
SJMP        SG_T_1


;=================================================================



RST_H:        ;RESET_TO_HIGH_ADDRESS
;将【源】和【目的】的地址重设为最后面的地址,准备从后往前传送
;
;个数-1得Δ(Δ的高位存CACHE_0,低位存CACHE_1)
CLR CY
MOV                A,R7
SUBB        A,#1
MOV                CACHE_1,A
MOV                A,R6
SUBB        A,#00H
MOV                CACHE_0,A

;源首地址        +        Δ        =        源末地址,        覆盖源        首地址R2、R3
CLR CY
MOV                A,CACHE_1
ADDC        A,R3
MOV                R3,A
MOV                A,CACHE_0
ADDC        A,R2
MOV                R2,A
;目的首地址        +        Δ        =        目的末地址,覆盖目的首地址R4、R5
CLR CY
MOV                A,CACHE_1
ADDC        A,R5
MOV                R5,A
MOV                A,CACHE_0
ADDC        A,R4
MOV                R4,A
;
;准备工作完成,跳“方法2”,从后往前传送
SJMP SG_T_2


;=================================================================





SG_T_1:                ;SINGLE_TRANSPORT_METHOD_1,方法1,从前往后传送
;=================================
;(1)取数:
MOV         DPL,R3
MOV         DPH,R2
MOVX        A,@DPTR
;===================
PUSH        DRA
;【源】清零
CLR                A
MOVX        @DPTR,A
;===================
;【源】数据指针+1,指向下一个单元
INC        DPTR
;保存即将【读取】的下一个单元的数据指针
MOV        R3,DPL
MOV R2,DPH
;
;=================================
;(2)传送:
MOV                DPL,R5
MOV                DPH,R4
POP                DRA
MOVX        @DPTR,A
;
;【目标】数据指针+1,指向下一个单元
INC        DPTR
;保存即将【写入】的下一个单元的数据指针
MOV        R5,DPL
MOV R4,DPH
;=================================

;跳转至方法1对应的剩余个数检查点
SJMP NUM_CK_1





SG_T_2:                ;SINGLE_TRANSPORT_METHOD_2,方法2,从后往前传送
;=================================
;(1)取数:
MOV         DPL,R3
MOV         DPH,R2
MOVX        A,@DPTR
;===================
PUSH        DRA
;【源】清零
CLR                A
MOVX        @DPTR,A
;===================
;【源】数据指针-1,指向下一个单元
ACALL DEC_DPTR
;保存即将【读取】的下一个单元的数据指针
MOV        R3,DPL
MOV R2,DPH
;
;===================
;(2)传送:
MOV                DPL,R5
MOV                DPH,R4
POP                DRA
MOVX        @DPTR,A
;
;【目标】数据指针-1,指向下一个单元
ACALL DEC_DPTR
;保存即将【写入】的下一个单元的数据指针
MOV        R5,DPL
MOV R4,DPH
;===================

;跳转至方法2对应的剩余个数检查点
SJMP NUM_CK_2






NUM_CK_1:        ;NUMBER_CHECK_1
;操作前是当前数字对应的编号,操作后是下一个数对应的编号
ACALL DEC_NUM
;比较减一后是否为零
;如不为零,说明还没有移动完,进行下一次循环
CJNE R7,#00H,SG_T_1
CJNE R6,#00H,SG_T_1
;如为零,说明结束了
SJMP FINISH

NUM_CK_2:        ;NUMBER_CHECK_2
;操作前是当前数字对应的编号,操作后是下一个数对应的编号
ACALL DEC_NUM
;比较减一后是否为零
;如不为零,说明还没有移动完,进行下一次循环
CJNE R7,#00H,SG_T_2
CJNE R6,#00H,SG_T_2
;如为零,说明结束了
SJMP FINISH





DEC_DPTR:        ;DPTR自减1函数
CLR CY
;低8位减法,并保存
MOV         A,DPL
SUBB        A,#1
MOV         DPL,A
;高8位减法,并保存
MOV         A,DPH
SUBB        A,#00H
MOV         DPH,A
;返回
RET

DEC_NUM:        ;剩余个数(编号)自减1函数
CLR CY
;低8位减法,并保存
MOV A,R7
SUBB A,#1
MOV R7,A
;高8位减法,并保存
MOV A,R6
SUBB A,#00H
MOV R6,A
;返回
RET




FINISH:
;恢复输入的参数
;交换【源】和【目标】的地址,
;软件中复位CPU后,下一次模拟运行时字符串就能移回原位
POP        DR3        ;R5-->R3
POP        DR2        ;R4-->R2
POP        DR5        ;R3-->R5
POP        DR4        ;R2-->R4
;个数保持不变
POP        DR7        ;R7-->R7
POP        DR6        ;R6-->R6
;
MOV PCON,#01H        ;待机
;
NOP                                ;多一行NOP,防止模拟执行时在程序末尾报“access violation”错误
END

本人单片机刚刚入门,抱着玩一玩的想法分享这段内容,各位就当看看热闹好了,大佬轻喷:lol

AWGemini 发表于 2023-5-11 08:04

汇编大佬!牛的

nohao8024 发表于 2023-5-11 08:51

兄弟,过来人给你个忠告,别学汇编,玩单片机就学C,Python,我大学也学的汇编,找工作都不用汇编,汇编过时了,但是有一个好处,节省空间

sunsjw 发表于 2023-5-11 09:29

汇编是基础,可以学习,了解一下就可以了。
工作还得用C

不谙世事的雨滴 发表于 2023-5-11 13:04

nohao8024 发表于 2023-5-11 08:51
兄弟,过来人给你个忠告,别学汇编,玩单片机就学C,Python,我大学也学的汇编,找工作都不用汇编,汇编过时 ...

确实,不过学校的单片机期末考试还得应付一下:lol

不谙世事的雨滴 发表于 2023-5-11 13:07

AWGemini 发表于 2023-5-11 08:04
汇编大佬!牛的

哪里哪里,刚刚入门:rggrg
页: [1]
查看完整版本: 如何在89C51/52下,将片外RAM中的一段内容,无损移动到片外RAM中的任意地方