.. index:: devtips, balancing the return stack, local variable, debugging,pr,stack:printing .. _devtips: Development Tips ================ Just a few handy debug methods I've developed along the way ... .. _pr: PR: Monitor Stack Values and Program Flow ----------------------------------------- Forth is definitely ALL about the Stack and visualising the Stack to design code has been, and continues to be my main challenge. This Stack visualisation method is *really simple*, yet I use it all the time when designing code and fixing bugs. Used with VIM Mapping, it's fast and easy to use. I'm still a Forth beginner and I need all the help I can get, so being able to rapidly insert print statements to follow the stack and program flow, plus show nothing is left on the stack has made Forth a lot easier for me. :: : pr ( identifier -- identifier print stack ) . .s cr ; \ A debugging stack printer Vim Key Mapping ^^^^^^^^^^^^^^^ :: " insert " pr" only in INSERT MODE for Forth debugging map! _P =printf("%d pr", line('.')) In Insert mode I move the cursor to the source code line I want the stack printed then I use the key sequence " _p" and Vim inserts the line number followed by "pr" as in the example below. PR: Usage Example ^^^^^^^^^^^^^^^^^ .. literalinclude:: library/pr-example.fs Balancing The Return Stack -------------------------- Forth does have a 'Local Variable', it's called The 'Return Stack', but it must be balanced or your program will crash. .. note:: "Balancing the Return Stack" means making sure that the number of >R equals the number of R> within the same definition. Special care is needed with looping. Why use the Return Stack to as a Local Variable, why not just use a Global Variable ? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1) The example Split program below saves 24 Bytes over Split2 and in Embedded, memory is a finite resource and costly to increase. 2) I prefer Words to be self contained if possible 3) The challenge of not using Global Variables .. warning:: It's often convenient to be able to put a stack item temporarily in the return stack, but the following rules must be observed because if the Return Stack isn't balanced, this is *very bad* and if it doesn't crash now, it will cause problems later. * Data put on the return stack must be taken back within the same word. * Data put on the return stack outside a ?DO (DO) ... LOOP (+LOOP) cannot be accessed within the loop. * Data put on the return stack within a ?DO (DO) ... LOOP (+LOOP) must be taken back before leaving the loop. * Data cannot be on the return stack when executing I or J in a loop. Split Program ^^^^^^^^^^^^^ This program splits a number into digits, and utilises the Return Stack as a local variable. :: : split >R \ Save the input on the Return Stack BEGIN R> ?DUP 0 > WHILE \ Fetch from the Return Stack, duplicate because " 0 >" and "/MOD" both use it, then continue WHILE greater than zero. ?DUP is used instead of DUP to prevent a zero being left on the stack after the program has completed. 10 /MOD >R . cr \ divide by 10 and save the result on the Return Stack, print each remainder on a new line REPEAT ; \ ------- Split program output ---------- \ 123456789 split 9 8 7 6 5 4 3 2 1 .. note:: At first glance, the Return Stack doesn't look balanced does it ? How To Determine if The Return Stack is Balanced ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In this case, I used a variable (rb) to tally my Return Stack *pushes* and *pulls*. A 'rb' total of 0 shows the Return Stack is balanced. ========================== ======================= ============================= Return Stack Operation code Description ========================== ======================= ============================= Push >R rb @ 1+ rb ! Add one to 'rb' Pull R> rb @ 1- rb ! Subtract one from 'rb' ========================== ======================= ============================= Return Stack Accounting ~~~~~~~~~~~~~~~~~~~~~~~ Every Return Stack ADD is worth 1 and REMOVE is worth -1. A total of 0 shows the Return Stack is balanced, so no visual comparison is required. :: 0 variable rb : split-rbalance cr >R rb @ 1+ rb ! BEGIN R> rb @ 1- rb ! ?DUP 0 > WHILE 10 /MOD >R rb @ 1+ rb ! . cr REPEAT cr rb @ 0= if ." The Return Stack is balanced " cr cr else ." UNBALANCED ! The Return Stack is NOT balanced! rb = " rb @ . cr cr then ; Return Stack Accounting Output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: 123456789 split-rbalance 9 8 7 6 5 4 3 2 1 The Return Stack is balanced ok. Compare Local Variable vs Return Stack code size ------------------------------------------------ Dissasemble Split ^^^^^^^^^^^^^^^^^ Split is 64 bytes :: see split 20000398: B500 push { lr } 2000039A: B440 push { r6 } 2000039C: CF40 ldmia r7 { r6 } 2000039E: BC08 pop { r3 } 200003A0: 3F04 subs r7 #4 200003A2: 603E str r6 [ r7 #0 ] 200003A4: 461E mov r6 r3 200003A6: 2E00 cmp r6 #0 200003A8: D001 beq 200003AE 200003AA: 3F04 subs r7 #4 200003AC: 603E str r6 [ r7 #0 ] 200003AE: 2E00 cmp r6 #0 200003B0: CF40 ldmia r7 { r6 } 200003B2: DD11 ble 200003D8 200003B4: 3F04 subs r7 #4 200003B6: 603E str r6 [ r7 #0 ] 200003B8: 260A movs r6 #A 200003BA: 2082 movs r0 #82 200003BC: 0180 lsls r0 r0 #6 200003BE: 3039 adds r0 #39 200003C0: 4780 blx r0 --> /mod 200003C2: B440 push { r6 } 200003C4: CF40 ldmia r7 { r6 } 200003C6: 2086 movs r0 #86 200003C8: 01C0 lsls r0 r0 #7 200003CA: 3021 adds r0 #21 200003CC: 4780 blx r0 --> . 200003CE: 2097 movs r0 #97 200003D0: 0180 lsls r0 r0 #6 200003D2: 3033 adds r0 #33 200003D4: 4780 blx r0 --> cr 200003D6: E7E2 b 2000039E 200003D8: BD00 pop { pc } Alternative Split2 Progran using Variable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: 0 variable split-1 : split2 split-1 ! BEGIN split-1 @ ?DUP 0 > WHILE 10 /MOD split-1 ! . cr REPEAT ; Dissasemble Split2 ~~~~~~~~~~~~~~~~~~ Split2 is 88 bytes long :: see split2 200003B6: 2080 movs r0 #80 200003B8: 0500 lsls r0 r0 #14 200003BA: 30E0 adds r0 #E0 200003BC: 0080 lsls r0 r0 #2 200003BE: 6246 str r6 [ r0 #24 ] 200003C0: CF40 ldmia r7 { r6 } 200003C2: B500 push { lr } 200003C4: 2080 movs r0 #80 200003C6: 0500 lsls r0 r0 #14 200003C8: 30E0 adds r0 #E0 200003CA: 0080 lsls r0 r0 #2 200003CC: 6A43 ldr r3 [ r0 #24 ] 200003CE: 3F04 subs r7 #4 200003D0: 603E str r6 [ r7 #0 ] 200003D2: 461E mov r6 r3 200003D4: 2E00 cmp r6 #0 200003D6: D001 beq 200003DC 200003D8: 3F04 subs r7 #4 200003DA: 603E str r6 [ r7 #0 ] 200003DC: 2E00 cmp r6 #0 200003DE: CF40 ldmia r7 { r6 } 200003E0: DD15 ble 2000040E 200003E2: 3F04 subs r7 #4 200003E4: 603E str r6 [ r7 #0 ] 200003E6: 260A movs r6 #A 200003E8: 2082 movs r0 #82 200003EA: 0180 lsls r0 r0 #6 200003EC: 3039 adds r0 #39 200003EE: 4780 blx r0 --> /mod 200003F0: 2080 movs r0 #80 200003F2: 0500 lsls r0 r0 #14 200003F4: 30E0 adds r0 #E0 200003F6: 0080 lsls r0 r0 #2 200003F8: 6246 str r6 [ r0 #24 ] 200003FA: CF40 ldmia r7 { r6 } 200003FC: 2086 movs r0 #86 200003FE: 01C0 lsls r0 r0 #7 20000400: 3021 adds r0 #21 20000402: 4780 blx r0 --> . 20000404: 2097 movs r0 #97 20000406: 0180 lsls r0 r0 #6 20000408: 3033 adds r0 #33 2000040A: 4780 blx r0 --> cr 2000040C: E7DA b 200003C4 2000040E: BD00 pop { pc }