from os import environ


def pit_mode_2(self):
    if environ.get("SKIP_EXPENSIVE"):
        self.skipTest("expensive test")

    self.mkfile("testit.bat", """\
pitmode2
rem end
""", newline="\r\n")

# compile sources
    self.mkcom_with_ia16("pitmode2", r"""
/*
  Sample program #15
  Demonstrates absolute timestamping in mode two
  Part of the PC Timing FAQ / Application notes
  By K. Heidenstrom (kheidens@actrix.gen.nz)

  Rewritten to be non-interactive and compile with ia16-elf-gcc
*/

#define _BORLANDC_SOURCE

#include <conio.h>
#include <dos.h>
#include <stdio.h>

typedef struct {
  unsigned int part;
  unsigned long ticks;
} __attribute__((packed)) timestamp;

// #define BIOS_TICK_COUNT_P ((volatile unsigned long far *)0x0040006CL)
volatile unsigned long __far *BIOS_TICK_COUNT_P = MK_FP(0x0040, 0x006C);

void set_mode2(void)
{
  unsigned int tick_loword;
  tick_loword = *BIOS_TICK_COUNT_P;
  while ((unsigned int)*BIOS_TICK_COUNT_P == tick_loword)
    /* do nothing */;

#if 0
  asm pushf;
  asm cli;
  outportb(0x43, 0x34); /* Channel 0, mode 2 */
  outportb(0x40, 0x00); /* Loword of divisor */
  outportb(0x40, 0x00); /* Hiword of divisor */
  asm popf;
#endif

  asm volatile("pushf\n"
               "push %%ax\n"
               "cli\n"
               "mov $0x34, %%al\n"
               "outb %%al, $0x43\n" /* Channel 0, mode 2 */
               "mov $0x00, %%al\n"
               "outb %%al, $0x40\n" /* Loword of divisor */
               "outb %%al, $0x40\n" /* Hiword of divisor */
               "pop %%ax\n"
               "popf\n"
               :
               :
               :
  );
  return;
}

void get_timestamp(timestamp *tsp)
{
  unsigned long tickcount1, tickcount2;
  unsigned int ctcvalue;
  unsigned char ctclow, ctchigh;

again:
  tickcount1 = *BIOS_TICK_COUNT_P;
  outportb(0x43, 0); /* Latch value */
  ctclow = inportb(0x40);
  ctchigh = inportb(0x40); /* Read count in progress */
  ctcvalue = -((ctchigh << 8) + ctclow);
  tickcount2 = *BIOS_TICK_COUNT_P;
  if (tickcount2 != tickcount1)
    goto again;
  tsp->ticks = tickcount1;
  tsp->part = ctcvalue;
  return;
}

int main(void)
{
  timestamp ts, ts1, ts2;
  uint64_t start;

  printf("Sample program #15 - Demonstrates absolute timestamping\n");
  printf("Part of the PC Timing FAQ / Application notes\n");
  printf("By K. Heidenstrom (kheidens@actrix.gen.nz)\n\n");

  set_mode2();

  // Info
  get_timestamp(&ts); /* Get timestamp */
  printf("Absolute timestamp: 0x%04X%04X%04X units of 0.8381 us\n", (unsigned int)(ts.ticks >> 16),
         (unsigned int)(ts.ticks & 0xFFFF), ts.part);

  // Continuous test for 2 minutes
  printf("\nContinuous timestamp test - start\n\n");

  for (start = (ts.ticks << 16) + ts.part; /* */; /* */) {
    ts2.ticks = ts1.ticks;
    ts2.part = ts1.part;
    ts1.ticks = ts.ticks;
    ts1.part = ts.part;
    get_timestamp(&ts);
//    printf("0x%04X%04X%04X\r", (unsigned int)(ts.ticks >> 16), (unsigned int)(ts.ticks & 0xFFFF), ts.part);
    if ((ts.ticks < ts1.ticks) || ((ts.ticks == ts1.ticks) && (ts.part < ts1.part))) { /* Went backwards? */
      printf("Timestamp went backwards: 0x%04X%04X%04X, 0x%04X%04X%04X, then 0x%04X%04X%04X\n",
             (unsigned int)(ts2.ticks >> 16), (unsigned int)(ts2.ticks & 0xFFFF), ts2.part,
             (unsigned int)(ts1.ticks >> 16), (unsigned int)(ts1.ticks & 0xFFFF), ts1.part,
             (unsigned int)(ts.ticks >> 16), (unsigned int)(ts.ticks & 0xFFFF), ts.part);
    }
    if (((uint64_t)((ts.ticks << 16) + ts.part) - start) > 143181004) // 120 secs: 120/(0.8381/1000000)
      break;
  }

  printf("\nContinuous timestamp test - complete\n\n");

  return 0;
}
""")

    starttime = self.utcnow()
    results = self.runDosemu("testit.bat", timeout=150)
    endtime = self.utcnow()

    self.assertIn("Continuous timestamp test - complete", results)
    self.assertNotIn("Timestamp went backwards", results)
    self.assertLess((endtime - starttime).seconds, 125)
