.. index:: assembly language, assembler, disassembler, interactive assembly .. _assembly: Assembly Language: Interactive ============================== * This page shows how to use interactive Assembly Language with Mecrisp-Stellaris via a :ref:`Blinky` example. The reader will need a basic knowledge of :ref:`Mecrisp-Stellaris` Forth and Cortex M :ref:`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. .. seealso:: :ref:`Accurate Blocking Delay` and :ref:`Machine and Assembly Code` Environment ----------- * Hardware is a :ref:`STM32F0 Discovery Board` (Cortex M0) but this example should run on any Cortex M supported by Mecrisp-Stellaris. * Kernel Image is :ref:`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 :ref:`STM32F0 Discovery Board` board doesn't have enough RAM to load the above files, so compile them into :ref:`Flash` instead. Registers --------- Non RA Kernel. .. note:: :ref:`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 :ref:`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 :ref:`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 ? .. Section instructions with one register-16 operand: bl cannot use a register. That's blx. .. _assemblerm0: 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