(The CCSC2SDCC Migration Guide)

This chapter summarises and explains how to migrate code written for the CCS compiler to code for the SDCC. (16bit targets)


Comparison of the two compilers

Feature matrix

aspect CCS Compiler SDCC
cost > 0 0 (GPL)
linkable object files no yes
support company community
supported platforms Windows®, Linux Windows®, Linux, Un*x, Mac OS X
supported target hardware MicroChip PIC (12, 14, 16 bit) MicroChip PIC (14 and 16 bit), Intel 8051, Z80 ...
common data type sizes int (unsigned 8 bit), long (unsigned 16 bit), int32 (unsigned 32 bit) char (signed 8 bit), int (16 bit), long (32 bit)
built-in support for RS232, I2C, LCD yes no
stability (16bit PIC targets) stable/production in development
function pointers yes (very limited) yes
interrupt handling complex, built-in dispatcher basic support for 2 priority levels
documentation available in print and online rather incomplete pdf file, active mailing lists
call stack
stack no yes
reentrant functions no yes
recursion no yes
variable argument lists no yes
overhead for function calls nearly zero noticable
code quality
inline functions yes no (use #define :-)
code size very densed code (closed world assumption) rather large

Language differences

If existing code for the CCS compiler is to be compiled using the SDCC, the following language extensions have to be replaced:

Tools for your convenience


The helper package contains the following tools:

This Perl script replaces most pragmas, all binary literals and modifies some calls to built-in CCS functions that cannot be handled directly by own functions. (e.g. polymorphic calls) This script does some general work (configuration words, literals, ...), but keep in mind that most modifications rely on our compatibility files.

before after
#fuses hs,noprotect,nobrownout,nolvp,put,nowdt (no)protect will be ignored at the moment. Multiple #fuses will be 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;
#use fast_io(A) Has only effects on the CCS style io-functions. #undef set_mode_A
#define set_mode_A(input, pin) portmode_fast(A, input, pin)
#use fixed_io(a_outputs=PIN_A2, PIN_A3) Has only effects on the CCS style io-functions. #undef FIXEDMASK_TRISA
#undef set_mode_A
#define set_mode_A(input, pin) portmode_fixed(A, input, pin)
#use standard_io(A) Has only effects on the CCS style io-functions. #undef set_mode_A
#define set_mode_A(input, pin) portmode_standard(A, input, pin)
a = 0b10010110; a = 0x96;
#include <path\to\file.h> #include <path/to/file.h>
ASIS will not be handled separately - #asm is always handled that way.
#int_timer1 fast noclear
void foo()
Interrupt handler function foo has to appear in the line immediately below #int_X, with the whole signature in one line.
#define timer1_fast
#define timer1_noclear
#define timer1_handler foo
void foo()
#use delay(clock=20000000, restart_wdt) #define RESTART_WDT
#define CLOCK_SPEED 20000000
#include <delay.c>
delay_us(5) (only replaced for constant values 0-19, #defines will not be recognized) delay_us_short(5)
char a;
signed char b;
unsigned char c;
int d;
signed int e;
unsigned int f;
long g;
signed long h;
unsigned long i;
signed int8 j;
unsigned int8 k;
unsigned char a;
char b;
unsigned char c;
uint8_t d;
int8_t e;
uint8_t f;
uint16_t g;
int16_t h; // alternative: int
uint16_t i;
int8_t j;
uint8_t k;
#byte name = 0x30
uint8_t at 0x30 name; Keep in mind, you must not pin variables on existing special function registers 0xF60-0xFFF or the area 0x000-0x022 used for local variables (SDCC specific).
long x;
#locate x = 0x50
#locate has to appear in the line immediately below the variable declaration; only uintX_t and intX_t types supported at the moment.
uint16_t at 0x50 x;
#use i2c(master, scl=PIN_C3, sda=PIN_C4, FORCE_HW) I2C slave mode, WDT restart and speed selection are not supported at the moment. #undef I2C_SCL
#undef I2C_SDA
#undef I2C_SUFFIX
#define I2C_SCL PIN_C4
#define I2C_SDA PIN_C3
#define I2C_SUFFIX _PIN_C4_PIN_C3
#include <compat_i2c.h>
#use i2c(master, scl=PIN_E2, sda=PIN_D4) I2C slave mode, WDT restart and fast mode are not supported at the moment. #undef I2C_SCL
#undef I2C_SDA
#undef I2C_SUFFIX
#define I2C_SCL PIN_D4
#define I2C_SDA PIN_E2
#define I2C_SUFFIX _PIN_D4_PIN_E2
#include <software_i2c.c>
i2c_read() argument is optional when using CCSC, defaults to 1. i2c_read(1)
Other shortcomings of this Perl script


SDCC has some reserved words that CCS does not know. If the sources contain such reserved words, the lexer ren_ident replaces these identifiers. Example:

void foo(byte data);


void foo(byte __PIC_data);

In addition to this, ren_ident tries to find identifiers in inline assembler code and prefixes them with a "_", because SDCC cannot reference variable names, only symbols known to the linker. An identifier foo is not modified to _foo but to ASM(foo), where ASM(...) as a macro.


CCSC allows enum type definitions without the use of typedef. This non-standard C can be fixed with this lexer (in some cases).

This bash script runs the programs and script mentioned above, converting all .c and .h files in the current directory and its subdirectories. It creates the new directory SDCC and places the current subtree with the modified source files there. Other files are simply copied, also some of the compatibility files. (see below)

The script requires a working bash, the tools above and dos2unix. The source files are converted by these tools in the following order: dos2unix, ren_ident, and enum-o-matic.

How to use

Run the script in the root directory of your project you want to convert. After processing the files, check your modified sources with the help of the following section.

Required manual work

All in all, keep in mind: This port of the SDCC is still in development. So: handle with care. Fragile.

Compatibility files


This file provides most of the CCS-builtin function replacements. If you want to know which are already implemented, have a look at the source. Generally, any of the RS232 related functions is working, also EEPROM access, ADC, PWM, SPI or functions you would expect in string.h. But corresponding libraries are in development from the SDCC pic16 core team.

SDCC expects one stack pragma in each project. compat.h adds such a line providing 96 bytes of stack at position 0x380. If you want to set your own stack, modify the first line of the main file to #define USERDEF_STACK
#include <compat.h>

compat.h also includes the files compat_defs.h, stddef.h, stdint.h and compat_i2c.h.


This header file contains many definitions of constants and types for CCS compatibility.


Some standard data types are defined here.


This file provides the functions delay_us and delay_ms and so may only be included once. The code relys on some macros, so if you want to use the functions, include it the way does.


This source file contains our interrupt dispatche and also includes the requrired initprio.c. If you want to use our interrupt dispatcher, insert #include <disptab.c> at the end of your main file. If you want to pack the irq handlers into separate object files, you have to copy all #define macros created from the former #int pragma in front of #include <disptab.c> so the dispatch table is generated properly.

If you want to write your own dispatcher, change the first line of the main file to #define OWN_IRQ_HANDLERS
#include <compat.h>.


The init_irq_priorites() function provided in initprio.c sets the interrupt priotiy registers according to the #define X_fast macros, generated by from the #int directives of CCSC. It is included by disptab.c. If you compile everything at a glance, it is enough to include this file at the end of the main file. If you want to pack the irq handlers into separate object files, you have to copy the #define X_fast macros in front of #include <disptab.c> so the code is generated properly.

Call init_irq_priorities() once before enabling interrupts in your code.

compat_i2c.h, software_i2c.c, software_i2c.h

These files provide functions to access the I2C bus. compat_i2c.h is included by compat.h, the other files have to be included manually. The code relys on many preprocessor macros, include them the way does.


You can have a look at this example to get an impression of the conversion.

General hints