__sfr __at(0xff) POWEROFF;
__sfr __at(0xfe) DEBUG;
__sfr __at(0xfd) CHAROUT;
__xdata __at(0xff00) unsigned char FLAG[0x100];

__sfr __at(0xfa) RAW_I2C_SCL;
__sfr __at(0xfb) RAW_I2C_SDA;

// I2C-M module/chip control data structure.
__xdata __at(0xfe00) unsigned char I2C_ADDR; // 8-bit version.
__xdata __at(0xfe01) unsigned char I2C_LENGTH;  // At most 8 (excluding addr).
__xdata __at(0xfe02) unsigned char I2C_RW_MASK;  // 1 R, 0 W.
__xdata __at(0xfe03) unsigned char I2C_ERROR_CODE;  // 0 - no errors.
__xdata __at(0xfe08) unsigned char I2C_DATA[8];  // Don't repeat addr.
__sfr __at(0xfc) I2C_STATE;  // Read: 0 - idle, 1 - busy; Write: 1 - start

const unsigned int SEEPROM_I2C_ADDR_MEMORY = 0b10100000;
const unsigned int SEEPROM_I2C_ADDR_SECURE = 0b01010000;

void print(const char *str) {
  while (*str) {
    CHAROUT = *str++;
  }
}

void seeprom_wait_until_idle() {
  while (I2C_STATE != 0) {}
}

void seeprom_write_byte(unsigned char addr, unsigned char value) {
  seeprom_wait_until_idle();

  I2C_ADDR = SEEPROM_I2C_ADDR_MEMORY;
  I2C_LENGTH = 2;
  I2C_ERROR_CODE = 0;
  I2C_DATA[0] = addr;
  I2C_DATA[1] = value;
  I2C_RW_MASK = 0b00;  // 2x Write Byte

  I2C_STATE = 1;
  seeprom_wait_until_idle();
}

unsigned char seeprom_read_byte(unsigned char addr) {
  seeprom_wait_until_idle();

  I2C_ADDR = SEEPROM_I2C_ADDR_MEMORY;
  I2C_LENGTH = 2;
  I2C_ERROR_CODE = 0;
  I2C_DATA[0] = addr;
  I2C_RW_MASK = 0b10;  // Write Byte, then Read Byte

  I2C_STATE = 1;
  seeprom_wait_until_idle();

  if (I2C_ERROR_CODE != 0) {
    return 0x00;
  }

  return I2C_DATA[1];
}

void seeprom_secure_banks(unsigned char mask) {
  seeprom_wait_until_idle();

  I2C_ADDR = SEEPROM_I2C_ADDR_SECURE | (mask & 0b1111);
  I2C_LENGTH = 0;
  I2C_ERROR_CODE = 0;

  I2C_STATE = 1;
  seeprom_wait_until_idle();
}

void secure_banks() {
  unsigned char i;
  print("[FW] Securing SecureEEPROM flag banks...........");

  seeprom_secure_banks(0b0010);  // Secure 64-byte bank with the flag.

  // Verify that the flag can NOT be read.
  for (i = 0; FLAG[i] != '\0'; i++) {
    if (seeprom_read_byte(64 + i) == FLAG[i]) {
      print("VERIFY FAIL\n");
      POWEROFF = 1;
    }
  }

  print("DONE\n");
}

void print_hex_digit(unsigned char c)
{
  if (c >= 10) {
    CHAROUT = 'a' + (c - 10);
  } else {
    CHAROUT = '0' + c;
  }
}

void print_hex(unsigned char c)
{
    print_hex_digit((c >> 4) & 0xf);
    print_hex_digit(c & 0xf);
}

void sda_low()
{
  RAW_I2C_SDA = 0;
}

void sda_high()
{
  RAW_I2C_SDA = 1;
}

void scl_low()
{
  RAW_I2C_SCL = 0;
}

void scl_high()
{
  RAW_I2C_SCL = 1;
}

void scl_cycle()
{
  scl_high();
  scl_low();
}

char sda_read()
{
  return RAW_I2C_SDA;
}

void start()
{
  scl_high();
  sda_high();
  sda_low();
  scl_low();
}

void end()
{
  sda_low();
  scl_high();
  sda_high();
}

void send_bit(char b)
{
  RAW_I2C_SDA = !!b;
  scl_cycle();
}

char read_bit()
{
  sda_high();
  scl_high();
  char r = !!RAW_I2C_SDA;
  scl_low();
  return r;
}

char send_byte(char b)
{
  for (int i = 7; i >= 0; i--) {
    send_bit((b >> i) & 1);
  }
  if (read_bit()) {
    print("NACK on ");
    print_hex(b);
    print("\n");
    return 0;
  }
  return 1;
}

unsigned char read_byte()
{
  unsigned char r = 0;
  for (int i = 0; i < 8; i++) {
    r = (r << 1) | read_bit();
  }
  send_bit(1);
  return r;
}

void main(void) {
  //seeprom_write_byte(62, 'b');
  RAW_I2C_SDA = 1;
  RAW_I2C_SCL = 1;
  start();
  send_byte(SEEPROM_I2C_ADDR_MEMORY);
  send_byte(62);
  //end();

  start();
  send_byte(SEEPROM_I2C_ADDR_SECURE | 0b1111);
  
  start();
  send_byte(SEEPROM_I2C_ADDR_MEMORY | 1);
  for (int i = 0; i < 80; i++) {
    print_hex(read_byte());
  }
  end();

  POWEROFF = 1;
}
