/* GRAPHITE2 LICENSING Copyright 2010, SIL International All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should also have received a copy of the GNU Lesser General Public License along with this library in the file named "LICENSE". If not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA or visit their web page on the internet at http://www.fsf.org/licenses/lgpl.html. Alternatively, the contents of this file may be used under the terms of the Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public License, as published by the Free Software Foundation, either version 2 of the License or (at your option) any later version. */ #pragma once // This file will be pulled into and integrated into a machine implmentation // DO NOT build directly and under no circumstances ever #include headers in // here or you will break the direct_machine. // // Implementers' notes // ================== // You have access to a few primitives and the full C++ code: // declare_params(n) Tells the interpreter how many bytes of parameter // space to claim for this instruction uses and // initialises the param pointer. You *must* before the // first use of param. // use_params(n) Claim n extra bytes of param space beyond what was // claimed using delcare_param. // param A const byte pointer for the parameter space claimed by // this instruction. // binop(op) Implement a binary operation on the stack using the // specified C++ operator. // NOT_IMPLEMENTED Any instruction body containing this will exit the // program with an assertion error. Instructions that are // not implemented should also be marked NILOP in the // opcodes tables this will cause the code class to spot // them in a live code stream and throw a runtime_error // instead. // push(n) Push the value n onto the stack. // pop() Pop the top most value and return it. // // You have access to the following named fast 'registers': // sp = The pointer to the current top of stack, the last value // pushed. // seg = A reference to the Segment this code is running over. // is = The current slot index // isb = The original base slot index at the start of this rule // isf = The first positioned slot // isl = The last positioned slot // ip = The current instruction pointer // endPos = Position of advance of last cluster // dir = writing system directionality of the font // #define NOT_IMPLEMENTED assert(false) // #define NOT_IMPLEMENTED #define binop(op) const uint32 a = pop(); *sp = uint32(*sp) op a #define sbinop(op) const int32 a = pop(); *sp = int32(*sp) op a #define use_params(n) dp += n #define declare_params(n) const byte * param = dp; \ use_params(n); #define push(n) { *++sp = n; } #define pop() (*sp--) #define slotat(x) (map[(x)]) #define DIE { is=seg.last(); status = Machine::died_early; EXIT(1); } #define POSITIONED 1 STARTOP(nop) do {} while (0); ENDOP STARTOP(push_byte) declare_params(1); push(int8(*param)); ENDOP STARTOP(push_byte_u) declare_params(1); push(uint8(*param)); ENDOP STARTOP(push_short) declare_params(2); const int16 r = int16(param[0]) << 8 | uint8(param[1]); push(r); ENDOP STARTOP(push_short_u) declare_params(2); const uint16 r = uint16(param[0]) << 8 | uint8(param[1]); push(r); ENDOP STARTOP(push_long) declare_params(4); const int32 r = int32(param[0]) << 24 | uint32(param[1]) << 16 | uint32(param[2]) << 8 | uint8(param[3]); push(r); ENDOP STARTOP(add) binop(+); ENDOP STARTOP(sub) binop(-); ENDOP STARTOP(mul) binop(*); ENDOP STARTOP(div_) const int32 b = pop(); const int32 a = int32(*sp); if (b == 0 || (a == std::numeric_limits::min() && b == -1)) DIE; *sp = int32(*sp) / b; ENDOP STARTOP(min_) const int32 a = pop(), b = *sp; if (a < b) *sp = a; ENDOP STARTOP(max_) const int32 a = pop(), b = *sp; if (a > b) *sp = a; ENDOP STARTOP(neg) *sp = uint32(-int32(*sp)); ENDOP STARTOP(trunc8) *sp = uint8(*sp); ENDOP STARTOP(trunc16) *sp = uint16(*sp); ENDOP STARTOP(cond) const uint32 f = pop(), t = pop(), c = pop(); push(c ? t : f); ENDOP STARTOP(and_) binop(&&); ENDOP STARTOP(or_) binop(||); ENDOP STARTOP(not_) *sp = !*sp; ENDOP STARTOP(equal) binop(==); ENDOP STARTOP(not_eq_) binop(!=); ENDOP STARTOP(less) sbinop(<); ENDOP STARTOP(gtr) sbinop(>); ENDOP STARTOP(less_eq) sbinop(<=); ENDOP STARTOP(gtr_eq) sbinop(>=); ENDOP STARTOP(next) if (map - &smap[0] >= int(smap.size())) DIE if (is) { if (is == smap.highwater()) smap.highpassed(true); is = is->next(); } ++map; ENDOP //STARTOP(next_n) // use_params(1); // NOT_IMPLEMENTED; //declare_params(1); //const size_t num = uint8(*param); //ENDOP //STARTOP(copy_next) // if (is) is = is->next(); // ++map; // ENDOP STARTOP(put_glyph_8bit_obs) declare_params(1); const unsigned int output_class = uint8(*param); is->setGlyph(&seg, seg.getClassGlyph(output_class, 0)); ENDOP STARTOP(put_subs_8bit_obs) declare_params(3); const int slot_ref = int8(param[0]); const unsigned int input_class = uint8(param[1]), output_class = uint8(param[2]); uint16 index; slotref slot = slotat(slot_ref); if (slot) { index = seg.findClassIndex(input_class, slot->gid()); is->setGlyph(&seg, seg.getClassGlyph(output_class, index)); } ENDOP STARTOP(put_copy) declare_params(1); const int slot_ref = int8(*param); if (is && !is->isDeleted()) { slotref ref = slotat(slot_ref); if (ref && ref != is) { int16 *tempUserAttrs = is->userAttrs(); if (is->attachedTo() || is->firstChild()) DIE Slot *prev = is->prev(); Slot *next = is->next(); memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16)); memcpy(is, ref, sizeof(Slot)); is->firstChild(NULL); is->nextSibling(NULL); is->userAttrs(tempUserAttrs); is->next(next); is->prev(prev); if (is->attachedTo()) is->attachedTo()->child(is); } is->markCopied(false); is->markDeleted(false); } ENDOP STARTOP(insert) if (smap.decMax() <= 0) DIE; Slot *newSlot = seg.newSlot(); if (!newSlot) DIE; Slot *iss = is; while (iss && iss->isDeleted()) iss = iss->next(); if (!iss) { if (seg.last()) { seg.last()->next(newSlot); newSlot->prev(seg.last()); newSlot->before(seg.last()->before()); seg.last(newSlot); } else { seg.first(newSlot); seg.last(newSlot); } } else if (iss->prev()) { iss->prev()->next(newSlot); newSlot->prev(iss->prev()); newSlot->before(iss->prev()->after()); } else { newSlot->prev(NULL); newSlot->before(iss->before()); seg.first(newSlot); } newSlot->next(iss); if (iss) { iss->prev(newSlot); newSlot->originate(iss->original()); newSlot->after(iss->before()); } else if (newSlot->prev()) { newSlot->originate(newSlot->prev()->original()); newSlot->after(newSlot->prev()->after()); } else { newSlot->originate(seg.defaultOriginal()); } if (is == smap.highwater()) smap.highpassed(false); is = newSlot; seg.extendLength(1); if (map != &smap[-1]) --map; ENDOP STARTOP(delete_) if (!is || is->isDeleted()) DIE is->markDeleted(true); if (is->prev()) is->prev()->next(is->next()); else seg.first(is->next()); if (is->next()) is->next()->prev(is->prev()); else seg.last(is->prev()); if (is == smap.highwater()) smap.highwater(is->next()); if (is->prev()) is = is->prev(); seg.extendLength(-1); ENDOP STARTOP(assoc) declare_params(1); unsigned int num = uint8(*param); const int8 * assocs = reinterpret_cast(param+1); use_params(num); int max = -1; int min = -1; while (num-- > 0) { int sr = *assocs++; slotref ts = slotat(sr); if (ts && (min == -1 || ts->before() < min)) min = ts->before(); if (ts && ts->after() > max) max = ts->after(); } if (min > -1) // implies max > -1 { is->before(min); is->after(max); } ENDOP STARTOP(cntxt_item) // It turns out this is a cunningly disguised condition forward jump. declare_params(3); const int is_arg = int8(param[0]); const size_t iskip = uint8(param[1]), dskip = uint8(param[2]); if (mapb + is_arg != map) { ip += iskip; dp += dskip; push(true); } ENDOP STARTOP(attr_set) declare_params(1); const attrCode slat = attrCode(uint8(*param)); const int val = pop(); is->setAttr(&seg, slat, 0, val, smap); ENDOP STARTOP(attr_add) declare_params(1); const attrCode slat = attrCode(uint8(*param)); const uint32_t val = pop(); if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) { seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); flags |= POSITIONED; } uint32_t res = uint32_t(is->getAttr(&seg, slat, 0)); is->setAttr(&seg, slat, 0, int32_t(val + res), smap); ENDOP STARTOP(attr_sub) declare_params(1); const attrCode slat = attrCode(uint8(*param)); const uint32_t val = pop(); if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) { seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); flags |= POSITIONED; } uint32_t res = uint32_t(is->getAttr(&seg, slat, 0)); is->setAttr(&seg, slat, 0, int32_t(res - val), smap); ENDOP STARTOP(attr_set_slot) declare_params(1); const attrCode slat = attrCode(uint8(*param)); const int offset = int(map - smap.begin())*int(slat == gr_slatAttTo); const int val = pop() + offset; is->setAttr(&seg, slat, offset, val, smap); ENDOP STARTOP(iattr_set_slot) declare_params(2); const attrCode slat = attrCode(uint8(param[0])); const uint8 idx = uint8(param[1]); const int val = int(pop() + (map - smap.begin())*int(slat == gr_slatAttTo)); is->setAttr(&seg, slat, idx, val, smap); ENDOP STARTOP(push_slot_attr) declare_params(2); const attrCode slat = attrCode(uint8(param[0])); const int slot_ref = int8(param[1]); if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) { seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); flags |= POSITIONED; } slotref slot = slotat(slot_ref); if (slot) { int res = slot->getAttr(&seg, slat, 0); push(res); } ENDOP STARTOP(push_glyph_attr_obs) declare_params(2); const unsigned int glyph_attr = uint8(param[0]); const int slot_ref = int8(param[1]); slotref slot = slotat(slot_ref); if (slot) push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); ENDOP STARTOP(push_glyph_metric) declare_params(3); const unsigned int glyph_attr = uint8(param[0]); const int slot_ref = int8(param[1]); const signed int attr_level = uint8(param[2]); slotref slot = slotat(slot_ref); if (slot) push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)); ENDOP STARTOP(push_feat) declare_params(2); const unsigned int feat = uint8(param[0]); const int slot_ref = int8(param[1]); slotref slot = slotat(slot_ref); if (slot) { uint8 fid = seg.charinfo(slot->original())->fid(); push(seg.getFeature(fid, feat)); } ENDOP STARTOP(push_att_to_gattr_obs) declare_params(2); const unsigned int glyph_attr = uint8(param[0]); const int slot_ref = int8(param[1]); slotref slot = slotat(slot_ref); if (slot) { slotref att = slot->attachedTo(); if (att) slot = att; push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); } ENDOP STARTOP(push_att_to_glyph_metric) declare_params(3); const unsigned int glyph_attr = uint8(param[0]); const int slot_ref = int8(param[1]); const signed int attr_level = uint8(param[2]); slotref slot = slotat(slot_ref); if (slot) { slotref att = slot->attachedTo(); if (att) slot = att; push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir))); } ENDOP STARTOP(push_islot_attr) declare_params(3); const attrCode slat = attrCode(uint8(param[0])); const int slot_ref = int8(param[1]), idx = uint8(param[2]); if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) { seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); flags |= POSITIONED; } slotref slot = slotat(slot_ref); if (slot) { int res = slot->getAttr(&seg, slat, idx); push(res); } ENDOP #if 0 STARTOP(push_iglyph_attr) // not implemented NOT_IMPLEMENTED; ENDOP #endif STARTOP(pop_ret) const uint32 ret = pop(); EXIT(ret); ENDOP STARTOP(ret_zero) EXIT(0); ENDOP STARTOP(ret_true) EXIT(1); ENDOP STARTOP(iattr_set) declare_params(2); const attrCode slat = attrCode(uint8(param[0])); const uint8 idx = uint8(param[1]); const int val = pop(); is->setAttr(&seg, slat, idx, val, smap); ENDOP STARTOP(iattr_add) declare_params(2); const attrCode slat = attrCode(uint8(param[0])); const uint8 idx = uint8(param[1]); const uint32_t val = pop(); if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) { seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); flags |= POSITIONED; } uint32_t res = uint32_t(is->getAttr(&seg, slat, idx)); is->setAttr(&seg, slat, idx, int32_t(val + res), smap); ENDOP STARTOP(iattr_sub) declare_params(2); const attrCode slat = attrCode(uint8(param[0])); const uint8 idx = uint8(param[1]); const uint32_t val = pop(); if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) { seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); flags |= POSITIONED; } uint32_t res = uint32_t(is->getAttr(&seg, slat, idx)); is->setAttr(&seg, slat, idx, int32_t(res - val), smap); ENDOP STARTOP(push_proc_state) use_params(1); push(1); ENDOP STARTOP(push_version) push(0x00030000); ENDOP STARTOP(put_subs) declare_params(5); const int slot_ref = int8(param[0]); const unsigned int input_class = uint8(param[1]) << 8 | uint8(param[2]); const unsigned int output_class = uint8(param[3]) << 8 | uint8(param[4]); slotref slot = slotat(slot_ref); if (slot) { int index = seg.findClassIndex(input_class, slot->gid()); is->setGlyph(&seg, seg.getClassGlyph(output_class, index)); } ENDOP #if 0 STARTOP(put_subs2) // not implemented NOT_IMPLEMENTED; ENDOP STARTOP(put_subs3) // not implemented NOT_IMPLEMENTED; ENDOP #endif STARTOP(put_glyph) declare_params(2); const unsigned int output_class = uint8(param[0]) << 8 | uint8(param[1]); is->setGlyph(&seg, seg.getClassGlyph(output_class, 0)); ENDOP STARTOP(push_glyph_attr) declare_params(3); const unsigned int glyph_attr = uint8(param[0]) << 8 | uint8(param[1]); const int slot_ref = int8(param[2]); slotref slot = slotat(slot_ref); if (slot) push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); ENDOP STARTOP(push_att_to_glyph_attr) declare_params(3); const unsigned int glyph_attr = uint8(param[0]) << 8 | uint8(param[1]); const int slot_ref = int8(param[2]); slotref slot = slotat(slot_ref); if (slot) { slotref att = slot->attachedTo(); if (att) slot = att; push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); } ENDOP STARTOP(temp_copy) slotref newSlot = seg.newSlot(); if (!newSlot || !is) DIE; int16 *tempUserAttrs = newSlot->userAttrs(); memcpy(newSlot, is, sizeof(Slot)); memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16)); newSlot->userAttrs(tempUserAttrs); newSlot->markCopied(true); *map = newSlot; ENDOP STARTOP(band) binop(&); ENDOP STARTOP(bor) binop(|); ENDOP STARTOP(bnot) *sp = ~*sp; ENDOP STARTOP(setbits) declare_params(4); const uint16 m = uint16(param[0]) << 8 | uint8(param[1]); const uint16 v = uint16(param[2]) << 8 | uint8(param[3]); *sp = ((*sp) & ~m) | v; ENDOP STARTOP(set_feat) declare_params(2); const unsigned int feat = uint8(param[0]); const int slot_ref = int8(param[1]); slotref slot = slotat(slot_ref); if (slot) { uint8 fid = seg.charinfo(slot->original())->fid(); seg.setFeature(fid, feat, pop()); } ENDOP