forked from minexew/Shrine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
HolyC.DD
268 lines (210 loc) · 11.4 KB
/
HolyC.DD
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
$WW,1$$FG,5$$TX+CX,"HolyC"$$FG$
* See $LK,"::/Doc/CompilerOverview.DD"$.
* See $LK,"Scoping and Linkage",A="FI:::/Doc/ScopingLinkage.DD"$ for details on $FG,2$extern$FG$, $FG,2$import$FG$, $FG,2$_extern$FG$, $FG,2$_import$FG$, etc.
* Built-in types include $FG,2$I0,I8,I16,I32,I64$FG$ for signed 0-8 byte ints and $FG,2$U0,U8,U16,U32,U64$FG$ for unsigned 0-8 byte ints and $FG,2$F64$FG$ for 8 byte floats.
$FG,2$ U0 void, but ZERO size!
I8 char
U8 unsigned char
I16 short
U16 unsigned short
I32 int
U32 unsigned int
I64 long (64-bit)
U64 unsigned long (64-bit)
F64 double$FG$
$FG,4$no F32 float.$FG$
* Function with no args, or just default args can be called without parentheses.
>$FG,2$Dir("*");$FG$
>$FG,2$Dir();$FG$
>$FG,2$Dir;$FG$
* Default args don't have to be on the end. This code is valid:
$ID,2$$FG,2$U0 Test(I64 i=4,I64 j,I64 k=5)
{
Print("%X %X %X\n",i,j,k);
}
Test(,3);$FG$
$ID,-2$
* A char const all alone is sent to $LK,"PutChars",A="MN:PutChars"$(). A string with or without args is sent to $LK,"Print",A="MN:Print"$(). An empty string literal signals a variable fmt_str follows.
$ID,2$$FG,2$void DemoC(char drv,char *fmt,char *name,int age)
{
printf("Hello World!\n");
printf("%s age %d\n",name,age);
printf(fmt,name,age);
putchar(drv);
putchar('*');
}
U0 DemoHolyC(U8 drv,U8 *fmt,U8 *name,I64 age)
{
"Hello World!\n";
"%s age %d\n",name,age;
"" fmt,name,age;
'' drv;
'*';
}
$FG$$ID,-2$
* When dealing with function addresses such as for callbacks, precede the name with "$FG,2$&$FG$".
* Type casting is postfix. To typecast int or F64, use $LK,"ToI64",A="MN:ToI64"$(), $LK,"ToBool",A="MN:ToBool"$() or $LK,"ToF64",A="MN:ToF64"$(). (TempleOS follows normal C float<-->int conversion, but sometimes you want to override. These functions are better than multiplying by "1.0" to convert to float.)
* There is no $FG,2$main()$FG$ function. Any code outside of functions gets executed upon start-up, in order.
* There are no bit fields, but there are $LK,"bit access",A="HI:Bit"$ routines and you can access bytes or words within any int. See $LK,"I64 declaration",A="MN:I64"$. A class can be accessed as a whole are subints, if you put a type in front of the $FG,2$class$FG$ declaration.
$ID,2$
$FG,2$public I64i union I64 //"I64i" is intrinsic. We are defining "I64".
{
I8i i8[8];
U8i u8[8];
I16 i16[4];
U16 u16[4];
I32 i32[2];
U32 u32[2];
};
I64 i=0x123456780000DEF0;
i.u16[1]=0x9ABC;
$FG$$ID,-2$
* Variable arg count functions ($FG,2$...$FG$) can access their args with built-in variables similar to '$FG,2$this$FG$' in C++. They are '$FG,2$I64 argc$FG$' and '$FG,2$I64 argv[]$FG$'.
$ID,2$$WW,0$
$FG,2$I64 AddNums(...)
{
I64 i,res=0;
for (i=0;i<argc;i++)
res+=argv[i];
return res;
}
$FG$>$FG,2$AddNums(1,2,3);$FG$
ans=6
$FG,2$
public U0 GrPrint(CDC *dc,I64 x,I64 y,U8 *fmt,...)
{
U8 *buf=$LK,"StrPrintJoin",A="MN:StrPrintJoin"$(NULL,fmt,argc,argv);//SPrintF() with $LK,"MAlloc",A="MN:MAlloc"$()ed string.
$LK,"GrPutS",A="MN:GrPutS"$(dc,x,y,buf); //Plot string at x,y pixels. GrPutS is not public.
Free(buf);
}
...
GrPrint(gr.dc,(GR_WIDTH-10*FONT_WIDTH)>>1,(GR_HEIGHT-FONT_HEIGHT)>>1,
"Score:%4d",score); //Print score in the center of the scrn.
...
$WW,1$$FG$$ID,-2$
* Allows "$FG,2$5<i<j+1<20$FG$" instead of "$FG,2$5<i && i<j+1 && j+1<20$FG$".
$ID,2$
$FG,2$if (13<=age<20)
"Teen-ager";
$FG$$ID,-2$
* if you know a switch stmt will not exceed the lowest or highest case values. $FG,2$switch []$FG$ is a little faster because it doesn't check.
* $FG,2$switch$FG$ stmts always use a jump table. Don't use them with cases with really big, sparse ranges.
* Allows ranges like "$FG,2$case 4...7:$FG$" in $FG,2$switch$FG$ stmts.
* A no case number causes next higher int case in $FG,2$switch$FG$ stmts. See $LK,"::/Demo/NullCase.HC"$.
$ID,2$$FG,2$I64 i;
for (i=0;i<20;i++)
switch (i) {
case: "Zero\n"; break; //Starts at zero
case: "One\n"; break; //One plus prev case.
case: "Two\n"; break;
case: "Three\n"; break;
case 10: "Ten\n"; break;
case: "Eleven\n"; break; //One plus prev case.
}$FG$$ID,-2$
* Switch statements can be nestled with a single switch expression! This is known as a "sub_switch" statement. $FG,2$start$FG$/$FG,2$end$FG$ are used to group cases. Don't goto out of, throw an exception out of, or return out of the $FG,2$start$FG$ front porch area. See $LK,"::/Demo/SubSwitch.HC"$.
$ID,2$$FG,2$I64 i;
for (i=0;i<10;i++)
switch (i) {
case 0: "Zero "; break;
case 2: "Two "; break;
case 4: "Four "; break;
start:
"[";
case 1: "One"; break;
case 3: "Three";break;
case 5: "Five"; break;
end:
"] ";
break;
}$FG$
$BG$OutPut:
>$FG,2$Zero [One] Two [Three] Four [Five]$FG$
$ID,-2$
* A $FG,2$no_warn$FG$ stmt will suppress an unused var warning.
* You can have multiple member vars of a class named "$FG,2$pad$FG$" or "$FG,2$reserved$FG$", and it won't issue warnings.
* $FG,2$noreg$FG$ or $FG,2$reg$FG$ can be placed before a function local var name. You can, optionally, specify a reg after the $FG,2$reg$FG$ keyword.
$ID,2$$FG,2$U0 Main()
{
//Only use $LK,"REGG_LOCAL_VARS",A="MN:REGG_LOCAL_VARS"$ or $LK,"REGG_LOCAL_NON_PTR_VARS",A="MN:REGG_LOCAL_NON_PTR_VARS"$ for reg vars or else clobbered.
I64 reg R15 i=5, noreg j=4;
no_warn i;
asm {
MOV RAX,R15
CALL &PUT_HEX_U64
MOV RAX,'\n'
CALL &PUT_CHARS
MOV RAX,U64 &j[RBP]
CALL &PUT_HEX_U64
MOV RAX,'\n'
CALL &PUT_CHARS
}
}
$FG$$ID,-2$
* $FG,2$interrupt$FG$, $FG,2$haserrcode$FG$, $FG,2$public$FG$, $FG,2$argpop$FG$ or $FG,2$noargpop$FG$ are function flags. See $LK,"IRQKbd",A="MN:IRQKbd"$().
* A single quote can encompass multiple characters. $FG,2$'ABC'$FG$ is equ to $FG,2$0x434241$FG$. $LK,"PutChars",A="MN:PutChars"$() takes multiple characters.
$ID,2$$FG,2$asm {
HELLO_WORLD::
PUSH RBP
MOV RBP,RSP
MOV RAX,'Hello '
CALL &PUT_CHARS
MOV RAX,'World\n'
CALL &PUT_CHARS
LEAVE
RET
}
Call(HELLO_WORLD);
PutChars('Hello ');
PutChars('World\n');
$FG$$ID,-2$
* The "$FG,2$`$FG$" operator raises a base to a power.
* There is no question-colon operator.
* TempleOS $LK,"operator precedence",A="FF:::/Compiler/CInit.HC,cmp.binary_ops"$
$FG,2$`$FG$,$FG,2$>>$FG$,$FG,2$<<$FG$
$FG,2$*$FG$,$FG,2$/$FG$,$FG,2$%$FG$
$FG,2$&$FG$
$FG,2$^$FG$
$FG,2$|$FG$
$FG,2$+$FG$,$FG,2$-$FG$
$FG,2$<$FG$,$FG,2$>$FG$,$FG,2$<=$FG$,$FG,2$>=$FG$
$FG,2$==$FG$,$FG,2$!=$FG$
$FG,2$&&$FG$
$FG,2$^^$FG$
$FG,2$||$FG$
$FG,2$=$FG$,$FG,2$<<=$FG$,$FG,2$>>=$FG$,$FG,2$*=$FG$,$FG,2$/=$FG$,$FG,2$&=$FG$,$FG,2$|=$FG$,$FG,2$^=$FG$,$FG,2$+=$FG$,$FG,2$-=$FG$
* You can use $LK,"Option",A="MN:Option"$($LK,"OPTf_WARN_PAREN",A="MN:OPTf_WARN_PAREN"$,ON) to find unnecessary parentheses in code.
* You can use $LK,"Option",A="MN:Option"$($LK,"OPTf_WARN_DUP_TYPES",A="MN:OPTf_WARN_DUP_TYPES"$,ON) to find dup local var type stmts.
* With the $FG,2$#exe{}$FG$ feature in your src code, you can place programs that insert text into the stream of code being compiled. See $LK,"#exe {}",A="FF:::/Kernel/KMain.HC,#exe {"$ for an example where the date/time and compile-time prompting for cfguration data is placed into a program. $LK,"StreamPrint",A="MN:StreamPrint"$() places text into a src program stream following the conclusion of the $FG,2$#exe{}$FG$ blk.
* No $FG,2$#define$FG$ functions exist (I'm not a fan)
* No $FG,2$typedef$FG$, use $FG,2$class$FG$.
* No type-checking
* Can't use $FG,2$<>$FG$ with $FG,2$#include$FG$, use $FG,2$""$FG$.
* "$FG,2$$$$FG$" is an escape character. Two dollar signs signify an ordinary $$. See $LK,"DolDoc",A="FI:::/Doc/DolDocOverview.DD"$. In $FG,2$asm$FG$ or $LK,"HolyC",A="FI:::/Doc/HolyC.DD"$ code, it also refers to the inst's address or the offset in a $FG,2$class$FG$ definition.
* $FG,2$union$FG$ is more like a class, so you don't reference it with a $FG,2$union$FG$ label after you define it. Some common unions are declared in $LK,"KernelA.HH",A="MN:U16"$ for 1,2,4 and 8 byte objects. If you place a type in front of a union declaration, that is the type when used by itself. See $LK,"::/Demo/SubIntAccess.HC"$.
* $FG,2$class$FG$ member vars can have meta data. $FG,2$format$FG$ and $FG,2$data$FG$ are two meta data types now used. All compiler structures are saved and you can access the compiler's info about classes and vars. See $LK,"::/Demo/ClassMeta.HC"$ and $LK,"DocForm",A="MN:DocForm"$().
* There is a keyword $FG,2$lastclass$FG$ you use as a dft arg. It is set to the class name of the prev arg. See $LK,"::/Demo/LastClass.HC"$, $LK,"ClassRep",A="MN:ClassRep"$(), $LK,"DocForm",A="MN:DocForm"$() and $LK,"::/Demo/Dsk/BlkDevRep.HC"$.
* See $LK,"::/Demo/Exceptions.HC"$. $FG,2$try{} catch{}$FG$ and $FG,2$throw$FG$ are different from C++. $FG,2$throw$FG$ is a function with an 8-byte or less char arg. The char string passed in $FG,2$throw()$FG$ can be accessed from within a $FG,2$catch{}$FG$ using the $FG,2$Fs->except_ch$FG$. Within a $FG,2$catch {}$FG$ blk, set the var $FG,2$Fs->catch_except$FG$ to $FG,2$TRUE$FG$ if you want to terminate the search for a hndlr. Use $LK,"PutExcept",A="MN:PutExcept"$() as a hndlr, if you like.
* A function is available similar to $FG,2$sizeof$FG$ which provides the offset of a member of a class. It's called $FG,2$offset$FG$. You place the class name and member inside as in $FG,2$offset(classname.membername)$FG$. It has nothing to do with 16-bit code. Both $FG,2$sizeof$FG$ and $FG,2$offset$FG$ only accept one level of member vars. That is, you can't do $FG,2$sizeof(classname.membername.submembername)$FG$.$FG$
* There is no $FG,2$continue$FG$ stmt. Use $FG,2$goto$FG$.
* $FG,2$lock{}$FG$ can be used to apply asm $FG,2$LOCK$FG$ prefixes to code for safe multicore read-modify-write accesses. The code bracked with have $FG,2$LOCK$FG$ asm prefix's applied to relevant insts within. It's a little shoddy. See $LK,"::/Demo/MultiCore/Lock.HC"$.
* There is a function called $LK,"MSize",A="MN:MSize"$() which gives the size of an object alloced off the heap. For larger size allocations, the system rounds-up to a power of two, so $FG,2$MSize()$FG$ lets you know the real size and you can take full advantage of it.
* You CAN $LK,"Free",A="MN:Free"$() a $FG,2$NULL$FG$ ptr. Useful variants of $LK,"MAlloc",A="MN:MAlloc"$() can be found $LK,"Here",A="MN:CAlloc"$. Each task has a heap and you can $FG,2$MAlloc$FG$ and $FG,2$Free$FG$ off-of other task's heaps, or make an independent heap with $LK,"HeapCtrlInit",A="MN:HeapCtrlInit"$(). See $LK,"HeapLog",A="MN:HeapLog"$() for an example.
* The stk does not grow because virtual mem is not used. I recommend allocating large local vars from the heap. You can change $LK,"MEM_DFT_STK",A="MN:MEM_DFT_STK"$ and recompile $FG,2$Kernel$FG$ or request more when doing a $LK,"Spawn",A="MN:Spawn"$(). You can use $LK,"CallStkGrow",A="MN:CallStkGrow"$(), but it's odd. See $LK,"::/Demo/StkGrow.HC"$.
* Only one base class is allowed.
* $FG,2$printf()$FG$ has new codes. See $LK,"Print(\"\") Fmt Strings",A="FI:::/Doc/Print.DD"$.
* All values are extended to 64-bit when accessed. Intermediate calculations are done with 64-bit values.
$ID,2$$FG,2$U0 Main()
{
I16 i1;
I32 j1;
j1=i1=0x12345678; //Resulting i1 is 0x5678 but j1 is 0x12345678
I64 i2=0x8000000000000000;
Print("%X\n",i2>>1); //Res is 0xC000000000000000 as expected
U64 u3=0x8000000000000000;
Print("%X\n",u3>>1); //Res is 0x4000000000000000 as expected
I32 i4=0x80000000; //const is loaded into a 64-bit reg var.
Print("%X\n",i4>>1); //Res is 0x40000000
I32 i5=-0x80000000;
Print("%X\n",i5>>1); //Res is 0xFFFFFFFFC0000000
}
$ID,-2$$FG$