tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

opcodes.h (18949B)


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