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 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 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 <loop1>:
   8:  4802            ldr     r0, [pc, #8]    ; (14 <loop2+0xa>)

0000000a <loop2>:
   a:  3801            subs    r0, #1
   c:  d1fd            bne.n   a <loop2>
   e:  3e01            subs    r6, #1
  10:  d1fa            bne.n   8 <loop1>
  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.