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