Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

arch_pit.c

/* $Id: arch_pit.c,v 1.33 2009-01-28 12:59:16 potyra Exp $ 
 *
 * Copyright (C) 2005-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/* set this to '1' to add debug messages */
#define DEBUG_PIT       0

#ifdef STATE

struct {
      unsigned long tsc_to_timer;   /* ticks per timer interval */
      unsigned long long last_event_tsc;
      unsigned int running;

      unsigned char status_latched[3];
      unsigned char status[3];
      unsigned char rw[3];
      unsigned char mode[3];
      unsigned char bcd[3];
      unsigned short start[3];
      unsigned char write_state[3];
      unsigned short act[3];
      unsigned char read_state[3];
      unsigned short latch[3];
      unsigned char latch_state[3];
      unsigned int gate[3];
      unsigned char null_count[3];
      unsigned char out[3];
} NAME;

#endif /* STATE */
#ifdef BEHAVIOR

#define UMPIT_REAL_HZ   1193180

/*forward*/ static void
NAME_(event)(void *data);

static void
NAME_(tick_one)(struct cpssp *css, unsigned int ch, unsigned long ticks)
{
      assert(/* 0 <= ch && */ ch < 3);
      assert(0 < ticks);

      switch (css->NAME.mode[ch]) {
      case 0:
            /*
             * Mode 0
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.null_count[ch] == 1) {
                  css->NAME.act[ch] = css->NAME.start[ch];
                  css->NAME.null_count[ch] = 0;
                  ticks--;
            }

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return;
            }

            if (css->NAME.act[ch] <= ticks
             && css->NAME.out[ch] == 0) {
                  /*
                   * Timer expires.
                   */
                  css->NAME.out[ch] = 1;
                  NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
            }

            /*
             * Timer counting.
             */
            css->NAME.act[ch] -= ticks;
            break;

      case 1:
            /*
             * Mode 1
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            assert(0);                    /* FIXME VOSSI */

      case 2:
      case 6:
            /*
             * Mode 2 binary
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return;
            }

            while (0 < ticks) {
                  if (css->NAME.act[ch] == 0x0001) {
                        /*
                         * Timer 0x0001 -> N
                         */
                        css->NAME.act[ch] = css->NAME.start[ch];
                        css->NAME.null_count[ch] = 0;
                        css->NAME.out[ch] = 1;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
                        ticks--;

                  } else if (css->NAME.act[ch] - 1 <= ticks) {
                        /*
                         * Timer N -> 0x0001
                         */
                        ticks -= css->NAME.act[ch] - 1;
                        css->NAME.act[ch] = 0x0001;
                        css->NAME.out[ch] = 0;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);

                  } else { assert(ticks < css->NAME.act[ch] - 1);
                        /*
                         * Timer N -> N-ticks
                         */
                        css->NAME.act[ch] -= ticks;
                        ticks = 0;
                  }
            }
            break;

      case 3:
      case 7:
            /*
             * Mode 3
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return;
            }

            while (0 < ticks) {
                  if (css->NAME.act[ch] == 0x0002) {
                        /*
                         * Timer 0x0002 -> N
                         */
                        css->NAME.act[ch] = css->NAME.start[ch];
                        css->NAME.null_count[ch] = 0;
                        css->NAME.out[ch] = ! css->NAME.out[ch];
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
                        ticks--;

                  } else if (css->NAME.act[ch] & 1) {
                        if (css->NAME.out[ch]) {
                              /*
                               * Timer N -> N-1
                               */
                              css->NAME.act[ch] -= 1;
                        } else {
                              /*
                               * Timer N -> N-3
                               */
                              css->NAME.act[ch] -= 3;
                        }
                        ticks--;

                  } else if (css->NAME.act[ch] / 2 - 2 <= ticks) {
                        /*
                         * Timer X -> 0x0002
                         */
                        ticks -= css->NAME.act[ch] / 2 - 2;
                        css->NAME.act[ch] = 0x0002;

                  } else { assert(ticks < css->NAME.act[ch] / 2 - 2);
                        /*
                         * Timer X -> Y
                         */
                        css->NAME.act[ch] -= ticks * 2;
                        ticks = 0;
                  }
            }
            break;

      case 4:
            /*
             * Mode 4
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return;
            }

            while (0 < ticks) {
                  if (css->NAME.act[ch] == 0x0001) {
                        /*
                         * Timer 0x0001 -> 0x0000
                         */
                        ticks--;
                        css->NAME.act[ch] = 0x0000;

                        css->NAME.out[ch] = 0;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);

                  } else if (css->NAME.act[ch] == 0x0000) {
                        /*
                         * Timer 0x0000 -> 0xffff
                         */
                        ticks--;
                        css->NAME.act[ch] = 0xffff;

                        css->NAME.out[ch] = 1;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);

                  } else if (css->NAME.act[ch] - 1 <= ticks) {
                        /*
                         * Timer X -> 0x0001
                         */
                        ticks -= css->NAME.act[ch] - 1;
                        css->NAME.act[ch] = 0x0001;

                  } else { assert(ticks < css->NAME.act[ch] - 1);
                        /*
                         * Timer X -> Y
                         */
                        css->NAME.act[ch] -= ticks;
                        ticks = 0;
                  }
            }
            break;

      case 5:
            /*
             * Mode 5
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            assert(0);

      default:
            fprintf(stderr, "css->NAME.mode[%d]=%d\n", ch, css->NAME.mode[ch]);
            assert(0);
      }
}

static void
NAME_(tick_all)(struct cpssp *css, unsigned long ticks)
{
      int ch;

      assert(0 < ticks);

      for (ch = 0; ch < 3; ch++) {
            NAME_(tick_one)(css, ch, ticks);
      }
}

static void
NAME_(update)(struct cpssp *css)
{
      unsigned long long cur_tsc;
      unsigned long delta;
      unsigned long ticks;

      cur_tsc = time_virt();

      assert(css->NAME.last_event_tsc <= cur_tsc);

      /* Might overflow on fast machines with high load - FIXME Florian */
      delta = (unsigned long) (cur_tsc - css->NAME.last_event_tsc);
      ticks = delta / css->NAME.tsc_to_timer;
      css->NAME.last_event_tsc = cur_tsc - delta % css->NAME.tsc_to_timer;
      
      assert(css->NAME.last_event_tsc <= cur_tsc);
      
      if (0 < ticks) {
            NAME_(tick_all)(css, ticks);
      }
}

static unsigned long
NAME_(next_one)(struct cpssp *css, unsigned int ch)
{
      assert(/* 0 <= ch && */ ch < 3);

      switch (css->NAME.mode[ch]) {
      case 0:
            /*
             * Mode 0
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.null_count[ch] == 1) {
                  /* Next clk will load start value. */
                  return 0x00000001;
            }

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return 0xffffffff;
            }

            if (css->NAME.out[ch] == 1) {
                  /* Timer already expired. */
                  return 0xffffffff;
            }

            /* Timer running. */
            return css->NAME.act[ch];

      case 1:
            /*
             * Mode 1
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */
            assert(0);                    /* FIXME VOSSI */

      case 2:
      case 6:
            /*
             * Mode 2
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return 0xffffffff;
            }

            if (css->NAME.act[ch] == 0x0000) {
                  return css->NAME.start[ch];
            } else {
                  return css->NAME.act[ch];
            }

      case 3:
      case 7:
            /*
             * Mode 3
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return 0xffffffff;
            }

            if (css->NAME.act[ch] == 0x0002) {
                  return 1;
            } else if (css->NAME.act[ch] & 1) {
                  if (css->NAME.out[ch]) {
                        return (css->NAME.act[ch] - 1) / 2;
                  } else {
                        return (css->NAME.act[ch] - 3) / 2;
                  }
            } else {
                  return css->NAME.act[ch] / 2;
            }

      case 4:
            /*
             * Mode 4
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */

            if (css->NAME.gate[ch] == 0) {
                  /* Timer stopped. */
                  return 0xffffffff;
            }

            return css->NAME.act[ch] - 1;

      case 5:
            /*
             * Mode 5
             */
            assert(! css->NAME.bcd[ch]);  /* FIXME VOSSI */
            assert(0);                    /* FIXME VOSSI */

      default:
            assert(0);
            return 0xffffffff;
      }
}

static unsigned long
NAME_(next_all)(struct cpssp *css)
{
      unsigned long next;
      int ch;

      next = 0xffffffff;

      for (ch = 0; ch < 3; ch++) {
            unsigned long tmp;

            tmp = NAME_(next_one)(css, ch);
            if (tmp < next) {
                  next = tmp;
            }
      }

      return next;
}

static void
NAME_(stop)(struct cpssp *css)
{
      if (css->NAME.running) {      
            int ret;

            ret = time_call_delete(NAME_(event), css);
            assert(ret == 0);

            css->NAME.running = 0;
      }

      NAME_(update)(css);
}

static void
NAME_(start)(struct cpssp *css)
{
      unsigned long next;

      assert(! css->NAME.running);

      next = NAME_(next_all)(css);

      if (next == 0xffffffff) {
            /* No next event. */
            return;
      }
      next++; /* FIXME VOSSI */
      assert(1 <= next);

      if (css->state_power) {
            css->NAME.running = 1;
            time_call_at(css->NAME.last_event_tsc
                        + (unsigned long long) next * css->NAME.tsc_to_timer,
                        NAME_(event), css);
      }
}

static void
NAME_(event)(void *_css)
{
      struct cpssp *css = (struct cpssp *) _css;

      /* assert(css->NAME.running); */
      css->NAME.running = 0;

      NAME_(stop)(css);
      NAME_(start)(css);
}

static void
NAME_(gate_set)(struct cpssp *css, unsigned int ch, unsigned char flag)
{
      NAME_(stop)(css);

      css->NAME.gate[ch] = flag;

      switch (css->NAME.mode[ch]) {
      case 2:
            if (flag == 0) {
                  css->NAME.out[ch] = 1;
                  NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
                  NAME_(out_period_set)(css, ch, (unsigned long long) -1);
            } else {
                  /* Reload counter. */
                  /* FIXME VOSSI */
            }
            break;
      }

      NAME_(start)(css);
}

static int
NAME_(timer_get)(struct cpssp *css, unsigned int ch)
{
      int ret;

      assert(ch == 2);

      NAME_(stop)(css);

      /* Speaker timer. */
      ret = css->NAME.out[ch];

      NAME_(start)(css);

      return ret;
}

static unsigned char
NAME_(_inb)(struct cpssp *css, unsigned short port)
{
      unsigned char value;

      if (css->NAME.status_latched[port]) {
            value = css->NAME.status[port];
            css->NAME.status_latched[port] = 0;

      } else {
            switch (port) {
            case 0: /* clock timer */
            case 1: /* memory refresh timer */
            case 2: /* speaker timer */
                  assert(port == 0 || port == 2);

                  if (css->NAME.latch_state[port] & 1) {
                        /* Reading low byte of latch. */
                        value = (css->NAME.latch[port] >> 0) & 0xff;
                        css->NAME.latch_state[port] &= ~1;

                  } else if (css->NAME.latch_state[port] & 2) {
                        /* Reading high byte of latch. */
                        value = (css->NAME.latch[port] >> 8) & 0xff;
                        css->NAME.latch_state[port] &= ~2;

                  } else if (css->NAME.read_state[port] & 1) {
                        /* Reading low byte of counter. */
                        value = (css->NAME.act[port] >> 0) & 0xff;
                        css->NAME.read_state[port] &= ~1;

                  } else if (css->NAME.read_state[port] & 2) {
                        /* Reading high byte of counter. */
                        value = (css->NAME.act[port] >> 8) & 0xff;
                        css->NAME.read_state[port] &= ~2;

                  } else {
                        assert(0);  /* Mustn't happen. */
                        value = 0;  /* To make gcc happy. */
                  }
                  if (css->NAME.read_state[port] == 0) {
                        css->NAME.read_state[port] = css->NAME.rw[port];
                  }
                  break;

            case 3:
                  /* control register */
                  /* write-only */
#if 0 /* FIXME VOSSI */
                  assert(0);
#else
                  fprintf(stderr, "WARNING: reading reg 3 of PIT!\n");
                  value = 0;
#endif
                  break;

            default:
                  assert(0);
                  value = 0;  /* To make gcc happy. */
            }
      }

#if DEBUG_PIT
      fprintf(stderr, "%s: read %02x from port %u\n",
                  __FUNCTION__, value, port);
#endif
      return value;
}

static void
NAME_(inb)(struct cpssp *css, unsigned char *valuep, unsigned short port)
{
      NAME_(stop)(css);
      *valuep = NAME_(_inb)(css, port);
      NAME_(start)(css);
}

static void
NAME_(_outb)(struct cpssp *css, unsigned char value, unsigned short port)
{
#if DEBUG_PIT
      fprintf(stderr, "%s: write 0x%02x to port %d\n", __FUNCTION__,
                  value, port);
#endif

      switch (port) {
      case 0: /* clock timer */
      case 1: /* memory refresh timer */
      case 2: /* speaker timer */

            if (css->NAME.write_state[port] & 0x1) {
                  /* should write low byte of counter */
                  css->NAME.start[port] &= 0xff00;
                  css->NAME.start[port] |= value << 0;
                  css->NAME.write_state[port] &= ~0x1;

                  switch (css->NAME.mode[port]) {
                  case 0:
                        css->NAME.act[port] &= 0xff00;
                        css->NAME.act[port] |= value << 0;
                        break;
                  }

            } else if (css->NAME.write_state[port] & 0x2) {
                  /* should write high byte of counter */
                  css->NAME.start[port] &= 0x00ff;
                  css->NAME.start[port] |= value << 8;
                  css->NAME.write_state[port] &= ~0x2;

                  switch (css->NAME.mode[port]) {
                  case 0:
                        css->NAME.act[port] &= 0x00ff;
                        css->NAME.act[port] |= value << 8;
                        break;
                  }

                  if (css->NAME.start[port] == 0) {
                        NAME_(out_period_set)(css, port,
                              (unsigned long long) -1);
                  } else {
                        NAME_(out_period_set)(css, port,
                              TIME_HZ / UMPIT_REAL_HZ * css->NAME.start[port]);
                  }
            }

            if ((css->NAME.write_state[port] & 0x3) == 0) {
                  css->NAME.write_state[port]
                        = css->NAME.rw[port];
                  css->NAME.null_count[port] = 1;

                  switch (css->NAME.mode[port]) {
                  case 0:
                        css->NAME.out[port] = 0;
                        NAME_(out_val_set)(css, port, css->NAME.out[port]);
                        break;
                  }
            }
            break;

      case 3: {
            /*
             * Control register
             */
            unsigned char ch;

            if (((value >> 6) & 3) == 3) {
                  /*
                   * Read-back command.
                   */
                  for (ch = 0; ch < 3; ch++) {
                        if (! (value & (2 << ch))) {
                              continue;
                        }
                        if (! (value & 0x20)
                         && ! css->NAME.latch_state[ch]) {
                              css->NAME.latch[ch] = css->NAME.act[ch];
                              css->NAME.latch_state[ch] = css->NAME.rw[ch];
                        }
                        if (! (value & 0x10)
                         && ! css->NAME.status_latched[ch]) {
                              /* Status latch */
                              /* FIXME: add BCD and null count */
                              css->NAME.status[ch] = 0 /* FIXME VOSSI */
                                    | (css->NAME.rw[ch] << 4)
                                    | (css->NAME.mode[ch] << 1)
                                    | (css->NAME.bcd[ch] << 0);
                              css->NAME.status_latched[ch] = 1;
                        }
                  }

            } else if (((value >> 4) & 0x03) == 0) {
                  /*
                   * Latch command.
                   */
                  ch = (value >> 6) & 3;
                  assert(/* 0 <= ch && */ ch < 3);

                  css->NAME.latch[ch] = css->NAME.act[ch];
                  css->NAME.latch_state[ch] = css->NAME.rw[ch];

            } else {
                  /*
                   * Set command.
                   */
                  ch = (value >> 6) & 3;
                  assert(/* 0 <= ch && */ ch < 3);

                  css->NAME.rw[ch] = (value >> 4) & 0x3;
                  css->NAME.mode[ch] = (value >> 1) & 0x7;
                  css->NAME.bcd[ch] = (value >> 0) & 0x1;
                  css->NAME.write_state[ch] = (value >> 4) & 0x3;
                  css->NAME.read_state[ch] = (value >> 4) & 0x3;
                  css->NAME.latch_state[ch] = 0x0;
                  css->NAME.null_count[ch] = 1;

                  switch (css->NAME.mode[ch]) {
                  case 0:
                        css->NAME.out[ch] = 0;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
                        break;
                  case 2:
                        css->NAME.out[ch] = 1;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
                        break;
                  case 4:
                        css->NAME.out[ch] = 1;
                        NAME_(out_val_set)(css, ch, css->NAME.out[ch]);
                        break;
                  }
            }
            break;
          }
      default:
            assert(0);
      }
}

static void
NAME_(outb)(struct cpssp *css, unsigned char value, unsigned short port)
{
      NAME_(stop)(css);
      NAME_(_outb)(css, value, port);
      NAME_(start)(css);
}

void
NAME_(reset)(struct cpssp *css)
{
      unsigned int ch;

      /* More to reset? - FIXME VOSSI */

      css->NAME.last_event_tsc = 0;

      for (ch = 0; ch < 3; ch++) {
            css->NAME.rw[ch] = 3;
            css->NAME.mode[ch] = 0;
            css->NAME.bcd[ch] = 0;
            css->NAME.start[ch] = 0;
            css->NAME.write_state[ch] = 0;
            css->NAME.act[ch] = 0;
            css->NAME.read_state[ch] = 0;
            css->NAME.latch[ch] = 0;
            css->NAME.latch_state[ch] = 0;
            css->NAME.null_count[ch] = 0;
      }

/* FIXME VOSSI */
      css->NAME.gate[0] = 1;
      css->NAME.gate[1] = 1;
      css->NAME.gate[2] = 0;
      
      css->NAME.running = 0;
}

void
NAME_(init)(struct cpssp *css)
{
      css->NAME.tsc_to_timer = TIME_HZ / UMPIT_REAL_HZ;

      /* Must be a valid value */
      assert(80 < css->NAME.tsc_to_timer);
}

#endif /* BEHAVIOR */

Generated by  Doxygen 1.6.0   Back to index