Machine and Assembly Code

Note

Mecrisp-Stellaris 2.4.2-RA used here

Note

Mecrisp-Stellaris also has Interactive Assembly Language Words.

Ra Register Allocator Usage

RA Register Alocator Register USAGE
r0 Free scratch register Saved on interrupt entry by hardware
r1 Free scratch register Saved on interrupt entry by hardware
r2 Free scratch register Saved on interrupt entry by hardware
r3 Free scratch register Saved on interrupt entry by hardware
r4 Inner loop count Needs Push and Pop when used otherwise
r5 Inner loop limit Needs Push and Pop when used otherwise
r6 TOS: Top-Of-Stack Stack design is interrupt safe
r7 PSP: Parameter Stack Pointer Stack design is interrupt safe
r8 Unused  
r9 Unused  
r10 Unused  
r11 Unused  
r12 Unused Saved on interrupt entry by hardware
r13 SP Return Stack Pointer  
r14 LR Link Register  
r15 PC Program Counter, always odd  

Executing Assembly/Machine Code

Executing and or Inlining Assembly/Machine code is fairly easily handled by Mecrisp-Stellaris and the following workflow example shows one way of doing it.

Methodology

  • Write your assemby code
  • Assemble your code
  • Paste the machine code into a Word
  • Dissasemble the Word
  • Test the Word

Warning

  • You are free to change R0, R1, R2 and R3
  • R4 and R5 need to pushed and popped if used.
  • R6 is the Top of Stack (TOS) on entry and exit. If there are more than one result, they have to be pushed onto to the stack.
  • Stack pointer R7 must be used in an interrupt-safe way.
  • Subroutines cannot be used
  • When you use “[” Mecrisp-Stellaris assumes “everything might happen” and frames your code with push {lr} / pop {pc} opcodes

Example 1: 2plus (adds 2 to any number)

Write 2plus.s assemby code

I’m only interested in the assembled code after “start:” because Mecrisp-Stellaris has its own Vector Table and Initialisation Code.

// Minimal assembler example for stm32f051 to be used in a Machine Code Word "2plus"
// Requires svd2as
// Board: stm32f0 Discovery
// 1 June 2018  Copyright <terry@tjporter.com.au> Released under the GPL
//

.cpu cortex-m0                                       // Tell the assembler what model of CortexM this is for
.thumb                                               // Cortex micros only understand thumb(2) code
.text                                                // what follows is code
.include "bitposn-equates.s"                         // a list of ".equ  BIT0,    0x00000001", ".equ  BIT1,    0x00000002" etc
.include "STM32F0xx-tp1.svd.s"                       // Created by svd2as
.syntax unified // use newer style instructions
Vector_Table:  .word     0x20000000                  // stack pointer value when stack is empty 0x20000000
ResetVector:    .word     start + 1                  // Reset Handler

start:
// Input parameter will be saved in r6 (TOS)
        movs r1, #2                            // move 2 into r1
        adds r2, r6, r1                        // add with carry, r1 + r6 and place result into r2. Note: r6 is TOS on entry
        movs r6, r2                            // move the contents of r2 into r6. Note: the result is placed back onto TOS via r6 on exit

Assemble 2plus.s

This text is from 2plus.list produced by this Makefile.

2plus.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <Vector_Table>:
   0:  20000000        andcs   r0, r0, r0

00000004 <ResetVector>:
   4:  00000009        andeq   r0, r0, r9

00000008 <start>:
   8:  2102            movs    r1, #2
   a:  1872            adds    r2, r6, r1
   c:  0016            movs    r6, r2

Paste The Machine Code Into 2plus Word

Add comments for readability

: 2plus ( u -- 2+ )
[          \ *execute* mode
$2102 h,   \ movs      r1, #2
$1872 h,   \ adds      r2, r6, r1
$0016 h,   \ movs      r6, r2
]          \ *compile* mode
;

Dissasemble 2plus

Compare this to the 2plus Word ? The Machine Code is unchanged. Mecrisp-Stellaris has framed the code with push {lr} / pop {pc} opcodes

see 2plus

200003AC: B500  push { lr }
200003AE: 2102  movs r1 #2
200003B0: 1872  adds r2 r6 r1
200003B2: 0016  lsls r6 r2 #0
200003B4: BD00  pop { pc }
ok.

Test 2plus

2 2plus . 4  ok.

2plus.s Makefile

ARMGNU?=arm-none-eabi

COPS = -Wall  -Os -nostdlib -nostartfiles -ffreestanding -save-temps
AOPS = --warn --fatal-warnings

all : 2plus.bin

2plus.o : 2plus.s
       $(ARMGNU)-as -mthumb -a=2plus.lst -g --gstabs+ 2plus.s -o 2plus.o

2plus.bin : memmap 2plus.o
       $(ARMGNU)-ld -Ttext 0x0 -o 2plus.elf -T memmap 2plus.o
       $(ARMGNU)-objdump -D 2plus.elf > 2plus.list
       $(ARMGNU)-objcopy 2plus.elf 2plus.bin -O binary

erase:
       st-flash erase

flash:
       st-flash write 2plus.bin 0x08000000

clean:
       rm -f *.bin
       rm -f *.o
       rm -f *.elf
       rm -f *.list
       rm -f *.lst

Download all the Files from the 2plus project

Example 2, more complex code, A blinky

This blinks the Blue led on a STM32F0 Discovery Board.

Methodology

  • Write your assemby code
  • Assemble your code
  • Paste the machine code into a Word
  • Dissasemble the Word
  • Test the Word

Blinky Assembly Code

  • Requires svd2gas to generate the hardware equate statements
// Minimal assembler blinky example for stm32f051
// Requires svd2as
// Board: stm32f0 Discovery
// Blue LED is on GPIOC bit 8
// 8th June 2018  Copyright <terry@tjporter.com.au> Released under the GPL

.cpu cortex-m0                                    // Tell the assembler what model of CortexM this is for
.thumb                                            // Cortex micros only understand thumb(2) code
.text                                             // what follows is code
.include "bitposn-equates.s"                      // a list of ".equ  BIT0,    0x00000001", ".equ  BIT1," etc
.include "STM32F0xx.svd.s"                        // Compliments of svd2gas
.syntax unified // use newer style instructions
Vector_Table:  .word     0x20000000               // stack pointer value when stack is empty 0x20000000
ResetVector:    .word     start + 1               // Reset Handler

start:
        ldr r1, = RCC_AHBENR
        ldr r2, = RCC_IOPCEN                      // Turn on clock for GPIOC
        str r2, [r1]

moder:
        ldr r1, = GPIOC_MODER
        movs r2, 0b01                             // Set GPIOC-8 to output
        lsls r2, GPIOC_MODER8_Shift               // BitWidth 2
        str r2, [r1]


led_on:                                           // Main Loop
        ldr r1, = GPIOC_ODR
        ldr r2, = BLUE_LED_ON                     // Turn LED on
        str r2, [r1]
        ldr r0, = DELAY                           // delay
delay_1: subs r0,r0,#1
        bne delay_1

led_off:
        ldr r1, = GPIOC_ODR
        ldr r2, = BLUE_LED_OFF                    // Turn LED off
        str r2, [r1]
        ldr r0, = DELAY                           // Delay
delay_2: subs r0,r0,#1
        bne delay_2

        b led_on                                  // Jump to main_loop

constants:
        .equ DELAY, 0xfffff
        .equ BLUE_LED_ON, GPIOC_ODR8
        .equ BLUE_LED_OFF, 0x0

Blinky.list Output

blinky.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <Vector_Table>:
   0:  20000000        andcs   r0, r0, r0

00000004 <ResetVector>:
   4:  00000009        andeq   r0, r0, r9

00000008 <start>:
   8:  4909            ldr     r1, [pc, #36]   ; (30 <constants>)
   a:  4a0a            ldr     r2, [pc, #40]   ; (34 <constants+0x4>)
   c:  600a            str     r2, [r1, #0]

0000000e <moder>:
   e:  490a            ldr     r1, [pc, #40]   ; (38 <constants+0x8>)
  10:  2201            movs    r2, #1
  12:  0412            lsls    r2, r2, #16
  14:  600a            str     r2, [r1, #0]

00000016 <led_on>:
  16:  4909            ldr     r1, [pc, #36]   ; (3c <constants+0xc>)
  18:  4a09            ldr     r2, [pc, #36]   ; (40 <BIT6>)
  1a:  600a            str     r2, [r1, #0]
  1c:  4809            ldr     r0, [pc, #36]   ; (44 <BIT6+0x4>)

0000001e <delay_1>:
  1e:  3801            subs    r0, #1
  20:  d1fd            bne.n   1e <delay_1>

00000022 <led_off>:
  22:  4906            ldr     r1, [pc, #24]   ; (3c <constants+0xc>)
  24:  4a08            ldr     r2, [pc, #32]   ; (48 <BIT6+0x8>)
  26:  600a            str     r2, [r1, #0]
  28:  4806            ldr     r0, [pc, #24]   ; (44 <BIT6+0x4>)

0000002a <delay_2>:
  2a:  3801            subs    r0, #1
  2c:  d1fd            bne.n   2a <delay_2>
  2e:  e7f2            b.n     16 <led_on>

00000030 <constants>:
  30:  40021014        andmi   r1, r2, r4, lsl r0
  34:  00080000        andeq   r0, r8, r0
  38:  48000800        stmdami r0, {fp}
  3c:  48000814        stmdami r0, {r2, r4, fp}
  40:  00000100        andeq   r0, r0, r0, lsl #2
  44:  000fffff        strdeq  pc, [pc], -pc   ; <UNPREDICTABLE>
  48:  00000000        andeq   r0, r0, r0

Paste Blinky machine code into a Word

Note

1) Although the Mecrisp Stellaris Kernel enables all the GPIOs, GPIOC-8 (the blue led) is enabled again here, which is totally redundant but doesn’t affect anything.

: blinky ( -- )
[
$4909 h,    \  ldr     r1, [pc, #36]   ; (30 <constants>)
$4a0a h,    \  ldr     r2, [pc, #40]   ; (34 <constants+0x4>)
$600a h,    \  str     r2, [r1, #0]
$490a h,    \  ldr     r1, [pc, #40]   ; (38 <constants+0x8>)
$2201 h,    \  movs    r2, #1
$0412 h,    \  lsls    r2, r2, #16
$600a h,    \  str     r2, [r1, #0]
$4909 h,    \  ldr     r1, [pc, #36]   ; (3c <constants+0xc>)
$4a09 h,    \  ldr     r2, [pc, #36]   ; (40 <BIT6>)
$600a h,    \  str     r2, [r1, #0]
$4809 h,    \  ldr     r0, [pc, #36]   ; (44 <BIT6+0x4>)
$3801 h,    \  subs    r0, #1
$d1fd h,    \  bne.n   1e <GPIOC_MODER15_Shift>
$4906 h,    \  ldr     r1, [pc, #24]   ; (3c <constants+0xc>)
$4a08 h,    \  ldr     r2, [pc, #32]   ; (48 <BIT6+0x8>)
$600a h,    \  str     r2, [r1, #0]
$4806 h,    \  ldr     r0, [pc, #24]   ; (44 <BIT6+0x4>)
$3801 h,    \  subs    r0, #1
$d1fd h,    \  bne.n   2a <BIT5+0xa>
$e7f2 h,    \  b.n     16 <GPIOC_MODER11_Shift>
            \ <constants>:
$40021014 , \  andmi   r1, r2, r4, lsl r0
$00080000 , \  andeq   r0, r8, r0
$48000800 , \  stmdami r0, {fp}
$48000814 , \  stmdami r0, {r2, r4, fp}
$00000100 , \  andeq   r0, r0, r0, lsl #2
$000fffff , \  strdeq  pc, [pc], -pc   ; <UNPREDICTABLE>
$00000000 , \  andeq   r0, r0, r0
]
;

Dissasemble the Blinky Word

This Blinky is 68 bytes long, not including the PUSH and POP added by Mecrisp-Stellaris.

see blinky
200003AE: B500  push { lr }
200003B0: 4909  ldr r1 [ pc #24 ]  Literal 200003D8: 40021014
200003B2: 4A0A  ldr r2 [ pc #28 ]  Literal 200003DC: 00080000
200003B4: 600A  str r2 [ r1 #0 ]
200003B6: 490A  ldr r1 [ pc #28 ]  Literal 200003E0: 48000800
200003B8: 2201  movs r2 #1
200003BA: 0412  lsls r2 r2 #10
200003BC: 600A  str r2 [ r1 #0 ]
200003BE: 4909  ldr r1 [ pc #24 ]  Literal 200003E4: 48000814
200003C0: 4A09  ldr r2 [ pc #24 ]  Literal 200003E8: 00000100
200003C2: 600A  str r2 [ r1 #0 ]
200003C4: 4809  ldr r0 [ pc #24 ]  Literal 200003EC: 000FFFFF
200003C6: 3801  subs r0 #1
200003C8: D1FD  bne 200003C6
200003CA: 4906  ldr r1 [ pc #18 ]  Literal 200003E4: 48000814
200003CC: 4A08  ldr r2 [ pc #20 ]  Literal 200003F0: 00000000
200003CE: 600A  str r2 [ r1 #0 ]
200003D0: 4806  ldr r0 [ pc #18 ]  Literal 200003EC: 000FFFFF
200003D2: 3801  subs r0 #1
200003D4: D1FD  bne 200003D2
200003D6: E7F2  b 200003BE
200003D8: 1014  asrs r4 r2 #0
200003DA: 4002  ands r2 r0
200003DC: 0000  lsls r0 r0 #0
200003DE: 0008  lsls r0 r1 #0
200003E0: 0800  lsrs r0 r0 #0
200003E2: 4800  ldr r0 [ pc #0 ]  Literal 200003E4: 48000814
200003E4: 0814  lsrs r4 r2 #0
200003E6: 4800  ldr r0 [ pc #0 ]  Literal 200003E8: 00000100
200003E8: 0100  lsls r0 r0 #4
200003EA: 0000  lsls r0 r0 #0
200003EC: FFFF
200003EE: 000F  lsls r7 r1 #0
200003F0: 0000  lsls r0 r0 #0
200003F2: 0000  lsls r0 r0 #0
200003F4: BD00  pop { pc }
ok.

Test the Blinky Word

blinky

Note

It works fine, the blue led blinks, but it’s a closed loop and requires resetting the board to stop blinking.

Comparison to a Blinky written in Forth

How will the assembly Blinky compare in code size compared to a Native Forth Blinky ?

Note

Answer: the Forth version is exactly twice the size in bytes.

Native Forth Blinky

Certainly much easier to write, only 8 lines of code. I pasted the Bitfields complete with memmory-mappings from svd2forth-v4 (not ready for release yet), making this a simple task.

This ran correctly first try. The code is not the most readable as I tried to emulate the assembly code layout as closely as possible.

: blinky ( -- )                       \ enable GPIOC-8 as a OUTPUT here for fair comparison to blinky-asm
   %1  19  lshift $40021014  bis!     \ RCC_AHBENR_IOPCEN; I/O port C clock enable
   %01 16  lshift $48000800  bis!     \ GPIOC_MODER_MODER8 set as OUTPUT
   begin                              \ endless loop
      %1  8  lshift $48000818   bis!  \ GPIOC_BSRR_BS8, turn on blue led
      $fffff 0 do loop                \ delay $fffff
      %1  24  lshift $48000818  bis!  \ GPIOC_BSRR_BR8, turn off blue led
      $fffff 0 do loop                \ delay $fffff
   0 until
 ;

Native Forth Blinky Assembly Listing

This is 136 Bytes long, making it exactly double the size of the assembly Blinky (68 Bytes).

see blinky
2000040A: 2080  movs r0 #80
2000040C: 0340  lsls r0 r0 #D
2000040E: 3084  adds r0 #84
20000410: 0280  lsls r0 r0 #A
20000412: 6943  ldr r3 [ r0 #14 ]
20000414: 2280  movs r2 #80
20000416: 0312  lsls r2 r2 #C
20000418: 4313  orrs r3 r2
2000041A: 6143  str r3 [ r0 #14 ]
2000041C: 2090  movs r0 #90
2000041E: 04C0  lsls r0 r0 #13
20000420: 3080  adds r0 #80
20000422: 0100  lsls r0 r0 #4
20000424: 6803  ldr r3 [ r0 #0 ]
20000426: 2280  movs r2 #80
20000428: 0252  lsls r2 r2 #9
2000042A: 4313  orrs r3 r2
2000042C: 6003  str r3 [ r0 #0 ]
2000042E: B500  push { lr }
20000430: 2090  movs r0 #90
20000432: 04C0  lsls r0 r0 #13
20000434: 3080  adds r0 #80
20000436: 0100  lsls r0 r0 #4
20000438: 6983  ldr r3 [ r0 #18 ]
2000043A: 2280  movs r2 #80
2000043C: 0052  lsls r2 r2 #1
2000043E: 4313  orrs r3 r2
20000440: 6183  str r3 [ r0 #18 ]
20000442: 3F04  subs r7 #4
20000444: 603E  str r6 [ r7 #0 ]
20000446: B430  push { r4  r5 }
20000448: 2400  movs r4 #0
2000044A: 25FF  movs r5 #FF
2000044C: 022D  lsls r5 r5 #8
2000044E: 35FF  adds r5 #FF
20000450: 012D  lsls r5 r5 #4
20000452: 350F  adds r5 #F
20000454: CF40  ldmia r7 { r6 }
20000456: 3401  adds r4 #1
20000458: 42AC  cmp r4 r5
2000045A: D1FC  bne 20000456
2000045C: BC30  pop { r4  r5 }
2000045E: 2090  movs r0 #90
20000460: 04C0  lsls r0 r0 #13
20000462: 3080  adds r0 #80
20000464: 0100  lsls r0 r0 #4
20000466: 6983  ldr r3 [ r0 #18 ]
20000468: 2280  movs r2 #80
2000046A: 0452  lsls r2 r2 #11
2000046C: 4313  orrs r3 r2
2000046E: 6183  str r3 [ r0 #18 ]
20000470: 3F04  subs r7 #4
20000472: 603E  str r6 [ r7 #0 ]
20000474: B430  push { r4  r5 }
20000476: 2400  movs r4 #0
20000478: 25FF  movs r5 #FF
2000047A: 022D  lsls r5 r5 #8
2000047C: 35FF  adds r5 #FF
2000047E: 012D  lsls r5 r5 #4
20000480: 350F  adds r5 #F
20000482: CF40  ldmia r7 { r6 }
20000484: 3401  adds r4 #1
20000486: 42AC  cmp r4 r5
20000488: D1FC  bne 20000484
2000048A: BC30  pop { r4  r5 }
2000048C: 2300  movs r3 #0
2000048E: 2B00  cmp r3 #0
20000490: D0CE  beq 20000430
20000492: BD00  pop { pc }
 ok.

Native Forth Blinky Written for Readability

: RCC_AHBENR_IOPCEN  19  lshift $40021014 ;  \ GPIOC Enable Bit
: GPIOC_MODER_MODER8 16  lshift $48000800 ;  \ GPIOC-8 is connected to blue led
: LEDON  %1  8  lshift $48000818   bis! ;    \ GPIOC_BSRR_BS8, turn on blue led
: LEDOFF %1  24  lshift $48000818  bis! ;    \ GPIOC_BSRR_BR8, turn off blue led
: DELAY $fffff 0 do loop ;                   \ delay $fffff


: blinky ( -- )
    %1  RCC_AHBENR_IOPCEN  bis!        \ enable I/O port C clock
    %01 GPIOC_MODER_MODER8  bis!       \ set GPIOC_MODER_MODER8 as OUTPUT
    begin                              \ endless loop
       LEDON
         DELAY
       LEDOFF
         DELAY
    0 until
;