Assembly Language: Interactive¶
This page shows how to use interactive Assembly Language with Mecrisp-Stellaris via a Blinky example. The reader will need a basic knowledge of Mecrisp-Stellaris Forth and Cortex M Assembly Language.
I think this is a great way to learn Assembly Language as the Forth Core provides the user support, with recovery only a reset button press on the development board. This is much faster than the usual code, assemble, burn and test development cycle of Assembly Language which gives no feedback at all on code failure unless additional tools such as OpenOCD and GDB are also used. Even then the only visible evidence may be the Program Counter flying off to the end of memory at the end of the problem code.
See also
Environment¶
Hardware is a STM32F0 Discovery Board (Cortex M0) but this example should run on any Cortex M supported by Mecrisp-Stellaris.
Kernel Image is Mecrisp-Stellaris-2.3.9 RA
Software Tools Required¶
Note: these tools are included with the Mecrisp-Stellaris-2.3.9 release (and many earlier releases) in the mecrisp-stellaris-2.3.9/common directory.
- assembler-m0.txt
allows the entry of assembly language into Forth code. Note: It also runs example/test code when loaded to show how it works.
- disassembler-m0.txt
provides the ‘see’ Word which ‘disassembles’ Forth Word compiled ‘Machine Code’ in the Dictionary into Assembly Language.
Note
the STM32F0 Discovery Board board doesn’t have enough RAM to load the above files, so compile them into Flash instead.
Registers¶
Non RA Kernel.
Note
Assembler-m0.txt Opcode Tables can be found at the end of this page
Warning
Never abuse the stack pointer registers for anything else unless you know what you do, or the next interrupt may crash sometimes, which is a nasty bug to find. Stacks always need to be maintained in an interrupt-safe way.
Note
You can use r0, r1, r2 and r3 freely; you need to push/pop r4 and r5 before use, and you can use r6/r7 and r13/r14 as stacks.
Register(s) |
Usage Hints |
---|---|
r 0 |
Saved by IRQ entry – Free scratch register, will be saved automatically on interrupt entry. Do not expect it to stay the same on a subroutine call |
r 1 |
Saved by IRQ entry – Free scratch register, will be saved automatically on interrupt entry. Do not expect it to stay the same on a subroutine call |
r 2 |
Saved by IRQ entry – Free scratch register, will be saved automatically on interrupt entry. Do not expect it to stay the same on a subroutine call |
r 3 |
Saved by IRQ entry – Free scratch register, will be saved automatically on interrupt entry. Do not expect it to stay the same on a subroutine call |
r 4 |
Is saved by code for every usage, innermost loop index. Save and restore with push/pop if you wish to use |
r 5 |
Is saved by code for every usage, innermost loop limit. Save and restore with push/pop if you wish to use |
r 6 |
Top of stack register. No need to save TOS, as stacks are inherently interrupt safe |
r 7 |
Data stack pointer. No need to save PSP, as stacks are inherently interrupt safe |
r 8 |
Unused, available for your special purposes. Keep in mind that on M0, only a few opcodes are available for them (MOV, ADD, CMP, as far as I know) |
r 9 |
Unused, available for your special purposes. You may get a error/warning ‘Use low registers’ |
r 10 |
Unused, available for your special purposes. |
r 11 |
Unused, available for your special purposes. |
r 12 |
Unused, but saved automatically on interrupt entry. |
r 13 = SP |
Return Stack Pointer, hardwired in silicon |
r 14 = LR |
Link Register, hardwired in silicon. For bl and bx lr or push {lr} & pop {pc}, automatically saved on interrupt entry |
r 15 = PC |
Program Counter, hardwired in silicon |
The Forth Blinky¶
This is a simple Blinky which turns on the blue LED by writing “$100” into memory location $48000814, then turns it off by writing a “0” into the same memory location.
$48000800 constant GPIOC \
GPIOC $0 + constant GPIOC_MODER \ Hardware Memory Map
GPIOC $14 + constant GPIOC_ODR \ GPIOC_ODR = $48000814, the blue LED is connected to GPIOC-8
%01 16 lshift GPIOC_MODER bis! \ set PC8 (blue LED) as output
: led-on $100 GPIOC_ODR ! ; \ turn blue LED on
: led-off $0 GPIOC_ODR ! ; \ turn blue LED off
: delay 900000 0 do loop ; \ time delay so we can see the blue LED blinking
: blink
begin
led-on
delay
led-off
delay
again
;
\ type 'blink' to run the blinky and flash the blue LED, press the board reset button to stop blinking.
Assembly Language ‘asm-led-off’ Word¶
This Word can replace the ‘led-off’ Word in the Blinky example. Performance of the blinking LED will be identical.
: asm-led-off \ This is my hand coded Assembly Language 'asm-led-off' using methods learned from my recent 'Blinky Challenge' with Matthias
ldr= r0 $48000800 \ For clarity I have chosen the same registers as used by Mecrisp Stellaris when it compiled 'led-off'
ldr= r3 0 \ Note: assembler-m0.fs must be loaded first, so Assembly Language statements can be used.
str r3 r0 #$14
;
‘asm-led-off’ Disassembled¶
see asm-led-off \ 'see' is provided by disassembler-m0.fs
20000402: B500 push { lr }
20000404: 2090 movs r0 #90
20000406: 04C0 lsls r0 r0 #13
20000408: 3080 adds r0 #80
2000040A: 0100 lsls r0 r0 #4
2000040C: 2300 movs r3 #0
2000040E: 6143 str r3 [ r0 #14 ]
20000410: BD00 pop { pc }
ok.
‘led-off’ Disassembled¶
Compare this to ‘asm-led-off’ above, neglecting the ‘push’ and ‘pop’ they’re identical.
see led-off
200003CE: 2090 movs r0 #90
200003D0: 04C0 lsls r0 r0 #13
200003D2: 3080 adds r0 #80
200003D4: 0100 lsls r0 r0 #4
200003D6: 2300 movs r3 #0
200003D8: 6143 str r3 [ r0 #14 ]
200003DA: 4770 bx lr
ok.
Assembly Language ‘asm-delay’ Word¶
This Word can replace the ‘delay’ Word in the Blinky example. Performance of the blinking LED will be identical.
: asm-delay
ldr= r0 $fffff
l-: subs r0 r0 #1
bne -
;
‘asm-delay’ Disassembled¶
This Word does save five instructions compared to the ‘delay’ code generated by Mecrisp-Stellaris, making it a worthwhile exercise.
see asm-delay
20000450: B500 push { lr }
20000452: 20FF movs r0 #FF
20000454: 0200 lsls r0 r0 #8
20000456: 30FF adds r0 #FF
20000458: 0100 lsls r0 r0 #4
2000045A: 300F adds r0 #F
2000045C: 1E40 subs r0 r0 #1
2000045E: D1FD bne 2000045C
20000460: BD00 pop { pc }
‘delay’ Disassembled¶
see delay
20000420: B500 push { lr }
20000422: 3F04 subs r7 #4
20000424: 603E str r6 [ r7 #0 ]
20000426: B430 push { r4 r5 }
20000428: 2400 movs r4 #0
2000042A: 25DB movs r5 #DB
2000042C: 022D lsls r5 r5 #8
2000042E: 35BA adds r5 #BA
20000430: 012D lsls r5 r5 #4
20000432: CF40 ldmia r7 { r6 }
20000434: 3401 adds r4 #1
20000436: 42AC cmp r4 r5
20000438: D1FC bne 20000434
2000043A: BC30 pop { r4 r5 }
2000043C: BD00 pop { pc }
Summary¶
Handwritten Assembly code can produce smaller code than the Mecrisp-Stellaris generated code, but can also be identical.
If you’d prefer to use Assembly only with Cortex M, see the Assembly Language article here.
Note
The Forth Handwritten Assembly code above, while smaller by five instructions then the Forth code, actually takes a LOT longer to run probably because the Forth only code is optimised within the Mecrisp-Stellaris system ?
Assembler-m0.txt Opcode Tables¶
Section Labels¶
label |
Description |
Comment |
---|---|---|
l- |
label, backward jump |
The assembler resolves the closest 3 labels in every direction relative to the current location. They can be referenced with bne - |
l+ |
label, forward jump |
bge ++ bhi - - - which means: one l-: label back, two l+: labels forward, three l-: back. Different to normal assembler. |
Branching¶
Opcode |
Description |
Comment |
---|---|---|
beq |
Branch if Z set |
equal |
bne |
Branch if Z clear |
not equal |
bcs |
Branch if C set |
unsigned higher or same |
bcc |
Branch if C clear |
unsigned lower |
bmi |
Branch if N set |
negative |
bpl |
Branch if N clear |
positive or zero |
bvs |
Branch if V set |
overflow |
bvc |
Branch if V clear |
no overflow |
bhi |
Branch if C set and V clear |
unsigned higher |
bls |
Branch if C clear and Z set |
unsigned lower or same |
bge |
Branch if N set and V set, or N clear and V clear |
greater or equal |
blt |
Branch if N set and V clear, or N clear and V set |
less than |
bgt |
Branch if Z clear, and either N set and V set or N clear and V clear |
greater than |
ble |
Branch if Z set, or N set and V clear, or N clear and V set |
less than or equal |
bhs |
Alias |
|
blo |
Alias |
|
b |
Unconditional branch |
Instructions with its own special handling¶
Opcode |
Description / example |
Comment |
---|---|---|
bl |
branch with link, a subroutine call |
If range is too far for bl opcode, it will generate a blx r0 sequence. |
ldr= |
ldr= r0 $48000800 |
Load register 0 with the Hexadecimal 48000800 ( GPIOC BASE address) |
Instructions with one register-16 operand¶
Opcode |
Description / example |
Comment |
---|---|---|
b |
Unconditional branch |
b <label> Branch PC relative +/- Offset11 << 1, where label is PC +/- 2048 bytes. |
blx |
Branch and Link (immediate) |
calls a subroutine at a PC-relative address |
Instructions with two register operands¶
Opcode |
Description / example |
Comment |
---|---|---|
ands |
||
eors |
||
adcs |
||
sbcs |
||
rors |
||
tst |
||
orrs |
||
muls |
||
bics |
||
mvns |
||
sxtb |
||
sxth |
||
uxtb |
||
uxth |
||
rev |
||
rev16 |
||
revsh |
Still Under Construction¶
Opcode |
Description / example |
Comment |
---|---|---|
Under Construction ! |