This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to disable C_STARTUP code generation and have "plain" hex file

Hallo!

I have boot loader written in asm and have to translate it to C-language (probably with asm inserts) to simplify life for successors. Keil does the following:
a) for the very first code, it always put jump instruction to transfer control to either generated C_STARTUP code section (if no startup asm module in the project) or to the startup asm module if it was added to the project
b) puts all associated startup code to the executable
c) generates jump back to main()

00000000 FA000800  JMPS     ?C_STARTUP(0x8)
...
00000XXX FA000400  JMPS     main(0x4)


Questions:
1) Is there any way to avoid this jump instructions, suppress startup code generation and just have "plain" compiler output
2) This plain executable should be aligned to the adress (FA60h) at which I need to place my bootloader entry point

So, the idea is how to get rid of all the code not written by you, i.e. with no any piece of code added by IDE s/w.

Does anybody have answers?

Thanks & Regards,
Nikolay.

Parents
  • Hallo, All!

    FYI (to whom it may concern):

    Since there was no any reply, I went through and figured out simple solution on how to write any bootloader-like f/w in C (with inline ASM inserts if needed) with influence of Keil IDE avoided. The influence involves requirement to have c_startup code section and if you exclude startup ASM module from the project, the IDE will generate some default code you do not need; as well, by default Keil inserts jumps from reset vector to the startup section and backward jump to the C-module main() entry point after the startup initialization done.

    The solution is as follows:

    1. Use something like this fake startup ASM module in Keil IDE project. This code does nothing in reality:

    ;
    ; Stub to get boot-loader compiled from C source files
    ; w/o any undesirable code created by IDE,
    ; i.e. this stub is to avoid the c_startup code and JMPS between
    ; reset vector, c_startup and main()
    ;
    ; THIS CODE DOES NOTHING IN REALITY, THIS IS JUST A STUB
    ;
    ; Bkl/Sep-2007
    ;
    
    $MOD167   ; C167 mode
    
    ; No real influence on HEX file out of memory model analyzed here
    $CASE
    $IF NOT TINY
    $SEGMENTED     ; boot-loader/monitor better uses EXTS
    $ENDIF
    
    ; below, all directives almost "fake" to make Keil happy
    _STKSZ          SET     1
    _STKSZ1         SET     2
    
    $IF NOT TINY
    ASSUME  DPP3:SYSTEM
    ASSUME  DPP2:NDATA
    $ENDIF
    
    NAME    ?C_STARTUP
    PUBLIC  ?C_STARTUP
    
    $IF MEDIUM OR LARGE OR HLARGE OR XLARGE
    Model   LIT     'FAR'
    $ELSE
    Model   LIT     'NEAR'
    $ENDIF
    
    EXTRN   main:Model
    
    PUBLIC          ?C_USRSTKBOT
    
    ?C_USERSTACK    SECTION DATA PUBLIC 'NDATA'
    $IF NOT TINY
    NDATA           DGROUP  ?C_USERSTACK
    $ENDIF
    
    USTSZ   EQU     80H
    
    ?C_USRSTKBOT:
                    DS      USTSZ
    ?C_USERSTKTOP:
    ?C_USERSTACK    ENDS
    
    ?C_MAINREGISTERS        REGDEF  R0 - R15
    
    
    ; No actual code will be generated (!)
    _TOS    EQU     0FC00H
    _BOS    EQU     _TOS - (1024 >> _STKSZ1)
    
    PUBLIC          ?C_SYSSTKBOT
    PUBLIC          ?C_SYSSTKTOP
    
    ?C_SYSSTKBOT    EQU     _BOS
    ?C_SYSSTKTOP    EQU     _TOS
    
                    SSKDEF  _STKSZ
    
    ;
    ; Empty c_startup section as formal requirement to be fulfilled
    ;
    ; No jump to main() and jump to here from reset will make
    ; debugging more hard but allows to produce from C source files
    ; nicely aligned HEX file as it being compiled from an ASM source
    ;
    ; To debug the code itself, either home-made s/w may require
    ; or a separate debug target options with this stub being re-modelled
    ;
    
    ; this boot-strap start location (0xFA60)
    ; has no code originated from c_startup
    
    ?C_STARTUP_CODE SECTION CODE AT 0FA60H
    
    ; RESET = 0 is fake vector since ignored due to NOVT directive in IDE
    ?C_RESET        PROC TASK C_STARTUP INTNO RESET = 0
    ?C_STARTUP:     LABEL   Model
    
    $IF TINY
                   ; JMP     main         ; COMMENTED OUT INTENTIONALLY
    $ELSE
                   ; JMP     FAR main     ; COMMENTED OUT INTENTIONALLY
    $ENDIF
    
    ?C_RESET        ENDP
    ?C_STARTUP_CODE ENDS
    
                    END
    

    The startup ASM code is just a stub which does not produce real code so all STARTUP stuff must be done in C source files.

    2. You should define your <User Classes> in <Target Options\L166 Locate> tab. E.g.: BOOTCODE(0xFA60-0xFDFF), BOOTDATA(0xFC00-0xFDFF), NDATA(0xFC00-0xFDFF). Uncheck <Use Memory Layout from Target Dialog>. Also, you may want to define User Sections as well. <DPPUSE> can also be unchecked if you take care of addressing (but be careful about it)

    3. Under <Target Options\L166 Misc\Misc Controls> put NOVT linker directive to get avoid JUMS from 0x0 address to the c_startup added to output HEX file. Remember, the JUMPS from c_startup to the main() is commented out in fake ASM module

    4. Put #pragma RENAMECLASS (NCODE=BOOTCODE) (see example on user-defined classes above) before the main(). Check you have no executable code before or at this address except the main()

    The main.c module (example) is shown below:

    //
    // Second-stage bootloader
    // C16x MCU family, Keil compiler
    //
    #include <intrins.h>
    #include <c167cs.h>
    
    #pragma SMALL
    #pragma RENAMECLASS (NCODE=BOOTCODE)
    
    // *************************************************************
    //      ENTRY POINT OF BOOT-LOADER STAGE #2
    // *************************************************************
    void main(void) {
     // the 3 instructions below are executed in 32-byte boot-loader
     // _diswdt_();
     // TFR= ZEROS;
     // PSW= ZEROS;
     STKOV= 0xFA00;
     STKUN= 0xFC00;
     SP= 0xFC00;
     CP= 0xFC00;
    
     SYSCON= 0x0;
     BUSCON0= 0x0;
     BUSCON1= 0x0;
     ADDRSEL1= 0x0;
     /*
     BUSCON2= 0x0; ADDRSEL2= 0x0;
     BUSCON3= 0x0; ADDRSEL3= 0x0;
     BUSCON4= 0x0; ADDRSEL4= 0x0;
       */
       __asm
     {
         JMPS 0, FAR PTR init_sys
       init_sys:
     }
    
     DPP0= 0x0;
     DPP1= 0x1;
     DPP2= 0x2;
     DPP3= 0x3;
    
      _einit_();
    
      // YOUR CODE HERE ...
      while(1);
    
    }
    

    As a result, "plain" hex file is generated, with no undesirable instruction in it, i.e. All instructions are 1:1 controlled in C-source/inline ASM:

    :020000040000FA
    :10 FA60 00 E60A00FAE60B00FCE60900FCE60800FC EA
    :10 FA70 00 E6890000E6860000E68A0000E60C0000 49
    :04 FA80 00 FA0084FA 0A
    :10 FA84 00 E6000000E6010100E6020200E6030300 CE
    :06 FA94 00 B54AB5B50DFF F7
    :00000001FF
    

    This HEX is equivalent to the ASM:

    0000FA60 E60A00FA  MOV      STKOV,#0xFA00
    0000FA64 E60B00FC  MOV      STKUN,#0xFC00
    0000FA68 E60900FC  MOV      SP,#0xFC00
    0000FA6C E60800FC  MOV      CP,#0xFC00
    0000FA70 E6890000  MOV      SYSCON,#0x0000
    0000FA74 E6860000  MOV      BUSCON0,#0x0000
    0000FA78 E68A0000  MOV      BUSCON1,#0x0000
    0000FA7C E60C0000  MOV      ADDRSEL1,#0x0000
    0000FA80 FA0084FA  JMPS     0x00FA84
    0000FA84 E6000000  MOV      DPP0,#0x0000
    0000FA88 E6010100  MOV      DPP1,#0x0001
    0000FA8C E6020200  MOV      DPP2,#0x0002
    0000FA90 E6030300  MOV      DPP3,#0x0003
    0000FA94 B54AB5B5  EINIT
    0000FA98 0DFF      JMPR     CC_UC,0x00FA98
    

    P.S. An useful example on bootstraping for further reading can be found at: www.rigelcorp.com/.../isp97.pdf

    Regards,
    Nikolay.

Reply
  • Hallo, All!

    FYI (to whom it may concern):

    Since there was no any reply, I went through and figured out simple solution on how to write any bootloader-like f/w in C (with inline ASM inserts if needed) with influence of Keil IDE avoided. The influence involves requirement to have c_startup code section and if you exclude startup ASM module from the project, the IDE will generate some default code you do not need; as well, by default Keil inserts jumps from reset vector to the startup section and backward jump to the C-module main() entry point after the startup initialization done.

    The solution is as follows:

    1. Use something like this fake startup ASM module in Keil IDE project. This code does nothing in reality:

    ;
    ; Stub to get boot-loader compiled from C source files
    ; w/o any undesirable code created by IDE,
    ; i.e. this stub is to avoid the c_startup code and JMPS between
    ; reset vector, c_startup and main()
    ;
    ; THIS CODE DOES NOTHING IN REALITY, THIS IS JUST A STUB
    ;
    ; Bkl/Sep-2007
    ;
    
    $MOD167   ; C167 mode
    
    ; No real influence on HEX file out of memory model analyzed here
    $CASE
    $IF NOT TINY
    $SEGMENTED     ; boot-loader/monitor better uses EXTS
    $ENDIF
    
    ; below, all directives almost "fake" to make Keil happy
    _STKSZ          SET     1
    _STKSZ1         SET     2
    
    $IF NOT TINY
    ASSUME  DPP3:SYSTEM
    ASSUME  DPP2:NDATA
    $ENDIF
    
    NAME    ?C_STARTUP
    PUBLIC  ?C_STARTUP
    
    $IF MEDIUM OR LARGE OR HLARGE OR XLARGE
    Model   LIT     'FAR'
    $ELSE
    Model   LIT     'NEAR'
    $ENDIF
    
    EXTRN   main:Model
    
    PUBLIC          ?C_USRSTKBOT
    
    ?C_USERSTACK    SECTION DATA PUBLIC 'NDATA'
    $IF NOT TINY
    NDATA           DGROUP  ?C_USERSTACK
    $ENDIF
    
    USTSZ   EQU     80H
    
    ?C_USRSTKBOT:
                    DS      USTSZ
    ?C_USERSTKTOP:
    ?C_USERSTACK    ENDS
    
    ?C_MAINREGISTERS        REGDEF  R0 - R15
    
    
    ; No actual code will be generated (!)
    _TOS    EQU     0FC00H
    _BOS    EQU     _TOS - (1024 >> _STKSZ1)
    
    PUBLIC          ?C_SYSSTKBOT
    PUBLIC          ?C_SYSSTKTOP
    
    ?C_SYSSTKBOT    EQU     _BOS
    ?C_SYSSTKTOP    EQU     _TOS
    
                    SSKDEF  _STKSZ
    
    ;
    ; Empty c_startup section as formal requirement to be fulfilled
    ;
    ; No jump to main() and jump to here from reset will make
    ; debugging more hard but allows to produce from C source files
    ; nicely aligned HEX file as it being compiled from an ASM source
    ;
    ; To debug the code itself, either home-made s/w may require
    ; or a separate debug target options with this stub being re-modelled
    ;
    
    ; this boot-strap start location (0xFA60)
    ; has no code originated from c_startup
    
    ?C_STARTUP_CODE SECTION CODE AT 0FA60H
    
    ; RESET = 0 is fake vector since ignored due to NOVT directive in IDE
    ?C_RESET        PROC TASK C_STARTUP INTNO RESET = 0
    ?C_STARTUP:     LABEL   Model
    
    $IF TINY
                   ; JMP     main         ; COMMENTED OUT INTENTIONALLY
    $ELSE
                   ; JMP     FAR main     ; COMMENTED OUT INTENTIONALLY
    $ENDIF
    
    ?C_RESET        ENDP
    ?C_STARTUP_CODE ENDS
    
                    END
    

    The startup ASM code is just a stub which does not produce real code so all STARTUP stuff must be done in C source files.

    2. You should define your <User Classes> in <Target Options\L166 Locate> tab. E.g.: BOOTCODE(0xFA60-0xFDFF), BOOTDATA(0xFC00-0xFDFF), NDATA(0xFC00-0xFDFF). Uncheck <Use Memory Layout from Target Dialog>. Also, you may want to define User Sections as well. <DPPUSE> can also be unchecked if you take care of addressing (but be careful about it)

    3. Under <Target Options\L166 Misc\Misc Controls> put NOVT linker directive to get avoid JUMS from 0x0 address to the c_startup added to output HEX file. Remember, the JUMPS from c_startup to the main() is commented out in fake ASM module

    4. Put #pragma RENAMECLASS (NCODE=BOOTCODE) (see example on user-defined classes above) before the main(). Check you have no executable code before or at this address except the main()

    The main.c module (example) is shown below:

    //
    // Second-stage bootloader
    // C16x MCU family, Keil compiler
    //
    #include <intrins.h>
    #include <c167cs.h>
    
    #pragma SMALL
    #pragma RENAMECLASS (NCODE=BOOTCODE)
    
    // *************************************************************
    //      ENTRY POINT OF BOOT-LOADER STAGE #2
    // *************************************************************
    void main(void) {
     // the 3 instructions below are executed in 32-byte boot-loader
     // _diswdt_();
     // TFR= ZEROS;
     // PSW= ZEROS;
     STKOV= 0xFA00;
     STKUN= 0xFC00;
     SP= 0xFC00;
     CP= 0xFC00;
    
     SYSCON= 0x0;
     BUSCON0= 0x0;
     BUSCON1= 0x0;
     ADDRSEL1= 0x0;
     /*
     BUSCON2= 0x0; ADDRSEL2= 0x0;
     BUSCON3= 0x0; ADDRSEL3= 0x0;
     BUSCON4= 0x0; ADDRSEL4= 0x0;
       */
       __asm
     {
         JMPS 0, FAR PTR init_sys
       init_sys:
     }
    
     DPP0= 0x0;
     DPP1= 0x1;
     DPP2= 0x2;
     DPP3= 0x3;
    
      _einit_();
    
      // YOUR CODE HERE ...
      while(1);
    
    }
    

    As a result, "plain" hex file is generated, with no undesirable instruction in it, i.e. All instructions are 1:1 controlled in C-source/inline ASM:

    :020000040000FA
    :10 FA60 00 E60A00FAE60B00FCE60900FCE60800FC EA
    :10 FA70 00 E6890000E6860000E68A0000E60C0000 49
    :04 FA80 00 FA0084FA 0A
    :10 FA84 00 E6000000E6010100E6020200E6030300 CE
    :06 FA94 00 B54AB5B50DFF F7
    :00000001FF
    

    This HEX is equivalent to the ASM:

    0000FA60 E60A00FA  MOV      STKOV,#0xFA00
    0000FA64 E60B00FC  MOV      STKUN,#0xFC00
    0000FA68 E60900FC  MOV      SP,#0xFC00
    0000FA6C E60800FC  MOV      CP,#0xFC00
    0000FA70 E6890000  MOV      SYSCON,#0x0000
    0000FA74 E6860000  MOV      BUSCON0,#0x0000
    0000FA78 E68A0000  MOV      BUSCON1,#0x0000
    0000FA7C E60C0000  MOV      ADDRSEL1,#0x0000
    0000FA80 FA0084FA  JMPS     0x00FA84
    0000FA84 E6000000  MOV      DPP0,#0x0000
    0000FA88 E6010100  MOV      DPP1,#0x0001
    0000FA8C E6020200  MOV      DPP2,#0x0002
    0000FA90 E6030300  MOV      DPP3,#0x0003
    0000FA94 B54AB5B5  EINIT
    0000FA98 0DFF      JMPR     CC_UC,0x00FA98
    

    P.S. An useful example on bootstraping for further reading can be found at: www.rigelcorp.com/.../isp97.pdf

    Regards,
    Nikolay.

Children
  • Hey Nikolay,

    Thank you for taking the trouble of following up on your own post and sharing what you've found. Nice netiquette!

    Some thoughts I had about this:

    We are not using the IDE at all (editor + GNU make instead) but apparently the IDE/linker requires a startup file (which we have so no problems there)? I haven't been able to find a directive to turn this off. But it got me thinking, would it be possible to rename your main.c file to c_startup.c ?

    Your startup file does contain some other definitions/symbols (stack, etc.) but I guess you could put them in the same .c file using #pragma asm.

    I'm not sure if it would be possible to rename the module name from inside a .c file. It's normally just the filename, see
    http://www.keil.com/support/man/docs/c166/c166_ap_sgc.htm
    That way you could make a single bootloader.c that contains some symbol definition asm from the startup file and is renamed to C_STARTUP to keep the linker happy.

    [...]

    Ah, take a look at:
    http://www.keil.com/support/man/docs/l166/l166_name.htm

    You could do it using the linker like so:

    C166 bootloader.c
    L166 bootloader.obj NAME(C_STARTUP) NOVECTAB
    

    Not sure it this works though :-)

    Thanks for the NOVECTAB/NOVT directive, didn't know it yet.

    Good luck.

    Kind regards,
    Joost

  • Hmm, the linker directive NAME from
    http://www.keil.com/support/man/docs/l166/l166_name.htm
    seems to do something different. It's a module name (?) (not the output file name) of the resulting absolute image file.

    Kind regards,
    Joost