reentrant function with _bp

Posted by: 邱小新 at 下午4:52 in

由於 8051 單晶片內部的堆疊空間有限,所以無法像 windows/unix 那樣使用堆疊。一般在 C 語言裏,呼叫函數時會將函數參數及函數使用的局部變數放入堆疊。因為 8051 堆疊空間有限,所以無法使用此方式,而是為每個函數的局部變數及參數設定一個空間來存放。比如下面的範例就是如此,參數 a 及 b 被分配到二個全域變數 _test_a_1_1 及 _test_PARM_2。

541 ;------------------------------------------------------------ 542 ;Allocation info for local variables in function 'test' 543 ;------------------------------------------------------------ 544 ;b Allocated with name '_test_PARM_2' 545 ;a Allocated with name '_test_a_1_1' 546 ;------------------------------------------------------------ 547 ; ../source/main.c:60: int test(int a, int b) 548 ; ----------------------------------------- 549 ; function test 550 ; ----------------------------------------- 551 _test: 552 push ar2 553 push ar3 554 push ar4 555 push ar5 556 mov r2,dph 557 mov a,dpl 558 mov dptr,#_test_a_1_1 559 movx @dptr,a 560 inc dptr 561 mov a,r2 562 movx @dptr,a 563 ; ../source/main.c:62: return a + b; 564 mov dptr,#_test_PARM_2 565 movx a,@dptr 566 mov r2,a 567 inc dptr 568 movx a,@dptr 569 mov r3,a 570 mov dptr,#_test_a_1_1 571 movx a,@dptr 572 mov r4,a 573 inc dptr 574 movx a,@dptr 575 mov r5,a 576 mov a,r2 577 add a,r4 578 mov r2,a 579 mov a,r3 580 addc a,r5 581 mov r3,a 582 mov dpl,r2 583 mov dph,r3 584 pop ar5 585 pop ar4 586 pop ar3 587 pop ar2 588 ret

呼叫方式也是直接把傳遞值直接存入全域變數 _test_PARM_2 裏。

607 ; ../source/main.c:74: test(1, 2); 608 mov dptr,#_test_PARM_2 609 mov a,#0x02 610 movx @dptr,a 611 clr a 612 inc dptr 613 movx @dptr,a 614 mov dptr,#0x0001 615 lcall _test

SDCC 把局部變數被放到全域變數裏,相當於被宣告成靜態變數一樣,只是差在沒宣告 static 而已。由於此種原因,造成此類函數變成不可當成遞迴函數來使用,也就是不能自我呼叫函數。而且在 interrupt function 裏不可以呼叫此類函數,因為當中斷發生在此類函數時,interrupt function 又呼叫一次原來函數,會造成局部變數被修改,而造成不可預期的錯誤。所以需要在函數宣告後加上 __reentrant,讓編譯器按照一般 C 語言方式把局部變數都放到堆疊裏。下面的函數就是如此,把局部變數放到堆疊裏(sp-4)。

536 ;------------------------------------------------------------ 537 ;Allocation info for local variables in function 'test' 538 ;------------------------------------------------------------ 539 ;b Allocated to stack - offset -4 540 ;a Allocated to registers r2 r3 541 ;------------------------------------------------------------ 542 ; ../source/main.c:60: int test(int a, int b) __reentrant 543 ; ----------------------------------------- 544 ; function test 545 ; ----------------------------------------- 546 _test: 547 push ar2 548 push ar3 549 push ar0 550 push ar1 551 push _bp 552 mov _bp,sp 553 mov r2,dpl 554 mov r3,dph 555 ; ../source/main.c:62: return a + b; 556 mov a,_bp 557 add a,#0xf8 558 mov r0,a 559 mov a,@r0 560 add a,r2 561 mov r2,a 562 inc r0 563 mov a,@r0 564 addc a,r3 565 mov r3,a 566 mov dpl,r2 567 mov dph,r3 568 pop _bp 569 pop ar1 570 pop ar0 571 pop ar3 572 pop ar2 573 ret

呼叫時,直接把傳遞值放入堆疊裏,而且在呼叫結束也會還原堆疊指針。

592 ; ../source/main.c:74: test(1, 2); 593 mov a,#0x02 594 push acc 595 clr a 596 push acc 597 mov dptr,#0x0001 598 lcall _test 599 dec sp 600 dec sp

各位有沒有在 reentrant function 裏發現一個不名的變數 _bp,用來做堆疊指標的運算,以取得參數的值。而這個 _bp 變數是宣告在 SDCC\lib\src\_bp.c --> __data unsigned char bp;。而且這個變數還被 printf_tiny 及 printf_fast 所使用,當然這二個函數有宣告成 __reentrant 函數,使用 _bp 變數也只是變相利用而已啦。

0 意見

張貼留言