.. index:: assembly:interactive .. _interactive-assy-delay: Interactive Assembly Delay ========================== This page shows how to use the Mecrisp-Interactive Assembly facility to create a millisecond blocking delay. .. note:: While the delay is being used, the CPU is not available to do anything else. To see the same thing but done by inlining the machine code from arm-none-eabi-as, see :ref:`Accurate Blocking Delay` Why do it this way ? -------------------- The reason that the millisecond blocking delay is done this way rather than in pure Forth is because it can be done in about half the binary code size compared to a pure Forth Word such as "delay" below. Another reason is that sometimes maximum speed is required and this means using minimal instructions in the Forth Word. The delay example is a great way to demonstrate how to achieve this. .. note:: The :ref:`THUMB Instruction Set` is used here Using Pure Forth Instead ------------------------ The pure Forth way is certainly much faster to write and debug compared to the alternate methods. The "delay" Word below uses the common "do-loop" twice and is calibrated. :: : delay 0 do 1526 0 do loop loop ; Disassembly ^^^^^^^^^^^ :: see delay 20000604: B500 push { lr } 20000606: B430 push { r4 r5 } 20000608: 2400 movs r4 #0 2000060A: 0035 lsls r5 r6 #0 2000060C: CF40 ldmia r7 { r6 } 2000060E: 3F04 subs r7 #4 20000610: 603E str r6 [ r7 #0 ] 20000612: B430 push { r4 r5 } 20000614: 2400 movs r4 #0 20000616: 25BE movs r5 #BE 20000618: 00ED lsls r5 r5 #3 2000061A: 3506 adds r5 #6 2000061C: CF40 ldmia r7 { r6 } 2000061E: 3401 adds r4 #1 20000620: 42AC cmp r4 r5 20000622: D1FC bne 2000061E 20000624: BC30 pop { r4 r5 } 20000626: 3401 adds r4 #1 20000628: 42AC cmp r4 r5 2000062A: D1F0 bne 2000060E 2000062C: BC30 pop { r4 r5 } 2000062E: BD00 pop { pc } Bytes: 44 ok. Usage ^^^^^ :: 1000 delay Produces a 1.0000 second delay. Interactive Assembly -------------------- You need to load the following programs first: Cortex-M0 ^^^^^^^^^ 1. mecrisp-stellaris-x.x.x/common/disassembler-m0.txt 2. mecrisp-stellaris-x.x.x/common/assembler-m0.txt Cortex-M3 ^^^^^^^^^ 1. mecrisp-stellaris-x.x.x/common/disassembler-m3.txt 2. mecrisp-stellaris-x.x.x/common/assembler-m0.txt Table-1 ^^^^^^^ :: l-: l+: jumps beq bne bcs bcc bmi bpl bvs bvc bhi bls bge blt bgt ble bhs blo b wfi bl ldr= add mov bx blx ands eors adcs sbcs rors tst orrs muls bics mvns sxtb sxth uxtb uxth rev rev16 revsh movs cmp adds subs str strb strh ldr ldrb ldrh ldrsb ldrsh lsls lsrs asrs lslsr lsrsr asrsr push pop stmia ldmia How does it work ? ------------------ Assembler-m0.txt provides the Words in Table-1 which look and behave much like assembler syntax using arm-none-eabi-as. However these are Forth Words and so run under Forth with all the interactive advantages.Forth compiles these Words into the same machine code produced by arm-none-eabi-as. These Words can be used to produce minimal machine code for inlining in muct the same was as using Assembly Examples ----------------- Using arm-none-eabi-as ^^^^^^^^^^^^^^^^^^^^^^ Example Blocking Delay Sourcecode ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. warning:: This code will run perfectly in GDB but it is NO GOOD for use as INLINED MACHINE CODE for Forth because the LDR command produces code that is PC (program counter) relative. To inline machine code in Forth, the code must be PC INDEPENDENT. :: .syntax unified .cpu cortex-m0 .thumb .text Vector_Table: .word 0x20000000 @ stack pointer value when stack is empty ResetVector: .word loop1 +1 @ Reset Handler loop1: ldr r0,=1913 loop2: subs r0,r0,#1 bne loop2 subs r6,r6,#1 bne loop1 Disassembly ----------- :: 00000008 : 8: 4802 ldr r0, [pc, #8] ; (14 ) 0000000a : a: 3801 subs r0, #1 c: d1fd bne.n a e: 3e01 subs r6, #1 10: d1fa bne.n 8 12: 07790000 ldrbeq r0, [r9, -r0]! Using Interactive Assembly ^^^^^^^^^^^^^^^^^^^^^^^^^^ Labels are supported but the syntax and use is different to arm-none-eabi-as. Example Interactive Assembly Sourcecode ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: : ms-interactive-assy ( u -- ) \ Blocking delay for use on STM32F0xx @ 8MHz RC Clock. "1000 ms" = 1 second delay l-: ldr= r0 1913 l-: subs r0 #1 bne - subs r6 #1 bne -- drop ; Jump to Labels Syntax ~~~~~~~~~~~~~~~~~~~~~ Jumping UP: :: - Jump to first occurence of "l-:" -- Jump to second occurence of "l-:" --- Jump to third occurence of "l-:" Jumping Down: :: + Jump to first occurence of "l+" ++ Jump to second occurence of "l+:" +++ Jump to third occurence of "l+:" Disassembly ~~~~~~~~~~~ The PUSH and POP are added by Forth :: see ms-interactive-assy 20000596: B500 push { lr } 20000598: 20EF movs r0 #EF 2000059A: 00C0 lsls r0 r0 #3 2000059C: 3001 adds r0 #1 2000059E: 3801 subs r0 #1 200005A0: D1FD bne 2000059E 200005A2: 3E01 subs r6 #1 200005A4: D1F8 bne 20000598 200005A6: CF40 ldmia r7 { r6 } 200005A8: BD00 pop { pc } 20000598 to 2000059E is the Forth statement "ldr= r0 1913" computing the shortest MOVS,LSLS,ADDS sequence to guarantee that R0 contains 1913 and is PC INDEPENDENT. :: movs r0 #EF lsls r0 r0 #3 adds r0 #1 Testing the result: :: : calc $ef 3 lshift 1 + . cr ; calc 1913 ok. Construct the Forth inlined Word from the disassembly above ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: : ms ( u -- ) \ millisecond blocking delay for Cortex-m0 with 8MHz rc clock (mecrisp-stellaris default) [ \ 20 bytes size $20EF h, $00C0 h, $3001 h, $3801 h, $D1FD h, $3E01 h, $D1F8 h, ] drop ; Disassembly ~~~~~~~~~~~ :: see ms 200005E2: B500 push { lr } 200005E4: 20EF movs r0 #EF 200005E6: 00C0 lsls r0 r0 #3 200005E8: 3001 adds r0 #1 200005EA: 3801 subs r0 #1 200005EC: D1FD bne 200005EA 200005EE: 3E01 subs r6 #1 200005F0: D1F8 bne 200005E4 200005F2: CF40 ldmia r7 { r6 } 200005F4: BD00 pop { pc } Bytes: 20 ok. Usage ----- :: 1000 ms Will provide a blocking delay of 1 second.