## Minimal program

Let’s try compiling and linking a minimal C program with the SDCC
compiler, and disassemble it to see what happens behind our back.

# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
# rebase in progress; onto 514701a
# You are currently editing a commit while rebasing branch 'sdcc' on '514701a'.
#
# Changes to be committed:
#	new file:   .gitignore
#	new file:   Makefile
#	new file:   main.c
#
Dissecting the minimal program

So, here’s our program:

```c
int main(void)
{
	return 0;
}
```

What could be shorter? Let’s see what it turns into.

```
$ make
```

Here’s the disassembly. (Using the map file generated by the linker, I
deobfuscated the `L00xx` labels and annotated with the sources from the
SDCC standard library.)

```
CSEG AT 0000h
  0000 020006       LJMP __sdcc_gsinit_startup

__sdcc_program_startup:
  0003 020062       LJMP _main

                                                        ; lib/src/mcs51/crtstart.asm line 43
__sdcc_gsinit_startup:                                  __sdcc_gsinit_startup::
  0006 758107       MOV SP, #7h                             mov     sp,#__start__stack - 1
                                                            .area   GSINIT2 (CODE)
  0009 120066       LCALL __sdcc_external_startup           lcall   __sdcc_external_startup
  000C E582         MOV A, DPL                              mov     a,dpl
  000E 6003         JZ __mcs51_genXINIT                     jz      __sdcc_init_data
  0010 020003       LJMP __sdcc_program_startup             ljmp    __sdcc_program_startup
                                                        __sdcc_init_data:
                                                        ; lib/src/mcs51/crtxinit.asm line 76
__mcs51_genXINIT:                                       __mcs51_genXINIT::
  0013 7900         MOV R1, #0h                             mov      r1,#l_XINIT
  0015 E9           MOV A, R1                               mov      a,r1
  0016 4400         ORL A, #0h                              orl      a,#(l_XINIT >> 8)
  0018 601B         JZ __mcs51_genRAMCLEAR                  jz       00003$
  001A 7A00         MOV R2, #0h                             mov      r2,#((l_XINIT+255) >> 8)
  001C 90006A       MOV DPTR, #006Ah                        mov      dptr,#s_XINIT
  001F 7800         MOV R0, #0h                             mov      r0,#s_XISEG
  0021 75A000       MOV P2, #0h                             mov      __XPAGE,#(s_XISEG >> 8)
L0008:                                                  00001$:
  0024 E4           CLR A                                   clr      a
  0025 93           MOVC A, @A+DPTR                         movc     a,@a+dptr
  0026 F2           MOVX @R0, A                             movx     @r0,a
  0027 A3           INC DPTR                                inc      dptr
  0028 08           INC R0                                  inc      r0
  0029 B80002       CJNE R0, #0h, L0007                     cjne     r0,#0,00002$
  002C 05A0         INC P2                                  inc      __XPAGE
L0007:                                                  00002$:
  002E D9F4         DJNZ R1, L0008                          djnz     r1,00001$
  0030 DAF2         DJNZ R2, L0008                          djnz     r2,00001$
  0032 75A0FF       MOV P2, #0FFh                           mov      __XPAGE,#0xFF
                                                        00003$:
                                                        ; lib/src/mcs51/crtclear.asm line 41
__mcs51_genRAMCLEAR:                                    __mcs51_genRAMCLEAR::
  0035 E4           CLR A                                   clr     a
  0036 78FF         MOV R0, #0FFh                           mov     r0,#(l_IRAM-1)  ; mov r0,a will do for most devices
L0009:                                                  00004$:

  0038 F6           MOV @R0, A                              mov     @r0,a
  0039 D8FD         DJNZ R0, L0009                          djnz    r0,00004$
                                                        ; lib/src/mcs51/crtxclear.asm line 41
__mcs51_genXRAMCLEAR:                                   __mcs51_genXRAMCLEAR::
  003B 7800         MOV R0, #0h                             mov     r0,#l_PSEG
  003D E8           MOV A, R0                               mov     a,r0
  003E 4400         ORL A, #0h                              orl     a,#(l_PSEG >> 8)
  0040 600A         JZ L0010                                jz      00006$
  0042 7900         MOV R1, #0h                             mov     r1,#s_PSEG
  0044 75A000       MOV P2, #0h                             mov     __XPAGE,#(s_PSEG >> 8)
  0047 E4           CLR A                                   clr     a
L0011:                                                  00005$:
  0048 F3           MOVX @R1, A                             movx    @r1,a
  0049 09           INC R1                                  inc     r1
  004A D8FC         DJNZ R0, L0011                          djnz    r0,00005$
L0010:                                                  00006$:
  004C 7800         MOV R0, #0h                             mov     r0,#l_XSEG
  004E E8           MOV A, R0                               mov     a,r0
  004F 4400         ORL A, #0h                              orl     a,#(l_XSEG >> 8)
  0051 600C         JZ L0012                                jz      00008$
  0053 7900         MOV R1, #0h                             mov     r1,#((l_XSEG + 255) >> 8)
  0055 900000       MOV DPTR, #0000h                        mov     dptr,#s_XSEG
  0058 E4           CLR A                                   clr     a
L0013:                                                  00007$:
  0059 F0           MOVX @DPTR, A                           movx    @dptr,a
  005A A3           INC DPTR                                inc     dptr
  005B D8FC         DJNZ R0, L0013                          djnz    r0,00007$
  005D D9FA         DJNZ R1, L0013                          djnz    r1,00007$
L0012:                                                  00008$:
  005F 020003       LJMP __sdcc_program_startup

                                                        ; main.c
_main:                                                  int main(void)
                                                        {
  0062 900000       MOV DPTR, #0000h
  0065 22           RET                                     return 0;
                                                        }

                                                        ; lib/src/_startup.c line 45
__sdcc_external_startup:                                unsigned char _sdcc_external_startup (void) __nonbanked
                                                        {
  0066 758200       MOV DPL, #0h
  0069 22           RET                                     return 0;
                                                        }
END
```

Now let’s see what’s happening.

* `__sdcc_gsinit_startup` sets up stack bottom at address 0x07. (The
  8051 microcontroller increments the stack pointer before storing a
  byte, so actual stack starts at 0x08.) This is understandable: the
  first 8 bytes of RAM are mapped to the 0th bank of registers R0÷R7,
  and all further space is available for the program variables and
  stack. And we don’t have any variables at this point.
* Next it calls `__sdcc_external_startup`. The intent is that we can
  replace this function if we want to do anything important before any
  other initialization takes place. The default implementation just
  returns 0, meaning “go on with initialization”.
* `__mcs51_genXINIT` looks like it could copy some data from the code
  memory (pseudo-ROM) into XRAM, if the value of `l_XINIT` were
  non-zero. But note that it by default assumes that the controller uses
  the P2 register for the high byte of XRAM address. Ours does not and
  in fact has dual DPTR, so we would need to recompile with
  `-DDUAL_DPTR`.
* `__mcs51_genRAMCLEAR` zeroes out the IRAM data.
* `__mcs51_genXRAMCLEAR` zeroes out the XRAM data (if any). Again it
  assumes P2 is used for accessing XRAM beyond 0xFF, and this time
  doesn’t let us opt out of that.

Luckily, there is no indication that we will need any initialized
variables — in fact, the stock firmware doesn’t have any. Or rather,
there are, but they are all initialized to zero, except for one
variable. We can replicate that with a dynamic initialization routine.
Set up XRAM-related compiler options

Last time we looked at the default startup code and found that it’s not
very suitable for our architecture, because it assumes that access to
high XRAM addresses is done by writing the high byte of the address into
P2.

SDCC has two options that are related to XRAM:

* `--no-xinit-opt` disables the optimization of initialized variables.
  If it is enabled (by default), the initial values are stored in a part
  of code and copied into XRAM on startup. If disabled, the code will
  contain actual MOV instructions to initialize each variable.
* `--xram-size` tells the compiler how much XRAM we have. It can then
  warn us if we define more variables than can fit.
Disassemble again

Now let’s look what those changes won us.

```
$ make
```

Again, I deobfuscated the labels and added source annotations.

```
CSEG AT 0000h
  0000 020006       LJMP __sdcc_gsinit_startup

__sdcc_program_startup:
  0003 02001C       LJMP _main

                                                        ; lib/src/mcs51/crtstart.asm line 43
__sdcc_gsinit_startup:                                  __sdcc_gsinit_startup::
  0006 758107       MOV SP, #7h                             mov     sp,#__start__stack - 1
                                                            .area   GSINIT2 (CODE)
  0009 120020       LCALL __sdcc_external_startup           lcall   __sdcc_external_startup
  000C E582         MOV A, DPL                              mov     a,dpl
  000E 6003         JZ __mcs51_genRAMCLEAR                  jz      __sdcc_init_data
  0010 020003       LJMP __sdcc_program_startup             ljmp    __sdcc_program_startup
                                                        __sdcc_init_data:
                                                        ; lib/src/mcs51/crtclear.asm line 41
__mcs51_genRAMCLEAR:                                    __mcs51_genRAMCLEAR::
  0013 E4           CLR A                                   clr     a
  0014 78FF         MOV R0, #0FFh                           mov     r0,#(l_IRAM-1)  ; mov r0,a will do for most devices
L0006:                                                  00004$:
  0016 F6           MOV @R0, A                              mov     @r0,a
  0017 D8FD         DJNZ R0, L0006                          djnz    r0,00004$
  0019 020003       LJMP __sdcc_program_startup

                                                        ; main.c
_main:                                                  int main(void)
                                                        {
  001C 900000       MOV DPTR, #0000h
  001F 22           RET                                     return 0;
                                                        }

                                                        ; lib/src/_startup.c line 45
__sdcc_external_startup:                                unsigned char _sdcc_external_startup (void) __nonbanked
                                                        {
  0020 758200       MOV DPL, #0h
  0023 22           RET                                     return 0;
                                                        }
END
```

Now that’s much better. Notice that by opting out of XRAM initialization
we also lost XRAM zeroing. So much for the better for now; if we ever
want XRAM variables, we can always write a custom
`_sdcc_external_startup` routine.
## Safety switch

Now that we are fairly confident that (1) the compiler works, (2) it
produces code for our processor, (3) the resulting binary does not
contain any unwanted code; we’d like to build something that will run on
the actual hardware. But first we want to make sure we can go back to a
working firmware, or to flash a different program.

The stock firmware does this by handling a special USB request, upon
receiving which it reboots in ISP DFU mode. But our experimental
programs will not have USB handling until much later.

So we will establish a rule: In any unfamiliar situation, reboot to ISP.

And, in the beginning, every situation is unfamiliar.

So let’s write a function to reboot to ISP, and call it immediately upon
entering `main`.