Jump to content

SD card bootloader - convert assembler to C++?

Recommended Posts

I just posted a Github repo on a 1K SD card bootloader for the G2553 and G2452:


It's written in assembler, but that's not really accessible to most people who use Energia.  So I wondered if it would be possible to rewrite it in C++.  But I don't understand much about how C++ does things, and don't know if it's possible to do the same things in the same space, or anywhere near the same space.

My code uses all 12  general purpose registers, mainly because operations on registers are smaller instructions than operations on memory locations.  Also, I specify in ram the exact location of every bit of ram I use, other than the stack of course.  It all fits in 1K, but with exactly four bytes to spare.

If I do away with setup() and loop(), and just stick with a main(), is it possible to do stuff like this in Energia?  Is it possible to specify exactly where things will be located?  Are there any examples of doing this kind of conversion?

Well, this may be a bridge too far.  But I thought I would ask.  I was also thinking that if the C++ code worked in Energia, it could possibly be ported back to the Arduino world.


Share this post

Link to post
Share on other sites

Interesting stuff.  Here are some things that seem like they might cause you issues:

  • Rewriting in C++ can be small. Using the Energia API will defeat that and not result in a 1k sd card bootloader.
  • Cross Platform issues, Energia supports Windows, OS/X, and Linux.  You need to supply the binaries for all the tools you are using that run on all platforms. It is easiest to stick with the gcc object tools for manipulating the .hex file.
  • Naken asm source - No one has that so you either provide the naken assembler binary.  Alternatively, if you want to just go with assembler + Energia, you might rewrite your asm code so that it is compatible with msp430-gcc assembly.  I think Energia still takes .S files (gcc assembler source)  The biggest difference between naken and gcc is having an external linker script for the symbol addresses you have hard coded in the .m43 source code.

You are facing the same kinds of issues I bumped up against doing a small GDB bootloader.  However, I was trying to avoid being part of the user code or changing the way Energia sets up the linker scripts.  I'm not sure you can overcome that without getting into the details of the vector table and the linker script. You might find this useful:


Share this post

Link to post
Share on other sites
Posted (edited)

So I spent some time with the latest energia 1.8.7E21. It is still using msp430-gcc. That project I linked in the previous post won't work because that code is using the newer msp430-elf-gcc.   I did put together an example of using msp430-gcc style code and the blink project above that compiles and create asm only project .. well almost main has to jump to our asm code so there are a couple of instructions to get there.  Three files are needed:


/* blink_with_msp430-gcc_asm.ino */
 * we aren't going to use the .ino file


// We override the main function from Energia
// and just jump to our __reset_vector__ routine
// in blink.S

int main()
  asm("jmp _reset_vector__");


; blinkasm.S - gcc compatible interrupt driven led blinker in msp430 asm
; Version 1.0  - 10/22/2011 rick@kimballsoftware.com
; Version 1.01 -  7/22/2014 rick@kimballsoftware.com modified for msp430-elf-gcc
; Version 1.02 - 11/02/2014 rick@kimballsoftware.com more changes for msp430-elf-gcc
; $ msp430-elf-gcc blinkasm.S -D_GNU_ASSEMBLER_ -Wall -Os -g \
;       -fdata-sections -ffunction-sections -mmcu=msp430g2553 -T \
;       -T msp430g2553.ld -I/tmp/a/include -L /tmp/a/include/ -nostdlib
; Version 1.03 - 03/16/2019 rick@kimballsoftware.com made compatible with energia
        .file "blinkasm.S"
#include <msp430.h>

;;; ---- gcc doesn't know about PC,SP,SR,CG1,CG2 ----

#define PC r0                   
#define SP r1
#define SR r2

;;; ---- CONSTANTS ----


 #define _50USEC (50-1)   /* @1MHz MCLK results in 50uSec/interrupt */
 #define _500MSEC 10000   /* @1MHz MCLK results in 500ms/LED toggle */
 ;; This test illustrates the minimum CCR0 value that can be used
 ;; 22 cycles is based on the interrupt overhead
 #define _50USEC 22       /* @1MHz MCLK results in 23uSec/interrupt */
 #define _500MSEC 1       /* @1MHz MCLK results in 23uSec/LED toggle */
#define _LED_PIN BIT6     /* PORT1 pin, Launchpad has BIT0=Red and BIT6=Green */

;;; ---- Registers used as globals ----

#define LED_PIN r4
#define TIMER_CYCLES r5
#define INTERVAL_CNT r6
#define CNT r7

        ;;  or you could use some ram variables
        .lcomm tcycles,2        ; example use of local bss data variable
        .lcomm cnt,2            ; example use of local bss data variable

        .section .text,"ax",@progbits
        .global _reset_vector__                 ; it is important to name it "_reset_vector__"
                                                ; we prevent the C runtime start code from getting
                                                ; linked by using this name
        ;; disable watchdog and set stack to highest RAM addr
        mov.w   #WDTPW|WDTHOLD,&WDTCTL
        mov.w   #__stack,SP                     ; gcc ldscripts compute __stack based on mmcu

        ;; initialize clock,gpio,timer
        ;; configure DCO to precalibrated 1MHz
        clr.b   &DCOCTL
        mov.b   &CALBC1_1MHZ,&BCSCTL1
        mov.b   &CALDCO_1MHZ,&DCOCTL

        ;; initialize global register values
        mov.w   #_LED_PIN,LED_PIN               ; load constant into register constant
        mov.w   #_50USEC,TIMER_CYCLES           ; load constant into register constant
        mov.w   #_500MSEC,INTERVAL_CNT          ; load constant into register constant
        mov.w   INTERVAL_CNT,CNT                ; initialize register based counter

        ;; initialize GPIO
        bic.b   LED_PIN,&P1OUT                  ; LED turned off to start
        bis.b   LED_PIN,&P1DIR                  ; Configure P1.0 as output pin

        ;; initialize TimerA0
        mov.w   #CCIE,&TA0CCTL0                 ; Enable TA0CCR0 interrupt
        mov.w   TIMER_CYCLES,&TA0CCR0           ; Set TIMER_CYCLES cycles
        mov.w   #TASSEL_2|MC_2,&TACTL           ; SMCLK, Continuous Up Mode

        ;; enable interrupts and loop forever
        ;; real work done in the CCR0 interrupt

        ;; Note: could sleep here instead
        jmp     loop                            ; cycles:2

; TIMER0_A3_ISR - Timer0_A3 CCR0 interrupt handler for vector 0xfff2 
        .global __isr_9                         ; to insert an interrupt vector into the vector
                                                ; table from assembly, we have to use a specific
                                                ; naming convention we must call this function 
                                                ; "__isr_9" this forces it into the 9th vector
                                                ; slot counting from 0 starting memory address
                                                ; 0xffe0. It is 9 because each vector is
                                                ; 2 bytes so we end up in slot 0xfff2
                                                ; all this is because of the way msp430-gcc
                                                ; works with interrupts
                                                ; .vectors in the msp430g2553/memory.x starts
                                                ; @ 0xffe0
                                                ; there are numbered isr_0 ... isr_14 even though
                                                ; there are 16 vectors. isr_15 is actually called
                                                ; _reset_vector__ you can see how we use it in this
                                                ; code.
                                                ; see the file:
                                                ; http://www.ti.com/lit/ds/symlink/msp430g2553.pdf
                                                ; look at the "Interrupt Vector Table" page 11
        ;;  before we even start running the mcu does a push PC, and a push SR cycles:6
        dec.w   CNT                             ; have we looped INTERVAL_CNT times? cycles:1
        jnz     1f                              ; exit if we haven't reached 0 cycles:2

        ;; toggle led pin after (INTERVAL * TIMER_CYCLES) has occured
        xor.b   LED_PIN,&P1OUT                  ; cycles:4
        mov.w   INTERVAL_CNT,CNT                ; reinitialize interval counter cycles:1
        add.w   TIMER_CYCLES,&TA0CCR0           ; set new CCR0 and go again cycles:4
        reti                                    ; cycles:5

; vim: ts=8 sw=8 expandtab:

Put those in a new Energia sketch compile it for an msp430g2553 and it should produce output that looks like this:

blink_with_msp430-gcc_asm.ino.elf:     file format elf32-msp430

Disassembly of section .text:

0000c000 <main>:
    c000:       03 3c           jmp     $+8             ;abs 0xc008
    c002:       0f 43           clr     r15             

0000c004 <__ctors_end>:
    c004:       30 40 5e c0     br      #0xc05e 

0000c008 <_reset_vector__>:
    c008:       b2 40 80 5a     mov     #23168, &0x0120 ;#0x5a80
    c00c:       20 01 
    c00e:       31 40 00 04     mov     #1024,  r1      ;#0x0400

0000c012 <init>:
    c012:       c2 43 56 00     mov.b   #0,     &0x0056 ;r3 As==00
    c016:       d2 42 ff 10     mov.b   &0x10ff,&0x0057 
    c01a:       57 00 
    c01c:       d2 42 fe 10     mov.b   &0x10fe,&0x0056 
    c020:       56 00 
    c022:       34 40 40 00     mov     #64,    r4      ;#0x0040
    c026:       35 40 31 00     mov     #49,    r5      ;#0x0031
    c02a:       36 40 10 27     mov     #10000, r6      ;#0x2710
    c02e:       07 46           mov     r6,     r7      
    c030:       c2 c4 21 00     bic.b   r4,     &0x0021 
    c034:       c2 d4 22 00     bis.b   r4,     &0x0022 
    c038:       b2 40 10 00     mov     #16,    &0x0162 ;#0x0010
    c03c:       62 01 
    c03e:       82 45 72 01     mov     r5,     &0x0172 
    c042:       b2 40 20 02     mov     #544,   &0x0160 ;#0x0220
    c046:       60 01 
    c048:       03 43           nop                     
    c04a:       32 d2           eint                    

0000c04c <loop>:
    c04c:       ff 3f           jmp     $+0             ;abs 0xc04c

0000c04e <__isr_9>:
    c04e:       17 83           dec     r7              
    c050:       03 20           jnz     $+8             ;abs 0xc058
    c052:       c2 e4 21 00     xor.b   r4,     &0x0021 
    c056:       07 46           mov     r6,     r7      
    c058:       82 55 72 01     add     r5,     &0x0172 
    c05c:       00 13           reti                    

0000c05e <_unexpected_>:
    c05e:       00 13           reti                    

Disassembly of section .vectors:

0000ffe0 <__ivtbl_16>:
    ffe0:       04 c0 04 c0 04 c0 04 c0 04 c0 04 c0 04 c0 04 c0     ................
    fff0:       04 c0 4e c0 04 c0 04 c0 04 c0 04 c0 04 c0 08 c0     ..N.............

To get your SD Card Bootloader running in Energia you would have to replace the blink.S with SDBSL-G2553.S and change your coding style to make msp430-gcc happy.  You would also have to replicate all the code that the normal C runtime code does to create an environment for a C++ application. Basically you have to setup the .data section (copying the initial values from flash to ram), initialize the .bss section all to zero, loop through all the global c++ constructors, then call the users main function.



Edited by Rickta59
added zip file

Share this post

Link to post
Share on other sites

Thanks very much for your replies.  I'll go back and study what you've said, but it kinda looks like it just needs to stay in Naken assembler. That  produces a .hex file which I flash to the chip with TI's MSP Flasher. There is no "environment".  And I think any attempt to convert it to C is just bound to make it larger and less efficient. 

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now