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 | |
60 | STARTOP(nop) |
61 | do {} while (0); |
62 | ENDOP |
63 | |
64 | STARTOP(push_byte) |
65 | declare_params(1); |
66 | push(int8(*param)); |
67 | ENDOP |
68 | |
69 | STARTOP(push_byte_u) |
70 | declare_params(1); |
71 | push(uint8(*param)); |
72 | ENDOP |
73 | |
74 | STARTOP(push_short) |
75 | declare_params(2); |
76 | const int16 r = int16(param[0]) << 8 |
77 | | uint8(param[1]); |
78 | push(r); |
79 | ENDOP |
80 | |
81 | STARTOP(push_short_u) |
82 | declare_params(2); |
83 | const uint16 r = uint16(param[0]) << 8 |
84 | | uint8(param[1]); |
85 | push(r); |
86 | ENDOP |
87 | |
88 | STARTOP(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); |
95 | ENDOP |
96 | |
97 | STARTOP(add) |
98 | binop(+); |
99 | ENDOP |
100 | |
101 | STARTOP(sub) |
102 | binop(-); |
103 | ENDOP |
104 | |
105 | STARTOP(mul) |
106 | binop(*); |
107 | ENDOP |
108 | |
109 | STARTOP(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; |
114 | ENDOP |
115 | |
116 | STARTOP(min_) |
117 | const int32 a = pop(), b = *sp; |
118 | if (a < b) *sp = a; |
119 | ENDOP |
120 | |
121 | STARTOP(max_) |
122 | const int32 a = pop(), b = *sp; |
123 | if (a > b) *sp = a; |
124 | ENDOP |
125 | |
126 | STARTOP(neg) |
127 | *sp = uint32(-int32(*sp)); |
128 | ENDOP |
129 | |
130 | STARTOP(trunc8) |
131 | *sp = uint8(*sp); |
132 | ENDOP |
133 | |
134 | STARTOP(trunc16) |
135 | *sp = uint16(*sp); |
136 | ENDOP |
137 | |
138 | STARTOP(cond) |
139 | const uint32 f = pop(), t = pop(), c = pop(); |
140 | push(c ? t : f); |
141 | ENDOP |
142 | |
143 | STARTOP(and_) |
144 | binop(&&); |
145 | ENDOP |
146 | |
147 | STARTOP(or_) |
148 | binop(||); |
149 | ENDOP |
150 | |
151 | STARTOP(not_) |
152 | *sp = !*sp; |
153 | ENDOP |
154 | |
155 | STARTOP(equal) |
156 | binop(==); |
157 | ENDOP |
158 | |
159 | STARTOP(not_eq_) |
160 | binop(!=); |
161 | ENDOP |
162 | |
163 | STARTOP(less) |
164 | sbinop(<); |
165 | ENDOP |
166 | |
167 | STARTOP(gtr) |
168 | sbinop(>); |
169 | ENDOP |
170 | |
171 | STARTOP(less_eq) |
172 | sbinop(<=); |
173 | ENDOP |
174 | |
175 | STARTOP(gtr_eq) |
176 | sbinop(>=); |
177 | ENDOP |
178 | |
179 | STARTOP(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; |
188 | ENDOP |
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 | |
202 | STARTOP(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)); |
206 | ENDOP |
207 | |
208 | STARTOP(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 | } |
220 | ENDOP |
221 | |
222 | STARTOP(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 | } |
247 | ENDOP |
248 | |
249 | STARTOP(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; |
304 | ENDOP |
305 | |
306 | STARTOP(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); |
325 | ENDOP |
326 | |
327 | STARTOP(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 | } |
347 | ENDOP |
348 | |
349 | STARTOP(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 | } |
362 | ENDOP |
363 | |
364 | STARTOP(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); |
369 | ENDOP |
370 | |
371 | STARTOP(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); |
382 | ENDOP |
383 | |
384 | STARTOP(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); |
395 | ENDOP |
396 | |
397 | STARTOP(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); |
403 | ENDOP |
404 | |
405 | STARTOP(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); |
411 | ENDOP |
412 | |
413 | STARTOP(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 | } |
428 | ENDOP |
429 | |
430 | STARTOP(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))); |
437 | ENDOP |
438 | |
439 | STARTOP(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)); |
447 | ENDOP |
448 | |
449 | STARTOP(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 | } |
459 | ENDOP |
460 | |
461 | STARTOP(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 | } |
472 | ENDOP |
473 | |
474 | STARTOP(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 | } |
486 | ENDOP |
487 | |
488 | STARTOP(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 | } |
504 | ENDOP |
505 | |
506 | #if 0 |
507 | STARTOP(push_iglyph_attr) // not implemented |
508 | NOT_IMPLEMENTED; |
509 | ENDOP |
510 | #endif |
511 | |
512 | STARTOP(pop_ret) |
513 | const uint32 ret = pop(); |
514 | EXIT(ret); |
515 | ENDOP |
516 | |
517 | STARTOP(ret_zero) |
518 | EXIT(0); |
519 | ENDOP |
520 | |
521 | STARTOP(ret_true) |
522 | EXIT(1); |
523 | ENDOP |
524 | |
525 | STARTOP(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); |
531 | ENDOP |
532 | |
533 | STARTOP(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); |
545 | ENDOP |
546 | |
547 | STARTOP(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); |
559 | ENDOP |
560 | |
561 | STARTOP(push_proc_state) |
562 | use_params(1); |
563 | push(1); |
564 | ENDOP |
565 | |
566 | STARTOP(push_version) |
567 | push(0x00030000); |
568 | ENDOP |
569 | |
570 | STARTOP(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 | } |
583 | ENDOP |
584 | |
585 | #if 0 |
586 | STARTOP(put_subs2) // not implemented |
587 | NOT_IMPLEMENTED; |
588 | ENDOP |
589 | |
590 | STARTOP(put_subs3) // not implemented |
591 | NOT_IMPLEMENTED; |
592 | ENDOP |
593 | #endif |
594 | |
595 | STARTOP(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)); |
600 | ENDOP |
601 | |
602 | STARTOP(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))); |
610 | ENDOP |
611 | |
612 | STARTOP(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 | } |
624 | ENDOP |
625 | |
626 | STARTOP(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; |
635 | ENDOP |
636 | |
637 | STARTOP(band) |
638 | binop(&); |
639 | ENDOP |
640 | |
641 | STARTOP(bor) |
642 | binop(|); |
643 | ENDOP |
644 | |
645 | STARTOP(bnot) |
646 | *sp = ~*sp; |
647 | ENDOP |
648 | |
649 | STARTOP(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; |
656 | ENDOP |
657 | |
658 | STARTOP(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 | } |
668 | ENDOP |
669 | |