Последняя редакция: 09.02.07

Как написать Hello world под разные ОСи?

1. Hello World для консоли Unix/Linux/FreeBSD, для консоли NT, для консоли CE.
Это пожалуй самый простой Hello World, за исключением случая с Windows CE, где консоли может не быть. Для POSIX-систем достаточно так:

#include <stdio.h>


main()
{
	printf( "Hello, World!\n" );
	return 0;
}

и все :)
Но Windows NT и все из её линейки (2000, XP, 2003, Vista) используют двухбайтовый уникод, поэтому правильнее писать так:

#include <tchar.h>
#include <stdio.h>


_tmain()
{
	printf( "Hello, World!\n" );
	return 0;
}

_tmain - макрос препроцессора, он развернётся в main, если не объявлен макрос _UNICODE или в wmain, если объявлен. Удобно использовать уникодный wmain, если вся консольная программа в уникоде.
Для Windows CE вообще нет такого понятия как консольное приложение, поэтому если программа написана на Си, то вызываемой при запуске функцией будет всё равно WinMain. Но если её запускать из Pocket CMD (консоль для Pocket PC), то printf будет выводить в консоль. В итоге код примерно такой (кстати, в Pocket PC всегда всё в уникоде):


#include <windows.h>

INT 
WINAPI 
WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPWSTR    lpCmdLine,
	int       nCmdShow)
{
	_tprintf( _T("Hello World\n") );	
	return 0;
}

2. Hello World в окошке NT, CE, X
(Про X пока пропустим, у кого есть пример кода - присылайте).
Самое простое для Windows - сделать окошко с Hello World в MessageBox. При вызове этой функции система создат модальное диалоговое окно и поместит в него указанный текст. Для NT и CE это выглядит примерно так:


#include <windows.h>

INT 
WINAPI 
WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	MessageBox( NULL, _T("Hello World!"), _T("Sample"), MB_ICONINFORMATION );
	return 0;
}

3. Hello World из драйвера режима ядре вместо Kernel Panic или BSOD. Hello World из сервиса Windows CE.
(Этот пункт тоже пока пропустим, потому как для Linux и FreeBSD кода нет, а для Windows надо его сначала проверить)

4. Hello World в Symbian
Wizard генерирует большой и громозкий код, но поверьте - он минимален для нормального функционирования программы для Symbian:


Ашник:
#ifndef __SYMBIAN1_H__
#define __SYMBIAN1_H__

// INCLUDES
// System includes, found under %EPOCROOT%\epoc32\include
#include <eikapp.h>             // CEikApplication
#include <eikdoc.h>             // CEikDocument
#include <coecntrl.h>           // CCoeControl
#include <uikon.hrh>            // Command codes
#include <eikenv.h>             // CEikonEnv
#include <eiklabel.h>           // CEikLabel
#include <aknappui.h>           // CAknAppUi
#include <aknnotewrappers.h>    // CAknInformationNote

// FORWARD DECLARATIONS
class CSymbian1;
class CSymbian1Container;

// CLASS DEFINITIONS

//----------------------------------------------------------------------------
// CSymbian1Application
//----------------------------------------------------------------------------
class CSymbian1Application : public CEikApplication
{
private:    // From CEikApplication
    CApaDocument* CreateDocumentL();

private:    // From CApaApplication (via CEikApplication)
    TUid AppDllUid() const;
};

//----------------------------------------------------------------------------
// CSymbian1Document (Model)
//----------------------------------------------------------------------------
class CSymbian1Document : public CEikDocument
{
public:     // Constructor
    CSymbian1Document(CEikApplication& aApp);

private:    // From CEikDocument
    CEikAppUi* CreateAppUiL();
};

//----------------------------------------------------------------------------
// CSymbian1AppUi (Controller)
//----------------------------------------------------------------------------
class CSymbian1AppUi : public CAknAppUi
{
public:     // Constructors and destructor
    void ConstructL(void);
    ~CSymbian1AppUi();

private:    // From CEikAppUi
    void HandleCommandL(TInt aCommand);

private:    // Data
    CSymbian1Container* iAppContainer;
};

//----------------------------------------------------------------------------
// CSymbian1Container (View)
//----------------------------------------------------------------------------
class CSymbian1Container : public CCoeControl, MCoeControlObserver
{
public:     // Constructors and destructor
    void ConstructL(const TRect& aRect);
    ~CSymbian1Container();

public:     // New functions

public:     // From CCoeControl
    void SizeChanged();
    TInt CountComponentControls() const;
    CCoeControl* ComponentControl(TInt aIndex) const;
    void Draw(const TRect& aRect) const;

public:     // From MCoeControlObserver
    void HandleControlEventL(CCoeControl* aControl,TCoeEvent aEventType);

private:    // Data
        CEikLabel* iLabel;          // example label
        CEikLabel* iToDoLabel;      // example label
};

#endif  // __SYMBIAN1_H__



сипипишник:
// INCLUDES
#include "Symbian1.h"

// CONSTANTS
const TUid KUidSymbian1App = { 0x0466e8ae }; // Application UID

// MEMBER FUNCTIONS

//----------------------------------------------------------------------------
// Application entry point
//----------------------------------------------------------------------------

// GLOBAL FUNCTIONS

// DLL entry point, needed for DLL initialization.
// Usually there is no need to initialize anything, so we just
// return that everything is ok
GLDEF_C TInt E32Dll(TDllReason /*aReason*/) {
    return KErrNone;
}

// EXPORTED FUNCTIONS

// Static function for instantiating a new application object.
// Called by the Symbian application framework.
EXPORT_C CApaApplication* NewApplication() {
    return new CSymbian1Application;
}

//----------------------------------------------------------------------------
// CSymbian1Application
//----------------------------------------------------------------------------

// Creates the application document (model)
// Called by the Symbian application framework when the application is started.
// Needed for all Symbian applications.
CApaDocument* CSymbian1Application::CreateDocumentL() {
    return new (ELeave) CSymbian1Document(*this);
}

// Specifies the application UID, needed for all Symbian applications.
TUid CSymbian1Application::AppDllUid() const {
    return KUidSymbian1App;
}

//----------------------------------------------------------------------------
// CSymbian1Document (The Model of the MVC)
//----------------------------------------------------------------------------

// Creates the application document (Symbian terminology for model).
// Needed for all Symbian applications, even if the application doesn't
// save or load files.
CSymbian1Document::CSymbian1Document(CEikApplication& aApp) : CEikDocument(aApp) {
}

// Creates the application UI class.
// Called by the application framework when the application needs to start
// handling events.
// Needed for all Symbian GUI applications.
CEikAppUi* CSymbian1Document::CreateAppUiL() {
    return new (ELeave) CSymbian1AppUi;
}

//----------------------------------------------------------------------------
// CSymbian1AppUi (The Controller of the MVC)
//----------------------------------------------------------------------------

// Second-phase construction function.
// Called by the Symbian application framework after calling
// the constructor from CSymbian1Document::CreateAppUiL().
// Needed for initialising the application views.
void CSymbian1AppUi::ConstructL(void) {
    // Load the application resources defined in the Symbian1.rss, e.g.
    // application menus, dialogs etc.
    BaseConstructL();

    iAppContainer = new (ELeave) CSymbian1Container;
    iAppContainer->SetMopParent( this );
    iAppContainer->ConstructL( ClientRect() );
    AddToStackL( iAppContainer );
}

// Releases resources used by the application UI.
// Called when shutting down the application.
CSymbian1AppUi::~CSymbian1AppUi() {
    if (iAppContainer)
    {
        RemoveFromStack( iAppContainer );
        delete iAppContainer;
    }
}

// Handles user commands such as those associated to the menu entries.
// Called by CEikAppUi::ProcessCommandL().
// Application commands are defined in the Symbian1.hrh file.
// Common commands are defined for instance in the uikon.hrh file, located
// under the system include directory (%EPOCROOT%\epoc32\include).
void CSymbian1AppUi::HandleCommandL(TInt aCommand) {
    switch (aCommand)
    {
    case EEikCmdFileOpen:
        {
        iEikonEnv->InfoMsg(_L("Open"));
        CAknInformationNote* note = new (ELeave) CAknInformationNote;
        note->ExecuteLD(_L("Hello world!"));
        break;
        }
    case EAknSoftkeyBack:
    case EEikCmdExit:
        {
        Exit();
        break;
        }
    default:
        break;
    }
}

//----------------------------------------------------------------------------
// CSymbian1Container (The View of the MVC)
//----------------------------------------------------------------------------

// Second-phase construction function.
// Initializes the container (creates a window, sets its bounds and activates
// it) and creates the child/nested controls (if any).
// Called from the AppUi after creating a new container object.
void CSymbian1Container::ConstructL(const TRect& aRect) {
    CreateWindowL();

    iLabel = new (ELeave) CEikLabel;
    iLabel->SetContainerWindowL( *this );
    iLabel->SetTextL( _L("Example View") );

    iToDoLabel = new (ELeave) CEikLabel;
    iToDoLabel->SetContainerWindowL( *this );
    iToDoLabel->SetTextL( _L("Add Your controls\n here") );

    SetRect(aRect);
    ActivateL();
}

// Destructor used for releasing the resources used by his container.
// Called from the AppUi's destructor.
CSymbian1Container::~CSymbian1Container() {
    delete iLabel;
    delete iToDoLabel;
}

// Lays out the child controls (if any).
// Called when the control is resized.
void CSymbian1Container::SizeChanged() {
    // TODO: Add here control resize code etc.
    iLabel->SetExtent( TPoint(10,10), iLabel->MinimumSize() );
    iToDoLabel->SetExtent( TPoint(10,100), iToDoLabel->MinimumSize() );
}

// Returns the number of child controls, 0 if none.
TInt CSymbian1Container::CountComponentControls() const {
    return 2; // return nbr of controls inside this container
}

// Returns the child control at the given index, NULL if none.
CCoeControl* CSymbian1Container::ComponentControl(TInt aIndex) const {
    switch ( aIndex )
    {
    case 0:
        return iLabel;
    case 1:
        return iToDoLabel;
    default:
        return NULL;
    }
}

// Draws to this controls canvas using graphics primitives.
// Note that the UI framework takes care of drawing the child controls.
void CSymbian1Container::Draw(const TRect& aRect) const {
    CWindowGc& gc = SystemGc();
    // TODO: Add your drawing code here
    // example code...
    gc.SetPenStyle( CGraphicsContext::ENullPen );
    gc.SetBrushColor( KRgbGray );
    gc.SetBrushStyle( CGraphicsContext::ESolidBrush );
    gc.DrawRect( aRect );
}

// Handles events generated by the child controls.
void CSymbian1Container::HandleControlEventL(CCoeControl* /*aControl*/,TCoeEvent /*aEventType*/) {
    // TODO: Add your control event handler code here
}
можно просто охренеть...

5. Hello World при старте PC когда процессор ещё не в защищенном режиме (по сути Hello world для 8086 до загрузки DOS).
Здесь я погонялся бы за размером кода, кто знает как можно вывести меньшим числом инструкций - присылайте (компилить fasm'ом, в результате получите образ загрузочной дискеты, которую можно загрузить в VM Ware):




org 0x7c00	; Команда указывает, что абсолютные адреса следует отсчитывать от
			; адреса 0x7C00. Если используются только "ближние" команды,
			; оперирующие со смещением, то эта команда не нужна

use16		; Генерировать 16-разрядный код. Процессор после включения - 
			; в 16-разрядном режиме реальных адресов

Startup:	; Это метка, она нужна для указания адреса первой команды.

	mov ax, cs	; Значение инициализированного сегментного регистра копируется в ax
	mov ds, ax	; а из ax - в ds и ss
	mov ss, ax  ; 


	mov sp, Startup		; Startup - метка, указывающая на первую команду. Стек
						; растет вверх, т.е. будет модифицировать байты ДО первой
						; команды
	mov bp, sp			; bp - служебный регистр, часто используется Си-компиляторами

	mov ax, 0B800h
	mov es, ax		; обращение к адресу 0xB8000 будем проводить через сегмент,
					; указатель на начало которого помещаем в es
	mov di, 0		; в di помещается адрес назначения

	mov si, OutputString
					; в si помещается адрес начало буфера со строкой. Кроме
					; того, требуется инициализация ds (установлен равным cs,
					; т.е. 07C0h
				          
	mov cx, word [OutputStringLength]
					; В cx помещается длина строки в байтах
					; Конструкция означает "взять значение по указателю на word 
					; (word - 16-битное слово)

	rep movsb		; Команда копирует байт из ds:si в es:di. Префикс rep указывает,
					; что требуется скопировать cx байтов


; Пустой цикл, чтобы не произошёл системный сброс
Endloop:
	hlt			; команда останавливает процессор до поступления прерывания
	jmp Endloop


OutputString:
	db 'H', 7, 'e', 7, 'l', 7, 'l', 7, 'o', 7, ' ', 7, 'W', 7, 'o', 7, 'r', 7, 'l', 7, 'd', 7, '!', 7

OutputStringLength:
	dw 12*2



; Выравнивание до конца блока в 512 (загрузочный сектор)
EndBlock: rb 510-(EndBlock-Startup)
db 055h, 0aah		;Сигнатура загрузочного сектора (требуется для некоторых BIOS)



; Дополнить полученный в результате компиляции образ до размера 2880*512 байт
FloppyEnd db 2880*512-(FloppyEnd-Startup) dup 0


6. Hello World когда процессор x86 уже в защищенном режиме (по сути Hello World Operating System).
Следующий код жёстко и грубо переведёт x86 в защищенный режим, и напечатает строчку "Hello World" из защищенного режима (компилить по прежнему fasm'ом, получите также образ загрузочной дискеты):

org 0x7c00	; Команда указывает, что абсолютные адреса следует отсчитывать от
			; адреса 0x7C00. Если используются только "ближние" команды,
			; оперирующие со смещением, то эта команда не нужна

use16		; Генерировать 16-разрядный код. Процессор после включения - 
			; в 16-разрядном режиме реальных адресов

Startup:	; Это метка, она нужна для указания адреса первой команды.

	mov ax, cs	; Значение инициализированного сегментного регистра копируется в ax
	mov ds, ax	; а из ax - во все другие сегментные регистры
	mov es, ax
	mov ss, ax

	mov sp, Startup		; Startup - метка, указывающая на первую команду. Стек
						; растет вверх, т.е. будет модифицировать байты ДО первой
						; команды
	mov bp, sp			; bp - служебный регистр, часто используется Си-компиляторами


	; Загрузка адреса в GDTR
	lgdt [GDTRegister]	

    ; открываем адресную линию A20
    in	 al, 92h
    or	 al, 2
    out  92h, al

    ; запрет всех прерываний
    cli
    in	 al, 70h
    or	 al, 80h
    out  70h, al  ; запрет немаскируемых прерываний (NMI)

	; переключение в защищенный режим	
	smsw ax				; Загрузка младшей части CR0
	or ax, 00000001b	; Установка бита PE (защищенный режим)
	lmsw ax				; Сохранение младшей части CR0
	             
	; дальний переход для очистки конвеера команд (регистры будут перезагружены
	; и код будет выполняться в защищенном режиме)                       
	jmp 00001000b:ProtectedModeStartup


use32		; Генерировать 32-разрядный код
align 8		; Выровнять таблицу GDT по 8-байтной границе

	;============
	; Таблица GDT
GDTTable:

; ФОРМАТ ДЕСКРИПТОРА GDT:
;
;                2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
; 31             4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7            0
; +---------------+-+-+-+-+-------+-+---+-+------+---------------+
; ¦               ¦ ¦ ¦ ¦A¦       ¦ ¦ D ¦ ¦      ¦               ¦
; ¦ BASE  31:24   ¦G¦D¦0¦V¦       ¦P¦ P ¦S¦ TYPE ¦   BASE 23:16  ¦
; ¦               ¦ ¦ ¦ ¦L¦       ¦ ¦ L ¦ ¦      ¦               ¦
; +---------------+-+-+-+-+-------+-+---+-+------+---------------+
; ¦         BASE 15:00            ¦    Граница сегмента 15:00    ¦
; +-------------------------------+------------------------------+
	; Нулевой дескриптор не используется
	dd 		0, 0

	; Первый дескриптор - код (0x0000000-0xFFFFFFFF)
	db  0FFh, 0FFh,     00h, 00h,    00h, 	      10011010b, 11001111b,   00h
	;   граница(15:00)  ;BASE(15:00) BASE(23:16)  PDDSTYPE   GD0Aграница  BASE(31:24)

	; Второй дескриптор - данные и стек
	db  0FFh, 0FFh,     00h, 00h,    00h,         10010010b, 11001111b,   00h
	;   граница(15:00)  ;BASE(15:00) BASE(23:16)  PDDSTYPE   GD0Aграница  BASE(31:24)

	; Третий дескриптор - видеоданные
	db  0FFh, 0FFh,     00h, 80h,    0Bh,         10010010b, 01000000b,   00h
	;   граница(15:00)  ;BASE(15:00) BASE(23:16)  PDDSTYPE   GD0Aграница  BASE(31:24)
	
	; S - системный сегмент (например, описывает LDT) (1=нет)
	; G - мерять границу в байтах или 4кб-блоках
	; D - размер операции (1=32бита, 0=16бит)
	; DD - уровень привилегий для доступа к сегементу
	; P - присутствие
	; TYPE - тип (только для несистемных): первый бит: 0 - данные,
	;												 1 - код
	;					Для данных: E (расш.вниз), W(запись), A(доступ произошёл)
	;					Для кода  : C (конформный),R(чтение), A(доступ произошёл)
DescriptorCode	equ		00001000b
;						00001=Number, 0=TI, 0=RPL

DescriptorData	equ		00010000b
;						00010=Number, 0=TI, 0=RPL

DescriptorVideo	equ		00011000b
;						00011=Number, 0=TI, 0=RPL

	

GDTRegister:
	dw	4*8-1
	dd	GDTTable



ProtectedModeStartup:
	mov ax, DescriptorData
	mov ds, ax	; В ds - данные
	mov ss, ax	; В ss - данные
	mov ax, DescriptorVideo
	mov es, ax	; В es - видеоданные

	mov esp, GDTTable	; Все что лежит выше GDTTable больше не нужно, там можно
						; разместить стек
	mov ebp, esp		

	
	;Печать строки
	mov edi, 0
	mov esi, OutputString
	mov ecx, 11*2
	rep movsb	; DS:[(E)SI] -> ES:[(E)DI]

	; В данный момент все прерывания запрещены, т.к. мы не определяли таблиу
	; обработчиков прерываний. Если их разрешить здесь, то процессор грохнется,
	; т.к. попытается вызвать неинициализированный код. Все, что можно
	; сделать сейчас, чтобы продемонстрировать работу кода - это зациклить
	; процессор

EndLoop:
	jmp EndLoop

OutputString:
	db 'H',7,'e',7,'l',7,'l',7,'o',7,' ',7,'W',7,'o',7,'r',7,'l',7,'d',7

EndBlock: rb 510-(EndBlock-Startup)
db 055h, 0aah


EndFloppy: rb 2880*512-(EndFloppy-Startup)-1
	db 00h



Варианты для AMD64, Intel Itanium, MIPS, ARM и прочих, производящихся сейчас. А также, если код исполняют несколько процессорных ядер.
(Сам пока не кодил, если у кого что есть подобного - присылайте, опубликую)




Ссылки:
Flat assembler (fasm) home page




Размещение статьи на других сайтах - только с разрешения автора
Запрещено использование исходного кода с этого сайта в коммерческом ПО с закрытым кодом
Вопросы по материалам статьи можно задать по почте: ntinside [at] yandex.ru

Copyright (C) Fur, 2007-2008
Хостинг от uCoz