THE PARTICLE COMPUTER DOCU
(The CCS2SDCC Migration Guide)

Example

We want to convert the following small CCS source.

Scripts

Imagine the follwing source is named interrupt.c and is located alone in a directory called test. We go into this directory and call our shell script: $ cd test
$ /path/to/file/convert.sh
Converting ./interrupt.c
We get a new directory SDCC: $ cd SDCC
$ ls
disptab.c  interrupt.c
Now lets have a look at the code and what our tools did with it:

before after  
#include "18F6720.h"
#use delay(clock=20000000)
#use fast_io(C)

#if 1 #define ON 1
  #define OFF 0
#else #define ON 0
  #define OFF 1 #endif
#include <compat.h>
#undef RESTART_WDT
#include "18F6720.h"
#define CLOCK_SPEED 20000000
#include <delay.c>
#undef set_mode_C
#define set_mode_C(input, pin) portmode_fast(C, input, pin)

#if 1 #define ON 1
  #define OFF 0
#else #define ON 0
  #define OFF 1 #endif
Included our "compatibility layer" compat.h and replaced the #use pragmas by our preprocessor macros. The other preprocessor macros were left untouched.
#byte foo = 0x10
#byte bar = 0x11

#byte PIC_TRISC = 0xF94
#byte PIC_TRISF = 0xF97
#byte PIC_LATF = 0xF8E

#define NINE 9
uint8_t at 0x10 foo;
uint8_t at 0x11 bar;

#define PIC_TRISC TRISC
#define PIC_TRISF TRISF
#define PIC_LATF LATF

#define NINE 9
#byte pragmas were mapped to SDCC readable constructions, except those starting with PIC_. We guess, we can map them to existing symbols already defined in the SDCC headers.
void PCLedRed (int state) {
  if (state)
    output_high(PIN_C2);
  else
    output_low(PIN_C2);
}

void PCLedBlueTgl () {
  PIC_LATF ^= 0b01000000;
}
void PCLedRed (uint8_t state) {
  if (state)
    output_high(PIN_C2);
  else
    output_low(PIN_C2);
}

void PCLedBlueTgl () {
  PIC_LATF ^= 0x40;
}
int was modified to uint8_t and the binary constant was converted into a hexadecimal constant.
#inline
void my_delay (long time) {
  long i;
  for (i = 0; i < time; i++) {
    // do nothing special
    foo = 10;
#asm
   loop:
    decfsz foo
    goto loop
#endasm
  }
}
 
void my_delay (uint16_t time) {
  uint16_t i;
  for (i = 0; i < time; i++) {
    // do nothing special
    foo = 10;
_asm
   ASM(loop):
    decfsz ASM(foo)
    goto ASM(loop)
_endasm;
  }
}
#inline was removed (has to be implemented manually) and symbols and labels appearing in inline assembler code were wrapped with a preprocessor macro that adds a "_" prefix.
#int_timer1
void timer1handler() {
  PCLedBlueTgl();
  my_delay(2000);
}
#define timer1_handler timer1handler
void timer1handler() {
  PCLedBlueTgl();
  my_delay(2000);
}
#int was replaced by a preprocessor macro.
#define hi(x) (*(&x+1))

#separate
int nothingspecial(int a, b, c, long d) {
  if (a + hi(d))
    return b;
  else
    return c;
}
#define hi(x) (*(&x+1))


uint8_t nothingspecial(uint8_t a, b, c, uint16_t d) {
  if (a + hi(d))
    return b;
  else
    return c;
}
Types were modified and #separate pragma was removed.
int main () {
  long data;
  data = 23;
  PIC_TRISF = 0;
  PIC_TRISC = 0;

  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  enable_interrupts(INT_TIMER1);
  enable_interrupts(GLOBAL);

  while (1) {
    PCLedRed(ON);
    my_delay(2000);
    PCLedRed(OFF);
    my_delay(200);
    nothingspecial(1, 2, 23, data);
    delay_ms(1000);
    data = 23;
    delay_us(10);
    data = 42;
    delay_us(NINE);
  }

  return 0;
}
uint8_t main () {
  uint16_t __PIC_data;
  __PIC_data = 23;
  PIC_TRISF = 0;
  PIC_TRISC = 0;

  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  enable_interrupts(INT_TIMER1);
  enable_interrupts(GLOBAL);

  while (1) {
    PCLedRed(ON);
    my_delay(2000);
    PCLedRed(OFF);
    my_delay(200);
    nothingspecial(1, 2, 23, __PIC_data);
    delay_ms(1000);
    __PIC_data = 23;
    delay_us_short(10);
    __PIC_data = 42;
    delay_us(NINE);
  }

  return 0;
}
Types were modified, variable data was renamed to __PIC_data (conflict with SDCC keyword) and delay_us(10) was replaced by delay_us_short(10). Although NINE will be expanded to 9, this line was not modified because the Perl script cannot interpret this macro.
#fuses hs,noprotect,nobrownout,nolvp,put,nowdt code char at __CONFIG1H config1h = 0xFF & _OSC_HS_1H;
code char at __CONFIG2H config2h = 0xFF & _WDT_OFF_2H;
code char at __CONFIG2L config2l = 0xFF & _PUT_ON_2L & _BODEN_OFF_2L;
code char at __CONFIG4L config4l = 0xFF & _LVP_OFF_4L;
// WARNING: "noprotect" ignored!
Configuration words were replaced, noprotect was ignored. (not yet implemented)

Manual work

According to the migration guide, we have to adjust some things manually:

Script output after manual modifications  
#include <compat.h>
#undef RESTART_WDT
#include "18F6720.h"
#define CLOCK_SPEED 20000000
#include <delay.c>
#undef set_mode_C
#define set_mode_C(input, pin) portmode_fast(C, input, pin)

#if 1 #define ON 1
  #define OFF 0
#else #define ON 0
  #define OFF 1 #endif
#include <compat.h>
#undef RESTART_WDT
//#include "18F6720.h"
#define CLOCK_SPEED 20000000
#include <delay.c>
#undef set_mode_C
#define set_mode_C(input, pin) portmode_fast(C, input, pin)

#if 1
  #define ON 1
  #define OFF 0
#else
  #define ON 0
  #define OFF 1
#endif
Only a single preprocessor directive may appear per line. We also do not need the former include file from CCS.
uint8_t at 0x10 foo;
uint8_t at 0x11 bar;

#define PIC_TRISC TRISC
#define PIC_TRISF TRISF
#define PIC_LATF LATF

#define NINE 9
uint8_t at 0x40 foo;
uint8_t at 0x41 bar;

#define PIC_TRISC TRISC
#define PIC_TRISF TRISF
#define PIC_LATF LATF

#define NINE 9
The locations of foo and bar raise a conflict. SDCC reserves the area from 0x00 to 0x22 for local variables, so foo and bar were moved.
void PCLedRed (uint8_t state) {
  if (state)
    output_high(PIN_C2);
  else
    output_low(PIN_C2);
}

void PCLedBlueTgl () {
  PIC_LATF ^= 0x40;
}
void PCLedRed (uint8_t state) {
  if (state)
    output_high(PIN_C2);
  else
    output_low(PIN_C2);
}

void PCLedBlueTgl () {
  PIC_LATF ^= 0x40;
}
Everything is fine here, no additional work to be done.
void my_delay (uint16_t time) {
  uint16_t i;
  for (i = 0; i < time; i++) {
    // do nothing special
    foo = 10;
_asm
   ASM(loop):
    decfsz ASM(foo)
    goto ASM(loop)
_endasm;
  }
}
void my_delay (uint16_t time) {
  uint16_t i;
  for (i = 0; i < time; i++) {
    // do nothing special
    foo = 10;
_asm
   loop:
    decfsz _foo
    goto loop
_endasm;
  }
}
Replaced label and symbol in the inlined assembler code for better readability. This function will still not be inlined, which could be achieved by replacing the function with a #define. But this would not be enough, because the label loop will appear twice in the main functionthen , raising an error in the assembler. So if we wanted to inline this code, we had to replace the whole part and remove the label somehow.
#define timer1_handler timer1handler
void timer1handler() {
  PCLedBlueTgl();
  my_delay(2000);
}
#define timer1_handler timer1handler
void timer1handler() {
  PCLedBlueTgl();
  my_delay(2000);
}
Everything is fine here, too.
#define hi(x) (*(&x+1))

uint8_t nothingspecial(uint8_t a, b, c, uint16_t d) {
  if (a + hi(d))
    return b;
  else
    return c;
}
#define hi(x) (*(((char *)&x)+1))

uint8_t nothingspecial(uint8_t a, uint8_t b, uint8_t c,  uint16_t d) {
  if (a + hi(d))
    return b;
  else
    return c;
}
Modified the hi macro, because SDCC &inttype + 1 would increase the address by two bytes (sizeof(int)), not 1 byte (CCS); hi(inttype) would return the value of the next integer in the registers behind inttype, not the upper byte of it. In addition to this, we had to add some types in the function parameter list.
uint8_t main () {
  uint16_t __PIC_data;
  __PIC_data = 23;
  PIC_TRISF = 0;
  PIC_TRISC = 0;

  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  enable_interrupts(INT_TIMER1);
  enable_interrupts(GLOBAL);

  while (1) {
    PCLedRed(ON);
    my_delay(2000);
    PCLedRed(OFF);
    my_delay(200);
    nothingspecial(1, 2, 23, __PIC_data);
    delay_ms(1000);
    __PIC_data = 23;
    delay_us_short(10);
    __PIC_data = 42;
    delay_us(NINE);
  }

  return 0;
}
uint8_t main () {
  uint16_t __PIC_data;
  __PIC_data = 23;
  PIC_TRISF = 0;
  PIC_TRISC = 0;

  init_irq_priorities();
  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  enable_interrupts(INT_TIMER1);
  enable_interrupts(INT_LOWPRIO);
  enable_interrupts(GLOBAL);

  while (1) {
    PCLedRed(ON);
    my_delay(2000);
    PCLedRed(OFF);
    my_delay(200);
    nothingspecial(1, 2, 23, __PIC_data);
    delay_ms(1000);
    __PIC_data = 23;
    delay_us_short(10);
    __PIC_data = 42;
    delay_us(NINE);
  }

  return 0;
}

#include <disptab.c>
Included disptab.c and added calls to initialize the interrupt priority registers and to enable the low priority interrupts.
code char at __CONFIG1H config1h = 0xFF & _OSC_HS_1H;
code char at __CONFIG2H config2h = 0xFF & _WDT_OFF_2H;
code char at __CONFIG2L config2l = 0xFF & _PUT_ON_2L & _BODEN_OFF_2L;
code char at __CONFIG4L config4l = 0xFF & _LVP_OFF_4L;
// WARNING: "noprotect" ignored!
code char at __CONFIG1H config1h = 0xFF & _OSC_HS_1H;
code char at __CONFIG2H config2h = 0xFF & _WDT_OFF_2H;
code char at __CONFIG2L config2l = 0xFF & _PUT_ON_2L & _BODEN_OFF_2L;
code char at __CONFIG4L config4l = 0xFF & _LVP_OFF_4L;
// WARNING: "noprotect" ignored!
Nothing to do here.

Back to the CCSC2SDCC migration guide.