To produce add-in executables using GCC, you need some initialization code, and some section magic. This information has been extracted by analyzing object code produced by the official SDK.
Toolchain installation
Gentoo
Confirmed to work with
GCC version 4.1.2 and
Binutils version 2.17, compiled and installed using Gentoo Linux's
crossdev software. The target string for my installation is sh3eb-unknown-linux-gnu.
Ubuntu
GCC 4.3.2 and binutils 2.19. Follow
http://wiki.osdev.org/GCC_Cross-Compiler#Step_1_-_Bootstrap∞ but set the TARGET variable to "sh3eb-elf". I have only compiled the toolchain with C support (only
--enable-languages=c when configuring GCC).
Required files
Firstly, you need a linker script that says where each portion of the code is located;
OUTPUT_ARCH(sh3)
ENTRY(initialize)
MEMORY
{
rom : o = 0x00300200, l = 512k
ram : o = 0x08100000, l = 64k /* pretty safe guess */
}
SECTIONS
{
.text : {
*(.pretext) /* init stuff */
*(.text)
} > rom
.rodata : {
*(.rodata)
*(.rodata.str1.4)
_romdata = . ; /* symbol for initialization data */
} > rom
.bss : {
_bbss = . ;
_bssdatasize = . ;
LONG(0); /* bssdatasize */
*(.bss) *(COMMON);
_ebss = . ;
} > ram
.data : AT(_romdata) {
_bdata = . ;
*(.data);
_edata = . ;
} > ram
}
The script specifies two areas, named ram and rom. The executable code and constant data are located in rom (they don't change), while stuff like variables and initialized variables have to stay in ram. Our
initialize() function is specified to stay in
.pretext, the first section in the executable, and will therefore be the first code executed when the file is written in binary.
.section .pretext
.global initialize
initialize:
sts.l pr, @-r15
! set up TLB
mov.l Hmem_SetMMU, r3
mov.l address_one, r4 ! 0x8102000
mov.l address_two, r5 ! 0x8801E000
jsr @r3 ! _Hmem_SetMMU
mov #108, r6
! clear the BSS
mov.l bbss, r4 ! start
mov.l ebss, r5 ! end
bra L_check_bss
mov #0, r6
L_zero_bss:
mov.l r6, @r4 ! zero and advance
add #4, r4
L_check_bss:
cmp/hs r5, r4
bf L_zero_bss
! Copy the .data
mov.l bdata, r4 ! dest
mov.l edata, r5 ! dest limit
mov.l romdata, r6 ! source
bra L_check_data
nop
L_copy_data:
mov.l @r6+, r3
mov.l r3, @r4
add #4, r4
L_check_data:
cmp/hs r5, r4
bf L_copy_data
mov.l bbss, r4
mov.l edata, r5
sub r4, r5 ! size of .bss and .data sections
add #4, r5
mov.l bssdatasize, r4
mov.l r5, @r4
mov.l GLibAddinAplExecutionCheck, r2
mov #0, r4
mov #1, r5
jsr @r2 ! _GLibAddinAplExecutionCheck(0,1,1);
mov r5, r6
mov.l CallbackAtQuitMainFunction, r3
mov.l exit_handler, r4
jsr @r3 ! _CallbackAtQuitMainFunction(&exit_handler)
nop
mov.l main, r3
jmp @r3 ! _main()
lds.l @r15+, pr
_exit_handler:
mov.l r14, @-r15
mov.l r13, @-r15
mov.l r12, @-r15
sts.l pr, @-r15
mov.l Bdel_cychdr, r14
jsr @r14 ! _Bdel_cychdr
mov #6, r4
jsr @r14 ! _Bdel_cychdr
mov #7, r4
jsr @r14 ! _Bdel_cychdr
mov #8, r4
jsr @r14 ! _Bdel_cychdr
mov #9, r4
jsr @r14 ! _Bdel_cychdr
mov #10, r4
mov.l BfileFLS_CloseFile, r12
mov #4, r14
mov #0, r13
L_close_files:
jsr @r12 ! _BfileFLS_CloseFile
mov r13, r4
add #1, r13
cmp/ge r14, r13
bf L_close_files
mov.l flsFindClose, r12
mov #0, r13
L_close_finds:
jsr @r12 ! _flsFindClose
mov r13, r4
add #1, r13
cmp/ge r14, r13
bf L_close_finds
lds.l @r15+, pr
mov.l @r15+, r12
mov.l @r15+, r13
mov.l Bkey_Set_RepeatTime_Default, r2
jmp @r2 ! _Bkey_Set_RepeatTime_Default
mov.l @r15+, r14
.align 4
address_two: .long 0x8801E000
address_one: .long 0x8102000
Hmem_SetMMU: .long _Hmem_SetMMU
GLibAddinAplExecutionCheck: .long _GLibAddinAplExecutionCheck
CallbackAtQuitMainFunction: .long _CallbackAtQuitMainFunction
Bdel_cychdr: .long _Bdel_cychdr
BfileFLS_CloseFile: .long _BfileFLS_CloseFile
flsFindClose: .long _flsFindClose
Bkey_Set_RepeatTime_Default: .long _Bkey_Set_RepeatTime_Default
bbss: .long _bbss
ebss: .long _ebss
edata: .long _edata
bdata: .long _bdata
romdata: .long _romdata
bssdatasize: .long _bssdatasize
exit_handler: .long _exit_handler
main: .long _main
_Hmem_SetMMU:
mov.l sc_addr, r2
mov.l 1f, r0
jmp @r2
nop
1: .long 0x3FA
_Bdel_cychdr:
mov.l sc_addr, r2
mov.l 1f, r0
jmp @r2
nop
1: .long 0x119
_BfileFLS_CloseFile:
mov.l sc_addr, r2
mov.l 1f, r0
jmp @r2
nop
1: .long 0x1E7
_Bkey_Set_RepeatTime_Default:
mov.l sc_addr, r2
mov.l 1f, r0
jmp @r2
nop
1: .long 0x244
_CallbackAtQuitMainFunction:
mov.l sc_addr, r2
mov.l 1f, r0
jmp @r2
nop
1: .long 0x494
_flsFindClose:
mov.l sc_addr, r2
mov.l 1f, r0
jmp @r2
nop
1: .long 0x218
_GLibAddinAplExecutionCheck:
mov.l sc_addr, r2
mov #0x13, r0
jmp @r2
nop
sc_addr: .long 0x80010070
.end
This code is similar to the code that is used by the official SDK. It basically clears out the RAM for the uninitialized variables, initializes the appropriate variables and registers a function to be run on program exit. The last thing it does, is to call
_main() where the user has written his code.
Note the underscore in _main()
The names of these functions are identical to the ones referenced by the SDK. There is no need to keep the exact names, but in some cases the function name is all that hints about what the function does (when I don't know for sure).
Compiling
That file should be all you need to compile a basic C program, and to run most, if not all OS-calls. You will also need a C-library if you want to use stuff like math functions. That is however another task.
I am assuming you are in a directory with
crt0.s,
addin.ld and your programs main() function in
main.c. To compile your program with your SH3-capable GCC, run:
sh3eb-gcc -m3 -mb -O9 -nostdlib -T addin.ld" crt0.s main.c -o myaddin.elf -lgcc
To avoid unnecessary compilation of crt0.s, compile it to an object and specify that object instead when compiling your files.
Note: If you get complaints from GCC about the unsupported emulation mode shelf, your GCC support libraries (libgcc.a) are not compiled in big-endian mode. Your GCC must support shelf to create big endian code.
If you are referencing other source files (you use code from multiple files), you can just list them right by the others. The option
-O9 enables full optimization, but is optional. If your compiler supports both little and big endian targets, add
-mb to make sure it is in the right format. GCC has now produced an ELF file called
myaddin.elf, The calculator does not handle ELF files, only binary files with a specific
HeaderFormat. Therefore, we need to copy the wanted sections into a binary file, using
objcopy:
sh3eb-objcopy -R .comment -R .bss -O binary myaddin.elf myaddin.bin
This strips out the .comment and .bss sections and stores the other ones in binary format. This binary needs a header before it is a ready add-in. You can do this with
g1awrapper.