1 // Written in the D programming language. Мохов Геннадий Владимирович 2015
2 // Версия v1.0 - 30.04.15 10:45
3 // Попытка перенести на asm D форт реализацию >SPF-Fork 2005-2013 mOleg mOlegg@ya.ru
4 /**
5   * <b><u>SPF-Fork 2005-2013 mOleg mOlegg@ya.ru для D.</u></b>
6   */
7 
8 // initForth() - инициализировать Форт
9 // evalForth(string str) - выполнить строку как строку Форта
10 // includedForth(string NameFileForth) - Загрузить и выполнить файл Форта
11 // pp asr = getCommonAdr(int n) - Вернуть из общей таблицы (ячейка n) значение
12 // setCommonAdr(int n, pp adr) - Записать в ячейку общей таблицы n значение равное adr
13 // adrContext() - Указатель на adr[256] со списками слов
14 // extern (C) pp executeForth(pp adrexec, uint kolPar, ...) {
15 
16 // История изменений
17 // 28.06.15 - BMOVE Копировать байты 
18 // 05.07.15 - Пропускать в поиске слова из одних цифр 
19 // 06.07.15 - Добавил DDUP
20 // 10.04.16 - Исправлена ошибка в EXECUTEFROMD
21  
22 module forth;
23 
24 import std.stdio;
25 import core.stdc.stdio : printf;
26 import std.conv;
27 // import std.c.stdio;
28 
29 int kolPer;
30 
31 alias	void*	p;		// Просто указатель
32 alias	void**	pp;		// Указатель на указатель
33 alias  ubyte*	pb;		// Указатель на байт
34 alias   char* 	ps;   	// Указатель на char
35 
36 // Стеки и кодофайл выделены в хипе и не пересекаются
37 private const CELL = 4;
38 private const sizeCodeFile = 30000; // Количество CELL для кодофайла
39 private const sizeStack    =  1000; // Количество CELL для стеков
40 
41 // Таблица общих для F и D адресов. В неё можно помещать адреса переменных или функций.
42 // Контроля над тем, что лежит нет!
43 private pp[100] commonTable;
44 
45 // Выдать адрес context из структуры forth
46 void* adrContext() {return gpcb.context; }
47 // Выдать адрес начала стека SD
48 void* adr_cSD() {	return gpcb.csd; }
49 // Выдать адрес сохраненого стека SP
50 void* adr_SD() {	return gpcb.saveEBP;  }
51 
52 // Выдать адрес начала кодофайла
53 void* adr_begKDF() {	return gpcb.akdf;  }
54 // Выдать адрес HERE
55 void* adr_here()   {	return gpcb.here;  }
56 // Выдать адрес конца кодофайла
57 void* adr_endKDF() {	return gpcb.akdf + sizeCodeFile;  }
58 
59 // Контекст Fotrh процесса
60 private struct NPcb {
61 	pp 	csd; 					// указатель на начало стека SD
62 	pp 	csr; 					// указатель на начало стека SR
63 	pp 	csc; 					// указатель на начало стека SC
64 	pp	akdf;					// указатель на начало кодофайла
65 	pb 	here; 					// указатель начала свободной области кодофайла
66 	pp 	latest;					// указатель на аоследнее скомпилированное слово
67 	pp	context;				// указатель на массив из 256 cell в каждой ячейке context на эту букву
68 	pp	executeFromD;			// ' EXECUTEFROMD
69 	pp	state;					// текущее состояние компиляции 0=интерпретация
70 	byte imm;					// запомнить состояние IMM в последнем FIND
71 	
72 	pp adrCommonTable;			// адрес общий таблицы
73 
74 	ps	In;                     // указатель на место интерпретации в вход. буфеpе
75     ps	Tib; 					// указатель на сам входной буфеp
76 	int dlTib;					// Размер строки прочитанной в Tib
77 	// Регистры сохранения состояния
78 	pp saveEBP;          		// Место под EBP форта
79 	pp saveEAX;          		// Место под EAX форта
80 	pp saveESI;          		// Место под ESI форта
81 	pp saveEDI;          		// Место под EDI форта
82 }
83 
84 /* private */ NPcb gpcb;						// Глобальное определение блока управления
85 private pb	kdf;						// Сюда будем компилировать код 
86 private pp  stSD, stSR, stSL;			// Указатели на стеки
87 
88 private ubyte[1000] tib;				// Буфер строки для иекстового разбора
89 
90 // Распечатать дамп памяти для указанного адреса
91 void dumpAdr(pp adr) {
92 	import std.stdio: writefln;
93 	ubyte* uk = cast(ubyte*)adr; ubyte* ur;
94 	for(int i; i !=5; i++) {
95 		ur = uk+(10*i);
96 		writefln("[%10s]  %3X  %3X  %3X  %3X  %3X  %3X  %3X  %3X  %3X  %3X", 
97 			cast(void*)ur, 
98 				*(cast(pb)ur+0), *(cast(pb)ur+1), *(cast(pb)ur+2), *(cast(pb)ur+3), 
99 				*(cast(pb)ur+4), *(cast(pb)ur+5), *(cast(pb)ur+6), *(cast(pb)ur+7), 
100 				*(cast(pb)ur+8), *(cast(pb)ur+9) 
101 		);
102 		writefln("[%10s]  [%1s]  [%1s]  [%1s]  [%1s]  [%1s]  [%1s]  [%1s]  [%1s]  [%1s]  [%1s]", 
103 			"--->", 
104 				*(cast(ps)ur+0), *(cast(ps)ur+1), *(cast(ps)ur+2), *(cast(ps)ur+3), 
105 				*(cast(ps)ur+4), *(cast(ps)ur+5), *(cast(ps)ur+6), *(cast(ps)ur+7), 
106 				*(cast(ps)ur+8), *(cast(ps)ur+9) 
107 		);
108 	}
109 }
110 
111 // ======================== defkern.f ========================
112 
113 // 01-02-2008 ~mOleg
114 // Copyright [C] 2006-2013 mOleg mOlegg@ya.ru
115 // Процедуры времени выполнения для CONSTANT, VARIABLE, etc.
116 
117 // Сравнение.
118 // CODE = ( A B --> T/F )
119 private void f_RAWNO() {
120 	asm {		naked;
121 		xor EAX,dword ptr SS:[EBP];
122 		sub EAX, 1;
123 		sbb EAX,EAX;
124 		lea EBP,[EBP+CELL];
125 		ret;
126 	}
127 }
128 // Сравнение.
129 // CODE <> ( A B --> T/F )
130 private void f_NRAWNO() {
131 	asm {		naked;
132 		xor EAX,dword ptr SS:[EBP];
133 		neg EAX;
134 		sbb EAX,EAX;
135 		lea EBP,[EBP+CELL];
136 		ret;
137 	}
138 }
139 // Сравнение.
140 // CODE < ( A B --> T/F )
141 private void f_MENSHE() {
142 	asm {		naked;
143 		cmp EAX,dword ptr SS:[EBP];
144 		setle AL;
145 		and EAX, 1;
146 		dec EAX;
147 		lea EBP,[EBP+CELL];
148 		ret;
149 	}
150 }
151 // Сравнение.
152 // CODE > ( A B --> T/F )
153 private void f_BOLSHE() {
154 	asm {		naked;
155 		cmp EAX,dword ptr SS:[EBP];
156 		setge AL;
157 		and EAX, 1;
158 		dec EAX;
159 		lea EBP,[EBP+CELL];
160 		ret;
161 	}
162 }
163 // ничего не делать.
164 // CODE NOOP ( --> )
165 private void f_NOOP() {
166 	asm {		naked;
167 		ret;
168 	}
169 }
170 //  выполнить слова, представленного своим исполнимым адресом xt v
171 // CODE EXECUTE ( xt --> )
172 private void f_EXECUTE() {
173 	asm {		naked;
174 		mov EBX, EAX;
175 		mov EAX,dword ptr SS:[EBP];
176 		lea EBP,[EBP+CELL];
177 		jmp EBX;
178 	}
179 }
180 // выполнить слово, адрес которого хранится в ячейке памяти addr v
181 // то есть A@ EXECUTE
182 // CODE PERFORM ( addr --> )
183 // выполнить слово, адрес которого хранится в ячейке памяти addr √
184 // то есть A@ EXECUTE
185 private void f_PERFORM() {
186 	asm {		naked;
187 		mov EBX,dword ptr DS:[EAX];
188 		mov EAX,dword ptr SS:[EBP];
189 		lea EBP, [EBP+CELL];
190 		jmp EBX;
191 	}
192 }
193 // безусловный переход на адрес без возможности возврата за точку JMP
194 // аналог  RDROP >R EXIT
195 // CODE JUMP ( addr --> )
196 private void f_JUMP() {
197 	asm {		naked;
198 		mov dword ptr [ESP],EAX;
199 		mov EAX,dword ptr SS:[EBP];
200 		lea EBP,[EBP+CELL];
201 		ret;
202 	}
203 }
204 // закончить выполнение текущего слова
205 // CODE EXIT ( --> )
206 private void f_EXIT() {
207 	asm {		naked;
208 		pop EDX;
209 		ret;
210 	}
211 }
212 // выйти из текущего слова, если флаг отличен от нуля v
213 // CODE ?EXIT ( flag --> )
214 private void f_Q_EXIT() {
215 	asm {		naked;
216 		or EAX,EAX;
217 		mov EAX,dword ptr SS:[EBP];
218 		lea EBP,[EBP+CELL];
219 		jz M1;
220 		lea ESP,[ESP];
221 M1:		
222 		ret;
223 	}
224 }
225 // выбор нужного варианта из списка v
226 // CODE (switch) ( n --> ) unfeasible
227 private void f_s_switch_s() {
228 	asm {		naked;
229 		pop EBX;
230 		mov ECX,dword ptr DS:[EBX];
231 		lea EDX,[EBX+ECX+CELL];
232 		push EDX;
233 		lea EAX,[EAX*4+CELL];
234 		cmp ECX,EAX;
235 		jbe M2;
236 		lea EBX,[EBX+EAX+CELL];
237 		mov EAX,dword ptr SS:[EBP];
238 		lea EBP,[EBP+CELL];
239 		jmp [EBX];
240 M2:		lea EBX,[EBX+CELL];
241 		mov EAX,dword ptr SS:[EBP];
242 		lea EBP,[EBP+CELL];
243 		jmp [EBX];
244 	}
245 }
246 // вернуть значение литерала скомпилированного в коде за (LIT)
247 // CODE (LIT) ( r: addr --> d: n ) unfeasible
248 private void h_s_LIT_s() {
249 	asm {		naked;
250 		pop EBX;
251 		lea EBP,[EBP-CELL];
252 		mov dword ptr SS:[EBP],EAX;
253 		mov EAX,dword ptr DS:[EBX];
254 		lea EBX,[EBX+CELL];
255 		jmp EBX;
256 	}
257 }
258 // вернуть значение литерала двойной длины скомпилированного в коде за (DLIT)
259 // CODE (DLIT) ( r: addr --> d ) unfeasible
260 private void f_s_DLIT_s() {
261 	asm {		naked;
262 		pop EBX;
263 		lea EBP,[EBP-CELL*2];
264 		mov dword ptr SS:[EBP+CELL],EAX;
265 		mov EDX,dword ptr DS:[EBX+CELL];
266 		mov EAX,dword ptr DS:[EBX];
267 		lea EBX,[EBX+CELL*2];
268 		mov dword ptr SS:[EBP],EDX;
269 		jmp EBX;
270 	}
271 }
272 // выполнить переход на адрес, значение которого содержится в коде за (BRANCH)
273 // CODE BRANCH ( r: addr --> ) unfeasible
274 private void f_BRANCH() {
275 	asm {		naked;
276 		pop EBX;
277 		add EBX,dword ptr DS:[EBX];
278 		jmp EBX;
279 	}
280 }
281 // условное ветвление по false, флаговое значение не удаляется
282 // адрес перехода хранится в коде следом за *BRANCH
283 // CODE *BRANCH ( r: addr d: flag --> flag ) unfeasible
284 private void f_Z_BRANCH() {
285 	asm {		naked;
286 		pop EBX;
287 		or EAX,EAX;
288 		jnz M3;
289 		add EBX,dword ptr DS:[EBX];
290 		jmp EBX;
291 M3:		lea EBX,[EBX+CELL];
292 		jmp EBX;
293 	}
294 }
295 // условное ветвление, если флаговое значение меньше нуля
296 // флаговое значение не удаляется с вершины стека данных
297 // адрес перехода хранится в коде следом за -BRANCH
298 // CODE -BRANCH ( r: addr d: flag --> flag ) unfeasible
299 private void f_N_BRANCH() {
300 	asm {		naked;
301 		pop EBX;
302 		cmp EAX,0;
303 		js M4;
304 		add EBX,dword ptr DS:[EBX];
305 		jmp EBX;
306 M4:		lea EBX,[EBX+CELL];
307 		jmp EBX;
308 	}
309 }
310 // условное ветвление, если флаговое значение нуль
311 // адрес перехода хранится в коде следом за ?BRANCH
312 // CODE ?BRANCH ( r: addr d: flag --> ) unfeasible
313 private void f_ZW_BRANCH() {
314 	asm {		naked;
315 		pop EBX;
316 		or EAX,EAX;
317 		mov EAX,dword ptr SS:[EBP];
318 		lea EBP,[EBP+CELL];
319 		jnz M5;
320 		add EBX,dword ptr DS:[EBX];
321 		jmp EBX;
322 M5:		lea EBX,[EBX+CELL];
323 		jmp EBX;
324 	}
325 }
326 // условное ветвление, если флаговое значение отлично от нуля
327 // адрес перехода хранится в коде следом за N?BRANCH
328 // CODE N?BRANCH ( r: addr d: flag --> ) unfeasible
329 private void f_N_ZW_BRANCH() {
330 	asm {		naked;
331 		pop EBX;
332 		or EAX,EAX;
333 		mov EAX,dword ptr SS:[EBP];
334 		lea EBP,[EBP+CELL];
335 		jz M6;
336 		add EBX,dword ptr DS:[EBX];
337 		jmp EBX;
338 M6:		lea EBX,[EBX+CELL];
339 		jmp EBX;
340 	}
341 }
342 
343 // ======================== dtc.f ========================
344 
345 // 01-02-2008 ~mOleg
346 // Copyright [C] 2006-2013 mOleg mOlegg@ya.ru
347 // Процедуры времени выполнения для CONSTANT, VARIABLE, etc.
348 
349 // вернуть адрес данных, следующих в коде за (CREATE)
350 // CODE (CREATE) ( r: addr --> addr )
351 private void f_s_CREATE_s() {
352 	asm {		naked;
353 		lea EBP,[EBP-CELL];
354 		mov dword ptr SS:[EBP],EAX;
355 		pop EAX;
356 		ret;
357 	}
358 }
359 // вернуть содержимое, хранимое в коде за скомпилированным (CONST)
360 // на вершину стека данных
361 // CODE (CONST) ( r: addr --> n )
362 private void f_s_CONST_s() {
363 	asm {		naked;
364 		lea EBP,[EBP-CELL];
365 		mov dword ptr SS:[EBP],EAX;
366 		pop EBX;
367 		mov EAX,dword ptr DS:[EBX];
368 		ret;
369 	}
370 }
371 // извлечь содержимое переменной, находящейся в коде за скомпилированным
372 // (value) (с неким фиксированным смещением, определяемым # методов),
373 // вернуть значение на вершину стека данных
374 // : (value) ( r: addr --> n ) R> [ 2 TOKEN * LIT, ] + @ ;
375 // CODE (value) ( --> n )
376 private void f_s_value_s() {
377 	asm {		naked;
378 		lea EBP,[EBP-CELL];
379 		mov dword ptr SS:[EBP],EAX;
380 		pop EBX;
381 		mov EAX,dword ptr DS:[EBX+10];
382 		ret;
383 	}
384 }
385 // сохранить значение с вершины стека данных в коде за скомпилированным
386 // (store) ( с некоторым фиксированным смещением, определяемым # методов)
387 // : (store) ( r: addr  d: n --> ) R> TOKEN + ! ;
388 // CODE (store) ( n --> )
389 private void f_s_store_s() {
390 	asm {		naked;
391 		pop EBX;
392 		mov dword ptr DS:[EBX+5],EAX;
393 		mov EAX,dword ptr SS:[EBP];
394 		lea EBP,[EBP+4];
395 		ret;
396 	}
397 }
398 // ======================== token.f ========================
399 // \ 22.06.2009 ~mOleg
400 // \ Copyright [C] 2009-2013 mOleg mOlegg@ya.ru
401 // \ работа со скомпилированными токенами
402 
403 // 5 CONSTANT CFL    ( --> cfl# )   \ длинна поля кода
404 private void f_CFL() {
405 	asm {		naked;
406 		call f_s_CONST_s;
407 		add EAX,0x9D000000;
408 		pop EDI;
409 		pop ESP;
410 	}
411 }
412 // ALIAS CFL TOKEN ( --> token# ) \ размер одной ссылки в коде √
413 private void f_TOKEN() {
414 	asm {		naked;
415 		call f_s_CONST_s;
416 		add EAX,0x9D000000;
417 		pop EDI;
418 		pop ESP;
419 	}
420 }
421 // \ вернуть адрес слова, скомпилированного в коде по указанному адресу
422 // : TOKEN@ ( addr --> xt ) DUP 1 + REF@ + TOKEN + ;
423 private void f_TOKEN_get() {
424 	asm {		naked;
425 		call h_DUP;
426 		call h_s_LIT_s;
427 		add dword ptr DS:[EAX],EAX;
428 		add byte ptr DS:[EAX],AL;
429 		call h_PLUS;
430 		call h_getFromAdr;
431 		call h_PLUS;
432 		call f_TOKEN;
433 		call h_PLUS;
434 		ret;
435 	}
436 }
437 // \ заменить значение токена dst на src
438 // : TOKEN! ( src dst --> ) TUCK TOKEN + - SWAP 1 + REF! ;
439 private void f_TOKEN_set() {
440 	asm {		naked;
441 		call h_TUCK;
442 		call f_TOKEN;
443 		call h_PLUS;
444 		call h_MINUS;
445 		call h_SWAP;
446 		call h_s_LIT_s;
447 		add dword ptr DS:[EAX],EAX;
448 		add byte ptr DS:[EAX],AL;
449 		call h_PLUS;
450 		call h_setToAdr;
451 		ret;
452 	}
453 }
454 // ======================== Быстрая арифметика ===============
455 // 1+  ( A -- A+1 )
456 private void h_inc() {
457 	asm {		naked;
458 		inc EAX;
459 		ret;
460 	}
461 }
462 // 1-  ( A -- A-1 )
463 private void h_dec() {
464 	asm {		naked;
465 		dec EAX;
466 		ret;
467 	}
468 }
469 
470 // ======================== marks.f ========================
471 
472 // %  ( A B -- A%B )
473 private void h_ZP() {
474 	asm {		naked;
475 		mov ECX, EAX;
476 		mov EAX, [EBP];
477 		cdq;
478 		idiv ECX;
479 		lea EBP,[EBP+CELL];
480 		mov EAX, EDX;
481 		ret;
482 	}
483 }
484 
485 // /  ( A B -- A/B )
486 private void h_ZD() {
487 	asm {		naked;
488 		mov ECX, EAX;
489 		mov EAX, [EBP];
490 		cdq;
491 		idiv ECX;
492 		lea EBP,[EBP+CELL];
493 		ret;
494 	}
495 }
496 
497 // *  ( A B -- A*B )
498 private void h_ZW() {
499 	asm {		naked;
500 		imul dword ptr SS:[EBP];
501 		lea EBP,[EBP+CELL];
502 		ret;
503 	}
504 }
505 // +  ( A B -- A+B )
506 private void h_PLUS() {
507 	asm {		naked;
508 		add EAX,dword ptr SS:[EBP];
509 		lea EBP,[EBP+CELL];
510 		ret;
511 	}
512 }
513 // -  ( A B -- A-B )
514 private void h_MINUS() {
515 	asm {		naked;
516 		neg EAX;
517 		add EAX,dword ptr SS:[EBP];
518 		lea EBP,[EBP+CELL];
519 		ret;
520 	}
521 }
522 // Выдать размер ячейки (32 разряда)
523 // 4 CONSTANT CELL
524 private void f_CELL() {
525 	asm {		naked;
526 		call f_s_CONST_s;
527 		add AL,0;
528 		add byte ptr DS:[EAX],AL;
529 		pop ESP;
530 	}
531 }
532 //   ALIAS CELL REF  ( --> const ) \ размер ссылки в байтах
533 private void f_REF() {
534 	asm {		naked;
535 		call f_s_CONST_s;
536 		add AL,0;
537 		add byte ptr DS:[EAX],AL;
538 		pop ESP;
539 	}
540 }
541 // \ компилировать ссылку на код
542 // : REF, ( ref --> ) REF PLACE REF! ;
543 private void f_REFzpt() {
544 	asm {		naked;
545 		call f_REF;
546 		call h_PLACE;
547 		call h_setToAdr;
548 		ret;
549 	}
550 }
551 // \ !!! часто используется и по сути выдает смещение от текущего
552 // \ адреса до указанного. Стоит вынести в отдельное слово.
553 // : atod ( addr --> disp ) HERE REF + - ;
554 private void f_atod() {
555 	asm {		naked;
556 		call h_HERE;
557 		call f_REF;
558 		call h_PLUS;
559 		call h_MINUS;
560 		ret;
561 	}
562 }
563 // \ разрешить ссылку вперед(в коде)
564 // \ : >resolve ( addr --> ) HERE OVER - REF - SWAP ! ;
565 private void f_R_RESOLVE() {
566 	asm {		naked;
567 		call h_HERE;
568 		call h_OVER;
569 		call h_MINUS;
570 		call f_REF;
571 		call h_MINUS;
572 		call h_SWAP;
573 		call h_setToAdr;
574 		ret;
575 	}
576 }
577 // \ разрешить ссылку(в коде, то есть в поле данных команды JMP или CALL) назад
578 // : <resolve ( addr --> ) atod REF, ;
579 private void f_L_resolve() {
580 	asm {		naked;
581 		call f_atod;
582 		call f_REFzpt;
583 		ret;
584 	}
585 }
586 // \ запомнить положение для ссылки вперед
587 // : >MARK ( --> addr ) HERE REF - ;
588 private void f_R_MARK() {
589 	asm {		naked;
590 		call h_HERE;
591 		call f_REF;
592 		call h_MINUS;
593 		ret;
594 	}
595 }
596 // \ заполнить положение для ссылки назад
597 // : <MARK ( --> addr ) HERE ;
598 private void f_L_MARK() {
599 	asm {		naked;
600 		call h_HERE;
601 		ret;
602 	}
603 }
604 // : <RESOLVE ( addr --> ) HERE - REF, ;
605 private void f_L_RESOLVE() {
606 	asm {		naked;
607 		call h_HERE;
608 		call h_MINUS;
609 		call f_REFzpt;
610 		ret;
611 	}
612 }
613 // : RESOLVE> ( addr --> ) HERE OVER - SWAP ! ;
614 private void f_RESOLVE_R() {
615 	asm {		naked;
616 		call h_HERE;
617 		call h_OVER;
618 		call h_MINUS;
619 		call h_SWAP;
620 		call h_setToAdr;
621 		ret;
622 	}
623 }
624 // 26-06-2005 ~mOleg
625 // Copyright [C] 2005-2013 mOleg mOlegg@ya.ru
626 // стековые манипуляции
627 
628 // -- стек данных ------------------------------------------------------------
629 
630 // установить новое значение указателя стека данных
631 //  SP! ( addr --> )
632 private void SP_set() {
633 	asm {		naked;
634     	lea  EBP,	[EAX+CELL];
635     	mov  EAX,	[EBP-CELL];
636     	ret;
637 	}	
638 }
639 // прочесть на вершину стека текущее значение указателя стека данных
640 // SP@ ( --> addr )
641 private void SP_get() {
642 	asm {		naked;
643 		lea EBP,	[EBP-CELL];
644 		mov [EBP],	EAX;
645 		mov EAX,	EBP;
646 		ret;
647 	}	
648 }
649 //       USER S0 ( --> addr ) \ ячейка хранит адрес дна стека данных
650 
651 // -- Стек возвратов ---------------------------------------------------------
652 // установить новое значение указателя стека возвратов
653 // RP! ( addr --> )
654 private void RP_set() {
655 	asm {		naked;
656 		pop EBX;			// адрес куда надо вернутся
657 		mov ESP,	EAX;
658 		mov EAX,	[EBP];
659 		lea EBP,	[EBP+CELL];
660 		jmp EBX;
661 	}
662 }
663 // прочесть на вершину стека данных текущее значение указателя стека возвратов
664 // RP@ ( --> addr )
665 private void RP_get() {
666 	asm {		naked;
667 		lea EBP,	[EBP-CELL];
668 		mov [EBP],	EAX;
669 		lea EAX,	[ESP+CELL];
670 		ret;
671 	}
672 }
673 //       USER R0   ( --> addr ) \ ячейка хранит адрес дна стека возвратов
674 // -- локальный стек ------------------------------------------------------------
675 // установить новое значение указателя стека данных
676 // LP! ( addr --> )
677 private void LP_set() {
678 	asm {		naked;
679 		mov ESI,	EAX;
680 		mov EAX,	[EBP];
681 		lea EBP,	[EBP+CELL];
682 		ret;
683 	}
684 }
685 // прочесть на вершину стека текущее значение указателя стека данных
686 // LP@ ( --> addr )
687 private void LP_get() {
688 	asm {		naked;
689 		lea EBP,	[EBP-CELL];
690 		mov [EBP],	EAX;
691 		mov EAX,	ESI;
692 		ret;
693 	}
694 }
695 //       USER L0   ( --> addr ) \ хранит адрес дна локального стека
696 // 26-06-2005 ~mOleg
697 // Copyright [C] 2006-2013 mOleg mOlegg@ya.ru
698 // манипуляция данными на стеке данных - псевдоассемблер
699 
700 // Продублировать верхнее значение на вершине стека данных.
701 // DUP ( n --> n n )
702 private void h_DUP() {
703 	asm {		naked;
704 		lea EBP,	[EBP-CELL];
705 		mov [EBP],	EAX;
706 		ret;
707 	}
708 }
709 // Убрать верхнее значение со стека данных.
710 // DROP ( n --> )
711 private void h_DROP() {
712 	asm {		naked;
713 		mov EAX,	[EBP];
714 		lea EBP,	[EBP+CELL];
715 		ret;
716 	}
717 }
718 // поменять местами два верхних элемента стека
719 // SWAP ( a b --> b a )
720 private void h_SWAP() {
721 	asm {		naked;
722 		mov EDX,	[EBP];
723 		mov [EBP],	EAX;
724 		mov EAX,	EDX;
725 		ret;
726 	}
727 }
728 // Положить копию x1 на вершину стека.
729 // OVER ( a b --> a b a )
730 private void h_OVER() { 
731 	asm {		naked;
732 		lea EBP, [EBP-CELL];
733 		mov dword ptr SS:[EBP],	EAX;
734 		mov EAX, dword ptr SS:[EBP+CELL];
735 		ret;
736 	}
737 }
738 // Убрать первый элемент под вершиной стека.
739 // NIP ( a b --> b )
740 private void SP_nip() {
741 	asm {		naked;
742 		lea EBP,	[EBP+CELL];
743 		ret;
744 	}
745 }
746 // Прокрутить три верхних элемента стека.
747 // ROT ( a b c --> b c a )
748 private void SP_rot() {
749 	asm {		naked;
750 		mov EDX,	[EBP];
751 		mov [EBP],	EAX;
752 		mov EAX,	[EBP+CELL];
753 		mov [EBP+CELL],	EDX;
754 		ret;
755 	}
756 }
757 // Прокрутить три верхних элемента стека.
758 //  -ROT ( a b c --> c a b )
759 private void SP_minusrot() {
760 	asm {		naked;
761 		mov EDX,	[EBP+CELL];
762 		mov [EBP+CELL],	EAX;
763 		mov EAX,	[EBP];
764 		mov [EBP],	EDX;
765 		ret;
766 	}
767 }
768 // Положить копию верхнего элемента стека под следующий за ним.
769 // TUCK ( a b --> b a b )
770 private void h_TUCK() {
771 	asm {		naked;
772 		lea EBP,	[EBP-CELL];
773 		mov EDX,	[EBP+CELL];
774 		mov [EBP+CELL],	EAX;
775 		mov [EBP],	EDX;
776 		ret;
777 	}
778 }
779 // Сделать копию верхней пары элементов стека данных
780 // DDUP ( d --> d d )
781 private void SP_ddup() {
782 	asm {		naked;
783 		mov EDX,	[EBP];
784 		mov [EBP-CELL],	EAX;
785 		mov [EBP-CELL*2],	EDX;
786 		lea EBP,	[EBP-CELL*2];
787 		ret;
788 	}
789 }
790 // Убрать со стека пару ячеек x1 x2.
791 // DDROP ( d --> )
792 private void SP_ddrop() {
793 	asm {		naked;
794 		mov EAX,	[EBP+CELL];
795 		lea EBP,	[EBP+CELL*2];
796 		ret;
797 	}
798 }
799 // Удалить с вершины стека данных три верхних ячейки
800 // TDROP ( n n n --> )
801 private void SP_tdrop() {
802 	asm {		naked;
803 		mov EAX,	[EBP+CELL*2];
804 		lea EBP,	[EBP+CELL*3];
805 		ret;
806 	}
807 }
808 // Поменять местами две верхние пары ячеек.
809 // DSWAP ( da db --> db da )
810 private void SP_dswap() {
811 	asm {		naked;
812 		mov EDX,	[EBP];
813 		mov EBX,	[EBP+CELL];
814 		mov ECX,	[EBP+CELL*2];
815 		mov [EBP+CELL*2],	EDX;
816 		mov [EBP+CELL],	EAX;
817 		mov [EBP],	ECX;
818 		mov EAX,	EBX;
819 		ret;
820 	}
821 }
822 // 26-06-2005 ~mOleg
823 // Copyright [C] 2005-2013 mOleg mOlegg@ya.ru
824 // манипуляция числами на стеке возвратов
825 
826 // прочесть верхнее значение со стека возвратов
827 // R@ ( r: n --> r: n d: n )
828 private void h_R_get() {
829 	asm {		naked;
830 		lea EBP,	[EBP-CELL];
831 		mov [EBP],	EAX;
832 		mov EAX,	[ESP+CELL];
833 		ret;
834 	}
835 }
836 // Удалить одно значение с вершины стека возвратов
837 // RDROP ( r: n --> )
838 private void SR_rdrop() {
839 	asm {		naked;
840 		pop EBX;
841 		pop EDX;
842 		jmp EBX;
843 	}
844 }
845 // Перенести значение со стека данных на стек возвратов
846 // >R ( d: n --> r: n )
847 private void h_toR() {
848 	asm {		naked;
849 		pop EBX;
850 		push EAX;
851 		mov EAX,	[EBP];
852 		lea EBP,	[EBP+CELL];
853 		jmp EBX;
854 	}
855 }
856 // Перенести значение со стека возвратов на стек данных
857 // R> ( r: n --> d: n )
858 private void h_Rto() {
859 	asm {		naked;
860 		lea EBP,	[EBP-CELL];
861 		mov [EBP],	EAX;
862 		pop EBX;
863 		pop EAX;
864 		jmp EBX;
865 	}
866 }
867 // Добавить значение к тому, что лежит на вершине стека возвратов
868 // R+ ( r: a d: b --> R: a+b )
869 private void h_R_PLUS() {
870 	asm {		naked;
871 		pop EBX;
872 		add [ESP],	EAX;
873 		mov EAX,	[EBP];
874 		lea EBP,	[EBP+CELL];
875 		jmp EBX;
876 	}
877 }
878 // Поместить значение 0 на вершину ст возвратов, вернуть адрес значения
879 // 0>R' ( --> r: 0 d: RP@ )
880 private void SR_SR_0toRadr() {
881 	asm {		naked;
882 		mov EBX,	[ESP];
883 		mov [ESP],	0;
884 		lea EBP,	[EBP-CELL];
885 		mov [EBP],	EAX;
886 		mov EAX,	ESP;
887 		jmp EBX;
888 	}
889 }
890 // Перенести два значения на стек возвратов со стека данных
891 // D>R ( D: x1 x2 --> R: --> x1 x2 )
892 private void SR_DtoR() {
893 	asm {		naked;
894 		pop EBX;
895 		push [EBP];
896 		push EAX;
897 		lea EBP,	[EBP+CELL*2];
898 		mov EAX,	[EBP-CELL];
899 		jmp EBX;
900 	}
901 }
902 // Вернуть два значения со стека возвратов на стек данных
903 // CODE DR> ( r: d --> D: d )
904 private void SR_DRfrom() {
905 	asm {		naked;
906 		mov EBX,	[ESP];
907 		mov [EBP-CELL],	EAX;
908 		mov EDX, [ESP+CELL*2];
909 		mov EAX, [ESP+CELL];
910 		mov [EBP-CELL*2], EDX;
911 		lea EBP, [EBP-CELL*2];  // замечание: выделять место на стеке нужно до 
912 		lea ESP, [ESP+CELL*3];  // того, как туда будут положены значения 
913 		jmp EBX;
914 	}
915 }
916 // Прочитать на вершину стека данных два верхних значения со ст возвратов
917 // DR@ ( r: d --> r: d d: d )
918 private void SR_DRrazm() {
919 	asm {		naked;
920 		mov [EBP-CELL],EAX;
921 		mov EAX, [ESP+CELL];
922 		mov EDX, [ESP+CELL*2];
923 		mov [EBP-CELL*2],EDX;
924 		lea EBP, [EBP-8];
925 		ret;
926 	}
927 }
928 
929 // локальный стек данных
930 
931 // прочитать значение с вершины локального стека
932 // L@ ( l: n --> l: n d: n )
933 private void SL_get() {
934 	asm {		naked;
935 		lea EBP, [EBP-CELL];
936 		mov [EBP], EAX;
937 		mov EAX, [ESI];
938 		ret;
939 	}
940 }
941 // прибавить значение с вершины стека данных, к значению на вершине локального стека
942 // L+ ( l: a d: b --> l: a+b )
943 private void SL_add() {
944 	asm {		naked;
945 		add [ESI], EAX;
946 		mov EAX, [EBP];
947 		lea EBP, [EBP+CELL];
948 		ret;
949 	}
950 }
951 // переместить значение на вершину локального стека
952 // >L ( d: n --> l: n )
953 private void SL_toL() {
954 	asm {		naked;
955 		lea ESI, [ESI-CELL];
956 		mov [ESI], EAX;
957 		mov EAX, [EBP];
958 		lea EBP, [EBP+CELL];
959 		ret;
960 	}
961 }
962 // переместить значение с вершины локального стека
963 // L> ( l: n --> d: n )
964 private void SL_Lfrom() {
965 	asm {		naked;
966 		lea EBP, [EBP-CELL];
967 		mov [EBP], EAX;
968 		mov EAX, [ESI];
969 		lea ESI, [ESI+CELL];
970 		ret;
971 	}
972 }
973 // дублировать значение на вершине локального стека
974 // LDUP ( l: n --> l: n n )
975 private void SL_Ldup() {
976 	asm {		naked;
977 		mov EDX, [ESI];
978 		lea ESI, [ESI-CELL];
979 		mov [ESI], EDX;
980 		ret;
981 	}
982 }
983 // удалить элемент с вершины локального стека
984 // LDROP ( l: n --> )
985 private void SL_Ldrop() {
986 	asm {		naked;
987 		lea ESI, [ESI+4];
988 		ret;
989 	}
990 }
991 
992 // Процедуры времени выполнения для CONSTANT, VARIABLE, etc.
993 
994 // Записать значение по адресу
995 // ! ( x a-addr --> )
996 private void h_setToAdr() {
997 	asm {		naked;
998 		mov EDX, dword ptr SS:[EBP];
999 		mov dword ptr DS:[EAX], EDX;
1000 		mov EAX, dword ptr SS:[EBP+CELL];
1001 		lea EBP, [EBP+CELL*2];
1002 		ret;
1003 	}
1004 }
1005 // Прочитать значение по адресу
1006 // @ ( a-addr --> x )
1007 private void h_getFromAdr() {
1008 	asm {		naked;
1009 		mov EAX,  DS:[EAX];
1010 		ret;
1011 	}
1012 }
1013 // Получить byte по адресу c-addr.
1014 // Незначащие старшие биты ячейки нулевые.
1015 // B@ ( c-addr --> byte )
1016 private void h_getFromAdrByte() {
1017 	asm {		naked;
1018 		movzx EAX, byte ptr DS:[EAX];
1019 		ret;
1020 	}
1021 }
1022 // Записать byte по адресу a-addr.
1023 // CODE B! ( byte c-addr --> )
1024 private void h_setToAdrByte() {
1025 	asm {		naked;
1026 		mov EDX,  SS:[EBP];
1027 		mov byte ptr DS:[EAX],DL;
1028 		mov EAX, dword ptr SS:[EBP+CELL];
1029 		lea EBP,[EBP+CELL*2];
1030 		ret;
1031 	}
1032 }
1033  
1034 // ======================== Проверочные слова ======================= 
1035 
1036 // Проверим передачу 3 параметров
1037 private void t9( int a, int b, int c) {
1038 	writeln();
1039 	writeln("~~~~> ", "3 param ", a, "  ", b, "  ", c);
1040 }
1041 private void exec_D() {
1042 	asm {		naked;
1043 		// mov EAX, 7;		call t1;  // t1(7);
1044 		call h_DUP;
1045 		push 2;
1046 		push 3;
1047 		mov EAX, 4;
1048 		// mov EAX, 4;
1049 		lea ECX, t9;
1050 		call ECX;
1051 		// call t9;
1052 		call h_DROP;
1053 		ret;
1054 	}
1055 } 
1056 
1057 // int qwe = 7;
1058 
1059 // Вызов внешних функций
1060 // ( .... Af -- ... ) 
1061 private void callD() {
1062 	asm {		naked;
1063 		mov ECX, EAX;		// Адрес функции, для вызова CALL
1064 		pop EAX;			// Забираем адрес возврата из callD
1065 		call SL_toL;		// Прячем его во временный стек
1066 		call h_DUP;
1067 		call ECX;			// Вызов функции по адресу
1068 		mov ECX, EAX;		// Сохраним Return
1069 		call SL_Lfrom;		// Вернем с доп стека в EAX адрес возврата из callD
1070 		push EAX;			// Вернем его на место
1071 		mov EAX, ECX;
1072 		ret;
1073 	}
1074 }
1075 // LATEST ( -- Aexec)
1076 // Выдать на стек данных F адрес CFA последнего изготовленного слова
1077 private void* d_LATEST() { return &(gpcb.latest);  }
1078 private void  h_LATEST() {
1079 	asm {	naked;	call h_DUP;  call d_LATEST; ret; }
1080 }
1081 // CONTEXT ( -- Alfa)
1082 // Выдать на стек данных F адрес NFA последнего изготовленного слова. С этого
1083 // адреса можно перебрать всю цепочку слов в словаре
1084 private void* d_CONTEXT() { return &(gpcb.context); }
1085 private void  h_CONTEXT() {
1086 	asm {	naked;	call h_DUP;  call d_CONTEXT; ret; }
1087 }
1088 //  TIB ( -- Atib)
1089 // Выдать на стек данных F адрес буфера, в котором содержится исходная строка форта
1090 // для текстового разбора словом WORD
1091 private void* d_TIB() { return &(gpcb.Tib); }
1092 private void  h_TIB() {
1093 	asm {	naked;	call h_DUP;  call d_TIB; ret; }
1094 }
1095 //  <IN ( -- A)
1096 // Выдать на стек данных F адрес (позицию) того места, в строковом буфере, откуда
1097 // будет начинаться поиск след слова (лексемы) словом WORD
1098 private void* d_IN() { return &(gpcb.In); }
1099 private void  h_IN() {
1100 	asm {	naked;	call h_DUP;  call d_IN; ret; }
1101 }
1102 //  dlTib ( -- N )
1103 // Выдать на стек данных размер строки в TIB
1104 private void* d_dlTib() { return cast(void*)gpcb.dlTib; }
1105 private void  h_dlTib() {
1106 	asm {	naked;	call h_DUP;  call d_dlTib; ret; }
1107 }
1108 // ALLOT ( n -- )
1109 // Зарезервировать в кодофайле n байт, под собственные нужды
1110 private void d_ALLOT(int n) {	gpcb.here = gpcb.here + n; }
1111 private void h_ALLOT() {
1112 	asm {	naked;	call d_ALLOT;	call h_DROP;	ret; }
1113 }
1114 // HERE ( -- Ahere)
1115 // Выдать позицию в кодофайле, куда будут записываться новые определяемые слова
1116 private void* d_HERE() { return gpcb.here; }
1117 private void  h_HERE() {
1118 	asm {	naked;	call h_DUP;  call d_HERE;	ret; }
1119 }
1120 // STATE ( -- Ahere)
1121 // Выдать состояние переменной, показывающий в компиляции или интерпретации сейчас 
1122 // мы находимся. TRUE=компиляция, FALSE=интерпретация
1123 private void* d_STATE() { return &gpcb.state; }
1124 private void  h_STATE() {
1125 	asm {	naked;	call h_DUP;  call d_STATE;	ret; }
1126 }
1127 // COMMONADR ( -- A )
1128 // Выдать указатель на начало общей таблицы CommonAdr
1129 private void* d_COMMONADR() { return gpcb.adrCommonTable; }
1130 private void  h_COMMONADR() {
1131 	asm {	naked;	call h_DUP;  call d_COMMONADR;	ret; }
1132 }
1133 // : PLACE ( # --> addr ) HERE SWAP ALLOT ;
1134 // Указатель на начало "дырки" свободной области в кодофайле
1135 private void h_PLACE() {	   
1136 	asm {		naked;
1137 		call h_HERE;
1138 		call h_SWAP;
1139 		call h_ALLOT;
1140 		ret;
1141 	}
1142 }
1143 // Зарезервировать одну ячейку в области данных и поместить x в эту ячейку.
1144 // : , ( x --> ) CELL PLACE ! ;
1145 private void h_zpt() {	   
1146 	asm {		naked;
1147 		// int 3;
1148 		call h_DUP; mov EAX, CELL;
1149 		call h_PLACE;
1150 		call h_setToAdr;
1151 		ret;
1152 	}
1153 }
1154 // Зарезервировать одну ячейку в области данных и поместить x в эту ячейку.
1155 // : B, ( x --> ) 1 PLACE B! ;
1156 private void h_Bzpt() {	   
1157 	asm {		naked;
1158 		call h_DUP; mov EAX, 1;
1159 		call h_PLACE;
1160 		call h_setToAdrByte;
1161 		ret;
1162 	}
1163 }
1164 
1165 // -------------------------- compie.f -------------------------
1166 
1167 // \ 31-01-2007 ~mOleg
1168 // \ Copyright [C] 1992-1999 A.Cherezov ac@forth.org
1169 // \ Компиляция.
1170 
1171 // \ скомпилировать адрес следующего токена в текущее определение
1172 // \ классический не-immediate вариант. Не работает со immediate словами
1173 // : COMPILE ( r: addr --> ) AR@ TOKEN@ TOKEN R+ COMPILE, ;
1174 private void h_COMPILE() {	   
1175 	asm {		naked;
1176 		call h_R_get;			// R@
1177 		call f_TOKEN_get;
1178 		call f_TOKEN;
1179 		call h_R_PLUS;
1180 		call h_COMPILEzpt;
1181 		ret;
1182 	}
1183 }
1184 // скомпилировать инструкцию INT3
1185 // : INT3, ( --> ) 0xCC B, ;
1186 private void h_INT3zpt() {	   
1187 	asm {		naked;
1188 		call h_DUP; mov EAX, 0xCC;
1189 		call h_Bzpt;
1190 		ret;
1191 	}
1192 }
1193 // скомпилировать инструкцию RET
1194 // : RET, ( --> ) 0xC3 B, ;
1195 private void h_RETzpt() {	   
1196 	asm {		naked;
1197 		call h_DUP; mov EAX, 0xC3;
1198 		call h_Bzpt;
1199 		ret;
1200 	}
1201 }
1202 // скомпилировать инструкцию CALL √
1203 // : CALL, ( --> ) 0xE8 B, ;
1204 private void h_CALLzpt() {	   
1205 	asm {		naked;
1206 		call h_DUP; mov EAX, 0xE8;
1207 		call h_Bzpt;
1208 		ret;
1209 	}
1210 }
1211 // \ компилировать вызов указанного xt √
1212 // : COMPILE, ( xt --> ) CALL, <resolve ;
1213 private void h_COMPILEzpt() {	   
1214 	asm {		naked;
1215 		call h_CALLzpt;
1216 		call f_L_resolve;
1217 		ret;
1218 	}
1219 }
1220 // \ компилировать безусловный переход на указанный адрес √
1221 // : JUMP, ( addr --> )  0xE9 B, <resolve ;
1222 private void h_JUMPzpt() {	   
1223 	asm {		naked;
1224 		call h_DUP; mov EAX, 0xE9;
1225 		call h_Bzpt;
1226 		call f_L_resolve;
1227 		ret;
1228 	}
1229 }
1230 // ???????????????????????? Возможно ошибочное словл
1231 // \ компилировать код, возвращающий число в текущее определение
1232 // : LIT, ( N --> ) COMPILE (LIT) , ;
1233 private void h_LITzpt() {	   
1234 	asm {		naked;
1235 	call h_COMPILE;
1236 	call h_s_LIT_s;
1237 	call h_zpt;
1238 	}
1239 }
1240 // Шитое слово TRUE
1241 // -1 CONSTANT TRUE
1242 private void f_TRUE() {	   
1243 	asm {		naked;
1244 		call f_s_CONST_s;
1245 		di 0xFFFFFFFF;
1246 	}
1247 }
1248 // Шитое слово TRUE
1249 // -1 CONSTANT TRUE
1250 private void f_FALSE() {	   
1251 	asm {		naked;
1252 		call f_s_CONST_s;
1253 		di 0x0;
1254 	}
1255 }
1256 // Шитое слово BL
1257 // 32 CONSTANT BL
1258 private void f_BL() {	   
1259 	asm {		naked;
1260 		call f_s_CONST_s;
1261 		di 0x20;
1262 	}
1263 }
1264 // CODE [ - начать интерпретацию
1265 private void h_COMP_OFF() {
1266 	asm {		naked;
1267 		call f_FALSE;
1268 		call h_STATE;
1269 		call h_setToAdr;
1270 		ret;
1271 	}
1272 }
1273 // CODE ] - начать компиляцию
1274 private void h_COMP_ON() {
1275 	asm {		naked;
1276 		call f_TRUE;
1277 		call h_STATE;
1278 		call h_setToAdr;
1279 		ret;
1280 	}
1281 }
1282 // DUMP ( A -- ) Распечатать указанный адрес
1283 void h_zz(pp adr) {
1284 	writeln("- dump -- dump -- dump -- dump -- dump -");
1285 	dumpAdr(adr);
1286 	writeln("- dump -- dump -- dump -- dump -- dump -");
1287 }
1288 void h_dump() {
1289 	asm {	naked;
1290 		call h_zz;
1291 		call h_DROP;
1292 		ret;
1293 	}
1294 }
1295 // Выдать тип OS W - windows,  L - Linux
1296 void h_osname() {
1297 version(Windows) {
1298 	asm {	naked;
1299 		call h_DUP;
1300 		mov EAX, 87;
1301 		ret;
1302 	}
1303 }
1304 version(linux) {
1305 	asm {	naked;
1306 		call h_DUP;
1307 		mov EAX, 76;
1308 		ret;
1309 	}
1310 }
1311 }
1312 // Вернуть на стек адрес функции LoadLibrary
1313 pp h_LoadLibrary() {
1314 	pp rez;
1315 	version(Windows) {
1316 		import core.sys.windows.windows: LoadLibraryA ;
1317 		rez = cast(pp)&LoadLibraryA;
1318 	}
1319 	return rez;
1320 }
1321 void f_LoadLibraryA() {
1322 	asm {	naked;
1323 		call h_DUP;
1324 		call h_LoadLibrary;
1325 		ret;
1326 	}
1327 }
1328 pp h_DlOpen() {
1329 	pp rez;
1330 	version(linux) {   
1331 		import core.sys.posix.dlfcn;  // Определения dlopen() и dlsym()
1332 		rez = cast(pp)&dlopen;
1333 	}
1334 	return rez;
1335 }
1336 void f_DlOpen() {
1337 	asm {	naked;
1338 		call h_DUP;
1339 		call h_DlOpen;
1340 		ret;
1341 	}
1342 }
1343 // Вернуть на стек адрес функции GetProcAdres
1344 pp h_GetPrAdressA() {
1345 	pp rez;
1346 	version(Windows) {
1347 		import core.sys.windows.windows: GetProcAddress ;
1348 		rez = cast(pp)&GetProcAddress;
1349 	}
1350 	return rez;
1351 }
1352 void f_GetPrAdressA() {
1353 	asm {	naked;
1354 		call h_DUP;
1355 		call h_GetPrAdressA;
1356 		ret;
1357 	}
1358 }
1359 pp h_DlSym() {
1360 	pp rez;
1361 	version(linux) {   
1362 		import core.sys.posix.dlfcn;  // Определения dlopen() и dlsym()
1363 		rez = cast(pp)&dlsym;
1364 	}
1365 	return rez;
1366 }
1367 void f_DlSym() {
1368 	asm {	naked;
1369 		call h_DUP;
1370 		call h_DlSym;
1371 		ret;
1372 	}
1373 }
1374 
1375 
1376 // use std.stdout instead of std.c.stdio.stdout 
1377 
1378 pp h_getSTDOUT() {
1379     // Linux
1380 	import core.stdc.stdio;
1381 	return cast(pp)(core.stdc.stdio.stdout);
1382 }
1383 // Выдать на стек стандартный указатель на stdout
1384 void getSTDOUT() {
1385 	asm {	naked;
1386 		call h_DUP;
1387 		call h_getSTDOUT;
1388 		ret;
1389 	}
1390 }
1391 
1392 // TYPE ( A -- ) Распечатать строку на консоли
1393 void h_TYPE(ps adr) {
1394 	printf("%s", adr); stdout.flush();
1395 }
1396 void f_TYPE() {
1397 	asm {	naked;
1398 		call h_TYPE;
1399 		call h_DROP;
1400 		ret;
1401 	}
1402 }
1403 
1404 // CMOVE ( Afrom Ato N -- ) Скопировать байты
1405 private void h_bmove(int n, ps to, ps from) {
1406 	import core.stdc..string : memcpy;
1407 	memcpy(to, from, n);
1408 }
1409 private void f_bmove() {
1410 	asm {	naked;
1411 		push EAX;
1412 		call h_DROP;
1413 		push EAX;
1414 		call h_DROP;
1415 		call h_bmove;
1416 		call h_DROP;
1417 		ret;
1418 	}
1419 }
1420 
1421 // Странное слово. Понятно, что вызывается, когда есть ветка с исключительной
1422 // ситуацией. По идее, должно проинформировать и корректно прервать 
1423 // программу
1424 private void h_THROW(int n) {
1425 	writeln();
1426 	writeln("[", n, "]", " THROW - error, this mast find ...");
1427 }
1428 private void f_THROW() {
1429 	asm {	naked;	call h_THROW;  call h_DROP;	ret; }
1430 }
1431 // ?COMP - разрешено только при компиляции 
1432 // private void f_ZNW_COMP() {
1433 //	asm {	naked;	call h_DUP; mov EAX, 1; call h_THROW;	ret; }
1434 // }
1435 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1436 
1437 // Обработка вызова слота Slot_A_N_v
1438 // Выдать адрес обработчика для вызова функции с параметрами A и N
1439 private void* h_A_CALL_AN() { return &executeForth_A_N; }
1440 // Forth слово: выдать адрес обработчика для вызова функции с параметрами A и N
1441 private void  f_A_CALL_AN() {
1442 	asm {	naked;
1443 		call h_DUP;
1444 		call h_A_CALL_AN;
1445 		ret;
1446 	}
1447 }
1448 // Обработка вызова слота Slot_A_N_v
1449 extern (C) void executeForth_A_N(pp adr, int n) {
1450 	writeln("this is Call from C witch adr = ", adr, "  n = ", n);
1451 	executeForth(adr, 1, n);
1452 }
1453 
1454 // Выполнить адрес через EXECUTE
1455 extern (C) pp executeForth(pp adrexec, uint kolPar, ...) {
1456 	pp Adr_execD = gpcb.executeFromD;  // ' EXECUTEFROMD
1457 	// writeln("Adr_execD = ", Adr_execD, "   adrexec = ", adrexec, "  kolPar = ", kolPar);
1458 	pp ret;  			// Место под возвращаемое значение
1459 	NPcb npcb = gpcb;   // Возможность работы с PCB (контекст) переменными в ASM
1460 	pp adrKolPar = cast(pp)&kolPar;  // Адрес количества параметров
1461 	asm {
1462 		align 4;
1463 		// Сохраним регистры D
1464 		push EBX; push ESI; push EAX; push ECX; push EDX; push EBP; 
1465 		// --------------------
1466 		// Запишем наши параметры
1467 		push Adr_execD;				// Адрес xt специальный слова в Forth (EXECUTEFROMD)
1468 		push adrexec;				// Адпес слова Forth которое будет выполнено из EXECUTEFROMD
1469 		push adrKolPar; 			// Адрес количества параметров для передачи в Форт
1470 		// Востановим регитры F
1471 		mov EAX, npcb.saveEAX.offsetof[npcb];
1472 		mov ESI, npcb.saveESI.offsetof[npcb];
1473 		mov EDI, npcb.saveEDI.offsetof[npcb];
1474 		mov EBP, npcb.saveEBP.offsetof[npcb];
1475   		call h_DUP;					// Сохраним то что было на вершине стека Форта
1476 		pop  EAX;					// На веншину SD количество пораметров
1477   		call h_DUP;					// Сохраним,освободив вершину SD
1478 		pop  EAX;					// На веншину SD адрес вызываемого слова
1479   		call h_DUP;					// Сохраним,освободив вершину SD
1480 		pop  EAX;					// На вершине SD адрес EXECUTEFROMD
1481 		call f_EXECUTE;				// Вызов EXECUTEFROMD
1482 		// mov EBX, EAX;				// Сохранить возвращаемое значение
1483 		mov ret, EAX;
1484 		call h_DROP;				// Выкинуть со стека в форте, так как вызов внешний 
1485 		
1486 		// Сохраним F
1487 		mov ECX, EBP;
1488 		pop EBP;
1489 		mov npcb.saveEAX.offsetof[npcb], EAX;
1490 		mov npcb.saveEBP.offsetof[npcb], ECX;  // Сохраним запомненный EBP
1491 		mov npcb.saveESI.offsetof[npcb], ESI;
1492 		mov npcb.saveEDI.offsetof[npcb], EDI;
1493 		// mov ret, EBX;
1494 		// ----------------------
1495 		// Восстановим регистры D
1496 		pop EDX; pop ECX; pop EAX; pop ESI; pop EBX;
1497 	}
1498 	gpcb.saveEBP = npcb.saveEBP;   // Возможность работы с PCB (контекст) переменными в ASM
1499 	gpcb.saveEAX = npcb.saveEAX;   // Возможность работы с PCB (контекст) переменными в ASM
1500 	gpcb.saveESI = npcb.saveESI;   // Возможность работы с PCB (контекст) переменными в ASM
1501 	gpcb.saveEDI = npcb.saveEDI;   // Возможность работы с PCB (контекст) переменными в ASM
1502 	return ret;
1503 }
1504 
1505 void evalForth(char *str) {
1506 	evalForth(to!string(str));
1507 }
1508 void evalForth(string str) {
1509 	// Linux корректировка
1510 	if(str.length>0 && str[$-1]==13) str.length = str.length-1;
1511 
1512 	gpcb.dlTib = str.length;		// Запишем длину строки в gpcb
1513 	gpcb.In = cast(ps)gpcb.Tib;     // указатель смещения во входном буфере
1514 	for(int i; i != str.length; i++) tib[i] = cast(ubyte)str[i];
1515 	NPcb npcb = gpcb;   // Возможность работы с PCB (контекст) переменными в ASM
1516 	asm {
1517 		align 4;
1518 		// Сохраним регистры D
1519 		push EBX; push ESI; push EAX; push ECX; push EDX; push EBP; 
1520 		// --------------------
1521 		// Востановим регитры F
1522 //		int 3;
1523 		mov EAX, npcb.saveEAX.offsetof[npcb];
1524 		mov ESI, npcb.saveESI.offsetof[npcb];
1525 		mov EDI, npcb.saveEDI.offsetof[npcb];
1526 		mov EBP, npcb.saveEBP.offsetof[npcb];
1527 		
1528 		call f_inter;
1529 		
1530 		// Сохраним F
1531 		mov ECX, EBP;
1532 		pop EBP;
1533 		mov npcb.saveEAX.offsetof[npcb], EAX;
1534 		mov npcb.saveEBP.offsetof[npcb], ECX;  // Сохраним запомненный EBP
1535 		mov npcb.saveESI.offsetof[npcb], ESI;
1536 		mov npcb.saveEDI.offsetof[npcb], EDI;
1537 		// ----------------------
1538 		// Восстановим регистры D
1539 		pop EDX; pop ECX; pop EAX; pop ESI; pop EBX;
1540 	}
1541 	gpcb.saveEBP = npcb.saveEBP;   // Возможность работы с PCB (контекст) переменными в ASM
1542 	gpcb.saveEAX = npcb.saveEAX;   // Возможность работы с PCB (контекст) переменными в ASM
1543 	gpcb.saveESI = npcb.saveESI;   // Возможность работы с PCB (контекст) переменными в ASM
1544 	gpcb.saveEDI = npcb.saveEDI;   // Возможность работы с PCB (контекст) переменными в ASM
1545 }
1546 // Записать в общую таблицу адрес adr в ячейку с номером n
1547 void setCommonAdr(int n, pp adr) {	commonTable[n] = adr; }
1548 // Прочитать из общий таблицы адрес в ячейке n
1549 pp getCommonAdr(int n) {	return commonTable[n];  }
1550 // Инициализировать Forth и подготовить его к работе
1551 
1552 void initForth() {
1553 	kdf = cast(pb)(new uint[sizeCodeFile]).ptr;		// Изготовим кодофайл на sizeCodeFile адр
1554 	NPcb npcb = gpcb;
1555 	npcb.adrCommonTable = cast(pp)commonTable.ptr;
1556 	const sizeSt = sizeStack; 				// По sizeStack CELL на каждый стек
1557 	// uint[sizeSt] stSD, stSR, stSL;  		// Память под стеки
1558 	stSD = cast(pp)(new uint[sizeSt]);		// Запомнить начало области SP в глобальной переменной
1559 	npcb.csd = stSD + sizeSt - 1;			// Запомнить вершину стека SP в контексте
1560 	
1561 	// npcb.csr = cast(pp)stSR[sizeSt-1]; ---> Совмещен со стеком D
1562 	stSL = cast(pp)(new uint[sizeSt]);		// Запомнить начало SP в глобальной переменной
1563 	npcb.csc = stSL + sizeSt - 1;			// Запомнить вершину SL в контексте
1564 	
1565 	npcb.here = kdf;						// HERE на начало буфера
1566 	npcb.context = cast(pp)kdf;				// Вектор context лежит в начале кодофайла
1567 	npcb.akdf = cast(pp)kdf;				// Указатель на кодофайл
1568 	npcb.Tib = cast(ps)&tib;				// Указатель на входной буфер текста
1569 	// npcb._Tib = cast(ps)&_tib;	// ??? не используется // Указатель на входной буфер WORD
1570 	npcb.In = cast(ps)&tib;                 // указатель смещения во входном буфере
1571 	asm {
1572 		align 4;
1573 		// Сохраним регистры D
1574 		push EBX; push ESI; push EAX; push ECX;	push EDX; push EBP; 
1575 		// --------------------
1576 		// В ESI запомним указатель на доп стек SL
1577 		lea EAX, npcb.csc.offsetof[npcb]; 
1578 		mov ESI,  DS:[EAX];
1579 		// Из контекста возьмем указатель на стек данных ...
1580 		lea EAX, npcb.csd.offsetof[npcb]; 
1581 		mov EAX,  DS:[EAX];
1582 		call SP_set; // ... и инициализируем его
1583 		mov EAX, ESI;	call LP_set; // Стек дополнительный для Форк
1584 		// Сохраним F
1585 		mov ECX, EBP;
1586 		pop EBP;
1587 		mov npcb.saveEAX.offsetof[npcb], EAX;
1588 		mov npcb.saveEBP.offsetof[npcb], ECX;  // Сохраним запомненный EBP
1589 		mov npcb.saveESI.offsetof[npcb], ESI;
1590 		mov npcb.saveEDI.offsetof[npcb], EDI;
1591 		// ----------------------
1592 		// Восстановим регистры D
1593 		pop EDX; pop ECX; pop EAX; pop ESI; pop EBX;
1594 	}
1595 	// writeln("Local PCB: ", npcb);
1596 	gpcb = npcb;
1597 	// Надо выделить 256 CELL для хранения цепочек context
1598 	pb u = gpcb.here; for(int i; i != (256 * CELL); i++) *u = 0; 
1599 	gpcb.here = gpcb.here + (256 * CELL);
1600 	// Перенесём сюда определение HARD слов
1601 	CreateVocItem(cast(char*)"\3EXD".ptr, 		cast(pp)&exec_D, 		&gpcb.context);
1602 	CreateVocItem(cast(char*)"\6CALL_A".ptr,cast(pp)&callD, 			&gpcb.context);
1603 	CreateVocItem(cast(char*)"\7CONTEXT".ptr, 	cast(pp)&h_CONTEXT,		&gpcb.context);
1604 	CreateVocItem(cast(char*)"\4JUMP".ptr, 		cast(pp)&f_JUMP,		&gpcb.context);
1605 	CreateVocItem(cast(char*)"\4EXIT".ptr, 		cast(pp)&f_EXIT,		&gpcb.context);
1606 	CreateVocItem(cast(char*)"\3NIP".ptr, 		cast(pp)&SP_nip,		&gpcb.context);
1607 	CreateVocItem(cast(char*)"\3ROT".ptr, 		cast(pp)&SP_rot,		&gpcb.context);
1608 	CreateVocItem(cast(char*)"\4-ROT".ptr, 		cast(pp)&SP_minusrot,	&gpcb.context);
1609 	CreateVocItem(cast(char*)"\3D>R".ptr, 		cast(pp)&SR_DtoR,		&gpcb.context);
1610 	CreateVocItem(cast(char*)"\3DR>".ptr, 		cast(pp)&SR_DRfrom,		&gpcb.context);
1611 //	CreateVocItem(cast(char*)"\5?COMP".ptr, 	cast(pp)&f_ZNW_COMP,		&gpcb.context);
1612 
1613 	CreateVocItem(cast(char*)"\6OSNAME".ptr, 	cast(pp)&h_osname,		&gpcb.context);
1614 
1615 	CreateVocItem(cast(char*)"\10(STDOUT)".ptr, 		cast(pp)&getSTDOUT, 	&gpcb.context);
1616 
1617 	CreateVocItem(cast(char*)"\14LOADLIBRARYA".ptr, 	cast(pp)&f_LoadLibraryA,	&gpcb.context);
1618 	CreateVocItem(cast(char*)"\10GPADRESS".ptr, 	cast(pp)&f_GetPrAdressA,	&gpcb.context);
1619 	CreateVocItem(cast(char*)"\6DLOPEN".ptr, 	cast(pp)&f_DlOpen,		&gpcb.context);
1620 	CreateVocItem(cast(char*)"\5DLSYM".ptr, 	cast(pp)&f_DlSym,		&gpcb.context);
1621 
1622 	CreateVocItem(cast(char*)"\2L@".ptr, 		cast(pp)&SL_get,		&gpcb.context);
1623 	CreateVocItem(cast(char*)"\2L+".ptr, 		cast(pp)&SL_add,		&gpcb.context);
1624 	CreateVocItem(cast(char*)"\2>L".ptr, 		cast(pp)&SL_toL,		&gpcb.context);
1625 	CreateVocItem(cast(char*)"\2L>".ptr, 		cast(pp)&SL_Lfrom,		&gpcb.context);
1626 	CreateVocItem(cast(char*)"\4LDUP".ptr, 		cast(pp)&SL_Ldup,		&gpcb.context);
1627 	CreateVocItem(cast(char*)"\5LDROP".ptr, 	cast(pp)&SL_Ldrop,		&gpcb.context);
1628 	
1629 	CreateVocItem(cast(char*)"\3SP!".ptr, 		cast(pp)&SP_set, 		&gpcb.context);
1630 	CreateVocItem(cast(char*)"\3SP@".ptr, 		cast(pp)&SP_get, 		&gpcb.context);
1631 	CreateVocItem(cast(char*)"\3RP!".ptr, 		cast(pp)&RP_set, 		&gpcb.context);
1632 	CreateVocItem(cast(char*)"\3RP@".ptr, 		cast(pp)&RP_get, 		&gpcb.context);
1633 	CreateVocItem(cast(char*)"\3LP!".ptr, 		cast(pp)&LP_set, 		&gpcb.context);
1634 	CreateVocItem(cast(char*)"\3LP@".ptr, 		cast(pp)&LP_get, 		&gpcb.context);
1635 //	CreateVocItem(cast(char*)"\2T5".ptr, 		cast(pp)&t5, 			&gpcb.context);
1636 //	CreateVocItem(cast(char*)"\3TCW".ptr, 		cast(pp)&TestCompileWord, &gpcb.context);
1637 	CreateVocItem(cast(char*)"\5INT3,".ptr, 		cast(pp)&h_INT3zpt,		&gpcb.context);
1638 	CreateVocItem(cast(char*)"\3<IN".ptr, 		cast(pp)&h_IN,			&gpcb.context);
1639 	CreateVocItem(cast(char*)"\3TIB".ptr, 		cast(pp)&h_TIB,			&gpcb.context);
1640 	CreateVocItem(cast(char*)"\5DLTIB".ptr, 	cast(pp)&h_dlTib,		&gpcb.context);
1641 	CreateVocItem(cast(char*)"\4NOOP".ptr, 		cast(pp)&f_NOOP,		&gpcb.context);
1642 
1643 	CreateVocItem(cast(char*)"\4DUMP".ptr, 		cast(pp)&h_dump, 		&gpcb.context);
1644 	CreateVocItem(cast(char*)"\5RDROP".ptr, 	cast(pp)&SR_rdrop,	&gpcb.context);
1645 	CreateVocItem(cast(char*)"\5DDROP".ptr, 	cast(pp)&SP_ddrop,	&gpcb.context);
1646 	CreateVocItem(cast(char*)"\4DDUP".ptr, 	    cast(pp)&SP_ddup,	&gpcb.context);
1647 	CreateVocItem(cast(char*)"\2>R".ptr, 		cast(pp)&h_toR, 		&gpcb.context);
1648 	CreateVocItem(cast(char*)"\2R>".ptr, 		cast(pp)&h_Rto, 		&gpcb.context);
1649 	CreateVocItem(cast(char*)"\2R+".ptr, 		cast(pp)&h_R_PLUS, 		&gpcb.context);
1650 	CreateVocItem(cast(char*)"\2R@".ptr, 		cast(pp)&h_R_get, 		&gpcb.context);
1651 	CreateVocItem(cast(char*)"\2B,".ptr, 		cast(pp)&h_Bzpt, 		&gpcb.context);
1652 	CreateVocItem(cast(char*)"\4REF,".ptr, 		cast(pp)&f_REFzpt, 		&gpcb.context);
1653 	
1654 	CreateVocItem(cast(char*)"\5(LIT)".ptr, 	cast(pp)&h_s_LIT_s, 	&gpcb.context);
1655 	CreateVocItem(cast(char*)"\4RET,".ptr,  	cast(pp)&h_RETzpt,  	&gpcb.context);
1656 	// ????? CreateVocItem(cast(char*)"\4LIT,".ptr,  	cast(pp)&h_LITzpt,  	&gpcb.context);
1657 	CreateVocItem(cast(char*)"\4TUCK".ptr, 		cast(pp)&h_TUCK, 		&gpcb.context);
1658 	CreateVocItem(cast(char*)"\3DUP".ptr, 		cast(pp)&h_DUP, 		&gpcb.context);
1659 	CreateVocItem(cast(char*)"\4SWAP".ptr, 		cast(pp)&h_SWAP, 		&gpcb.context);
1660 	CreateVocItem(cast(char*)"\4DROP".ptr, 		cast(pp)&h_DROP, 		&gpcb.context);
1661 	CreateVocItem(cast(char*)"\4OVER".ptr, 		cast(pp)&h_OVER, 		&gpcb.context);
1662 	CreateVocItem(cast(char*)"\1+".ptr, 		cast(pp)&h_PLUS, 		&gpcb.context);
1663 	CreateVocItem(cast(char*)"\1*".ptr, 		cast(pp)&h_ZW, 			&gpcb.context);
1664 	CreateVocItem(cast(char*)"\1/".ptr, 		cast(pp)&h_ZD, 			&gpcb.context);
1665 	CreateVocItem(cast(char*)"\1%".ptr, 		cast(pp)&h_ZP, 			&gpcb.context);
1666 	CreateVocItem(cast(char*)"\1-".ptr, 		cast(pp)&h_MINUS, 		&gpcb.context);
1667 	CreateVocItem(cast(char*)"\1=".ptr, 		cast(pp)&f_RAWNO, 		&gpcb.context);
1668 	CreateVocItem(cast(char*)"\2<>".ptr, 		cast(pp)&f_NRAWNO, 		&gpcb.context);
1669 	CreateVocItem(cast(char*)"\1<".ptr, 		cast(pp)&f_MENSHE, 		&gpcb.context);
1670 	CreateVocItem(cast(char*)"\1>".ptr, 		cast(pp)&f_BOLSHE, 		&gpcb.context);
1671 
1672 	CreateVocItem(cast(char*)("\2" ~ "1+").ptr, 		cast(pp)&h_inc, 		&gpcb.context);
1673 	CreateVocItem(cast(char*)("\2" ~ "1-").ptr, 		cast(pp)&h_dec, 		&gpcb.context);
1674 
1675 	CreateVocItem(cast(char*)"\4CELL".ptr, 		cast(pp)&f_CELL, 		&gpcb.context);
1676 	CreateVocItem(cast(char*)"\3REF".ptr, 		cast(pp)&f_REF, 		&gpcb.context);
1677 	CreateVocItem(cast(char*)"\5PLACE".ptr, 	cast(pp)&h_PLACE, 		&gpcb.context);
1678 	CreateVocItem(cast(char*)"\5ALLOT".ptr, 	cast(pp)&h_ALLOT, 		&gpcb.context);
1679 
1680 	// ========== Странные шитые слова =============
1681 	CreateVocItem(cast(char*)"\4TRUE".ptr, 		cast(pp)&f_TRUE, 		&gpcb.context);
1682 	CreateVocItem(cast(char*)"\5FALSE".ptr, 	cast(pp)&f_FALSE, 		&gpcb.context);
1683 	CreateVocItem(cast(char*)"\5STATE".ptr, 	cast(pp)&h_STATE, 		&gpcb.context);
1684 	CreateVocItem(cast(char*)"\3IMM".ptr, 		cast(pp)&f_getIMM, 		&gpcb.context);
1685 	CreateVocItem(cast(char*)"\2BL".ptr, 		cast(pp)&f_BL, 			&gpcb.context);
1686 	CreateVocItem(cast(char*)"\3CFL".ptr, 		cast(pp)&f_CFL, 		&gpcb.context);
1687 	CreateVocItem(cast(char*)"\5TOKEN".ptr, 	cast(pp)&f_TOKEN, 		&gpcb.context);
1688 	CreateVocItem(cast(char*)"\6TOKEN@".ptr, 	cast(pp)&f_TOKEN_get,	&gpcb.context);
1689 	CreateVocItem(cast(char*)"\6TOKEN!".ptr, 	cast(pp)&f_TOKEN_set,	&gpcb.context);
1690 	CreateVocItem(cast(char*)"\4WORD".ptr, 		cast(pp)&f_word,		&gpcb.context);
1691 	CreateVocItem(cast(char*)"\4FIND".ptr, 		cast(pp)&f_find,		&gpcb.context);
1692 	CreateVocItem(cast(char*)"\4HERE".ptr, 		cast(pp)&h_HERE,		&gpcb.context);
1693 	CreateVocItem(cast(char*)"\6NUMBER".ptr, 	cast(pp)&h_NUMBER,		&gpcb.context);
1694 	CreateVocItem(cast(char*)"\11COMMONADR".ptr,cast(pp)&h_COMMONADR,	&gpcb.context);
1695 	CreateVocItem(cast(char*)"\1.".ptr,		 	cast(pp)&h_tck,			&gpcb.context);
1696 	CreateVocItem(cast(char*)"\1[".ptr,		 	cast(pp)&h_COMP_OFF,	&gpcb.context, 1);
1697 	CreateVocItem(cast(char*)"\1]".ptr,		 	cast(pp)&h_COMP_ON,		&gpcb.context);
1698 	CreateVocItem(cast(char*)"\1:".ptr,		 	cast(pp)&h_dwoetoc,		&gpcb.context);
1699 	CreateVocItem(cast(char*)"\1;".ptr,		 	cast(pp)&h_tckzpt,		&gpcb.context, 1);
1700 	
1701 	// ========== kernel\vm\STC\BASE\memory.f =============
1702 	CreateVocItem(cast(char*)"\1@".ptr, 		cast(pp)&h_getFromAdr,	&gpcb.context);
1703 	CreateVocItem(cast(char*)"\1!".ptr, 		cast(pp)&h_setToAdr,	&gpcb.context);
1704 	CreateVocItem(cast(char*)"\2B@".ptr, 		cast(pp)&h_getFromAdrByte,	&gpcb.context);
1705 	CreateVocItem(cast(char*)"\2B!".ptr, 		cast(pp)&h_setToAdrByte,&gpcb.context);
1706 	CreateVocItem(cast(char*)"\5BMOVE".ptr, 	cast(pp)&f_bmove,       &gpcb.context);
1707 
1708 	// ========== List words for call from C++ QtE5 =============
1709 	CreateVocItem(cast(char*)"\11A_CALL_AN".ptr, cast(pp)&f_A_CALL_AN,  &gpcb.context);
1710 	
1711 	// ========== kernel\vm\STC\BASE\  ...... ============= ссылки
1712 	CreateVocItem(cast(char*)"\5>MARK".ptr, 	cast(pp)&f_R_MARK,		&gpcb.context);
1713 	CreateVocItem(cast(char*)"\5<MARK".ptr, 	cast(pp)&f_L_MARK,		&gpcb.context);
1714 	CreateVocItem(cast(char*)"\10<RESOLVE".ptr, cast(pp)&f_L_RESOLVE,	&gpcb.context);
1715 	CreateVocItem(cast(char*)"\10RESOLVE>".ptr, cast(pp)&f_RESOLVE_R,	&gpcb.context);
1716 	CreateVocItem(cast(char*)"\7?BRANCH".ptr, 	cast(pp)&f_ZW_BRANCH,	&gpcb.context);
1717 	CreateVocItem(cast(char*)"\6BRANCH".ptr, 	cast(pp)&f_BRANCH,		&gpcb.context);
1718 	CreateVocItem(cast(char*)"\6LATEST".ptr, 	cast(pp)&h_LATEST,		&gpcb.context);
1719 	CreateVocItem(cast(char*)"\5THROW".ptr, 	cast(pp)&f_THROW,		&gpcb.context);
1720 	
1721 	// ========== Компиляция =============
1722 	CreateVocItem(cast(char*)"\1,".ptr, 		cast(pp)&h_zpt, 		&gpcb.context);
1723 	CreateVocItem(cast(char*)"\5JUMP,".ptr, 		cast(pp)&h_JUMPzpt,		&gpcb.context);
1724 	CreateVocItem(cast(char*)"\10COMPILE,".ptr, cast(pp)&h_COMPILEzpt, 	&gpcb.context);
1725 	CreateVocItem(cast(char*)"\7COMPILE".ptr, 	cast(pp)&h_COMPILE, 	&gpcb.context);
1726 	CreateVocItem(cast(char*)"\10(CREATE)".ptr, cast(pp)&f_s_CREATE_s, 	&gpcb.context);
1727 
1728 	CreateVocItem(cast(char*)"\6CREATE".ptr, 	cast(pp)&h_CREATE, 		&gpcb.context);
1729 	CreateVocItem(cast(char*)"\7EXECUTE".ptr, 	cast(pp)&f_EXECUTE, 	&gpcb.context);
1730 
1731 	CreateVocItem(cast(char*)"\4TYPE".ptr, 		cast(pp)&f_TYPE, 		&gpcb.context);
1732 	CreateVocItem(cast(char*)"\10INCLUDED".ptr, cast(pp)&f_INCLUDED, 	&gpcb.context);
1733 
1734 /* 	// Проверим вектор context
1735 	pp[256]* vect = cast(pp[256]*)gpcb.context;
1736 	
1737 	writeln((*vect));
1738 	asm { int 3; }
1739  */	//    *cast(pp)(gpcb.here) = (*vect)[b1b];		// запись LFA, то что лежало в ячейке vect[69]
1740 
1741 	
1742 	// Работа со словарной статьёй
1743 	evalForth(": C@ B@ ; : C! B! ; : CFA>NFA DUP 6 - C@ 8 + - ; : CFA>LFA CELL - ; : NFA>LFA DUP C@ DUP + + ; : LFA>NFA CELL + CFA>NFA ; : NFA>CFA NFA>LFA CELL + ;");
1744 	// Классический Immediate
1745 	evalForth(": IMMEDIATE 1 LATEST @ 1 CELL + - B! ;");
1746 	// Create Does> - классика
1747 	evalForth(": (JOIN) R> LATEST @ TOKEN! ; : (DOES) R> R> SWAP EXECUTE ; : DOES>  COMPILE (JOIN) COMPILE (DOES) ; IMMEDIATE");
1748 	// Constant и Variable
1749 	evalForth(": CONST CREATE COMPILE (CREATE) , DOES> @ ; : VAR CREATE 0 COMPILE (CREATE) , DOES> ;");
1750 	// Комментарий
1751 	evalForth(r": \  TIB @ DLTIB + <IN ! ; IMMEDIATE : // TIB @ DLTIB + <IN ! ; IMMEDIATE");
1752 	// IF ELSE THEN
1753 	evalForth(": IF COMPILE ?BRANCH CELL ALLOT >MARK ; IMMEDIATE");
1754 	evalForth(": ELSE COMPILE BRANCH CELL ALLOT >MARK SWAP RESOLVE> ; IMMEDIATE");
1755 	evalForth(": THEN RESOLVE> ; IMMEDIATE");
1756 	// BEGIN WHILE UNTIL
1757 	evalForth(": BEGIN <MARK ; IMMEDIATE : WHILE COMPILE ?BRANCH CELL ALLOT >MARK ; IMMEDIATE");
1758 	evalForth(": REPEAT COMPILE BRANCH SWAP <RESOLVE RESOLVE> ; IMMEDIATE");
1759 	evalForth(": UNTIL COMPILE ?BRANCH <RESOLVE ; IMMEDIATE");
1760 	// Работа с символами
1761 	// LITERAL ( n --> \\ --> n ) I (ни чего не делать), С (закомпилировать код выкл на стек)
1762 	evalForth(": LIT, COMPILE (LIT) , ; : LITERAL STATE @ IF LIT, THEN ; : [CHAR] BL WORD 1+ C@ LITERAL ; IMMEDIATE");
1763 	evalForth(": ' BL WORD DUP IF FIND DUP IF ELSE 3 THROW DROP THEN ELSE 2 THROW DROP THEN ;");
1764 	// ['] Найти xt идущего следом слова и закомпилировать его в новое определение
1765 	// BOX -  обойти данные в коде, начинающиеся со следующей ячейки, вернуть адрес начала данных
1766 	evalForth(": ['] ' LIT, ; IMMEDIATE : (BOX) R@ DUP B@ 2 + R+ ;");
1767 	evalForth(`: S" [CHAR] " STATE @ IF COMPILE (BOX) WORD ELSE WORD DUP THEN B@ 2 + ALLOT ; IMMEDIATE`);
1768 	// Работа с векторами
1769 	// VECT ( / name --> ) Создать слово, которое передаёт управление по JMP на NOOP
1770 	evalForth(": VECT CREATE ['] NOOP JUMP, ;");
1771 	// LITERAL ( n --> \\ --> n ) I (ни чего не делать), С (закомпилировать код выкл на стек)
1772 	// evalForth(": LITERAL STATE @ IF LIT, THEN ;");
1773 	// REGULAR ( xt --> ) I (исполнить слово), С (закомпилировать в определение)
1774 	evalForth(": REGULAR STATE @ IF COMPILE, ELSE EXECUTE THEN ; : CELLS CELL * ; ");
1775 	// IS ( xt / name --> ) Присвоить значение вектору, HAS ( / name --> xt ) получить значение вектора
1776 	evalForth(": IS ' LITERAL ['] TOKEN! REGULAR ; : HAS ' LITERAL ['] TOKEN@ REGULAR ;");
1777 	// COMMONADR@ ( n -- Value ) Значение в ячейке n общй таблицы. COMMONADR! ( Value n -- ) Запись значения в ячейку n
1778 	evalForth(": COMMONADR! CELL * COMMONADR + ! ; : COMMONADR@ CELL * COMMONADR + @ ;");
1779 	evalForth(": IF=W OSNAME 76 = IF TIB @ DLTIB + <IN ! THEN ; IMMEDIATE");
1780 	evalForth(": IF=L OSNAME 87 = IF TIB @ DLTIB + <IN ! THEN ; IMMEDIATE");
1781 	evalForth(": NOT IF FALSE ELSE TRUE THEN ;");
1782 	// Проверить, что мы в режиме компиляции или интерпретации
1783 	evalForth(": ?COMP STATE NOT IF 1 THROW THEN ; : ?EXEC STATE IF 2 THROW THEN ;");
1784 	// ( -- ) Забрать из потока слово немедленного исполнения и закомпилировать его
1785 	evalForth(": [COMPILE] ?COMP ' COMPILE, ; IMMEDIATE");
1786 	// Счетный цикл 10 0 DO .. I .. LOOP - 10 раз от 0 до 9 - в любом случае 1 раз выполнение
1787 	// Для работы использует стек L
1788 	evalForth(": (DO) SWAP >L >L ; : DO COMPILE (DO) <MARK ; IMMEDIATE : I L@ ;");
1789 	evalForth(": (LOOP) L> 1+ L> DDUP < NOT IF DDROP TRUE ELSE >L >L FALSE THEN ;");
1790 	evalForth(": LOOP COMPILE (LOOP) COMPILE ?BRANCH <RESOLVE ; IMMEDIATE");
1791 	evalForth(": (+LOOP) L> + L> DDUP < NOT IF DDROP TRUE ELSE >L >L FALSE THEN ;");
1792 	evalForth(": +LOOP COMPILE (+LOOP) COMPILE ?BRANCH <RESOLVE ; IMMEDIATE");
1793 	
1794 	// EXECUTEFROMD ( Aколпарамтровcpp Aсловафорта -- Rez ) Выполнить из D слово по EXECUTE
1795 	evalForth(": EXECUTEFROMD >R DUP @ BEGIN DUP WHILE DDUP CELL * + @ -ROT 1- REPEAT DDROP R> EXECUTE ;");
1796 	gpcb.executeFromD = gpcb.latest; // Сохраним адрес EXECUTEFROMD
1797 	
1798 }
1799 // CODE WORD ( Rz -- A/0) Выдать адрес на начало следующей лексемы в формате
1800 // \4ABCD\0\4 Причем эта лексема находится по адресу HERE
1801 private ps h_word(char rz) {
1802 	// Указатель на TIB
1803 	ps adr = null;
1804 	ps uTib = cast(char*)gpcb.Tib;
1805 	ps uIn = cast(char*)gpcb.In;
1806 	int dlTib = gpcb.dlTib;
1807 	ps maxTib = uTib + dlTib - 1;			// Это максимальный знак в Tib
1808 	// Строка сейчас кладется в специальный бцфер _Tib
1809 	// ps _tib = gpcb._Tib + 1;
1810 	// Строка кладется по HERE
1811 	ps _tib = cast(ps)gpcb.here + 1;
1812 	
1813 	int kps;
1814 	// dumpAdr(cast(pp)uIn);
1815 	for(;;) {
1816 		// writeln();
1817 		//writeln("[", *uIn,"]  uIn+1 = ", *(uIn+1), "   *uIn = ", cast(ubyte)*uIn);
1818 		//writeln(uIn, " ~ ", maxTib);
1819 		
1820  		if(uIn > maxTib+1) { adr = null; goto en; }
1821 		if((*uIn == rz) || (uIn > maxTib)) {
1822 			if(adr != null) { 
1823 				*_tib++ = 0;  
1824 				// *gpcb._Tib = cast(char)kps;  // Это если в буфер
1825 				*gpcb.here = cast(char)kps;     // Это если в HERE
1826 				*_tib =  cast(char)kps;  
1827 				gpcb.In = ++uIn;  
1828 				// adr = gpcb._Tib;				// Это если в буфер
1829 				adr = cast(ps)gpcb.here;		// Это если HERE
1830 				goto en;	
1831 			}
1832 		}
1833 		else {
1834 			if(adr == null)  adr = _tib;
1835 			*_tib++ = *uIn;
1836 			kps++;
1837 		}
1838 	uIn++;
1839 	}
1840 en:	
1841 	return adr;
1842 }
1843 void f_word() {
1844 	asm {	naked;
1845 		call h_word;
1846 		ret;
1847 	}
1848 }
1849 // CODE FIND - ( Astr -- Acfa/0 ) Найти в словаре CFA (если не нашли, то 0) 
1850 private ps h_find(ps s) {
1851 	char* str = s;
1852 	// printf("\n Start find [%s]  STATE = %d\n", s+1, gpcb.state);
1853 	ps _nfa; pp[256]* vect; ubyte b1b;
1854 	// Надо проверить, может это число? Если строго в строке одни цифры, то пропускаем и не ищем в словаре
1855 	char* ss = s + 1; bool isNoDig = false;
1856 	for(; *ss != 0; ss++) { if( !((*ss > 47) && (*ss < 58)) ) { isNoDig = true;  break; }  }
1857 	if(!isNoDig) { goto kn; }
1858 
1859 	// Тут надо подумать. В этот момент context ulfa показывает на вектор
1860 	b1b = *(s + 1); 					// смещение в векторе context
1861 	vect = cast(pp[256]*)gpcb.context;
1862     // *cast(pp)(gpcb.here) = (*vect)[b1b];		// запись LFA, то что лежало в ячейке vect[69]
1863 	// было ---> ps _nfa = cast(ps)gpcb.context;
1864 	_nfa = cast(ps)(*vect)[b1b];
1865 	for(;;) {
1866 		kolPer++;
1867 		if(_nfa == null) goto kn;
1868 		// printf("{%s}", _nfa+1);
1869 		if(cmpString(str, _nfa) == 1) {
1870 			// вычислим CFA
1871 			// ps cfa = _nfa + (*_nfa + 8);
1872 			// надо установвть глобальный признак IMM
1873 			gpcb.imm = *(_nfa + (*_nfa + 3));
1874 			// printf("-----> {%s}", _nfa+1);
1875 			return _nfa + (*_nfa + 8);
1876 		}
1877 		else {
1878 			_nfa = _nfa + (*_nfa + 4); 			// Перейти на lfa
1879 			_nfa = cast(ps)(*cast(pp)_nfa);
1880 		}
1881 	}
1882 kn:	
1883 	return null;
1884 }
1885 private void f_find() {
1886 	asm {	naked;
1887 		call h_find;
1888 		ret;
1889 	}
1890 }
1891 // CODE IMM ( -- N ) Выдать на стек байт IMM для анализа
1892 private pp h_getIMM() {
1893 	return cast(pp)gpcb.imm;
1894 }
1895 // Выдать IMM на стек
1896 private void f_getIMM() {
1897 	asm {	naked;
1898 		call h_DUP;
1899 		call h_getIMM;
1900 		ret;
1901 	}
1902 }
1903 // Первый (простейший) интерпретатор на основе asm
1904 private void f_inter() {
1905 	asm {	naked;
1906 //		int 3;
1907 //		mov EAX, 7;				// По этой 7 будем контролировать стек   ... 7
1908 ms:		call f_BL;				// Положить на стек 32 0x20;             ... 7 32
1909 		call f_word;
1910 		mov ECX, 0;
1911 		cmp EAX, ECX;
1912 		je me;					// WORD выдало 0 - входной поток исчерпан
1913 		call f_find;            // 7 Адр_найденного_слова 
1914 		mov ECX, 0;
1915 		cmp EAX, ECX;
1916 		je m2;					// FIND не нашло слово в словаре
1917 		call h_STATE;			// 7 Адр_найденного_слова STATE
1918 		call h_getFromAdr;
1919 		mov ECX, 0;
1920 		cmp EAX, ECX;
1921 		call h_DROP;
1922 		je m1;					// Мы в интерпретации и идем на выполнение слова
1923 		call f_getIMM;			// Мы в компиляции, проверим IMM
1924 		mov ECX, 1;
1925 		cmp EAX, ECX;
1926 		call h_DROP;			// сбросим IMM со стека
1927 		je m1;					// Мы в икомпиляции и идем на компилирование
1928 		// int 3;
1929 		call h_COMPILEzpt;		// Закомпилируем вызов этого слова
1930 		jmp ms;					// Начинаем всё сначала
1931 m1:		call f_EXECUTE;         // Выполним слово
1932 		jmp ms;					// Начинаем всё сначала
1933 m2:		call h_DROP;			// Сбросим 0 после не найденного Find слова
1934 		call h_HERE;			// слово не найдено, может это цифра?
1935 		call h_NUMBER;			// попытка преобразовать в число
1936 		// call f_TRUE;
1937 		mov ECX, 0;
1938 		cmp EAX, ECX;
1939 		call h_DROP;			// сбросим возврат NUMBER
1940 		je ms;					// ----> ошибка прочто пропустив в потоке
1941 		call h_STATE;			// 7 Число STATE
1942 		call h_getFromAdr;
1943 		mov ECX, 0;
1944 		cmp EAX, ECX;
1945 		call h_DROP;
1946 		je ms;					// 7 Число
1947 		call h_COMPILE;
1948 		call h_s_LIT_s;
1949 		call h_zpt;
1950 		jmp ms;
1951 me:		call h_DROP;
1952 		ret;
1953 	}
1954 }
1955 // Слово NUMBER. Задача, положить на стек число 32 разр знаковое
1956 private int number(ps str) {
1957 	import std.conv;
1958 	int rez = 0;
1959 	// dumpAdr(cast(pp)str);
1960 	try {
1961 		rez = to!int(to!string(str+1));
1962 	}
1963 	catch {
1964 		writeln("Error conv [", to!string(str+1) ,"] --> Integer");
1965 	}
1966 	// printf("\nInput str = [%s], Output rez = [%d]\n", str, rez);
1967 	return rez;
1968 }
1969 private void h_NUMBER() {
1970 	asm {	naked;
1971 		// call h_DUP;
1972 		call number;
1973 		call f_TRUE;
1974 		ret;
1975 	}
1976 }
1977 
1978 private void tck(int n) {
1979 	writeln(n);
1980 }
1981 private void h_tck() {
1982 	asm {	naked;
1983 		call tck;
1984 		call h_DROP;
1985 		ret;
1986 	}
1987 }
1988 // Часть слова CREATE ( Astr -- ) Создаёт словарную статью, с LFA, но дальше ни чего не делает
1989 // т.к. дальнейшее изготовление кода последует позже
1990 private void h_createST(ps name) {
1991  	pb con_tmp  = gpcb.here;						// Контекст на начало Here
1992 	// Нужно обойти имя
1993 	gpcb.here = gpcb.here +(*gpcb.here + 3); 		// Перейти на Imm
1994     *gpcb.here = cast(ubyte)0;						// запись Immediate
1995     gpcb.here = gpcb.here + 1;						// обходим Imm
1996 	
1997 	// Тут надо подумать. В этот момент context ulfa показывает на вектор
1998 	ubyte b1b = *(name + 1); 					// смещение в векторе context
1999 	pp[256]* vect = cast(pp[256]*)gpcb.context;
2000 	// printf("[%s]  - %d\n", name, b1b);
2001 	// writeln("[", cast(string)name ,"] b1b = ", b1b);
2002     *cast(pp)(gpcb.here) = (*vect)[b1b];		// запись LFA, то что лежало в ячейке vect[69]
2003 	
2004     // *cast(pp)(gpcb.here) = cast(pp)gpcb.context;	// запись LFA
2005 	// gpcb.context = cast(pp)con_tmp;
2006 	(*vect)[b1b] = cast(pp)con_tmp; 			// фактически управляем Context
2007 
2008     gpcb.here = gpcb.here + CELL;					// обходим LFA
2009 }
2010 private void h_CREATE() {
2011 	asm {	naked;
2012 		call f_BL;				// Положить на стек 32 0x20;             ... 7 32
2013 		call f_word;
2014 		call h_createST;
2015 		call h_DROP;
2016 		call h_HERE;
2017 		call h_LATEST;
2018 		call h_setToAdr;
2019 		ret;
2020 	}
2021 }
2022 // CODE : ( слово читает имя из входного потока и создаёт новое слово )
2023 private void h_dwoetoc() {
2024 	asm {	naked;
2025 		call h_CREATE;
2026 		call h_COMP_ON;
2027 		ret;
2028 	}
2029 }
2030 // CODE ; ( заканчивает компиляцию слова начатаю : )
2031 private void h_tckzpt() {
2032 	asm {	naked;
2033 		call h_RETzpt;
2034 		call h_COMP_OFF;
2035 		ret;
2036 	}
2037 }
2038 // Создаёт список в кодофайле начальных "hard" слов
2039 private void CreateVocItem(ps name, pp ucfa, p ulfa, ubyte imm=0) {
2040   //   ps name - указатель на строку со счетчиком (имея,типа \4gena)
2041   //   p ucfa - указатель на процедуру C++
2042   //   p ulfa - указатель на предыдущее слово ( NFA )
2043   //   unsigned char imm - признак Immediate (+1=немедленная,0=обычная)
2044   
2045 	import core.stdc..string : memcpy;
2046 	// import asc1251;
2047 	// Отладочное слово
2048 /* 	writeln(toCON("Начало кодофайла = "), cast(uint)gpcb.akdf);
2049 	writeln(toCON("НERE показывает  = "), cast(uint)gpcb.here);
2050 	writeln(toCON("ulfa показывает  = "), cast(uint)*cast(pp)ulfa);
2051  */		
2052 	pb con_tmp  = gpcb.here;					// Контекст на начало Here
2053 	int dlina = *name;							// Запомним длину имени созд слова
2054 	memcpy(gpcb.here, name, dlina+1);    		// копируем строку по Here
2055     // uprString(ps(mTakeHERE));				// конвертируем в большие быквы
2056     gpcb.here = gpcb.here + dlina + 1;			// сдвигаем Here за имя
2057     *gpcb.here = 0;                				// Пишем 0
2058     gpcb.here = gpcb.here + 1;					// обходим 0
2059     *gpcb.here = cast(ubyte)dlina;				// запись длины имени
2060     gpcb.here = gpcb.here + 1;					// обходим длину имени
2061     *gpcb.here = cast(ubyte)imm;				// запись Immediate
2062     gpcb.here = gpcb.here + 1;					// обходим Imm
2063 
2064 	// Тут надо подумать. В этот момент context ulfa показывает на вектор
2065 	ubyte b1b = *(con_tmp + 1); 					// смещение в векторе context
2066 	pp[256]* vect = cast(pp[256]*)gpcb.context;
2067     *cast(pp)(gpcb.here) = (*vect)[b1b];		// запись LFA, то что лежало в ячейке vect[69]
2068 
2069 	// (*vect)[b1b] = cast(pp)7;
2070 	// writeln(toCON("Записываю в (*vect)[b1b] по адресу = "), cast(uint)&((*vect)[b1b]), "     b1b = ", b1b);
2071 	
2072 	
2073     gpcb.here = gpcb.here + CELL;				// обходим LFA
2074 	gpcb.latest = cast(pp)(gpcb.here);			// запомним LATEST
2075     *gpcb.here = cast(ubyte)0xE9;				// компиляция кода JMP 
2076     gpcb.here = gpcb.here + 1;					// обходим JAMP
2077 	*cast(pp)(gpcb.here) = cast(pp)(cast(pb)ucfa 
2078 		- (gpcb.here + CELL)); 					// и смещения для JMP
2079     gpcb.here = gpcb.here + CELL;				// обходим CFA
2080 	//    *cast(pp)ulfa = cast(pp)con_tmp;			// фактически управляем Context
2081 	(*vect)[b1b] = cast(pp)con_tmp; 			// фактически управляем Context
2082 }
2083 
2084 // Выдать 1 - если строки равны
2085 private int cmpString (const char *s1, const char *s2)
2086 {
2087 	char *s11 = cast(char*)s1;
2088 	char *s22 = cast(char*)s2;
2089 	byte d1 = cast(byte)*s11++;
2090 	byte d2 = cast(byte)*s22++;
2091 	if ( d1 != d2 ) return 0;
2092 	for( int i=0; i < d1; i++ )	{	
2093 		if ( *s11 != *s22 ) return 0;
2094 		s11++; s22++;	
2095 	}
2096 	return 1;
2097 }
2098 
2099 // INCLUDED ( Astrz -- ) Загрузить файл Форта
2100 private void h_INCLUDED(ps adr) {
2101 	string s = to!string(adr);
2102 	File f1 = File(s, "r"); foreach(line; f1.byLine()) evalForth(cast(string)line); 
2103 }
2104 private void f_INCLUDED() {
2105 	asm {	naked;
2106 		call h_INCLUDED;
2107 		call h_DROP;
2108 		ret;
2109 	}
2110 }
2111 
2112 // Загрузить и выполнить файл Forth
2113 void includedForth(string s) {
2114 	h_INCLUDED(cast(ps)(s ~ 0).ptr);
2115 }
2116 void includedForth(char* sz) {
2117 	h_INCLUDED(sz);
2118 }