#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/io.h>

#define OPTROX_BASE 0x340

// +0, +1 = CCD control?
#define OPTROX_REG_STATUS (OPTROX_BASE + 0x02)
// reads 0xFF when the scanner is not connected
#define OPTROX_REG_CMD    (OPTROX_BASE + 0x03)
#define OPTROX_REG_DATA   (OPTROX_BASE + 0x04)
#define OPTROX_REG_MODE   (OPTROX_BASE + 0x05)
#define OPTROX_REG_INDEX1 (OPTROX_BASE + 0x06)
#define OPTROX_REG_INDEX2 (OPTROX_BASE + 0x07)
#define OPTROX_REG_STATUS2 (OPTROX_BASE + 0x07)
// reading from 7 = status?
// reads 0x58 even when the scanner is not connected (all other registers read 0xFF)
// reads 0x5A (bit 1 set) when REG_MODE bit 0 is set
// bit 3 - ?ready? scanning polls 0x347 until bit 3 is clear before reading
//         next bytes from 0x344
#define OPTROX_REG_MAGIC  (OPTROX_BASE + 0x0e)

#define OPTROX_CMD_HOME 0x12
//#define OPTROX_CMD_MOVE 0x50
#define OPTROX_CMD_MOVE 0x50
// 0x50 is the same as 0xD0 ?
// 0x52 - make noise and don't move
//? bit 4 = 1: normal step, 0: noisy step
#define OPTROX_CMD_LAMP_BIT		(1 << 2)
#define OPTROX_CMD_DIR_FORWARD_BIT	(1 << 3)

#define OPTROX_STATUS_HOME_MASK (1 << 3)
#define OPTROX_STATUS_LAMP_MASK (1 << 7)

#define OPTROX_MODE_AUTOINCREMENT_READ	(1 << 0)	/* autoincrement index after read */
#define OPTROX_MODE_SRAM		(1 << 2)
//#define OPTROX_MODE_??????		(1 << 3)
#define OPTROX_MODE_AUTOINCREMENT_WRITE (1 << 4)	/* autoincrement index after write */
#define OPTROX_MODE_BLOCK0_ONLY		(1 << 6)	/* limit access to first 4KB block */

#define OPTROX_BLOCK_SIZE 4096

#define DIV_ROUND_UP(n,d)	(((n) + (d) - 1) / (d))
#define MIN(a,b)		(((a)<(b))?(a):(b))

unsigned char status() {
  return inb(OPTROX_REG_STATUS);
}

/* Access 32KB SRAM on the controller card */
void write_sram(unsigned short index, unsigned char data) {
  outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(index & 0xFF, OPTROX_REG_INDEX1);
  outb(index >> 8, OPTROX_REG_INDEX2);
  outb(data, OPTROX_REG_DATA);
}

void write_sram_block(unsigned short index, unsigned short length, char *p) {
  outb(OPTROX_MODE_SRAM | OPTROX_MODE_AUTOINCREMENT_WRITE, OPTROX_REG_MODE);
  outb(index & 0xFF, OPTROX_REG_INDEX1);
  outb(index >> 8, OPTROX_REG_INDEX2);
  for (int i = 0; i < length; i++)
    outb(p[i], OPTROX_REG_DATA);
}

unsigned char read_sram(unsigned short index) {
  outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(index & 0xFF, OPTROX_REG_INDEX1);
  outb(index >> 8, OPTROX_REG_INDEX2);
  return inb(OPTROX_REG_DATA);
}

void read_sram_block(unsigned short index, unsigned short length, char *p) {
/*  outb(OPTROX_MODE_SRAM | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);
  outb(index & 0xFF, OPTROX_REG_INDEX1);
  outb(index >> 8, OPTROX_REG_INDEX2);*/
  for (unsigned short i = 0; i < length; i++) {
/*    if (i % 4096 == 0) {
      outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
      outb((index+i) & 0xFF, OPTROX_REG_INDEX1);
      outb((index+i) >> 8, OPTROX_REG_INDEX2);
      printf("0: %d\n", i);
    } else */if (i % 4096 == 0) {
      outb(OPTROX_MODE_SRAM | OPTROX_MODE_AUTOINCREMENT_READ | OPTROX_MODE_BLOCK0_ONLY, OPTROX_REG_MODE);
      outb((index+i) & 0xFF, OPTROX_REG_INDEX1);
      outb((index+i) >> 8, OPTROX_REG_INDEX2);
      printf("1: %d\n", i);
    }
    p[i] = inb(OPTROX_REG_DATA);
  }
}

void clear_sram() {
  for (unsigned int i = 0; i < 32768; i++)
    write_sram(i, 0);
}

/* Attach/detach controller card from the I/O ports */
void power(bool state) {
  for (int i = 0; i < 3; i++) {
    outb(0x55, OPTROX_REG_MAGIC);
    outb(0x55, OPTROX_REG_MAGIC);
    outb(0xAA, OPTROX_REG_MAGIC);
    outb(0xAA, OPTROX_REG_MAGIC);
    outb(0x55, OPTROX_REG_MAGIC);
    inb(OPTROX_REG_MAGIC);
  }
//  outb(state ? 0x9B : 0xFF, OPTROX_REG_MAGIC);
  outb(state ? 0x9B : 0x00, OPTROX_REG_MAGIC);
}

void poweron_from_coma() {
//  inb(OPTROX_REG_INDEX2); //0xff

//  write_sram(0x00, 0x14);
//  write_sram(0x01, 0x04);
//  write_sram(0x02, 0x97);
//  write_sram(0x03, 0x19);

  read_sram(0x00); //0xff
  read_sram(0x01); //0xff
  read_sram(0x02); //0xff
  read_sram(0x03); //0xff

  for (int i = OPTROX_BASE; i < OPTROX_BASE + 0x10; i++)
    inb(i); // 0xff
  inb(OPTROX_REG_INDEX2); // 0xff*/
  
  power(1);

//  inb(OPTROX_REG_INDEX2);
//  inb(OPTROX_REG_STATUS);
//  inb(OPTROX_REG_STATUS);

//  outb(0x00, OPTROX_BASE + 1);
//  outb(0x04, OPTROX_REG_CMD);		// enabling this causes noisy stepping
//  outb(0x04, OPTROX_REG_MODE);
//  outb(0x04, OPTROX_REG_MODE);
//  outb(0x14, OPTROX_REG_MODE);
//  outb(0x14, OPTROX_REG_MODE);
//  outb(0x00, OPTROX_REG_INDEX2);
//  inb(OPTROX_BASE + 6);
}

void lamp_power(bool state) {
  outb(state ? OPTROX_CMD_LAMP_BIT : 0, OPTROX_REG_CMD);
}

void home() {
  outb(OPTROX_CMD_HOME/* | OPTROX_DIR_FORWARD_BIT*/, OPTROX_REG_CMD);
//  while (!(status() & OPTROX_STATUS_HOME_MASK))
//    ;
}

void wait_something_true() {
  while (!(inb(OPTROX_REG_STATUS2) & (1<<3)))
    ;
}

void wait_something_false() {
  while (inb(OPTROX_REG_STATUS2) & (1<<3))
    ;
}

void reg_index(char index1, char index2) {
  outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(index1, OPTROX_REG_INDEX1);
  outb(OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(index2, OPTROX_REG_INDEX2);
}

char reg_read(char index1, char index2) {
  reg_index(index1, index2);
  return inb(OPTROX_REG_DATA);
}

void reg_write(char index1, char index2, char value) {
  reg_index(index1, index2);
  outb(value, OPTROX_REG_DATA);
}

void write_data_multiple(char value, int count) {
  for (int i = 0; i < count; i++)
    outb(value, OPTROX_REG_DATA);
}

void calibrate_something(char x, bool y, bool z) {
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0xC5, OPTROX_BASE + 0x01);
  outb(x, OPTROX_BASE + 0x00);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x00, OPTROX_BASE + 0x00);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  if (y)
    outb(0x70, OPTROX_REG_INDEX2);
  else
    outb(0x33, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  wait_something_true();
  wait_something_false();
  inb(OPTROX_REG_INDEX1);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  if (y)
    outb(0x73, OPTROX_REG_INDEX2);
  else
    outb(0x33, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x33, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  if (z)
    outb(0x11, OPTROX_REG_INDEX2);
  else
    outb(0x01, OPTROX_REG_INDEX2);
  for (int i = 0; i < 48; i++)
    inb(OPTROX_REG_DATA);
}

void upload_something(bool x) {
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  write_data_multiple(0x02, 1917);
  write_data_multiple(0x42, 49);
  write_data_multiple(0x4A, 1);
  write_data_multiple(0x02, 805);
  write_data_multiple(0x06, 4);
  write_data_multiple(0x00, 1319);
  write_data_multiple(0x01, 1);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  write_data_multiple(0x02, 165);
  write_data_multiple(0x42, 49);
  write_data_multiple(0x62, 1);
  write_data_multiple(0x02, 2557);
  write_data_multiple(0x06, 4);
  write_data_multiple(0x02, 1317);
  write_data_multiple(0x12, 1);
  write_data_multiple(0x0A, 1);
  write_data_multiple(0x03, 1);		// 1308, 4556
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  if (x)
    outb(0x13, OPTROX_REG_INDEX2);
  else
    outb(0x03, OPTROX_REG_INDEX2);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  if (x)
    outb(0x13, OPTROX_REG_INDEX2);
  else
    outb(0x03, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x08, OPTROX_REG_STATUS); // WTF?
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  if (x)
    outb(0xE6, OPTROX_REG_INDEX2);
  else
    outb(0x06, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x80 | 0x10 | OPTROX_CMD_LAMP_BIT | 0x02, OPTROX_REG_CMD);	// 0x96

  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0xC5, OPTROX_BASE + 0x01);
  outb(0x5B, OPTROX_BASE + 0x00);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x00, OPTROX_BASE + 0x00);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_false();
  wait_something_true();
  wait_something_false();
  inb(OPTROX_REG_INDEX1);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  outb(0x73, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x33, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  if (x)
    outb(0x13, OPTROX_REG_INDEX2);
  else
    outb(0x03, OPTROX_REG_INDEX2);
  for (int i = 0; i < 4; i++)
    inb(OPTROX_REG_DATA);	// 0x00		1346, 4594
}

void init_1() {
  power(true);
  inb(OPTROX_REG_STATUS2);
  reg_write(0x00, 0x00, 0x15);
  reg_write(0x01, 0x00, 0x04);
  reg_write(0x02, 0x00, 0x97);
  reg_write(0x03, 0x00, 0x19);
  reg_read(0x00, 0x00);	// 0x15
  reg_read(0x01, 0x00);	// 0x04
  reg_read(0x02, 0x00);	// 0x97
  reg_read(0x03, 0x00);	// 0x19
  inb(OPTROX_REG_STATUS); //0xBB
  inb(OPTROX_REG_STATUS); //0xBB
  outb(0x00, OPTROX_BASE + 0x01);
  outb(OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);
  inb(OPTROX_REG_STATUS); //0xBB
  reg_read(0x10, 0x00); // 0x00
  reg_read(0x11, 0x00); // 0x00
  reg_read(0x12, 0x00); // 0x00
  reg_read(0x13, 0x00); // 0x00
  inb(OPTROX_REG_STATUS2); //0x52
  inb(OPTROX_REG_STATUS); //0xBB
  inb(OPTROX_REG_STATUS); //0xBB    scan_preview_init.txt line 67    trace2-start-and-preview-uniq line 940
  outb(OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);
  outb(0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(0x01, OPTROX_REG_INDEX1);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_SRAM, OPTROX_REG_MODE);
  outb(0x10 | OPTROX_CMD_LAMP_BIT | 0x02, OPTROX_REG_CMD);
  inb(OPTROX_REG_STATUS); //0xBB
  outb(0x00, OPTROX_BASE + 0x01);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(0x40, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0xFF, 2776);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(0x50, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0xFF, 2776);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(0x60, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0xFF, 2776);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(0x70, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0x00, 13);
  write_data_multiple(0x04, 48);
  write_data_multiple(0x00, 4035);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(0x70, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0x02, 13);
  write_data_multiple(0x06, 48);
  write_data_multiple(0x02, 1856);
  write_data_multiple(0x42, 49);
  write_data_multiple(0x4A, 1);
  write_data_multiple(0x02, 809);
  write_data_multiple(0x00, 1319);
  write_data_multiple(0x00, 1);
  
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x96
  outb(0x01, OPTROX_REG_INDEX2);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x96
  outb(0x01, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x56
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0xC0, OPTROX_BASE + 0x01);
  outb(0x08, OPTROX_REG_STATUS); // WTF?
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x56  
  outb(0x06, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x56
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x96

  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0xC5, OPTROX_BASE + 0x01);
  outb(0x80, OPTROX_BASE + 0x00);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x00, OPTROX_BASE + 0x00);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x16
  outb(0x70, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x17	trace2-start-and-preview.uniq line 1000		scan_preview_init.txt line 127
  wait_something_false();
  wait_something_true();
  wait_something_false();
  inb(OPTROX_REG_INDEX1);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x17
  outb(0x73, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x33, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x01, OPTROX_REG_INDEX2);
  for (int i = 0; i < 48; i++)
    inb(OPTROX_REG_DATA);	// 0xC8 and 0xC9 bytes scattered randomly(?) - something scanned?

  calibrate_something(0x40, 0, 0);	// 0x00				1078
  calibrate_something(0x60, 0, 0);	// 0x08 and 0x07 bytes scattered randomly(?) - something scanned?	1131		198
  calibrate_something(0x50, 0, 0);	// 0x00 and 0x06 bytes scattered randomly(?) - something scanned?	1170		225
  calibrate_something(0x58, 0, 0);	// 0x00
  calibrate_something(0x5C, 0, 0);	// 0x00
  calibrate_something(0x5E, 0, 0);	// 0x00
  calibrate_something(0x5F, 0, 0);	// 0x01 and 0x02 bytes scattered randomly(?) - something scanned?	1285		330
// ^ looks like bisection - finding a calibration value - maybe the largest one that results in all zeros?


  upload_something(0);	// 1346(?)


  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  write_data_multiple(0x02, 1000);
  write_data_multiple(0x00, 825);
  write_data_multiple(0x40, 49);
  write_data_multiple(0x41, 1);
  write_data_multiple(0x00, 897);
  write_data_multiple(0x04, 4);
  write_data_multiple(0x00, 1320);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x08, OPTROX_REG_STATUS); // WTF?
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x06, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x80 | 0x10 | OPTROX_CMD_LAMP_BIT | 0x02, OPTROX_REG_CMD);	// 0x96
  outb(0x80 | 0x10 | OPTROX_CMD_LAMP_BIT | 0x02, OPTROX_REG_CMD);	// 0x96
  outb(0x80 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0x94
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  for (int i = 0; i < 180; i++) {		//180..2160
    wait_something_true();
    wait_something_false();
    outb(0xFC, OPTROX_REG_CMD); ///////
  }
  wait_something_true();
  wait_something_false();		// 2163			1208

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0xBC, OPTROX_REG_CMD);
  inb(OPTROX_REG_STATUS);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  write_data_multiple(0x02, 1000);
  write_data_multiple(0x00, 825);
  write_data_multiple(0x40, 49);
  write_data_multiple(0x41, 1);
  write_data_multiple(0x00, 897);
  write_data_multiple(0x04, 4);
  write_data_multiple(0x00, 1320);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x08, OPTROX_REG_STATUS); // WTF?
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x06, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  for (int i = 0; i < 180; i++) {	//	180..3142
    wait_something_true();
    wait_something_false();
    outb(0xF4, OPTROX_REG_CMD); ///////
  }
  wait_something_true();
  wait_something_false();

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  write_data_multiple(0x02, 1000);
  write_data_multiple(0x00, 825);
  write_data_multiple(0x40, 49);
  write_data_multiple(0x41, 1);
  write_data_multiple(0x00, 897);
  write_data_multiple(0x04, 4);
  write_data_multiple(0x00, 1320);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x08, OPTROX_REG_STATUS); // WTF?
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x06, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xB4
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x73, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02 | OPTROX_MODE_AUTOINCREMENT_READ, OPTROX_REG_MODE);//0x1F
  wait_something_true();
  wait_something_false();
  wait_something_true();
  wait_something_false();	//  3179 	2224
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0xBC, OPTROX_REG_CMD);
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x10, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x15, OPTROX_REG_DATA);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x11, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x04, OPTROX_REG_DATA);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x12, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x97, OPTROX_REG_DATA);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x13, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x19, OPTROX_REG_DATA);
  inb(OPTROX_REG_STATUS);	//0xBB
  inb(OPTROX_REG_STATUS2);	//0x5A	3204		2249
  outb(0x80 | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x8E
  outb(0x40, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x4E
  outb(0xE6, OPTROX_REG_INDEX2);
  outb(0x80 | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x8E
  outb(0x40, OPTROX_REG_INDEX2);
  outb(0x80 | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x8E
  outb(0x10, OPTROX_REG_INDEX2);
  power(false);		// 3225		2270
}

bool presence_test_sram_read(char offset) {
  bool retval = true;

  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(offset + 0x00, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  if (inb(OPTROX_REG_DATA) != 0x15)
    retval=false;
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(offset + 0x01, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  if (inb(OPTROX_REG_DATA) != 0x04)
    retval=false;
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(offset + 0x02, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  if (inb(OPTROX_REG_DATA) != 0x97)
    retval=false;
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(offset + 0x03, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  if (inb(OPTROX_REG_DATA) != 0x19)
    retval=false;

  return retval;
}

void init_2() {
  /* this is probably BS as the scanner access is turned off here */
  inb(OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x15, OPTROX_REG_DATA);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x01, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x04, OPTROX_REG_DATA);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x02, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x97, OPTROX_REG_DATA);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x0E
  outb(0x00, OPTROX_REG_INDEX2);
  outb(0x19, OPTROX_REG_DATA);

  presence_test_sram_read(0x00);	// false

  /* Some presence test */
  for (int i = 0; i < 0x0F; i++)
    inb(OPTROX_BASE + i);	// 0xFF = fail as the power is still off

  power(true);	// 4170
  inb(OPTROX_REG_STATUS2);	//0x5A
  inb(OPTROX_REG_STATUS);	//0xBB
  inb(OPTROX_REG_STATUS);	//0xBB
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x80 | 0x20 | 0x10 | OPTROX_CMD_DIR_FORWARD_BIT | OPTROX_CMD_LAMP_BIT, OPTROX_REG_CMD);	// 0xBC  
  inb(OPTROX_REG_STATUS);	//0xBB

  presence_test_sram_read(0x10);	// true
  inb(OPTROX_REG_STATUS2);	//0x5A
  inb(OPTROX_REG_STATUS);	//0xBB
  inb(OPTROX_REG_STATUS);	//0xBB
  outb(0xBC, OPTROX_REG_CMD);
  outb(0xBC, OPTROX_REG_CMD);
  outb(0x9C, OPTROX_REG_CMD);
  outb(0x94, OPTROX_REG_CMD);
  outb(0x94, OPTROX_REG_CMD);
  outb(OPTROX_MODE_BLOCK0_ONLY | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x4E
  outb(0x03, OPTROX_REG_INDEX1);
  outb(OPTROX_MODE_BLOCK0_ONLY | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x4E
  outb(0x96, OPTROX_REG_CMD);
  inb(OPTROX_REG_STATUS);	//0xBB	4205
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(OPTROX_MODE_BLOCK0_ONLY | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x4E
  outb(OPTROX_MODE_BLOCK0_ONLY | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x4E
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x40, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);
  write_data_multiple(0xFF, 2776);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x50, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0xFF, 2776);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x60, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0xFF, 2776);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x70, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF
  write_data_multiple(0x02, 13);
  write_data_multiple(0x06, 48);
  write_data_multiple(0x02, 939);
  write_data_multiple(0x00, 825);
  write_data_multiple(0x40, 49);
  write_data_multiple(0x41, 1);
  write_data_multiple(0x00, 2221);

  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x1E
  outb(0x70, OPTROX_REG_INDEX2);
  inb(OPTROX_REG_INDEX1);	//0xFF	4233
  write_data_multiple(0x02, 13);
  write_data_multiple(0x06, 48);
  write_data_multiple(0x02, 1856);
  write_data_multiple(0x42, 49);
  write_data_multiple(0x4A, 1);
  write_data_multiple(0x02, 809);
  write_data_multiple(0x00, 1319);
  write_data_multiple(0x01, 1);

  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x11, OPTROX_REG_INDEX2);
  outb(0x80 | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x9E
  outb(0x11, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E  
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0xC1, OPTROX_BASE + 0x01);
  outb(0x08, OPTROX_REG_STATUS); // WTF?
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E  
  outb(0xE6, OPTROX_REG_INDEX2);
  outb(OPTROX_MODE_BLOCK0_ONLY | OPTROX_MODE_AUTOINCREMENT_WRITE | 0x08 | OPTROX_MODE_SRAM | 0x02, OPTROX_REG_MODE);//0x5E  
  outb(0x03, OPTROX_REG_INDEX1);
  outb(0x80 | 0x10 | OPTROX_CMD_LAMP_BIT | 0x02, OPTROX_REG_CMD);	// 0x96		4255

  calibrate_something(0x80, 1, 1);// 0xC7, 0xC8 and 0xC9 bytes scattered randomly(?) - something scanned?	4294
  calibrate_something(0x40, 0, 1);// 0x00
  calibrate_something(0x60, 0, 1);	// 0x07, 0x08 and 0x07 bytes scattered randomly(?) - something scanned?
  calibrate_something(0x50, 0, 1);	// 0x00 and 0x06 bytes scattered randomly(?) - something scanned?
  calibrate_something(0x58, 0, 1);	// 0x00
  calibrate_something(0x5C, 0, 1);	// 0x00
  calibrate_something(0x5E, 0, 1);	// 0x00
  calibrate_something(0x5F, 0, 1);	// 0x00 and 0x01 bytes scattered randomly(?) - something scanned?	4533
// ^ looks like bisection - finding a calibration value - maybe the largest one that results in all zeros?

  upload_something(1);	// 4594
}

void scan_one_line(char *line) {
  /* First 26*16=416 bytes */
  for (int i = 0; i < 26; i++) {
    wait_something_false();
    for (int j = 0; j < 16; j++)
      line[i*16+j] = inb(OPTROX_REG_DATA);
  }
  /* Last 8 bytes */
  wait_something_false();
  for (int j = 0; j < 8; j++)
    line[416+j] = inb(OPTROX_REG_DATA);

}

//#undef inb
//#define inb(x) printf("inb(%hx)\n",x)
//#undef outb
//#define outb(x,y) printf("outb(%hhx,%hx)\n",x,y)

void scan() {
  char c0[] = { 0xD0, 0xC1, 0xD2, 0xC0, 0xD1, 0xC2 };
  char c1 = 0xD1;
  char c2[] = { 0x5A, 0x73, 0x64 };
  char c3[] = { 0x0E, 0x00, 0x10 };
  char c4[] = { 0x62, 0x51 };
  char c5[] = { 0x52, 0x61 };
  char line[3][424];	// 3 lines for R,G,B
  FILE *f = fopen("scan.raw", "w");
  if (!f) {
    perror("fopen");
    exit(1);
  }
  for (int i = 0; i < 2000; i++) {
    wait_something_true();
    wait_something_false();
    inb(OPTROX_REG_STATUS);
    outb(0x17, OPTROX_REG_MODE);
    outb(0xDC, OPTROX_REG_CMD);
    if (i % 4 == 0)
      outb(c0[(i % 24)/4], OPTROX_BASE + 0x01);
    wait_something_true();
    wait_something_false();
    inb(OPTROX_REG_STATUS);
    outb(c1, 0x341);
    outb(c1 + 0x04, 0x341);
    outb(c2[i % 3], 0x340);
    outb(c1, 0x341);
    outb(c3[i % 3], 0x340);
    c1++;
    if ((c1 & 0xF) > 2)
      c1 &= 0xF0;
    if (i % 4 == 3) {
      if ((c1 & 0xF0) == 0xC0)
        c1 = 0xD0 + (c1 & 0x0F);
      else
        c1 = 0xC0 + (c1 & 0x0F);
    }
    inb(0x346);
    outb(0x17, OPTROX_REG_MODE);
    if (i == 0)
      outb(0x72, OPTROX_REG_INDEX2);
    else
      outb(c4[i % 2], OPTROX_REG_INDEX2);
    outb(0x1F, OPTROX_REG_MODE);
    outb(0xDC, OPTROX_REG_CMD);
    outb(0x1F, OPTROX_REG_MODE);
    outb(0x1F, OPTROX_REG_MODE);
    outb(0x1F, OPTROX_REG_MODE);
    outb(c5[i % 2], OPTROX_REG_INDEX2);
    inb(0x345);
    scan_one_line(line[i % 3]);
    if (i % 3 == 0 && i != 0) {		// We have all 3 lines
      for (int j = 0; j < 424; j++) {
        fwrite(&line[0][j], 1, 1, f);	// R
        fwrite(&line[1][j], 1, 1, f);	// G
        fwrite(&line[2][j], 1, 1, f);	// B
      }
    }
  }
  fclose(f);
}

void sram2pgm(void) {
  FILE *f = fopen("out.pgm", "wb");
  fputs("P2\n", f);
  fputs("256 128\n", f);
  fputs("255\n", f);
  for (int i = 0; i < 32768; i++) {
    fprintf(f, "%d ", read_sram(i));
    if ((i+1) % 256 == 0)
      fputs("\n", f);
  }
  fclose(f);
}

int main(void) {
  if (ioperm(OPTROX_BASE, 0x10, 1)) {
    perror("ioperm");
    return 1;
  }
//init_1();
printf("Run second initialization manually and press ENTER\n");
getchar();
scan();
exit(0);
power(true);
home();
  char buffer[32768];
//  clear_sram();
//  for (int i = 0; i < 32768; i++)
//    if (read_sram(i) != 0) {
//      printf("Not empty!\n");
//      exit(1);
//    }
//  printf("empty\n");
//  home();
//sram2pgm();
//scan();
/*exit(0);
for (int i = 0; i < 32768; i++)
  buffer[i] = read_sram(i);
FILE *f = fopen("out.bin","w");
fwrite(buffer, 1, sizeof(buffer), f);
fclose(f);
  exit(0);*/
/*  FILE *f = fopen("random.test","r");
  if (!f)
    perror("fopen");
  fread(buffer,1,sizeof(buffer),f);
  fclose(f);
  for (int i = 0; i < 32768; i++)
   write_sram(i,buffer[i]);
//  write_sram_block(0, sizeof(buffer), buffer);*/

/*  FILE *f = fopen("random.out","w");
  if (!f)
    perror("fopen");
//  for (int i = 0; i < 32768; i++)
//   buffer[i] = read_sram(i); 
  read_sram_block(0, sizeof(buffer), buffer);
  fwrite(buffer,1,sizeof(buffer),f);
  fclose(f);*/
//return 0;
//  scan();
//  power(true);
//  power(true);
//  clear_sram();
//  lamp_power(true);
//  printf("%x %x %x %x\n", read_sram(0x00), read_sram(0x01), read_sram(0x02), read_sram(0x03));
//  for (unsigned char i = 0; i < 255; i++)
//    if (i == 0) write_sram(i,255-i); else outb(255-i, OPTROX_REG_DATA);
//  scan();
//  for (unsigned int i = 0; i < 32768; i++) {
//      printf("%x:%x\n", i, (i == 0) ? read_sram(i) : inb(OPTROX_REG_DATA));
//    char b = read_sram(i);
//    if (b)
//      printf("%x:%x\n", i, b);
//  }
//  poweron_from_coma();
  for (int i=0x341; i < 0x350; i++)
      printf("0x%x: 0x%x\n", i, inb(i));
      
//  printf("status=0x%x\n", status());
//  poweron_from_coma();
//  outb(0x10, OPTROX_REG_CMD);
//  for (int i=0; i < 100000; i++)
//    outb(OPTROX_CMD_MOVE | OPTROX_CMD_DIR_FORWARD_BIT, OPTROX_REG_CMD);
//  home();
  printf("status=0x%x\n", status());
//  for (int i = 0; i < 4096; i++)
//    outb(0xFF, OPTROX_REG_DATA);
//    printf("0x344=%x\n", inb(OPTROX_REG_DATA));
//  write_sram(0x00, 0x04);
//  write_sram(0x01, 0x04);
//  write_sram(0x02, 0x97);
//  write_sram(0x03, 0x19);
//  outb(0x66, OPTROX_BASE + 4);
//  printf("%x\n", read_sram(0x00));
//  printf("%x %x %x %x\n", read_sram(0x00), read_sram(0x01), read_sram(0x02), read_sram(0x03));

  return 0;
}

