1// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2// Copyright 2010, SIL International, All rights reserved.
3
4#pragma once
5// This file will be pulled into and integrated into a machine implmentation
6// DO NOT build directly and under no circumstances ever #include headers in
7// here or you will break the direct_machine.
8//
9// Implementers' notes
10// ==================
11// You have access to a few primitives and the full C++ code:
12// declare_params(n) Tells the interpreter how many bytes of parameter
13// space to claim for this instruction uses and
14// initialises the param pointer. You *must* before the
15// first use of param.
16// use_params(n) Claim n extra bytes of param space beyond what was
17// claimed using delcare_param.
18// param A const byte pointer for the parameter space claimed by
19// this instruction.
20// binop(op) Implement a binary operation on the stack using the
21// specified C++ operator.
22// NOT_IMPLEMENTED Any instruction body containing this will exit the
23// program with an assertion error. Instructions that are
24// not implemented should also be marked NILOP in the
25// opcodes tables this will cause the code class to spot
26// them in a live code stream and throw a runtime_error
27// instead.
28// push(n) Push the value n onto the stack.
29// pop() Pop the top most value and return it.
30//
31// You have access to the following named fast 'registers':
32// sp = The pointer to the current top of stack, the last value
33// pushed.
34// seg = A reference to the Segment this code is running over.
35// is = The current slot index
36// isb = The original base slot index at the start of this rule
37// isf = The first positioned slot
38// isl = The last positioned slot
39// ip = The current instruction pointer
40// endPos = Position of advance of last cluster
41// dir = writing system directionality of the font
42
43
44// #define NOT_IMPLEMENTED assert(false)
45// #define NOT_IMPLEMENTED
46
47#define binop(op) const uint32 a = pop(); *sp = uint32(*sp) op a
48#define sbinop(op) const int32 a = pop(); *sp = int32(*sp) op a
49#define use_params(n) dp += n
50
51#define declare_params(n) const byte * param = dp; \
52 use_params(n);
53
54#define push(n) { *++sp = n; }
55#define pop() (*sp--)
56#define slotat(x) (map[(x)])
57#define DIE { is=seg.last(); status = Machine::died_early; EXIT(1); }
58#define POSITIONED 1
59
60STARTOP(nop)
61 do {} while (0);
62ENDOP
63
64STARTOP(push_byte)
65 declare_params(1);
66 push(int8(*param));
67ENDOP
68
69STARTOP(push_byte_u)
70 declare_params(1);
71 push(uint8(*param));
72ENDOP
73
74STARTOP(push_short)
75 declare_params(2);
76 const int16 r = int16(param[0]) << 8
77 | uint8(param[1]);
78 push(r);
79ENDOP
80
81STARTOP(push_short_u)
82 declare_params(2);
83 const uint16 r = uint16(param[0]) << 8
84 | uint8(param[1]);
85 push(r);
86ENDOP
87
88STARTOP(push_long)
89 declare_params(4);
90 const int32 r = int32(param[0]) << 24
91 | uint32(param[1]) << 16
92 | uint32(param[2]) << 8
93 | uint8(param[3]);
94 push(r);
95ENDOP
96
97STARTOP(add)
98 binop(+);
99ENDOP
100
101STARTOP(sub)
102 binop(-);
103ENDOP
104
105STARTOP(mul)
106 binop(*);
107ENDOP
108
109STARTOP(div_)
110 const int32 b = pop();
111 const int32 a = int32(*sp);
112 if (b == 0 || (a == std::numeric_limits<int32>::min() && b == -1)) DIE;
113 *sp = int32(*sp) / b;
114ENDOP
115
116STARTOP(min_)
117 const int32 a = pop(), b = *sp;
118 if (a < b) *sp = a;
119ENDOP
120
121STARTOP(max_)
122 const int32 a = pop(), b = *sp;
123 if (a > b) *sp = a;
124ENDOP
125
126STARTOP(neg)
127 *sp = uint32(-int32(*sp));
128ENDOP
129
130STARTOP(trunc8)
131 *sp = uint8(*sp);
132ENDOP
133
134STARTOP(trunc16)
135 *sp = uint16(*sp);
136ENDOP
137
138STARTOP(cond)
139 const uint32 f = pop(), t = pop(), c = pop();
140 push(c ? t : f);
141ENDOP
142
143STARTOP(and_)
144 binop(&&);
145ENDOP
146
147STARTOP(or_)
148 binop(||);
149ENDOP
150
151STARTOP(not_)
152 *sp = !*sp;
153ENDOP
154
155STARTOP(equal)
156 binop(==);
157ENDOP
158
159STARTOP(not_eq_)
160 binop(!=);
161ENDOP
162
163STARTOP(less)
164 sbinop(<);
165ENDOP
166
167STARTOP(gtr)
168 sbinop(>);
169ENDOP
170
171STARTOP(less_eq)
172 sbinop(<=);
173ENDOP
174
175STARTOP(gtr_eq)
176 sbinop(>=);
177ENDOP
178
179STARTOP(next)
180 if (map - &smap[0] >= int(smap.size())) DIE
181 if (is)
182 {
183 if (is == smap.highwater())
184 smap.highpassed(true);
185 is = is->next();
186 }
187 ++map;
188ENDOP
189
190//STARTOP(next_n)
191// use_params(1);
192// NOT_IMPLEMENTED;
193 //declare_params(1);
194 //const size_t num = uint8(*param);
195//ENDOP
196
197//STARTOP(copy_next)
198// if (is) is = is->next();
199// ++map;
200// ENDOP
201
202STARTOP(put_glyph_8bit_obs)
203 declare_params(1);
204 const unsigned int output_class = uint8(*param);
205 is->setGlyph(&seg, seg.getClassGlyph(output_class, 0));
206ENDOP
207
208STARTOP(put_subs_8bit_obs)
209 declare_params(3);
210 const int slot_ref = int8(param[0]);
211 const unsigned int input_class = uint8(param[1]),
212 output_class = uint8(param[2]);
213 uint16 index;
214 slotref slot = slotat(slot_ref);
215 if (slot)
216 {
217 index = seg.findClassIndex(input_class, slot->gid());
218 is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
219 }
220ENDOP
221
222STARTOP(put_copy)
223 declare_params(1);
224 const int slot_ref = int8(*param);
225 if (is && !is->isDeleted())
226 {
227 slotref ref = slotat(slot_ref);
228 if (ref && ref != is)
229 {
230 int16 *tempUserAttrs = is->userAttrs();
231 if (is->attachedTo() || is->firstChild()) DIE
232 Slot *prev = is->prev();
233 Slot *next = is->next();
234 memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
235 memcpy(is, ref, sizeof(Slot));
236 is->firstChild(NULL);
237 is->nextSibling(NULL);
238 is->userAttrs(tempUserAttrs);
239 is->next(next);
240 is->prev(prev);
241 if (is->attachedTo())
242 is->attachedTo()->child(is);
243 }
244 is->markCopied(false);
245 is->markDeleted(false);
246 }
247ENDOP
248
249STARTOP(insert)
250 if (smap.decMax() <= 0) DIE;
251 Slot *newSlot = seg.newSlot();
252 if (!newSlot) DIE;
253 Slot *iss = is;
254 while (iss && iss->isDeleted()) iss = iss->next();
255 if (!iss)
256 {
257 if (seg.last())
258 {
259 seg.last()->next(newSlot);
260 newSlot->prev(seg.last());
261 newSlot->before(seg.last()->before());
262 seg.last(newSlot);
263 }
264 else
265 {
266 seg.first(newSlot);
267 seg.last(newSlot);
268 }
269 }
270 else if (iss->prev())
271 {
272 iss->prev()->next(newSlot);
273 newSlot->prev(iss->prev());
274 newSlot->before(iss->prev()->after());
275 }
276 else
277 {
278 newSlot->prev(NULL);
279 newSlot->before(iss->before());
280 seg.first(newSlot);
281 }
282 newSlot->next(iss);
283 if (iss)
284 {
285 iss->prev(newSlot);
286 newSlot->originate(iss->original());
287 newSlot->after(iss->before());
288 }
289 else if (newSlot->prev())
290 {
291 newSlot->originate(newSlot->prev()->original());
292 newSlot->after(newSlot->prev()->after());
293 }
294 else
295 {
296 newSlot->originate(seg.defaultOriginal());
297 }
298 if (is == smap.highwater())
299 smap.highpassed(false);
300 is = newSlot;
301 seg.extendLength(1);
302 if (map != &smap[-1])
303 --map;
304ENDOP
305
306STARTOP(delete_)
307 if (!is || is->isDeleted()) DIE
308 is->markDeleted(true);
309 if (is->prev())
310 is->prev()->next(is->next());
311 else
312 seg.first(is->next());
313
314 if (is->next())
315 is->next()->prev(is->prev());
316 else
317 seg.last(is->prev());
318
319
320 if (is == smap.highwater())
321 smap.highwater(is->next());
322 if (is->prev())
323 is = is->prev();
324 seg.extendLength(-1);
325ENDOP
326
327STARTOP(assoc)
328 declare_params(1);
329 unsigned int num = uint8(*param);
330 const int8 * assocs = reinterpret_cast<const int8 *>(param+1);
331 use_params(num);
332 int max = -1;
333 int min = -1;
334
335 while (num-- > 0)
336 {
337 int sr = *assocs++;
338 slotref ts = slotat(sr);
339 if (ts && (min == -1 || ts->before() < min)) min = ts->before();
340 if (ts && ts->after() > max) max = ts->after();
341 }
342 if (min > -1) // implies max > -1
343 {
344 is->before(min);
345 is->after(max);
346 }
347ENDOP
348
349STARTOP(cntxt_item)
350 // It turns out this is a cunningly disguised condition forward jump.
351 declare_params(3);
352 const int is_arg = int8(param[0]);
353 const size_t iskip = uint8(param[1]),
354 dskip = uint8(param[2]);
355
356 if (mapb + is_arg != map)
357 {
358 ip += iskip;
359 dp += dskip;
360 push(true);
361 }
362ENDOP
363
364STARTOP(attr_set)
365 declare_params(1);
366 const attrCode slat = attrCode(uint8(*param));
367 const int val = pop();
368 is->setAttr(&seg, slat, 0, val, smap);
369ENDOP
370
371STARTOP(attr_add)
372 declare_params(1);
373 const attrCode slat = attrCode(uint8(*param));
374 const uint32_t val = pop();
375 if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
376 {
377 seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
378 flags |= POSITIONED;
379 }
380 uint32_t res = uint32_t(is->getAttr(&seg, slat, 0));
381 is->setAttr(&seg, slat, 0, int32_t(val + res), smap);
382ENDOP
383
384STARTOP(attr_sub)
385 declare_params(1);
386 const attrCode slat = attrCode(uint8(*param));
387 const uint32_t val = pop();
388 if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
389 {
390 seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
391 flags |= POSITIONED;
392 }
393 uint32_t res = uint32_t(is->getAttr(&seg, slat, 0));
394 is->setAttr(&seg, slat, 0, int32_t(res - val), smap);
395ENDOP
396
397STARTOP(attr_set_slot)
398 declare_params(1);
399 const attrCode slat = attrCode(uint8(*param));
400 const int offset = int(map - smap.begin())*int(slat == gr_slatAttTo);
401 const int val = pop() + offset;
402 is->setAttr(&seg, slat, offset, val, smap);
403ENDOP
404
405STARTOP(iattr_set_slot)
406 declare_params(2);
407 const attrCode slat = attrCode(uint8(param[0]));
408 const uint8 idx = uint8(param[1]);
409 const int val = int(pop() + (map - smap.begin())*int(slat == gr_slatAttTo));
410 is->setAttr(&seg, slat, idx, val, smap);
411ENDOP
412
413STARTOP(push_slot_attr)
414 declare_params(2);
415 const attrCode slat = attrCode(uint8(param[0]));
416 const int slot_ref = int8(param[1]);
417 if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
418 {
419 seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
420 flags |= POSITIONED;
421 }
422 slotref slot = slotat(slot_ref);
423 if (slot)
424 {
425 int res = slot->getAttr(&seg, slat, 0);
426 push(res);
427 }
428ENDOP
429
430STARTOP(push_glyph_attr_obs)
431 declare_params(2);
432 const unsigned int glyph_attr = uint8(param[0]);
433 const int slot_ref = int8(param[1]);
434 slotref slot = slotat(slot_ref);
435 if (slot)
436 push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
437ENDOP
438
439STARTOP(push_glyph_metric)
440 declare_params(3);
441 const unsigned int glyph_attr = uint8(param[0]);
442 const int slot_ref = int8(param[1]);
443 const signed int attr_level = uint8(param[2]);
444 slotref slot = slotat(slot_ref);
445 if (slot)
446 push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir));
447ENDOP
448
449STARTOP(push_feat)
450 declare_params(2);
451 const unsigned int feat = uint8(param[0]);
452 const int slot_ref = int8(param[1]);
453 slotref slot = slotat(slot_ref);
454 if (slot)
455 {
456 uint8 fid = seg.charinfo(slot->original())->fid();
457 push(seg.getFeature(fid, feat));
458 }
459ENDOP
460
461STARTOP(push_att_to_gattr_obs)
462 declare_params(2);
463 const unsigned int glyph_attr = uint8(param[0]);
464 const int slot_ref = int8(param[1]);
465 slotref slot = slotat(slot_ref);
466 if (slot)
467 {
468 slotref att = slot->attachedTo();
469 if (att) slot = att;
470 push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
471 }
472ENDOP
473
474STARTOP(push_att_to_glyph_metric)
475 declare_params(3);
476 const unsigned int glyph_attr = uint8(param[0]);
477 const int slot_ref = int8(param[1]);
478 const signed int attr_level = uint8(param[2]);
479 slotref slot = slotat(slot_ref);
480 if (slot)
481 {
482 slotref att = slot->attachedTo();
483 if (att) slot = att;
484 push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)));
485 }
486ENDOP
487
488STARTOP(push_islot_attr)
489 declare_params(3);
490 const attrCode slat = attrCode(uint8(param[0]));
491 const int slot_ref = int8(param[1]),
492 idx = uint8(param[2]);
493 if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
494 {
495 seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
496 flags |= POSITIONED;
497 }
498 slotref slot = slotat(slot_ref);
499 if (slot)
500 {
501 int res = slot->getAttr(&seg, slat, idx);
502 push(res);
503 }
504ENDOP
505
506#if 0
507STARTOP(push_iglyph_attr) // not implemented
508 NOT_IMPLEMENTED;
509ENDOP
510#endif
511
512STARTOP(pop_ret)
513 const uint32 ret = pop();
514 EXIT(ret);
515ENDOP
516
517STARTOP(ret_zero)
518 EXIT(0);
519ENDOP
520
521STARTOP(ret_true)
522 EXIT(1);
523ENDOP
524
525STARTOP(iattr_set)
526 declare_params(2);
527 const attrCode slat = attrCode(uint8(param[0]));
528 const uint8 idx = uint8(param[1]);
529 const int val = pop();
530 is->setAttr(&seg, slat, idx, val, smap);
531ENDOP
532
533STARTOP(iattr_add)
534 declare_params(2);
535 const attrCode slat = attrCode(uint8(param[0]));
536 const uint8 idx = uint8(param[1]);
537 const uint32_t val = pop();
538 if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
539 {
540 seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
541 flags |= POSITIONED;
542 }
543 uint32_t res = uint32_t(is->getAttr(&seg, slat, idx));
544 is->setAttr(&seg, slat, idx, int32_t(val + res), smap);
545ENDOP
546
547STARTOP(iattr_sub)
548 declare_params(2);
549 const attrCode slat = attrCode(uint8(param[0]));
550 const uint8 idx = uint8(param[1]);
551 const uint32_t val = pop();
552 if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
553 {
554 seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
555 flags |= POSITIONED;
556 }
557 uint32_t res = uint32_t(is->getAttr(&seg, slat, idx));
558 is->setAttr(&seg, slat, idx, int32_t(res - val), smap);
559ENDOP
560
561STARTOP(push_proc_state)
562 use_params(1);
563 push(1);
564ENDOP
565
566STARTOP(push_version)
567 push(0x00030000);
568ENDOP
569
570STARTOP(put_subs)
571 declare_params(5);
572 const int slot_ref = int8(param[0]);
573 const unsigned int input_class = uint8(param[1]) << 8
574 | uint8(param[2]);
575 const unsigned int output_class = uint8(param[3]) << 8
576 | uint8(param[4]);
577 slotref slot = slotat(slot_ref);
578 if (slot)
579 {
580 int index = seg.findClassIndex(input_class, slot->gid());
581 is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
582 }
583ENDOP
584
585#if 0
586STARTOP(put_subs2) // not implemented
587 NOT_IMPLEMENTED;
588ENDOP
589
590STARTOP(put_subs3) // not implemented
591 NOT_IMPLEMENTED;
592ENDOP
593#endif
594
595STARTOP(put_glyph)
596 declare_params(2);
597 const unsigned int output_class = uint8(param[0]) << 8
598 | uint8(param[1]);
599 is->setGlyph(&seg, seg.getClassGlyph(output_class, 0));
600ENDOP
601
602STARTOP(push_glyph_attr)
603 declare_params(3);
604 const unsigned int glyph_attr = uint8(param[0]) << 8
605 | uint8(param[1]);
606 const int slot_ref = int8(param[2]);
607 slotref slot = slotat(slot_ref);
608 if (slot)
609 push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
610ENDOP
611
612STARTOP(push_att_to_glyph_attr)
613 declare_params(3);
614 const unsigned int glyph_attr = uint8(param[0]) << 8
615 | uint8(param[1]);
616 const int slot_ref = int8(param[2]);
617 slotref slot = slotat(slot_ref);
618 if (slot)
619 {
620 slotref att = slot->attachedTo();
621 if (att) slot = att;
622 push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
623 }
624ENDOP
625
626STARTOP(temp_copy)
627 slotref newSlot = seg.newSlot();
628 if (!newSlot || !is) DIE;
629 int16 *tempUserAttrs = newSlot->userAttrs();
630 memcpy(newSlot, is, sizeof(Slot));
631 memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16));
632 newSlot->userAttrs(tempUserAttrs);
633 newSlot->markCopied(true);
634 *map = newSlot;
635ENDOP
636
637STARTOP(band)
638 binop(&);
639ENDOP
640
641STARTOP(bor)
642 binop(|);
643ENDOP
644
645STARTOP(bnot)
646 *sp = ~*sp;
647ENDOP
648
649STARTOP(setbits)
650 declare_params(4);
651 const uint16 m = uint16(param[0]) << 8
652 | uint8(param[1]);
653 const uint16 v = uint16(param[2]) << 8
654 | uint8(param[3]);
655 *sp = ((*sp) & ~m) | v;
656ENDOP
657
658STARTOP(set_feat)
659 declare_params(2);
660 const unsigned int feat = uint8(param[0]);
661 const int slot_ref = int8(param[1]);
662 slotref slot = slotat(slot_ref);
663 if (slot)
664 {
665 uint8 fid = seg.charinfo(slot->original())->fid();
666 seg.setFeature(fid, feat, pop());
667 }
668ENDOP
669