C/C++ programming

From KolibriOS wiki
Revision as of 14:11, 21 March 2013 by Asiekierka (talk | contribs) (Adding more)
Jump to navigation Jump to search

This section describes high-level language (i.e. non-Assembly) programming for Kolibri.

Warning: The pages below this line require rewriting - copied from Diamond's pages

C/C++

Visual C++ 6/.NET/2005

  • License: command-line version (Visual C++ Toolkit, compiler/linker, standard include files and RTL libraries) is free (http://microsoft.com, search on site), full version (IDE, RTL sources) is commercial,
  • Available Kolibri libraries: LZMA compression library lzmapack.lib; any code, which does not use OS calls and is compiled to object files understandable by the Microsoft linker, can be used (in particular, the many C libraries). Unfortunately, this does not apply to standard RTL (Run-Time Library), so all missing functions must be implemented by hand. However, some of them are already written.
  • Examples: ac97snd, fara, xonix (the sources are included in the sources of the distributive in folders programs\Serge\ac97snd, programs\Diamond\fara, programs\Diamond\xonix correspondingly)
  • Generated code format: 32-bit PE and (for later versions) 64-bit PE64 code depending on used format
  • Environment/platform: Windows, command line or IDE

Visual C++ is one from the best optimizing C++ compilers. Command-line compiler is distributed by Microsoft for free, for IDE one must pay (at least for licensed version :-) ), so both variants are examined here. The version VC6, though written enough time ago, is still popular, the versions VS.NET and VS2005 like to brake.

VC package (more strictly, the linker link.exe) generates only PE-files, so 32-bitness is not a problem, but creation of binary files is tricky.

Under work in IDE let us at first create project: (for VC6) File->New->Projects->Win32 Application, Project name: hello, (for VS) File->New->Project->Visual C++,General->Empty Project, Name: hello, for VC6 the wizard will appear, say to it "An empty project", accept seriousness of our intensions by pressing OK in the last dialog window and we will get fully according to our wishes the empty project with two configurations. It is recommended to delete the configuration Debug right away (for VC6 Build->Configurations->button Remove, for VS Build->Configuration Manager->(in listbox)Edit->button Remove), as the VC debugger is certainly useless in this context. Now let us add to the project (for VC6 Project->Add to Project->Files, for VS Project->Add Existing Item) include files kosSyst.h, KosFile.h, mcsmemm.h and source files kosSyst.cpp, KosFile.cpp, mcsmemm.cpp (are included to examples attached to the article - slightly modified variant from distributive sources to allow compilation in VC6). Apropos, the appearing dialog supports multiple choice (with holded Ctrl). Next, create main file hello.cpp (or main.cpp, or any other name) (for VC6 File->New->Files->C++ Source File, File name: hello, for VS File->New->File->Visual C++,C++ File, then File->Save source1.cpp As, then File->Move hello.cpp into->hello) and next write the code. It is recommended to learn kosSyst.h, this file contains prototypes of system calls wrappers.

#include "kosSyst.h"
#include "kosFile.h"

const char header[] = "HelloWorld test";
const char string[] = "Hello, World!";

void draw_window(void)
{
	// start redraw
	kos_WindowRedrawStatus(1);
	// define&draw window
	kos_DefineAndDrawWindow(10,40,150,50,
		0x33,0xFFFFFF,0,0,(Dword)header);
	// display string
	kos_WriteTextToWindow(30,10,8,0,(char*)string,0);
	// end redraw
	kos_WindowRedrawStatus(2);
}

void kos_Main()
{
	draw_window();
	for (;;)
	{
		switch (kos_WaitForEvent())
		{
		case 1:
			draw_window();
			break;
		case 2:
			// key pressed, read it and ignore
			Byte keyCode;
			kos_GetKey(keyCode);
			break;
		case 3:
			// button pressed; we have only one button, close
			kos_ExitApp();
		}
	}
}

Now let us tune compilation. We can not use RTL-library, because it will force link to Windows-libraries, so for VC6 on the tab "Project->Settings->Link" in "Category: Input" clear editbox "Object/library modules" and set checkbox "Ignore all default libraries". Entry point is the function crtStartUp, so in "Category: Output" set "Entry-point symbol:" in "crtStartUp". Moreover, in "Project Options" it is recommended to add option "/align:16" (this is not necessary, but greatly decreases binary size). For VS the corresponding dialog is called by "Project->hello Properties" and has treeview instead of tabs, similar actions are executed as follows: Configuration Properties->Linker->Input-> Ignore All Default Libraries: Yes, Linker->Advanced->Entry Point: crtStartUp, Linker->Command Line->Additional options: /align:16. Moreover, VS requires obvious subsystem guideline: Linker->System->SubSystem (select any, it has no influence) and disable at compilation buffer overflow checks and RTTI (they use RTL): C/C++ ->Code Generation->Buffer Security Check: No, C/C++ ->Language->Enable Run-Time Type Info: No. Also manifest inserted by VS has no value for us, so Linker->Manifest File->Generate Manifest: No. Now the compiler can already generate code, but it will be created in PE format. The main idea is to pass generated PE-file through the program pe2kos.exe, which will change its format to used in Kolibri. pe2kos.exe is included with the sources to the distributive sources (the folder "develop\pe2kos"), and also without the sources to examples attached to the article. (There exists also another way, it is shown in MASM chapter, description of linking.) Kolibri-binaries are loaded at zero address, Kolibri-header will be created in the file beginning instead of PE-header, so base address must be set (at the same tab - "Output" for VC6, "Linker->Advanced" for VS - field "Base address") to 0, VS also requires "Fixed Base Address" set to "Image must be loaded at a fixed address (/FIXED)" (VC6 by default does not generate fixups itself).

Hll vc1.gif

Hll vc2.gif

The remaining part is to setup call to pe2kos. For VC6: Project->Settings->Custom Build, for VS: Project->hello Properties->Custom Build Step. In the editbox Commands/Command Line write

pe2kos Release\hello.exe hello

(pe2kos is assumed to be placed either in one from PATH-folders or in project folder), in the editbox Outputs write binary name - hello, it will be generated in the project folder. The build process is now as usually - either F7, or Build->Build hello.exe(VC)/Build->Build Solution(VS), or corresponding button on toolbar.

Now, let us work with the command line. At first, set required environment variables. When VC Toolkit, VC6 or VS are installing, they create in the corresponding partition of main menu the item "... Command Prompt", which calls console, sets up used environment and waits for user actions. One can also independently run console and execute the file vcvars32.bat. Next, go to work folder (drive is changed by the command "X:", folder on drive - by the command "cd \folder1\folder2"). Following assumes that the folder already contains kosFile.cpp,kosSyst.cpp,mcsmemm.cpp,kosFile.h,kosSyst.h,mcsmemm.h and created hello.cpp. Required compilation options are the same as in IDE, but now they are selected not through GUI, but in command line.

Compilation before VS2005:

cl /c /O2 /nologo hello.cpp kosFile.cpp kosSyst.cpp mcsmemm.cpp
link /nologo /entry:crtStartUp /subsystem:native /base:0 /fixed
	/align:16 /nodefaultlib hello.obj kosFile.obj kosSyst.obj mcsmemm.obj
pe2kos hello.exe hello

Hll vc3.gif

VS2005 adds new options:

cl /c /O2 /nologo /GS- /GR- hello.cpp kosFile.cpp kosSyst.cpp mcsmemm.cpp
link /nologo /manifest:no /entry:crtStartUp /subsystem:native /base:0 /fixed
	/align:16 /nodefaultlib hello.obj kosFile.obj kosSyst.obj mcsmemm.obj
pe2kos hello.exe hello

Hll vc4.gif

GCC/G++

  • License: free, open-source
  • Available Kolibri libraries: ported RTL (Run-Time Library, standard C-library), SDL (Simple DirectMedia Layer, it is base for many programs); any code, which does not use OS calls and is compiled to object files understandable by GNU linker, can be used (in particular, many C libraries).
  • Examples: dosbox, sdlfire, sdlquake, pig
  • Generated code format: 32-bit, probably, 16-bit
  • Environment/platform: MinGW - command line in Windows (http://www.mingw.org); GCC/G++ are standard compilers included in all Linux and cygwin packages (http://www.cygwin.com)

GCC/G++ are one from the best optimizing C/C++ compilers. They do not support binary files as special format, but linker understands special scripts, which can give to him enough information.

Developing requires in addition to MinGW/cygwin/linux (any variant can be used) the library menuetlibc, available at http://diamond.kolibrios.org/menuetlibc.7z. Download it, allocate some folder for it, unpack the archive there and create environment variable MENUETDEV with the value "full path to the selected folder". (Under cygwin/linux when using standard bash shell environment variables are set by the command of the form "export MENUETDEV=/home/username/menuetlibc", which is reasonable to be placed in .bash_profile to avoid necessity of manually enter each time when loading. Under Win9x the command of the form "SET MENUETDEV=c:\kolibri\menuetlibc" should be placed in autoexec.bat and then one should reboot computer. Under WinNT/2k/XP setting of environment variables is done through GUI: Control Panel->System->Advanced ->Environment variables.) After those settings give the command "make" from selected folder. And wait, because compilation of libraries from the beginning is long enough. If all will succeed, the subfolder "lib" will contain 6 libraries, and "programs\binclock" - test Kolibri-program mbinclk.

And now let us write "helloworld" program. Here prototypes of system calls wrappers are placed in $(MENUETDEV)/include/menuet/os.h. The code (hello.c):

#include <menuet/os.h>

const char header[] = "HelloWorld test";
const char string[] = "Hello, World!";

void draw_window(void)
{
	// start redraw
	__menuet__window_redraw(1);
	// define&draw window
	__menuet__define_window(10,40,150,50,
		0x33FFFFFF,0,(__u32)header);
        // display string
        __menuet__write_text(30,10,0x80000000,string,0);
        // end redraw
        __menuet__window_redraw(2);
}

void app_main(void)
{
	draw_window();
	for (;;)
	{
		switch (__menuet__wait_for_event())
		{
		case 1:
			draw_window();
			break;
		case 2:
			// key pressed, read it and ignore
			__menuet__getkey();
			break;
		case 3:
			// button pressed; we have only one button, close
			return;
		}
	}
}

The compilation is carried out, as anywhere in the GNU world, by the command

make

which requires Makefile with the following contents:

OUTFILE = hello
OBJS = hello.o
include $(MENUETDEV)/makefiles/Makefile_for_program

Hll gcc1.gif

Some explanation for actions that happened offscreen.

  • Before compilation of everything the tools mgcc,mgpp,mld,mmkdep in the folder "linuxtools" are compiled. They simply call accordingly standard tools gcc,g++,ld,mkdep, adding certain command-line options. The full list of adding options can be figured out from the sources, and essential ones for compilation process are the following:
-nostdinc -I$(MENUETDEV)/include
for gcc/g++ and
-nostdlib -L$(MENUETDEV)/lib -T$(MENUETDEV)/include/scripts/menuetos_app_v01.ld
	$(MENUETDEV)/stub/crt0.o
for mld. Later instead of standard tools those compiled tools are used.
  • The compiler only generates the code. Assembling this code to unified binary is the task of linker. The wish to create binary file is expressed in the script "menuetos_app_v01.ld" (argument of option -T). In fact, this script directs to create certain sections in certain order by grouping existing sections. At that the first section is placed at address 0, next sections in the sequential order. Moreover, the script finds out begin/end of code/data/program, they will be used later.
  • The resulting file from linker is passed through tool objcopy, which changes it to binary.
  • The stub "stub/crt0.o" plays an important role. It is the file where header with main program parameters is created. Some parameters are declared as extrn, they will be filled by linker with values found by script.

Borland C++

  • License: command-line tools are free ( www.borland.com/bcppbuilder/freecompiler or seatch "Command-Line tools" on site), IDE is commercial.
  • Available Kolibri libraries: base library, necessary for work (includes multithreading, system calls wrappers, heap allocation/free, files handling, but does not contain RTL).
  • Examples: checkers, life2 (the sources are included to the distributive sources - folders programs\Diamond\checkers и ...\life2)
  • Generated code format: 32-bit PE
  • Environment/platform: command line in Windows, IDE for Windows

The compiler can not generate binary files. Here the interesting approach is used: since Kolibri-binaries can not be created with the compiler, let us do not use compiler! We will use FASM, it can generate anything needed. But in which connection C++ stays here? The answer is: we will write on C++, but compile in the assembler text! "Minor" problems with inconsistency between TASM-syntax of output files from Borland C++ and FASM-syntax are solved with not complicated program t2fasm.exe, which is included with the sources to the distributive sources (folder "develop"), and also (without sources) to examples attached to the article.

For compilation the library of base functions is required, it can be found in mentioned sources of checkers and life2, and also in the examples for the article.

The code (hello.cpp):

#include <menuet.h>
#include <me_heap.h>
#include <me_file.h>

using namespace Menuet;

const char header[] = "HelloWorld test";
const char string[] = "Hello, World!";

bool MenuetOnStart(TStartData &me_start, TThreadData /*th*/)
{
	me_start.Left = 10;
	me_start.Top = 40;
	me_start.Width = 150;
	me_start.Height = 30;
	me_start.WinData.Title = header;
	return true;
}

void MenuetOnDraw(void)
{
	DrawString(30,10,0,string);
}

bool MenuetOnClose(TThreadData /*th*/)
{return true;}
int MenuetOnIdle(TThreadData /*th*/)
{return -1;}
void MenuetOnSize(int /*window_rect*/[], TThreadData /*th*/)
{}
void MenuetOnKeyPress(TThreadData /*th*/)
{GetKey();}
void MenuetOnMouse(TThreadData /*th*/)
{}

The compilation requires FASM with version not above 1.64. On condition that you have got such version:

bcc32 -S -v- -R- -6 -a4 -O2 -Og -Oi -Ov -OS -k- -D__MENUET__ -Iinclude hello.cpp
echo include "me_make.inc" > f_hello.asm
t2fasm < hello.asm >> f_hello.asm
fasm f_hello.asm hello

Hll bc1.gif

Tiny C Compiler

  • License: free, OpenSource
  • Available Kolibri libraries: big part of RTL (Run-Time Library, standard C library)
  • Examples: spektr
  • Generated code format: 32-bit COFF, ELF, PE, Kolibri
  • Environment/platform: command line in Windows

The compiler TCC was elaborated for generation of Kolibri-binaries. Also some part of C RTL on the base of Kolibri functions has been written. The sources of the compiler and RTL are available at Kolibri svn-server: svn://kolibrios.org/programs/develop/metcc/trunk.

For a start, one should compile the compiler :) For this one needs anything of MinGW/cygwin/linux, where the compiler GCC is present. In the presence of GCC one should say in the folder "source"

gcc tcc.c -o tcc.exe

The library is also compiled with GCC. Under Windows it is enough to run build.bat, for cygwin/linux there is Makefile. In the result GCC must create files melibc.a and start\start.o.

Now let us copy tcc.exe, melibc.a and start.o to the work folder. Let us also copy to the same folder files from the folder "include".

The code (hello.c):

#include "mesys.h"

const char header[] = "HelloWorld test";
const char string[] = "Hello, World!";

void draw_window(void)
{
	// start redraw
	_msys_window_redraw(1);
	// define&draw window
	_msys_draw_window(10,40,150,50,0xFFFFFF,0x33,0,0,(int)header);
	// display string
	_msys_write_text(30,10,0x80000000,string,0);
	// end redraw
	_msys_window_redraw(2);
}

int main(int argc, char** argv[])
{
	draw_window();
	for (;;)
	{
		switch (_msys_wait_for_event_infinite())
		{
		case 1:
			draw_window();
			break;
		case 2:
			// key pressed, read it and ignore
			_msys_get_key();
			break;
		case 3:
			// button pressed; we have only one button, close
			return 0;
		}
	}
}

Compilation:

tcc hello.c start.o melibc.a -o hello

Hll tcc1.gif