/**
 * General Register implementation.
 * Specific register for CPU and PPU are implemented in each class.
 */

/**
 *
 */
function Register(type, onBeforeLoad, onAfterStore) {
  this.data = new type(1);  // Uint8Array or Uint16Array
  this.data[0] = 0;
  this.onBeforeLoad = onBeforeLoad;
  this.onAfterStore = onAfterStore;
}

Register.TYPE_8BIT = Uint8Array;
Register.TYPE_16BIT = Uint16Array;

Object.assign(Register.prototype, {
  isRegister: true,

  /**
   *
   */
  load: function() {
    if (this.onBeforeLoad !== undefined)
      this.onBeforeLoad();

    return this.data[0];
  },

  /**
   *
   */
  loadWithoutCallback: function() {
    return this.data[0];
  },

  /**
   *
   */
  loadBit: function(pos) {
    return (this.data[0] >> pos) & 1;
  },

  /**
   *
   */
  loadBits: function(offset, size) {
    return (this.data[0] >> offset) & ((1 << size) - 1);
  },

  /**
   *
   */
  store: function(value) {
    this.data[0] = value;

    if(this.onAfterStore !== undefined)
      this.onAfterStore();
  },

  /**
   *
   */
  storeWithoutCallback: function(value) {
    this.data[0] = value;
  },

  /**
   *
   */
  storeBit: function(pos, value) {
    value = value & 1;  // just in case
    this.data[0] = this.data[0] & ~(1 << pos) | (value << pos);
  },

  /**
   *
   */
  storeBits: function(offset, size, value) {
    var mask = (1 << size) - 1;
    value = value & mask;  // just in case
    this.data[0] = this.data[0] & ~(mask << offset) | (value << offset);
  },

  /**
   *
   */
  clear: function() {
    this.data[0] = 0;
  },

  /**
   *
   */
  increment: function() {
    this.data[0]++;
  },

  /**
   *
   */
  incrementBy2: function() {
    this.data[0] += 2;
  },

  /**
   *
   */
  add: function(value) {
    this.data[0] += value;
  },

  /**
   *
   */
  decrement: function() {
    this.data[0]--;
  },

  /**
   *
   */
  decrementBy2: function() {
    this.data[0] -= 2;
  },

  /**
   *
   */
  sub: function(value) {
    this.data[0] -= value;
  },

  /**
   *
   */
  lshift: function(value) {
    value = value & 1;  // just in case
    var width = this.data.byteLength * 8;
    var carry = this.data[0] >> (width - 1);
    this.data[0] = (this.data[0] << 1) | value;
    return carry;
  },

  /**
   *
   */
  dump: function() {
    return __10to16(this.load(), this.data.byteLength * 2);
  }
});

/**
 *
 */
function Register8bit(onBeforeLoad, onAfterStore) {
  Register.call(this, Register.TYPE_8BIT, onBeforeLoad, onAfterStore);
}

Register8bit.prototype = Object.assign(Object.create(Register.prototype), {
  isRegister8bit: true
});

/**
 *
 */
function Register16bit(onBeforeLoad, onAfterStore) {
  Register.call(this, Register.TYPE_16BIT, onBeforeLoad, onAfterStore);
  this.bytes = new Uint8Array(this.data.buffer);
}

Register16bit.prototype = Object.assign(Object.create(Register.prototype), {
  isRegister16bit: true,

  /**
   *
   */
  loadHigherByte: function() {
    return this.bytes[1];
  },

  /**
   *
   */
  loadLowerByte: function() {
    return this.bytes[0];
  },

  /**
   *
   */
  storeHigherByte: function(value) {
    this.bytes[1] = value;
  },

  /**
   *
   */
  storeLowerByte: function(value) {
    this.bytes[0] = value;
  },

  /**
   *
   */
  lshiftByte: function() {
    this.bytes[1] = this.bytes[0];
    this.bytes[0] = 0;
  }
});

@@ -1548,32 +1548,273 @@
 a%5B0%5D = 0;%0A  %7D,%0A%0A
+  /**%0A   *%0A   */%0A  setBit: function(pos) %7B%0A    this.storeBit(pos, 1);%0A  %7D,%0A%0A  /**%0A   *%0A   */%0A  clearBit: function(pos) %7B%0A    this.storeBit(pos, 0);%0A  %7D,%0A%0A  /**%0A   *%0A   */%0A  isBitSet: function(pos) %7B%0A    return this.loadBit(pos) === 1;%0A  %7D,%0A%0A
   /**%0A   *%0A   */

Add some methods to Register