<commit_before>#include "types.h"
#include "mmu.h"
#include "kernel.hh"
#include "amd64.h"
#include "cpu.hh"
#include "traps.h"
#include "queue.h"
#include "spinlock.h"
#include "condvar.h"
#include "proc.hh"
#include "kmtrace.hh"
#include "bits.hh"

struct intdesc idt[256] __attribute__((aligned(16)));

// boot.S
extern u64 trapentry[];

void
trap(struct trapframe *tf)
{
  writegs(KDSEG);
  writemsr(MSR_GS_BASE, (u64)&cpus[cpunum()].cpu);

  if (tf->trapno == T_NMI) {
    // The only locks that we can acquire during NMI are ones
    // we acquire only during NMI.
    if (sampintr(tf))
      return;
    panic("NMI");
  }

  // XXX(sbw) sysenter/sysexit
  if(tf->trapno == T_SYSCALL){
    sti();
    if(myproc()->killed) {
      mtstart(trap, myproc());
      exit();
    }
    myproc()->tf = tf;
    syscall();
    if(myproc()->killed) {
      mtstart(trap, myproc());
      exit();
    }
    return;
  }

#if MTRACE
  if (myproc()->mtrace_stacks.curr >= 0)
    mtpause(myproc());
  mtstart(trap, myproc());
#endif

  switch(tf->trapno){
  case T_IRQ0 + IRQ_TIMER:
    if (mycpu()->timer_printpc) {
      cprintf("cpu%d: proc %s rip %lx rsp %lx cs %x\n",
              mycpu()->id,
              myproc() ? myproc()->name : "(none)",
              tf->rip, tf->rsp, tf->cs);
      if (mycpu()->timer_printpc == 2 && tf->rbp > KBASE) {
        uptr pc[10];
        getcallerpcs((void *) tf->rbp, pc, NELEM(pc));
        for (int i = 0; i < 10 && pc[i]; i++)
          cprintf("cpu%d:   %lx\n", mycpu()->id, pc[i]);
      }
      mycpu()->timer_printpc = 0;
    }
    if (mycpu()->id == 0)
      cv_tick();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE:
    ideintr();
    lapiceoi();
    piceoi();
    break;
  case T_IRQ0 + IRQ_IDE+1:
    // Bochs generates spurious IDE1 interrupts.
    break;
  case T_IRQ0 + IRQ_KBD:
    kbdintr();
    lapiceoi();
    piceoi();
    break;
  case T_IRQ0 + IRQ_COM2:
  case T_IRQ0 + IRQ_COM1:
    uartintr();
    lapiceoi();
    piceoi();
    break;
  case T_IRQ0 + 7:
  case T_IRQ0 + IRQ_SPURIOUS:
    cprintf("cpu%d: spurious interrupt at %x:%lx\n",
            mycpu()->id, tf->cs, tf->rip);
    lapiceoi();
  case T_TLBFLUSH: {
    u64 nreq = tlbflush_req.load();
    lapiceoi();
    lcr3(rcr3());
    mycpu()->tlbflush_done = nreq;
    break;
  }
  case T_SAMPCONF:
    lapiceoi();
    sampconf();
    break;
  default:
    if (tf->trapno == T_IRQ0+e1000irq) {
      e1000intr();
      lapiceoi();
      piceoi();
      break;
    }

    if (myproc() == 0 || (tf->cs&3) == 0)
      kerneltrap(tf);

    if(tf->trapno == T_PGFLT){
      uptr addr = rcr2();
      sti();
      if(pagefault(myproc()->vmap, addr, tf->err) >= 0){
#if MTRACE
        mtstop(myproc());
        if (myproc()->mtrace_stacks.curr >= 0)
          mtresume(myproc());
#endif
        return;
      }
      cli();
    }

    // In user space, assume process misbehaved.
    cprintf("pid %d %s: trap %lu err %d on cpu %d "
            "rip 0x%lx rsp 0x%lx addr 0x%lx--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno, tf->err,
            mycpu()->id, tf->rip, tf->rsp, rcr2());
    myproc()->killed = 1;
  }

  // Force process exit if it has been killed and is in user space.
  // (If it is still executing in the kernel, let it keep running
  // until it gets to the regular system call return.)
  if(myproc() && myproc()->killed && (tf->cs&3) == 0x3)
    exit();

  // Force process to give up CPU on clock tick.
  // If interrupts were on while locks held, would need to check nlock.
  if(myproc() && myproc()->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER)
    yield();

  // Check if the process has been killed since we yielded
  if(myproc() && myproc()->killed && (tf->cs&3) == 0x3)
    exit();

#if MTRACE
  mtstop(myproc());
  if (myproc()->mtrace_stacks.curr >= 0)
    mtresume(myproc());
#endif
}

void
inittrap(void)
{
  u64 entry;
  u8 bits;
  int i;

  bits = INT_P | SEG_INTR64;  // present, interrupt gate
  for(i=0; i<256; i++) {
    entry = trapentry[i];
    idt[i] = INTDESC(KCSEG, entry, bits);
  }
  entry = trapentry[T_SYSCALL];
  idt[T_SYSCALL] = INTDESC(KCSEG, entry, SEG_DPL(3) | SEG_INTR64 |INT_P);
}

void
initseg(void)
{
  extern void sysentry(void);
  volatile struct desctr dtr;
  struct cpu *c;

  dtr.limit = sizeof(idt) - 1;
  dtr.base = (u64)idt;
  lidt((void *)&dtr.limit);

  // TLS might not be ready
  c = &cpus[cpunum()];
  // Load per-CPU GDT
  memmove(c->gdt, bootgdt, sizeof(bootgdt));
  dtr.limit = sizeof(c->gdt) - 1;
  dtr.base = (u64)c->gdt;
  lgdt((void *)&dtr.limit);

#if 0
  // When executing a syscall instruction the CPU sets the SS selector
  // to (star >> 32) + 8 and the CS selector to (star >> 32).
  // When executing a sysret instruction the CPU sets the SS selector
  // to (star >> 48) + 8 and the CS selector to (star >> 48) + 16.
  u64 star = ((((u64)UCSEG|0x3) - 16)<<48)|((u64)KCSEG<<32);
  writemsr(MSR_STAR, star);
  writemsr(MSR_LSTAR, (u64)&sysentry);
  writemsr(MSR_SFMASK, FL_TF);
#endif
}

// Pushcli/popcli are like cli/sti except that they are matched:
// it takes two popcli to undo two pushcli.  Also, if interrupts
// are off, then pushcli, popcli leaves them off.
void
pushcli(void)
{
  u64 rflags;

  rflags = readrflags();
  cli();
  if(mycpu()->ncli++ == 0)
    mycpu()->intena = rflags & FL_IF;
}

void
popcli(void)
{
  if(readrflags()&FL_IF)
    panic("popcli - interruptible");
  if(--mycpu()->ncli < 0)
    panic("popcli");
  if(mycpu()->ncli == 0 && mycpu()->intena)
    sti();
}

// Record the current call stack in pcs[] by following the %rbp chain.
void
getcallerpcs(void *v, uptr pcs[], int n)
{
  uptr *rbp;
  int i;

  rbp = (uptr*)v;
  for(i = 0; i < n; i++){
    if(rbp == 0 || rbp < (uptr*)KBASE || rbp == (uptr*)(~0UL))
      break;
    pcs[i] = rbp[1];     // saved %rip
    rbp = (uptr*)rbp[0]; // saved %rbp
  }
  for(; i < n; i++)
    pcs[i] = 0;
}
<commit_msg>lapic error interrupt shows up on real hardware for some reason<commit_after>#include "types.h"
#include "mmu.h"
#include "kernel.hh"
#include "amd64.h"
#include "cpu.hh"
#include "traps.h"
#include "queue.h"
#include "spinlock.h"
#include "condvar.h"
#include "proc.hh"
#include "kmtrace.hh"
#include "bits.hh"

struct intdesc idt[256] __attribute__((aligned(16)));

// boot.S
extern u64 trapentry[];

void
trap(struct trapframe *tf)
{
  writegs(KDSEG);
  writemsr(MSR_GS_BASE, (u64)&cpus[cpunum()].cpu);

  if (tf->trapno == T_NMI) {
    // The only locks that we can acquire during NMI are ones
    // we acquire only during NMI.
    if (sampintr(tf))
      return;
    panic("NMI");
  }

  // XXX(sbw) sysenter/sysexit
  if(tf->trapno == T_SYSCALL){
    sti();
    if(myproc()->killed) {
      mtstart(trap, myproc());
      exit();
    }
    myproc()->tf = tf;
    syscall();
    if(myproc()->killed) {
      mtstart(trap, myproc());
      exit();
    }
    return;
  }

#if MTRACE
  if (myproc()->mtrace_stacks.curr >= 0)
    mtpause(myproc());
  mtstart(trap, myproc());
#endif

  switch(tf->trapno){
  case T_IRQ0 + IRQ_TIMER:
    if (mycpu()->timer_printpc) {
      cprintf("cpu%d: proc %s rip %lx rsp %lx cs %x\n",
              mycpu()->id,
              myproc() ? myproc()->name : "(none)",
              tf->rip, tf->rsp, tf->cs);
      if (mycpu()->timer_printpc == 2 && tf->rbp > KBASE) {
        uptr pc[10];
        getcallerpcs((void *) tf->rbp, pc, NELEM(pc));
        for (int i = 0; i < 10 && pc[i]; i++)
          cprintf("cpu%d:   %lx\n", mycpu()->id, pc[i]);
      }
      mycpu()->timer_printpc = 0;
    }
    if (mycpu()->id == 0)
      cv_tick();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE:
    ideintr();
    lapiceoi();
    piceoi();
    break;
  case T_IRQ0 + IRQ_IDE+1:
    // Bochs generates spurious IDE1 interrupts.
    break;
  case T_IRQ0 + IRQ_KBD:
    kbdintr();
    lapiceoi();
    piceoi();
    break;
  case T_IRQ0 + IRQ_COM2:
  case T_IRQ0 + IRQ_COM1:
    uartintr();
    lapiceoi();
    piceoi();
    break;
  case T_IRQ0 + 7:
  case T_IRQ0 + IRQ_SPURIOUS:
    cprintf("cpu%d: spurious interrupt at %x:%lx\n",
            mycpu()->id, tf->cs, tf->rip);
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_ERROR:
    cprintf("cpu%d: lapic error?\n", mycpu()->id);
    lapiceoi();
    break;
  case T_TLBFLUSH: {
    u64 nreq = tlbflush_req.load();
    lapiceoi();
    lcr3(rcr3());
    mycpu()->tlbflush_done = nreq;
    break;
  }
  case T_SAMPCONF:
    lapiceoi();
    sampconf();
    break;
  default:
    if (tf->trapno == T_IRQ0+e1000irq) {
      e1000intr();
      lapiceoi();
      piceoi();
      break;
    }

    if (myproc() == 0 || (tf->cs&3) == 0)
      kerneltrap(tf);

    if(tf->trapno == T_PGFLT){
      uptr addr = rcr2();
      sti();
      if(pagefault(myproc()->vmap, addr, tf->err) >= 0){
#if MTRACE
        mtstop(myproc());
        if (myproc()->mtrace_stacks.curr >= 0)
          mtresume(myproc());
#endif
        return;
      }
      cli();
    }

    // In user space, assume process misbehaved.
    cprintf("pid %d %s: trap %lu err %d on cpu %d "
            "rip 0x%lx rsp 0x%lx addr 0x%lx--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno, tf->err,
            mycpu()->id, tf->rip, tf->rsp, rcr2());
    myproc()->killed = 1;
  }

  // Force process exit if it has been killed and is in user space.
  // (If it is still executing in the kernel, let it keep running
  // until it gets to the regular system call return.)
  if(myproc() && myproc()->killed && (tf->cs&3) == 0x3)
    exit();

  // Force process to give up CPU on clock tick.
  // If interrupts were on while locks held, would need to check nlock.
  if(myproc() && myproc()->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER)
    yield();

  // Check if the process has been killed since we yielded
  if(myproc() && myproc()->killed && (tf->cs&3) == 0x3)
    exit();

#if MTRACE
  mtstop(myproc());
  if (myproc()->mtrace_stacks.curr >= 0)
    mtresume(myproc());
#endif
}

void
inittrap(void)
{
  u64 entry;
  u8 bits;
  int i;

  bits = INT_P | SEG_INTR64;  // present, interrupt gate
  for(i=0; i<256; i++) {
    entry = trapentry[i];
    idt[i] = INTDESC(KCSEG, entry, bits);
  }
  entry = trapentry[T_SYSCALL];
  idt[T_SYSCALL] = INTDESC(KCSEG, entry, SEG_DPL(3) | SEG_INTR64 |INT_P);
}

void
initseg(void)
{
  extern void sysentry(void);
  volatile struct desctr dtr;
  struct cpu *c;

  dtr.limit = sizeof(idt) - 1;
  dtr.base = (u64)idt;
  lidt((void *)&dtr.limit);

  // TLS might not be ready
  c = &cpus[cpunum()];
  // Load per-CPU GDT
  memmove(c->gdt, bootgdt, sizeof(bootgdt));
  dtr.limit = sizeof(c->gdt) - 1;
  dtr.base = (u64)c->gdt;
  lgdt((void *)&dtr.limit);

#if 0
  // When executing a syscall instruction the CPU sets the SS selector
  // to (star >> 32) + 8 and the CS selector to (star >> 32).
  // When executing a sysret instruction the CPU sets the SS selector
  // to (star >> 48) + 8 and the CS selector to (star >> 48) + 16.
  u64 star = ((((u64)UCSEG|0x3) - 16)<<48)|((u64)KCSEG<<32);
  writemsr(MSR_STAR, star);
  writemsr(MSR_LSTAR, (u64)&sysentry);
  writemsr(MSR_SFMASK, FL_TF);
#endif
}

// Pushcli/popcli are like cli/sti except that they are matched:
// it takes two popcli to undo two pushcli.  Also, if interrupts
// are off, then pushcli, popcli leaves them off.
void
pushcli(void)
{
  u64 rflags;

  rflags = readrflags();
  cli();
  if(mycpu()->ncli++ == 0)
    mycpu()->intena = rflags & FL_IF;
}

void
popcli(void)
{
  if(readrflags()&FL_IF)
    panic("popcli - interruptible");
  if(--mycpu()->ncli < 0)
    panic("popcli");
  if(mycpu()->ncli == 0 && mycpu()->intena)
    sti();
}

// Record the current call stack in pcs[] by following the %rbp chain.
void
getcallerpcs(void *v, uptr pcs[], int n)
{
  uptr *rbp;
  int i;

  rbp = (uptr*)v;
  for(i = 0; i < n; i++){
    if(rbp == 0 || rbp < (uptr*)KBASE || rbp == (uptr*)(~0UL))
      break;
    pcs[i] = rbp[1];     // saved %rip
    rbp = (uptr*)rbp[0]; // saved %rbp
  }
  for(; i < n; i++)
    pcs[i] = 0;
}
<|endoftext|>


<commit_before>//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

typedef int errno_t;
#define RANDOM_CACHE_SIZE 8
constexpr int rand_byte_len = sizeof(unsigned int) * RANDOM_CACHE_SIZE;
static union {
    char rstack[rand_byte_len];
    unsigned int result[RANDOM_CACHE_SIZE];
} random_cache;
static unsigned cache_index(RANDOM_CACHE_SIZE);

class Lock{
    static pthread_mutex_t random_generator_mutex;
public:
  __attribute__((noinline))
  static bool Init()
  {
      return pthread_mutex_init(&random_generator_mutex, NULL) == 0;
  }

  __attribute__((noinline))
  Lock() {
     pthread_mutex_lock(&random_generator_mutex);
  }

  __attribute__((noinline))
  ~Lock() {
     pthread_mutex_unlock(&random_generator_mutex);
  }
};

pthread_mutex_t Lock::random_generator_mutex;

static bool GetRandom(unsigned int *result) noexcept
{
    static bool mutex_initialized = Lock::Init();
    // Do not put the `if check` into `Init` or replace it with Assert
    if(!mutex_initialized) {
      fprintf(stderr, "pthread_mutex_init has failed");
      abort();
    }
    Lock lock;

    if (cache_index < RANDOM_CACHE_SIZE) {
        *result = random_cache.result[cache_index++];
        return true;
    }

    const int rdev = open("/dev/urandom", O_RDONLY);
    unsigned len = 0;

    do
    {
        int added = read(rdev, random_cache.rstack + len, rand_byte_len - len);
        if (added < 0) {
            close(rdev);
            return false;
        }
        len += added;
    } while (len < rand_byte_len);

    close(rdev);
    *result = random_cache.result[0];
    cache_index = 1;
    return len == rand_byte_len;
}

extern "C"
errno_t __cdecl rand_s(unsigned int* randomValue) noexcept
{
    if (randomValue == nullptr) return 1;

    if (GetRandom(randomValue))
    {
        return 0;
    }
    else
    {
        *randomValue = 0;
        return 1;
    }
}
<commit_msg>[2.0>master] [MERGE #2630 @obastemur] xplat: rand_s fallback<commit_after>//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

typedef int errno_t;
#define RANDOM_CACHE_SIZE 8
constexpr int rand_byte_len = sizeof(unsigned int) * RANDOM_CACHE_SIZE;
static union {
    char rstack[rand_byte_len];
    unsigned int result[RANDOM_CACHE_SIZE];
} random_cache;
static unsigned cache_index(RANDOM_CACHE_SIZE);

class Lock{
    static pthread_mutex_t random_generator_mutex;
public:
  __attribute__((noinline))
  static bool Init()
  {
      return pthread_mutex_init(&random_generator_mutex, NULL) == 0;
  }

  __attribute__((noinline))
  Lock() {
     pthread_mutex_lock(&random_generator_mutex);
  }

  __attribute__((noinline))
  ~Lock() {
     pthread_mutex_unlock(&random_generator_mutex);
  }
};

pthread_mutex_t Lock::random_generator_mutex;

// not needed to be a THREAD_LOCAL. GetRandom acquires a mutex lock
unsigned int WEAK_RANDOM_SEED = 12345;

static void GetRandom(unsigned int *result) noexcept
{
    static bool mutex_initialized = Lock::Init();
    // Do not put the `if check` into `Init` or replace it with Assert
    if(!mutex_initialized) {
      fprintf(stderr, "pthread_mutex_init has failed");
      abort();
    }
    Lock lock;

    if (cache_index < RANDOM_CACHE_SIZE) {
        *result = random_cache.result[cache_index++];
        return;
    }

    const int rdev = open("/dev/urandom", O_RDONLY);
    unsigned len = 0;
    bool failed = false;

    do
    {
        int added = read(rdev, random_cache.rstack + len, rand_byte_len - len);
        if (added < 0) {
            close(rdev);
            // io starvation ?
            failed = true;
            goto LEAVE;
        }
        len += added;
    } while (len < rand_byte_len);

    close(rdev);
    *result = random_cache.result[0];
    failed = len != rand_byte_len;
LEAVE:
    if (failed) // fallback to weak rnd
    {
        WEAK_RANDOM_SEED += rand();
        *result = rand_r(&WEAK_RANDOM_SEED);
    }
    else
    {
        cache_index = 1; // enable cache only when len == rand_byte_len
    }
}

extern "C"
errno_t __cdecl rand_s(unsigned int* randomValue) noexcept
{
    if (randomValue == nullptr) return 1;

    GetRandom(randomValue);
    return 0;
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mrs_load_ddr4.C
/// @brief Run and manage the DDR4 mrs loading
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 1
// *HWP Consumed by: FSP:HB

#include <fapi2.H>

#include <mss.H>
#include <lib/dimm/ddr4/mrs_load_ddr4.H>
#include <lib/dimm/bcw_load.H>
#include <lib/eff_config/timing.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_DIMM;

using fapi2::FAPI2_RC_SUCCESS;

namespace mss
{

///
/// @brief Sets up MRS CCS instructions
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_data the completed MRS data to send
/// @param[in] i_rank the rank to send to
/// @param[in,out] io_inst a vector of CCS instructions we should add to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
template< >
fapi2::ReturnCode mrs_engine( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                              const mrs_data<fapi2::TARGET_TYPE_MCBIST>& i_data,
                              const uint64_t i_rank,
                              std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& io_inst )
{
    FAPI_TRY( mrs_engine(i_target,  i_data, i_rank, i_data.iv_delay, io_inst) );

fapi_try_exit:
    return fapi2::current_err;
}

namespace ddr4
{

///
/// @brief Perform the mrs_load DDR4 operations - TARGET_TYPE_DIMM specialization
/// @param[in] i_target a fapi2::Target<TARGET_TYPE_DIMM>
/// @param[in,out] io_inst a vector of CCS instructions we should add to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode mrs_load( const fapi2::Target<TARGET_TYPE_DIMM>& i_target,
                            std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& io_inst)
{
    FAPI_INF("ddr4::mrs_load %s", mss::c_str(i_target));

    fapi2::buffer<uint16_t> l_cal_steps;
    uint64_t tDLLK = 0;
    uint8_t l_dimm_type = 0;

    static std::vector< mrs_data<TARGET_TYPE_MCBIST> > l_mrs_data =
    {
        // JEDEC ordering of MRS per DDR4 power on sequence
        {  3, mrs03, mrs03_decode, mss::tmrd()  },
        {  6, mrs06, mrs06_decode, mss::tmrd()  },
        {  5, mrs05, mrs05_decode, mss::tmrd()  },
        {  4, mrs04, mrs04_decode, mss::tmrd()  },
        {  2, mrs02, mrs02_decode, mss::tmrd()  },
        {  1, mrs01, mrs01_decode, mss::tmrd()  },

        // We need to wait either tmod or tmrd before zqcl.
        {  0, mrs00, mrs00_decode, std::max(mss::tmrd(), mss::tmod(i_target))  },
    };

    std::vector< uint64_t > l_ranks;
    FAPI_TRY( mss::rank::ranks(i_target, l_ranks) );
    FAPI_TRY( mss::tdllk(i_target, tDLLK) );

    // Load MRS
    for (const auto& d : l_mrs_data)
    {
        for (const auto& r : l_ranks)
        {
            FAPI_TRY( mrs_engine(i_target, d, r, io_inst) );
        }
    }

    // Load ZQ Cal Long instruction only if the bit in the cal steps says to do so.
    FAPI_TRY( mss::cal_step_enable(i_target, l_cal_steps) );

    if (l_cal_steps.getBit<EXT_ZQCAL>() != 0)
    {
        for (const auto& r : l_ranks)
        {
            // Note: this isn't general - assumes Nimbus via MCBIST instruction here BRS
            ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst_a_side = ccs::zqcl_command<TARGET_TYPE_MCBIST>(i_target, r);
            ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst_b_side;

            FAPI_TRY( mss::address_mirror(i_target, r, l_inst_a_side) );
            l_inst_b_side = mss::address_invert(l_inst_a_side);

            l_inst_a_side.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES,
                                               MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(tDLLK + mss::tzqinit());
            l_inst_b_side.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES,
                                               MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(tDLLK + mss::tzqinit());

            // There's nothing to decode here.
            FAPI_INF("ZQCL 0x%016llx:0x%016llx %s:rank %d a-side",
                     l_inst_a_side.arr0, l_inst_a_side.arr1, mss::c_str(i_target), r);
            FAPI_INF("ZQCL 0x%016llx:0x%016llx %s:rank %d b-side",
                     l_inst_b_side.arr0, l_inst_b_side.arr1, mss::c_str(i_target), r);

            // Add both to the CCS program
            io_inst.push_back(l_inst_a_side);
            io_inst.push_back(l_inst_b_side);
        }
    }

    // For LRDIMMs, program BCW to send ZQCal Long command to all databuffers
    // in broadcast mode
    FAPI_TRY( eff_dimm_type(i_target, l_dimm_type) );

    if( l_dimm_type == fapi2::ENUM_ATTR_EFF_DIMM_TYPE_LRDIMM )
    {
        constexpr uint8_t FSPACE = 0;
        constexpr uint8_t WORD = 6;

        // From the DDR4DB02 Spec: BC06 - Command Space Control Word
        // After issuing a data buffer command via writes to BC06 waiting for tMRC(16 tCK)
        // is required before the next DRAM command or BCW write can be issued.
        FAPI_TRY( function_space_select<0>(i_target, io_inst) );

        FAPI_TRY( control_word_engine<BCW_8BIT>(i_target,
                                                cw_data(FSPACE, WORD, eff_dimm_ddr4_bc06, mss::tmrc()),
                                                io_inst) );
    }

fapi_try_exit:
    return fapi2::current_err;
}

} // ns ddr4
} // ns mss
<commit_msg>Add BCW API for rank presence, buffer training, mrep timing and UTs.<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mrs_load_ddr4.C
/// @brief Run and manage the DDR4 mrs loading
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 1
// *HWP Consumed by: FSP:HB

#include <fapi2.H>

#include <mss.H>
#include <lib/dimm/ddr4/mrs_load_ddr4.H>
#include <lib/dimm/ddr4/control_word_ddr4.H>
#include <lib/dimm/ddr4/data_buffer_ddr4.H>
#include <lib/eff_config/timing.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_DIMM;

using fapi2::FAPI2_RC_SUCCESS;

namespace mss
{

///
/// @brief Sets up MRS CCS instructions
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_data the completed MRS data to send
/// @param[in] i_rank the rank to send to
/// @param[in,out] io_inst a vector of CCS instructions we should add to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
template< >
fapi2::ReturnCode mrs_engine( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                              const mrs_data<fapi2::TARGET_TYPE_MCBIST>& i_data,
                              const uint64_t i_rank,
                              std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& io_inst )
{
    FAPI_TRY( mrs_engine(i_target,  i_data, i_rank, i_data.iv_delay, io_inst) );

fapi_try_exit:
    return fapi2::current_err;
}

namespace ddr4
{

///
/// @brief Perform the mrs_load DDR4 operations - TARGET_TYPE_DIMM specialization
/// @param[in] i_target a fapi2::Target<TARGET_TYPE_DIMM>
/// @param[in,out] io_inst a vector of CCS instructions we should add to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode mrs_load( const fapi2::Target<TARGET_TYPE_DIMM>& i_target,
                            std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& io_inst)
{
    FAPI_INF("ddr4::mrs_load %s", mss::c_str(i_target));

    fapi2::buffer<uint16_t> l_cal_steps;
    uint64_t tDLLK = 0;
    uint8_t l_dimm_type = 0;

    static std::vector< mrs_data<TARGET_TYPE_MCBIST> > l_mrs_data =
    {
        // JEDEC ordering of MRS per DDR4 power on sequence
        {  3, mrs03, mrs03_decode, mss::tmrd()  },
        {  6, mrs06, mrs06_decode, mss::tmrd()  },
        {  5, mrs05, mrs05_decode, mss::tmrd()  },
        {  4, mrs04, mrs04_decode, mss::tmrd()  },
        {  2, mrs02, mrs02_decode, mss::tmrd()  },
        {  1, mrs01, mrs01_decode, mss::tmrd()  },

        // We need to wait either tmod or tmrd before zqcl.
        {  0, mrs00, mrs00_decode, std::max(mss::tmrd(), mss::tmod(i_target))  },
    };

    std::vector< uint64_t > l_ranks;
    FAPI_TRY( mss::rank::ranks(i_target, l_ranks) );
    FAPI_TRY( mss::tdllk(i_target, tDLLK) );

    // Load MRS
    for (const auto& d : l_mrs_data)
    {
        for (const auto& r : l_ranks)
        {
            FAPI_TRY( mrs_engine(i_target, d, r, io_inst) );
        }
    }

    // Load ZQ Cal Long instruction only if the bit in the cal steps says to do so.
    FAPI_TRY( mss::cal_step_enable(i_target, l_cal_steps) );

    if (l_cal_steps.getBit<EXT_ZQCAL>() != 0)
    {
        for (const auto& r : l_ranks)
        {
            // Note: this isn't general - assumes Nimbus via MCBIST instruction here BRS
            ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst_a_side = ccs::zqcl_command<TARGET_TYPE_MCBIST>(i_target, r);
            ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst_b_side;

            FAPI_TRY( mss::address_mirror(i_target, r, l_inst_a_side) );
            l_inst_b_side = mss::address_invert(l_inst_a_side);

            l_inst_a_side.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES,
                                               MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(tDLLK + mss::tzqinit());
            l_inst_b_side.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES,
                                               MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(tDLLK + mss::tzqinit());

            // There's nothing to decode here.
            FAPI_INF("ZQCL 0x%016llx:0x%016llx %s:rank %d a-side",
                     l_inst_a_side.arr0, l_inst_a_side.arr1, mss::c_str(i_target), r);
            FAPI_INF("ZQCL 0x%016llx:0x%016llx %s:rank %d b-side",
                     l_inst_b_side.arr0, l_inst_b_side.arr1, mss::c_str(i_target), r);

            // Add both to the CCS program
            io_inst.push_back(l_inst_a_side);
            io_inst.push_back(l_inst_b_side);
        }
    }

    // For LRDIMMs, program BCW to send ZQCal Long command to all databuffers
    // in broadcast mode
    FAPI_TRY( eff_dimm_type(i_target, l_dimm_type) );

    if( l_dimm_type == fapi2::ENUM_ATTR_EFF_DIMM_TYPE_LRDIMM )
    {
        FAPI_TRY( set_command_space(i_target, command::ZQCL, io_inst) );
    }

fapi_try_exit:
    return fapi2::current_err;
}

} // ns ddr4
} // ns mss
<|endoftext|>


<commit_before><commit_msg>Missing const<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2017 Leonid Yuriev <leo@yuriev.ru>
 * and other libmdbx authors: please see AUTHORS file.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */

#include "test.h"

static void fflushall() { fflush(nullptr); }

void failure(const char *fmt, ...) {
  va_list ap;
  fflush(NULL);
  va_start(ap, fmt);
  logging::output(logging::failure, fmt, ap);
  va_end(ap);
  fflushall();
  exit(EXIT_FAILURE);
}

const char *test_strerror(int errnum) {
  static __thread char buf[1024];
  return mdbx_strerror_r(errnum, buf, sizeof(buf));
}

void __noreturn failure_perror(const char *what, int errnum) {
  failure("%s failed: %s (%d)\n", what, test_strerror(errnum), errnum);
}

//-----------------------------------------------------------------------------

namespace logging {

static std::string prefix;
static std::string suffix;
static loglevel level;
static FILE *last;

void setup(loglevel _level, const std::string &_prefix) {
  level = (_level > error) ? failure : _level;
  prefix = _prefix;
}

void setup(const std::string &_prefix) { prefix = _prefix; }

const char *level2str(const loglevel level) {
  switch (level) {
  default:
    return "invalid/unknown";
  case extra:
    return "extra";
  case trace:
    return "trace";
  case verbose:
    return "verbose";
  case info:
    return "info";
  case notice:
    return "notice";
  case warning:
    return "warning";
  case error:
    return "error";
  case failure:
    return "failure";
  }
}

bool output(loglevel priority, const char *format, ...) {
  if (priority < level)
    return false;

  va_list ap;
  va_start(ap, format);
  output(priority, format, ap);
  va_end(ap);
  return true;
}

bool output(loglevel priority, const char *format, va_list ap) {
  if (last) {
    putc('\n', last);
    fflush(last);
    last = nullptr;
  }

  if (priority < level)
    return false;

  chrono::time now = chrono::now_realtime();
  struct tm tm;
  time_t time = now.utc;
#ifdef _MSC_VER
  int rc = _localtime32_s(&tm, (const __time32_t *)&now.utc);
#else
  int rc = localtime_r(&time, &tm) ? MDB_SUCCESS : errno;
#endif
  if (rc != MDB_SUCCESS)
    failure_perror("localtime_r()", rc);

  last = (priority >= error) ? stderr : stdout;
  fprintf(last,
          "[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05u %-10s %.4s ] %s" /* TODO */,
          tm.tm_year - 100, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min,
          tm.tm_sec, chrono::fractional2us(now.fractional), osal_getpid(),
          prefix.c_str(), level2str(priority), suffix.c_str());
  vfprintf(last, format, ap);

  size_t len = strlen(format);
  char end = len ? format[len - 1] : '\0';
  switch (end) {
  default:
    putc('\n', last);
  case '\n':
    fflush(last);
    last = nullptr;
    if (priority > info)
      fflushall();
  case ' ':
  case '_':
  case ':':
  case '|':
  case ',':
  case '\t':
  case '\b':
  case '\r':
  case '\0':
    break;
  }
  return true;
}

bool feed(const char *format, va_list ap) {
  if (!last)
    return false;

  vfprintf(last, format, ap);
  size_t len = strlen(format);
  if (len && format[len - 1] == '\n') {
    fflush(last);
    last = nullptr;
  }
  return true;
}

bool feed(const char *format, ...) {
  if (!last)
    return false;

  va_list ap;
  va_start(ap, format);
  feed(format, ap);
  va_end(ap);
  return true;
}

local_suffix::local_suffix(const char *c_str)
    : trim_pos(suffix.size()), indent(0) {
  suffix.append(c_str);
}

local_suffix::local_suffix(const std::string &str)
    : trim_pos(suffix.size()), indent(0) {
  suffix.append(str);
}

void local_suffix::push() {
  indent += 1;
  suffix.push_back('\t');
}

void local_suffix::pop() {
  assert(indent > 0);
  if (indent > 0) {
    indent -= 1;
    suffix.pop_back();
  }
}

local_suffix::~local_suffix() { suffix.erase(trim_pos); }

} /* namespace log */

void log_trace(const char *msg, ...) {
  if (logging::trace >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::trace, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_verbose(const char *msg, ...) {
  if (logging::verbose >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::verbose, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_info(const char *msg, ...) {
  if (logging::info >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::info, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_notice(const char *msg, ...) {
  if (logging::notice >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::notice, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_warning(const char *msg, ...) {
  if (logging::warning >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::warning, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_error(const char *msg, ...) {
  if (logging::error >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::error, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_touble(const char *where, const char *what, int errnum) {
  log_error("%s: %s %s", where, what, test_strerror(errnum));
}
<commit_msg>test: log error into stdout too.<commit_after>/*
 * Copyright 2017 Leonid Yuriev <leo@yuriev.ru>
 * and other libmdbx authors: please see AUTHORS file.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */

#include "test.h"

static void fflushall() { fflush(nullptr); }

void failure(const char *fmt, ...) {
  va_list ap;
  fflush(NULL);
  va_start(ap, fmt);
  logging::output(logging::failure, fmt, ap);
  va_end(ap);
  fflushall();
  exit(EXIT_FAILURE);
}

const char *test_strerror(int errnum) {
  static __thread char buf[1024];
  return mdbx_strerror_r(errnum, buf, sizeof(buf));
}

void __noreturn failure_perror(const char *what, int errnum) {
  failure("%s failed: %s (%d)\n", what, test_strerror(errnum), errnum);
}

//-----------------------------------------------------------------------------

namespace logging {

static std::string prefix;
static std::string suffix;
static loglevel level;
static FILE *last;

void setup(loglevel _level, const std::string &_prefix) {
  level = (_level > error) ? failure : _level;
  prefix = _prefix;
}

void setup(const std::string &_prefix) { prefix = _prefix; }

const char *level2str(const loglevel level) {
  switch (level) {
  default:
    return "invalid/unknown";
  case extra:
    return "extra";
  case trace:
    return "trace";
  case verbose:
    return "verbose";
  case info:
    return "info";
  case notice:
    return "notice";
  case warning:
    return "warning";
  case error:
    return "error";
  case failure:
    return "failure";
  }
}

bool output(loglevel priority, const char *format, ...) {
  if (priority < level)
    return false;

  va_list ap;
  va_start(ap, format);
  output(priority, format, ap);
  va_end(ap);
  return true;
}

bool output(loglevel priority, const char *format, va_list ap) {
  if (last) {
    putc('\n', last);
    fflush(last);
    last = nullptr;
  }

  if (priority < level)
    return false;

  chrono::time now = chrono::now_realtime();
  struct tm tm;
  time_t time = now.utc;
#ifdef _MSC_VER
  int rc = _localtime32_s(&tm, (const __time32_t *)&now.utc);
#else
  int rc = localtime_r(&time, &tm) ? MDB_SUCCESS : errno;
#endif
  if (rc != MDB_SUCCESS)
    failure_perror("localtime_r()", rc);

  last = stdout;
  fprintf(last,
          "[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05u %-10s %.4s ] %s" /* TODO */,
          tm.tm_year - 100, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
          tm.tm_sec, chrono::fractional2us(now.fractional), osal_getpid(),
          prefix.c_str(), level2str(priority), suffix.c_str());
  vfprintf(last, format, ap);

  size_t len = strlen(format);
  char end = len ? format[len - 1] : '\0';

  switch (end) {
  default:
    putc('\n', last);
  case '\n':
    fflush(last);
    last = nullptr;
  case ' ':
  case '_':
  case ':':
  case '|':
  case ',':
  case '\t':
  case '\b':
  case '\r':
  case '\0':
    break;
  }

  if (priority >= error && last != stderr) {
    fprintf(stderr, "[ %05u %-10s %.4s ] %s", osal_getpid(), prefix.c_str(),
            level2str(priority), suffix.c_str());
    vfprintf(stderr, format, ap);
    if (end != '\n')
      putc('\n', stderr);
    fflush(stderr);
  }

  return true;
}

bool feed(const char *format, va_list ap) {
  if (!last)
    return false;

  vfprintf(last, format, ap);
  size_t len = strlen(format);
  if (len && format[len - 1] == '\n') {
    fflush(last);
    last = nullptr;
  }
  return true;
}

bool feed(const char *format, ...) {
  if (!last)
    return false;

  va_list ap;
  va_start(ap, format);
  feed(format, ap);
  va_end(ap);
  return true;
}

local_suffix::local_suffix(const char *c_str)
    : trim_pos(suffix.size()), indent(0) {
  suffix.append(c_str);
}

local_suffix::local_suffix(const std::string &str)
    : trim_pos(suffix.size()), indent(0) {
  suffix.append(str);
}

void local_suffix::push() {
  indent += 1;
  suffix.push_back('\t');
}

void local_suffix::pop() {
  assert(indent > 0);
  if (indent > 0) {
    indent -= 1;
    suffix.pop_back();
  }
}

local_suffix::~local_suffix() { suffix.erase(trim_pos); }

} /* namespace log */

void log_trace(const char *msg, ...) {
  if (logging::trace >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::trace, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_verbose(const char *msg, ...) {
  if (logging::verbose >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::verbose, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_info(const char *msg, ...) {
  if (logging::info >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::info, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_notice(const char *msg, ...) {
  if (logging::notice >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::notice, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_warning(const char *msg, ...) {
  if (logging::warning >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::warning, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_error(const char *msg, ...) {
  if (logging::error >= logging::level) {
    va_list ap;
    va_start(ap, msg);
    logging::output(logging::error, msg, ap);
    va_end(ap);
  } else
    logging::last = nullptr;
}

void log_touble(const char *where, const char *what, int errnum) {
  log_error("%s: %s %s", where, what, test_strerror(errnum));
}
<|endoftext|>


<commit_before>#include "io.h"

#include <sstream>
#include <fstream>
#include <iomanip>

#include <gflags/gflags.h>

#include "proto/metadata.pb.h"
#include "proto/chunk.pb.h"

DEFINE_string(dataset, "", "Input dataset");
DEFINE_string(output, "", "Path to file where to store calculated PageRank");

/*
 * NOTE! It's possible that begin >= chunk_begin, or end < chunk_end.
 * The real bounds are intersection of [begin; end] and [chunk_begin; chunk_end].
 */
void ReadPagesFromChunk(int chunk_size, int chunk_id,
		int begin, int end, std::vector<pr::Page> &pages) {
	int chunk_begin = chunk_id * chunk_size;
	int chunk_end = chunk_begin + chunk_size;
	if (begin < chunk_begin) begin = chunk_begin;
	if (end >= chunk_end) end = chunk_end - 1;

	pr::Chunk chunk;
	std::ifstream in(FLAGS_dataset + "_" + std::to_string(chunk_id) + ".chnk");
	chunk.ParseFromIstream(&in);

	int shift = begin - chunk_begin;
	int cnt = end - begin + 1;
	std::move(chunk.mutable_pages()->begin() + shift,
			chunk.mutable_pages()->end(),
			std::back_inserter(pages));
}

/*
 * Read pages and push them to the back of output vector.
 */
void ReadPages(int chunk_size, int begin, int end,
		std::vector<pr::Page> &pages) {
	int begin_chunk = begin / chunk_size;
	int end_chunk = end / chunk_size;

	for (int chunk = begin_chunk; chunk <= end_chunk; chunk++) {
		ReadPagesFromChunk(chunk_size, chunk, begin, end, pages);
	}
}

void ReadMetadata(int *page_cnt_ptr, int *chunk_size_ptr,
		std::vector<int> &out_link_cnts) {
	pr::Metadata meta;

	std::ifstream in(FLAGS_dataset + ".meta", std::ios::in | std::ios::binary);
	meta.ParseFromIstream(&in);

	*page_cnt_ptr = meta.page_cnt();
	*chunk_size_ptr = meta.chunk_size();

	out_link_cnts.clear();
	out_link_cnts.reserve(meta.page_cnt());
	std::move(meta.mutable_out_link_cnts()->begin(),
			meta.mutable_out_link_cnts()->end(),
			std::back_inserter(out_link_cnts));
}

void WritePr(const std::vector<double> pr) {
	std::ofstream out(FLAGS_output);
	for (double x : pr) {
		out << std::setprecision(10) << std::fixed << x << std::endl;
	}
}
<commit_msg>Add I/O error handling<commit_after>#include "io.h"

#include <sstream>
#include <fstream>
#include <iomanip>

#include <gflags/gflags.h>
#include <glog/logging.h>

#include "proto/metadata.pb.h"
#include "proto/chunk.pb.h"

DEFINE_string(dataset, "", "Input dataset");
DEFINE_string(output, "", "Path to file where to store calculated PageRank");

/*
 * NOTE! It's possible that begin >= chunk_begin, or end < chunk_end.
 * The real bounds are intersection of [begin; end] and [chunk_begin; chunk_end].
 */
void ReadPagesFromChunk(int chunk_size, int chunk_id,
		int begin, int end, std::vector<pr::Page> &pages) {
	int chunk_begin = chunk_id * chunk_size;
	int chunk_end = chunk_begin + chunk_size;
	if (begin < chunk_begin) begin = chunk_begin;
	if (end >= chunk_end) end = chunk_end - 1;

	pr::Chunk chunk;
	std::string filename = FLAGS_dataset  + "_" + std::to_string(chunk_id) +
		".chnk";
	std::ifstream in(filename);
	if (!in)
		LOG(FATAL) << "Error opening chunk file " + filename + " for reading.";
	if (!chunk.ParseFromIstream(&in))
		LOG(FATAL) << "Error parsing chunk from file " + filename + ".";

	int shift = begin - chunk_begin;
	std::move(chunk.mutable_pages()->begin() + shift,
			chunk.mutable_pages()->end(),
			std::back_inserter(pages));
}

/*
 * Read pages and push them to the back of output vector.
 */
void ReadPages(int chunk_size, int begin, int end,
		std::vector<pr::Page> &pages) {
	int begin_chunk = begin / chunk_size;
	int end_chunk = end / chunk_size;

	for (int chunk = begin_chunk; chunk <= end_chunk; chunk++) {
		ReadPagesFromChunk(chunk_size, chunk, begin, end, pages);
	}
}

void ReadMetadata(int *page_cnt_ptr, int *chunk_size_ptr,
		std::vector<int> &out_link_cnts) {
	pr::Metadata meta;

	std::string filename = FLAGS_dataset + ".meta";
	std::ifstream in(filename, std::ios::in | std::ios::binary);
	if (!in)
		LOG(FATAL) << "Error opening metadata file " + filename + " for reading.";
	if (!meta.ParseFromIstream(&in))
		LOG(FATAL) << "Error parsing metadata from file " + filename + ".";

	*page_cnt_ptr = meta.page_cnt();
	*chunk_size_ptr = meta.chunk_size();

	out_link_cnts.clear();
	out_link_cnts.reserve(meta.page_cnt());
	std::move(meta.mutable_out_link_cnts()->begin(),
			meta.mutable_out_link_cnts()->end(),
			std::back_inserter(out_link_cnts));
}

void WritePr(const std::vector<double> pr) {
	std::ofstream out(FLAGS_output);
	if (!out)
		LOG(FATAL) << "Error opening output file " + FLAGS_output +
			" for writing.";
	for (double x : pr) {
		out << std::setprecision(10) << std::fixed << x << std::endl;
	}
}
<|endoftext|>


<commit_before><commit_msg>update python bindings some more<commit_after><|endoftext|>


<commit_before>#ifndef VIENNAFVM_QUANTITY_HPP
#define VIENNAFVM_QUANTITY_HPP

/* =======================================================================
   Copyright (c) 2011, Institute for Microelectronics, TU Wien
   http://www.iue.tuwien.ac.at
                             -----------------
           ViennaFVM - The Vienna Finite Volume Method Library
                             -----------------

   authors:    Karl Rupp                          rupp@iue.tuwien.ac.at
               (add your name here)

   license:    To be discussed, see file LICENSE in the ViennaFVM base directory
======================================================================= */

#include <numeric>

#include "viennafvm/forwards.h"

#include "viennamath/forwards.h"
#include "viennamath/manipulation/substitute.hpp"
#include "viennamath/expression.hpp"

#include "viennagrid/forwards.hpp"
#include "viennagrid/mesh/segmentation.hpp"

/** @file  quantity.hpp
    @brief Defines the basic quantity which may be entirely known or unknown (with suitable boundary conditions) over a mesh
*/

namespace viennafvm
{
  namespace traits {

  template<typename EleT>
  inline std::size_t id(EleT const& elem)
  {
    return elem.id().get();
  }

  } // traits

  template<typename AssociatedT, typename ValueT = double>
  class quantity
  {
    public:
      typedef ValueT         value_type;
      typedef AssociatedT    associated_type;

      quantity() {}  // to fulfill default constructible concept!

      quantity(std::size_t id,
               std::string const & quan_name,
               std::size_t num_values,
               value_type default_value = value_type())
        : id_(id),
          name_(quan_name),
          values_          (num_values, default_value),
          boundary_types_  (num_values, BOUNDARY_NONE),
          boundary_values_ (num_values, default_value),
          unknown_mask_    (num_values, false),
          unknowns_indices_(num_values, -1)
      {}

      std::string get_name() const { return name_; }

      ValueT get_value(associated_type const & elem) const         { return values_.at(viennafvm::traits::id(elem));         }
      void   set_value(associated_type const & elem, ValueT value) {        values_.at(viennafvm::traits::id(elem)) = value; }

      // Dirichlet and Neumann
      ValueT get_boundary_value(associated_type const & elem) const         { return boundary_values_.at(viennafvm::traits::id(elem));         }
      void   set_boundary_value(associated_type const & elem, ValueT value) {        boundary_values_.at(viennafvm::traits::id(elem)) = value; }

      boundary_type_id get_boundary_type(associated_type const & elem) const                   { return boundary_types_.at(viennafvm::traits::id(elem));         }
      void             set_boundary_type(associated_type const & elem, boundary_type_id value) {        boundary_types_.at(viennafvm::traits::id(elem)) = value; }

      bool   get_unknown_mask(associated_type const & elem) const       { return unknown_mask_.at(viennafvm::traits::id(elem));         }
      void   set_unknown_mask(associated_type const & elem, bool value) {        unknown_mask_.at(viennafvm::traits::id(elem)) = value; }

      long   get_unknown_index(associated_type const & elem) const       { return unknowns_indices_.at(viennafvm::traits::id(elem));         }
      void   set_unknown_index(associated_type const & elem, long value) {        unknowns_indices_.at(viennafvm::traits::id(elem)) = value; }

      std::size_t get_unknown_num() const
      {
        std::size_t num = 0;
        for (std::size_t i=0; i<unknowns_indices_.size(); ++i)
        {
          if (unknowns_indices_[i] >= 0)
            ++num;
        }
        return num;
      }

      bool are_entries_zero()
      {
        if( std::fabs(this->get_sum()) < 1.0E-20 ) return true;
        else return false;
      }

      value_type get_sum()
      {
        return std::accumulate(values_.begin(), values_.end(), 0.0);
      }

      // possible design flaws:
      std::vector<ValueT> const & values() const { return values_; }

      std::size_t const& id    ()              const { return id_; }
      void               set_id(std::size_t id)      { id_ = id;   }

    private:
//      std::size_t id(associated_type const elem) const { return elem.id().get(); }

      std::size_t                    id_;
      std::string                    name_;
      std::vector<ValueT>            values_;
      std::vector<boundary_type_id>  boundary_types_;
      std::vector<ValueT>            boundary_values_;
      std::vector<bool>              unknown_mask_;
      std::vector<long>              unknowns_indices_;
  };
}

#endif
<commit_msg>removed 'are_entries_zero' function<commit_after>#ifndef VIENNAFVM_QUANTITY_HPP
#define VIENNAFVM_QUANTITY_HPP

/* =======================================================================
   Copyright (c) 2011, Institute for Microelectronics, TU Wien
   http://www.iue.tuwien.ac.at
                             -----------------
           ViennaFVM - The Vienna Finite Volume Method Library
                             -----------------

   authors:    Karl Rupp                          rupp@iue.tuwien.ac.at
               (add your name here)

   license:    To be discussed, see file LICENSE in the ViennaFVM base directory
======================================================================= */

#include <numeric>

#include "viennafvm/forwards.h"

#include "viennamath/forwards.h"
#include "viennamath/manipulation/substitute.hpp"
#include "viennamath/expression.hpp"

#include "viennagrid/forwards.hpp"
#include "viennagrid/mesh/segmentation.hpp"

/** @file  quantity.hpp
    @brief Defines the basic quantity which may be entirely known or unknown (with suitable boundary conditions) over a mesh
*/

namespace viennafvm
{
  namespace traits {

  template<typename EleT>
  inline std::size_t id(EleT const& elem)
  {
    return elem.id().get();
  }

  } // traits

  template<typename AssociatedT, typename ValueT = double>
  class quantity
  {
    public:
      typedef ValueT         value_type;
      typedef AssociatedT    associated_type;

      quantity() {}  // to fulfill default constructible concept!

      quantity(std::size_t id,
               std::string const & quan_name,
               std::size_t num_values,
               value_type default_value = value_type())
        : id_(id),
          name_(quan_name),
          values_          (num_values, default_value),
          boundary_types_  (num_values, BOUNDARY_NONE),
          boundary_values_ (num_values, default_value),
          unknown_mask_    (num_values, false),
          unknowns_indices_(num_values, -1)
      {}

      std::string get_name() const { return name_; }

      ValueT get_value(associated_type const & elem) const         { return values_.at(viennafvm::traits::id(elem));         }
      void   set_value(associated_type const & elem, ValueT value) {        values_.at(viennafvm::traits::id(elem)) = value; }

      // Dirichlet and Neumann
      ValueT get_boundary_value(associated_type const & elem) const         { return boundary_values_.at(viennafvm::traits::id(elem));         }
      void   set_boundary_value(associated_type const & elem, ValueT value) {        boundary_values_.at(viennafvm::traits::id(elem)) = value; }

      boundary_type_id get_boundary_type(associated_type const & elem) const                   { return boundary_types_.at(viennafvm::traits::id(elem));         }
      void             set_boundary_type(associated_type const & elem, boundary_type_id value) {        boundary_types_.at(viennafvm::traits::id(elem)) = value; }

      bool   get_unknown_mask(associated_type const & elem) const       { return unknown_mask_.at(viennafvm::traits::id(elem));         }
      void   set_unknown_mask(associated_type const & elem, bool value) {        unknown_mask_.at(viennafvm::traits::id(elem)) = value; }

      long   get_unknown_index(associated_type const & elem) const       { return unknowns_indices_.at(viennafvm::traits::id(elem));         }
      void   set_unknown_index(associated_type const & elem, long value) {        unknowns_indices_.at(viennafvm::traits::id(elem)) = value; }

      std::size_t get_unknown_num() const
      {
        std::size_t num = 0;
        for (std::size_t i=0; i<unknowns_indices_.size(); ++i)
        {
          if (unknowns_indices_[i] >= 0)
            ++num;
        }
        return num;
      }

      value_type get_sum()
      {
        return std::accumulate(values_.begin(), values_.end(), 0.0);
      }

      // possible design flaws:
      std::vector<ValueT> const & values() const { return values_; }

      std::size_t const& id    ()              const { return id_; }
      void               set_id(std::size_t id)      { id_ = id;   }

    private:
//      std::size_t id(associated_type const elem) const { return elem.id().get(); }

      std::size_t                    id_;
      std::string                    name_;
      std::vector<ValueT>            values_;
      std::vector<boundary_type_id>  boundary_types_;
      std::vector<ValueT>            boundary_values_;
      std::vector<bool>              unknown_mask_;
      std::vector<long>              unknowns_indices_;
  };
}

#endif
<|endoftext|>


<commit_before>#include "CommunicationInterface.h"

// constructor
CommunicationInterface::CommunicationInterface(void)
:
    outputDataBuffer_(""),
    placeInBuffer_(false),
	dataBufferIndex_(0),
    nModules_(0)
{
    // Do nothing.
}

// Destructor - make sure to clean up those pointers!
CommunicationInterface::~CommunicationInterface(void)
{
    for(long int moduleI=0; moduleI<nModules_; moduleI++)
        delete modules_[moduleI];
}

// Initialises the communications and stores pointers to all linked modules
// inside the current instance.
void CommunicationInterface::setup(Module* modulePtrs[], size_t nPtrs)
{
    // Copy the pointers to the class container. Necessary to do it this way
    // to make sure they are kept even if the original array passed to this function
    // gets out of scope.
    nModules_ = nPtrs;
    modules_ = (Module**) malloc(nModules_ * sizeof(Module*));

    for(long int moduleI=0; moduleI<nModules_; moduleI++)
        modules_[moduleI] = modulePtrs[moduleI];

    #ifdef DEBUG_PRINTOUT
    Serial.println("Initialised communications with modules:");
    for(long int moduleI=0; moduleI<nModules_; moduleI++)
    {
        Serial.print("    ");
        Serial.print(moduleI);
        Serial.print(": ");
        Serial.println(modules_[moduleI]->getIdentifier());
    }
    #endif
}

// Main funciton which calles getSerial() and parseInput() sequentially to
// update the state of all of the connected modules.
void CommunicationInterface::loop(void)
{
    getSerial();
    parseInput();
}

boolean CommunicationInterface::getSerial(void)
/* Read input from the serial port and store it in the inputDataBuffer buffer.
 * The serial input has to conform to a protocol, whereby the messages begin with
 * INPUT_START_CHAR and terminate with END_CHAR. The message in between is
 * a sequence of strings and integers. Strings represent the destination of the
 * command (e.g. engine throttle) and integers are the arguments for the command
 * (e.g. throttle value).
 *
 * @return - true if we've parsed the entire command successfully; false otherwise,
 * 			e.g. when we didn't receive a command.
 */
{
	char incomingbyte; // The currently read serial input byte.

	while(Serial.available()>0)
	{
		incomingbyte = Serial.read(); // Read bytes from the serial input one by one.

		if(incomingbyte==INPUT_START_CHAR) // A command has been received.
		{
			dataBufferIndex_ = 0; // We'll be reading the first input value next.
			placeInBuffer_ = true; // We should be storing the inputs in the buffer now.
		}

		if(placeInBuffer_)
		{
			if(dataBufferIndex_==DATABUFFERSIZE)
			{
				// Index is pointing to an array element outside our buffer.
                // We're done reading this command, so have to start reading the next one from the beginning.
				dataBufferIndex_ = 0;
                // Exit the while loop, we're done with this command.
				break;
			}

			if(incomingbyte==END_CHAR) // We've reached the end of this command.
			{
                // Null terminate the C string
				inputDataBuffer_[dataBufferIndex_] = 0;
				// Don't store anything here any more.
                placeInBuffer_ = false;
				// Make sure next command gets written to the start of the buffer.
                dataBufferIndex_ = 0;
				// Say that we've parsed the whole command.
                return true;
			}
			else if (incomingbyte!=INPUT_START_CHAR) // Simply record the telecommand byte.
			{
				inputDataBuffer_[dataBufferIndex_++] = incomingbyte;
			}
		}
	}

	return false; // Something went wrong or we didn't receive an actual command.
}

// Extract commands and corresponding numbers from the inputDataBuffer input buffer.
void CommunicationInterface::parseInput(void)
{
    Serial.print("Buffer=");
    Serial.println(inputDataBuffer_);

    // Holds the tokens used to match passed values to each listening module.
	char * token;
    // Get the first token. DATA_DELIMITER is a char but need to convert to const char*
	token = strtok(inputDataBuffer_, String(DATA_DELIMITER).c_str());

	// Parse the entire inputDataBuffer of tokens (commands and their value arguments).
	int nextModuleIndex = -1; // Index of the module value for which is sent in this part of the telecommand.

    while (token != NULL)
	{
        Serial.print("Token=");
        Serial.println(token);

		// If next module index < 0 we have no value to read now
		if (nextModuleIndex >= 0)
		{
			#ifdef DEBUG_PRINTOUT
			Serial.print("Trying to set value of module ");
			Serial.print(modules_[nextModuleIndex]->getIdentifier());
			Serial.print(" to ");
			Serial.println( String(int(atof(token))) );
			#endif

			modules_[nextModuleIndex] -> setValue( int(atof(token)) );
			// Indicate that on the next pass there is no value to read.
			nextModuleIndex = -1;
		}
		else
		{
			// the current token may be a string corresponding to one of the modules
			for(long int moduleI=0; moduleI<nModules_; moduleI++)
			{
				if (strcmp(token, modules_[moduleI]->getIdentifier()) == 0)
				{
					nextModuleIndex = moduleI;
					break;
				}
			}
		}

		// Continue to the new token.
        // If given a NULL pointer, the funciton continues from the point where
        // the previous call finished, thereby moving along the buffer string.
		token = strtok(NULL, String(DATA_DELIMITER).c_str());
	}

    // Reset the buffer as the first token is still in it
    memset(&inputDataBuffer_[0], 0, sizeof(inputDataBuffer_));
}
<commit_msg>Added buffer reset in comms module, improved documentation of tare() method in load cell<commit_after>#include "CommunicationInterface.h"

// constructor
CommunicationInterface::CommunicationInterface(void)
:
    outputDataBuffer_(""),
    placeInBuffer_(false),
	dataBufferIndex_(0),
    nModules_(0)
{
    // Do nothing.
}

// Destructor - make sure to clean up those pointers!
CommunicationInterface::~CommunicationInterface(void)
{
    for(long int moduleI=0; moduleI<nModules_; moduleI++)
        delete modules_[moduleI];
}

// Initialises the communications and stores pointers to all linked modules
// inside the current instance.
void CommunicationInterface::setup(Module* modulePtrs[], size_t nPtrs)
{
    // Copy the pointers to the class container. Necessary to do it this way
    // to make sure they are kept even if the original array passed to this function
    // gets out of scope.
    nModules_ = nPtrs;
    modules_ = (Module**) malloc(nModules_ * sizeof(Module*));

    for(long int moduleI=0; moduleI<nModules_; moduleI++)
        modules_[moduleI] = modulePtrs[moduleI];

    #ifdef DEBUG_PRINTOUT
    Serial.println("Initialised communications with modules:");
    for(long int moduleI=0; moduleI<nModules_; moduleI++)
    {
        Serial.print("    ");
        Serial.print(moduleI);
        Serial.print(": ");
        Serial.println(modules_[moduleI]->getIdentifier());
    }
    #endif
}

// Main funciton which calles getSerial() and parseInput() sequentially to
// update the state of all of the connected modules.
void CommunicationInterface::loop(void)
{
    getSerial();
    parseInput();
}

boolean CommunicationInterface::getSerial(void)
/* Read input from the serial port and store it in the inputDataBuffer buffer.
 * The serial input has to conform to a protocol, whereby the messages begin with
 * INPUT_START_CHAR and terminate with END_CHAR. The message in between is
 * a sequence of strings and integers. Strings represent the destination of the
 * command (e.g. engine throttle) and integers are the arguments for the command
 * (e.g. throttle value).
 *
 * @return - true if we've parsed the entire command successfully; false otherwise,
 * 			e.g. when we didn't receive a command.
 */
{
	char incomingbyte; // The currently read serial input byte.

	while(Serial.available()>0)
	{
		incomingbyte = Serial.read(); // Read bytes from the serial input one by one.

		if(incomingbyte==INPUT_START_CHAR) // A command has been received.
		{
			dataBufferIndex_ = 0; // We'll be reading the first input value next.
			placeInBuffer_ = true; // We should be storing the inputs in the buffer now.
		}

		if(placeInBuffer_)
		{
			if(dataBufferIndex_==DATABUFFERSIZE)
			{
				// Index is pointing to an array element outside our buffer.
                // We're done reading this command, so have to start reading the next one from the beginning.
				dataBufferIndex_ = 0;
                // Exit the while loop, we're done with this command.
				break;
			}

			if(incomingbyte==END_CHAR) // We've reached the end of this command.
			{
                // Null terminate the C string
				inputDataBuffer_[dataBufferIndex_] = 0;
				// Don't store anything here any more.
                placeInBuffer_ = false;
				// Make sure next command gets written to the start of the buffer.
                dataBufferIndex_ = 0;
				// Say that we've parsed the whole command.
                return true;
			}
			else if (incomingbyte!=INPUT_START_CHAR) // Simply record the telecommand byte.
			{
				inputDataBuffer_[dataBufferIndex_++] = incomingbyte;
			}
		}
	}

	return false; // Something went wrong or we didn't receive an actual command.
}

// Extract commands and corresponding numbers from the inputDataBuffer input buffer.
void CommunicationInterface::parseInput(void)
{
    // Serial.print("Buffer=");
    // Serial.println(inputDataBuffer_);

    // Holds the tokens used to match passed values to each listening module.
	char * token;
    // Get the first token. DATA_DELIMITER is a char but need to convert to const char*
	token = strtok(inputDataBuffer_, String(DATA_DELIMITER).c_str());

	// Parse the entire inputDataBuffer of tokens (commands and their value arguments).
	int nextModuleIndex = -1; // Index of the module value for which is sent in this part of the telecommand.

    while (token != NULL)
	{
        // Serial.print("Token=");
        // Serial.println(token);

		// If next module index < 0 we have no value to read now
		if (nextModuleIndex >= 0)
		{
			#ifdef DEBUG_PRINTOUT
			Serial.print("Trying to set value of module ");
			Serial.print(modules_[nextModuleIndex]->getIdentifier());
			Serial.print(" to ");
			Serial.println( String(int(atof(token))) );
			#endif

			modules_[nextModuleIndex] -> setValue( int(atof(token)) );
			// Indicate that on the next pass there is no value to read.
			nextModuleIndex = -1;
		}
		else
		{
			// the current token may be a string corresponding to one of the modules
			for(long int moduleI=0; moduleI<nModules_; moduleI++)
			{
				if (strcmp(token, modules_[moduleI]->getIdentifier()) == 0)
				{
					nextModuleIndex = moduleI;
					break;
				}
			}
		}

		// Continue to the new token.
        // If given a NULL pointer, the funciton continues from the point where
        // the previous call finished, thereby moving along the buffer string.
		token = strtok(NULL, String(DATA_DELIMITER).c_str());
	}

    // Reset the buffer as the first token is still in it
    memset(&inputDataBuffer_[0], 0, sizeof(inputDataBuffer_));
}
<|endoftext|>


<commit_before>#include "imgui_extra.h"

#include "imgui.h"

#define _USE_MATH_DEFINES
#include <cmath>

namespace imgui
{
  bool
  Knob(const char* label, float* p_value, float v_min, float v_max)
  {
    constexpr auto  pi           = static_cast<float>(M_PI);
    constexpr float angle_min    = pi * 0.75f;
    constexpr float angle_max    = pi * 2.25f;
    constexpr float radius_outer = 20.0f;
    constexpr float radius_inner = radius_outer * 0.40f;

    ImGuiIO&    io    = ImGui::GetIO();
    ImGuiStyle& style = ImGui::GetStyle();

    ImVec2      pos    = ImGui::GetCursorScreenPos();
    ImVec2      center = ImVec2(pos.x + radius_outer, pos.y + radius_outer);
    float       line_height = ImGui::GetTextLineHeight();
    ImDrawList* draw_list   = ImGui::GetWindowDrawList();

    ImGui::InvisibleButton(
        label,
        ImVec2(
            radius_outer * 2,
            radius_outer * 2 + line_height + style.ItemInnerSpacing.y));
    const bool is_active  = ImGui::IsItemActive();
    const bool is_hovered = ImGui::IsItemHovered();

    // changing value
    const bool value_changed = is_active && io.MouseDelta.x != 0.0f;
    if(value_changed)
    {
      float step = (v_max - v_min) / 200.0f;
      *p_value += io.MouseDelta.x * step;
      if(*p_value < v_min)
        *p_value = v_min;
      if(*p_value > v_max)
        *p_value = v_max;
    }

    const float t         = (*p_value - v_min) / (v_max - v_min);
    const float angle     = angle_min + (angle_max - angle_min) * t;
    const float angle_cos = cosf(angle);
    const float angle_sin = sinf(angle);

    // colors
    const auto label_color        = ImGui::GetColorU32(ImGuiCol_Text);
    const auto outer_circle_color = ImGui::GetColorU32(ImGuiCol_FrameBg);
    const auto value_indicator_color =
        ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
    const auto inner_circle_color = ImGui::GetColorU32(
        is_active ? ImGuiCol_FrameBgActive
                  : is_hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);

    // visualization
    draw_list->AddCircleFilled(center, radius_outer, outer_circle_color, 16);
    draw_list->AddLine(
        ImVec2(
            center.x + angle_cos * radius_inner,
            center.y + angle_sin * radius_inner),
        ImVec2(
            center.x + angle_cos * (radius_outer - 2),
            center.y + angle_sin * (radius_outer - 2)),
        value_indicator_color,
        2.0f);
    draw_list->AddCircleFilled(center, radius_inner, inner_circle_color, 16);
    draw_list->AddText(
        ImVec2(pos.x, pos.y + radius_outer * 2 + style.ItemInnerSpacing.y),
        label_color,
        label);

    // tooltip
    if(is_active || is_hovered)
    {
      ImGui::SetNextWindowPos(ImVec2(
          pos.x - style.WindowPadding.x,
          pos.y - line_height - style.ItemInnerSpacing.y -
              style.WindowPadding.y));
      ImGui::BeginTooltip();
      ImGui::Text("%.3f", *p_value);
      ImGui::EndTooltip();
    }

    return value_changed;
  }
}<commit_msg>improved knob style<commit_after>#include "imgui_extra.h"

#include "imgui.h"

#define _USE_MATH_DEFINES
#include <cmath>
#include <sstream>
#include <imgui_internal.h>

namespace imgui
{
  bool
  Knob(const char* label, float* p_value, float v_min, float v_max)
  {
    constexpr auto  pi              = static_cast<float>(M_PI);
    constexpr float angle_min       = pi * 0.75f;
    constexpr float angle_max       = pi * 2.25f;
    constexpr float angle_step      = 20 * (pi / 180);
    constexpr float size_outer      = 20;
    constexpr float peg_max_end     = size_outer;
    constexpr float peg_end         = 19;
    constexpr float peg_start       = 15;
    constexpr float knob_size       = 15;
    constexpr float knob_mark_start = 15;
    constexpr float knob_mark_end   = 8;
    constexpr int   seg             = 16;

    ImGuiIO&    io        = ImGui::GetIO();
    ImGuiStyle& style     = ImGui::GetStyle();
    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    const ImVec2 start  = ImGui::GetCursorScreenPos();
    const ImVec2 center = ImVec2(start.x + size_outer, start.y + size_outer);
    const float  line_height = ImGui::GetTextLineHeight();

    ImGui::InvisibleButton(
        label,
        ImVec2(
            size_outer * 2,
            size_outer * 2 + line_height + style.ItemInnerSpacing.y));
    const bool is_active  = ImGui::IsItemActive();
    const bool is_hovered = ImGui::IsItemHovered();

    // changing value
    const bool value_changed = is_active && io.MouseDelta.x != 0.0f;
    if(value_changed)
    {
      float step = (v_max - v_min) / 200.0f;
      *p_value += io.MouseDelta.x * step;
      if(*p_value < v_min)
        *p_value = v_min;
      if(*p_value > v_max)
        *p_value = v_max;
    }

    const float t     = (*p_value - v_min) / (v_max - v_min);
    const float angle = angle_min + (angle_max - angle_min) * t;

    // colors
    const auto label_color     = ImGui::GetColorU32(ImGuiCol_Text);
    const auto fill_color      = ImGui::GetColorU32(ImGuiCol_FrameBg);
    const auto knob_color      = ImGui::GetColorU32(ImGuiCol_SliderGrab);
    const auto indicator_color = ImGui::GetColorU32(ImGuiCol_Text);
    const auto peg_color_off   = ImGui::GetColorU32(ImGuiCol_TextDisabled);
    const auto peg_color_max   = ImGui::GetColorU32(ImGuiCol_Text);

    // util function
    const auto Pos = [=](float angle, float rad) -> ImVec2 {
      return ImVec2(center.x + cosf(angle) * rad, center.y + sinf(angle) * rad);
    };

    // ----------------- visualization
    // background
    draw_list->AddCircleFilled(center, size_outer, fill_color, seg);

    // peg indicators
    for(float a = angle_min; a <= angle; a += angle_step)
    {
      draw_list->AddLine(
          Pos(a, peg_start), Pos(a, peg_end), peg_color_off, 1.0f);
    }
    draw_list->AddLine(
        Pos(angle_max, peg_start),
        Pos(angle_max, peg_max_end),
        peg_color_max,
        1.0f);
    draw_list->AddLine(
        Pos(angle_min, peg_start),
        Pos(angle_min, peg_max_end),
        peg_color_max,
        1.0f);

    // the knob
    draw_list->AddCircleFilled(center, knob_size, knob_color, seg);
    draw_list->AddLine(
        Pos(angle, knob_mark_start),
        Pos(angle, knob_mark_end),
        indicator_color,
        2.0f);

    // knob control name
    draw_list->AddText(
        ImVec2(start.x, start.y + size_outer * 2 + style.ItemInnerSpacing.y),
        label_color,
        label);

    // tooltip
    if(is_active || is_hovered)
    {
      ImGui::SetNextWindowPos(ImVec2(
          start.x - style.WindowPadding.x,
          start.y - line_height - style.ItemInnerSpacing.y -
              style.WindowPadding.y));
      ImGui::BeginTooltip();
      ImGui::Text("%.3f", *p_value);
      ImGui::EndTooltip();
    }

    return value_changed;
  }
}<|endoftext|>


<commit_before>// Copyright (c) 2012-2015 Dano Pernis
// See LICENSE for details

#include <gtk/gtk.h>
#include <glib.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <cassert>
#include "CPU.h"

namespace hcc {

struct ROM : public IROM {
    unsigned short *data;
    static const unsigned int size = 0x8000;
    ROM() {
        data = new unsigned short[size];
    }
    virtual ~ROM() {
        delete[] data;
    }

    bool load(const char *filename) {
        std::ifstream input(filename);
        std::string line;
        unsigned int counter = 0;
        while (input.good() && counter < size) {
            getline(input, line);
            if (line.size() == 0)
                continue;
            if (line.size() != 16)
                return false;

            unsigned int instruction = 0;
            for (unsigned int i = 0; i<16; ++i) {
                instruction <<= 1;
                switch (line[i]) {
                case '0':
                    break;
                case '1':
                    instruction |= 1;
                    break;
                default:
                    return false;
                }
            }
            data[counter++] = instruction;
        }
        // clear the rest
        while (counter < size) {
            data[counter++] = 0;
        }

        return true;
    }
    virtual unsigned short get(unsigned int address) const {
        if (address < size) {
            return data[address];
        } else {
            std::cerr << "requested memory at " << address << '\n';
            throw std::runtime_error("Memory::get");
        }
    }
};

} // end namespace

struct GUIEmulatorRAM : public hcc::IRAM {
    static const unsigned int CHANNELS = 3;
    static const unsigned int SCREEN_WIDTH = 512;
    static const unsigned int SCREEN_HEIGHT = 256;
    static const unsigned int size = 0x6001;

    unsigned short *data;
    unsigned char *vram;
    GdkPixbuf *pixbuf;
    GtkWidget *screen;

    void putpixel(unsigned short x, unsigned short y, bool black) {
        unsigned int offset = CHANNELS*(SCREEN_WIDTH*y + x);
        for (unsigned int channel = 0; channel<CHANNELS; ++channel) {
            vram[offset++] = black ? 0x00 : 0xff;
        }
    }
public:
    GUIEmulatorRAM() {
        data = new unsigned short[size];
        vram = new unsigned char[CHANNELS*SCREEN_WIDTH*SCREEN_HEIGHT];
        pixbuf = gdk_pixbuf_new_from_data(vram, GDK_COLORSPACE_RGB, FALSE, 8, SCREEN_WIDTH, SCREEN_HEIGHT, CHANNELS*SCREEN_WIDTH, NULL, NULL);
        screen = gtk_image_new_from_pixbuf(pixbuf);
    }
    virtual ~GUIEmulatorRAM() {
        delete[] data;
        delete[] vram;
    }
    void keyboard(unsigned short value) {
        data[0x6000] = value;
    }
    GtkWidget* getScreenWidget() {
        return screen;
    }
    virtual void set(unsigned int address, unsigned short value) {
        if (address >= size) {
            throw std::runtime_error("RAM::set");
        }

        data[address] = value;

        // check if we are writing to video RAM
        if (0x4000 <= address && address <0x6000) {
            address -= 0x4000;

            unsigned short y = address / 32;
            unsigned short x = 16*(address % 32);
            for (int bit = 0; bit<16; ++bit) {
                putpixel(x + bit, y, value & 1);
                value = value >> 1;
            }
            gdk_threads_enter();
            gtk_widget_queue_draw(screen);
            gdk_threads_leave();
        }
    }
    virtual unsigned short get(unsigned int address) const {
        if (address >= size) {
            throw std::runtime_error("RAM::get");
        }

        return data[address];
    }
};
hcc::ROM rom;
GUIEmulatorRAM *ram;
hcc::CPU cpu;

bool running = false;

GtkWidget *window;
GtkToolItem *button_load, *button_run, *button_pause;

void load_clicked(GtkButton *button, gpointer user_data)
{
    bool loaded = false;

    GtkWidget *dialog = gtk_file_chooser_dialog_new(
        "Load ROM", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN,
        "gtk-cancel", GTK_RESPONSE_CANCEL,
        "gtk-open", GTK_RESPONSE_ACCEPT,
        NULL);
    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
        char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
        loaded = rom.load(filename);
        g_free(filename);
    }
    gtk_widget_destroy(dialog);

    if (!loaded) {
        GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
            "Error loading program");
        gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_destroy(dialog);
    } else {
        cpu.reset();
        gtk_widget_set_sensitive(GTK_WIDGET(button_run), TRUE);
    }
}

gpointer run_thread(gpointer user_data)
{
    int steps = 0;
    while (running) {
        cpu.step(&rom, ram);
        if (steps>100) {
            g_usleep(10);
            steps = 0;
        }
        ++steps;
    }
    return NULL;
}
void run_clicked()
{
    assert(!running);

    running = true;
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), TRUE);

    g_thread_create(run_thread, NULL, FALSE, NULL);
}
void pause_clicked()
{
    assert(running);

    running = false;
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_run), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(button_run), TRUE);
}

// Translate special keys. See Figure 5.6 in TECS book.
unsigned short translate(guint keyval)
{
    switch (keyval) {
    case GDK_KEY_Return:    return 128;
    case GDK_KEY_BackSpace: return 129;
    case GDK_KEY_Left:      return 130;
    case GDK_KEY_Up:        return 131;
    case GDK_KEY_Right:     return 132;
    case GDK_KEY_Down:      return 133;
    case GDK_KEY_Home:      return 134;
    case GDK_KEY_End:       return 135;
    case GDK_KEY_Page_Up:   return 136;
    case GDK_KEY_Page_Down: return 137;
    case GDK_KEY_Insert:    return 138;
    case GDK_KEY_Delete:    return 139;
    case GDK_KEY_Escape:    return 140;
    case GDK_KEY_F1:        return 141;
    case GDK_KEY_F2:        return 142;
    case GDK_KEY_F3:        return 143;
    case GDK_KEY_F4:        return 144;
    case GDK_KEY_F5:        return 145;
    case GDK_KEY_F6:        return 146;
    case GDK_KEY_F7:        return 147;
    case GDK_KEY_F8:        return 148;
    case GDK_KEY_F9:        return 149;
    case GDK_KEY_F10:       return 150;
    case GDK_KEY_F11:       return 151;
    case GDK_KEY_F12:       return 152;
    }
    return keyval;
}

gboolean keyboard_callback(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
    if (event->type == GDK_KEY_RELEASE) {
        ram->keyboard(0);
    } else {
        ram->keyboard(translate(event->keyval));
    }
    return TRUE;
}

GtkToolItem *create_button(const gchar *stock_id, const gchar *text, GCallback callback)
{
    GtkToolItem *button = gtk_tool_button_new(NULL, text);
    gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(button), stock_id);
    gtk_tool_item_set_tooltip_text(button, text);
    g_signal_connect(button, "clicked", callback, NULL);
    return button;
}

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    ram = new GUIEmulatorRAM();

    /* toolbar buttons */
    button_load    = create_button("document-open",        "Load...", G_CALLBACK(load_clicked));
    button_run     = create_button("media-playback-start", "Run",     G_CALLBACK(run_clicked));
    button_pause   = create_button("media-playback-pause", "Pause",   G_CALLBACK(pause_clicked));

    GtkToolItem *separator1 = gtk_separator_tool_item_new();

    gtk_widget_set_sensitive(GTK_WIDGET(button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(button_pause), FALSE);

    /* toolbar itself */
    GtkWidget *toolbar = gtk_toolbar_new();
    gtk_widget_set_hexpand(toolbar, TRUE);
    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_load,  -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), separator1,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_run,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button_pause, -1);

    /* keyboard */
    GtkWidget *keyboard = gtk_toggle_button_new_with_label("Grab keyboard focus");
    gtk_widget_add_events(keyboard, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
    g_signal_connect(keyboard, "key-press-event",   G_CALLBACK(keyboard_callback), NULL);
    g_signal_connect(keyboard, "key-release-event", G_CALLBACK(keyboard_callback), NULL);

    /* main layout */
    GtkWidget *grid = gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), ram->getScreenWidget(), 0, 1, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), keyboard, 0, 2, 1, 1);

    /* main window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "HACK emulator");
    gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
    gtk_window_set_focus(GTK_WINDOW(window), NULL);
    g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_add(GTK_CONTAINER(window), grid);
    gtk_widget_show_all(window);
    gtk_widget_set_visible(GTK_WIDGET(button_pause), FALSE);

    gtk_main();
    return 0;
}
<commit_msg>Got rid of global state<commit_after>// Copyright (c) 2012-2015 Dano Pernis
// See LICENSE for details

#include <gtk/gtk.h>
#include <glib.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <cassert>
#include "CPU.h"

namespace hcc {

struct ROM : public IROM {
    unsigned short *data;
    static const unsigned int size = 0x8000;
    ROM() {
        data = new unsigned short[size];
    }
    virtual ~ROM() {
        delete[] data;
    }

    bool load(const char *filename) {
        std::ifstream input(filename);
        std::string line;
        unsigned int counter = 0;
        while (input.good() && counter < size) {
            getline(input, line);
            if (line.size() == 0)
                continue;
            if (line.size() != 16)
                return false;

            unsigned int instruction = 0;
            for (unsigned int i = 0; i<16; ++i) {
                instruction <<= 1;
                switch (line[i]) {
                case '0':
                    break;
                case '1':
                    instruction |= 1;
                    break;
                default:
                    return false;
                }
            }
            data[counter++] = instruction;
        }
        // clear the rest
        while (counter < size) {
            data[counter++] = 0;
        }

        return true;
    }
    virtual unsigned short get(unsigned int address) const {
        if (address < size) {
            return data[address];
        } else {
            std::cerr << "requested memory at " << address << '\n';
            throw std::runtime_error("Memory::get");
        }
    }
};

} // end namespace

struct GUIEmulatorRAM : public hcc::IRAM {
    static const unsigned int CHANNELS = 3;
    static const unsigned int SCREEN_WIDTH = 512;
    static const unsigned int SCREEN_HEIGHT = 256;
    static const unsigned int size = 0x6001;

    unsigned short *data;
    unsigned char *vram;
    GdkPixbuf *pixbuf;
    GtkWidget *screen;

    void putpixel(unsigned short x, unsigned short y, bool black) {
        unsigned int offset = CHANNELS*(SCREEN_WIDTH*y + x);
        for (unsigned int channel = 0; channel<CHANNELS; ++channel) {
            vram[offset++] = black ? 0x00 : 0xff;
        }
    }
public:
    GUIEmulatorRAM() {
        data = new unsigned short[size];
        vram = new unsigned char[CHANNELS*SCREEN_WIDTH*SCREEN_HEIGHT];
        pixbuf = gdk_pixbuf_new_from_data(vram, GDK_COLORSPACE_RGB, FALSE, 8, SCREEN_WIDTH, SCREEN_HEIGHT, CHANNELS*SCREEN_WIDTH, NULL, NULL);
        screen = gtk_image_new_from_pixbuf(pixbuf);
    }
    virtual ~GUIEmulatorRAM() {
        delete[] data;
        delete[] vram;
    }
    void keyboard(unsigned short value) {
        data[0x6000] = value;
    }
    GtkWidget* getScreenWidget() {
        return screen;
    }
    virtual void set(unsigned int address, unsigned short value) {
        if (address >= size) {
            throw std::runtime_error("RAM::set");
        }

        data[address] = value;

        // check if we are writing to video RAM
        if (0x4000 <= address && address <0x6000) {
            address -= 0x4000;

            unsigned short y = address / 32;
            unsigned short x = 16*(address % 32);
            for (int bit = 0; bit<16; ++bit) {
                putpixel(x + bit, y, value & 1);
                value = value >> 1;
            }
            gdk_threads_enter();
            gtk_widget_queue_draw(screen);
            gdk_threads_leave();
        }
    }
    virtual unsigned short get(unsigned int address) const {
        if (address >= size) {
            throw std::runtime_error("RAM::get");
        }

        return data[address];
    }
};

struct emulator {
    hcc::ROM rom;
    GUIEmulatorRAM ram;
    hcc::CPU cpu;

    bool running = false;

    GtkWidget* window;
    GtkToolItem* button_load;
    GtkToolItem* button_run;
    GtkToolItem* button_pause;
};

void load_clicked(GtkButton *button, gpointer user_data)
{
    emulator* e = reinterpret_cast<emulator*>(user_data);

    bool loaded = false;

    GtkWidget *dialog = gtk_file_chooser_dialog_new(
        "Load ROM", GTK_WINDOW(e->window), GTK_FILE_CHOOSER_ACTION_OPEN,
        "gtk-cancel", GTK_RESPONSE_CANCEL,
        "gtk-open", GTK_RESPONSE_ACCEPT,
        NULL);
    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
        char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
        loaded = e->rom.load(filename);
        g_free(filename);
    }
    gtk_widget_destroy(dialog);

    if (!loaded) {
        GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(e->window),
            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
            "Error loading program");
        gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_destroy(dialog);
    } else {
        e->cpu.reset();
        gtk_widget_set_sensitive(GTK_WIDGET(e->button_run), TRUE);
    }
}

gpointer run_thread(gpointer user_data)
{
    emulator* e = reinterpret_cast<emulator*>(user_data);

    int steps = 0;
    while (e->running) {
        e->cpu.step(&e->rom, &e->ram);
        if (steps>100) {
            g_usleep(10);
            steps = 0;
        }
        ++steps;
    }
    return NULL;
}
void run_clicked(GtkButton *button, gpointer user_data)
{
    emulator* e = reinterpret_cast<emulator*>(user_data);

    assert(!e->running);

    e->running = true;
    gtk_widget_set_sensitive(GTK_WIDGET(e->button_run), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(e->button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(e->button_pause), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(e->button_pause), TRUE);

    g_thread_create(run_thread, e, FALSE, NULL);
}
void pause_clicked(GtkButton *button, gpointer user_data)
{
    emulator* e = reinterpret_cast<emulator*>(user_data);

    assert(e->running);

    e->running = false;
    gtk_widget_set_sensitive(GTK_WIDGET(e->button_pause), FALSE);
    gtk_widget_set_visible(GTK_WIDGET(e->button_pause), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(e->button_run), TRUE);
    gtk_widget_set_visible(GTK_WIDGET(e->button_run), TRUE);
}

// Translate special keys. See Figure 5.6 in TECS book.
unsigned short translate(guint keyval)
{
    switch (keyval) {
    case GDK_KEY_Return:    return 128;
    case GDK_KEY_BackSpace: return 129;
    case GDK_KEY_Left:      return 130;
    case GDK_KEY_Up:        return 131;
    case GDK_KEY_Right:     return 132;
    case GDK_KEY_Down:      return 133;
    case GDK_KEY_Home:      return 134;
    case GDK_KEY_End:       return 135;
    case GDK_KEY_Page_Up:   return 136;
    case GDK_KEY_Page_Down: return 137;
    case GDK_KEY_Insert:    return 138;
    case GDK_KEY_Delete:    return 139;
    case GDK_KEY_Escape:    return 140;
    case GDK_KEY_F1:        return 141;
    case GDK_KEY_F2:        return 142;
    case GDK_KEY_F3:        return 143;
    case GDK_KEY_F4:        return 144;
    case GDK_KEY_F5:        return 145;
    case GDK_KEY_F6:        return 146;
    case GDK_KEY_F7:        return 147;
    case GDK_KEY_F8:        return 148;
    case GDK_KEY_F9:        return 149;
    case GDK_KEY_F10:       return 150;
    case GDK_KEY_F11:       return 151;
    case GDK_KEY_F12:       return 152;
    }
    return keyval;
}

gboolean keyboard_callback(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
    emulator* e = reinterpret_cast<emulator*>(user_data);

    if (event->type == GDK_KEY_RELEASE) {
        e->ram.keyboard(0);
    } else {
        e->ram.keyboard(translate(event->keyval));
    }
    return TRUE;
}

GtkToolItem *create_button(const gchar *stock_id, const gchar *text, GCallback callback, gpointer user_data)
{
    GtkToolItem *button = gtk_tool_button_new(NULL, text);
    gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(button), stock_id);
    gtk_tool_item_set_tooltip_text(button, text);
    g_signal_connect(button, "clicked", callback, user_data);
    return button;
}

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);

    emulator e;

    /* toolbar buttons */
    e.button_load    = create_button("document-open",        "Load...", G_CALLBACK(load_clicked), &e);
    e.button_run     = create_button("media-playback-start", "Run",     G_CALLBACK(run_clicked), &e);
    e.button_pause   = create_button("media-playback-pause", "Pause",   G_CALLBACK(pause_clicked), &e);

    GtkToolItem *separator1 = gtk_separator_tool_item_new();

    gtk_widget_set_sensitive(GTK_WIDGET(e.button_run), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(e.button_pause), FALSE);

    /* toolbar itself */
    GtkWidget *toolbar = gtk_toolbar_new();
    gtk_widget_set_hexpand(toolbar, TRUE);
    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), e.button_load,  -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), separator1,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), e.button_run,   -1);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), e.button_pause, -1);

    /* keyboard */
    GtkWidget *keyboard = gtk_toggle_button_new_with_label("Grab keyboard focus");
    gtk_widget_add_events(keyboard, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
    g_signal_connect(keyboard, "key-press-event",   G_CALLBACK(keyboard_callback), &e);
    g_signal_connect(keyboard, "key-release-event", G_CALLBACK(keyboard_callback), &e);

    /* main layout */
    GtkWidget *grid = gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), e.ram.getScreenWidget(), 0, 1, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), keyboard, 0, 2, 1, 1);

    /* main window */
    e.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(e.window), "HACK emulator");
    gtk_window_set_resizable(GTK_WINDOW(e.window), FALSE);
    gtk_window_set_focus(GTK_WINDOW(e.window), NULL);
    g_signal_connect(e.window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_add(GTK_CONTAINER(e.window), grid);
    gtk_widget_show_all(e.window);
    gtk_widget_set_visible(GTK_WIDGET(e.button_pause), FALSE);

    gtk_main();
    return 0;
}
<|endoftext|>