Updated September 14, 2002. Under construction.
Partially based on a document by Peter Cellik, May 1995.
I used Peter's document as a starting point, but ended up totally rewriting
it. There are several paragraphs remaining that can be traced back to Peter's
document, and due acknowledgement is given here.
I am using C-- to develop applications for the MenuetOS operating system,
so the main thrust of this page is 32-bit FLAT memory coding. However, the
compiler itself runs at the DOS commandline, and can produce output for DOS
16- or 32-bit or for Windows 32-bit apps or DLLs. The official Sphinx C-- project page has example code, libraries, etc: http://www.sheker.chat.ru/index_e.htm. My own C-- introductory page is: http://www.goosee.com/cmm/. My MenuetOS page is: http://www.goosee.com/menuetos/.
Note that Michael Sheker's C-- "readme" file has been translated to English,
and is included with the full package downloadable from his site. It is in
places difficult to read, due to the fact that it is a translation, and also
it is basically a historical record of changes introduced with each version.
It is useful as a reference in conjunction with this document. I am sifting
through it and have extracted the most important information to this page.
Michael has provided me with assistance. He has supplied some more translated
files, and I have incorporated most of them into this document,
by rewriting them in a format that seems to me like readable English. Due acknowledgement is also given here of that source.
This document is based on my very limited experience with C--, and input is welcome.
Please use Mozilla Composer if you would like to contribute to updating this page! (see www.goosee.com/best/).
You might like to add text in some other color, such as red, prior to me vetting it and making it black.
-- email any updates to me. Find my email address at: http://www.goosee.com/bkauler/.
Introduction
Identifiers and symbols
Constants
Data types
Expressions
Declaring functions and macros
Conditional statements
Arrays
Structures
Pointers
Other syntax
Internal macros
Inline assembly
Directives and commandline options
Appendices
How to install C--
Further documentation
Some examples of valid C-- indentifiers are:
_DOGSome examples of invalid C-- indentifiers are:
CoW
loony12
HowdYBoys_AND_Girls
WOW___
x
12bogus /* cannot start an identifier with a numerical digit */
y_es sir /* spaces not allowed */
the-end /* hyphens not allowed */
SYMBOL | FUNCTION | EXAMPLE
------------------------------------------------------------------------
/* | start comment block | /* comment */
*/ | end comment block | /* comment */
| |
// | comment to end of line | // comment
| |
= | assignment | AX = 12;
+ | addition | AX = BX + 12;
- | subtraction | house = dog - church;
* | multiplication | x = y * z;
/ | division | x1 = dog / legs;
& | bitwise AND | polution = stupid & pointless;
| | bitwise inclusive OR | yes = i | mabe;
^ | bitwise exclusive OR | snap = got ^ power;
<< | bit shift left | x = y << z;
>> | bit shift right | x = y >> z;
| |
+= | addition | fox += 12; // fox = fox +12;
-= | subtraction | cow -= BX; // cow = cow - BX;
*= | multiplication | cow *= dog; // cow = cow * dog;
/= | division | cow /= dog; // cow = cow / dog;
&= | bitwise AND | p &= q; // p = p & q;
|= | bitwise inclusive OR | p |= z; // p = p | z;
^= | bitwise exclusive OR | u ^= s; // u = u ^ s;
<<= | bit shift left | x <<= z; // x = x << z
>>= | bit shift right | x >>= z; // x = x >> z
| |
>< | swap | x >< y; /* exchange values of x and y */
| |
== | equal to | IF(AX == 12)
> | greater than | IF(junk > BOGUS)
< | less than | if( x < y )
>= | greater or equal to | if(AX >= 12)
<= | less than or equal to | IF(BL <= CH)
!= | not equal to | IF(girl != boy)
<> | different than | IF(cat <> dog) /* same function as != */
| |
@ | insert code | @ COLDBOOT(); /* insert COLDBOOT code */
: | dynamic procedure | : functionname () // declare functionname
$ | assembly operation | $ PUSH AX /* push AX onto stack */
# | offset address of | loc = #cow; /* loc = address of cow */
# | compiler directive | #define cow dog;
! | NOT operator | !x_var; if(!xflag)
... |any number of parameters| void proc(...);
:: |allowing of visibility | ::var=0;
| |
~ | | This symbol is currently unused.
BREAK CARRYFLAG CASE CONTINUE ELSEIf you use at compilation the option "/ia" on the command line (or "#pragma option ia" in the source code), which allows use of the assembly instructions without the "asm" or "$" keywords, all names of the assembly instructions become reserved words.
EXTRACT FALSE FOR FROM GOTO
IF LOOPNZ MINUSFLAG NOTCARRYFLAG NOTOVERFLOW
NOTZEROFLAG OVERFLOW PLUSFLAG RETURN SWITCH
TRUE WHILE ZEROFLAG
__CODEPTR__ __COMPILER__ __DATAPTR__ __DATESTR__ __DATE__
__DAY__ __FILE__ __HOUR__ __LINE__ __MINUTE__
__MONTH__ __POSTPTR__ __SECOND__ __TIME__ __VER1__
__VER2__ __WEEKDAY__ __YEAR__
_export asm break byte case
cdecl char continue default do
dword else enum extern far
fastcall float for goto if
inline int interrupt long loop
loopnz pascal return short signed
sizeof static stdcall struct switch
union unsigned void while word
ESCHAR ESBYTE ESINT ESWORD ESLONG
ESDWORD ESFLOAT
CSCHAR CSBYTE CSINT CSWORD CSLONG
CSDWORD CSFLOAT
SSCHAR SSBYTE SSINT SSWORD SSLONG
SSDWORD SSFLOAT
DSCHAR DSBYTE DSINT DSWORD DSLONG
DSDWORD DSFLOAT
FSCHAR FSBYTE FSINT FSWORD FSLONG
FSDWORD FSFLOAT
GSCHAR GSBYTE GSINT GSWORD GSLONG
GSDWORD GSFLOAT
AX CX DX BX SP BP SI DI
EAX ECX EDX EBX ESP EBP ESI EDI
AL CL DL BL AH CH DH BH
ES CS SS DS FS GS ST(0) ST(1) ST(2) ST(3) ST(4) ST(5) ST(6) ST(7)
ST
st(0) st(1) st(2) st(3) st(4) st(5) st(6) st(7)
st
ax cx dx bx sp bp si di
eax ecx edx ebx esp ebp esi edi
al cl dl bl ah ch dh bh
es cs ss ds fs gs
DR0 DR1 DR2 DR3 DR4 DR5 DR6 DR7
CR0 CR1 CR2 CR3 CR4 CR5 CR6 CR7
TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7
MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7
XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7
dr0 dr1 dr2 dr3 dr4 dr5 dr6 dr7
cr0 cr1 cr2 cr3 cr4 cr5 cr6 cr7
tr0 tr1 tr2 tr3 tr4 tr5 tr6 tr7
mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7
xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7
(E)AX = 0;The compiler will use at compilation either AX for 16-bit code, or the register EAX for 32-bit code.
__TLS__ there is a compilation under windows (w32, w32c, dll).These identifiers can be checked up by the directives "#ifdef" or "#ifndef". The identifier "cpu" can be used with operators as shown:
__DLL__ there is a compilation of a dll.
__CONSOLE__ there is a compilation of the console windows application.
__WIN32__ there is a compilation of a GUI application.
__FLAT__ the 32-bit code is compiled.
__MSDOS__ the 16-bit code is compiled.
__TINY__ the memory model tiny in 16-bit mode is used.
__SMALL__ the memory model small in 16-bit mode is used.
__DOS32__ the 32-bit code for DOS (d32) is compiled.
__COM__ the com-file is compiled.
__SYS__ the sys-file is compiled.
__ROM__ the rom-file is compiled.
__OBJ__ the obj-file is compiled
__TEXE__ the exe-file of a model tiny is compiled.
__EXE__ the exe-file of a model small is compiled.
codesize compilation is carried on with optimization on the size of the code.
speed compilation is carried on with optimization on speed of the code.
cpu defines the type of the processor for which the compilation is carried on:
0 - 8086
1 - 80186
2 - 80286
3 - 80386
4 - 80486
5 - Pentium
6 - Pentium MMX
7 - Pentium II
#ifdef cpu > 3 //if the type of the processor is more 80386
Expressing numerical constants in decimal (base 10) or hexadecimal (base 16) are the same as in C. To express a numerical constant in binary (base 2) notation, the sequence of 1's and 0's are preceded by 0b, with no spaces in between. To express a numerical constant in octal (base 8) notation, the sequence of octal digits (0 to 7) are preceded by 0o with no spaces.
Some examples:
0b11111111 // binary. same as 255 decimal.This notation may be used both in normal high-level expressions and in inline assembly instructions. Also, for those familiar with traditional assemblers such as MASM, hexadecimal numbers may also be written with a trailing "h" or "H", as for example:
0x00F // hexadecimal. same as 15 decimal.
0o10 // octal. same as 8 decimal.
000FhNote that it is possible to define a constant as being of a particular datatype, by means of the "L", "U" and "F" postfixes, however the compiler currently ignores this information. Upper or lowercase letters may be used. Some examples:
127AH
#define DEF1 1023L //"long" (signed 32-bit integer) value.
#define DEF2 2561Lu //"unsigned long" (unsigned 32-bit integer).
#define DEF3 3.02F //"float" (32-bit floating point).
Single character constants are, like in C, enclosed in single quotes ('). Also as in C, special characters are expressed by a back slash (\) followed by the key letter or letters. Special characters supported are:
'\a' /* beep (same as in C) */Any other character following a back slash is just accepted. This allows the single quote to be included by '\'', for '' is the NULL character. Note that NULL is the numeric value zero.
'\b' /* back space (same as C)*/
'\f' /* form feed (same as C)*/
'\l' /* line feed */
'\n' /* carriage return (in C called "newline")*/
'\r' /* carriage return (same as C)*/
'\t' /* horizontal tab */
'\x??' /* ASCII character formed from the ?? which would be two
hexadecimal digits for the character value */
'\???' /* ASCII character formed from the ??? which would be three
decimal digits for the character value */
'ab'There is no limit to the number of characters in a character constant, but only the last 4 characters are significant. This is the maximum that can be stored in a 32 bit variable. For example, 'this is large' would be equivalent to 'arge'.
'the'
'this is large'
The current maximum length of string constants is 2048 characters including the 0 terminator (NULL), thus a maximum of 2047 characters.
Some examples of constant expressions are:
45 & 1 + 3 // equals 4The above examples are integers, which is why "14-1/2" gives an answer of "6".
14 - 1 / 2 // equals 6
1 * 2 * 3 / 2 + 4 // equals 7
float y; //define 32-bit floating-point variable.This will load the 32-bit value "0x42A0C000" into "y", where this value is the floating point representation of the value "80.375".
y=80+0.375;
0.98"E" means "exponent", so the second-last example is actually 3.14*(10^2). The compiler will automatically recognise any of these as floating point numbers.
-15.75
3.14e2
1.234567E-20
NAME | SIZE | VALUE RANGE | VALUE RANGENote that the default is that the compiler aligns 16-bit variables on even addresses. In the example below, "vchar" is padded by an extra memory location so that "vshort" can be on an even address. 32-bit variables align on address multiples of 4, so in the example "vshort" is padded with an extra 16 bits as "vint" (32-bit) follows it. This results in less compact code.
|(bytes)| (decimal) | (hex)
----------------------------------------------------------------------------
byte | 1 | 0 to 255 | 0x00 to 0xFF
word | 2 | 0 to 65535 | 0x0000 to 0xFFFF
dword | 4 | 0 to 4294967295 | 0x00000000 to 0xFFFFFFFF
char | 1 | -128 to 127 | 0x80 to 0x7F
short | 2 | -32768 to 32767 | 0x8000 to 0x7FFF
long | 4 | -2147483648 to 2147483647 | 0x80000000 to 0x7FFFFFFF
float | 4 | +/-1.17E-38 to +/-3.37E38
Equivalents:
byte | unsigned char
int | short or long <<<CAREFUL HERE!
word | unsigned short
dword | unsigned long
cpuspeed.c-- 22: char vchar=0;
00000036 0000 db 0,0
cpuspeed.c-- 23: short vshort=0;
00000038 00000000 dw 0,0
cpuspeed.c-- 24: int vint=0;
0000003C 00000000 dd 0
cpuspeed.c-- 25: long vlong=0;
00000040 00000000 dd 0
cpuspeed.c-- 26: word vword=0;
00000044 0000 dw 0
variable-type identifier;Where 'variable-type' is any one of char, byte, int, word, long, dword or float. Several identifers may be declared of the same type as follows:
variable-type identifier1, identifier2, ... , identifierN;One dimensional arrays may be declared as follows:
variable-type identifier[elements];Where 'elements' is a constant expression for the amount of entries of that variable type to be in the array.
variable-type identifier [] = {const1, const2};These syntaxes are allowed for string arrays:
char msg1[]="this is a string";
char msg1="this is a string";
Some examples of global declarations:
byte i,j; /* declare i and j to be of type byte */
word ss[10]; /* declare ss to be an array of 10 word's */
short h,x[27]; /* declare h to be of type short and declare x to
be an array of 27 short's */
long zz = 0; /* the variable zz of a type long
is declared and it is assigned value 0. */
char smsg1="CPU speed:"; //compiles right here. Note, square brackets not needed.However, it is possible to force all global variables to compile in the place in which they are defined. This is done by:
dword cpuspeed; //compiles at end of file.
void draw_window(void)
{
sys_window_redraw(1);
sys_draw_window(100<<16+300,100<<16+200,0x001111CC,0x8099BBFF,0x00FFFFFF);
sys_write_text(8<<16+8,0xFFFFFF,"C-- CPU speed example for MenuetOS",34);
sys_draw_button(281<<16+12,5<<16+12,1,0x5599CC);
sys_write_number(4<<16,cpuspeed,25<<16+40,0xFFFF80);
sys_window_redraw(2);
}
//the function "sys_write_text()" passes address of a string. The string is
//compiled immediately after the function.
dword test1=0; //compiles right here.
dword test2; //compiles at end of file.
void main(void)
{
dword test3=0; //temporary, created on stack.
cpuspeed=sys_service(5,0)/1000000; //get CPU speed.
draw_window();
while(1)
{
switch(sys_wait_event())
{
case 1:
draw_window();
continue;
case 2:
/*sys_get_key();*/
continue;
case 3:
if(sys_get_button_id()==1) sys_exit_process();
continue;
}
}
}
# initallvar TRUEOr the "/iv" option on the command line.
static dword zz;"static" can also be used and for global objects (variables, structures, procedures). Such objects will be visible only in that file in which they are declared, allowing use of their names in other files for other purposes.
static long xx=0;
: dword zz; //the colon prefix makes it dynamic.Of what use are these? ... possibly in header files (include files) or library files. You may also have dynamic procedures (see below).
: struct RECT xx;
Constant expressions are introduced above. Now we look at expressions involving constants, variables, and registers.
In particular, there is the most important question about the mixing of explicit
register names within a C-- expression -- isn't this risky?
EAX=y+100<<7;What is particularly interesting about this, is assigning the result of the first statement to EAX means that a temporary variable doesn't have to be defined.
EBX=EAX<<2;
offset1=EAX+EBX+x;
long y,x=5;The mathematical statement compiles to:
void main(void) {
long time=1;
y=100+90*cos(time*1.5+2.0)/x;
cpuspeed.c-- 52: y=100+90*cos(time*1.5+2.0)/x;Notice something very important -- only the EAX register is used (altered).
00000131 DB45FC fild [ebp-4]
00000134 D80DB8010000 fmul [1B8h]
0000013A D805BC010000 fadd [1BCh]
00000140 D9FF fcos
00000142 50 push eax
00000143 DB1C24 fistp [esp]
00000146 58 pop eax
00000147 69C0BE000000 imul eax,eax,0BEh
0000014D 99 cwd
0000014E F73D04010000 idiv dword ptr [104h]
00000154 A3D0010000 mov [1D0h],eax
cpuspeed.c-- 52: y=100+90*EAX*cos(time*1.5+2.0)/x;Wow, this compiler is clever! It is smart enough to avoid a clash, even though I deliberately placed the "EAX" in a position in the statement that could have upset the computation. But no, the very first line performs "190*EAX", then pushes it on the stack, and gets it back with the "pop edx".
00000131 69C0BE000000 imul eax,eax,0BEh
00000137 50 push eax
00000138 DB45FC fild [ebp-4]
0000013B D80DBC010000 fmul [1BCh]
00000141 D805C0010000 fadd [1C0h]
00000147 D9FF fcos
00000149 50 push eax
0000014A DB1C24 fistp [esp]
0000014D 58 pop eax
0000014E 5A pop edx
0000014F F7EA imul edx
00000151 99 cwd
00000152 F73D04010000 idiv dword ptr [104h]
00000158 A3D4010000 mov [1D4h],eax
cpuspeed.c-- 52: y=100+90*EAX*cos(time*1.5+2.0)*EDX/x;Bingo! I broke it!
00000131 69C0BE000000 imul eax,eax,0BEh
00000137 50 push eax
00000138 DB45FC fild [ebp-4]
0000013B D80DBC010000 fmul [1BCh]
00000141 D805C0010000 fadd [1C0h]
00000147 D9FF fcos
00000149 50 push eax
0000014A DB1C24 fistp [esp]
0000014D 58 pop eax
0000014E 5A pop edx
0000014F F7EA imul edx
00000151 F7EA imul edx
00000153 99 cwd
00000154 F73D04010000 idiv dword ptr [104h]
0000015A A3D4010000 mov [1D4h],eax
cpuspeed.c-- 52: loop(count) y=100+90*cos(time*1.5+2.0)/x;However, I found that if I use ECX explicitly:
00000138 DB45F8 fild [ebp-8]
0000013B D80DC4010000 fmul [1C4h]
00000141 D805C8010000 fadd [1C8h]
00000147 D9FF fcos
00000149 50 push eax
0000014A DB1C24 fistp [esp]
0000014D 58 pop eax
0000014E 69C0BE000000 imul eax,eax,0BEh
00000154 99 cwd
00000155 F73D04010000 idiv dword ptr [104h]
0000015B A3DC010000 mov [1DCh],eax
00000160 FF4DFC dec dword ptr [ebp-4]
00000163 75D3 jne 138h
loop(ECX) y=100+90*cos(time*1.5+2.0)/x;then the compiler does use the assembly language "loop" instruction. So, the loop will exit with ECX=0.
cpuspeed.c-- 55: EBX=*z+EAX<<2/x;And another example:
00000178 8B1D18020000 mov ebx,[218h]
0000017E 8B1B mov ebx,[ebx]
00000180 01C3 add ebx,eax
00000182 B102 mov cl,2
00000184 D3E3 shl ebx,cl
00000186 93 xchg ebx,eax
00000187 31D2 xor edx,edx
00000189 F73504010000 div dword ptr [104h]
0000018F 93 xchg ebx,eax
cpuspeed.c-- 53: *z=100+90*cos(time*1.5+2.0)/x;Yes, it uses EBX. So, can we throw a spanner in the works?
00000138 DB45F8 fild [ebp-8]
0000013B D80DC0010000 fmul [1C0h]
00000141 D805C4010000 fadd [1C4h]
00000147 D9FF fcos
00000149 50 push eax
0000014A DB1C24 fistp [esp]
0000014D 58 pop eax
0000014E 69C0BE000000 imul eax,eax,0BEh
00000154 99 cwd
00000155 F73D04010000 idiv dword ptr [104h]
0000015B 8B1DDC010000 mov ebx,[1DCh]
00000161 8903 mov [ebx],eax
cpuspeed.c-- 55: EBX=*z+EBX+EAX<<2/x; //stuffs up the value in EBX!Can we get the compiler to use ESI or EDI? It may have to do so to handle multidimensional arrays or multiple pointers in the same statement. For example, let's try two pointers, "z" and "zz":
cpuspeed.c-- 57: EBX=*z+*zz;Push this further, with three pointers:
00000138 8B1DB8010000 mov ebx,[1B8h]
0000013E 8B1B mov ebx,[ebx]
00000140 8B35BC010000 mov esi,[1BCh]
00000146 031E add ebx,[esi]
cpuspeed.c-- 58: EBX=*z+*zz+*zzz;So far I can't get the compiler to use EDI. Also, it tries to avoid using ESI -- it only does so above because I have put EBX in the statement explicitly. If I have this:
00000138 8B1DC0010000 mov ebx,[1C0h]
0000013E 8B1B mov ebx,[ebx]
00000140 8B35C4010000 mov esi,[1C4h]
00000146 031E add ebx,[esi]
00000148 8B35C8010000 mov esi,[1C8h]
0000014E 031E add ebx,[esi]
cpuspeed.c-- 58: y=*z+*zz+*zzz;...you see, only using EAX and EBX.
00000138 8B05C8010000 mov eax,[1C8h]
0000013E 8B00 mov eax,[eax]
00000140 8B1DCC010000 mov ebx,[1CCh]
00000146 0303 add eax,[ebx]
00000148 8B1DD0010000 mov ebx,[1D0h]
0000014E 0303 add eax,[ebx]
00000150 A3C4010000 mov [1C4h],eax
struct test {short a;char b[8];long c;} rr[5];The "><" operator exchanges the contents of each parameter. Here we have ESI, EDI, EAX, and EDX used.
dword i,k;
cpuspeed.c-- 71: rr[i].b[k] >< rr[i+1].b[k+2];
00000127 8B352C020000 mov esi,[22Ch]
0000012D 8B3D28020000 mov edi,[228h]
00000133 6BFF0E imul edi,edi,0Eh
00000136 8A843EE4010000 mov al,[esi+edi+1E4h]
0000013D 8B3D2C020000 mov edi,[22Ch]
00000143 83C702 add edi,2
00000146 8B1528020000 mov edx,[228h]
0000014C 42 inc edx
0000014D 6BD20E imul edx,edx,0Eh
00000150 868417E4010000 xchg [edi+edx+1E4h],al
00000157 88843EE4010000 mov [esi+edi+1E4h],al
Therefore, it would seem safe to explicitly use EAX and ECX, though you
can expect their values to be destroyed by the end of execution of the expression. For the vast majority of expressions ESI and especially EDI won't be used, which means that you can explicitly use them and they will come out of the expression unchanged. Do not explicitly use EBX and EDX as a clash is highly likely. |
EAX=y+100<<7; //do NOT insert another statement between...There is no problem here because a value assigned to a register is read immediately in the next statement. Obviously, inserting a statement between the second and last line may cause a problem. However, this fragment of code, that I got out of an existing program, is fraught with danger.
EBX=EAX<<2; //do NOT always expect EAX to be preserved!
offset1=EAX+EBX+x; //AVOID using EBX in an expression!
#warning TRUETo redirect these warnings from output on the screen to a file, it is necessary to use an option on the command line:
wf=file_name
Conditional expressions are expressions which are used for generating a 'yes' or 'no' for 'if' statements and 'do {} while' loops.
There are two types of conditional expressions, simple and complex.
Simple conditional expressions are a single token or expression that will be taken as a 'yes' if the calculated value is non-zero, or a 'no' if the calculated value is zero.
Complex conditional expressions are of the following form:
( leftside compare_op rightside )Where:
"leftside" is any AL/AX/EAX or constant expression. The expressionSome examples of valid complex conditional expressions:
type will be determined by the first token (register or
variable): default is "word" for 16-bit code, "dword" for
32-bit code. If an other type is desired, the keyword
"byte", "char", "int", "word", "long", "dword" or "float"
can preceed the expression to specify its type.
"compare_op" is any one of "==", "!=", "<>", "<", ">", "<=", or ">=".
"rightside" is any single register, variable or constant expression.
( x+y > z )Some examples of invalid complex conditional expressions:
(int CX*DX <= 12*3 )
(byte first*second+hold == cnumber )
( x+y >= x-y ) //rightside is not a single token or constant expression.
( z = y ) //"==", not "=" must be used.
float f = 1.0;The allowed modifiers are 'signed', 'unsigned' and 'float'.
void PROC ()
{
IF (f < signed ECX) // in the register ECX is sign number
IF (unsigned EBX > f) // in the register EBX is unsigned number
IF (f == float EAX) // in EAX is number of a format float. WARNING: EAX destroyed.
}
cpuspeed.c-- 59: i=a+j;What you can see here is that the values on the right side are converted to short datatype before the calculation is performed, then the result assigned to variable "i".
0000011F 50 push eax //convert "a" to integer.
00000120 D905C0010000 fld [1C0h] // /
00000126 DB1C24 fistp [esp] // /
00000129 58 pop eax // /
0000012A 660305BC010000 add ax,[1BCh] //evaluation done in short.
00000131 66A3B8010000 mov [1B8h],ax
z = a+j;WARNING:
cpuspeed.c-- 59: i= float a+j;In this example, the parameters on the rightside are converted to float format prior to calculation. Note that the generated asm code achieves this by loading both "a" and "j" into the FPU which does all internal calculations in floating point format.
0000011F D905BC010000 fld [1BCh]
00000125 DA05B8010000 fiadd [1B8h]
0000012B DF1DB4010000 fistpw [1B4h]
00000131 9B fwait
EAX = a*j; //a is float, j is long.Variables "a" and "j" will both be converted to long values prior to calculation, but as they are being treated as unsigned, an unsigned multiply instruction "mul" will be generated by the compiler.
EAX = long a*j; //a is float, j is long.This will cause the compiler to treat the computation on the rightside as signed, so an "imul" instruction will be generated. HOWEVER, as per the warning above, no actual sign-conversion is done, so the result of the calculation on the rightside is simply assigned as-is to the leftside.
var1 = 0;That can be written down in a short form:
var2 = 0;
var3 = 0;
var1 = var2 = var3 = 0;This format, as well as being brief as source code, will also generate more compact asm code.
Parameters for stack procedures, if any, may be of any type (specified by 'byte', 'char', 'word', 'int', 'dword' or 'long'). Parameters are passed using a Pascal-like calling convention, that is, the first parameter is pushed first and the second parameter is pushed second, and so on. The Pascal-calling convention does not support variable number of parameters, so you have to be sure to pass the proper number of parameters to a stack procedure.
The following example stack procedure returns the sum as a 'word' of all its parameters, which are of different types:
word add_them_all (short a,b; long c,d)This is what the stack looks like at entry to the function "add_them_all()":
word x;
dword y;
{
return( a+b+c+d );
}
EBP - 6 Local variable "y".In the case of 32-bit code, the compiler passes all function parameters as 32-bit quantities, regardless of datatype. The reason that EBP is used here, is because the compiler places this asm code at the beginning of procedures:
EBP - 2 Local variable "x".
EBP + 0 Saved EBP.
EBP + 4 Return address.
EBP + 8 Rightmost passed parameter, "d".
EBP + 12 Parameter "c".
EBP + 16 Parameter "b".
EBP + 20 Parameter "a".
push ebp //save EBP.Then, at the end of the procedure, the compiler places:
mov ebp,esp //use EBP to access stack frame.
sub esp,value //value=number of bytes allocated for local variables.
mov ebp,esp //restore EBP.For the Pascal calling-convention, the local variables are removed by the operand of the "ret" instruction, which in this example removes 12 bytes from the stack prior to a return.
pop esp //restore ESP.
ret 12 //dump saved-EBP, local "x", local "y".
void afunc(word a, b, c; dword d, e; byte f); //shorthand format.A single datatype declaration "word" applies to all comma-separated parameters, until a semicolon denotes a change of datatype. There are two parameters of type "dword" and finally "f" is of type "byte".
void afunc(word a, word b, word c, dword d, dword e, byte f); //full format.
A macro is an inline register procedure. The keyword "inline" is described below.
Return values from functions are returned via registers, below is a table showing what register is used for each return type:
return type | register returned inThe easiest way to return a value from a function is to use the "return()" command, but the appropriate register can also be assigned the required return value instead. For example, the following two functions return the same value:
----------------------------------------
byte | AL
word | AX
dword | EAX
char | AL
short | AX
long | EAX
dword proc_one ()Strictly speaking, a procedure does not return a value, whereas a function does (in C-- this value is returned in the EAX register). However, I do not bother with this distinction and use the words procedure and function interchangeably.
{
return( 42 );
}
dword proc_two ()
{
EAX = 42;
}
long CARRYFLAG fopen(); //the declaration of the procedure.The conditional expression will evaluate as true if the function has returned with the Carry flag set.
if(fopen()) { code here } //evaluates returned Carry flag TRUE/FALSE.
if(handle=fopen()) { code here } //still evaluates Carry flag.
if(fopen() == 5) { code here } //reverts to return value.You can also use OVERFLOW and ZEROFLAG flags.
rettype modif procname ();where "modif" can be "pascal", "cdecl", "stdcall", or "fastcall".
Calling convention |
Explanation |
cdecl |
This type of call of procedures is the default for
the language C. It is characterized by the parameters of the procedure being
transferred (pushed onto the stack) in the order right-to-left. The clearing
of the stack of parameters is made after return from the procedure. This
way of calling procedures is very convenient for procedures with variable number of parameters, but is inefficient. |
pascal |
This type of call assumes, that the parameters are
transferred (pushed) in the order left-to-right, that is, in which they are
written down in the program. The procedure itself removes the parameters
from the stack, by use of the "ret n" instruction. This type of call is more compact than 'cdecl'. |
stdcall |
This type of call is a hybrid first two. The parameters
are transferred to the procedure in the order right-to-left (reverse of how
they are written). The parameters are removed from the stack by the procedure
itself. |
fastcall /register |
For this type of call, parameters are passed via registers.
Thus no parameters need be released from the stack. A maximum of six parameters
may be passed, using registers EAX, EBX, ECX, EDX, EDI, and ESI. 16-bit parameters
can be passed in AX, BX, CX and DX. 8-bit parameters can be passed in AL,
BL, CL and DL. |
void cdecl printf (word,...);This example has at least one passed parameter, of datatype "word", but may have more.
inline void sys_write_text(dword EBX, ECX, EDX, ESI){The keyword "inline" achieves this.
EAX = 4;
$int 0x40;
}
cpuspeed.c-- 26: sys_write_text(8<<16+8,0xFFFFFF,"C-- CPU speed example for MenuetOS",34);If this looks a bit useless, that's because it is. Despite registers being specified in the function definition, they aren't used -- they are just treated as dummy names (which is logical, and in keeping with standard C).
0000005F B808000800 mov eax,80008h
00000064 50 push eax
00000065 68FFFFFF00 push 0FFFFFFh
0000006A B8E0000000 mov eax,0E0h
0000006F 50 push eax
00000070 6A22 push 22h
msys.h-- 99: EAX = 4;
00000072 B804000000 mov eax,4
msys.h-- 100: $int 0x40;
00000077 CD40 int 40h
00000079 83C410 add esp,10h
inline void sys_write_text(dword xystart, color, #message, length){Note that it is allowed to have an explicit "return;" statement in an inline function. The compiler is intelligent enough to realise that it should not generate a "ret" instruction, just remove local stack parameters if required.
EAX = 4;
$int 0x40;
}
inline fastcall void sys_write_text(dword EBX, ECX, EDX, ESI){The keyword "fastcall" causes the passed parameters to be loaded into EBX, ECX, EDX, and ESI (in this example).
EAX = 4;
$int 0x40;
}
cpuspeed.c-- 26: sys_write_text(8<<16+8,0xFFFFFF,"C-- CPU speed example for MenuetOS",34);Personally, I don't like the choice of the name "fastcall" -- it would be more appropriate to use the keyword "register". You can do this by a simple #define:
0000005F BB08000800 mov ebx,80008h
00000064 B9FFFFFF00 mov ecx,0FFFFFFh
00000069 BAE0000000 mov edx,0E0h
0000006E BE22000000 mov esi,22h
msys.h-- 99: EAX = 4;
00000073 B804000000 mov eax,4
msys.h-- 100: $int 0x40;
00000078 CD40 int 40h
#define register fastcallInline procedures are dynamic, that is, only inserted in the code when needed.
fastcall void sys_write_text(dword EBX, ECX, EDX, ESI){must pass parameters in registers, but the function itself is not compiled inline. The compiled code will look like this:
EAX = 4;
$int 0x40;
}
cpuspeed.c-- 26: sys_write_text(8<<16+8,0xFFFFFF,"C-- CPU speed example for MenuetOS",34);where the actual function is compiled elsewhere:
00000067 BB08000800 mov ebx,80008h
0000006C B9FFFFFF00 mov ecx,0FFFFFFh
00000071 BAE4000000 mov edx,0E4h
00000076 BE22000000 mov esi,22h
0000007B E8A4FFFFFF call 24h
msys.h-- 99: EAX = 4;This example is not a dynamic function, as it is always compiled into the program.
00000024 B804000000 mov eax,4
msys.h-- 100: $int 0x40;
00000029 CD40 int 40h
0000002B C3 ret
void sys_write_text(dword xystart, color, #message, length){is a conventional non-inline function, in which parameters are passed via the stack.
EAX = 4;
$int 0x40;
}
: sys_write_text(dword xystart, color, #message, length){that is, the function will only be compiled into the program if called. Note that the colon is allowed to be on column one.
EAX = 4;
$int 0x40;
}
Interrupt procedures, procedures which are used as handles for interrupts, are defined in the following manner:
interrupt procedure_name ()Interrupt procedures do not automatically preserve any registers, and no registers are modified before the interrupt gains control, therefore it is your responsibility to 'push' and 'pop' registers to save and restore them.
{
// put code here
}
interrupt safe_handle ()
{
$PUSHAD //save all 32-bit registers.
/* do your thing here */
$POPAD
}
Selection statements, like in C can be followed by either a single command, or a block of many commands enclosed within '{' and '}'. C-- selection statements are restricted to C-- conditional expressions (as described in section 1.4 Expressions).
If more than 127 bytes of code follow an 'IF' statement, the compiler will issue the following error message:
IF jump distance too far, use if.This can be simply remeded by changing the offending 'IF' statement to 'if'.
'else' and 'ELSE' statements are used just like the 'else' command in C, except that 'ELSE' has the same 127 byte jump restriction as 'IF' of 127 bytes. 'else' generates 1 more byte of code than 'ELSE'.
'IF' and 'else', and 'if' and 'ELSE' may be mixed freely, such as the following example:
if( x == 2 )If more than 127 bytes of code follow an 'ELSE' statement, the compiler will issue the following error message:
WRITESTR("Two");
ELSE{WRITESTR("not two.");
printmorestuff();
}
ELSE jump distance too far, use else.Simply change the 'ELSE' statement to 'else' to correct the error.
count = 0;The conditional expression in the 'do {} while' statement must conform to the same rules as 'IF' and 'if' statements.
do {
count++;
WRITEWORD(count);
WRITELN();
} while (count < 5);
count = 5;Use of the register CX for small code block loops will yield the greatest code size efficiency for a 'loop', for the loop will be implemented by the use of the machine language 'LOOP' command.
loop( count )
{WRITEWORD(count);
WRITELN();
}
If the loop counter is zero before starting the 'loop' command, the loop will be executed the maximum number of times for the range of the variable. 256 times for a 8 bit (byte or char) counter, 65536 for a 16 bit (word or int) counter, and 4294967296 for a 32 bit (dword or long) loop counter. For example, the following loop will execute 256 times:
BH = 0;If no loop counter is given, the loop will loop forever. The following example will write *'s to the screen forever:
loop( BH )
{
}
loop()The programmer may, if he or she wishes to, use and/or change the value of the loop counter variable within the loop. For example the following loop will only execute 3 times:
WRITE('*');
CX = 1000;
loop( CX )
{
IF( CX > 3 )
CX = 3;
}
FOR(ECX=0;ECX<200;ECX++){The limited gotos, "break", "continue", "BREAK" and "CONTINUE" are allowed, the latter for short (+/-127) jumps.
EAX=ECX<<7;
EBX=EAX<<2;
EDI=EAX+EBX;
FOR(EDX=0;EDX<320;EDX++,EDI++){
ESI=EDI+offset1;
c1=flowers[ESI];
ESI=EDI+offset2;
c2=flowers[ESI];
ESI=EDI+offset3;
c3=flowers[ESI];
EAX=ECX<<6;
EBX=EAX<<2;
ESI=EAX+EBX+EDX;
screen[ESI]=c1+c2+c3;
}
}
switch(sys_wait_event())The limited gotos, "break", "continue", "BREAK" and "CONTINUE" are allowed, the latter for short (+/-127) jumps.
{
case 1:
draw_window();
continue; //go back up and re-evaluate switch.
case 2:
/*sys_get_key();*/
break; //exit from switch.
case 3:
if(sys_get_button_id()==1) sys_exit_process();
continue;
}
dword array[10];However, Michael Sheker has improved things so arrays can also be referenced by an index determined by datatype. For example:
dword i;
EBX=4;
array[EBX]=0; //*byte* offset.
array[4]=0; //*byte* offset.
i=1;This last example also accesses the second element of the array. Here you can see what gets compiled:
array[i]=0; //index determined by datatype.
cpuspeed.c-- 66: EBX=4;The array starts at address 1C4h, and you can see that the first two cases compute a byte offset from start of the array. However, by using a variable as the index, the variable correctly indexes the array, as in normal C.
0000011F BB04000000 mov ebx,4
cpuspeed.c-- 67: array[EBX]=0;
00000124 C783C401000000000000 mov dword ptr [ebx+1C4h],0
cpuspeed.c-- 68: array[4]=0;
0000012E C705C801000000000000 mov dword ptr [1C8h],0
cpuspeed.c-- 69: i=1;
00000138 C705EC01000001000000 mov dword ptr [1ECh],1
cpuspeed.c-- 70: array[i]=0;
00000142 8B35EC010000 mov esi,[1ECh]
00000148 C704B5C401000000000000 mov dword ptr [1C4h+esi*4],0
variable[index]Note that nesting of arrays is allowed, and it will generate the correct index as determined by datatype, as this example shows:
variable[index+EBX+ESI]
variable[index+EBX+EDI]
variable[index+EBP+ESI]
variable[index+EBP+EDI]
variable[index+ESI]
variable[index+EDI]
variable[index+EBP]
variable[index+EBX]
cpuspeed.c-- 69: buf[array[i]]=0;In the above example, both "buf[]" and "array[]" are defined with datatype "dword". You can see the "*4" in both cases.
0000013A 8B35C0010000 mov esi,[1C0h]
00000140 8B34B514020000 mov esi,[214h+esi*4]
00000147 C704B5C401000000000000 mov dword ptr [1C4h+esi*4],0
array[*i] = 0; //here variable i will contain an absolute byte-displacement in...hmmm.
//array, instead of number of an element (index).
struct <tag> {<fields>};where
<tag> is the name of the structure definition (not an instantiation),These are the two allowed formats for instantiating a structure:
<fields> are the definitions of the fields of the structure.
struct [<tag>] {<fields>} <name>[,<name>...];where
[struct] <tag> <name> [, <name>...];
[ ] square brackets denote optional,The first format both defines and instantiates a structure. Here are examples of the first format:
<name> is the name of an instantiation of the structure.
struct rect {long x; long y;} myrect;Note that the <tag>, being the name of the structure definition, is optional if all instantiations are declared within this format.
struct {long x; long y;} myrect;
struct {long x; long y;} rect1, rect2; //two structures instantiated.
struct rect {long x; long y;}; //definition of a structure.Note that fields of a structure are not aligned in any way -- only the start of an instantiated structure may be aligned, if the compiler has alignment turned on.
struct rect myrect; //instantiation of rect.
struct FileInfo{dword read,firstBlock,qnBlockRead,retPtr,Work; byte filedir;};
struct FileInfo myfile; //instantiation of FileInfo.
struct test {In this example there are instantiations of the structure "test" with the name "rr" and and array of four structures with the name "ff".
int a;
char b [8];
long c;
} rr, ff [4];
struct test dd;Furthermore, it is permissible to leave off the "struct" keyword:
test dd;
struct test dd = 2;In this example the memory area of the structure "dd" is filled with the value 2. By default the value is 8-bit unless there is a datatype override, so in this example every memory location of the structure will be asigned the value 2.
struct test dd = {1,2,,6};In this example to the first field of structure "dd" the value 1 is assigned, second has value 2, fourth field has value 6. Missed and noninitialised fields will be assigned the value zero.
struct test dd = FROM "file.dat";In this example at the place where the structure "dd" is located at compilation (instantiation), the contents of the <filename> file are loaded. If the file size is more than the size of the structure, the superfluous bytes will overflow into the code of the program. If the file size is less than the size of the structure, the missed bytes of the structure will be filled in zero.
struct test dd = EXTRACT "file.dat",24,10;In this example at the place where the structure "dd" is located at compilation will be inserted a fragment from the <filename> of length 10 bytes from offset 24. The missed bytes will be filled with zero.
struct test {short a;char b[8];long c;};In the first example memory occupied by the first structure of the array (of 5 structures) will be filled in byte 0x78 (by default). In the second example, the higher part of the value is ignored.
void proc() {
struct test aa[5], rr;
short i;
aa[0] = 0x78; //1. all memory filled with 0x78.
aa[0] = 0x12345678; //2. ditto
aa[i] = int 0x12345678; //3. all memory filled with 0x5678.
aa = long 0x12345678; //4. entire array filled with 0x12345678.
rr = i; //5. 16-bit value fills the memory.
rr = aa [2];Contents of the third structure of the array of structures "aa" in will be copied to structure "rr".
struct test {short a;char b[8];long c;} rr[5];Note:
rr.a = 1;
rr.b[i] = 2;
rr[i].c = 3;
rr[j].b[i] = 4;
struct bb // tag of the structure.
{
word b; // the first field.
dword c; // the second field.
} ss; // instantiation.
void proc ()There is an important difference between the above two examples. The statement "EAX=#ss.b;" gets the absolute address of the field, whereas the statement "EAX=#bb.b;" only gets the offset of the field from the start of the structure.
{
EAX=#ss.b; // get address of field "b" in structure "ss".
EAX=#bb.b; // get offset of the same field in a tag "bb".
}
struct RGBLet's assume it is necessary for you to receive contents of field "Red" in the tenth field of "color" array. It can be written down so:
{
byte Red;
byte Green;
byte Blue;
byte Reserved;
};
struct BMPINFO
{
struct BMPHEADER header; //the description of this structure is missed.
struct RGB color [256]; //array of RGB structures.
} info; //instantiation.
AL = info.color[10].Red;But there is one limitation of use of nested structures in C--. It is not allowed to use more than one variable. Let's explain it with an example:
struct ABC {
int a;
int b;
int c;
};
struct {
struct ABC first [4]; // 4 copies of structure ABC
int d;
} second [4];
int i, k;
void proc ()
{
AX=second[i].first[k].a; //an error as variables used in two places.
AX=second[2].first[k].a; //this syntax is allowable.
AX=second[i].first[3].a; //allowable.
}
<type>[<identifier>]:< a constant >;Here is an example:
struct test {byte a:1;b:2;c:1;d:1;long vv;};The bit field consists of some number of bits, which is set by the numerical expression <constant>. The value should be a whole positive number and must not exceed the numbers of bits appropriate to <type>.
struct test rr=0;
cpuspeed.c-- 73: EAX=rr.a;As you can see from the generated asm code, the bit fields "a", "b", "c" and "d" are compacted to all fit in one "byte" memory location.
0000012D A104010000 mov eax,[104h]
00000132 83E001 and eax,1
cpuspeed.c-- 74: EAX=rr.b;
00000135 A104010000 mov eax,[104h]
0000013A 83E006 and eax,6
0000013D D1E8 shr eax,1
cpuspeed.c-- 75: EAX=rr.c;
0000013F A104010000 mov eax,[104h]
00000144 83E008 and eax,8
00000147 C1E803 shr eax,3
cpuspeed.c-- 75: rr.c=1;
0000013F 800D0401000008 or byte ptr [104h],8
struct Point //the declaration of the class.The call to "p.SetX(1);" also transfers the address of the structure (class) via the stack to the called procedure (method). This extra paramter is not specified explicitly in the source statement. In the procedure this address is available through the name of a parametric variable "this".
{
int x; //data items
int y; // of the class of a type Point.
void SetX (int); //the declaration of methods
void SetY (int); // of the Point class.
};
void Point::SetX (int _x) //definition of the procedure of the Point class.
{
IF((_x>=0)&&(_x<=MAX_X)) x=_x;
//The variables x, y are the members of this class and consequently access
//to them from procedures of the same class is carried out directly.
}
void main ()
Point p; //structure p instantiated in the stack.
{
p.y = p.x = 0;
p.SetX(1);
}
struct Derived: Base1, Base2... BasenThe number of base structures is not limited.
{
long x0;
};
struct AIn this example the structure "D" inherits two copies of structure "A" and in it is two fields with the "x" name. When C++ Compilers encounter this "d.x=0", they produce an error message.
{
long x, y;
...
};
struct B: A //the structure 'B' inherits 'A'.
{
...
};
struct C: A //the structure 'C' inherits 'A'.
{
...
};
struct D: B, C //the structure 'D' inherits 'B' and 'C'.
{
...
};
void main ()
D d; //structure 'D' instantiated as 'd' on stack.
{
d.x = 0;
d.B::x=0;From this it follows that:
d.x=0;and
d.C::x=0;are equivalent.
char *string [4] = {"string1", "string2", "string3", 0}; // a pointer arrayPointers can be passed as parameters to procedures.
char *str = "string4";
main ()
int i;
char *tstr;
{
FOR (i = 0; string [i] != 0; i++) {
WRITESTR (string [i]);
WRITELN ();
}
FOR (tstr = str; byte *tstr != 0; tstr++) {
WRITE (byte *tstr);
}
}
void (*ptr)(); // the declaration of a pointer to a procedureIn this example, the function returns void, ie., nothing.
char *z;Although "z" is defined as a pointer to char datatype, the next line is assigning the address of a function to it. The compiler will not object, which may cause an error in your program if you are not careful. On the other hand, it gives you total freedom to assign whatever you want to pointers without being hassled by the compiler.
z = #main;
struct FileInfo *pmyfile; //unacceptable format.Well, even if I can declare a pointer, I can't use the "->" operator, so how do I use a pointer to reference a structure? From example code, it seems that this is a situation where we must use an explicit register, as for example:
cpuspeed.c-- 73: ESI.FileInfo.read=0;Note the syntax of the source code, "ESI.FileInfo.read=0;".
0000013E C70600000000 mov dword ptr [esi],0
//this time I've initialised the elements when instantiating...... I tried it with and without the "#" ...interesting.
cpuspeed.c-- 56: struct FileInfo myfile={0,0,0,0,0,0};
00000104 000000000000000000000000 dd 0,0,0
00000110 0000000000000000 dd 0,0
00000118 00 db 0
//now, pretend that "myfile" is in the ES (extra segment):
cpuspeed.c-- 74: ESDWORD[#myfile.read]=0;
0000015A 26C7050401000000000000 mov dword ptr es:[104h],0
cpuspeed.c-- 75: ESDWORD[myfile.read]=0;
00000165 8B3504010000 mov esi,[104h]
0000016B 26C70600000000 mov dword ptr es:[esi],0
Labels are defined by a identifier followed by a colon. If the identifier used contains one or more lower case letters, it is a global jump label, otherwise it is a local jump label.
Global jump labels must not be used within inline procedures, only local labels may be used. This is important to remember, for inline procedures are inserted inline maybe in multiple places, at compile time.
AX >< BX; // store the value of BX in AX and the value of AX in BXIf a swap is between two 8 bit memory variables, AL will be destroyed. If a swap is between two 16 bit memory variables, AX will be destroyed. If a swap is between 32 bit memory variables, EAX will be destroyed. In all other cases, such as a memory variable and a register, all register values will be preserved.
CH >< BL; // swap the values of CH and BL
dog >< cat; // swap the values of the variable dog and the variable cat
counter >< CX; // swap the values of counter and CX
-AX; // same as 'AX = -AX;' but faster.
-tree; // same as 'tree = -tree;' but faster.
-BH; // toggle the sign of BH.
C-- supports a quick syntax of doing a logical NOT toggling on a variable, the NOT operator. By placing a '!' in front of a memory variable or register followed by a ';', the value of the memory variable or register will be changed to the logical NOT of its current value. Some examples:
!AX; // same as 'AX ^= 0xFFFF;' but faster.
!node; // change the value of 'node' to its logical NOT.
!CL; // same as 'CL ^= 0xFF' but faster.
C-- supports six special conditional expressions:
CARRYFLAGThese can be used in place of any normal conditional expressions. If for example you wish to execute a block of code only if the carry flag is set, then you would use the following code sequence:
NOTCARRYFLAG
OVERFLOW
NOTOVERFLOW
ZEROFLAG
NOTZEROFLAG
IF( CARRYFLAG )If you wish to continuously execute a block of code until the overflow flag is set, you would use something like the following section of code:
{
// do some stuff here
}
do {
// do your thing in here
} while( NOTOVERFLOW );
sizeof (<a name of a type>)The result is the size of memory in bytes. The operator can apply to a variable, registers, types variables, structures, text strings and files.
sizeof ("Test") //result=5. includes terminating zero.Example of usage:
char a = "Test";
sizeof (a) //result=1. because "a" is char datatype.
sizeof (file "filename.dat") //result= size of the file.
sizeof (func1) //returns size of the procedure.
sizeof (ss.bb) //returns the size of "bb" member in structure "ss".
sizeof (FileInfo) //returns the size of structure "FileInfo".
z = sizeof (FileInfo.read);In the case of obtaining the size of a procedure, it must have been defined earlier in the file. If it is a dynamic procedure, the size of zero will be returned.
unionIt is possible to unite variables of various types, arrays, string variables and structures. The associations can be global and local, and also to settle down inside structures (while in associations inside structures it is impossible to use structures). The global associations can be initialized and non-initialized. To receive the initialized association needs to be initialized only first unit of association. If the first unit of association is not initialized, and the following units are initialized, it will cause a compiler error message.
{
dword regEAX;
word regAX;
byte regAL;
}; // have declared 3 variables located on same physical address.
void test ()
{
regEAX = 0x2C;
BL = regAL; //in the register BL there will be a value 0x2C.
}
atan(x); //calculate arctangent of number x.You can pass and return both floating point ("float") or signed integer ("long") to and from these macros. Note that the FPU internally does everything in floating point, but the load and store instructions can convert integer numbers.
atan2(x,y); //calculate arctangent of the attitude x/y.
cos(x); //return cosine of a corner x.
exp(x); //return to an exhibitor of number x (erects the
basis the natural logarithms in a degree x).
fabs(x); //calculate absolute value of number x.
log(x); //calculate the natural logarithm of number x.
log10(x); //calculate the decimal logarithm of number x.
sin(x); //return a sine of a corner x.
sqrt(x); //take a square root from among x.
tan(x); //return tangent of a corner x.
inp(port) //read one byte from a port
inportb(port) //read one byte from a port
inport(port) //read a word from a port
inportd(port) //read a double word from a port
/*port - the "port" parameter is not essential. If the value
is not given, the generated instruction will be "in al,dx".
If the port value is less than 256, the generated
instruction will be "in al,port".*/
outp(val,port) //writes byte "val" to a port
outportb(val,port) //writes byte "val" to a port
outport(val,port) //writes word "val" to a port
outportd(val,port) //writes double word to a port
/*val - written value
port - the word with the address of a port is not essential.
If the value is not given, the generated instruction will
be "out dx,al".
If the port value is less than 256, the instruction
will be "out port,al".*/
C-- inline assembly supports all of the 8088/8086 assembly codes, plus the
80286, 80386, 80486 and Pentium to Pentium III enhanced instructions.
Here is some example inline assembly:
//at the C level, a variable declared:
dword cpuspeed;
$mov eax,cpuspeed //loads contents of "cpuspeed" (square brackets not allowed).
$mov edi,#smsg1+16 //the "#" means immediate-mode (address-of "smsg1").
$mov ecx,5
$newnum: //labels are allowed.
$xor edx,edx
$mov ebx,10
$div ebx
$add dl,48
$mov DSBYTE[edi],dl //keyword "DSBYTE" is required.
$sub edi,1
$loop newnum
Note that all instructions start with the "$" inline assembly specifier.
There is a problem for those familiar with NASM and FASM, as the instruction
"mov eax,cpuspeed" loads the contents of variable "cpuspeed". You are not
allowed to put square brackets to clarify this.
If you want to load the address of the variable, you put "mov eax,#cpuspeed".
Basically, the assembler syntax follows that of MASM (Microsoft assembler).
It is not allowed to have just "mov [edi],dl". You have got to specify which segment and the size, hence "DSBYTE" prefix.
Even though we are using the FLAT memory model with MenuetOS, in which CS=DS=SS
(that is, point to the start of the virtual memory address space allocated
for the program), the instruction still has encoded into it which segment
it is accessing.
You are allowed to have a block of inline assembly. That is:
asm {
//asm code here.
}
is allowed. For this you use the "asm" keyword. I found that "$ { }" doesn't work.
Now for the ultimate integration of asm and C. If you put the commandline option as follows:
#pragma option ia //"$" and "asm" keywords not required.
Then asm code does not have to be in asm blocks. That is, "$" and "asm { }" are not required. For example:
dword cpuspeed;
void main(void)
{
cpuspeed=sys_service(5,0)/1000000; //a C statement.
push eax //wow, no asm block.
mov eax,cpuspeed
pop eax; xor eax,eax
draw_window(); //back to normal C.
I have just put in some random code to illustrate. You throw in asm instructions
just like C statements, and you can even use the standard ";" terminator
if you wish, as I've done to place more than one instruction per line.
Some keywords may clash. For example, "int" is both an instruction and
a datatype. However, C-- is in many cases able to distinguish between the
different usage of "int" by the context in which it is placed.
To avoid a conflict between "int" used in header (include) files and the
redefinition as an instruction mneumonic, it is prudent to place the "#pragma
option ia" line after the "#include" lines.
Also, this example is a clash:
EAX = int 1 + a;
To clarify that inline asm is basically MASM-compatible, here are examples:
$jmp short place1 //"short" keyword, +/-127 locations.
$mov eax,cpuspeed //load contents of variable.
$mov eax,#cpuspeed //load address of variable.
SPHINX C-- Compiler Version 0.238 Jun 03 2002
USAGE: C-- [options] [FILE_NAME.INI] [SOURCE_FILE_NAME]
C-- COMPILER OPTIONS
OPTIMIZATION
/OC optimize for code size /DE enable temporary expansion variable
/OS optimize for speed /OST enable optimization string
/ON enable optimization number /AP[=n] align start procedure
/UST use startup code for variables /AC[=n] align start cycles
CODE GENERATION
/2 80286 code optimizations /SA=#### start code address
/3 80386 code optimizations /AL=## set value insert byte
/4 80486 code optimizations /WFA fast call API procedures
/5 pentium code optimizations /IV initial all variables
/A enable address alignment /SUV=#### start address variables
PREPROCESSOR
/IP=<path> include file path /IA assembly instructions as identifier
/D=<idname> defined identifier /CRI- not check include file on repeated
/MIF=<file> main input file /IND=<name> import name from dll
LINKING
/AT insert ATEXIT support block /STM startup code in main procedure
/ARGC insert parse command line /NS disable stub
/P insert parse command line /S=##### set stack size
/C insert CTRL<C> ignoring code /WIB=##### set image base address
/R insert resize memory block /WFU add Fix Up table (for Windows32)
/ENV insert variable with environ /WMB create windows mono block
/J0 disable initial jump to main() /WS=<name> set name stub file for win32
/J1 initial jump to main() short /WBSS set post data in bss section
/J2 initial jump to main() near /WO call API procedures on ordinals
/STUB= <name> set name stub file /CPA clear post area
/DOS4GW file running with DOS4GW
OUTPUT FILES
/TEXE DOS EXE file (model TINY) /D32 EXE file (32bit code for DOS)
/EXE DOS EXE file (model SMALL) /W32 EXE for Windows32 GUI
/OBJ OBJ output file /W32C EXE for Windows32 console
/SOBJ slave OBJ output file /DLL DLL for Windows32
/SYM COM file symbiosis /DBG creation debug information
/SYS device (SYS) file /LST creation assembly listing
MISCELLANEOUS
/HELP /H /? help, this info /WORDS list of C-- reserved words
/W enable warning /LAI list of assembler instructions
/WF=<file> direct warnings to a file /ME display my name and my address
/MER=## set maximum number errors /X disable SPHINXC-- header in output
/NW=## disable select warning
#startaddress 0
#code32 TRUE
#pragma option X //disable sphinx header in output file.
#pragma option LST //generate asm listing file.
//#pragma option OC //optimisation for code size.(BK:makes run file bigger!!!)
//#pragma option 4 //for i80486.
//#pragma option A //align data from parity address.
#pragma option J0 //disable initial jump to main().
#resize 0 //disable mem. resizing code at start of output file.
# directive |
Explanation |
#includepath |
Same
as '/IP' on commandline. Tells the compiler where to look for files specified
by the "#include" directive (see below). Example:#includepath C:\progra~2\c--\inc |
#include |
This can be in two forms. Firstly:#include "windows.h--"An attempt at first is made to open the file in the current directory. If file is not present there, an attempt is made to open the file in the directory specified by the "#includepath" directive. If "#includepath" is not given or the specified file is not in this directory, an attempt is made to open the file in the directory by the option "/ip=path" in the command line. If this command is not given or the file is not in the indicated directory, an attempt is made to open the file in the directory indicated in the file "C--.INI" by the "ip=" command (note, "c--.ini" is placed in the current directory). If this command is not given or file is not there, an attempt is made to open the file in the directory specified by the C-- environment variables. Failing all that, a last attempt is made to open the file in directory in which the C-- compiler itself resides. Wow! #include <windows.h-->The search for the included file is made in the opposite direction to the above, except that search in the current folder is not made. |
#inline |
I'm not 100% sure how this works, but here is the translated explanation: But sometimes it happens it is necessary by the included optimization on the size of the code, that the procedures were inserted into the code, instead of their call was done. For these objectives is entered the instruction #inline TRUE. By same instruction ( #inline FALSE ), it is possible at optimization on speed to do calls of procedures, instead of their insert. It is important to remember, that the status of the instruction #inline automatically varies at to mode change of optimization. At installation of optimization on speed the status the instructions #inline is installed in TRUE, and at mode change of optimization on to the size of the code, is installed in FALSE. Therefore apply the instruction #inline only after mode change of optimization. One more change in the compiler: the instructions changing a mode of optimization #codesize, #speed and instruction #inline,, declared inside the procedure, are distributed only to the rest of the procedure, i.e. they become local. That the changes were global these instructions it necessary to declare outside of a body of the procedure. |
R-The ini-file can have any name (but the extension must be ".ini"). The name of this file with the extension should be transferred to the compiler by the command line. The file c--.ini is processed automatically before loading the file indicated in the command line.
X
3 ;comments are allowed, preceded by a ";".
os
SPHINX C-- Compiler Version 0.238 Jun 03 2002
LIST OF SUPPORTED ASSEMBLER INSTRUCTIONS:
AAA AAD AAM AAS ADC ADD ADDPS ADDSS
ADRSIZE AND ANDNPS ANDPS ARPL
BOUND BSF BSR BSWAP BT BTC BTR BTS
CALL CALLF CBW CDQ CLC CLD CLI CLTS
CMC CMOVA CMOVAE CMOVB CMOVBE CMOVC CMOVE CMOVG
CMOVGE CMOVL CMOVLE CMOVNA CMOVNAE CMOVNB CMOVNBE CMOVNC
CMOVNE CMOVNG CMOVNGE CMOVNL CMOVNLE CMOVNO CMOVNP CMOVNS
CMOVNZ CMOVO CMOVP CMOVPE CMOVPO CMOVS CMOVZ CMP
CMPPS CMPSB CMPSD CMPSS CMPSW CMPXCHG CMPXCHG8B COMISS
CPUID CVTPI2PS CVTPS2PI CVTSI2SS CVTSS2SI CVTTPS2PI CVTTSS2SI CWD
CWDE
DAA DAS DB DD DEC DIV DIVPS DIVSS
DW
EMMS EMMX ENTER
F2XM1 FABS FADD FADDP FBLD FBSTP FCHS FCLEX
FCMOVB FCMOVBE FCMOVE FCMOVNB FCMOVNBE FCMOVNE FCMOVNU FCMOVU
FCOM FCOMI FCOMIP FCOMP FCOMPP FCOS FDECSTP FDISI
FDIV FDIVP FDIVR FDIVRP FENI FFREE FIADD FICOM
FICOMP FIDIV FIDIVR FILD FILDQ FIMUL FINCSTP FINIT
FIST FISTP FISUB FISUBR FLD FLD1 FLDCW FLDENV
FLDL2E FLDL2T FLDLG2 FLDLN2 FLDPI FLDZ FMUL FMULP
FNCLEX FNDISI FNENI FNINIT FNOP FNSAVE FNSETPM FNSTCW
FNSTENV FNSTSW FPATAN FPREM FPREM1 FPTAN FRNDINT FRSTOR
FSAVE FSCALE FSETPM FSIN FSINCOS FSQRT FST FSTCW
FSTENV FSTP FSTSW FSUB FSUBP FSUBR FSUBRP FTST
FUCOM FUCOMI FUCOMIP FUCOMP FUCOMPP FWAIT FXAM FXCH
FXRSTOR FXSAVE FXTRACT FYL2X FYL2XP1
HALT HLT
IDIV IMUL IN INC INSB INSD INSW INT
INTO INVD INVLPD INVLPG IRET IRETD
JA JAE JB JBE JC JCXZ JE JECXZ
JG JGE JL JLE JMP JMPF JMPN JMPS
JNA JNAE JNB JNBE JNC JNE JNG JNGE
JNL JNLE JNO JNP JNS JNZ JO JP
JPE JPO JS JZ
LAHF LAR LDMXCSR LDS LEA LEAVE LES LFS
LGDT LGS LIDT LLDT LMSW LOADALL LOCK LODSB
LODSD LODSW LOOP LOOPD LOOPE LOOPNE LOOPNZ LOOPW
LOOPZ LSL LSS LTR
MASKMOVQ MAXPS MAXSS MINPS MINSS MOV MOVAPS MOVD
MOVHLPS MOVHPS MOVLHPS MOVLPS MOVMSKPS MOVNTPS MOVNTQ MOVQ
MOVSB MOVSD MOVSS MOVSW MOVSX MOVUPS MOVZX MUL
MULPS MULSS NEG NOP NOT
OPSIZE OR ORPS OUT OUTSB OUTSD OUTSW
PACKSSDW PACKSSWB PACKUS PACKUSWB PADDB PADDD PADDSB PADDSW
PADDUSB PADDUSW PADDW PAND PANDN PAVGB PAVGW PCMPEQB
PCMPEQD PCMPEQW PCMPGTB PCMPGTD PCMPGTW PEXTRW PINSRW PMADD
PMADDWD PMAXSW PMAXUB PMINSW PMINUB PMOVMSKB PMULH PMULHUW
PMULHW PMULL PMULLW POP POPA POPAD POPF POPFD
POR PREFETCHNTA PREFETCHT0 PREFETCHT1 PREFETCHT2 PSADBW PSHUFW
PSLLD PSLLQ PSLLW PSRAD PSRAW PSRLD PSRLQ PSRLW
PSUBB PSUBD PSUBSB PSUBSW PSUBUSB PSUBUSW PSUBW PUNPCKHBW
PUNPCKHDQ PUNPCKHWD PUNPCKLBW PUNPCKLDQ PUNPCKLWD PUSH PUSHA
PUSHAD PUSHF PUSHFD PXOR
RCL RCPPS RCPSS RCR RDMSR RDPMC RDTSC REP
REPE REPNE REPNZ REPZ RET RETF ROL ROR
RSM RSQRTPS RSQRTSS
SAHF SAL SAR SBB SBC SCASB SCASD SCASW
SETA SETAE SETALC SETB SETBE SETC SETE SETG
SETGE SETL SETLE SETNA SETNAE SETNB SETNBE SETNC
SETNE SETNG SETNGE SETNL SETNLE SETNO SETNP SETNS
SETNZ SETO SETP SETPE SETPO SETS SETZ SFENCE
SGDT SHL SHLD SHR SHRD SHUFPS SIDT SLDT
SMSW SQRTPS SQRTSS STC STD STI STMXCSR STOSB
STOSD STOSW STR SUB SUBPS SUBSS SYSENTER SYSEXIT
TEST
UCOMISS UD2 UNPCKHPS UNPCKLPS
VERR VERW
WAIT WBINVD WRMSR
XADD XCHG XLAT XLATB XOR XORPS
code32.txt 32-bit programming.
comstr.txt Commandline parameters.
comstrd.txt Optimisation of numerical expressions.
directin.txt Conditional compilation.
ifloop.txt Conditonal instructions.
import.txt FROM and EXTRACT.
index.txt Index addressing.
instr.txt Compiler directives.
label.txt Labels of transition.
output.txt Output files.
sintc.txt Special operators.
sys.txt Compilation of device drivers.