標題: 請教各位大神一個匯編語言堆棧大小計算的問題 [打印本頁]

作者: newlined    時間: 2022-7-12 06:34
標題: 請教各位大神一個匯編語言堆棧大小計算的問題
我有一個主程序A(是51單片機的匯編),在STC 8H上跑,這個主程序A調用了子程序A1,A1又調用了A2,A2又調用了A3,即A->A1->A2->A3。中斷用了5個,可以定義成中斷B,C,D,E,F(xiàn)。其中中斷B,C是高級中斷,D,E,F(xiàn)是低級中斷。所有中斷中都是PUSH ACC,PUSH PSW,沒有再PUSH其他寄存器,出中斷時POP PSW ,POP ACC。中斷B中,調用了子程序B1,B1又調用了B2即B-B1->B2。C中斷中沒有調用其他子程序。D中斷中調用了子程序D1,D1又調用了D2,即D->D1-D2。E中斷中調用了子程序E1,E1又調用了E2,E2又調用了E3,即E->E1->E2-E3。所有子程序都是簡單的對單片機的端口進行操作,即置高或低,子程序沒有用堆棧傳遞參數(shù)。按照我的理解,考慮一個最復雜的情況,主程序在調用A3時,堆棧用了6個字節(jié),這時發(fā)生E中斷,E中斷在調用E3時,堆棧用了8個字節(jié),這是發(fā)生B中斷,在B中斷調用B2時,堆棧用了6個字節(jié),這樣堆棧最多用20個字節(jié)就可以了,但在實際情況中,單片機不時會死機.這個程序原先不是我寫的,我不過做了一點修改,即在中斷E中加了一個子程序E3。以前堆棧留的比較小,只有20個字節(jié),后來我又找了幾個加上了,但還是不大,F(xiàn)在我懷疑是堆棧溢出了,請問我堆棧大小的計算對嗎?衷心感謝

作者: datouyuan    時間: 2022-7-12 12:03
1.51的任何子程序都需要通過堆棧傳遞PC值,最少2字節(jié)。
2.51的中斷子程序通過堆棧傳遞PC、ACC、PSW ,最少4字節(jié)。
3.中斷內部調用子程序算情形1,最少2字節(jié)。

你的代碼有2級中斷調用,情形1調用算6級,所以最少20字節(jié)(2*4+6*2)。
但上述是最理想的情況。我們一般按10級情形1算,但情形1算5字節(jié),預留50字節(jié)堆棧。
作者: Y_G_G    時間: 2022-7-12 12:26
8051是軟件堆棧,如果不是刻意去填滿它,堆;旧鲜遣粫袉栴}的
卡死的問題不一定是堆棧的問題,可能是中斷太多,而且中斷觸發(fā)間隔時間太短
比如說:
1,ADC中你調用了某個子程序,ADC執(zhí)行的總時長是5mS,ADC中斷你又設定成最高優(yōu)先等級,而ADC中斷間隔是1mS
2,定時器中斷是2mS的
那么,這個程序就一直在ADC和定時器之間運行了,主程序基本是不會執(zhí)行的
作者: newlined    時間: 2022-7-12 14:21
datouyuan 發(fā)表于 2022-7-12 12:03
1.51的任何子程序都需要通過堆棧傳遞PC值,最少2字節(jié)。
2.51的中斷子程序通過堆棧傳遞PC、ACC、PSW ,最少 ...

謝謝您的回復,我算的是最多需要20字節(jié),您算的最少需要20字節(jié)
我是這樣理解的同為高級的2個中斷B,C,同一時刻只能有一個中斷響應,C中斷響應,堆棧占用2個字節(jié),B中斷響應,最多占用6個字節(jié),所以按B中斷記,D,E,F三個低級中斷,它們三個同一時刻只有1個被CPU響應,主貼中忘記說了,F(xiàn)中斷內沒有子程序,按子程序調用最深的E中斷算,低級中斷最多需要8個字節(jié),主程序中子程序調用最多需要6個字節(jié),這樣需要20個字節(jié)就可以了。按照您的計算,我肯定是哪里理解錯了,錯在那里,麻煩您再說一下,我在這方面是半路出家,有些方面理解不透,謝謝。
作者: newlined    時間: 2022-7-12 14:28
Y_G_G 發(fā)表于 2022-7-12 12:26
8051是軟件堆棧,如果不是刻意去填滿它,堆;旧鲜遣粫袉栴}的
卡死的問題不一定是堆棧的問題,可能是中 ...

謝謝您的回復,我明白了您的意思,您是說,中斷觸發(fā)間隔的時間短,而中斷本身執(zhí)行的時間長,結果就是CPU在中斷之間運行,一個中斷執(zhí)行完,馬上又去響應另一個中斷,然后又去響應前一個中斷,沒有時間去執(zhí)行主程序,我按您的思路拿示波器跟蹤下,看看每一個中斷需要多少時間。
作者: hb_lhw    時間: 2022-7-12 15:00
每次中斷,自動入棧一個PC寄存器,具體幾個字節(jié)我忘了,然后中斷返回的時候再自動出棧,樓主沒考慮這個,所以可能會亂。
作者: newlined    時間: 2022-7-12 15:24
hb_lhw 發(fā)表于 2022-7-12 15:00
每次中斷,自動入棧一個PC寄存器,具體幾個字節(jié)我忘了,然后中斷返回的時候再自動出棧,樓主沒考慮這個,所 ...

您說的應該是程序寄存器,也就是PC寄存器,它是16位的,占用2個字節(jié),按說我已經(jīng)考慮到了。
作者: newlined    時間: 2022-7-12 15:27
datouyuan 發(fā)表于 2022-7-12 12:03
1.51的任何子程序都需要通過堆棧傳遞PC值,最少2字節(jié)。
2.51的中斷子程序通過堆棧傳遞PC、ACC、PSW ,最少 ...

我剛才又想到,是不是中斷嵌套時,堆棧需要消耗更多的字節(jié)?中斷嵌套時堆棧消耗的字節(jié)是怎樣計算的?
作者: newlined    時間: 2022-7-12 15:44
本帖最后由 newlined 于 2022-7-12 15:47 編輯
Y_G_G 發(fā)表于 2022-7-12 12:26
8051是軟件堆棧,如果不是刻意去填滿它,堆;旧鲜遣粫袉栴}的
卡死的問題不一定是堆棧的問題,可能是中 ...

您好,我剛才拿示波器跟蹤了一下,一共5個中斷,2個中斷是1毫秒一次,一個外部中斷是20毫秒到100毫秒一次,這3個1個請問-中斷的時間在5微妙到10微妙之間,應該沒有問題。還有一個是30毫秒1次,執(zhí)行時間是10微秒,也沒有問題,執(zhí)行時間比較長的是一個掉電中斷,是高級中斷,執(zhí)行時間10毫秒,這個中斷執(zhí)行完就不再執(zhí)行其他語句了,單片機就等著掉電了。按說也不影響主程序的運行
作者: 188610329    時間: 2022-7-12 16:25
是不是堆棧大小的問題, 用KEIL 編譯后,仿真一下,看一下 SP_max 就知道了。有什么好算得?

我現(xiàn)在好奇的是,你確定是堆棧問題? 你那么多中斷 你居然   都是只 PUSH ACC 和 PSW 的? 你不用 R0~R7 的? 不用 B 的?  不用 DPTR 的? 這些只要有一個,你該PUSH 的沒有PUSH 都會有可能造成發(fā)生過 中斷后,程序出現(xiàn)錯誤,最終跑飛。
作者: Y_G_G    時間: 2022-7-12 16:56
newlined 發(fā)表于 2022-7-12 14:28
謝謝您的回復,我明白了您的意思,您是說,中斷觸發(fā)間隔的時間短,而中斷本身執(zhí)行的時間長,結果就是CPU ...

大概就是這么個意思了
也就是說程序是在中斷之間來回跑的
主程序幾乎沒有執(zhí)行的時間
為什么要在中斷中執(zhí)行那么多功能呢?能不能放在主程序執(zhí)行呢?
作者: newlined    時間: 2022-7-12 17:03
Y_G_G 發(fā)表于 2022-7-12 16:56
大概就是這么個意思了
也就是說程序是在中斷之間來回跑的
主程序幾乎沒有執(zhí)行的時間

這個程序是20年前別人編寫的,我現(xiàn)在接手維護,整個程序還沒有吃透。
作者: newlined    時間: 2022-7-12 17:13
Y_G_G 發(fā)表于 2022-7-12 16:56
大概就是這么個意思了
也就是說程序是在中斷之間來回跑的
主程序幾乎沒有執(zhí)行的時間

大約1小時前回復過,不知道回帖為什么沒有了。今天下午我拿示波器跟蹤了各個中斷的執(zhí)行時間,有2個是1毫秒1次的中斷,執(zhí)行時間在10微妙左右,1個是30毫秒的中斷,執(zhí)行時間是10微妙多一點,一個外部中斷,20毫秒到100毫秒一次,執(zhí)行時間在8微妙左右,應該都可以,不會引起主程序執(zhí)行時間的不足,一個掉電中斷,是高級中斷,執(zhí)行時間10毫秒,這個按說也不影響主程序的運行,因為這個中斷執(zhí)行完,就等著掉電了,不再執(zhí)行其他語句了。
作者: newlined    時間: 2022-7-12 17:18
188610329 發(fā)表于 2022-7-12 16:25
是不是堆棧大小的問題, 用KEIL 編譯后,仿真一下,看一下 SP_max 就知道了。有什么好算得?

我現(xiàn)在好奇 ...

這個程序是20年前別人寫的,我接手維護,小改下,不好動架構。
作者: datouyuan    時間: 2022-7-12 17:23
newlined 發(fā)表于 2022-7-12 14:21
謝謝您的回復,我算的是最多需要20字節(jié),您算的最少需要20字節(jié)
我是這樣理解的同為高級的2個中斷B ...
考慮一個最復雜的情況,主程序在調用A3時,堆棧用了6個字節(jié),這時發(fā)生E中斷,E中斷在調用E3時,堆棧用了8個字節(jié),這是發(fā)生B中斷,在B中斷調用B2時,堆棧用了6個字節(jié),這樣堆棧最多用20個字節(jié)就可以了

A到A3,是函數(shù)調用3級,最少6字節(jié)。
E中斷,是中斷函數(shù)調用,最少4字節(jié)。
E到E3,是函數(shù)調用3級,最少6字節(jié)。
B中斷,是中斷函數(shù)調用,最少4字節(jié)。
B到B2,是函數(shù)調用2級,最少4字節(jié)。
最少24字節(jié)。

不需要按上述計算,堆棧就是按函數(shù)調用深度和中斷函數(shù)調用深度估算。
函數(shù)調用深度8級,最少占用16(2*8)字節(jié)。
中斷函數(shù)調用深度2級,最少占用8(4*2)字節(jié)。
我提到的3點原則只考慮了PCH PCL ACC PSW的保存、恢復,是最低要求的情形。有些應用還要考慮DPTR、B、R0~R7的保存、恢復,所以堆棧需求會明顯大于24個字節(jié)。
作者: datouyuan    時間: 2022-7-12 17:35
C51中斷中使用函數(shù)是很不好的習慣。
你把中斷中的函數(shù)改成不是函數(shù),以代碼空間換時間和RAM。
應該能解決問題。
作者: Y_G_G    時間: 2022-7-12 18:20
newlined 發(fā)表于 2022-7-12 17:13
大約1小時前回復過,不知道回帖為什么沒有了。今天下午我拿示波器跟蹤了各個中斷的執(zhí)行時間,有2個是1毫 ...

所謂"死機"和中斷無法跳出,這不過是推測而已,說不定可能就是其它的小問題而已
匯編是很容易出問題的
如果說是以前的代碼是可以量產(chǎn)的,就說明以前的代碼是沒有問題的
重點看你的代碼會影響到哪些
如果不是什么商業(yè)機密,就把完整代碼上傳,大家一看就知道了
作者: newlined    時間: 2022-7-13 13:32
188610329 發(fā)表于 2022-7-12 16:25
是不是堆棧大小的問題, 用KEIL 編譯后,仿真一下,看一下 SP_max 就知道了。有什么好算得?

我現(xiàn)在好奇 ...

您好,以前的程序就是沒有對DPTR保護?戳四陌l(fā)言后,我看了下程序,里邊的確用了DPTR,不知道為什么程序還可以運行。工作寄存器的4個區(qū),有1個區(qū)是2個低級中斷公用的,這2個中斷不會同時響應,只用了R0和R1,每次用之前,都要先賦值,訪問間接尋址的空間,所以不會沖突。B寄存器也用了,不知道為什么沒有保護。這個程序大幾千行,沒有文字的介紹資料,注釋也很少,估計不止一人維護過.我還遠沒有吃透.
作者: 188610329    時間: 2022-7-13 13:44
newlined 發(fā)表于 2022-7-13 13:32
您好,以前的程序就是沒有對DPTR保護?戳四陌l(fā)言后,我看了下程序,里邊的確用了DPTR,不知道為什么程 ...

程序沒吃透的前提下, 最不變應萬變的做法,就是中斷里用了了什么, 進入中斷前就 PUSH什么, 出中斷前 POP什么, 這是最笨,但也是最穩(wěn)妥的辦法。 等到吃透之后,可以選擇性的 PUSH 和 POP
作者: newlined    時間: 2022-7-13 13:47
datouyuan 發(fā)表于 2022-7-12 17:35
C51中斷中使用函數(shù)是很不好的習慣。
你把中斷中的函數(shù)改成不是函數(shù),以代碼空間換時間和RAM。
應該能解決 ...

您好,您這一說,我意識到一個問題,函數(shù)重入,這個程序編譯時曾經(jīng)出過這個警告,后來我把一個函數(shù)復制了一份,另起了一個名字,供不同的子程序還是中斷調用,現(xiàn)在是不是還有這個問題?在keil c中也不能這么用嗎?
作者: datouyuan    時間: 2022-7-13 15:12
newlined 發(fā)表于 2022-7-13 13:47
您好,您這一說,我意識到一個問題,函數(shù)重入,這個程序編譯時曾經(jīng)出過這個警告,后來我把一個函數(shù)復制了 ...

一般的C51函數(shù)是不支持再入的。
一個函數(shù)復制了一份,另起了一個名字

可以這樣解決(這個函數(shù)內部不能有函數(shù)),但你必須確保業(yè)務邏輯不會有問題。

中斷調用的函數(shù)一般只被中斷調用,所以沒有必要寫成函數(shù)。
作者: newlined    時間: 2022-7-13 15:42
datouyuan 發(fā)表于 2022-7-12 17:35
C51中斷中使用函數(shù)是很不好的習慣。
你把中斷中的函數(shù)改成不是函數(shù),以代碼空間換時間和RAM。
應該能解決 ...

您說的對,開始我并沒有理解您的意思,剛才看中斷中的函數(shù),我想到,假設在中斷中,我使用了R0,然后調用函數(shù),在函數(shù)中我又使用了R0,從函數(shù)返回后,R0的值肯定變了.好在我用R0都是用來訪問間接尋址的地址,隨用隨賦值,要是用R0保存某個數(shù)據(jù)的話,調用函數(shù)前,可不可以PUSH到堆棧,調用函數(shù)后再POP?
作者: datouyuan    時間: 2022-7-13 15:52
newlined 發(fā)表于 2022-7-13 15:42
您說的對,開始我并沒有理解您的意思,剛才看中斷中的函數(shù),我想到,假設在中斷中,我使用了R0,然后調用 ...

當然可以。但這又會增加堆棧需求。
匯編代碼要自己考慮如何保護現(xiàn)場,恢復現(xiàn)場。

C51編譯器能根據(jù)上下文,自動做好保護現(xiàn)場,恢復現(xiàn)場。
好在我用R0都是用來訪問間接尋址的地址,隨用隨賦值

你這種做法沒有用。這個操作不是原子操作,中斷會發(fā)生在操作中間,發(fā)生中斷后,不能恢復現(xiàn)場。
作者: newlined    時間: 2022-7-13 16:11
datouyuan 發(fā)表于 2022-7-13 15:52
當然可以。但這又會增加堆棧需求。
匯編代碼要自己考慮如何保護現(xiàn)場,恢復現(xiàn)場。

您是說中斷自己不能恢復現(xiàn)場,需要人為的PUSH,POP來恢復現(xiàn)場?而函數(shù)調用,C51的編譯器可以調用函數(shù)時,用堆棧保存PC的值,調用完,函數(shù)執(zhí)行到RET,PC的值自動恢復,其他寄存器的值也自動恢復?
作者: datouyuan    時間: 2022-7-13 17:12
newlined 發(fā)表于 2022-7-13 16:11
您是說中斷自己不能恢復現(xiàn)場,需要人為的PUSH,POP來恢復現(xiàn)場?而函數(shù)調用,C51的編譯器可以調用函數(shù)時, ...

只有PC值能自動恢復,其它值得恢復要靠軟件操作。
作者: newlined    時間: 2022-7-13 17:22
是這樣,明天我仿真下,再仔細領會您的意思。
作者: 188610329    時間: 2022-7-13 18:22
newlined 發(fā)表于 2022-7-13 16:11
您是說中斷自己不能恢復現(xiàn)場,需要人為的PUSH,POP來恢復現(xiàn)場?而函數(shù)調用,C51的編譯器可以調用函數(shù)時, ...

你是不是理解 混了?
你如果寫匯編的話, 是不用C51 編譯器, 用A51 編譯器的。
C51 的話, 函數(shù)是可以做成重入函數(shù)的, (即便他編譯報了有重入風險)。
而 A51 的話,你只要用好了 PUSH POP 函數(shù)也可以做成  重入的。比如: 被調用函數(shù),用到 R0, 你只要函數(shù)入口 PUSH AR0, 出口 POP AR0  那么,這個函數(shù),你中斷調用了,一樣現(xiàn)場會被保護。
作者: newlined    時間: 2022-7-14 08:26
Y_G_G 發(fā)表于 2022-7-12 18:20
所謂"死機"和中斷無法跳出,這不過是推測而已,說不定可能就是其它的小問題而已
匯編是很容易出問題的
如 ...

您好,這個可能不方便貼出,隨說是20年的程序,但老板交代過。
經(jīng) 188610329大神提醒,我DPTR沒有保護造成的,但這個寄存器以前就沒有保護,可能是我修改了程序,不保護不行了。匯編是很麻煩,要面面俱到。、z*cx您好,這個可能不方便貼出,隨說是20年的程序,但老板交代過。

作者: newlined    時間: 2022-7-14 08:39
您好,是這樣,程序是匯編的,擴展名是ASM,但是在KEIL C下編譯的,我注意到一個問題,在KEIL C下新建一個匯編工程,它的擴展名是A51,我懷疑這個程序最開始不是在KEIL C下編譯的,后來轉到KEIL C下,KEIL C內部是不是有A51的編譯器?
作者: newlined    時間: 2022-7-14 08:45
Y_G_G 發(fā)表于 2022-7-12 18:20
所謂"死機"和中斷無法跳出,這不過是推測而已,說不定可能就是其它的小問題而已
匯編是很容易出問題的
如 ...

您好,剛才回復過,提交時不小心按錯了鍵,出現(xiàn)了亂碼,可能審核不通過。
這個程序,雖說是20年前的了,但老板有交代,不方便貼出。
經(jīng)188610329 大神提醒,我懷疑是DPTR沒有保護造成的,我補充了一些代碼,可能是不保護不行了。
作者: newlined    時間: 2022-7-14 09:57
datouyuan 發(fā)表于 2022-7-13 17:12
只有PC值能自動恢復,其它值得恢復要靠軟件操作。

剛才仿真了下,只看的R0,在中斷下,給它賦一個值,比如說5,然后調用一個函數(shù),在函數(shù)中對R0修改為8,函數(shù)執(zhí)行完后,再返回調用它的那個中斷,發(fā)現(xiàn)R0的值還是8,看來在中斷中使用函數(shù)真不是一個好的習慣。
作者: Hephaestus    時間: 2022-7-14 19:44
你的軟件用到了idata了嗎?
作者: 188610329    時間: 2022-7-14 19:55
newlined 發(fā)表于 2022-7-14 08:39
您好,是這樣,程序是匯編的,擴展名是ASM,但是在KEIL C下編譯的,我注意到一個問題,在KEIL C下新建一個 ...

KEIL 就是編譯器,
KEIL C51 是在KEIL 下的 51單片機 用的C語言
KEIL A51 是在KEIL 下的 51單片機 用的A語言
你既然是 用的匯編,后綴還是 ASM 就肯定是用的 A51 編譯,怎么可能用 C51編譯?
兩者編譯方式完全不一樣, KEIL中的提示也不一樣,一個是: compiling xxxxxxx.C...  一個是:assembling xxxxxxx.ASM...
作者: 188610329    時間: 2022-7-14 20:06
newlined 發(fā)表于 2022-7-14 09:57
剛才仿真了下,只看的R0,在中斷下,給它賦一個值,比如說5,然后調用一個函數(shù),在函數(shù)中對R0修改為8,函 ...

如果,你進入中斷,用的是不同的寄存器組, 那么,你對 R0 的操作,是不會影響 主程序的 R0的,因為,此 R0 不是 彼R0,  相對于這個問題,我反而覺得,你應該是 對某些寄存器的 現(xiàn)場保護沒有做好。你新加的內容才是關鍵。
而且,你說的部分內容,我覺得非常困惑,“以前堆棧留的比較小,只有20個字節(jié),后來我又找了幾個加上了,但還是不大! 你這個  只有20字節(jié)是什么來的? 又找了幾個加上 是怎么加的? 正常情況下, 一般分配完內存地址后, 我們就在  內存末尾 打上 Stack,作為堆棧的起點,賦值給SP, 假定SP 為 80H, 那么 從80H 往后 到 0FFH 相當于都是 堆棧用的。 所以不存在原來 堆棧只有多少,然后你還“找了幾個”的情況存在。 所以,你描述的這個情況,到底是什么情況?
作者: Y_G_G    時間: 2022-7-14 20:43
newlined 發(fā)表于 2022-7-14 08:26
您好,這個可能不方便貼出,隨說是20年的程序,但老板交代過。
經(jīng) 188610329大神提醒,我DPTR沒有保護造 ...

DPTR,R0,R1什么的,你在多個地方用到了,就進行保護,如果用不到就不用管
匯編子程序的原則是你在調用的時候,如果在其它地方你也用到某個地址的RAM.那就進行保護,每個子程序都要保護
常用的就是R0R1之類,比如DELAY:               
        PUSH R0        PUSH R1
        MOV R0,#100
        MOV R1,#100
NEXT:
        DJNZ R1,NEXT
        DJNZ R0,NEXT
        POP R1
        POP R0
        RET

這就是一個延時程序,這樣的話,你在其它的程序中包括中斷,再使用R0R1,也同樣的PUSH,POP,那么這個程序就不會出問題
假設你整個完整的程序中,只有這一個地方用到R0R1,那么,這個PUSH,POP就是多余的
還有R0-R7這幾個地址,默認的情況下,在整個程序地址是固定的,你在任何一個地方修改了R0-R7其中一個的值,它在其它地方的也是會改變的
假設你延時中用到了R0,中斷中也用到R0,那么當延時程序被中斷打斷之后,如果不用PUSH,POP保護R0的話,等到中斷中修改了R0之后
RETI返回之后,R0的值就是中斷中最后操作的值,那么,你這個延時程序就出錯了
你不要都是想著堆棧滿不滿的,基本不會的,完全可以先不管堆棧的大小問題
專注找你自己代碼的問題

作者: 188610329    時間: 2022-7-14 21:07
Y_G_G 發(fā)表于 2022-7-14 20:43
DPTR,R0,R1什么的,你在多個地方用到了,就進行保護,如果用不到就不用管
匯編子程序的原則是你在調用的時 ...

R0~R7 屬于 通用寄存器, 是不能PUSH的。
如果是完全自己寫的代碼,配合USING 可以用 PUSH AR0~AR7 方式來PUSH 但是,如果不是自己的代碼,而4組寄存器 一直輪換在用的話,非常不好處理。
所以,還是比較建議樓主多注重一下,改了部分的代碼,到底涉及到哪些東西,針對性的處理一下,而不是去動那些既存的,由來已久的代碼。
簡易樓主,全程序  查找一下, "MOV  PSW,#"   看看具體用了幾組 通用寄存器,為了影響最小化,假定,之前只用了 3組寄存器,建議樓主加的部分代碼全都用 第四組 通用寄存器,這樣,可以把影響降到最低。
作者: Hephaestus    時間: 2022-7-14 21:41
函數(shù)的好處是可以復用,一處定義多處調用,節(jié)約程序空間。

但是對于大多數(shù)8位單片機編譯器,包括C51,沒有按照標準c語言的做法——在進入函數(shù)的時候在堆棧上臨時分配局部變量(具體做法可以看《數(shù)據(jù)結構》關于遞歸的那一部分),因為8位機間接尋址指令和空間非常有限,按標準c語言的做法,最后生成的機器碼會非常龐大,占用的RAM空間也非常多,對本來就很少的資源造成巨大浪費。

C51的做法是對函數(shù)調用關系進行分析,然后靜態(tài)分配變量,以樓主的問題為例A->A1->A2->A3,那么A的局部變量可能是27H~29H,A1的局部變量占用2AH~2FH,A2的局部變量占用30H~35H,A3的局部變量占用36H以后空間。這種做法導致中斷調用的函數(shù)是無法復用的!比如中斷B調用了函數(shù)B1,B1的局部變量占用33H這個空間,那么如果主程序調用B1,局部變量在運算過程中發(fā)生了中斷,中斷B1修改了局部變量,那么中斷結束后,回到主程序,局部變量33H的內容被修改了,那么主程序顯然就會執(zhí)行錯誤。同樣的原因,不僅主程序不能調用B1,其他中斷C、D也不能調用B1。。∵@個函數(shù)B1是中斷B專用的。

綜上所述,使用函數(shù),可以復用的優(yōu)點在中斷這里不存在!在中斷中調用函數(shù)只有各種各樣的缺點,一點好處都沒有,樓主為什么要這么做?
作者: Y_G_G    時間: 2022-7-14 22:01
188610329 發(fā)表于 2022-7-14 21:07
R0~R7 屬于 通用寄存器, 是不能PUSH的。
如果是完全自己寫的代碼,配合USING 可以用 PUSH AR0~AR7 方式 ...

好久不用匯編了,忘記了
作者: newlined    時間: 2022-7-15 10:58
Hephaestus 發(fā)表于 2022-7-14 19:44
你的軟件用到了idata了嗎?

您好,用到了
作者: newlined    時間: 2022-7-15 11:08
Hephaestus 發(fā)表于 2022-7-14 21:41
函數(shù)的好處是可以復用,一處定義多處調用,節(jié)約程序空間。

但是對于大多數(shù)8位單片機編譯器,包括C51,沒 ...

您好,在中斷中有十幾行語句,用了幾次,早先我不知道函數(shù)調用會產(chǎn)生一些列的問題,就把它們寫成了函數(shù).前邊datouyuan大神也已經(jīng)指出了.
作者: newlined    時間: 2022-7-15 11:15
188610329 發(fā)表于 2022-7-14 21:07
R0~R7 屬于 通用寄存器, 是不能PUSH的。
如果是完全自己寫的代碼,配合USING 可以用 PUSH AR0~AR7 方式 ...

您好,四組寄存器都已經(jīng)用到了,其中有1組還是有2個同級別的低級中斷共用的.考慮到這2個低級別中斷不會同時被單片機響應,也沒有用它們傳遞參數(shù),只是用R0和R1訪問間接地址,應該是不會沖突吧。

作者: Y_G_G    時間: 2022-7-15 14:06
我感覺你還是把代碼上傳上來吧
既然你說是多年前的程序,那就說明以前的代碼是對的,問題估計就是出現(xiàn)你自己的代碼上
把你自己的代碼上傳就行,不用把整個程序都上傳的
你的代碼在程序中是哪里調用的,如果是中斷,就把中斷中調用的位置和代碼上傳就行
MOV R0,#20H  之類的,它總不會連這也算商業(yè)機密吧
現(xiàn)在都是一群人在亂猜,壓根就不知道你程序一個大概的流程
很有可能這只是一個簡單的問題而已,卻搞得那么復雜
作者: 188610329    時間: 2022-7-15 15:34
newlined 發(fā)表于 2022-7-15 11:15
您好,四組寄存器都已經(jīng)用到了,其中有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 這個函數(shù), 重入了, 但是,有影響么? 沒有!!, 只要確保  主程序所在 寄存器組, 不等于 中斷使用的寄存器組, 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             ; 函數(shù)內部,保護現(xiàn)場, 這也是常規(guī)意義的, “可重入函數(shù)”
MOV   R7,#100H
DJNZ  R7,$
POP    AR7
RET


這個例子,我們依然 重入了,   但是,有影響么??, 沒有。。!
所以,重入問題,是完全可以  靠人解決的, 編譯器,絕對不會比人“更高級”,你既然現(xiàn)在在折騰匯編代碼,就不要考慮 C 可能的問題,畢竟C 你是依賴編譯器的,而,匯編,你只能依靠你自己(因為你的代碼要“保密”,所以你也靠不到我們)。


基本上,目前從你這里得到的信息,只能幫到你這里了, 當然,如果你回復了 我在34樓的問題,也許可以給多你一點支持。


作者: Hephaestus    時間: 2022-7-15 20:13
現(xiàn)在越來越混亂了。樓主在20樓說重入警告,這是c51獨有的,匯編的重入問題完全由用戶控制,根本不可能出現(xiàn)警告,然后樓主又說用匯編。樓主連自己用的是個啥都不知道,大家還是散了吧。
作者: newlined    時間: 2022-7-16 08:06
Y_G_G 發(fā)表于 2022-7-15 14:06
我感覺你還是把代碼上傳上來吧
既然你說是多年前的程序,那就說明以前的代碼是對的,問題估計就是出現(xiàn)你自己 ...

是這樣,是一家小公司,硬件都在那里,大家都可以看到,軟件是這家公司的支柱,老板有交代,不好貼,請大家原諒。在在大家的指導下,把DPTR保護后,跑了1天多,軟件不死機了,謝謝大家。
作者: newlined    時間: 2022-7-16 08:12
本帖最后由 newlined 于 2022-7-16 08:42 編輯
188610329 發(fā)表于 2022-7-15 15:34
看你另外一個帖子, 你再找單片機原理的書? 所以,問題出在,你并不理解你的單片機?
我之前34樓的問題,你也 ...

這一段程序我看懂了,函數(shù)重入后,對這個函數(shù)用到的寄存器,函數(shù)被調用之前用到,函數(shù)調用完成后,還要用到的要保護后,就不會出問題。有了問題后,總是漫天找問題。
作者: newlined    時間: 2022-7-16 08:24
Hephaestus 發(fā)表于 2022-7-15 20:13
現(xiàn)在越來越混亂了。樓主在20樓說重入警告,這是c51獨有的,匯編的重入問題完全由用戶控制,根本不可能出現(xiàn) ...

不好意思,最近記憶力嚴重衰退,連老板都笑話我,也許我記串了。有一次,一個問題我們兩個人,商討后解決了,過了幾天,我說那個問題怎么辦,老板說我們兩個商量后不是已經(jīng)解決了嗎,如何如何解決的,我聽后好久才回憶起來。
對51的匯編,我是邊學邊用,實際上,我一般是先用KEIL C寫出來,運行通過后再按照這個思路,改成匯編。這個匯編程序中,有一個函數(shù),主程序和中斷都調用,也許是出問題,我就復制了一份,另起一個名字,函數(shù)內部用到的直接尋址的地址,用另外的地址,運行通過了。
作者: lksbbs    時間: 2022-7-16 08:38
這么復雜的程序,空間夠用的情況下直接使用全局變量不好么,隨便你咋中斷,隨便你咋調用,我都不用壓堆的.
作者: newlined    時間: 2022-7-16 08:42
188610329 發(fā)表于 2022-7-14 20:06
如果,你進入中斷,用的是不同的寄存器組, 那么,你對 R0 的操作,是不會影響 主程序的 R0的,因為,此  ...

程序中,四個工作區(qū)都用到了,其中兩個低級中斷共用一個區(qū),因為不會同時被單片機響應,不會出問題。以前的堆棧,設在50H到64H之間,64H到80H有的地址用了,有的還空著,我就把用的改在一起,空出來的跟64H連在一起做堆棧.
作者: Hephaestus    時間: 2022-7-16 08:51
newlined 發(fā)表于 2022-7-16 08:42
程序中,四個工作區(qū)都用到了,其中兩個低級中斷共用一個區(qū),因為不會同時被單片機響應,不會出問題。以前的堆 ...

越來越混亂了,我在32樓問你用到idata了嗎?你在39樓回答用到了。只有用到了80h以上空間才會用到idata,而C51編譯器會自動把堆棧分配到idata上面。如果你用匯編的話,也應該利用高128字節(jié)來做堆棧,然而你又說堆棧在50h和64h之間???到底是怎么寫的?這種空對空的說根本解決任何問題,按42樓建議,如果原程序是商業(yè)機密你不可以貼,但是你自己寫的那部分,你拿捏不準的地方不貼出來,沒有討論的價值,也不太可能有人能提出關鍵性建議。
作者: datouyuan    時間: 2022-7-16 10:13
本帖最后由 datouyuan 于 2022-7-16 10:22 編輯
newlined 發(fā)表于 2022-7-16 08:42
程序中,四個工作區(qū)都用到了,其中兩個低級中斷共用一個區(qū),因為不會同時被單片機響應,不會出問題。以前的堆 ...

看來你這代碼問題很大。
51的堆棧要安排在ram的最后。
例如你需要32字節(jié)堆棧,那么堆?臻g為0xe0~0xff。初始化SP要小于等于0xe0。
例如你需要48字節(jié)堆棧,那么堆?臻g為0xd0~0xff。初始化SP要小于等于0xd0。
小于0x80區(qū)域(data區(qū)),可以直接尋址,變量盡量安排在此區(qū)域。
>=0x80區(qū)域(idata區(qū)),只能間接尋址,變量在data區(qū)安排不下時(重要!。。瑢(shù)組變量、使用不頻繁的變量安排在此區(qū)域。

編寫匯編代碼時,要確定所有變量使用了多少字節(jié),例如使用了150個字節(jié),那么SP應等于150,那么堆?臻g為150~0xff(共106字節(jié))。
這帖子中,我提到的僅僅是些基本原則,估計樓主獨立解決應該比較困難,建議樓主將代碼交給有經(jīng)驗的幫忙。

作者: datouyuan    時間: 2022-7-16 11:58
newlined 發(fā)表于 2022-7-16 08:24
不好意思,最近記憶力嚴重衰退,連老板都笑話我,也許我記串了。有一次,一個問題我們兩個人,商討后解決 ...

這樣只是解決了編譯器報警問題。
前提你要保證你的業(yè)務邏輯是正確的,假如業(yè)務邏輯有bug,即使你添加了再入屬性,或者復制一份,都不能解決問題。
作者: 188610329    時間: 2022-7-16 18:00
newlined 發(fā)表于 2022-7-16 08:42
程序中,四個工作區(qū)都用到了,其中兩個低級中斷共用一個區(qū),因為不會同時被單片機響應,不會出問題。以前的堆 ...

堆棧不能這么搞, 堆棧是向上增長的,所以應該分配在內存的最末尾,而且,我不知道你用的什么單片機,最初的編寫環(huán)境是怎么樣的,照理你這代碼 如果用的標準 A51 規(guī)范來寫,你所有的變量聲明不用  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

你那個代碼,到底是怎么折騰的,能夠把 堆棧地址夾在那么奇怪的一個地方的?



作者: newlined    時間: 2022-7-18 16:39
datouyuan 發(fā)表于 2022-7-16 10:13
看來你這代碼問題很大。
51的堆棧要安排在ram的最后。
例如你需要32字節(jié)堆棧,那么堆棧空間為0xe0~0xff ...

堆棧以前就是那樣設定的。再就是提到堆?梢园才旁80H到FFH之間,我以前不知道,我以為這些地址只可以利用R0,R1間接尋址來訪問,使用。
作者: newlined    時間: 2022-7-18 16:42
datouyuan 發(fā)表于 2022-7-16 11:58
這樣只是解決了編譯器報警問題。
前提你要保證你的業(yè)務邏輯是正確的,假如業(yè)務邏輯有bug,即使你添加了 ...

業(yè)務邏輯保證是正確的,里邊用到的地址都改了,不會沖突。
作者: newlined    時間: 2022-7-18 16:53
188610329 發(fā)表于 2022-7-16 18:00
堆棧不能這么搞, 堆棧是向上增長的,所以應該分配在內存的最末尾,而且,我不知道你用的什么單片機,最 ...

我知道堆棧是向上生長的,程序中以前就是那樣設定的,我也不知道為什么,我設定的話起碼會設在80H之前,我原以為堆棧的最大地址是7FH,80H到FFH是間接地址,不可以做堆棧。程序對DPTR保護后,運行了2天。沒有再出以前的問題。在這里請教下,您前邊的帖子里提到的指令 PUSH AR0  ,PUSH  AR1 是在哪里能學到,一般的書里都沒有。再就是這個帖子里的偽指令,在哪里可以學到?一般的書里也沒有,謝謝。
作者: newlined    時間: 2022-7-18 17:01
買了這2本書,也沒有起多大作用,講到中斷,也只是說,保護現(xiàn)場,恢復現(xiàn)場,也沒有具體的說要恢復那些東西,還不如在本帖中學到的東西多

IMG_20220718_165504[1].jpg (3.82 MB, 下載次數(shù): 717)

IMG_20220718_165504[1].jpg

作者: newlined    時間: 2022-7-18 17:03
還有這一本

IMG_20220718_165516[1].jpg (3.14 MB, 下載次數(shù): 705)

IMG_20220718_165516[1].jpg

作者: newlined    時間: 2022-7-18 17:07
@ 188610329,單片機是STC 8H
作者: newlined    時間: 2022-7-18 17:08
也許看這些書需要一定的功力,我還遠遠不夠。
作者: 188610329    時間: 2022-7-18 19:36
newlined 發(fā)表于 2022-7-18 16:53
我知道堆棧是向上生長的,程序中以前就是那樣設定的,我也不知道為什么,我設定的話起碼會設在80H之前, ...

AR0~AR7 是偽地址, 屬于KEIL A51 專用,在KEIL的幫助里面可以找到,一般講匯編的書,不會寫,要講KEIL的書才會寫, 主要是為了彌補 A51 指令的空白, 你如果已經(jīng)打了一點匯編基礎,你會知道, 你如果要把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 的替補用。

最后,說一下,代碼是死的,就看你怎么用。說到底,還是要去體會和理解 單片機的工作原理。




作者: Y_G_G    時間: 2022-7-19 01:18
newlined 發(fā)表于 2022-7-18 17:03
還有這一本

你要看的是這一本書,網(wǎng)上應該可以搜索到不要錢的PDF版本,也可以買二手的,馬云家30塊左右,超過這個價錢就不要買了,我買的時候才二十多塊
匯編沒有那么復雜的,不要糾結堆棧了,8051是軟件堆棧,我從來都不去管它的,等到你用到了硬件堆棧,你才會體驗到匯編的毛病而且8051是復雜指令,功能多得很,你要是用到精簡指令,你就會更加的體會到匯編的無聊
現(xiàn)在的編譯器效率已經(jīng)非常不錯了,優(yōu)先選擇C語言,實在不行了再用匯編

作者: newlined    時間: 2022-8-1 08:16
前一段時間,我母親生病住院,我去陪床,我母親出院那一天,我父親很高興,去做飯,又燙傷了腳,我沒有時間仔細看這個帖子,很抱歉。
作者: newlined    時間: 2022-8-1 08:21
188610329 發(fā)表于 2022-7-18 19:36
AR0~AR7 是偽地址, 屬于KEIL A51 專用,在KEIL的幫助里面可以找到,一般講匯編的書,不會寫,要講KEIL的 ...

看了這個帖子,我明白了,AR7是不是已經(jīng)宏定義成了07H?我以前對地址了解不透徹,看來我要對這方面仔細揣摩。
作者: newlined    時間: 2022-8-1 08:25
Y_G_G 發(fā)表于 2022-7-19 01:18
你要看的是這一本書,網(wǎng)上應該可以搜索到不要錢的PDF版本,也可以買二手的,馬云家30塊左右,超過這個價錢就 ...

在網(wǎng)上我搜到了這本書的下載網(wǎng)站,但好像需要注冊,需要電話號碼什么的。在淘寶上有一家賣30幾元,但需要45天發(fā)貨,不知道怎么回事。
作者: Y_G_G    時間: 2022-8-1 13:17
這么些天了,還沒有搞明白這個?
00H-1FH  這就是R0-R7的幾個工作組
20H-2FH 這是可以位尋址的,位地址是從00H開始的,字節(jié)地址也有00H,通過指令來區(qū)分是位操作還是字節(jié)操作
SETE 00H 這是位操作
MOV 00H,#255 這是字節(jié)操作

一般是編寫代碼的時候,00H-1FH留給工作組使用,20H-2FH留給位尋址用
所以,你一般看到的用到內存的,都是多30H開始的


作者: 188610329    時間: 2022-8-1 17:21
newlined 發(fā)表于 2022-8-1 08:21
看了這個帖子,我明白了,AR7是不是已經(jīng)宏定義成了07H?我以前對地址了解不透徹,看來我要對這方面仔細揣 ...

AR7  不一定是 07H,  也可能是 0FH, 17H 或者 1FH。 這牽涉到另一條 偽指令: USING  ,  當代碼向上搜索 找到USING 0 則編譯的時候會把  PUSH AR7  當作 PUSH 07H 來編譯, 如果找到 USING 1 則會在編譯的時候 把 PUSH AR7 當作 PUSH 0FH 來編譯。 所以 AR7 不是絕對的,是可變的,好處是,如果更換換寄存器組,你可以不需要一行行代碼去把 代表R7地址手動更改。缺點是,USING 必需要控制好。
作者: newlined    時間: 2022-8-4 09:59
188610329 發(fā)表于 2022-8-1 17:21
AR7  不一定是 07H,  也可能是 0FH, 17H 或者 1FH。 這牽涉到另一條 偽指令: USING  ,  當代碼向上搜索  ...

這一段我還不理解,但我會盡量避免出現(xiàn)這個問題,以后我會慢慢領會,F(xiàn)在我遇到一個新的問題,就是單片機是STC的8H系列,您肯定知道,它對扇區(qū)的讀寫命令是類似這樣的:

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

        MOV                          ISP_CONTR,#ENABLE_ISP
        MOV                               ISP_CMD,#01H                  ; 讀扇區(qū)
        MOV                          ISP_TPS,#0CH  ;
這一段程序在主程序中用到了,在一個中斷中也用到了,在中斷中,對 ISP_CMD,ISP_CONTR進行壓棧保護?就是 PUSH ISP_CMD,PUSH ISP_CONTR ,退出中斷時 再 POP ISP_CONTR,POP ISP_CMD,這樣保護能達到目的嗎?編譯是通過了,但我沒有把握,您看可以嗎?
作者: newlined    時間: 2022-8-4 11:12
Y_G_G 發(fā)表于 2022-8-1 13:17
這么些天了,還沒有搞明白這個?
00H-1FH  這就是R0-R7的幾個工作組
20H-2FH 這是可以位尋址的,位地址是從0 ...

這個地址我基本明白了,但對地址的運用上,還是不行,我可能需要一些實踐才能領會。
作者: 188610329    時間: 2022-8-4 14:56
newlined 發(fā)表于 2022-8-4 09:59
這一段我還不理解,但我會盡量避免出現(xiàn)這個問題,以后我會慢慢領會。現(xiàn)在我遇到一個新的問題,就是單片機 ...

要說保護的話……
ISP_CMD, ISP_CONTR,之外,ISP_ADDRH, ISP_ADDRL 這些都要一起保護。
但是這些都沒用。嚴格來講,STC系列而言, ISP/IAP操作是唯一無法保護現(xiàn)場的操作。
因為: IAP_TRIG  這個啟動指令, 需要連續(xù)發(fā)送兩條 0x5A, 和 0xA5 的啟動指令,這個是絕對不允許被打斷的。所以,一般,會需要在程序中,先  CLR EA,禁止中斷, 等所有 IAP操作完成 后 再次 SET EA 允許中斷,這個才是關鍵。所以說,了解單片機的工作原理,還是重點中的重點。而如果在 主程序中已經(jīng)禁止了中斷,那么,相關操作就不需要 被PUSH和POP來現(xiàn)場保護了。

最后:IAP_TPS 是STC8系列用來設置 IAP操作速度的,理論上來說,只要你單片機不要不停的改變速度, MOV IAP_TPS,#0CH 之需要執(zhí)行一次,里面的值是不會,也不需要再修改的。所以,通常來說,IAP_TPS 也是沒有必要PUSH和POP的。
作者: newlined    時間: 2022-8-4 15:56
188610329 發(fā)表于 2022-8-4 14:56
要說保護的話……
ISP_CMD, ISP_CONTR,之外,ISP_ADDRH, ISP_ADDRL 這些都要一起保護。
但是這些都沒 ...

真是的,您提醒了我,ISP_ADDRH, ISP_ADDRL我沒有保護,程序運行主程序*ISP_ADDRH, ISP_ADDRL
作者: newlined    時間: 2022-8-4 16:00
筆記本的鍵盤不好用,可能是我前一段時間摔了一下.
作者: newlined    時間: 2022-8-4 16:11
程序運行過程中,有是會出現(xiàn)問題,可能是這個原因,兩條 0x5A, 和 0xA5 的啟動指令之前,中斷是關的,之后打開了,實際上這個是以前的程序,我后來補充了一些程序中,尤其是中斷中用到了操作EEPROM的語句,沒有對主程序中操作EEPROM的現(xiàn)場保護。我如果對DPTR,ISP_CMD, ISP_CONTR,ISP_ADDRH, ISP_ADDRL保護了,就沒有問題了吧。
作者: 188610329    時間: 2022-8-4 18:10
手機打字,我就長話短說了。 首先,trig指令發(fā)送前關中斷,發(fā)送后開中斷,是最低底線。 其次,我不太清楚你程序如何設計,stc8系列是支持movc讀取eeprom的,所以,我的話,所有讀指令都是movc完成的,寫指令才會用iap指令,所以基本上是不需要保護iap相關寄存器,只需要保護dptr就可以的。 最后,牽涉到架構了,如果有可能,所有的寫,擦eeprom的行為,集中在一個函數(shù),或者一個中段中完成,對整個程序是最優(yōu)的。
作者: newlined    時間: 2022-8-6 14:32
讀指令movc可以不需要保護iap相關寄存器嗎?我仔細揣摩下。
作者: 188610329    時間: 2022-8-7 17:59
newlined 發(fā)表于 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系列的手冊。
作者: newlined    時間: 2022-10-6 09:23
Y_G_G 發(fā)表于 2022-7-19 01:18
你要看的是這一本書,網(wǎng)上應該可以搜索到不要錢的PDF版本,也可以買二手的,馬云家30塊左右,超過這個價錢就 ...

我買回了這本書,正在學習中,謝謝。
作者: newlined    時間: 2022-10-6 09:28
188610329 發(fā)表于 2022-8-7 17:59
STC的 Eeprom 是把 Flash 空間模擬成 Eeprom 來操作,尤其是STC8系列,他在物理上就是 ROM的一部分。所以 ...

好的,您說的,我慢慢理會。這一段時間,正看程序,原程序中bug不少,比如,一個子程序,有時用LCALL調用,當然,這是對的,竟然有時用LJMP跳轉過去,我都不知道最后執(zhí)行了RET之后去了哪里。
作者: 188610329    時間: 2022-10-6 15:00
newlined 發(fā)表于 2022-10-6 09:28
好的,您說的,我慢慢理會。這一段時間,正看程序,原程序中bug不少,比如,一個子程序,有時用LCALL調用 ...

唉…… 這是基礎中的基礎啊……
給你舉個例子吧:
有個函數(shù):
SEND_HALF_BCD:
ANL   A,#0FH
ORL   A,#30H

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

你要發(fā)送  一個BCD出去, 可以制作這么一個函數(shù):
_Send_BCD:         ;通過R7傳入
MOV   A,R7
SWAP A
LCALL  SEND_HALF_BCD
MOV   A,R7
LCALL  SEND_HALF_BCD

RET
通常, 你覺得應該這么寫對吧? 但是,這么寫其實是完全沒有 用到匯編的優(yōu)勢 。
所以,既然用了匯編,通常用如下寫法:
_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  還是你沒理解  程序本身的 真正目的?




作者: newlined    時間: 2022-10-25 13:50
188610329 發(fā)表于 2022-10-6 15:00
唉…… 這是基礎中的基礎啊……
給你舉個例子吧:
有個函數(shù):

你好,這個例子,我一直試圖理解,但我沒有想通。我是這樣理解的,LCALL 一個函數(shù)時,堆棧會保存當前調用時的地址,RET 時,這個地址會恢復到PC程序計數(shù)器中,如果LJMP到一個函數(shù),堆棧不會保存當前地址,執(zhí)行到RET時,堆棧里的兩個字節(jié),恢復到PC程序計數(shù)器中,這個應該是錯誤的吧。
作者: 188610329    時間: 2022-10-25 14:38
newlined 發(fā)表于 2022-10-25 13:50
你好,這個例子,我一直試圖理解,但我沒有想通。我是這樣理解的,LCALL 一個函數(shù)時,堆棧會保存當前調用 ...

CALL 一次,就會把當前地址放入  棧   然后跳轉,讀到RET  從 棧里 讀出地址, 然后返回  CALL 的地方。
作者: 人中狼    時間: 2022-10-25 14:43
lcall和ljmp是不同的機制,lcall會壓棧,ljmp不對棧操作,lcal必須有ret,ljmp一定不能有ret,51除了硬件自動壓棧的以外,其他都是人工壓棧的
作者: 188610329    時間: 2022-10-25 14:58
newlined 發(fā)表于 2022-10-25 13:50
你好,這個例子,我一直試圖理解,但我沒有想通。我是這樣理解的,LCALL 一個函數(shù)時,堆棧會保存當前調用 ...

CALL 一次  就會把地址  存入棧  然后  跳轉執(zhí)行, 運行到 RET 就會從 棧中取出 地址, 返回CALL 的地方。
CALL 和 RET  應該是 成對的關系。沒錯吧? C 就是這么做的。

然后,你 CALL 一個函數(shù), 這個函數(shù),里面還要 調用 另一個函數(shù),  
這個時候,應該  CALL  然后, CALL  然后 RET  然后 RET  返回最初的地方對不對?
那么,我簡單點,不要CALL 直接JMP可以么?  CALL   JMP   RET  是不是結果一樣?

再舉個簡單的例子吧

就是我之前舉的那個例子:

SEND_HALF_BCD:
ANL   A,#0FH
ORL   A,#30H
SEND_BYTE:
JNB     TI,$
CLR     TI
MOV   SBUF,A
RET

這是一個復合函數(shù),

CALL SEND_HALF_BCD  就能把 半個BCD 轉成 ASCCII 發(fā)出去。
CALL SEND_BYTE      就能直接把 一個字節(jié) 發(fā)出去,這個能理解么?

到這里為止,是不是沒有問題? 能理解么?  能的話,我們往下看。

換個寫法:
SEND_HALF_BCD:
ANL   A,#0FH
ORL   A,#30H
CALL        SEND_BYTE
RET

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

這樣,徹底拆成兩個函數(shù),你能看明白么?  
作用和剛才一樣,這是純C的寫法。 你比較喜歡這樣寫是么?
此時如果 CALL SEND_HALF_BCD
這就是你認為應該的, 兩次CALL 兩次RET 對吧?


然后,我們再換個寫法:

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

SEND_HALF_BCD:
ANL   A,#0FH
ORL   A,#30H
LJMP SEND_BYTE

這樣, CALL SEND_HALF_BCD   有沒有問題?  假如你說有問題……  那么,我們換個個。

SEND_HALF_BCD:
ANL   A,#0FH
ORL   A,#30H
LJMP SEND_BYTE

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

這樣總沒問題了吧?

匯編之所以比C 效率高,就是可以避免無意義的  RET。 節(jié)約非必要的 RET 是提高效率的一種必要手段。這真的是基礎中的基礎了……

作者: 人中狼    時間: 2022-10-25 15:17
樓上的例子有點特例了,像send_byte可以作為一個公用的子程序,如果用jmp,ret會不好控制,除非只有一個地方專門調用,即使是匯編,最好也是模塊話,條理會清晰很多
作者: Hephaestus    時間: 2022-10-25 18:44
188610329 發(fā)表于 2022-10-25 14:58
CALL 一次  就會把地址  存入棧  然后  跳轉執(zhí)行, 運行到 RET 就會從 棧中取出 地址, 返回CALL 的地方。 ...
  1. %*DEFINE(SEND_BYTE)(
  2.     JNB     TI,$
  3.     CLR     TI
  4.     MOV   SBUF,A
  5. )
  6. SEND_HALF_BCD:
  7.     ANL   A,#0FH
  8.     ORL   A,#30H
  9.     %SEND_BYTE       
復制代碼


我這個效率比你的還要高,連LJMP都省掉了,你的匯編還沒學到家。

我用的是Intel MCS-51 MACRO ASSEMBLER, V2.2,如果用Keil A51編譯不通過請查手冊。
作者: 188610329    時間: 2022-10-25 19:45
Hephaestus 發(fā)表于 2022-10-25 18:44
我這個效率比你的還要高,連LJMP都省掉了,你的匯編還沒學到家。

我用的是Intel MCS-51 MACRO ASS ...

人家問 CALL 和 RET,  你發(fā)個 沒CALL 沒 RET 的 代碼
作者: newlined    時間: 2022-10-26 02:20
Hephaestus 發(fā)表于 2022-10-25 18:44
我這個效率比你的還要高,連LJMP都省掉了,你的匯編還沒學到家。

我用的是Intel MCS-51 MACRO ASS ...

您這個看起來更簡單,俗話說人外有人,天外有天,但我現(xiàn)在是更看不懂,感謝大家提供不同的代碼讓我們這些初學者揣摩。
作者: Hephaestus    時間: 2022-10-26 03:38
newlined 發(fā)表于 2022-10-26 02:20
您這個看起來更簡單,俗話說人外有人,天外有天,但我現(xiàn)在是更看不懂,感謝大家提供不同的代碼讓我 ...

這不是“人外有人,天外有天”的問題,時間久了你就能明白最重要的學問是數(shù)學和英語。
作者: 188610329    時間: 2022-10-26 20:02
Hephaestus 發(fā)表于 2022-10-26 03:38
這不是“人外有人,天外有天”的問題,時間久了你就能明白最重要的學問是數(shù)學和英語。

數(shù)一數(shù)你這編譯出來多少字節(jié), 在來講話
先不說人家問的  CALL RET , 你 CALL 和 RET 呢?  忘帶了?
其次, define 到底是什么? 你應該很清楚,沒必要再這里%。
你怎么不把所有代碼  define 只后,你還能 只要一句代碼  就能把智能蔽障小車寫出來呢?
%

作者: Hephaestus    時間: 2022-10-26 20:18
188610329 發(fā)表于 2022-10-26 20:02
所以,先把數(shù)學學好, 數(shù)一數(shù)你這編譯出來多少字節(jié), 在來講話, 然后把語文學好,學學怎么講話。
先不 ...

教你一句成語——不屑一顧。
作者: 188610329    時間: 2022-10-27 00:20
Hephaestus 發(fā)表于 2022-10-26 20:18
教你一句成語——不屑一顧。

教你一句成語——夜郎自大。




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1